[应用相关] STM32 FreeModbus RTU从机移植

[复制链接]
2835|19
 楼主| gaoke231 发表于 2018-8-19 23:20 | 显示全部楼层 |阅读模式
FreeModbus的具体介绍就不提了。至于为什么要移植,大概就是因为移植比较快,而且比较稳定,可以减少因为自己编写出现的漏洞。
但是FreeModbus 1.5版本是没有主机的,因此移植的时候只可以做从机。
 楼主| gaoke231 发表于 2018-8-19 23:20 | 显示全部楼层
移植过程:
1.将modbus目录下所有文件拷贝加入工程。
2.对modbus中的include下的mbconfig.h进行编辑,裁剪其中需要的模块。(此处我没有进行裁剪,因此选项都是默认)
3.将demo中的合适的port文件夹下的文件加入工程。
4.修改port文件夹下的代码,移植UART驱动。
5.使用modbus poll调试。
 楼主| gaoke231 发表于 2018-8-19 23:22 | 显示全部楼层

因为没有配置mbconfig.h,因此直接从port的移植开始说。

port.c文件如下,port.c中的两个函数作用是开关总中断并保存。

  1. #include "stm32f10x.h"

  2. void EnterCriticalSection(  )
  3. {
  4.     __set_PRIMASK(1);
  5. }

  6. void ExitCriticalSection(  )
  7. {
  8.     __set_PRIMASK(0);
  9. }


 楼主| gaoke231 发表于 2018-8-19 23:26 | 显示全部楼层
port.h只要是针对一些类型的跨平台支持。
  1. #ifndef _PORT_H
  2. #define _PORT_H

  3. #include "stm32f10x.h"
  4. #include <assert.h>
  5. #include <inttypes.h>

  6. #define INLINE
  7. #define PR_BEGIN_EXTERN_C           extern "C" {
  8. #define PR_END_EXTERN_C             }

  9. #define ENTER_CRITICAL_SECTION( )       EnterCriticalSection( )
  10. #define EXIT_CRITICAL_SECTION( )    ExitCriticalSection( )

  11. void            EnterCriticalSection( void );
  12. void            ExitCriticalSection( void );


  13. #ifndef TRUE
  14. #define TRUE            1
  15. #endif

  16. #ifndef FALSE
  17. #define FALSE           0
  18. #endif

  19. typedef u8 UCHAR;
  20. typedef u16 USHORT;
  21. typedef u8 BOOL;
  22. typedef u32 ULONG;
  23. typedef char CHAR;
  24. typedef long LONG;
  25. typedef short SHORT;
  26. typedef int INT;

  27. #endif


 楼主| gaoke231 发表于 2018-8-19 23:27 | 显示全部楼层
portevent.c没有改动,作用是起到一个简单的事件队列。
  1. #include "mb.h"                  
  2. #include "mbport.h"

  3. /* ----------------------- Variables ----------------------------------------*/
  4. static eMBEventType eQueuedEvent;
  5. static BOOL     xEventInQueue;

  6. /* ----------------------- Start implementation -----------------------------*/
  7. BOOL
  8. xMBPortEventInit( void )
  9. {
  10.     xEventInQueue = FALSE;
  11.     return TRUE;
  12. }

  13. BOOL
  14. xMBPortEventPost( eMBEventType eEvent )
  15. {
  16.     xEventInQueue = TRUE;
  17.     eQueuedEvent = eEvent;
  18.     return TRUE;
  19. }

  20. BOOL
  21. xMBPortEventGet( eMBEventType * eEvent )
  22. {
  23.     BOOL            xEventHappened = FALSE;

  24.     if( xEventInQueue )
  25.     {
  26.         *eEvent = eQueuedEvent;
  27.         xEventInQueue = FALSE;
  28.         xEventHappened = TRUE;
  29.     }
  30.     return xEventHappened;
  31. }


 楼主| gaoke231 发表于 2018-8-19 23:28 | 显示全部楼层

portserial.c

1).vMBPortSerialEnable 函数的作用是单独禁止或开启发送或接受中断。在此处有人推荐使用发送完成中断而并非发送缓存为0中断。想想是有道理的,这里还在测试阶段,因此还没有去修改。

  1. void vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable )
  2. {
  3.     if(xRxEnable)
  4.     {
  5.         USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);
  6.         DE1 = 0;
  7.     }
  8.     else
  9.     {
  10.         USART_ITConfig(USART2, USART_IT_RXNE, DISABLE);
  11.         DE1 = 1;
  12.     }   
  13.     if(xTxEnable)
  14.     {
  15.         USART_ITConfig(USART2, USART_IT_TXE, ENABLE);
  16.         prvvUARTTxReadyISR();
  17.     }
  18.     else
  19.         USART_ITConfig(USART2, USART_IT_TXE, DISABLE);
  20. }


 楼主| gaoke231 发表于 2018-8-19 23:29 | 显示全部楼层
(2).vMBPortClose 在Demo的源代码中没有编写,我也没有移植。
 楼主| gaoke231 发表于 2018-8-19 23:29 | 显示全部楼层
