MODBUS MASTER RTU在STM32上的實現


MODBUS MASTER RTU在STM32上的實現

1.概述

  • 最近需要將幾個信號采集模塊通過總線串聯起來,這樣便於系統模塊化。故將目光關注到了工業上經常使用的modbus協議。

  • modbus協議是一種一主多從的拓撲結構,主要是應用層軟件協議,有關modbus的相關信息,可以自行google、百度。

  • STM32實現的Master工程代碼在github上,點擊獲取

2.開發環境

  • STM32F042單片機
  • MDK-KEIL5
  • STM32-CUBE庫
  • Modbus slaver測試軟件

3.移植來源

  • 信號采集模塊作為slaver,采用的是開源的freemodbus協議。關於其的實現大家百度一下都能發現,相關的移植過程介紹也很多,不再一一贅述。值得注意的是:這個freemodbus的源碼值得一看,其判斷對一幀數據包的接受采用的是定時器判斷超時。大體思路是中斷接收函數在接收每一個字節數據時會重置定時器,如果定時器發生定時溢出中斷,則說明沒有新數據到來,代表一個數據包接收完整。

  • 然后就是master的設計實現。freemodbus並沒有開源的master實現代碼,故這部分需要我們自己開發完成。在github上發現有人發布了ardunio版本的master,但是ardunio的代碼采C++語言編寫,需要我們做一些C語言的移植和一些硬件底層接口的移植。

    ardunio master的github源工程鏈接點擊,感謝他的分享。

4.移植過程

  • 了解ardunio modbus庫的實現思路——很簡單明了。打開源工程,里面有源代碼和例程代碼,不過例程代碼需要用ardunio的IDE打開。其大體思路就是每個Modbus Function都用一個函數實現,如

      uint8_t ModbusMaster::readDiscreteInputs(uint16_t u16ReadAddress,uint16_t u16BitQty)
      {
        _u16ReadAddress = u16ReadAddress;
        _u16ReadQty = u16BitQty;
        return ModbusMasterTransaction(ku8MBReadDiscreteInputs);
       }
    

這個ModbusMasterTransaction函數就是根據用戶選擇的功能模塊填充數組並且發送,然后等待從機回應的數據(帶超時檢測),接着解析接收到的數據包,如果成功則將數據放在_u16ResponseBuffer數組中

  • 將ardunio的C++代碼移植為C語言

  • 將ardunio相關的serial等函數使用自己的代碼實現,serial函數其實就是硬件層接口的函數封裝,這也是移植到其他平台必須要根據自身平台做相應的改變。
    (1). 在ModbusMasterTransaction函數中涉及到_serial->read()、_serial->write()、_serial->flush()、_serial->available()、millis()、bitWrite()、bitRead()、word()幾種函數,從名字中我們就可以知道什么意思,故我們需要在我們的系統中重新實現這幾個函數。
    (2). 我們底層串口的設計思路如下,數據發送采用數據的發送直接采用循環發送,而STM32cube庫已經將這個功能封裝好API接口,我們再封裝一層即可,如下所示

      /**
        * @brief  將數據包發送出去
        * @param
        * @note
        * @retval void
        * @author xiaodaqi
        */
      uint8_t Modbus_Master_Write(uint8_t *buf,uint8_t length)
      {
       if(HAL_UART_Transmit(&huart2 ,(uint8_t *)buf,length,0xff))
       {
         return HAL_ERROR;
       }
       	else
      	{
      	  return HAL_OK;
      	}
      }
    

這句函數就相當於_serial->write()的實現。
(3). 串口數據的接收:我們采用中斷接收的方式,並且在中斷處理函數中將接收到的字節采用循環隊壓人緩沖區,這樣子就能實現ardunio的功能代碼。

>  跟底層相關的移植代碼可到我的工程 Modbus_Master--trans_recieve_buff_control.c .h文件查看。  

_serial->read()的變體為:  

	/**
	  * @brief  讀出緩沖區的數據
	  * @param
	  * @note
	  * @retval void
	  * @author xiaodaqi
	  */
	uint8_t Modbus_Master_Read(void)
	{
		uint8_t cur =0xff;
		if( !rbIsEmpty(&m_Modbus_Master_RX_RingBuff))
		{
			  cur = rbPop(&m_Modbus_Master_RX_RingBuff);
		}
		return cur;
	}

_serial->flush()的變體為:

	/**
	  * @brief  清除環形隊列
	  * @param
	  * @note
	  * @retval void
	  * @author xiaodaqi
	  */
	uint8_t Modbus_Master_Rece_Flush(void)
	{
	  rbClear(&m_Modbus_Master_RX_RingBuff);
	}

_serial->available()的變體為:

	/**
	  * @brief  判斷ringbuffer里面是否有尚未處理的字節
	  * @param
	  * @note
	  * @retval void
	  * @author xiaodaqi
	  */
	uint8_t Modbus_Master_Rece_Available(void)
	{
		/*如果數據包buffer里面溢出了,則清零,重新計數*/
			if(m_Modbus_Master_RX_RingBuff.flagOverflow==1)
			{
				rbClear(&m_Modbus_Master_RX_RingBuff);
			}
		return !rbIsEmpty(&m_Modbus_Master_RX_RingBuff);
	}

millis()的變體為:

	/**
	  * @brief  1ms周期的定時器
	  * @param
	  * @note
	  * @retval void
	  * @author xiaodaqi
	  */
	uint32_t Modbus_Master_Millis(void)
	{
	  return HAL_GetTick();
	}

其余幾個函數的變體如下:

	/*模擬ardunio函數*************************************************/
	static inline uint8_t lowByte(uint16_t ww)
	{
	  return (uint8_t) ((ww) & 0x00FF);
	}
	
	static inline uint8_t highByte(uint16_t ww)
	{
	  return (uint8_t) ((ww) >> 8);
	}
	
	static inline uint16_t word(uint8_t H_Byte,uint8_t L_Byte)
	{
		uint16_t word;
		word = (uint16_t)(H_Byte<<8);
		word = word + L_Byte;
	  return word;
	}
	
	#define bitSet(value, bit)  ((value) |= (1UL << (bit))) 
	#define bitClear(value, bit)  ((value) &= ~(1UL << (bit)))
	
	#define bitWrite(value, bit, bitvalue) (bitvalue ? bitSet(value, bit) : bitClear(value, bit))
	#define bitRead(value, bit)  (((value) >> (bit)) & 0x01) 

(4).剩下的就是串口的一些硬件初始化,如波特率等,這些配置由於我是用CUBEMX配置直接生成的代碼,這里不做過多闡述。

5.代碼調試

完成上述關鍵代碼的移植后就可以進行調試了。

  • 在main函數中增加如下的測試代碼

        while (1)
        {
      		uint8_t result;
      		//測試read input registers功能
              //從機地址0x01 ,連續都2個地址為0x2的寄存器
      		result = ModbusMaster_readInputRegisters(0x01,0x2, 2);
      		if (result == 0x00)
      		{
      			Input_Result[0] = ModbusMaster_getResponseBuffer(0x00);
      			Input_Result[1] = ModbusMaster_getResponseBuffer(0x01);
      		}
      			HAL_Delay(1000);
        }
    
  • 我們使用Modbus Slave軟件來模擬從機。並且設置相應的地址里面的數值為1和2。軟件使用方法可自行百度、google。下圖為軟件設置及運行結果。

  • 然后我們用keil仿真查看結果Input_Result數組的結果。如下圖所示,結果顯示modbus通信正確。

6.總結

以上即為本次移植的過程,有需要的朋友可以直接使用我的代碼,亦可使用ardunio代碼自行移植。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM