1. NVIC
NVIC 是嵌套向量中断控制器,控制着整个芯片中断相关的功能,它跟内核紧密耦合,是内核里面的一个外设。但是各个芯片厂商在设
计芯片的时候会对 Cortex-M4 内核里面的 NVIC 进行裁剪,把不需要的部分去掉,所以说 STM32的 NVIC 是 Cortex-M4 的 NVIC 的一个子集。
优先级分组
优先级设置
中断编程
1. 使能外设某个中断,这个具体由每个外设的相关中断使能位控制。比如串口有发送完成中断,接收完成中断,这两个中断都由串口控制寄存器的相关中断使能位控制。
- 初始化 NVIC_InitTypeDef 结构体,配置中断优先级分组,设置抢占优先级和子优先级,使能
中断请求。
typedef struct {
uint8_t NVIC_IRQChannel; // 中断源 用来设置中断源,不同的中断中断源不一样具体参考stm32f4xx.h typedef enum IRQn
uint8_t NVIC_IRQChannelPreemptionPriority; // 抢占优先级
uint8_t NVIC_IRQChannelSubPriority; // 子优先级
FunctionalState NVIC_IRQChannelCmd; // 中断使能或者失能
} NVIC_InitTypeDef;
NVIC_PriorityGroupConfifig 是整个程序中只需要设置一次
示例程序
static void NVIC_Configuration(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
/* 配置 NVIC 为优先级组 1 */
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
/* 配置中断源:按键 1 */
NVIC_InitStructure.NVIC_IRQChannel = KEY1_INT_EXTI_IRQ;
/* 配置抢占优先级:1 */
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
/* 配置子优先级:1 */
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
/* 使能中断通道 */
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
/* 配置中断源:按键 2,其他使用上面相关配置 */
NVIC_InitStructure.NVIC_IRQChannel = KEY2_INT_EXTI_IRQ;
NVIC_Init(&NVIC_InitStructure);
}
2. EXTI 外部中断事件控制器
外部中断/事件控制器 (EXTI) 管理了控制器的 23 个中断/事件线。每个中断/事件线都对应有一个边沿检测器,可以实现输入信号的上升沿检测和下降沿的检测。EXTI 可以实现对每个中断/事件线进行单独配置,可以单独配置为中断或者事件,以及触发事件的属性。
功能框图
红色线产生中断,绿色线产生事件
中断:
-
1是输入线,有23根,除了每个GPIO的十六根以外,还有7根用于控制其他事件
SYSCFG 外部中断配置寄存器 1(SYSCFG_EXTICR1) 的 EXTI0[3:0] 位选择配置为 PA0、PB0、PC0、PD0、PE0、PF0、PG0、PH0 或者 PI0
-
2是边沿检测电路,配置为上升沿触发或者下降沿触发,也可以同时触发
-
3是或门,提供了软件中断事件寄存器也可以触发中断的功能
-
4是与门,当中断屏蔽寄存器为0时可以屏蔽中断
-
5是将EXTI_PR寄存器内容给NVIC
事件:
- 6的作用与4相同,用于选择屏蔽事件
- 编号 7 是一个脉冲发生器电路,当它的输入端,即编号 6 电路的输出端,是一个有效信号 1 时就会产生一个脉冲;如果输入端是无效信号就不会输出脉冲。
- 编号 8 是一个脉冲信号,就是产生事件的线路最终的产物,这个脉冲信号可以给其他外设电路使用,比如定时器 TIM、模拟数字转换器 ADC 等等。
EXTI初始化结构体
typedef struct {
uint32_t EXTI_Line; // 中断/事件线
EXTIMode_TypeDef EXTI_Mode; // EXTI 模式
EXTITrigger_TypeDef EXTI_Trigger; // 触发事件
FunctionalState EXTI_LineCmd; // EXTI 控制
} EXTI_InitTypeDef;
- EXTI_Line:EXTI中断/事件选择,EXTI0-EXTI22
- EXTI 模式选择,可选为产生中断 (EXTI_Mode_Interrupt) 或者产生事件(EXTI_Mode_Event)。
- EXTI_Trigger:EXTI 边沿触发事件,可选上升沿触发 (EXTI_Trigger_Rising)、下降沿触发 (EXTI_Trigger_Falling) 或者上升沿和下降沿都触发 ( EXTI_Trigger_Rising_Falling)。
- EXTI_LineCmd:控制是否使能 EXTI 线,可选使能 EXTI 线 (ENABLE) 或禁用 (DISABLE)
- 初始化 RGB 彩灯的 GPIO;
-
开启按键 GPIO 时钟和 SYSCFG 时钟;
/* 使能 SYSCFG 时钟 ,使用 GPIO 外部中断时必须使能 SYSCFG 时钟 */ RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);
-
配置 NVIC;
-
配置按键 GPIO 为输入模式;
-
将按键 GPIO 连接到 EXTI 源输入;
SYSCFG_EXTILineConfig(KEY1_INT_EXTI_PORTSOURCE,KEY1_INT_EXTI_PINSOURCE);
-
配置按键 EXTI 中断/事件线;
-
编写 EXTI 中断服务函数
void EXTI_Key_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
EXTI_InitTypeDef EXTI_InitStructure;
/* 开启按键 GPIO 口的时钟 */
RCC_AHB1PeriphClockCmd(KEY1_INT_GPIO_CLK|KEY2_INT_GPIO_CLK ,ENABLE);
/* 使能 SYSCFG 时钟 ,使用 GPIO 外部中断时必须使能 SYSCFG 时钟 */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);
/* 配置 NVIC */
NVIC_Configuration();
/* 选择按键 的引脚 */
GPIO_InitStructure.GPIO_Pin = KEY1_INT_GPIO_PIN;
/* 设置引脚为输入模式 */
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
/* 设置引脚不上拉也不下拉 */
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
/* 使用上面的结构体初始化按键 */
GPIO_Init(KEY1_INT_GPIO_PORT, &GPIO_InitStructure);
/* 连接 EXTI 中断源 到 key引脚 */
SYSCFG_EXTILineConfig(KEY1_INT_EXTI_PORTSOURCE,
KEY1_INT_EXTI_PINSOURCE);
/* 选择 EXTI 中断源 */
EXTI_InitStructure.EXTI_Line = KEY1_INT_EXTI_LINE;
/* 中断模式 */
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
/* 下降沿触发 */
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
/* 使能中断/事件线 */
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
/* 选择按键 的引脚 */
GPIO_InitStructure.GPIO_Pin = KEY2_INT_GPIO_PIN;
/* 其他配置与上面相同 */
GPIO_Init(KEY2_INT_GPIO_PORT, &GPIO_InitStructure);
/* 连接 EXTI 中断源 到 key引脚 */
SYSCFG_EXTILineConfig(KEY2_INT_EXTI_PORTSOURCE,
KEY2_INT_EXTI_PINSOURCE);
/* 选择 EXTI 中断源 */
EXTI_InitStructure.EXTI_Line = KEY2_INT_EXTI_LINE;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
/* 上升沿触发 */
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
}
3. SysTick系统定时器
SysTick—系统定时器是属于 CM4 内核中的一个外设,内嵌在 NVIC 中。系统定时器是一个 24bit的向下递减的计数器,计数器每计数次的时间为 1/SYSCLK,一般我们设置系统时钟 SYSCLK等于 180M。当重装载数值寄存器的值递减到 0 的时候,系统定时器就产生一次中断,以此循环往复。
因为 SysTick 是属于 CM4 内核的外设,所以所有基于 CM4 内核的单片机都具有这个系统定时器,使得软件在 CM4 单片机中可以很容易的移植。系统定时器一般用于操作系统,用于产生时基,维持操作系统的心跳。
SysTick配置函数在core_cm4.h中实现,只需要调用即可。
__STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks);
形参 ticks 用来设置重装载寄存器的值,最大不能超过重装载寄存器的值 2 24,当重装载寄存器的值递减到 0 的时候产生中断,然后重装载寄存器的值又重新装载往下递减计数,以此循环往复。紧随其后设置好中断优先级,最后配置系统定时器的时钟为 168M,使能定时器和定时器中断,这样系统定时器就配置好了
SysTick初始化函数,SysTick初始化函数由我们自己编写
/**
* @brief 启动系统滴答定时器 SysTick
* @param 无
* @retval 无
*/
void SysTick_Init(void)
{
/* SystemFrequency / 1000 1ms 中断一次
* SystemFrequency / 100000 10us 中断一次
* SystemFrequency / 1000000 1us 中断一次
*/
if (SysTick_Config(SystemCoreClock / 100000)) {
/* Capture error */
while (1);
}
}
SysTick中断时间计算
SystemFrequency为168M/100000是1680,SysTick是自上往下递减的,计数一次时间T = 1/CLKAHB,AHB为168M,1680/168M=10us,因此10us中断一次
示例程序:通过SysTick实现延时
static uint32_t TimingDelay = 0;
void SysTick_Handler(void)//SysTick中断服务函数
{
if (TimingDelay != 0x00)
TimingDelay--;
}
void Delay(__IO uint32_t nTime)
{
TimeingDelay = nTime;
while(TimingDelay != 0);
}