stm32(六)中断
什么是中断呢?可以举一个日常生活中的例子来说明,假如你正在给看书,电话铃响了。这时,你放下手中的书,记录下你看书的页数,去接电话。通话完毕,再继续从原来的位置继续看书。这个例子就表现了中断及其处理过程:电话铃声使你暂时中止当前的看书的动作,而去处理更为急需处理的事情(接电话),把急需处理的事情处理完毕之后,再回头来继续原来的事情。
在这个例子中,电话铃声称为"中断请求",你暂停看书去接电话叫作"中断响应",接电话的过程就是"中断处理",相应地,在STM32执行程序的过程中,由于出现某个特殊情况(中断异常),使得CPU中止正在执行程序,而转去执行处理中断服务程序,待中断服务程序执行完毕,再返回断点继续执行原来的程序,这个过程称为中断。
1、中断源
中断源也就是导致产生中断的原因,在STM32中打断正常工作(程序)的异常,我们就叫它为中断源。
2、中断执行的过程

- 主程序正常运行
- 产生中断异常,CPU保存当前程序执行的位置
- 进入中断服务函数
- 处理中断服务函数
- 中断服务函数处理完成后,返回断点处
- 程序继续运行
上图中,步骤:3~5,只有在中断异常产生是才会执行,中断异常产生则步骤3~5立即执行。
3、中断的意义
生活中的情景:假设你有一个朋友来拜访你,但是由于不知道何时到达,你只能在大门等待,于是什么事情也干不了。如果在门口装一个门铃,你就不必在门口等待而去干其它的工作,朋友来了按门铃通知你,你这时才中断你的手头上的工作去开门,这样就避免等待和浪费时间。
STM32:串口阻塞接收机制原理是只有接收到一个字节的数据,才继续往下执行。这种机制的弊端:CPU无法及时处理其他的代码。
例如:

STM32中断机制:如果用了串口中断,则不需要阻塞等待接收数据。

4、中断优先级和中断嵌套
4.1、中断优先级
生活中的情景:你正看书,如果在电话铃响的同时,门铃也响了,那么你将在"接电话"和"开门"这两个中断请求中选择,先响应哪一个请求。考虑优先级的问题。如果"开门"比"接电话"重要(或者说"开门"比"接电话"的优先级高),那么就应该先开门,然后再接电话,接完电话后再回头来继续看书。这就是说,当同时有多个中断请求时,应该先响应优先级较高的中断请求。
STM32:如果有多个中断,则每个中断都应给分配一个优先级。举例:串口1中断需要有自己的优先级,串口2的中断也要有自己优先级。
4.2、中断嵌套
高优先级的中断可以 打断 低优先级的中断的执行。处理完高优先级的服务程序后,再处理低优先级服务程序的未完成的代码。中断服务程序全部执行完成后,回到主函数,继续执行主函数的代码。

5、Cortex-M4中断控制系统
5.1、NVIC控制器介绍
嵌套向量中断控制器(Nested Vectored Interrupt Controller),是属于内核控制器,该控制器是有ARM公司设计,Coretex-M4的芯片的NVIC控制都是一样。该控制器的作用:用于控制中断的使能禁止、设置中断的优先级。
Core-M4内核最多支持256个中断通道,但是芯片设计公司,例如ST,考虑到实际成本和功耗的情况会裁剪调一部分的中断通道。
STM32F4007ZGT6具有 82 个可屏蔽中断通道(可屏蔽:可以使能也可以禁止), 不包括 的 16 个Cortex™-M4F 内核中断)。
5.2、异常向量表



5.3、NVIC控制器中断优先级
5.3.1、优先级分类
- 抢占优先级:抢占优先级高的中断可以打断抢占优先级低的中断的执行。抢占优先级是用数字来表示:数字越小,优先级越高。(这个和生活中的“酒店”级别刚好相反)
- 响应优先级:如果多个中断的抢占级优先级相同,响应优先级高的中断先响应。响应优先级也是用数字来表示:数字越小,优先级越高。
- 自然优先级:如果多个抢占优先级和响应优先级相同的中断同时触发,自然优先级高的中断先执行。自然优先级是用数字来表示:数字越小,优先级越高。自然优先级是固定,具体请查看STM32F4xx中文参考手册的P234的异常向量表。
Core-M4内核最多支持256级的可编程优先级。用8位来表示优先级级别,,优先级级别分为8组,分别是组0~组7,如下图所示:


