FreeModbus 移植于STM32 實現Modbus RTU通信
Modbus中文協議.PDF
本文引用地址:http://www.104case.com/article/201612/325168.htmSTM32移植FreeModbus的步驟:
首先去http://www.freemodbus.org下載文件 一定要是官方可靠的才行,我起先為了圖方便網上隨便下載了一個,結果白白浪費了一下午的時間
不知道是哪里被改動了。目前最新的版本是1.5。
http://115.com/file/bee0jrth#freemodbus-v1.5.0.zip這是官方的可靠版本。
Demo 文件夾下都是官方移植好的其他芯片的版本。選BARE文件下的“赤裸”文件加入工程 同時添加全部的庫文件,可參考下圖
需要移植修改的在 port 目錄下
porttimer.c
中 xMBPortTimersInit( USHORT usTim1Timerout50us ) 負責配置一個 時基 ,vMBPortTimersEnable( ) 啟用這個時基。
比如執行
xMBPortTimersInit( 10000 );
vMBPortTimersEnable( );
for( ;; );
定時器按中斷內 便會每500MS調用一次pxMBPortCBTimerExpired( );同時你也要檢測vMBPortTimersDisable( ) 是否可以可靠的關閉定時器。用仿真器 用LED燈都行的.
portother.c
//負責一個串口的配置 為了省事我只支持了波特率的修改
xMBPortSerialInit( UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity )
vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable )負責控制串口【收/發】中斷的禁止與使能
pxMBFrameCBByteReceived( ); //在串口接收中斷內調用 用于通訊偵測
pxMBFrameCBTransmitterEmpty( ); //在串口發送中斷內調用 用于告知完成了發送 發送緩沖為空
xMBPortSerialGetByte( CHAR * pucByte ) xMBPortSerialPutByte( CHAR ucByte ) 兩個為 串口字節的收發
port.h
中定義了 全局中斷的開關
#define ENTER_CRITICAL_SECTION( ) __set_PRIMASK(1) /*關中中斷*/
#define EXIT_CRITICAL_SECTION( ) __set_PRIMASK(0) /*開總中斷*/
__set_PRIMASK() 來源于 core_cm3.c
這個頭文件中添加了#include assert() 斷言宏 freeModbus的作者有點意思,為此不可以定義NDEBUG 。#include "stm32f10x.h" 似乎要添加到#include 的后邊 不然編譯會有問題。
port.C
添加了些Modbus協議棧與寄存器的接口函數 這個也要自己寫。
FreeModbus 通過 eMBRegInputCB eMBRegHoldingCB eMBRegCoilsCBeMBRegDiscreteCB 四個接口函數完成數據的讀寫操作
其中最常用的是這個 eMBRegHoldingCB 為了方便測試可以構造usRegHoldingBuf[]這樣的一個數組進行讀寫調試。
上位機可以用諸如Modbus調試精靈這樣的軟件。

// 寄存器的讀寫函數 支持的命令為讀 0x03 和寫0x06
eMBErrorCodeeMBRegHoldingCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs, eMBRegisterMode eMode ){ eMBErrorCode eStatus = MB_ENOERR; int iRegIndex;u16 *PRT=(u16*)pucRegBuffer;
if( ( usAddress >= REG_HOLDING_START ) && ( usAddress + usNRegs <= REG_HOLDING_START + REG_HOLDING_NREGS ) ) { iRegIndex = ( int )( usAddress - usRegHoldingStart ); switch ( eMode ) { case MB_REG_READ: while( usNRegs > 0 ) { *PRT++ = __REV16(usRegHoldingBuf[iRegIndex++]); //數據序轉 REV16.W
// *pucRegBuffer++ = ( unsigned char )( usRegHoldingBuf[iRegIndex] >> 8 );// *pucRegBuffer++ = ( unsigned char )( usRegHoldingBuf[iRegIndex] & 0xFF );// iRegIndex++; usNRegs--; } break;
case MB_REG_WRITE: while( usNRegs > 0 ) { usRegHoldingBuf[iRegIndex++] = __REV16(*PRT++); //數據序轉 REV16.W
// usRegHoldingBuf[iRegIndex] = *pucRegBuffer++ << 8;// usRegHoldingBuf[iRegIndex] |= *pucRegBuffer++;// iRegIndex++; usNRegs--; } } } else { eStatus = MB_ENOREG; } return eStatus;}
受到freeModbus作者使用“assert()”的影響在這個里我用了__REV16()這個函數
*PRT++ = __REV16(usRegHoldingBuf[iRegIndex++]);
這是Cortex—M3中的一個匯編指令REV16 功能是交換一個字的高位和地位位的兩個字節,若0x1234==__REV16(0x3412)。字節在*pucRegBuffer中的順序與串口發送的順序是一致的所以要有這么個轉換,當然用代碼中注釋掉的部分也能實現同樣的功能。這是用__REV16()看起來更“酷”一些。當然這樣編譯后的結果是大約減少4條指令,效率提升有限。


評論