这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » 高校专区 » 坤创E-Geek/天科大新电社 » 【原创】手把手教你移植FreeModbus到STM32(二)

共14条 1/2 1 2 跳转至

【原创】手把手教你移植FreeModbus到STM32(二)

高工
2020-04-21 12:01:20     打赏

------------------紧接上一帖----------------

3.01移植freemodbusstm32平台

移植之前需要准备:STM32基础工程(标准库、HAL库均可);FreeModbus Library V1.6;IDE(这里选择常用的MDK);如果需要添加操作系统的,可将STM32的基础工程改为带有操作系统的基础工程,比如常用的FreeRTOS、RT-Thread等。

01.复制正点原子战舰V3库函基础工程,在此工程上添加FreeModbus,先在工程中新建一个Modbus文件,并将freemodbus v1.6下的modbus文件夹下的文件全部复制过来,再将Demo文件夹下的BARE文件夹复制过来,Modbus文件内的具体内容如下:

图片5.png

02.使用MDK打开基础工程,向工程中添加Modbus分组,将Modbus文件夹下对应的文件都添加到Modbus分组中,主要包括:ascii文件夹的mbascii.c文件;function文件夹下的所有.c文件;rtu和tcp文件夹下的.c文件;mb.c文件。添加后的效果如下:

图片6.png

03.添加对应的头文件,工程需要添加以下文件夹:ascii文件夹、BARE/port文件夹、include文件夹、rtu文件夹、tcp文件夹;总之有.h的文件夹统统包含,具体如下:

图片7.png

04.需要对添加过来部分文件进行补充,以完成Modbus对串口和定时器的需要,第一个需要补充的是portserial.c文件,补充Modbus串口发送中断和接收中断使能函数、Modbus串口初始化函数xMBPortSerialInit,具体代码如下:(这里只实现了232功能,没有加485,其实两个代码几乎一致

//该函数实现STM32串口发送中断和接收中断使能
void vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable )
{
//STM32串口 接收中断使能
if(xRxEnable==TRUE) 
{ 
 //使能接收和接收中断
     USART_ITConfig(MODBUS_USART, USART_IT_RXNE, ENABLE);
  } 
else if(xRxEnable == FALSE)
{ 
     //禁止接收和接收中断  
     USART_ITConfig(MODBUS_USART, USART_IT_RXNE, DISABLE);
  }
  //STM32串口 发送中断使能
   if(xTxEnable==TRUE) 
{
     //使能发送完成中断
     USART_ITConfig(MODBUS_USART, USART_IT_TXE, ENABLE);
  } 
else if(xTxEnable == FALSE) 
{
     //禁止发送完成中断
     USART_ITConfig(MODBUS_USART, USART_IT_TXE, DISABLE);
  }
} else if(xTxEnable == FALSE) 
{
     MODBUS_RECIEVE();
     USART_ITConfig(MODBUS_USART, USART_IT_TC, DISABLE);
  }
}
/*******************************************************************/
//对串口进行初始化由eMBRTUInt函数进行调用
BOOL xMBPortSerialInit( UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity )
{
 GPIO_InitTypeDef  GPIO_InitStructure;
 USART_InitTypeDef USART_InitStructure;
 NVIC_InitTypeDef  NVIC_InitStructure;
(void)ucPORT; //不修改串口
   (void)ucDataBits; //不修改数据长度
   (void)eParity; //不许改效验格式
   /***引脚初始化*************************************/
 
   //时钟使能
 RCC_APB2PeriphClockCmd(MODBUS_USART_GPIO_CLK,ENABLE);
 RCC_APB1PeriphClockCmd(MODBUS_USART_CLK,ENABLE);
   //TX
   GPIO_InitStructure.GPIO_Pin = MODBUS_USART_TX_PIN;
 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
   GPIO_Init(MODBUS_USART_TX_PORT, &GPIO_InitStructure);
   //RX
   GPIO_InitStructure.GPIO_Pin = MODBUS_USART_RX_PIN;
 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
   GPIO_Init(MODBUS_USART_RX_PORT, &GPIO_InitStructure);
 
  /***************串口初始化********************/
   USART_InitStructure.USART_BaudRate    = ulBaudRate;
   USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
   USART_InitStructure.USART_Mode        = USART_Mode_Rx|USART_Mode_Tx;
   USART_InitStructure.USART_Parity      = USART_Parity_No;
   USART_InitStructure.USART_StopBits    = USART_StopBits_1;
   USART_InitStructure.USART_WordLength  = USART_WordLength_8b;
USART_Init(MODBUS_USART, &USART_InitStructure);
   USART_Cmd(MODBUS_USART, ENABLE);
   
   /*****************************中断初始化*************************************/
 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
 NVIC_InitStructure.NVIC_IRQChannel = MODBUS_USART_IRQ ;
 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0 ;//抢占优先级0
 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;  //子优先级0
 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;   //IRQ通道使能
 NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器
 
 return TRUE;
}
/*******************************************************************/


