这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » 高校专区 » 坤创E-Geek/天科大新电社 » 2队--基于STM32F429与FreeRTOS的ModBus协议移植

共5条 1/1 1 跳转至

2队--基于STM32F429与FreeRTOS的ModBus协议移植

菜鸟
2019-07-14 22:56:17     打赏

关于ModBus协议的基础知识就不在详述了,只需要大致的知道它是基于RS485协议的一个软件层协议就行,基本上现在都采用RTU传输模式。网上类似的资料也是多的数不胜数,不过大多都是基于标准库的,也有少部分基于HAL库的,但是找不到基于操作系统的。为了实用性考虑,便决定打算移植一个基于FreeRTOS的代码,当前ST公司主推HAL库,对于STM32F4以及F7系列的而言,标准库已经不再更新了,想了想,还是要适应时代的潮流,便也采用了HAL库。简单的分享一下移植过程中的经验。

一、操作系统定时器

首先对于FreeRTOS而言,SYS从时基来源SYSTICK选择其他计时器,这里选择TIM6计时器,用来进行配置Timebase Source以防止与操作系统的软件定时器产生冲突。

二、FreeModbus端口 

  A. <port.h>修改 

1、向EXIT_CRITICAL_SECTION添加中断启用/禁用代码。

 2包含“stm32f4xx_hal.h”头文件。 
  为关键部分添加中断启用/禁用功能。

#ifndef _PORT_H
#define _PORT_H
 
#include <assert.h>
#include "stm32f4xx_hal.h"
 
#define    INLINE                      inline
#define PR_BEGIN_EXTERN_C           extern "C" {
#define    PR_END_EXTERN_C             }
 
#define ENTER_CRITICAL_SECTION() ( __disable_irq())      
#define EXIT_CRITICAL_SECTION() ( __enable_irq())    
 
typedef uint8_t BOOL;
 
typedef unsigned char UCHAR;
typedef char CHAR;
 
typedef uint16_t USHORT;
typedef int16_t SHORT;
 
typedef uint32_t ULONG;
typedef int32_t LONG;
 
#ifndef TRUE
#define TRUE            1
#endif
 
#ifndef FALSE
#define FALSE           0
#endif
 
#endif

B.<porttimer.c>修改

     Modbus RTU   根据规范进行帧分类 ,所以需要设置 3.5 字符空白时间操作的计时器,这里选择TIM7为计时器,定时器用于测量3.5个字符的时间,分隔帧。“xMBPortTimersInit”   在函数中初始化计时器 ,定时器频率需要配置为20kHz

xMBPortTimersInit( USHORT usTim1Timerout50us )
{
  TIM_MasterConfigTypeDef sMasterConfig;
  
  htim7.Instance = TIM7;
  htim7.Init.Prescaler = (HAL_RCC_GetPCLK1Freq() / 1000000) - 1;
  htim7.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim7.Init.Period = 50 - 1;
  
  timeout = usTim1Timerout50us;
  
  if (HAL_TIM_Base_Init(&htim7) != HAL_OK)
  {
    return FALSE;
  }
  
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE;
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  if (HAL_TIMEx_MasterConfigSynchronization(&htim7, &sMasterConfig) != HAL_OK)
  {
    return FALSE;
  }
  
  return TRUE;

“vMBPortTimersEnable”用于启用计时器。   
 “vMBPortTimersDisable”用于停止计时器。

void vMBPortTimersEnable(  )
{
  downcounter = timeout;
  HAL_TIM_Base_Start_IT(&htim7);
} 
void vMBPortTimersDisable(  )
{
  HAL_TIM_Base_Stop_IT(&htim7);
}

需要注意的是“prvvTIMERExpiredISR”,这个是定时器中断函数,但是在移植过程中,发现其与FreeRTOS产生了冲突,所以不使用它,从TIM7中断处理程序调用“pxMBPortCBTimerExpired”。

/* 
static void prvvTIMERExpiredISR( void )
{
( void )pxMBPortCBTimerExpired(  );
}
*/

C. <stm32f4xx_it.c>修改

这是RTU端口最重要的部分。需要修改中断处理程序的两个部分。

首先,需要使“pxMBFrameCBByteReceived”“pxMBFrameCBTransmitterEmpty”在接收数据时可以被“USART2_IRQHandler”中断调用。如果无法及时发送或接收数据,TIM7定时器中断处理程序将调用“pxMBPortCBTimerExpired”。

 void USART2_IRQHandler(void)
{
  /* USER CODE BEGIN USART2_IRQn 0 */
    uint32_t tmp_flag = __HAL_UART_GET_FLAG(&huart2, UART_FLAG_RXNE);
  uint32_t tmp_it_source = __HAL_UART_GET_IT_SOURCE(&huart2, UART_IT_RXNE);
  
  if((tmp_flag != RESET) && (tmp_it_source != RESET)) {
    pxMBFrameCBByteReceived();
    __HAL_UART_CLEAR_PEFLAG(&huart2);    
    return;
  }
  
  if((__HAL_UART_GET_FLAG(&huart2, UART_FLAG_TXE) != RESET) &&(__HAL_UART_GET_IT_SOURCE(&huart2, UART_IT_TXE) != RESET)) {
    pxMBFrameCBTransmitterEmpty();    
    return ;
  }
 
  /* USER CODE END USART2_IRQn 0 */
  HAL_UART_IRQHandler(&huart2);
  /* USER CODE BEGIN USART2_IRQn 1 */
 
  /* USER CODE END USART2_IRQn 1 */
}

三、测试

   主要代码则在<mbtask.c>中,功能是处理对具有来自地址1000的8个数据的输入寄存器的请求。每100 ms从输入寄存器读取8个值进行测试。输入寄存器地址从1000开始。

image.png

四、总结

移植整个工程真的是花费了相当多的时间。在慢慢摸索的过程中,为了提高整个工程的效率,将“mbconfig.h”中库配置为仅使用于ModBus RTU

/*! \brief If Modbus ASCII support is enabled. */
#define MB_ASCII_ENABLED                        (  0 )
 
/*! \brief If Modbus RTU support is enabled. */
#define MB_RTU_ENABLED                          (  1 )
 
/*! \brief If Modbus TCP support is enabled. */
#define MB_TCP_ENABLED                          (  0 )

而最令人头疼的是,FreeRTOS与ModBus的冲突问题。且由于HAL库更偏向于上层的原因,在使用USART的时候,写入需要中断,发信号通知传输缓冲器为空并且接收缓冲器不为空。但是HAL库并不支持这样的回调,所以只得重新从底层采用寄存器控制。

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 = (uint8_t)(huart2.Instance->DR & (uint8_t)0x00FF);  
  return TRUE;
}

不过最终还是完成了整个工程,感觉收获也是相当多。现将代码分享出来,大家可以参考参考,共同进步学习。

STM32_HAL_FREEMODBUS_RTU.rar




高工
2019-07-15 08:42:17     打赏
2楼

很详细 不错!


高工
2019-07-15 09:43:57     打赏
3楼

很不错,很前卫。但是仍需跟踪使用过程中的不确定因素


高工
2019-07-18 23:36:56     打赏
4楼

宝贝,真棒


菜鸟
2024-03-09 08:26:48     打赏
5楼

学习了,谢谢,也遇到了同样的问题


共5条 1/1 1 跳转至

回复

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