FreeMobus移植在FreeRTOS上的移植(从机)

1、FreeModbus的源码下载地址,Modbus协议的文档

下载地址:https://sourceforge.net/projects/freemodbus.berlios/

FreeMdbus的V1.6版本源码和调试工具,以及中文版的Modbus协议文档在下面百度云连接里。

链接:https://pan.baidu.com/s/1W6iQsXbmZb8QY3khkLGwFQ
提取码:h217

 FreeModbus是对Modbus协议封装,所以要想使用它需要先熟悉Modbus协议。Modbus协议本身也不是特别难,所以有时间的话可以自己先实现这个协议,然后再移植FreeModbus。

2、FreeModbus的移植

(1)源码文件结构

下载下来的源码的目录结构如下图所示,重要的代码部分在modbus文件夹下。移植的时候我们需要modbus文件夹下所有文件以及demo文件夹下的对应芯片的demo文件。

我们这里使用的是STM32芯片,所以使用demo文件夹下的BARE里面的工程文件。modbus文件夹里面是FreeModbus的主要源码,而demo文件夹下就是开发者根据不同的平台写的相关示例,我们根据自己的使用情况选择对应的示例完成移植。

FreeMobus移植在FreeRTOS上的移植(从机)

FreeMobus移植在FreeRTOS上的移植(从机)

(2)创建工程,开始移植。项目工程使用STM32CubeMX生成模板工程

这里有很多博客都写过了,我移植的时候参考的几个博客放到这里,自己就不在重复的写一遍了。

STM32 HAL库移植FreeModbus详细步骤:https://blog.csdn.net/qq153471503/article/details/104840279

STM32 移植FreeModbus详细过程:http://www.mcublog.cn/software/2020_03/stm32-freemodbus-yizhi/

利用STM32CubeMx创建好工程后,最后项目的目录结构如下所示:

FreeMobus移植在FreeRTOS上的移植(从机)

 

(2)创建好工程后,我们开始移植,完善底层代码,实现应用层代码,完成移植。

一、串口部分

①首先是串口部分,这是实现modbus的基础,一般实现的485功能都是通过串口实现的,如果您有其他方式,请根据实际情况修改。

串口部分在FreeModbus中是在 portserial.c文件中。主要有下面几个函数需要我们实现。下面是源码中关于串口需要我们完善的几个函数。

 

FreeMobus移植在FreeRTOS上的移植(从机)portserial.c的源码

 

 ② xMBPortSerialInit( UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity )函数

FreeMobus移植在FreeRTOS上的移植(从机)
 1 /*ucPORT: 可以用来选择是哪一个串口(这里默认串口1,没有使用这个参数)
 2   ulBaudRate:波特率 
 3   ucDataBits:数据长度(没有用到)
 4   eParity:校验模式
 5 */
 6 BOOL
 7 xMBPortSerialInit( UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity )
 8 {
 9     huart1.Instance = USART1;
10     huart1.Init.BaudRate = ulBaudRate;
11     huart1.Init.StopBits = UART_STOPBITS_1;
12     huart1.Init.Mode = UART_MODE_TX_RX;
13     huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
14     huart1.Init.OverSampling = UART_OVERSAMPLING_16;
15 
16     switch(eParity)
17     {
18         // 奇校验
19         case MB_PAR_ODD:
20             huart1.Init.Parity = UART_PARITY_ODD;
21             huart1.Init.WordLength = UART_WORDLENGTH_9B;            // 带奇偶校验数据位为9bits
22             break;
23 
24         // 偶校验
25         case MB_PAR_EVEN:
26             huart1.Init.Parity = UART_PARITY_EVEN;
27             huart1.Init.WordLength = UART_WORDLENGTH_9B;            // 带奇偶校验数据位为9bits
28             break;
29 
30         // 无校验
31         default:
32             huart1.Init.Parity = UART_PARITY_NONE;
33             huart1.Init.WordLength = UART_WORDLENGTH_8B;            // 无奇偶校验数据位为8bits
34             break;
35     }
36      return HAL_UART_Init(&huart1) == HAL_OK ? TRUE : FALSE;
37 }
xMBPortSerialInit完善代码

 注意:如果使用的是HAL库初始化串口的话,最后会调用HAL_UART_MspInit(huart);函数去初始化芯片的硬件,这个时候如果我们生成时使用的串口和初始化的时候不一致,我们需要在HAL_UART_MspInit()函数中加判断从而确保我们的串口是初始化成功的。此外HAL库的串口初始化后我们需要调用一下  HAL_UART_Receive_IT( ); 函数,原因就是因为HAL库中并初始化时并没有使能串口中断我们需要用这个接收函数间接的使能串口的中断。使用下面这个函数是一样的  __HAL_UART_ENABLE_IT(huart, UART_IT_RXNE);

③vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable )函数

FreeMobus移植在FreeRTOS上的移植(从机)
 1 /* ----------------------- Start implementation -----------------------------*/
 2 void
 3 vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable )
 4 {
 5     /* If xRXEnable enable serial receive interrupts. If xTxENable enable
 6      * transmitter empty interrupts.
 7      */
 8     if(xRxEnable == TRUE)
 9     {
10               MONITOR_485_DE_DISENABLE;                   //使能485接收
11         __HAL_UART_ENABLE_IT(&huart1, UART_IT_RXNE);
12     }
13     else
14     {
15         __HAL_UART_DISABLE_IT(&huart1, UART_IT_RXNE);
16     }
17 
18     if(xTxEnable == TRUE)
19     {
20               MONITOR_485_DE_ENABLE;                    //485发送
21         __HAL_UART_ENABLE_IT(&huart1, UART_IT_TXE);
22     }
23     else
24     {
25         __HAL_UART_DISABLE_IT(&huart1, UART_IT_TXE); 
26         }
27 }
vMBPortSerialEnable()串口使能函数完善

这里 MONITOR_485_DE_DISENABLE; 是个宏定义 #define MONITOR_485_DE_DISENABLE HAL_GPIO_WritePin(MONITOR_485_DE_GPIO_Port, MONITOR_485_DE_Pin, GPIO_PIN_RESET)

需要根据自己的硬件环境的不同进行定义。

④xMBPortSerialPutByte( CHAR ucByte ) , xMBPortSerialGetByte( CHAR * pucByte ),串口中断函数  void USART1_IRQHandler(void)

FreeMobus移植在FreeRTOS上的移植(从机)
 1 BOOL
 2 xMBPortSerialPutByte( CHAR ucByte )
 3 {
 4     /* Put a byte in the UARTs transmit buffer. This function is called
 5      * by the protocol stack if pxMBFrameCBTransmitterEmpty( ) has been
 6      * called. */
 7       USART1->DR = ucByte;
 8     return TRUE;
 9 }
10 
11 BOOL
12 xMBPortSerialGetByte( CHAR * pucByte )
13 {
14     /* Return the byte in the UARTs receive buffer. This function is called
15      * by the protocol stack after pxMBFrameCBByteReceived( ) has been called.
16      */
17      *pucByte = (USART1->DR & (uint16_t)0x00FF);
18     return TRUE;
19 }
20 void USART1_IRQHandler(void)
21 {
22     if(__HAL_UART_GET_FLAG(&huart1, UART_FLAG_RXNE))            // 接收非空中断标记被置位
23     {
24         __HAL_UART_CLEAR_FLAG(&huart1, UART_FLAG_RXNE);        // 清除中断标记
25         prvvUARTRxISR();                                                      // 通知modbus有数据到达
26     }
27 
28     if(__HAL_UART_GET_FLAG(&huart1, UART_FLAG_TXE))                // 发送为空中断标记被置位
29     {
30         __HAL_UART_CLEAR_FLAG(&huart1, UART_FLAG_TXE);      // 清除中断标记
31         prvvUARTTxReadyISR();                                                 // 通知modbus数据可以发松
32     }
33 }
串口接收,发送处理,中断函数

接收和发送都是通过寄存器实现的,中断函数可以不放到这里,可以放到HAL库的放中断函数的位置,这样我们需要将 prvvUARTRxISR静态函数再封装一层,在其他.C文件内调用我们封装好的函数即可。向下面这样。

void freeModbusSlavePrvvUARTRxISR(void )
{
        prvvUARTRxISR();
}

void USART1_IRQHandler(void)
{
     if(__HAL_UART_GET_FLAG(&huart1, UART_FLAG_RXNE)) // 接收非空中断标记被置位
    {
            __HAL_UART_CLEAR_FLAG(&huart1, UART_FLAG_RXNE); // 清除中断标记
            freeModbusSlavePrvvUARTRxISR(); // 替换成我们再次封装好的函数即可
    }
}

二、定时器部分

