赞
踩
在STM32常用串口协议实现与其它外设交互数据,发送数据简单,但是如何方便快捷的处理通过串口接收到的数据并没那么简单。在STM32中,USART发送接收有三种方式:轮询,中断,DMA。其中轮询的方式过于笨拙,一般本人不会选择这种方式,下文中将从中断方式,IDLE+DMA方式进行介绍,以及如何对循环数组中的数据提取当次接收的数据。
发送方式:
HAL_UART_Transmit(&huart1, (uint8_t*)"test\r\n", 7, 0xFFFF);
调用此函数即可输出字符串
接收方式:
HAL_UART_Receive_IT(&huart1, data_rec, 10);
使用此函数后,即可当串口收到数据后便往data_rec数组里存数据,存了10个数据以后,会自动调用回调函数
void HAL_UART_RxCpltCallback(UART_HandleTypeDef* huart)
在此函数中,即可对存入的10个数据统一进行处理。这种方式勉强能实现功能,但是谈不上方便,缺点如下:
为了解决上述痛点,引入下面这种解决方案
空闲中断(IDLE)简单来说,就是检测到接收数据后,在数据总线上的一个字节时间内,没有接收到数据触发空闲中断。RXNE置位一次,空闲总线就检测一次!另外为了减少误进入串口空闲中断,串口RX的IO管脚一定设置成Pull-up<上拉模式>。
__HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE);//使能空闲中断
DMA就不做详细介绍了,它的作用就是不断的将串口接收到的数据转移到内存中(程序中定义存数据的数组),将其配置为循环模式,则可以初始化一次,就拥有一个永久的搬运工了。当一帧数据搬完以后,空闲中断就会触发,此时代码中就可以去对数组中的数据进行处理了。
HAL_UART_Receive_DMA(&huart1, data_rec, 10);//告诉DMA传送地址及数据长度
上面介绍了IDLE和DMA相互配合的逻辑,下面介绍如何对数据进行提取。因为不同帧的数据都在同一个数组中,如何分开它们是一个问题。首先得知道DMA搬运的效果是怎么样的,利用stlink的在线仿真,能看到数据从数组的头开始存放,一直到末尾,然后多余的数据将会从头继续存放,覆盖掉原先的数据,如此往复,类似于一个循环数组。
为了定位本次帧在数组中的起始位置和终止位置,需要用到函数
__HAL_DMA_GET_COUNTER()
此函数可以获得经过此次数据传递后,该数组还剩余多少空间,比如第一次传了4个数据,那么此函数返回值即为10-4=6,第二次传了3个数据,那么此函数返回值为10-3-4=3,第三次传了3个数据,理论上是10-3-3-4=0;然而此函数返回值为10,这是需要注意的地方,空的同时也是满!
基于上述的描述,提取当次帧的函数如下:
void USER_UART_IDLECallback(UART_HandleTypeDef *huart) { static uint8_t pre_index,now_index = 0; now_index = length - __HAL_DMA_GET_COUNTER(&hdma_usart1_rx); //当前指向的位置 Ble_DataProcess(pre_index,now_index); pre_index = now_index; } void Ble_DataProcess(uint8_t pre_index,uint8_t now_index) { uint8_t i,j,temp_index = 0; if(now_index <= pre_index) { temp_index = now_index + length; } else{ temp_index = now_index; } j=0; for(i=0;i<length;i++)//暂存数组清空 { temp[j] = 0; j++; } j=0; for(i=pre_index;i<temp_index;i++)//将本次获得的数据进行填充到临时数组 { temp[j] = data_rec[i%length]; j++; } HAL_UART_Transmit(&huart1, temp, length, 0xFFFF); }
大家稍微看一下应该能理清里面逻辑。
最后提一下串口DMA的发送方式
HAL_UART_Receive_DMA();
使用此函数发送数据出现一个问题:不能连续使用此函数进行数据发送,自己理解的原因大概是第一个DMA在传送数据的并没有传完,而第二个DMA数据发送请求就到了,因此第二个DMA传送函数便无法执行。解决办法:可以延时一段时间;也可以加个while循环,当判断第一个DMA传送数据完成后才跳出。
个人更倾向于直接用HAL_UART_Transmit(),毕竟人懒,就喜欢代码写的少的,哈哈哈。
以后再遇到关于串口接收不定长数据的情形时就可采用上述方法,如果理解有困难或者想知道在STM32CubeMX中的相关配置,可以点击这里下载完整工程代码
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。