平台:正点原子STM32F407探索者开发板 + FreeModbus V1.6 + RT-Thread
源码链接:https://www.embedded-solutions.at/en/freemodbus-downloads/
源码列表如图所示,需要的文件包括modbus文件夹和demo文件夹内的port相关文件,port文件位置如下图
在BASE文件夹内有一个port文件夹(内含需要的port相关文件)和一个demo文件,demo文件写好了FreeModbus的启动使用。
将这些文件载入到工程中,并包含相关的头文件
由于第一次移植,没敢乱动,直接全部移植进来只开启了需要的功能。mb开头的文件为协议栈程序,暂时不必改动;port开头文件是需要用户进行相关配置的文件。其中 portevent.c 不用动,portserial.c 和 porttimer.c 内部实现串口功能和定时器功能,包括初始化配置以及启动和关闭。
第一步:配置portserial.c
源码文件中portserial.c包含下面几个函数,大体功能如注释所述。
1 void
2 vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable )//可以根据输入参数配置RS485收发器的收发模式
3 {
4 /* If xRXEnable enable serial receive interrupts. If xTxENable enable
5 * transmitter empty interrupts.
6 */
7 }
8
9 BOOL
10 xMBPortSerialInit( UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity )
11 {
//初始化串口 --> 查找串口设备,根据参数配置串口,打开串口设备,设置串口接收回调函数等
12 return FALSE;//添加代码后需要修改为 TRUE
13 }
14
15 BOOL
16 xMBPortSerialPutByte( CHAR ucByte )
17 {
18 /* Put a byte in the UARTs transmit buffer. This function is called
19 * by the protocol stack if pxMBFrameCBTransmitterEmpty( ) has been
20 * called. */
//发送一个字节数据,只需要调用发送接口即可
21 return TRUE;
22 }
23
24 BOOL
25 xMBPortSerialGetByte( CHAR * pucByte )
26 {
27 /* Return the byte in the UARTs receive buffer. This function is called
28 * by the protocol stack after pxMBFrameCBByteReceived( ) has been called.
29 */
//接收一个字节数据
30 return TRUE;
31 }
32
33 /* Create an interrupt handler for the transmit buffer empty interrupt
34 * (or an equivalent) for your target processor. This function should then
35 * call pxMBFrameCBTransmitterEmpty( ) which tells the protocol stack that
36 * a new character can be sent. The protocol stack will then call
37 * xMBPortSerialPutByte( ) to send the character.
38 */
39 static void prvvUARTTxReadyISR( void )
40 {
//协议栈的发送中断函数,可以在串口发送中断里面调用它
41 pxMBFrameCBTransmitterEmpty( );
42 }
43
44 /* Create an interrupt handler for the receive interrupt for your target
45 * processor. This function should then call pxMBFrameCBByteReceived( ). The
46 * protocol stack will then call xMBPortSerialGetByte( ) to retrieve the
47 * character.
48 */
49 static void prvvUARTRxISR( void )
50 {
//协议栈的接收中断函数,可以在串口接收中断里面调用它;放在回调函数即可
51 pxMBFrameCBByteReceived( );
52 }
第二步:配置porttimer.c
源码文件与串口的形式类似,具体如下
1 BOOL
2 xMBPortTimersInit( USHORT usTim1Timerout50us )//定时器设备初始化,查找设备,打开设备,配置设备,根据参数设置超时时间
3 {
4 return FALSE;//添加代码后需要改为 TRUE
5 }
6
7
8 inline void
9 vMBPortTimersEnable( )//此处理解为定时器的启动,调用该接口即开始定时
10 {
11 /* Enable the timer with the timeout passed to xMBPortTimersInit( ) */
12 }
13
14 inline void
15 vMBPortTimersDisable( )//定时器的关闭,我个人使用的单次定时,所以就没做处理。如果是循环的需要关闭定时器
16 {
17 /* Disable any pending timers. */
18 }
19
20 static void prvvTIMERExpiredISR( void )//超时函数,可以放到定时器超时回调函数中调用
21 {
22 ( void )pxMBPortCBTimerExpired( );
23 }
第三步:配置demo.c文件
该文件下有有一个main函数,其它函数为不同功能的回调函数。官方下载的源码中main函数和eMBRegInputCB已经实现,可以参考使用。
1 int
2 main( void )
3 {
4 const UCHAR ucSlaveID[] = { 0xAA, 0xBB, 0xCC };
5 eMBErrorCode eStatus;
6
7 eStatus = eMBInit( MB_RTU, 0x0A, 0, 38400, MB_PAR_EVEN );
8
9 eStatus = eMBSetSlaveID( 0x34, TRUE, ucSlaveID, 3 );
10 sei( );
11
12 /* Enable the Modbus Protocol Stack. */
13 eStatus = eMBEnable( );
14
15 for( ;; )
16 {
17 ( void )eMBPoll( );
18
19 /* Here we simply count the number of poll cycles. */
20 usRegInputBuf[0]++;
21 }
22 }
main函数内容实现的是协议栈的初始化、启动和循环更新,重要的三个函数为eMBInit、eMBEnable、eMBPoll。官方源码中作为一个功能循环使用,在RT-Thread中我把其作为一个独立任务,在任务函数里进行协议栈初始化和启动,状态更新作为线程循环。
1 eMBErrorCode
2 eMBRegInputCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs )
3 {
4 eMBErrorCode eStatus = MB_ENOERR;
5 int iRegIndex;
6
7 if( ( usAddress >= REG_INPUT_START )
8 && ( usAddress + usNRegs <= REG_INPUT_START + REG_INPUT_NREGS ) )
9 {
10 iRegIndex = ( int )( usAddress - usRegInputStart );
11 while( usNRegs > 0 )
12 {
13 *pucRegBuffer++ =
14 ( unsigned char )( usRegInputBuf[iRegIndex] >> 8 );
15 *pucRegBuffer++ =
16 ( unsigned char )( usRegInputBuf[iRegIndex] & 0xFF );
17 iRegIndex++;
18 usNRegs--;
19 }
20 }
21 else
22 {
23 eStatus = MB_ENOREG;
24 }
25
26 return eStatus;
27 }
28
29 eMBErrorCode
30 eMBRegHoldingCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs,
31 eMBRegisterMode eMode )
32 {
33 return MB_ENOREG;
34 }
35
36
37 eMBErrorCode
38 eMBRegCoilsCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNCoils,
39 eMBRegisterMode eMode )
40 {
41 return MB_ENOREG;
42 }
43
44 eMBErrorCode
45 eMBRegDiscreteCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNDiscrete )
46 {
47 return MB_ENOREG;
48 }
这些函数是协议栈对应功能的回调函数,并且给了一个示例,它们由function文件下的函数调用,只需在需要的功能里作出对应实现即可。这些函数的返回值是特定的形式,参考示例函数进行操作即可,至于相应地址数据的处理根据实际需求。
最后:
新手上路,还有很多地方理解不到位,暂时记录这么多,继续学习!