5.3.2、STM32F407ZGT6中断优先级
Coretex-M4内核支持256个可编程优先级,ST公司设计STM32F407ZGT6时,为了精简设计,只用了16 个可编程优先级(使用了 高四位4 位来设置中断优先级,设置低四位的值是无效的)。

5.4、NVIC中断中断控制器相关函数
这些函数由内核设置控制中断的函数,已经封装好的。具体代码在:core_cm4.h
5.4.1、NVIC分组设置
void NVIC_SetPriorityGrouping(uint32_t PriorityGroup)
位置:core_cm4.h的1453行
作用:设置优先级分组。
参数:PriorityGroup优先级分组组号
举例:STM32的优先级分组设置为组5,则对应的代码如下:NVIC_SetPriorityGrouping(5);
5.4.2、NVIC具体中断优先级编码
uint32_t NVIC_EncodePriority (uint32_t PriorityGroup, uint32_t PreemptPriority, uint32_t SubPriority)
作用:设置抢占优先级和响应优先级的级别
位置:core_cm4.h的1610行
参数:PriorityGroup优先级分组组号;PreemptPriority:抢占优先级;SubPriority:响应优先级
返回值:32位的编码值,编码值用于中断优先级设置
举例:优先级分组选择为组5,抢占优先级为2,响应优先级为2,代码如下:
u32 prio; prio = NVIC_EncodePriority(5,2,2);
5.4.3、NVIC中断优先级设置
void NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority)
作用:将优先级分组情况以及抢占优先级和响应优先级设置到响应的中断。
参数:
IRQn :中断通道编号。
priority:是NVIC_EncodePriority函数的返回值
举例:设置串口1全局中断的优先级分组选择为组5,抢占优先级为2,响应优先级为2
对应的代码:
u32 prio;
prio = NVIC_EncodePriority(5,2,2);
NVIC_SetPriority(37,prio);
也可以写成NVIC_SetPriority(USART1_IRQn,prio);
5.4.4、NVIC中断使能
void NVIC_EnableIRQ(IRQn_Type IRQn)
作用:使能中断通道
参数:IRQn :中断通道编号。
举例:使能串口1全局中断,NVIC_EnableIRQ(37); 可以写成NVIC_EnableIRQ(USART1_IRQn);
5.4.5、NVIC中断禁能
void NVIC_DisableIRQ(IRQn_Type IRQn)
作用:禁止中断通道。
参数:IRQn :中断通道编号。
举例:禁止串口1全局中断,NVIC_DisableIRQ(37);
5.4.6、中断通道编号
中断通断编号在“stm32f4xx.h”中有定义。
5.4.7、中断服务函数编写原则
- 设置STM32的优先级分组。注意:一个工程只能有一个分组。
- 设置相应通道的抢占优先级和响应优先级
- 使能片上外设中断的使能位,需要操作片上外设的寄存器。(片上外设 peripheral on chip)
- 查找中断函数名,在startup_stm32f40_41xxx.s查找
- 定义一个无返回值,无形参的中断服务函数,举例:串口1中断服务函数
void USART1_IRQHandler(void)
{
//中断服务程序代码
}
注意:不可以主动调用中断函数;中断函数的代码不能太多。中断函数的执行时间不能过长。
5.4.7、中断程序设计
“读取数据寄存器不为空”则表明成功接收到数据,这是串口中断的一个“中断源”,如果成功接收一个字节的数据且使能了串口中断,则会执行中断服务函数。

