STM32 Uart中断接收

        小明正在玩游戏,小明的妈妈喊小明吃饭,这时,小明放下游戏,先去吃饭,吃完饭后,继续玩游戏。

        这就是中断!“正在玩游戏”是执行程序,“小明的妈妈”是中断源,“喊小明吃饭”是中断产生,“小明吃饭”是中断处理,“吃完饭”是中断处理完成,“继续玩游戏”是继续执行程序;

        中断,就是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 Interrupt Controller。

弹出如下页面:

    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:开启接收中断;

完成,编译,烧录,复位,用串口调试工具看看运行结果。

整个工程及代码呢,请上百度网盘上下载:

    链接:https://pan.baidu.com/s/19usUcgZPX8cCRTKt_NPcfg

    密码:07on

    文件夹:\Stm32CubeMx\Code\UartRx_IT.rar

上一篇:《STM32 Uart及其配置

下一篇:《STM32 Uart DMA方式接收数据

猜你喜欢

转载自blog.csdn.net/ForeverIT/article/details/81612334