05.补充串口发送函数和接收函数、中断处理函数,将STM32串口发送函数和接收函数进行封装,供协议栈使用。代码如下:

//串口发送
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. */
  USART_SendData(MODBUS_USART, ucByte);  //??????
  while (USART_GetFlagStatus(MODBUS_USART, USART_FLAG_TC) == RESET){}; 
    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.
     */
 *pucByte = USART_ReceiveData(MODBUS_USART);  
    return TRUE;
}
/*******************************************************************/
//串口中断处理函数
void MODBUS_USART_IRQHandler(void)
{
   if(USART_GetITStatus(MODBUS_USART, USART_IT_TXE) == SET) 
 {
     prvvUARTTxReadyISR();
     USART_ClearITPendingBit(MODBUS_USART, USART_IT_TXE);
   }
 
   if(USART_GetITStatus(MODBUS_USART, USART_IT_RXNE) == SET) 
 {
     prvvUARTRxISR();
     USART_ClearITPendingBit(MODBUS_USART, USART_IT_RXNE);
   }
}


06.第二个需要补充的是porttimerl.c文件,需要补充的就是Modbus定时器初始化函数、Modbus定时器使能和失能函数,以及Modbus 定时器中断函数。这个对于熟悉stm32编程的就是分分中的事。具体代码如下:

//Modbus定时器初始化函数
BOOL xMBPortTimersInit( USHORT usTim1Timerout50us )
{
  TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
NVIC_InitTypeDef NVIC_InitStructure;
   uint16_t PrescalerValue = 0;
//使能定时器4的时钟
RCC_APB1PeriphClockCmd(MODBUS_TIM_CLK, ENABLE); //时钟使能
//定时器4时间配置说明
//HCLK为72MHz,APB1经2分频为36MHz
  //TIM4时钟倍频后为72MHz(硬件自动倍频,达到最大)
  //TIM4的分频系数为3599,时间基频率为:72 / (1 + Prescaler) = 20KHz,基准为50us
  //TIM最大计数值为:usTim1Timerout50u
PrescalerValue = (uint16_t) (SystemCoreClock / 20000) - 1; 
//定时器TIM4初始化
TIM_TimeBaseStructure.TIM_Period = usTim1Timerout50us; 
TIM_TimeBaseStructure.TIM_Prescaler =PrescalerValue;
  TIM_TimeBaseStructure.TIM_ClockDivision = 0; 
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  
TIM_TimeBaseInit(MODBUS_TIM, &TIM_TimeBaseStructure); 
//使能预装载
  TIM_ARRPreloadConfig(MODBUS_TIM, ENABLE);
 
//中断优先级NVIC设置
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
NVIC_InitStructure.NVIC_IRQChannel = MODBUS_TIM_IRQ ;  
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;  
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;  
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; 
NVIC_Init(&NVIC_InitStructure); 
//清除溢出中断标志位
TIM_ClearITPendingBit(MODBUS_TIM, TIM_IT_Update);
//定时器溢出中断关闭
  TIM_ITConfig(MODBUS_TIM,TIM_IT_Update,DISABLE);
//失能定时器
TIM_Cmd(MODBUS_TIM, DISABLE);  
return TRUE;
}
/*******************************************************************/
//Modbus定时器使能函数
void vMBPortTimersEnable()
{
    /* Enable the timer with the timeout passed to xMBPortTimersInit( ) */
 TIM_ClearITPendingBit(MODBUS_TIM, TIM_IT_Update);
   TIM_ITConfig(MODBUS_TIM, TIM_IT_Update, ENABLE);
 //设置定时器的初值
 TIM_SetCounter(MODBUS_TIM,0x0000);
 TIM_Cmd(MODBUS_TIM, ENABLE);
}
/*******************************************************************/
//Modbus定时器失能函数
void vMBPortTimersDisable()
{
  /* Disable any pending timers. */
TIM_ClearITPendingBit(MODBUS_TIM, TIM_IT_Update);
  TIM_ITConfig(MODBUS_TIM, TIM_IT_Update, DISABLE);
  TIM_SetCounter(MODBUS_TIM,0x0000); 
  //关闭定时器
  TIM_Cmd(MODBUS_TIM, DISABLE);
}
/*******************************************************************/
//Modbus 定时器中断函数
void MODBUS_TIM_IRQHandler( void )
{
  if(TIM_GetITStatus(MODBUS_TIM, TIM_IT_Update) == SET) 
{
     TIM_ClearITPendingBit(MODBUS_TIM, TIM_IT_Update);
     prvvTIMERExpiredISR();
   }
}


