第四节:中断与串口(黄老师)
作者:互联网
一、中断
中断的概念我在51篇已经有整理过了,这次就不再详细描述了,只介绍在STM32中的中断相关内容。
1.Cortex-M3的中断
其中断的框架是由内核设计者ARM公司设计的,设计了256个,很多型号的芯片由于资源不够,是用不了那么多个中断的,例如F1型号的芯片最多70多个中断,而在本次MINI开发板上的芯片,大概只有40多个。
2.中断有哪些
这是F1系列芯片的中断表。可以看到,分为系统异常和外部中断,系统异常是由CPU内核产生的,而外部中断是由外设产生的,在效果上两者是一样的,通常统一称为中断。
在表上还有一点需要注意的,优先级类型,固定则代表其不能修改,而可编程就代表可以利用我们下面要讲到的NVIC去配置。
3.中断优先级
STM32的中断优先级分为抢占优先级和子优先级。每个中断都要指定这两种优先级。
抢占优先级决定的是中断嵌套,例如抢占优先级高的中断可以中断抢占优先级低的中断。
子优先级决定的是响应顺序,即两个中断同时产生,子优先级高的先执行。
优先级的指定或者说配置是通过NVIC中断控制器。
4.GPIO中断
对于GPIO中断来说,除了NVIC之外,还有EXTI也是来控制它的。(下图EXIT打错了)
中断的原理图如下:
可以看到,GPIO的中断需要先通过EXTI,再到NVIC。
EXTI分为外部中断以及事件控制器,其中外部中断就是我们常见的,中断出现后,执行我们自定义的函数。而事件控制器则不需要我们自定义函数,当中断出现后,会自动联动其他外设做指定的事,例如可以是:中断出现后,自动进行ADC采集,这部分的代码是不需要我们自己编写的,而且由于它是自动处理的,它的响应速度会更快。
在原理图中我们还注意到,PA0 ~ PG0是通过同一个AFIO,最后通过EXTI0的。这代表它们是处于同一条中断线,即当出现中断时,我们并不能区分是PA0 ~ PG0中的哪一个,因此,在编程中我们要避开这种情况,当PA0做中断引脚时,其他的第0口不能作为中断源。
5.使用STM32CubeMX创建一个简单的按键中断程序
作为初学者,我们应该学会如何用STM32CubeMX去配置一个带有中断的程序。这里我们创建一个程序,当按下按键时产生中断,从而控制开发板的LED亮灭。
①将PA0口设置为GPIO_EXIT0
前面我们已经知道,按键对应的GPIO口是PA0口,所以这里我们将PA0口设置为中断模式。
②设置触发方式
因为开发板上的按键默认是高电平,所以我们配置为下降沿触发(Falling),即当我们按下按键后触发中断。
③配置NVIC
在NVIC中,我们首先需要开启中断使能。同时我们也可以在后面设置优先级。
然后是点击Code generation
,跳转到下面示意图,勾选生成代码方框。
④生成代码
最后点击生成代码。
⑤编写代码实现功能
首先我们在startup_stm32f103xb.s
汇编文件中,找到EXTI0的中断处理函数名,接着右键点击跳转到函数的定义。
可以看到里面有一个HAL库的处理函数,我们再次跳转。
HAL库的函数表明,当中断到来时,会自动判断是否是该中断引脚,若是,则清除中断标志,然后调用回调函数,我们需要做的,就是编写回调函数,在其中实现我们的功能。
在该HAL库函数定义的下方,我们可以看到回调函数的定义,回调函数是一个弱函数,因此我们要自己编写它。因为本次中断与GPIO有关,所以我们将回调函数写在gpio.c
文件里。
对回调函数的编写结果如下,先判断按下的是哪个按键,因为PA0,PB0…都能够进入该中断,并且用key_flag
来做标志位,使每次按下按键时,灯的亮灭状态翻转。
同时我们编写函数来在主函数调用,返回key_flag
的值。注意该函数需要在头文件中进行声明。
最后是在主函数中实现。
注:
1.中断函数里不要进行复杂操作。
2.对两种优先级的分配并不是随便设置的,还需要在NVIC里通过Priority Group对两种优先级的控制位数进行分组,总共只有4位,例如这里是给前面的抢占优先级分配了4位,所以就没有给子优先级的分配位数了。这样,我们就可以对抢占优先级设置2的4次方,即16级,而对子优先级就设置不了。如果我们给抢占优先级分配了2位,则自动给子优先级分配2位,这样每种优先级都能设置4级。
二、串口
这里同样在51单片机篇已经介绍过了。
1.同步/异步通信
在异步通信中,我们需要指定波特率,在同步中则不用。同步通信的协议有例如I2C、SPI,它们需要通信双方连接时钟线,这里两种协议也是需要重点掌握的,后面学习到再整理。
2.串口电平
像我们平常的学习过程中,单片机通常和我们的模块距离很近,这时候我们选择TTL电平即可进行单片机与模块间的通信,例如本次实验与WIFI模块。
而在实际工程中,有时候会遇到单片机与模块间的距离较远,这时候只使用TTL电平就会干扰很大,因为远距离传输会使信号衰减,选用TTL电平会导致我们对高低电平的识别出现差错。因此需要转换成其他电平。
3.串口的连接和时序
一般串口之间的连接有3个引脚就足够了,要注意的是Tx和Rx必须交叉连线。
串口传输一帧数据即一个字节的示意图如下,与51单片机篇一样:
默认状态是高电平,当变为低电平时,进入到开始位。最后传输结束时又将电平拉高,进入默认状态。
在本开发板上串口的作用主要有两个:一是单片机与单片机的通信,二是做调试用,需要将TTL电平转换USB电平。
4.使用CubeMX创建一个简单的调试打印程序
这里与51单片机不同的是,我们可以不选用中断。有一个原因是在本程序中我们只需要单片机向电脑发送数据而已,并不需要向单片机发送程序,在下一个有关接收的程序则会用到中断。
开发板的串口1是用来调试打印的,因此我们对串口1进行配置。
①将串口1设置为“异步通信”
②配置波特率等参数
我们可以在Configuration里配置串口的波特率、数据位数、奇偶校验位和停止位,还可以在高级选项中设置数据的方向,例如可以只接收不发送,只发送不接收,当然,默认情况下我们是选择双向。
③编写代码
配置后即可生成代码。
HAL库中同样也有关于串口发送接收的函数。例如:
可以看到这个发送函数还有DMA的以及IT(中断)的类型。
DMA即不用经过CPU处理,直接利用DMA将数据从某个点传送到另一个点,后面会学到。
IT则是带有中断处理函数的,用中断方式进行传输,在下一个程序会用到。
Transmit
函数的使用很不方便,需要的参数比较多。当我们需要打印出多行内容时,很繁琐,因此这里我们用重定向打印函数来打印内容,具体方式见学习手册。
具体操作就是将下列函数放在uart.c
文件中,并且加上<stdio.h>
头文件。然后我们就可以在主函数中直接使用printf
和scanf
来处理内容。
注:函数中的参数&huart1
代表该方式仅应用于串口1。
最终我们直接在主函数里使用printf
函数,并用**串口助手
**类型的软件,显示出内容即可。
5.使用CubeMX创建一个带有中断的串口程序
这里我们编写一个程序,将单片机与WIFI模块进行连接,然后简单的向WIFI模块发送AT指令,并且单片机接收到WIFI模块返回的数据并且打印出来。
开发板的串口2是用来和外部模块通信的。
①将串口2设置“异步通信”并配置中断
设置成异步通信方式与上一个程序同理,而配置中断则和按键的中断配置一样。
②编写代码
与GPIO中断一样,串口中断后一样是进入回调函数。
第一步:使用Receive_IT
函数来等待接收串口2的数据,并保存在rx_data
中。
第二步:当接收到数据时,会自动进入中断程序,最终来到回调函数,所以我们要编写回调函数。
这里我们将接收到的数据存储在buf数组中,并且再次调用Receive_IT
函数等待下一次数据的到来。
第三步:再将buf数组中的数据读取,并最终打印出来。
下面的函数是写在主函数里的。
注:这里还有几点需要注意的。
1.我们还需要在上电之后给WIFI模块发送AT指令,WIFI模块才会返回信息到单片机;
2.每次打印出内容后,我们需要清除buf,因为buf里存储了上一次的数据,在我们进行下一次循环时,如果不进行清除,会再次读取buf中保存的上一次接收的数据,并再次打印出来,最终显示的结果会是一直进行打印;
3.连接示意图如下:
标签:优先级,函数,中断,单片机,串口,第四节,我们 来源: https://blog.csdn.net/weixin_48021291/article/details/123131345