RabbitMQ 分布式服务 主从复制 ISP database listview import Ractivejs vue前端框架 npm安装vue android开发项目 多商户商城模板 bootstrap侧边栏 oracle无效的列索引 solr索引 js教程文档 反函数的二阶导数 hbuilder插件 mysql重启 mysql学习 mysql 导入数据 python文件 python学习文档 python字符串匹配 python路径设置 java的基本数据类型 java架构 音频频谱分析软件 dvwa安装教程 免费家谱制作软件 迅雷免费会员号共享 backtrack3 在线手册 无法打开搜索页 flash基础 c语言编程实例 俄罗斯方块代码 backtrack4 grep正则表达式 冰冠堡垒单刷路线
当前位置: 首页 > 学习教程  > 编程语言

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

2020/8/11 20:33:35 文章标签:

一、不定长数据接收的原理及其解决的问题

在 STM32 中,UART是最为常见的通信方式——它每次接收一个字节。我们可以使用轮询的方式,但是对于某些数据不固定时间发送的数据,轮询的方式有时候不够灵活。也可以使用中断的方式,如每一个字节都中断一次,当时比较消耗系统资源。特别是HAL库中,从中断到回调函数运行了不少的程序,频繁的中断很可能造成数据溢出。在本文使用F051单片机中,这种情更加明显。为了避免这个问题,我们使用指定接收一定长度的数据,再调用回调函数,这会让我们可以接收大数据,但是这种情况则造成了,要求每次的包是固定长度。为了解决以上一些问题,网上最常用的办法是使用空闲中断,即在串口空闲的时候,触发一次中断,通知内核,本次运输完成了。串口空闲中断的判定是:当串口开始接收数据后,检测到1字节数据的时间内没有数据发送,则认为串口空闲了。由于我们的内核在串口接收数据到空闲这段时间,是不受理串口数据的,所以我们还需要使用DMA来协助我们把数据传送到指定的地方,当数据传输完成后,通知内核去处理。

二、STM32CubeMx 中 UART 和 DMA 的配置

1. UART 的 配置

1.1 UART 基本参数的配置

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

1.2 使能串口 NVIC 中断并生成代码

接着需要使能中断,让串口内核可以相应串口的中断:
在这里插入图片描述
在这里插入图片描述
注意,这里还要配置NVIC生成代码,否则中断无法正常响应:
在这里插入图片描述

在这里插入图片描述

1.3 DMA 的配置

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

1.4 配置串口 IO 口模式

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

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

三、 编程步骤

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)
	{
	.. ..
	}
}
... ...

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字节数据
}

本文链接: http://www.dtmao.cc/news_show_100369.shtml

附件下载

相关教程

    暂无相关的数据...

共有条评论 网友评论

验证码: 看不清楚?