×
单片机 > 单片机程序设计 > 详情

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

发布时间:2020-08-26 发布时间:
|

这几天因为工作需要,移植了modbus RTU到STM32来,之前也听说过modbus,但是没有深入了解过,还以为会像usb 那样复杂的,经过这几天的折腾,发现真的太简单了。为了防止过段时间又忘记了怎么移植,在这里把移植过程记录下来,也为了方便初次接触modbus的人。

    废话少说,首先去下载源码,我下载的是freemodbus-v1.5.0,解压后如图所示:

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 ---------------------------------*/
staticvoidprvvUARTTxReadyISR( void);
staticvoidprvvUARTRxISR( void);
 
/* ----------------------- Start implementation -----------------------------*/
void
vMBPortSerialEnable(BOOLxRxEnable,BOOLxTxEnable )
{
    /* If xRXEnable enable serial receive interrupts. If xTxENable enable
     * transmitter empty interrupts.
     */
}
 
BOOL
xMBPortSerialInit(UCHARucPORT,ULONGulBaudRate,UCHARucDataBits, eMBParity eParity )
{
    returnFALSE;
}
 
BOOL
xMBPortSerialPutByte(CHARucByte )
{
    /* Put a byte in the UARTs transmit buffer. This function is called
     * by the protocol stack if pxMBFrameCBTransmitterEmpty( ) has been
     * called. */
    returnTRUE;
}
 
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.
     */
    returnTRUE;
}
 
/* 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.
 */
staticvoidprvvUARTTxReadyISR( 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.
 */
staticvoidprvvUARTRxISR( 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
voidvMBPortSerialEnable(BOOLxRxEnable,BOOLxTxEnable )
{
    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(CHARucByte )
{
    USART_SendData(USART1, ucByte);
  while(USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET)/*????·????ê??*/
  {
   
  }    
    returnTRUE;
}
 
BOOL
xMBPortSerialGetByte(CHAR* pucByte )
{
    *pucByte = USART_ReceiveData(USART1);
    returnTRUE;
}

    最后还有两个中断处理函数,把前面的static 去掉,因为我不想把我的串口中断函数放到这个文件。然后我们在stm32f10x_it.c添加串口中断函数,如下:
 

[C] 纯文本查看 复制代码

?

01
02
03
04
05
06
07
08
09
10
11
12
13
14
voidUSART1_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(USHORTusTim1Timerout50us )
{
    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);
    returnTRUE;
}
 
 
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
voidTIM2_IRQHandler(void)
{
    TIMERExpiredISR();
    TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
}


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



『本文转载自网络,版权归原作者所有,如有侵权请联系删除』

热门文章 更多
TQ210天嵌开发板S5PV210 LED闪烁程序C语言代码记录