07.第三个需要修改的文件是port.h文件,这里文件我放置了一些需要定义的宏,方便后面修改,万一哪一天Modbus、串口2、定时器4打起来了,那我有的忙了,一个伟大程序员总是在考虑谁会打起来,不希望出现劝架的那一天。port.h文件中主要补充进入临界区和退出临界区的宏定义、portserial.c 文件中用到的宏、porttimer.c 文件中用到的宏。(注:是补充哦!不是全部的port.h文件内容,不要傻傻的去全部替换!

#define ENTER_CRITICAL_SECTION( )    __set_PRIMASK(1); //关闭中断
#define EXIT_CRITICAL_SECTION( )     __set_PRIMASK(0); //开启中断
//Modbus串口 
#define  MODBUS_USART            USART2
#define  MODBUS_USART_CLK        RCC_APB1Periph_USART2
#define  MODBUS_USART_GPIO_CLK   RCC_APB2Periph_GPIOA
#define  MODBUS_USART_TX_PORT    GPIOA
#define  MODBUS_USART_RX_PORT    GPIOA
#define  MODBUS_USART_TX_PIN     GPIO_Pin_2
#define  MODBUS_USART_RX_PIN     GPIO_Pin_3
#define  MODBUS_USART_IRQ           USART2_IRQn
#define  MODBUS_USART_IRQHandler    USART2_IRQHandler
//Modbus定时器
#define  MODBUS_TIM              TIM4      
#define  MODBUS_TIM_CLK          RCC_APB1Periph_TIM4
#define  MODBUS_TIM_IRQ          TIM4_IRQn
#define  MODBUS_TIM_IRQHandler   TIM4_IRQHandler


08.编译之后,果然不出所料,有错误。学会修改错误,是一个程序猿的基本修养。如果连这点基本修养都没有,那只能说,路漫漫其修远兮,还得继续上下左右之求索。在这里我们尝试着去删除porttimer.c 中定时器使能和失能函数前的inline 字样,咦,奇怪的东西消失了呢。

图片8.png

09.当然困难不止一个,就像当前的新冠病毒,一波未平一波又起。再次编译,出现关于assert的错误...........,不要挠头皮,继续想法搞定吧!于是打开搜索引擎,开始找对应的解决办法的过程。

图片9.png

10. 搜索并思考了很久,偶然在CSDN上看到一位博主是这么解决的:在主函数下面添加以下代码(代码在下方),即可解决以上问题,硬着头皮先试一试(因为暂时不相信也没有其它办法),复制过来,直接编译,居然轻松解决,至于原因嘛,就留给各位读者自己思索咯。

#ifdef  USE_FULL_ASSERT
/** 
  * @brief  Reports the name of the source file and the source line number 
  *         where the assert_param error has occurred. 
  * @param  file: pointer to the source file name 
  * @param  line: assert_param error line source number 
  * @retval None 
  */  
void assert_failed(uint8_t* file, uint32_t line)  
{  
  /* User can add his own implementation to report the file name and line number, 
     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */  
   
  /* Infinite loop */  
  while (1)  
  {  
  }  
}
#else
void __aeabi_assert(const char * x1, const char * x2, int x3)
{
}
#endif


好了各位小伙伴,到这里FreeModbus已经移植到STM32平台上了,下一步就是测试验证移植的FreeModbus是否可以正常通讯。(验证方式和全部代码下载,请见下一帖)

----未完待续----       等你来哦





关键词: FreeModbus     移植     STM32     Modbu    

管理员
2020-04-21 15:50:31     打赏
2楼

祝老师,代码可以用下面这个文本框试试

按钮在工具栏的【代码语言】

比如,截取一部分

void vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable )
{
//STM32串口 接收中断使能
if(xRxEnable==TRUE)
{
 //使能接收和接收中断
     USART_ITConfig(MODBUS_USART, USART_IT_RXNE, ENABLE);
  }
else if(xRxEnable == FALSE)
{
     //禁止接收和接收中断  
     USART_ITConfig(MODBUS_USART, USART_IT_RXNE, DISABLE);
  }
......

院士
2020-04-22 10:22:44     打赏
3楼

我觉得 祝老师这个代码框挺好看的


专家
2020-04-26 21:52:50     打赏
4楼

文字写的轻松又有内涵,赞



菜鸟
2020-10-23 21:10:27     打赏
5楼

这里第一个代码区。大括号多了一个。

  1. //该函数实现STM32串口发送中断和接收中断使能  

  2. void vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable )  

  3. {  

  4. //STM32串口 接收中断使能  

  5. if(xRxEnable==TRUE)   

  6. {   

  7.  //使能接收和接收中断  

  8.      USART_ITConfig(MODBUS_USART, USART_IT_RXNE, ENABLE);  

  9.   }   

  10. else if(xRxEnable == FALSE)  

  11. {   

  12.      //禁止接收和接收中断    

  13.      USART_ITConfig(MODBUS_USART, USART_IT_RXNE, DISABLE);  

  14.   }  

  15.   //STM32串口 发送中断使能  

  16.    if(xTxEnable==TRUE)   

  17. {  

  18.      //使能发送完成中断  

  19.      USART_ITConfig(MODBUS_USART, USART_IT_TXE, ENABLE);  

  20.   }   

  21. else if(xTxEnable == FALSE)   

  22. {  

  23.      //禁止发送完成中断  

  24.      USART_ITConfig(MODBUS_USART, USART_IT_TXE, DISABLE);  

  25.   }  

  26. else if(xTxEnable == FALSE)   

  27. {  

  28.      MODBUS_RECIEVE();  

  29.      USART_ITConfig(MODBUS_USART, USART_IT_TC, DISABLE);  

  30.   }  

  31. }  



菜鸟
2021-01-23 15:22:56     打赏
6楼

真不错!


菜鸟
2022-01-20 21:44:13     打赏
7楼

哈喽!文章很不错,能发一下这个工程么,谢谢了


专家
2022-01-20 21:46:43     打赏
8楼

看看  


菜鸟
2022-05-17 07:55:37     打赏
9楼

请教老师:

MODBUS_RECIEVE()

这个函数的实现在哪里?


助工
2022-05-17 08:28:55     打赏
10楼

谢谢分享


共14条 1/2 1 2 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]