小明正在玩游戏,小明的妈妈喊小明吃饭,这时,小明放下游戏,先去吃饭,吃完饭后,继续玩游戏。
这就是中断!“正在玩游戏”是执行程序,“小明的妈妈”是中断源,“喊小明吃饭”是中断产生,“小明吃饭”是中断处理,“吃完饭”是中断处理完成,“继续玩游戏”是继续执行程序;
中断,就是CPU在执行程序过程中(小明玩游戏),由于计算机内部/外部发生其它事件(小明妈妈喊小明吃饭),需要CPU处理,CPU暂停当前程序运行,去处理发生的事情(吃饭),处理完成后(吃完饭),CPU返回原来执行的地方,继续执行程序(继续玩游戏);
小明正在玩游戏,小明的妈妈喊小明吃饭,这时,小明放下游戏,先去吃饭,饭还没吃完,小明的老板打电话要求小明给客户回个邮件,小明只好给客户回个邮件,回完邮件,再继续吃完,吃完饭后,再继续玩游戏。
这就是中断嵌套!
中断发生后,CPU去处理发生的中断A(吃饭),此时,发生另一个中断B(老板要求发邮件),CPU转而去处理另一中断B(发邮件),处理完中断B后(发完邮件),再继续处理中断A(继续吃饭),处理完中断A后(吃完饭),才继续执行当前程序(玩游戏)。
中断嵌套,就有多个中断事件发生,就存在着优先级。优先级,决定了哪个中断最优先被处理,或者一个中断能否打断另一个中断。
例子中,很明显,“发邮件”的优先级,要比“吃饭”的优先级高,而且,“发邮件”能够打断“吃饭”。
那么,为什么要用中断呢?保证实时性,能够优先处理紧急事件。
虽然在《STM32 Uart及其配置》中,使用轮询的方式,同样能实现需要的功能,但是,如果程序量很多,while(1)里面有很多个Handler()需要处理很多事情,当CPU正在处理某个Handler()里的事件时,串口有数据需要优先处理,这时,使用轮询,就无法保证数据能及时处理了,故,使用中断接收。
和《STM32 Uart及其配置》一样,新建一个工程,配好时钟和引脚,接下来,配置中断。
点Configuration,点NVIC,NVIC =Nested Vectored InterruptController。
弹出如下页面:
Enable:好说,钩上就对了,既然要用中断,肯定要让它Enable。
Preemption Priority / Sub Priority:中断的优先级,决定于先吃饭还是先发邮件,或者吃饭过程中能不能叫跑去发邮件!
要理解这个东西,我们得先来看一看STM32的中断管理机制。
参考《PM0056 Programming manual》,我们先看一下STM32的中断管理机制。
下图是STM32中断简介:
总共有81路中断,取决于芯片;
16级可编程优先级,数字越小优先级越高;
优先级使用分组机制,分为组优先级(Group Priority)和子优先级(subpriority)。
接下来看下“组优先级(Group Priority)”和"子优先级(subpriority)"的相关说明
它告诉我们,高位表示组优先级(Group Priority),低位表示该组中的子优先级(subpriority);
只有组优先级(Group Priority)可以抢占,例如,“吃饭”的组优先级(Group Priority)为1,“发邮件”的组优先级(Group Priority)为0,那“发邮件”可以打断“吃饭”,也就是饭还没吃完,老板叫小明发邮件,小明必须去发邮件;
子优先级(subpriority)不能抢占,但它决定中断响应和执行的顺序,若是“吃饭”和“发邮件”的组优先级都为0,但是“吃饭”的子优先级(subpriority)为1,“发邮件”的子优先级(subpriority)为0,那如果“妈妈喊小明吃饭”的同时“老板叫小明发邮件”,那就必须先发邮件,发完邮件再响应吃饭指令;
再看一个图,
16级优先级,需要4个bit表示,PRI_N[7:4],正好高4个bit。
组优先级(Group Priority)可以占4、3、2、1、0位,相应的组优先级(Group Priority)级数和子优先级(subpriority)级数,在图中均有体现。
再回到这个图,是不是清楚明白了?
Priority Group:组优先级(Group Priority)占的bit数,如果组优先级(Group Priority)占2bit,那么,组优先级(Group Priority)就有4级,剩余的2bit,分配给子优先级(subpriority),也是4级;
Preemption Priority / Sub Priority:就选个3吧,因为我们要用这个口来作为调试及打印,设置低一点也没关系;
点 OK,然后生成代码。
生成代码完成,打开代码,看一下这个函数,这个函数在HAL_UART_Init() 里调用,HAL_UART_Init() 在 MX_UART4_Init() 调用,自己跟一下代码。
接下来我们看一下中断服务例程,在Stm32f2xx_it.c 文件里,有各种Handler,UART4_IRQHandler()这个,就是我们UART4的中断服务例程。
中断服务例程是啥?中断产生时,CPU所需要做的事,就是服务例程。
比如,产生一个中断,叫“喊小明吃饭”,小明要去吃饭,怎么吃?吃多少?这些,都是中断服务例程里所做的事。
这个例程的代码可以自己实现,不一定非要用HAL_UART_IRQHandler()。
继续跟,看一下中断服务例程里面实现了啥?
一些标志位的置位,复位,检测等,详细请参考《RM0033 Reference manual》;
跟一下UART_Receive_IT() 这个函数;
看这里,清除 USART_CR1_RXNEIE 标志位,关闭接收中断,接着,调用HAL_UART_RxCpltCallback 这个函数。
问题1:开启接收中断呢?在哪里开?
问题2:这个函数,干啥?
问题1:开启接收中断呢?在哪里开?
搜一下 USART_CR1_RNNEIE,在这个函数里开了,所以,等会我们初始化完成后,要调用这个函数,等待数据。
HAL_UART_Receive_IT() 这个函数的作用,主要是开启接收中断,如果有中断发生,把数据放入 pData里面。
问题2:这个函数,干啥?
继续跟,看它的说明,这个函数不能改,如果需要,就在用户文件里实现就行了。
那么,再理一理脉络,
1. 初始化->开启接收中断;
2. 中断产生->进入中断服务例程->清除 USART_CR1_RXNEIE 标志位,关闭接收中断->调用回调函数;
所以,我们就在重写回调函数实现我们想做的事儿就行了,做完事儿后,如果想继续接收数据,就在回调函数里面调用HAL_UART_Receive_IT()开启接收中断。
脉络理清,那我们应该做的是
1. 开启接收中断;
00046:用两个变量来存接收和发送;
00069:初始化完成后,调用这个函数,开启接收中断,等待数据;
2. 实现回调函数:
00134:给要发送的数据赋值 = 收到的数据+1;
00135:发送;
00136:开启接收中断;
完成,编译,烧录,复位,用串口调试工具看看运行结果。
整个工程及代码呢,请上百度网盘上下载:
链接:/s/19usUcgZPX8cCRTKt_NPcfg
密码:07on
文件夹:\Stm32CubeMx\Code\UartRx_IT.rar
上一篇:《STM32 Uart及其配置》
下一篇:《STM32 Uart DMA方式接收数据》
回目录:《前言》