6、软件设计
以串口中断为例
寄存器版
void usart1_Init()
{
u16 integer ;
u16 fraction;
float USARTDIV;
u32 baudRate = 115200;
RCC->AHB1ENR |= 1<<0;//使能GPIOA的时钟 RCC_AHB1ENR
GPIOA->MODER &= ~(3<<18);//清零
GPIOA->MODER |= 2<<18;//设置PA9为复用功能模式MODER
GPIOA->MODER &= ~(3<<20);//清零
GPIOA->MODER |= 2<<20;//设置PA10为复用功能模式MODER
GPIOA->AFR[1]&= ~(0XF<<4);//清零
GPIOA->AFR[1]|= (7<<4);//设置PA9的复用功能为第7复用功能 RXD, AFRH
GPIOA->AFR[1]&= ~(0XF<<8);//清零
GPIOA->AFR[1]|= (7<<8);//设置PA10的复用功能为第7复用功能 TXD, AFRH
RCC->APB2ENR |= 1<<4;//使能串口1模块时钟 RCC_APB2ENR
USART1->CR1 |= 1<<15;//设置串口OVER8为1
USART1->CR1 &= ~(1<<12);//设置串口数据位长度 :1 起始位, 8 数据位
USART1->CR2 &=~(3<<12);//设置串口停止位长度 :1 个停止位
USART1->CR1 &= ~(1<<10);//无校验
USART1->CR1 |= 1<<3;//发送使能
USART1->CR1 |= 1<<2;//接收使能
USARTDIV = 84000000/8/baudRate;//串口波特率设置:USARTDIV = fCK/(8*(2-OVER8) /波特率
integer = (u16)USARTDIV;
fraction = ((u16)(USARTDIV-integer))<<4;
USART1->BRR |= integer<<4 | fraction;
/* 使能中断源中断条件 */
USART1->CR1 |= 1 << 5; //使能接收中断
NVIC_EnableIRQ(USART1_IRQn); //使能USART_1NVIC中断
USART1->CR1 |= 1<<13;//使能串口
}
void USART1_IRQHandler(void)
{
u8 res;
if(USART1->SR&(1<<5))//接收到数据
{
res=USART1->DR;
if(res == 0x0D || res == 0x0A)
return;
USART1->DR = res+1;
while(!(USART1->SR&(1<<6))); //SR寄存器的位6为1则表明成功发送一个字节数据,跳出循环
USART1->SR &=~(1<<6); //清零
}
}
int main(void)
{
usart1_Init();
while(1)
{
}
}
库函数版
void usart1_Init()
{
u32 baudRate = 115200;
GPIO_InitTypeDef GPIO_InitStructe;
USART_InitTypeDef USART_InitStructe;
NVIC_InitTypeDef NVIC_InitStructe;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE); //使能 A 端口
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE); //使能串口端口
GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_USART1); //GPIO 端口映射到 USART
GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_USART1); //GPIO 端口映射到 USART
// USART_DeInit(USART1); //复位
GPIO_InitStructe.GPIO_Mode =GPIO_Mode_AF; //配置为输出模式
GPIO_InitStructe.GPIO_Pin =GPIO_Pin_9|GPIO_Pin_10; //初始化 GPIOA9/10;
GPIO_Init(GPIOA,&GPIO_InitStructe);
USART_InitStructe.USART_BaudRate =baudRate; //波特率
USART_InitStructe.USART_HardwareFlowControl =USART_HardwareFlowControl_None;//无硬件流控制
USART_InitStructe.USART_Mode =USART_Mode_Tx|USART_Mode_Rx; //接收模式,发送模式使能
USART_InitStructe.USART_Parity =USART_Parity_No; //无奇偶校验
USART_InitStructe.USART_StopBits =USART_StopBits_1; //一位停止位
USART_InitStructe.USART_WordLength =USART_WordLength_8b;//8 位数据位
USART_Init(USART1,&USART_InitStructe);
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启相关中断
NVIC_InitStructe.NVIC_IRQChannel= USART1_IRQn; //串口 1 中断
NVIC_InitStructe.NVIC_IRQChannelCmd =ENABLE; //串口 1 使能
NVIC_InitStructe.NVIC_IRQChannelPreemptionPriority =0; //抢占优先级设为 0
NVIC_InitStructe.NVIC_IRQChannelSubPriority =0; //子优先级设为 0
NVIC_Init(&NVIC_InitStructe);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
USART_Cmd(USART1,ENABLE); //使能串口
}
void USART1_IRQHandler(void)
{
u8 res;
while(USART_GetFlagStatus(USART1,USART_FLAG_RXNE)) //等待接收完成
{
USART_ClearITPendingBit(USART1,USART_FLAG_RXNE); //清楚标志位
res=USART_ReceiveData(USART1); //接收串口的数据
if(res == 0x0D || res == 0x0A)
continue;
USART_SendData(USART1,res+1); //输出+1
}
}
int main(void)
{
usart1_Init();
while(1)
{
}
}
更多推荐
所有评论(0)