基于STM32和W5500的Modbus TCP通讯
在最近的一個項目中需要實現Modbus TCP通訊,而選用的硬件平臺則是STM32F103和W5500,軟件平臺則選用IAR EWAR6.4來實現。
1、移植前的準備工作
為了實現Modbus TCP通訊首先需要下載W5500的驅動源碼,可以到WIZnet的官網下載:products:w5500:driver [Document Wiki]
下載下來的壓縮包,解壓后如下圖:
需要將ethernet文件夾拷貝到我們的項目目錄中:
并在IAR的項目下添加相關的文件和路徑,主要是socket.c、w5500.c、wizchip_.conf.c三個文件。這三個文件分別實現socket、硬件驅動及相關通訊配置功能,具體可以查看相應的源碼級手冊。
并在如下圖所示的項目選項設置中添加Ethernet和Ethernet\W5500目錄。
2、移植過程和代碼編寫
在完成以上工作后就可以開始真正地移植工作了。具體步驟如下:
-
硬件配置及初始化。
-
以太網通訊配置的初始化。
-
實現具體的通訊過程。
2.1、硬件的配置及初始化
由于W5500通過SPI接口與STM32通訊,所以硬件配置和初始化是非常簡單的,與W5500實際上沒有關系,使一些通用的操作。事實上就是STM32F103的SPI接口初始化的過程,需要實現RCC、GPIO以及SPI的初始化就可以了。關于這部分可以查看ST的例程。
2.2、以太網通訊配置的初始化
以太網通訊配置的初始化主要有三個方面的內容:
-
注冊TCP通訊相關的回調函數?RegisterFunction();
-
初始化芯片參數?ChipParametersConfiguration();
-
初始化網絡通訊參數?NetworkParameterConfiguration()
三個函數的具體實現內容如下:
//函數注冊,首先,應由用戶實現SPI注冊回調函數來訪問WIZCHIP void RegisterFunction(void) {?//臨界區回調函數reg_wizchip_cris_cbfunc(SPI_CrisEnter, SPI_CrisExit);?//注冊臨界區函數//片選回調函數 #if??_WIZCHIP_IO_MODE_ == _WIZCHIP_IO_MODE_SPI_VDM_reg_wizchip_cs_cbfunc(SPI_CS_Select, SPI_CS_Deselect);//注冊SPI片選信號函數#elif _WIZCHIP_IO_MODE_ ==_WIZCHIP_IO_MODE_SPI_FDM_reg_wizchip_cs_cbfunc(SPI_CS_Select, SPI_CS_Deselect);?// CS必須為低電平.#else#if(_WIZCHIP_IO_MODE_ & _WIZCHIP_IO_MODE_SIP_) != _WIZCHIP_IO_MODE_SIP_#error "Unknown _WIZCHIP_IO_MODE_"#elsereg_wizchip_cs_cbfunc(wizchip_select, wizchip_deselect);#endif#endif//SPI的讀寫回調函數reg_wizchip_spi_cbfunc(SPI_ReadByte, SPI_WriteByte);????//注冊讀寫函數}注冊函數實際上就是函數指針的調用,可參考C語言函數指針部分內容。對于以上注冊的函數,SPI_WriteByte需要說明一下,無論是用可函數還是直接操作寄存器,在寫完之后都需要再讀一下(紅色部分),否則就會在客戶端出現連接TCPServer超時的報警,沒明白什么原因。
//寫1字節數據到SPI總線 void SPI_WriteByte(uint8_t TxData) {?????????????????????? //?while((SPI2->SR&SPI_I2S_FLAG_TXE)==0);???????//等待發送區空????????????? //?SPI2->DR=TxData;?????????????????????????????//發送一個byte //?while((SPI2->SR&SPI_I2S_FLAG_RXNE)==0);??????//等待接收完一個byte? //?SPI2->DR;while(SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET);???????//等待發送區空SPI_I2S_SendData(SPI2,TxData);???????????????????????????????????????//發送一個bytewhile(SPI_I2S_GetFlagStatus(SPI2,SPI_I2S_FLAG_RXNE) == RESET);???????//等待接收完一個byteSPI_I2S_ReceiveData(SPI2);???????????????????????????????????????????//返回接收的數據 }//初始化芯片參數 void ChipParametersConfiguration(void) {uint8_ttmp;uint8_tmemsize[2][8] = {{2,2,2,2,2,2,2,2},{2,2,2,2,2,2,2,2}};//WIZCHIPSOCKET緩存區初始化if(ctlwizchip(CW_INIT_WIZCHIP,(void*)memsize) == -1){//printf("WIZCHIP Initialized fail.\r\n");while(1);}//PHY物理層連接狀態檢查do{if(ctlwizchip(CW_GET_PHYLINK, (void*)&tmp) == -1){//printf("Unknown PHY Link stauts.\r\n");}}while(tmp == PHY_LINK_OFF); }以上實現網絡物理層的配置。
//初始化WIZCHIP中的網絡參數信息 void NetworkParameterConfiguration(void) {uint8_ttmpstr[6];ctlnetwork(CN_SET_NETINFO, (void*)&gWIZNETINFO);ctlnetwork(CN_GET_NETINFO, (void*)&gWIZNETINFO);ctlwizchip(CW_GET_ID,(void*)tmpstr); }其中gWIZNETINFO是一個wiz_NetInfo類型的結構體變量,該結構體在wizchip_conf.h中定義,用于設置mac地址、IP地址等網絡參數,具體如下:
typedef struct wiz_NetInfo_t {uint8_tmac[6];?///< Source Mac Addressuint8_tip[4];??///< Source IP Addressuint8_tsn[4];??///< Subnet Maskuint8_tgw[4];??///< Gateway IP Addressuint8_tdns[4];?///< DNS server IP Addressdhcp_mode dhcp;?///< 1 -Static, 2 - DHCP }wiz_NetInfo;至此網絡部分的初始化就已完成。
2.3、具體通訊過程的實現
經過前面的配置網絡已經可以ping通了,下面可以實現具體的應用。對于我這個項目就是可是實現Modbus TCP的編寫了。
編寫TCP Server,這部分有很多資料,直接附代碼:
//TCP服務器數據通訊 int32_t TCPServer(uint8_t sn, uint16_t port) {int32_tret;uint8_tsocketStatus=getSn_SR(sn);switch(socketStatus){caseSOCK_ESTABLISHED :{if(getSn_IR(sn) & Sn_IR_CON){setSn_IR(sn,Sn_IR_CON);}uint16_t size=0;if((size = getSn_RX_RSR(sn)) > 0){if(size > DATA_BUFFER_SIZE){size = DATA_BUFFER_SIZE;}uint8_t rxBuffer[DATA_BUFFER_SIZE];ret = recv(sn,rxBuffer,size);if(ret <= 0){return ret;}//添加數據解析及響應的函數uint8_t txBuffer[DATA_BUFFER_SIZE];uint16_t length=ReceivedDataParsing(rxBuffer,txBuffer);uint16_t sentsize=0;while(length != sentsize){ret = send(sn,txBuffer+sentsize,length-sentsize);if(ret < 0){close(sn);return ret;}sentsize += ret; //不用管SOCKERR_BUSY,因為它是零.}}break;}caseSOCK_CLOSE_WAIT :if((ret=disconnect(sn)) != SOCK_OK){return ret;}break;caseSOCK_INIT :if((ret = listen(sn)) != SOCK_OK){return ret;}break;caseSOCK_CLOSED:if((ret=socket(sn,Sn_MR_TCP,port,0x00)) != sn){return ret;}break;default:break;}return 1;//基于STM32和W5500的Modbus TCP通訊 }其中ReceivedDataParsing(rxBuffer,txBuffer)實現具體的Modbus協議,根據具體的需求而定。
通過Modscan連接測試,結果正確。
歡迎關注:
?實例下載:ModbusExample.zip-其它文檔類資源-CSDN下載
實例說明:Modbus協議棧綜合實例設計_木南創智-CSDN博客
同時發到Github,其地址為:https://github.com/foxclever/Modbus
同時也發布到碼云,其地址為:https://gitee.com/ErichMoonan/Modbus
在開源的協議棧中example文件夾下即是本次發布的例子。
總結
以上是生活随笔為你收集整理的基于STM32和W5500的Modbus TCP通讯的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 改进初学者的PID-修改整定参数
- 下一篇: linux gentoo安装,Gento