①在FreeModbus中定时器的作用是为了精准实现modbus中3.5字符的超时时间。这里我们可以使用任何一个定时器,源码中的时间基数是50us,然后在根据波特率算出溢出数,我们在移植的时候需要看一下自己的定时器的时钟频率一次实现50us的定时。这里不是很精准也没关系,我自己试的是差别不是很大也不影响数据的接收。在源码中主要是porttimer.c文件中。下面是需要完善的源码:

FreeMobus移植在FreeRTOS上的移植(从机)定时器部分需要完善的源码
 1 /* ----------------------- static functions ---------------------------------*/
 2 static void prvvTIMERExpiredISR( void );
 3 
 4 /* ----------------------- Start implementation -----------------------------*/
 5 BOOL
 6 xMBPortTimersInit( USHORT usTim1Timerout50us )
 7 {
 8    //定时器初始化
 9     return FALSE;
10 }
11 
12 
13 inline void
14 vMBPortTimersEnable(  )
15 {
16 //使能定时器
17     /* Enable the timer with the timeout passed to xMBPortTimersInit( ) */
18 }
19 
20 inline void
21 vMBPortTimersDisable(  )
22 {
23 //取消使能
24     /* Disable any pending timers. */
25 }
26 
27 /* Create an ISR which is called whenever the timer has expired. This function
28  * must then call pxMBPortCBTimerExpired( ) to notify the protocol stack that
29  * the timer has expired.
30  */
31 static void prvvTIMERExpiredISR( void )
32 {
33    //定时器中断调用的回调函数
34     ( void )pxMBPortCBTimerExpired(  );
35 }

②xMBPortTimersInit( USHORT usTim1Timerout50us )函数,这是定时器的初始化函数

FreeMobus移植在FreeRTOS上的移植(从机)
 1 BOOL
 2 xMBPortTimersInit( USHORT usTim1Timerout50us )
 3 {
 4     BOOL TimersInitState;
 5     TimersInitState = MX_TIM2_Init(usTim1Timerout50us);
 6     return TimersInitState;
 7 }
 8 /* TIM2 init function */
 9 uint8_t MX_TIM2_Init(uint16_t timeCount)
10 {
11   /* USER CODE BEGIN TIM2_Init 0 */
12   /* USER CODE END TIM2_Init 0 */
13   TIM_ClockConfigTypeDef sClockSourceConfig = {0};
14   TIM_MasterConfigTypeDef sMasterConfig = {0};
15   /* USER CODE BEGIN TIM2_Init 1 */
16   /* USER CODE END TIM2_Init 1 */
17   htim2.Instance = TIM2;
18   htim2.Init.Prescaler = 5999;
19   htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
20   htim2.Init.Period = timeCount-1;
21   htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
22   htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
23   if (HAL_TIM_Base_Init(&htim2) != HAL_OK)
24   {
25     return 0;
26   }
27   sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
28   if (HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig) != HAL_OK)
29   {
30      return 0;
31   }
32   sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
33   sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
34   if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK)
35   {
36       return 0;
37   }
38   /* USER CODE BEGIN TIM2_Init 2 */
39   //清理TIM开启时的中断标识
40     __HAL_TIM_CLEAR_IT(&htim2, TIM_IT_UPDATE);
41     //使能TIM中断
42     HAL_TIM_Base_Start_IT(&htim2);
43   /* USER CODE END TIM2_Init 2 */
44   return 1;
45 }
定时器初始化

这里我使用的是STM32CubeMx生成的定时器初始化,稍微修改了一下,得到了一个返回值。

③定时器使能,失能,中断函数

FreeMobus移植在FreeRTOS上的移植(从机)定时器使能,失能,中断函数源码完善后

这里比较和串口的一样,要是不想把定时器的中断函数放到这里,封装一下static void prvvTIMERExpiredISR( void )这个函数即可,我不确定prvvTIMERExpiredISR函数去掉静态修饰是否可行,所以又封装了一次。

三、串口、定时器初始化函数的调用

这两个的部分的初始化在哪一个地方调用的呢,他们是在eMBRTUInit()函数中被调用,当然这是RTU模式下的。

