打印
[应用相关]

STM32 HAL 之 UART:空闲中断结合DMA实现不定长数据收发

[复制链接]
940|10
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
晓伍|  楼主 | 2021-6-5 15:25 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
一、不定长数据接收的原理及其解决的问题
在 STM32 中,UART是最为常见的通信方式——它每次接收一个字节。我们可以使用轮询的方式,但是对于某些数据不固定时间发送的数据,轮询的方式有时候不够灵活。也可以使用中断的方式,如每一个字节都中断一次,当时比较消耗系统资源。特别是HAL库中,从中断到回调函数运行了不少的程序,频繁的中断很可能造成数据溢出。在本文使用F051单片机中,这种情更加明显。为了避免这个问题,我们使用指定接收一定长度的数据,再调用回调函数,这会让我们可以接收大数据,但是这种情况则造成了,要求每次的包是固定长度。为了解决以上一些问题,网上最常用的办法是使用空闲中断,即在串口空闲的时候,触发一次中断,通知内核,本次运输完成了。串口空闲中断的判定是:当串口开始接收数据后,检测到1字节数据的时间内没有数据发送,则认为串口空闲了。由于我们的内核在串口接收数据到空闲这段时间,是不受理串口数据的,所以我们还需要使用DMA来协助我们把数据传送到指定的地方,当数据传输完成后,通知内核去处理。


使用特权

评论回复
沙发
晓伍|  楼主 | 2021-6-5 15:26 | 只看该作者
二、STM32CubeMx 中 UART 和 DMA 的配置1. UART 的 配置1.1 UART 基本参数的配置

首先需要配置串口的基本参数,波特率可根据实际情况设置,一般设置为115200bps,9600bps 等:在这里插入图片描述


使用特权

评论回复
板凳
晓伍|  楼主 | 2021-6-5 15:26 | 只看该作者
1.2 使能串口 NVIC 中断并生成代码

接着需要使能中断,让串口内核可以相应串口的中断:
在这里插入图片描述


注意,这里还要配置NVIC生成代码,否则中断无法正常响应:
在这里插入图片描述


使用特权

评论回复
地板
晓伍|  楼主 | 2021-6-5 15:29 | 只看该作者
1.3 DMA 的配置

接着配置DMA,让DMA和串口接收联系起来,实现DMA串口数据的运输,记得切换回USART1选项。
在这里插入图片描述


使用特权

评论回复
5
晓伍|  楼主 | 2021-6-5 15:30 | 只看该作者
1.4 配置串口 IO 口模式

某些电路可能已经配置有了外部上拉,本文在默认的模式下也可以通信,但为了保证更稳定的电平,这里配置为上拉输入:
在这里插入图片描述

至此,我们已经把串口,以及和串口相关的NVIC和DMA配置完毕,接下来就可以开始程序的实现了。


使用特权

评论回复
6
晓伍|  楼主 | 2021-6-5 15:30 | 只看该作者
三、 编程步骤
1.开启串口空闲中断:在程序初始化时候,使能串口中断

... ...
void main(void)
{
        .. ...
        __HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE);
        while(1)
        {
        .. ..
        }
}
... ...



2.虽然我们使用的CubeMx来配置DMA,但只是配置DMA模式为串口到内存,所以还需要在程序中进一步指定:DMA具体搬运到内存的哪一个位置中,我们建立一个数组用以存放DMA搬运的串口数据,并使用HAL_UART_Receive_DMA()函数来配置,具体代码如下所示:

... ...
uint8_t receive_buff[255];                //定义接收数组
... ...
void main(void)
{
        .. ...
        __HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE);
        HAL_UART_Receive_DMA(&huart1, (uint8_t*)receive_buff, 255);     //设置DMA传输,讲串口1的数据搬运到recvive_buff中,
                                                                        //每次255个字节
        while(1)
        {
        .. ..
        }
}
... ...


使用特权

评论回复
7
晓伍|  楼主 | 2021-6-5 15:31 | 只看该作者
3.定义串口空闲中断处理函数:在串口中断中添加串口空间中断处理函数

void USART1_IRQHandler(void)
{
    /* USER CODE BEGIN USART1_IRQn 0 */
    /* USER CODE END USART1_IRQn 0 */

    HAL_UART_IRQHandler(&huart1);

    /* USER CODE BEGIN USART1_IRQn 1 */
    USER_UART_IRQHandler(&huart1);                                //新添加的函数,用来处理串口空闲中断
    /* USER CODE END USART1_IRQn 1 */
}



void USER_UART_IRQHandler(UART_HandleTypeDef *huart)
{
    if(USART1 == huart1.Instance)                                   //判断是否是串口1(!此处应写(huart->Instance == USART1)
    {
        if(RESET != __HAL_UART_GET_FLAG(&huart1, UART_FLAG_IDLE))   //判断是否是空闲中断
        {
            __HAL_UART_CLEAR_IDLEFLAG(&huart1);                     //清楚空闲中断标志(否则会一直不断进入中断)
            printf("\r\nUART1 Idle IQR Detected\r\n");
            USAR_UART_IDLECallback(huart);                          //调用中断处理函数
        }
    }
}



4.定义串口空闲中断回调函数:用以标记数据接收完成,计算接收到数据的长度

extern uint8_t receive_buff[255];                                                  //声明外部变量
void USAR_UART_IDLECallback(UART_HandleTypeDef *huart)
{
    HAL_UART_DMAStop(&huart1);                                                     //停止本次DMA传输

    uint8_t data_length  = BUFFER_SIZE - __HAL_DMA_GET_COUNTER(&hdma_usart1_rx);   //计算接收到的数据长度

    printf("Receive Data(length = %d): ",data_length);
    HAL_UART_Transmit(&huart1,receive_buff,data_length,0x200);                     //测试函数:将接收到的数据打印出去
    printf("\r\n");

    memset(receive_buff,0,data_length);                                            //清零接收缓冲区
    data_length = 0;
    HAL_UART_Receive_DMA(&huart1, (uint8_t*)receive_buff, 255);                    //重启开始DMA传输 每次255字节数据
}


使用特权

评论回复
8
卧室谁| | 2021-6-5 18:23 | 只看该作者
支持楼主,学习一下

使用特权

评论回复
9
carefull3357dc| | 2021-6-6 10:14 | 只看该作者
学习学习!

使用特权

评论回复
10
carefull3357dc| | 2021-6-6 10:15 | 只看该作者
请问一下,空闲中断,会不会有误差?比如 波特率慢的时候,导致两个数据字节之间的时差大,导致了误触发?

使用特权

评论回复
11
EDA设计爱好者| | 2021-6-6 15:08 | 只看该作者
uint8_t data_length  = BUFFER_SIZE - __HAL_DMA_GET_COUNTER(&hdma_usart1_rx);   //计算接收到的数据长度

是不是说 这个长度要+1?

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

60

主题

4143

帖子

1

粉丝