【STM32+cubemx】0004 HAL库开发:uart串口的轮询和中断使用

本节我们来学习异步串口uart的应用,使用轮询和中断两种方式,来实现计算机向单片机发送数据,单片机处理之后再将数据返回。

1)cubemx生成代码

首先还是在cubemx中生成代码,选择器件、设置SYS(调试接口)、设置RCC(外部晶振时钟源)这几个步骤和前一节一样(也可以复制前面闪灯的工程,在上面修改)。

然后设置串口引脚,这里我们选择uart1,异步串口,选完后,已经使用的串口引脚PA9和PA10会变成绿色;然后选择开启串口全局中断:

之后,和上一节一样,在时钟选项卡设置主时钟为72M;在project Manager选项卡设置工程名和路径,生成工程代码。

2)uart的轮询模式

在keil中打开生成的工程,可以看到软件生成的代码中,main函数中已经有uart硬件初始化的代码。

如果我们使用串口的轮询模式,那么已经可以直接使用hal函数了:

HAL_UART_Transmit()和HAL_UART_Receive()。

如下图,是实现等待计算机发送16个字节数据,收到后,每个数据加1后再返回给计算机:

轮询模式使用时,函数的最后一个参数是超时数值,图中设置的是0xffff,也就是说函数的执行超时是65535ms。

在接收函数中,如果65535ms还未接收完16个字节的数据,则会向后执行,如果接收完则立即向后执行。发送数据的函数也一样。

轮询模式使用起来最简单,但是会占用大量的cpu时间,在等待接收和等待发送完毕时,cpu不能去做别的运算,只能在这里空等,运行的效率很低。

3)uart的中断模式

中断模式可以提高cpu的使用效率,在等待数据接收和发送时,cpu可以取做别的运算,只需在发送/接收完的一点时间进入中断做一些处理。

由于我们在cubemx中配置串口时,已经设置了串口的全局中断,所以这里可以很方便地使用下面这两个函数:

HAL_UART_Transmit_IT();发送函数好理解一些,就是将要发送的数据和长度填入,然后程序可以执行后面的任务,系统会自动使用中断模式将数据发完(之后只在每发完一个字节时进一次中断,占用很少的时间)。

HAL_UART_Receive_IT();接收函数,将要接收的数据和长度填入,然后程序可以执行后面的任务,系统会自动使用中断模式接收数据,当接收完指定长度的数据后,会产生一个中断;在实际使用时,要在中断回调函数中设置一个标志位,循环去检测标志位,才能知道它什么时候已收完。

我们以一个例子来看一下它们的使用方法:

先定义变量:

再重写接收函数的回调函数,在回调函数中设置标志位:

主循环中,先设置中断接收16个字节数据,然后等待,当接收满16个字节后,回调函数置标志位,主循环中检测到后,可以作进一步处理:

4)HAL库中断发送和接收的缺点

先讲讲HAL_UART_Transmit_IT()发送的这个函数。要注意这个函数使用时,如果前一次要发送的数据还未发完,又进行了一次新的发送,那么它会继续发送前一次的数据,本次发的数据不会执行。如果需要确保两次都发送出去,可以这样使用:

while(HAL_BUSY == HAL_UART_Transmit_IT(&huart1, str, 12));    

while(HAL_BUSY == HAL_UART_Transmit_IT(&huart1, str2, 4));

这里,如果前一次未发完,会一直等待到发完,再发送本次的数据,和轮询时的等待差不多。

HAL_UART_Receive_IT()接收的这个函数,最大的不方便就是你得事先设定接收多少数据,这个是比较违反直觉的,因为你并不会知道外部什么时候发数据过来,也不知道外部一次会发多少数据。这会使得处理接收数据产生一定的困难。

当然,你可以设置每次接收一个字节数据就产生一次中断,再对收到的数做处理,这是可以的,但是,这样的做的效率很低,尤其HAL库每进一次中断要判断很多情况,会占用大量的cpu时间;以前测试过用921600波特率,中断占用的cpu时间大约有1/3之多!另外,这个方法在同时有收和发时,可能会进入溢出错误的死锁;可能是由于同时出现了溢出中断和收数中断时,处理不完全的问题;有网友遇到过,没有深究。

一般来讲,串口使用fifo环形缓冲队列是很好的解决方案,下一节将详细讲讲,如何改写HAL库的中断函数,实现高效的串口收发。

欢迎关注我的公众号,文章同步更新,可留言获取相关资料和软件:

posted @ 2021-10-31 23:59  xiaobaibai_2021  阅读(1208)  评论(0编辑  收藏  举报