FreeMobus移植在FreeRTOS上的移植(从机)
 1 /* ----------------------- Start implementation -----------------------------*/
 2 eMBErrorCode
 3 eMBRTUInit( UCHAR ucSlaveAddress, UCHAR ucPort, ULONG ulBaudRate, eMBParity eParity )
 4 {
 5     eMBErrorCode    eStatus = MB_ENOERR;
 6     ULONG           usTimerT35_50us;
 7 
 8     ( void )ucSlaveAddress;
 9     ENTER_CRITICAL_SECTION(  );
10 
11     /* Modbus RTU uses 8 Databits. */
12     if( xMBPortSerialInit( ucPort, ulBaudRate, 8, eParity ) != TRUE )
13     {
14         eStatus = MB_EPORTERR;
15     }
16     else
17     {
18         /* If baudrate > 19200 then we should use the fixed timer values
19          * t35 = 1750us. Otherwise t35 must be 3.5 times the character time.
20          */
21         if( ulBaudRate > 19200 )
22         {
23             usTimerT35_50us = 35;       /* 1800us. */
24         }
25         else
26         {
27             /* The timer reload value for a character is given by:
28              *
29              * ChTimeValue = Ticks_per_1s / ( Baudrate / 11 )
30              *             = 11 * Ticks_per_1s / Baudrate
31              *             = 220000 / Baudrate
32              * The reload for t3.5 is 1.5 times this value and similary
33              * for t3.5.
34              */
35             usTimerT35_50us = ( 7UL * 220000UL ) / ( 2UL * ulBaudRate );
36         }
37         if( xMBPortTimersInit( ( USHORT ) usTimerT35_50us ) != TRUE )
38         {
39             eStatus = MB_EPORTERR;
40         }
41     }
42     EXIT_CRITICAL_SECTION(  );
43 
44     return eStatus;
45 }
eMBRTUInit

从这个源码中可以看出它是根据波特率和晶振频率确定的定时器的溢出值。这是调用我们刚才完善串口和定时器初始化部分的代码,然后这不是我们最后使用函数,我们最后使用的是下面这个函数

FreeMobus移植在FreeRTOS上的移植(从机)
 1 /* ----------------------- Start implementation -----------------------------*/
 2 eMBErrorCode
 3 eMBInit( eMBMode eMode, UCHAR ucSlaveAddress, UCHAR ucPort, ULONG ulBaudRate, eMBParity eParity )
 4 {
 5     eMBErrorCode    eStatus = MB_ENOERR;
 6 
 7     /* check preconditions */
 8     if( ( ucSlaveAddress == MB_ADDRESS_BROADCAST ) ||
 9         ( ucSlaveAddress < MB_ADDRESS_MIN ) || ( ucSlaveAddress > MB_ADDRESS_MAX ) )
10     {
11         eStatus = MB_EINVAL;
12     }
13     else
14     {
15         ucMBAddress = ucSlaveAddress;
16         switch ( eMode )
17         {
18 #if MB_RTU_ENABLED > 0
19         case MB_RTU:
20             pvMBFrameStartCur = eMBRTUStart;
21             pvMBFrameStopCur = eMBRTUStop;
22             peMBFrameSendCur = eMBRTUSend;
23             peMBFrameReceiveCur = eMBRTUReceive;
24             pvMBFrameCloseCur = MB_PORT_HAS_CLOSE ? vMBPortClose : NULL;
25             pxMBFrameCBByteReceived = xMBRTUReceiveFSM;
26             pxMBFrameCBTransmitterEmpty = xMBRTUTransmitFSM;
27             pxMBPortCBTimerExpired = xMBRTUTimerT35Expired;
28             eStatus = eMBRTUInit( ucMBAddress, ucPort, ulBaudRate, eParity );
29             break;
30 #endif
31 #if MB_ASCII_ENABLED > 0
32         case MB_ASCII:
33             pvMBFrameStartCur = eMBASCIIStart;
34             pvMBFrameStopCur = eMBASCIIStop;
35             peMBFrameSendCur = eMBASCIISend;
36             peMBFrameReceiveCur = eMBASCIIReceive;
37             pvMBFrameCloseCur = MB_PORT_HAS_CLOSE ? vMBPortClose : NULL;
38             pxMBFrameCBByteReceived = xMBASCIIReceiveFSM;
39             pxMBFrameCBTransmitterEmpty = xMBASCIITransmitFSM;
40             pxMBPortCBTimerExpired = xMBASCIITimerT1SExpired;
41 
42             eStatus = eMBASCIIInit( ucMBAddress, ucPort, ulBaudRate, eParity );
43             break;
44 #endif
45         default:
46             eStatus = MB_EINVAL;
47         }
48 
49         if( eStatus == MB_ENOERR )
50         {
51             if( !xMBPortEventInit(  ) )
52             {
53                 /* port dependent event module initalization failed. */
54                 eStatus = MB_EPORTERR;
55             }
56             else
57             {
58                 eMBCurrentMode = eMode;
59                 eMBState = STATE_DISABLED;
60             }
61         }
62     }
63     return eStatus;
64 }
eMBInit()

 eMBInit(MB_RTU, 0x01, 0, 115200, MB_PAR_NONE); // 初始化modbus为RTU方式,波特率115200
 eMBEnable(); // 使能modbus协议栈

