一、RS485基礎(chǔ)通信例程實(shí)現(xiàn)的功能描述
開發(fā)板通過(guò)UART1接口 SP485EEN芯片實(shí)現(xiàn)和電腦端串口調(diào)試助手的通信。并實(shí)現(xiàn)如下兩個(gè)收發(fā)范例功能: 如果PC端通過(guò)485向開發(fā)板發(fā)送5字節(jié)數(shù)據(jù),且5字節(jié)數(shù)據(jù)為06 07 08 09 0A,則黃色LED1閃爍一次 開發(fā)板每隔1秒通過(guò)485向PC端發(fā)送5字節(jié)數(shù)據(jù),01 02 03 04 05
二、本實(shí)驗(yàn)教學(xué)目的
掌握基于ShineBlink的: UART通信(占用RX1,TX1) GPIO控制485轉(zhuǎn)換芯片的方向(D2引腳控制485芯片的方向,高電平) -GPIO控制LED1亮滅(D8連接黃色LED1)
三、本實(shí)驗(yàn)涉及的模塊 485接口在開發(fā)板上的位置如下:
注意,為了使用485接口,必須將P7和P8跳線帽短接才能讓TX1引腳、RX1引腳和485轉(zhuǎn)換芯片連接。
四、完整源代碼 以下代碼實(shí)現(xiàn)了如下功能: 如果PC端通過(guò)485向開發(fā)板發(fā)送5字節(jié)數(shù)據(jù),且5字節(jié)數(shù)據(jù)為06 07 08 09 0A,則黃色LED1閃爍一次 開發(fā)板每隔1秒通過(guò)485向PC端發(fā)送5字節(jié)數(shù)據(jù),01 02 03 04 05
- LIB_GpioOutputConfig("D8","STANDARD") –初始化GPIO控制黃色LED1
- LIB_GpioOutputConfig("D2","STANDARD") –初始化GPIO控制Max485 RE DE 收發(fā)控制
- –配置Uart1串口波特率為19200,用作485通訊
- LIB_Uart1Config("BAUDRATE_19200")
- –使能MAX485發(fā)送
- function SendEn()
- LIB_GpioWrite("D2",1)
- end
- –使能MAX485接收
- function recvEn()
- LIB_GpioWrite("D2",0)
- end
- –使能10毫秒定時(shí)器開始工作
- LIB_10msTimerConfig("ENABLE")
- cnt_10ms = 0
- –定義10毫秒定時(shí)器的中斷函數(shù)
- function LIB_10msTimerCallback()
- cnt_10ms = cnt_10ms 1
- end
- –開始大循環(huán)
- while(GC(1) == true)
- do
- –每1秒發(fā)送5字節(jié)數(shù)據(jù)給PC端
- if cnt_10ms >= 100 then –1000ms
- cnt_10ms = 0
- send_data = {1,2,3,4,5}
- SendEn()
- LIB_Uart1BlockSend(send_data)
- RecvEn()
- end
- –查詢是否收到PC端發(fā)來(lái)的5字節(jié)數(shù)據(jù),并驗(yàn)證
- recv_flag,recv_data = LIB_Uart1Recv()
- if recv_flag == 1 and #recv_data == 5 then
- if recv_data[1] == 6 and recv_data[2] == 7 and recv_data[3] == 8 and recv_data[4] == 9 and recv_data[5] == 10 then
- LIB_GpioToggle("D8") –切換LED狀態(tài)
- end
- end
- end
復(fù)制代碼
五、實(shí)驗(yàn)現(xiàn)象
將開發(fā)板的485接口通過(guò)485轉(zhuǎn)USB工具和PC端連接以后,將上面的代碼復(fù)制到開發(fā)板的虛擬TF卡中并開始運(yùn)行,之后每秒鐘可以在PC端串口調(diào)試助手收到開發(fā)板發(fā)來(lái)的5字節(jié)數(shù)據(jù)(01 02 03 04 05),并且當(dāng)調(diào)試助手向開發(fā)板下發(fā)(06 07 08 09 0a)以后,開發(fā)板的黃色LED燈會(huì)閃爍以下,如下圖:
注意:軟件需要勾選“HEX顯示”和"HEX發(fā)送"。設(shè)備和上位機(jī)(Modbus主機(jī))通信的實(shí)現(xiàn)簡(jiǎn)介:下文介紹了如何用ShineBlink作為設(shè)備端(Modbus從機(jī))來(lái)和上位機(jī)(Modbus主機(jī))來(lái)通信,并在ShineBlink設(shè)備端實(shí)現(xiàn)了0x03功能碼(讀取多個(gè)保持寄存器)和0x05功能碼(寫單個(gè)線圈)的程序代碼。一、實(shí)現(xiàn)環(huán)境設(shè)備作為Modbus從機(jī)通過(guò)RS485總線和上位機(jī)通信,我們?cè)陔娔X上運(yùn)行知名的Modbus Poll調(diào)試軟件作為上位機(jī)來(lái)模擬Modbus主機(jī),Modbus Poll軟件可以到其官網(wǎng)上下載。二、設(shè)備介紹設(shè)備作為Modbus網(wǎng)絡(luò)中的其中一個(gè)節(jié)點(diǎn)有如下特性:串口屬性:19200、N、8、1設(shè)備地址:21(0x15)設(shè)備支持的Modbus功能碼:0x05 寫單個(gè)線圈0x03 讀取多個(gè)保持寄存器功能介紹:0x05,上位機(jī)通過(guò)向設(shè)備發(fā)送0x05功能碼,對(duì)線圈地址為0x0000的線圈寫入值0xFF00時(shí),設(shè)備開始運(yùn)行,對(duì)線圈地址為0x0000的線圈寫入值0x0000時(shí),設(shè)備停止運(yùn)行。0x03,上位機(jī)通過(guò)向設(shè)備發(fā)送0x03功能碼,讀取保持寄存器起始地址為0x0000的9個(gè)保持寄存器(每個(gè)保持寄存器值為16bit無(wú)符號(hào)數(shù)據(jù)),每個(gè)寄存器對(duì)應(yīng)的數(shù)據(jù)如下:
三、Modbus通信實(shí)現(xiàn)代碼實(shí)例
以下代碼不僅實(shí)現(xiàn)了03和05功能碼,并實(shí)現(xiàn)了將各種異常情況回復(fù)給Modbus主機(jī)。
- –程序中用到的全局變量定義
- Pm25Percent = 0
- HchoPercent = 0
- TvocPercent = 0
- MeshPercent = 0.0
- Temprature1 = 0.00
- Temprature2 = 0.00
- Wind485DisSpeed = 0
- DevIsRunning = 0 –控制設(shè)備運(yùn)行或停止
- FaultCode = 0 –故障代碼
- –ModBus通信函數(shù)定義
- function ModbusProcess()
- local sdata = {}
- –查詢是否收到Modbus主機(jī)發(fā)來(lái)的消息
- flag, data = LIB_Uart1Recv()
- if flag == 1 then
- –判斷消息是不是發(fā)給本機(jī),是本機(jī)的才理會(huì)
- if data[1] == PI[2] then –PI[2], Modbus本機(jī)地址(1-247)
- –判斷Modbus功能碼
- if data[2] == 0x05 then — 0x05 寫單個(gè)線圈
- –這里定義線圈地址為0x0000的線圈為開機(jī)/關(guān)機(jī)控制信號(hào)
- if data[3] == 0x00 and data[4] == 0x00 then
- if data[5] == 0xff and data[6] == 0x00 then –ON
- DevIsRunning = 1 –置1開機(jī)全局變量
- elseif data[5] == 0x00 and data[6] == 0x00 then –OFF
- DevIsRunning = 0 –置0開機(jī)全局變量
- else
- –這里需回復(fù)非法數(shù)據(jù)03異常消息(非法數(shù)據(jù)值),讀者可自行完成
- end
- –回復(fù)OK,把收到的數(shù)據(jù)原封不動(dòng)回傳
- LIB_GpioWrite("D2",1) –使能485模塊發(fā)送
- LIB_Uart1BlockSend(data)
- LIB_GpioWrite("D2",0) –使能485模塊接收
- else
- –回復(fù)異常消息(非法數(shù)據(jù)地址)
- sdata[1] = data[1] –本機(jī)地址
- sdata[2] = data[2] 0x80 –異常的時(shí)候功能碼加0x80
- sdata[3] = 0x02 –異常碼0x02表示設(shè)備不支持此數(shù)據(jù)地址
- CRC = LIB_CrcCalculate("CRC16_MODBUS", sdata)
- sdata[4] = CRC & 0x00ff –低位在前
- sdata[5] = CRC >> 8 –高位在后
- LIB_GpioWrite("D2",1) –使能485模塊發(fā)送
- LIB_Uart1BlockSend(sdata)
- LIB_GpioWrite("D2",0) –使能485模塊接收
- end
- –這里用0x03而不用0x04是因?yàn)楹芏嘀鳈C(jī)只支持03 06 16指令,所以就隨大流咯
- elseif data[2] == 0x03 then –0x03 讀多個(gè)保持寄存器
- –這里定義起始地址為0x0000的這些寄存器存放傳感器數(shù)據(jù),且讀取的寄存器個(gè)數(shù)必須是9個(gè)
- if data[3] == 0x00 and data[4] == 0x00 and data[5] == 0x00 and data[6] == 0x09 then
- sdata[1] = data[1] –本機(jī)地址
- sdata[2] = data[2] –功能碼
- sdata[3] = 18 –數(shù)據(jù)域字節(jié)數(shù): 9個(gè)寄存器一共18字節(jié)
- sdata[4] = Pm25Percent >> 8
- sdata[5] = Pm25Percent & 0x00ff
- sdata[6] = HchoPercent >> 8
- sdata[7] = HchoPercent & 0x00ff
- sdata[8] = TvocPercent >> 8
- sdata[9] = TvocPercent & 0x00ff
- sdata[10] = math.floor(MeshPercent) >> 8
- sdata[11] = math.floor(MeshPercent) & 0x00ff
- sdata[12] = math.floor(Temprature1) >> 8
- sdata[13] = math.floor(Temprature1) & 0x00ff
- sdata[14] = math.floor(Temprature2) >> 8
- sdata[15] = math.floor(Temprature2) & 0x00ff
- sdata[16] = Wind485DisSpeed >> 8
- sdata[17] = Wind485DisSpeed & 0x00ff
- sdata[18] = DevIsRunning >> 8
- sdata[19] = DevIsRunning & 0x00ff
- sdata[20] = FaultCode >> 8
- sdata[21] = FaultCode & 0x00ff
- CRC = LIB_CrcCalculate("CRC16_MODBUS", sdata)
- sdata[22] = CRC & 0x00ff –低位在前
- sdata[23] = CRC >> 8 –高位在后
- –回復(fù)傳感器數(shù)據(jù)
- LIB_GpioWrite("D2",1) –使能485模塊發(fā)送
- LIB_Uart1BlockSend(sdata)
- LIB_GpioWrite("D2",0) –使能485模塊接收
- else
- –回復(fù)異常消息(非法數(shù)據(jù)地址)
- sdata[1] = data[1] –本機(jī)地址
- sdata[2] = data[2] 0x80 –異常的時(shí)候功能碼加0x80
- sdata[3] = 0x02 –異常碼0x02表示設(shè)備不支持此數(shù)據(jù)地址
- CRC = LIB_CrcCalculate("CRC16_MODBUS", sdata)
- sdata[4] = CRC & 0x00ff –低位在前
- sdata[5] = CRC >> 8 –高位在后
- LIB_GpioWrite("D2",1) –使能485模塊發(fā)送
- LIB_Uart1BlockSend(sdata)
- LIB_GpioWrite("D2",0) –使能485模塊接收
- end
- else
- –回復(fù)異常消息(非法功能碼)
- sdata[1] = data[1] –本機(jī)地址
- sdata[2] = data[2] 0x80 –異常的時(shí)候功能碼加0x80
- sdata[3] = 0x01 –異常碼0x01表示設(shè)備不支持此功能碼
- CRC = LIB_CrcCalculate("CRC16_MODBUS", sdata)
- sdata[4] = CRC & 0x00ff –低位在前
- sdata[5] = CRC >> 8 –高位在后
- LIB_GpioWrite("D2",1) –使能485模塊發(fā)送
- LIB_Uart1BlockSend(sdata)
- LIB_GpioWrite("D2",0) –使能485模塊接收
- end
- end
- end
- end
- ?
- –開始初始化ShineBlink
- –配置Uart1串口波特率為19200,接485模塊
- LIB_Uart1Config("BAUDRATE_19200")
- –485發(fā)送和接收控制引腳
- LIB_GpioOutputConfig("D2","STANDARD")
- LIB_GpioWrite("D2",0) –使能485模塊接收
- ?
- –開始大循環(huán)
- while(GC(1) == true)
- do
- –Modbus通信處理
- ModbusProcess()
- end
復(fù)制代碼
四、Modbus Poll 上位機(jī)實(shí)驗(yàn)流程 (1)配置0x03讀取保持寄存器功能(Setup)
(2)建立Modbus串行通信連接(Connectiong)
(3)建立連接后的數(shù)據(jù)通信 1. 上位機(jī)每秒鐘自動(dòng)下發(fā)0x03指令讀取設(shè)備的起始地址為0x0000的9個(gè)保持寄存器的值
通信數(shù)據(jù)日志:
上面設(shè)備回復(fù)的23個(gè)字節(jié)數(shù)據(jù)舉例說(shuō)明:例如:15 03 12 00 06 00 00 00 01 00 00 00 16 00 16 00 00 00 00 00 00 D5 16數(shù)據(jù)結(jié)尾的CRC算法采用Modbus Crc16
- 本機(jī)地址:0x15
- 功能碼:0x03
- 字節(jié)數(shù):18字節(jié)(9個(gè)16bit無(wú)符號(hào)寄存器)
- 寄存器1:0x0006 表示pm25=6
- 寄存器2:0x0000 表示hch0=0
- 寄存器3:0x0001 表示tvoc=1
- 寄存器4:0x0000 表示mesh=6
- 寄存器5:0x0016 表示temprature1=21度
- 寄存器6:0x0016 表示temprature2=21度
- 寄存器7:0x0000 表示windspeed=0
- 寄存器8:0x0000 表示running=0
- 寄存器9:0x0000 表示faultcode=0
- CRC_L:0xD5
- CRC_H:0x16
復(fù)制代碼 2. 上位機(jī)下發(fā)開機(jī)命令(功能碼0x05,向線圈地址為0x0000處寫單個(gè)線圈值0xFF00)
通信數(shù)據(jù)日志:
3. 上位機(jī)下發(fā)停機(jī)命令(功能碼0x05, 向線圈地址為0x0000處寫單個(gè)線圈值0x0000)
上位機(jī)發(fā)送: 15 05 00 00 00 00 CE DE 設(shè)備應(yīng)回復(fù): 15 05 00 00 00 00 CE DE
|