3).xMBPortSerialInit 是串口初始化的函数,其中只要把对应需要的串口移植添加进来就可以了。串口初始化程序很简单。设置一下数据位和校验还有波特率就可以了。此处我只针对了UART2移植,因此没有加入其他的串口初始化。
  1. BOOL xMBPortSerialInit( UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity )
  2. {
  3.     BOOL bInitialized = TRUE;
  4.     GPIO_InitTypeDef GPIO_InitStructure;
  5.     USART_InitTypeDef  USART_InitStructure;
  6.     NVIC_InitTypeDef NVIC_InitStructure;

  7.     (void)ucPORT;
  8.     (void)ucDataBits;
  9.     (void)eParity;
  10.     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB|RCC_APB2Periph_AFIO, ENABLE);//使能GPIO外设时钟
  11.     RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
  12.     GPIO_InitStructure.GPIO_Pin=GPIO_Pin_3;             //RX2
  13.     GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
  14.     GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING;     
  15.     GPIO_Init(GPIOA, &GPIO_InitStructure);
  16.     GPIO_InitStructure.GPIO_Pin=GPIO_Pin_2;           //TX2
  17.     GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;
  18.     GPIO_Init(GPIOA, &GPIO_InitStructure);

  19.     GPIO_InitStructure.GPIO_Pin=GPIO_Pin_9;           //DE
  20.     GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_OD;
  21.     GPIO_Init(GPIOB, &GPIO_InitStructure);

  22. /*************************************************************************************/

  23.     USART_InitStructure.USART_BaudRate = ulBaudRate;     

  24.     USART_InitStructure.USART_StopBits = USART_StopBits_1;   
  25. //  USART_InitStructure.USART_Parity = USART_Parity_No; //设置奇校验时,通信出现错误

  26.     switch(eParity)
  27.     {
  28.     case MB_PAR_NONE:USART_InitStructure.USART_Parity = USART_Parity_No;USART_InitStructure.USART_WordLength = USART_WordLength_8b; break;
  29.     case MB_PAR_ODD:USART_InitStructure.USART_Parity = USART_Parity_Odd;USART_InitStructure.USART_WordLength = USART_WordLength_9b; break;
  30.     case MB_PAR_EVEN:USART_InitStructure.USART_Parity = USART_Parity_Even;USART_InitStructure.USART_WordLength = USART_WordLength_9b; break;
  31.     default:break;
  32.     }


  33.     USART_InitStructure.USART_HardwareFlowControl=USART_HardwareFlowControl_None;   
  34.     USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
  35.     USART_Init(USART2, &USART_InitStructure);
  36.     USART_Cmd(USART2,ENABLE);

  37.     vMBPortSerialEnable(FALSE,FALSE);
  38.     USART_ClearFlag(USART2,USART_FLAG_TC);

  39. /*************************************************************************************/

  40.     NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
  41.     NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn; //通道设置为串口2中断
  42.     NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority =0;//抢占优先级
  43.     NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;  //中断响应优先级0
  44.     NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //打开中断
  45.     NVIC_Init(&NVIC_InitStructure);   //初始化

  46.     return bInitialized;
  47. }


 楼主| gaoke231 发表于 2018-8-19 23:30 | 显示全部楼层
4).xMBPortSerialPutByte 的作用是将字节数据送到串口。把正常用的串口发送拿过来就可以了
  1. BOOL xMBPortSerialPutByte( UCHAR ucByte )
  2. {
  3.     USART_SendData(USART2, ucByte);  
  4.     while(USART_GetFlagStatus(USART2, USART_FLAG_TC) == RESET){};

  5.     return TRUE;
  6. }


 楼主| gaoke231 发表于 2018-8-19 23:53 | 显示全部楼层
5).xMBPortSerialGetByte 的作用是从串口接收数据并且把数据传给指定内存。此处的UARTRecvBuffer是一个接受缓冲区。我这样做是为了防止在串口接收的时候数据来不及接收被冲毁,是自己因为在仿真测试的时候发现数据经常丢字节而做的一个措施,具体正确性要等待上板测试。
  1. BOOL
  2. xMBPortSerialGetByte( UCHAR * pucByte )
  3. {
  4.     *pucByte = (UCHAR)UARTRecvBuffer;
  5.     return TRUE;
  6. }






 楼主| gaoke231 发表于 2018-8-19 23:53 | 显示全部楼层
6).USART2_IRQHandler 串口2中断此处中断有两个。发送和接受。
  1. void USART2_IRQHandler(void)
  2. {
  3.     if(USART_GetITStatus(USART2,USART_IT_RXNE)!=RESET)
  4.     {
  5.         USART_ClearITPendingBit(USART2,USART_IT_RXNE);
  6.         UARTRecvBuffer = USART_ReceiveData(USART2);
  7.         prvvUARTRxISR();
  8.     }
  9.     if(USART_GetITStatus(USART2,USART_IT_TXE)!=RESET)
  10.     {
  11.         USART_ClearITPendingBit(USART2,USART_IT_TXE);
  12.         prvvUARTTxReadyISR();
  13.     }
  14. }


 楼主| gaoke231 发表于 2018-8-20 00:05 | 显示全部楼层