最后我们在主函数前像这样调用eMBInit()函数,就可以了实现FreeModbus函数的初始化了。

(3)应用层代码

①初始化和使能 FreeModbus从机的函数

1 eMBInit(MB_RTU, 0x01, 0, 115200, MB_PAR_NONE);        // 初始化modbus为RTU方式,波特率115200
2 eMBEnable();                                    // 使能modbus协议栈

这两个函数的功能就是初始化和使能FreeModbus,上述所有初始化都是由这个函数调用的,在初始化部分里面有对源码中使用的函数进行重定向,所以如果使用了实时操作系统,最好在调度器开启前使用这两个函数,否则可能会导致系统出现错误。下面的代码是对源码中的一些函数进行的重定向。

 1 #if MB_RTU_ENABLED > 0
 2         case MB_RTU:
 3             pvMBFrameStartCur = eMBRTUStart;
 4             pvMBFrameStopCur = eMBRTUStop;
 5             peMBFrameSendCur = eMBRTUSend;
 6             peMBFrameReceiveCur = eMBRTUReceive;
 7             pvMBFrameCloseCur = MB_PORT_HAS_CLOSE ? vMBPortClose : NULL;
 8             pxMBFrameCBByteReceived = xMBRTUReceiveFSM;
 9             pxMBFrameCBTransmitterEmpty = xMBRTUTransmitFSM;
10             pxMBPortCBTimerExpired = xMBRTUTimerT35Expired;
11             eStatus = eMBRTUInit( ucMBAddress, ucPort, ulBaudRate, eParity );
12             break;
13 #endif

②应用代码

下面是使用FreeModbus实现的从机的回应和填充发送的内容,根据命令的不同分为了四个部分。

