STM32 + FREERTOS + HAL库 + MODBUSRTU

本文介绍了在使用ModbusRTU协议时如何在FREERTOS环境下处理串口数据的断帧问题,包括通过定时器检测完整帧,以及在HAL库的串口中断回调函数和软件定时器回调中的操作细节。
摘要由CSDN通过智能技术生成

一、程序思路整理

  • 串口数据怎样断帧

使用ModbusRTU最大的难点在于串口接收时,怎样判断一个命令的起始和截止点,也就是怎样断帧。modbus-RTU对于介质管理规定了2个重要的时间参数,以实现成帧、冲突管理等,如下图所示。
在这里插入图片描述
这个图可以用于断帧,也就时判断是否接收到一个完整的帧,因此只需要使用一个定时器在每次收到一个字节后,就重启一个3.5字节定时器,如果这个3.5字节定时器中断了,就证明收到了一个Modbus报文,至于这个报文是不是正确的报文,可以在进一步根据帧格式进行校验。

另外还规定了报文需要连续发送,字节间隔不得超过1.5字节时间(这作为补充知识点,对于断帧没有帮助)。
在这里插入图片描述
综上,**我们可以通过检测在每个字节收到后,如果有超过3.5个字符的时间没有收到新的字节,那么就可以判定一个完整的MODBUSRTU命令已经发送。**这里又涉及到一个问题,怎样判断3.5个字符,这里又两种方案,一种是通过硬件定时器,另一种是通过FREERTOS自带的软件计时器。硬件计时器需要使用一个TIM,个人不倾向使用。但是如果使用软件计时器,涉及到一个问题,就是精度不够,FREERTOS软件计时器的精度是毫秒级别的。如果串口使用115200bits/s的波特率的话,那么3.5个字符的时间是不到1ms的。那么这里我们又要考虑,到底有没有必要使用极限的3.5个字符,也就是客户端在发送MODBUSRTU时,会不会间隔3.5个字符。实际上,我们在使用MODBUSRTU时,命令之间的间隔最少也是几十毫秒的,因此,我们完全可以通过软件计时器,计时几毫秒,比如说5ms来断帧。

  • 命令帧接收流程设计
    在FREERTOS调度开始后,开启串口接收中断,每接受到一个字符,进入中断回调函数。在中断回调函数中,复位软件计时器,并把接受到的字节取出,放入接受命令的缓存中,并记录接受到命令的字节数。
    当软件计时器触发回调函数时,说明一个完整的客户端命令已经接收完毕,这时,将完整的命令放入队列中。

  • 队列设计
    由于MODBUSRTU的命令帧的长度是不一致的,因此,需要记录下每一个命令帧的长度,也就是在串口接收函数中记录的接受到的字节数。因此,可以将队列设计为以下模式:前两个字节记录命令的总字节数,后面的是命令部分。
    因此,在串口中断回调函数中,将接受到的命令从队列缓存的第三位开始填充。在软件定时器回调函数中,将命令字节数填入到队列缓存的前两位;在入队之后,需要把字节数清零。
    以下是串口回调函数和软件定时器回调函数。

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	BaseType_t xHigherPriorityTaskWoken = pdFALSE;
	    			
	HAL_UART_Receive_IT(huart, &ModbusH.rec_single_data, 1);
	ModbusH.rec_data[ModbusH.rec_data_location++] = ModbusH.rec_single_data;
	ModbusH.rec_count++;
    			
	xTimerResetFromISR(ModbusH.xMDTimer, &xHigherPriorityTaskWoken);
	
}

void vMBTimerCallback(TimerHandle_t *pxTimer)
{
//将命令字节数填入
ModbusH.rec_data[0] = (uint8_t)(ModbusH.rec_count >> 8);
ModbusH.rec_data[1] = (uint8_t)ModbusH.rec_count;

//加入队列
xQueueSend(ModbusH.Queue_485_Handle, &ModbusH.rec_data, 0);

ModbusH.rec_data_location = 2;
ModbusH.rec_count = 0;
memset(ModbusH.rec_data, 0, sizeof(ModbusH.rec_data));	

}

二、HAL库串口中断介绍

  • HAL_UART_Receive_IT函数介绍
    该函数原型为
    HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
    该函数的作用是打开接收中断,设置接收的字节数Size和接收位置。串口在接收到函数指定的字节数Size后,会关闭接收中断并且调用HAL_UART_RxCpltCallback函数。因此如果要一直保持中断接收,那么就需要在回调函数中再次调用HAL_UART_Receive_IT函数。

  • 何时打开串口中断
    因为在串口中断的回调函数中,需要调用xTimerResetFromISR这一FREERTOS函数来复位软件定时器,所以需要保证在进入串口中断回调函数时,FERRRTOS内核已运行,也就是执行了 osKernelStart()函数。因此,一个有效的方法就是在一个任务中,创建其他任务一级初始化一些硬件,包括中断等,此时由于任务已经运行,FREERTOS内核肯定已经初始化完毕。

  • 29
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
实现STM32 FreeRTOS LwIP TCP服务器需要按照以下步骤进行操作: 1. 首先,需要配置LwIP和FreeRTOS。可以在STM32CubeMX中选择配置相应的组件,生成对应的代码和初始化函数。 2. 在代码中创建任务来处理TCP服务器。通过创建一个任务,可以将其分配给特定的核心,以处理TCP请求和响应。 3. 在任务中,首先需要进行LwIP和FreeRTOS的初始化。这样可以确保网络和操作系统的适当设置。需要调用lwip_init()和vTaskStartScheduler()函数。 4. 配置和创建TCP服务器的套接字。可以通过调用lwip_socket()函数创建一个TCP套接字,并使用lwip_bind()函数将其与特定的IP地址和端口绑定。 5. 通过调用lwip_listen()函数监听TCP套接字,等待客户端的连接。 6. 使用lwip_accept()函数接受客户端的连接请求,并获得一个新的套接字来处理与该客户端之间的通信。 7. 通过调用lwip_recv()和lwip_send()函数来接收和发送数据。可以使用这些函数接收来自客户端的数据,并发送响应数据给客户端。 8. 当与客户端的通信完成后,使用lwip_close()函数关闭套接字。 9. 循环进行步骤6-8,以处理其他客户端的连接和通信请求。 需要注意的是,STM32系列芯片的内存和处理能力有限,因此在编写代码时需要谨慎处理内存和资源的分配和释放,以确保程序的稳定性和性能。 总结:通过以上步骤,可以在STM32上使用FreeRTOS和LwIP实现TCP服务器,使其能够接受和处理客户端的连接和通信请求。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值