在STM32上移植FreeModbus RTU的一点经验总结

 这几天因为工作需要,移植了modbus RTU到STM32来,之前也听说过modbus,但是没有深入了解过,还以为会像usb 那样复杂的,经过这几天的折腾,发现真的太简单了。为了防止过段时间又忘记了怎么移植,在这里把移植过程记录下来,也为了方便初次接触modbus的人。
    废话少说,首先去下载源码,我下载的是 freemodbus-v1.5.0 ,解压后如图所示:
 
在demo文件夹中有很多移植好的例子,但是没有STM32的,不要紧,我们参考已有的例子来操作就可以了。在demo文件夹下新建一个文件夹,命名为STM32,将BARE文件夹里的文件全部复制过来。
    然后,我们建立一个STM32的工程,我用的是mdk4.72,关于怎么建立工程我就不啰嗦了,在工程里添加modbus 和 port两个文件夹,并在文件夹里添加需要的文件,这些modbus的是在modbus文件夹下面,port的是在刚才新建的那个stm32文件夹下,port.c是我从别的地方弄过来的,里面就是一个开中断和一个关中断的函数,可以不要,我的工程如图:
   
乍一看,也有十几个文件,其实这些文件内容不多,很好理解,而且需要修改的只有port文件夹下的portserial.c 和 porttimer.c 。这两个文件里面有几个空函数,我们看名字就知道这些函数的作用了。
portserial.c 如下:
[C]  纯文本查看  复制代码
?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#include \"port.h\"
 
/* ----------------------- Modbus includes ----------------------------------*/
#include \"mb.h\"
#include \"mbport.h\"
 
/* ----------------------- static functions ---------------------------------*/
static void prvvUARTTxReadyISR( void );
static void prvvUARTRxISR( void );
 
/* ----------------------- Start implementation -----------------------------*/
void
vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable )
{
     /* If xRXEnable enable serial receive interrupts. If xTxENable enable
      * transmitter empty interrupts.
      */
}
 
BOOL
xMBPortSerialInit( UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity )
{
     return FALSE;
}
 
BOOL
xMBPortSerialPutByte( CHAR ucByte )
{
     /* Put a byte in the UARTs transmit buffer. This function is called
      * by the protocol stack if pxMBFrameCBTransmitterEmpty( ) has been
      * called. */
     return TRUE;
}
 
BOOL
xMBPortSerialGetByte( CHAR * pucByte )
{
     /* Return the byte in the UARTs receive buffer. This function is called
      * by the protocol stack after pxMBFrameCBByteReceived( ) has been called.
      */
     return TRUE;
}
 
/* Create an interrupt handler for the transmit buffer empty interrupt
  * (or an equivalent) for your target processor. This function should then
  * call pxMBFrameCBTransmitterEmpty( ) which tells the protocol stack that
  * a new character can be sent. The protocol stack will then call
  * xMBPortSerialPutByte( ) to send the character.
  */
static void prvvUARTTxReadyISR( void )
{
     pxMBFrameCBTransmitterEmpty(  );
}
 
/* Create an interrupt handler for the receive interrupt for your target
  * processor. This function should then call pxMBFrameCBByteReceived( ). The
  * protocol stack will then call xMBPortSerialGetByte( ) to retrieve the
  * character.
  */
static void prvvUARTRxISR( void )
{
     pxMBFrameCBByteReceived(  );
}
    根据注释,可以知道vMBPortSerialEnable是串口发送和接收中断的控制的,包括发送中断和接收中断,在这里,我们用的是RXNE 和 TXE中断,代码如下:
[C]  纯文本查看  复制代码
?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
void vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable )
{
     if (TRUE==xRxEnable)
     {
         USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
     }
     else
     {
         USART_ITConfig(USART1, USART_IT_RXNE, DISABLE);
     }
 
     if (TRUE==xTxEnable)
     {
         USART_ITConfig(USART1, USART_IT_TXE, ENABLE);
     }
     else
     {
        USART_ITConfig(USART1, USART_IT_TXE, DISABLE);
     }
}
    而 xMBPortSerialInit 函数显然是串口初始化的了,因为我在usart.c已经有一个串口初始化函数,这里直接调用该初始化函数usart_init(ulBaudRate);同时将return FALSE 改成 return TRUE; 注意这里我们只用了波特率这个参数,其他参数直接忽略,你也可以根据自己需要改一下。
然后 xMBPortSerialPutByte 和 xMBPortSerialGetByte 分别是发送和接收一个字节数据的函数,这里我直接调用库函数;
 
[C]  纯文本查看  复制代码
?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
BOOL
xMBPortSerialPutByte( CHAR ucByte )
{
     USART_SendData(USART1, ucByte);
   while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET) /*????·????ê??*/
   {
   
   }    
     return TRUE;
}
 
BOOL
xMBPortSerialGetByte( CHAR * pucByte )
{
     *pucByte = USART_ReceiveData(USART1);
     return TRUE;
}
    最后还有两个中断处理函数,把前面的static 去掉,因为我不想把我的串口中断函数放到这个文件。然后我们在stm32f10x_it.c添加串口中断函数,如下:
 