FreeMobus移植在FreeRTOS上的移植(从机)
  1 #include "mb.h"
  2 #include "mbport.h"
  3 
  4 
  5 // 十路输入寄存器
  6 #define REG_INPUT_SIZE  10
  7 uint16_t REG_INPUT_BUF[REG_INPUT_SIZE];
  8 
  9 
 10 // 十路保持寄存器
 11 #define REG_HOLD_SIZE   10
 12 uint16_t REG_HOLD_BUF[REG_HOLD_SIZE];
 13 
 14 
 15 // 十路线圈
 16 #define REG_COILS_SIZE 10
 17 uint8_t REG_COILS_BUF[REG_COILS_SIZE];
 18 
 19 
 20 // 十路离散量
 21 #define REG_DISC_SIZE  10
 22 uint8_t REG_DISC_BUF[10];
 23 
 24 /// CMD4
 25 eMBErrorCode
 26 eMBRegInputCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs )
 27 {
 28     USHORT usRegIndex = usAddress - 1;
 29 
 30     // 非法检测
 31     if((usRegIndex + usNRegs) > REG_INPUT_SIZE)
 32     {
 33         return MB_ENOREG;
 34     }
 35 
 36     // 循环读取
 37     while( usNRegs > 0 )
 38     {
 39         *pucRegBuffer++ = ( unsigned char )( REG_INPUT_BUF[usRegIndex] >> 8 );
 40         *pucRegBuffer++ = ( unsigned char )( REG_INPUT_BUF[usRegIndex] & 0xFF );
 41         usRegIndex++;
 42         usNRegs--;
 43     }
 44 
 45     // 模拟输入寄存器被改变
 46     for(usRegIndex = 0; usRegIndex < REG_INPUT_SIZE; usRegIndex++)
 47     {
 48         REG_INPUT_BUF[usRegIndex]++;
 49     }
 50 
 51     return MB_ENOERR;
 52 }
 53 
 54 /// CMD6、3、16
 55 eMBErrorCode
 56 eMBRegHoldingCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs, eMBRegisterMode eMode )
 57 {
 58     USHORT usRegIndex = usAddress - 1;
 59 
 60     // 非法检测
 61     if((usRegIndex + usNRegs) > REG_HOLD_SIZE)
 62     {
 63         return MB_ENOREG;
 64     }
 65 
 66     // 写寄存器
 67     if(eMode == MB_REG_WRITE)
 68     {
 69         while( usNRegs > 0 )
 70         {
 71             REG_HOLD_BUF[usRegIndex] = (pucRegBuffer[0] << 8) | pucRegBuffer[1];
 72             pucRegBuffer += 2;
 73             usRegIndex++;
 74             usNRegs--;
 75         }
 76     }
 77 
 78     // 读寄存器
 79     else
 80     {
 81         while( usNRegs > 0 )
 82         {
 83             *pucRegBuffer++ = ( unsigned char )( REG_HOLD_BUF[usRegIndex] >> 8 );
 84             *pucRegBuffer++ = ( unsigned char )( REG_HOLD_BUF[usRegIndex] & 0xFF );
 85             usRegIndex++;
 86             usNRegs--;
 87         }
 88     }
 89 
 90     return MB_ENOERR;
 91 }
 92 
 93 /// CMD1、5、15
 94 eMBErrorCode
 95 eMBRegCoilsCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNCoils, eMBRegisterMode eMode )
 96 {
 97     USHORT usRegIndex   = usAddress - 1;
 98     USHORT usCoilGroups = ((usNCoils - 1) / 8 + 1);
 99     UCHAR  ucStatus     = 0;
100     UCHAR  ucBits       = 0;
101     UCHAR  ucDisp       = 0;
102 
103     // 非法检测
104     if((usRegIndex + usNCoils) > REG_COILS_SIZE)
105     {
106         return MB_ENOREG;
107     }
108 
109     // 写线圈
110     if(eMode == MB_REG_WRITE)
111     {
112         while(usCoilGroups--)
113         {
114             ucStatus = *pucRegBuffer++;
115             ucBits   = 8;
116 
117             while((usNCoils--) != 0 && (ucBits--) != 0)
118             {
119                 REG_COILS_BUF[usRegIndex++] = ucStatus & 0X01;
120                 ucStatus >>= 1;
121             }
122         }
123     }
124 
125     // 读线圈
126     else
127     {
128         while(usCoilGroups--)
129         {
130             ucDisp = 0;
131             ucBits = 8;
132 
133             while((usNCoils--) != 0 && (ucBits--) != 0)
134             {
135                 ucStatus |= (REG_COILS_BUF[usRegIndex++] << (ucDisp++));
136             }
137 
138             *pucRegBuffer++ = ucStatus;
139         }
140     }
141 
142     return MB_ENOERR;
143 }
144 
145 
146 /// CMD4
147 eMBErrorCode
148 eMBRegDiscreteCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNDiscrete )
149 {
150     USHORT usRegIndex   = usAddress - 1;
151     USHORT usCoilGroups = ((usNDiscrete - 1) / 8 + 1);
152     UCHAR  ucStatus     = 0;
153     UCHAR  ucBits       = 0;
154     UCHAR  ucDisp       = 0;
155 
156     // 非法检测
157     if((usRegIndex + usNDiscrete) > REG_DISC_SIZE)
158     {
159         return MB_ENOREG;
160     }
161 
162     // 读离散输入
163     while(usCoilGroups--)
164     {
165         ucDisp = 0;
166         ucBits = 8;
167 
168         while((usNDiscrete--) != 0 && (ucBits--) != 0)
169         {
170             if(REG_DISC_BUF[usRegIndex])
171             {
172                 ucStatus |= (1 << ucDisp);
173             }
174 
175             ucDisp++;
176         }
177 
178         *pucRegBuffer++ = ucStatus;
179     }
180 
181     // 模拟改变
182     for(usRegIndex = 0; usRegIndex < REG_DISC_SIZE; usRegIndex++)
183     {
184         REG_DISC_BUF[usRegIndex] = !REG_DISC_BUF[usRegIndex];
185     }
186 
187     return MB_ENOERR;
188 }
应用代码

 然后我们在一个任务函数中轮训调用 eMBPoll(); 函数从而改变状态完成整个协议。协议使用的是状态机实现的协议实现的过程,状态机的过程是可以使用系统的队列实现的,这样系统实时性会更加好,下篇讲解源码的实现过程和使用队列替代状态机。

1 void Monitor485Task(void *argument)
2 {
3   /* USER CODE BEGIN StartDefaultTask */    
4   for(;;)
5   {        
6          eMBPoll();    // 轮巡查询
7   }
8   /* USER CODE END StartDefaultTask */
9 }

 

上一篇:基于HSI和局部同态滤波的彩色图像增强算法


下一篇:基于HSI和局部同态滤波的彩色图像增强算法