7).prvvUARTTxReadyISR 这个函数其实就是发送的时候会调用的函数。
  1. static void prvvUARTTxReadyISR( void )
  2. {
  3.     DE1 = 1;
  4.     delay_ms(1);
  5.     pxMBFrameCBTransmitterEmpty(  );
  6.     delay_ms(1);
  7.     DE1 = 0;
  8. }


 楼主| gaoke231 发表于 2018-8-20 00:06 | 显示全部楼层
8).prvvUARTRxISR是串口接收函数,调用pxMBFrameCBByteReceived();
  1. static void prvvUARTRxISR( void )
  2. {
  3.     pxMBFrameCBByteReceived(  );
  4. }


 楼主| gaoke231 发表于 2018-8-20 00:08 | 显示全部楼层

porttimer.c

1).xMBPortTimersInit 是用来初始化定时器的,modbus-rtu需要有一个定时器来计算每个字节之间的间隔时间,若间隔时间超过3.5T,则是认为此帧已经接收完毕。
此处的
timerBaseInit.TIM_Period = usTim1Timerout50us - 1;
timerBaseInit.TIM_Prescaler = 1799;// 50us
周期是重复次数,是在定时器中断为50us的前提下的重复次数,一般在波特率≤19200的情况下,公式为:
usTimerT35_50us = ( 7UL * 220000UL ) / ( 2UL * ulBaudRate );
在大于19200的情况下,固定为35.
TIM_Period 和TIM_Prescaler 传入的时候 都要减一才准确。此处我的时钟是TIM2,所以挂载APB1上,APB1我设置为最大的3600MHz。因此当分频为1800的时候,刚刚好是50us中断一次,所以分频设置为1799.

  1. BOOL xMBPortTimersInit( USHORT usTim1Timerout50us )
  2. {
  3.     TIM_TimeBaseInitTypeDef timerBaseInit;
  4.     NVIC_InitTypeDef NVIC_InitStructure;

  5.     TIM_DeInit(TIM2); //重新将Timer设置为缺省值
  6.     RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);// 使能Timer2外设时钟
  7.     //TIM_InternalClockConfig(TIM2); //采用内部时钟给TIM2提供时钟源

  8.     // TIM2
  9.     timerBaseInit.TIM_Period = usTim1Timerout50us - 1;
  10.     timerBaseInit.TIM_Prescaler = 1799;// 50us
  11.     timerBaseInit.TIM_CounterMode = TIM_CounterMode_Up;

  12.     timerBaseInit.TIM_ClockDivision = TIM_CKD_DIV1;
  13.     TIM_TimeBaseInit(TIM2, &timerBaseInit);

  14.     TIM_ClearITPendingBit(TIM2, TIM_IT_Update);//清标志
  15.     TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);     //中断使能
  16.     TIM_Cmd(TIM2, DISABLE);


  17.     NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
  18.     NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
  19.     NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;//抢占优先级
  20.     NVIC_InitStructure.NVIC_IRQChannelSubPriority =0;//响应优先级
  21.     NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//允许中断
  22.     NVIC_Init(&NVIC_InitStructure);
  23.     return TRUE;
  24. }


 楼主| gaoke231 发表于 2018-8-20 00:09 | 显示全部楼层
2).vMBPortTimersEnable TIM2使能函数 在此使能时钟的时候,要把时钟重置。
  1. void vMBPortTimersEnable(  )
  2. {
  3.     TIM_Cmd(TIM2, DISABLE);
  4.     TIM_Cmd(TIM2, ENABLE);
  5. }


 楼主| gaoke231 发表于 2018-8-20 00:09 | 显示全部楼层
3).vMBPortTimersDisable TIM2除能函数
  1. voidvMBPortTimersDisable(  )
  2. {
  3.     TIM_Cmd(TIM2, DISABLE);
  4. }


 楼主| gaoke231 发表于 2018-8-20 00:10 | 显示全部楼层
4).TIM2_IRQHandler 定时器中断,定时进入判断是否接收超时或完毕,至此FreeModbus从机部分移植完毕。接下来就是调试了。
  1. void TIM2_IRQHandler(void)
  2. {
  3.    u8 i;
  4.    (void)i;
  5.    if(TIM_GetITStatus(TIM2, TIM_IT_Update) == SET){
  6.       TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
  7.       ( void )pxMBPortCBTimerExpired();

  8.    }
  9. }


yediezeus 发表于 2018-8-20 09:13 | 显示全部楼层
磨砂 发表于 2018-8-20 10:29 | 显示全部楼层
非常改写楼主分享
yfgww 发表于 2020-6-26 18:50 | 显示全部楼层
您需要登录后才可以回帖 登录 | 注册

本版积分规则

54

主题

1310

帖子

5

粉丝
快速回复 在线客服 返回列表 返回顶部