[C]  纯文本查看  复制代码
?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
void USART1_IRQHandler( void )
{
     if (USART_GetITStatus(USART1, USART_IT_RXNE) == SET)
     {      
         prvvUARTRxISR();
         USART_ClearITPendingBit(USART1, USART_IT_RXNE);
     }
 
     if (USART_GetITStatus(USART1, USART_IT_TXE) == SET)
     {
         prvvUARTTxReadyISR();
//      USART_ClearITPendingBit(USART1, USART_IT_TXE);
     }
}
     至此,portserial.c处理完毕。
     porttimer.c的移植和portserial.c十分相似,但是要特别注意定时器中断的时间长度应该是3.5个字符时间,我这里只是简单粗暴的按照波特率是9600时候计算的。文件很短,直接上代码
[C]  纯文本查看  复制代码
?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
BOOL
xMBPortTimersInit( USHORT usTim1Timerout50us )
{
     TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
 
     RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
   
   TIM_DeInit(TIM2);
 
#if 0  
     TIM_TimeBaseStructure.TIM_Period = 0x7E54;        //CLK==24MHz ((1000000000/9600)*11*3.5)/(1000/24) == 0x7e54
     TIM_TimeBaseStructure.TIM_Prescaler = 0x3;
#endif
   // ?????????¤·?????????7200/72M = 0.0001,????100us????????1
   //10us x 50 = 5ms,??5ms????????  
   TIM_TimeBaseStructure.TIM_Period = 50;
   TIM_TimeBaseStructure.TIM_Prescaler = (7200 - 1);
     TIM_TimeBaseStructure.TIM_ClockDivision = 0x0;
     TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
     TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
//  TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
     TIM_Cmd(TIM2, ENABLE);
     return TRUE;
}
 
 
void
vMBPortTimersEnable(  )
{  
     TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
     TIM_SetCounter(TIM2, 0);
     //TIM_Cmd(TIM2, ENABLE);
     TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
}
 
void
vMBPortTimersDisable(  )
{
     TIM_SetCounter(TIM2, 0);
     //TIM_Cmd(TIM2, DISABLE);
     TIM_ITConfig(TIM2, TIM_IT_Update, DISABLE);
}
 
void
TIMERExpiredISR( void )
{
     ( void )pxMBPortCBTimerExpired();
 
}
     同样,在stm32f10x_it.c添加定时器中断处理函数,
[C]  纯文本查看  复制代码
?
1
2
3
4
5
void TIM2_IRQHandler( void )
{
     TIMERExpiredISR();
     TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
}

     然后,我们还需要自己写四个回调函数,分别是读输入寄存器函数、读写保持寄存器函数、读写线圈函数和读离散寄存器函数,一般只用读写保持寄存器函数即可,具体怎么实现可以参考demo文件夹里面众多的demo.c文件。
     
   附近是我的代码,屡试不爽,大家可以参考一下



  • 20
    点赞
  • 63
    收藏
    觉得还不错? 一键收藏
  • 12
    评论
STM32F103是一款非常常用的微控制器,它具有丰富的外设接口和功能强大的处理能力。要实现在STM32F103上同时运行Modbus RTU和Modbus TCP,我们可以移植FreeModbus协议栈来实现。 首先,我们需要了解Modbus RTU和Modbus TCP的基本原理和通信协议。Modbus RTU是一种串口通信协议,而Modbus TCP是基于以太网的通信协议。它们之间的通信方式和数据格式略有不同。 在移植FreeModbus时,我们可以使用STM32F103上的通用串口外设来实现Modbus RTU的通信。我们需要编写串口中断处理函数来接收和发送Modbus RTU帧。同时,我们还需要实现Modbus TCP的通信功能。这可以通过STM32F103上的以太网接口来完成。我们需要编写TCP/IP协议栈的相关代码来处理Modbus TCP的通信。 在移植FreeModbus时,我们需要进行以下步骤: 1. 配置STM32F103的串口和以太网外设。我们需要设置串口的波特率、数据位、停止位等参数,以及配置以太网接口的IP地址和端口号。 2. 编写串口接收中断处理函数。当接收到串口数据时,我们需要解析Modbus RTU帧,提取出功能码和数据内容。 3. 编写串口发送函数。根据Modbus协议,我们需要根据功能码和数据内容生成Modbus RTU帧,并发送到串口。 4. 编写TCP/IP协议栈。我们需要编写用于处理Modbus TCP通信的TCP/IP协议栈代码。这包括解析TCP报文、提取出Modbus TCP报文、根据功能码和数据内容生成响应报文等。 5. 整合以上功能。将串口接收中断处理函数、串口发送函数和TCP/IP协议栈整合到一个主循环中,以实现同时运行Modbus RTU和Modbus TCP。 通过以上步骤,我们可以在STM32F103上实现同时运行Modbus RTU和Modbus TCP的功能。这样,我们就可以通过串口和以太网来实现与其他设备的通信。同时,我们还可以根据实际需求,对FreeModbus进行修改和优化,以满足项目的具体要求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值