赞
踩
NVIC是嵌套向量中断控制器,控制整一个芯片的中断功能,它是属于内核的一个外设。STM32中的NVIC是对内核的NVIC进行裁剪的,相当于NVIC中的一个子集。
typedef struct { __IO uint32_t ISER[8]; // Offset: 0x000 中断使能寄存器 uint32_t RESERVED0[24]; __IO uint32_t ICER[8]; //Offset: 0x080 中断清除寄存器 uint32_t RSERVED1[24]; __IO uint32_t ISPR[8]; // Offset: 0x100 中断使能悬起寄存器 uint32_t RESERVED2[24]; __IO uint32_t ICPR[8]; // Offset: 0x180 中断清除悬起寄存器 uint32_t RESERVED3[24]; __IO uint32_t IABR[8]; // Offset: 0x200 中断有效位寄存器 uint32_t RESERVED4[56]; __IO uint8_t IP[240]; // Offset: 0x300 中断优先级寄存器 (8Bit) uint32_t RESERVED5[644]; __O uint32_t STIR; // Offset: 0xE00 软件触发中断寄存器 } NVIC_Type;
在中断编程之前,先配置中断优先级的分组,设置抢占优先级和子优先级,抢占优先级高于子优先级,数字小的优先级高,当抢占优先级一样时,在比较子优先级,当子优先级相同时,比较硬件中断向量表。在NVIC中,一共有五个分组,分别是:
优先级分组 | 主优先级 | 子优先级 | 描述 |
---|---|---|---|
NVIC_PriorityGroup_0 | 0 | 0-15 | 主-0bit 子-4bit |
NVIC_PriorityGroup_1 | 0-1 | 0-7 | 主-1bit 子-3bit |
NVIC_PriorityGroup_2 | 0-3 | 0-3 | 主-2bit 子-2bit |
NVIC_PriorityGroup_3 | 0-7 | 0-1 | 主-3bit 子-1bit |
NVIC_PriorityGroup_4 | 0-15 | 0 | 主-4bit 子-0bit |
固件库函数:
/** * @brief Configures the priority grouping: pre-emption priority and subpriority. * (配置优先级分组:抢占优先级和子优先级) * @param NVIC_PriorityGroup: specifies the priority grouping bits length. * (参数:选择特定的优先级分组) * This parameter can be one of the following values: * (这个参数可以是以下值中的一个) * @arg NVIC_PriorityGroup_0: 0 bits for pre-emption priority * 4 bits for subpriority * (0个抢占优先级,4个子优先级) * @arg NVIC_PriorityGroup_1: 1 bits for pre-emption priority * 3 bits for subpriority * @arg NVIC_PriorityGroup_2: 2 bits for pre-emption priority * 2 bits for subpriority * @arg NVIC_PriorityGroup_3: 3 bits for pre-emption priority * 1 bits for subpriority * @arg NVIC_PriorityGroup_4: 4 bits for pre-emption priority * 0 bits for subpriority * @retval None */ void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup) { // 设置优先级分组,NVIC的分组主要是内核外设的寄存器SCB控制 SCB->AIRCR = AIRCR_VECTKEY_MASK | NVIC_PriorityGroup; }
typedef struct
{
uint8_t NVIC_IRQChannel; // 指定中断源,不同的中断中断源不一样,这个参数的值参考stm32f10x.h 头文件里面的IRQn_Type结构体的定义,里面包含了完整的中断源
uint8_t NVIC_IRQChannelPreemptionPriority; //设置抢占优先级,这个参数具体的值由优先级分组来确定
uint8_t NVIC_IRQChannelSubPriority; //设置抢占优先级,这个参数具体的值由优先级分组来确定
FunctionalState NVIC_IRQChannelCmd; //设置中断使能或者失能
} NVIC_InitTypeDef;
每个中断都有对应的中断服务函数,中断服务函数在启动文件 startup_stm32f10x_hd.s预先已经写好,目的是为了初始化中断向量表。因此,每一个中断函数的函数名必须与启动文件预先写好的函数名一致,不一致将会导致系统在中断向量表中找不到中断服务函数的入口,直接跳入到了预先写好的空函数,在里面无限循环,实现不了中断。
EXTI外部中断/事件控制器,管理着20个中断/事件线,每一个都对应着一个边沿检测器,用来实现输入信号的上升沿检测和下降沿检测。EXTI能够对每一个中断或者事件进行单独的配置,即能够单独配置为中断或者事件,以及触发事件的属性
EXTI可分为两个部分的功能,一个是产生中断,一个是产生事件。
输入线:EXTI控制器有19个中断/事件输入线(互联型是20个),这些输入线通过寄存器设置成任意的GPIO,和一些外设事件。输入线得到的是电平变化的信号。
接下来进入到边沿检测电路:它能够根据上升沿触发选择寄存(EXTI_RTSR) 和下降沿触发选择寄存器 (EXTI_FTSR) 对应位的设置来控制信号触发,有效输出1,无效则输出0。可以是只有上升沿触发,只有下降沿触发,或者上升沿和下降沿都触发。
之后经过或门电路,一个输入是边沿检测电路出来的信号,另外一个是来自软件中断事件寄存器,两个输入中只要有一个输出1,结果都为1.
上面与门:与中断屏蔽器(EXTI_IMR)一起输出。与门电路要求输入都为 1 才输出 1,因此当中断屏蔽器为零,则停止中断。通过中断屏蔽器去实现是否产生中断。当中断屏蔽器为1时,电路的输出信号会被保存到挂起寄存器 (EXTI_PR) 内,如果确定最终电路输出为 1 就会把 EXTI_PR 对应位置置1。然后将EXTI_PR寄存器内容输出到NVIC内,从而实现系统中断事件控制。
下面与门:一个输入来自前面的信号,另一个输入来自事件屏蔽寄存器 (EXTI_EMR)。如果EXTI_EMR设置为0时,那不管前面的输入信号,结果都为0,起到了屏蔽事件的作用。
脉冲发生器电路:输入端输入信号为1,即输入有效,产生一个脉冲,如果输入端是无效信号(为0),则不输出脉冲。这个脉冲信号可以给其他外设电路使用,比如定时器TIM、模拟数字转换器ADC等。
产生中断线路目的是把输入信号输入到 NVIC,进一步会运行中断服务函数,实现功能,这样是软件级的。而产生事件线路目的就是传输一个脉冲信号给其他外设使用,并且是电路级别的信号传输属于硬件级的。
程序内部,部分外设使用是复用IO口,便可以开该复用时钟。在STM32F103VET6中USART3的IO口为TX/PB10,RX/PB11。假若你要是将给外设IO口映射到(TX/PC10,RX/PC11)或者(TX/PD8,RX/PD9),便需要开启AFIO时钟即可正常工作。
而GPIO的初始功能没有中断功能,因此需要用到端口的映射。AFIO中EXTI0 可以通过 AFIO 的外部中断配置寄存器 1(AFIO_EXTICR1) 的 EXTI0[3:0] 位选择配置为 PA0、PB0、PC0、PD0、PE0、PF0、PG0、PH0 或者 PI0。其他EXTI中断线同样的配置。一共有4个外部中断寄存器去实现GPIO映射到外部中断。
typedef struct { 2 uint32_t EXTI_Line; // 中断/事件线
EXTIMode_TypeDef EXTI_Mode; // EXTI 模式
EXTITrigger_TypeDef EXTI_Trigger; // 触发类型
FunctionalState EXTI_LineCmd; // EXTI 使能
} EXTI_InitTypeDef;
EXTI_Line:EXTI 中断/事件线选择,可选 EXTI0 至 EXTI19
EXTI_Mode: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)。
static void NVIC_Configuration(void) { NVIC_InitTypeDef NVIC_InitStructure; /* 配置NVIC为优先级组1 */ NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1); /* 配置中断源:按键 */ NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn; /* 配置抢占优先级 */ NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; /* 配置子优先级 */ NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; /* 使能中断通道 */ NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); } void EXTI_Key_Config(void) { GPIO_InitTypeDef GPIO_InitStructure; EXTI_InitTypeDef EXTI_InitStructure; /*开启按键GPIO口的时钟和AFIO端口复用*/ RCC_APB2PeriphClockCmd((RCC_APB2Periph_GPIOA|RCC_APB2Periph_AFIO),ENABLE); /* 配置 NVIC 中断*/ NVIC_Configuration(); /* 选择按键用到的GPIO */ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; /* 配置为浮空输入 */ GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOA, &GPIO_InitStructure); /* 选择EXTI的信号源 */ GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0); EXTI_InitStructure.EXTI_Line = EXTI_Line0; /* EXTI为中断模式 */ EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; /* 上升沿中断 */ EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising; /* 使能中断 */ EXTI_InitStructure.EXTI_LineCmd = ENABLE; EXTI_Init(&EXTI_InitStructure); } void LED_GPIO_Config(void) { /*定义一个GPIO_InitTypeDef类型的结构体*/ GPIO_InitTypeDef GPIO_InitStructure; /*开启LED相关的GPIO外设时钟*/ RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); /*选择要控制的GPIO引脚*/ GPIO_InitStructure.GPIO_Pin =GPIO_Pin_5; /*设置引脚模式为通用推挽输出*/ GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; /*设置引脚速率为50MHz */ GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; /*调用库函数,初始化GPIO*/ GPIO_Init(GPIOB, &GPIO_InitStructure); GPIO_SetBits(GPIOB, GPIO_Pin_5); } void KEY1_IRQHandler(void) { //确保是否产生了EXTI Line中断 if(EXTI_GetITStatus(KEY1_INT_EXTI_LINE) != RESET) { // LED 取反 GPIOB->ODR ^= GPIO_Pin_5; //清除中断标志位 EXTI_ClearITPendingBit(KEY1_INT_EXTI_LINE); } } int main(void) { /* LED 端口初始化 */ LED_GPIO_Config(); EXTI_Key_Config(); /* 等待中断,由于使用中断方式,CPU不用轮询按键 */ while(1) { } }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。