NVIC与中断控制

前言:本文章部分代码参考自野火的例程,优先级部分参考自Julius_world

本人使用的是野火家的指南者开发板,芯片型号是STM32f103VET6

分外部中断、串口和系统滴答定时器中断 三部分

串口部分在本栏目的另外一篇会重点讲解,此次只讲串口与EXTI类似的中断部分

有纰漏请指出,转载请说明。

学习交流请发邮件 1280253714@qq.com

本篇源代码在这里

EXTI中断

串口中断

sysTick中断

1 NVIC

1.1 NVIC是什么

内嵌向量中断控制器Nested Vectored Interrupt Controller (NVIC)

1.2 中断优先级配置的函数

NVIC_IRQChannelPreemptionPriority 配置抢占优先级

NVIC_IRQChannelSubPriority 配置响应优先级

1.3 抢占优先级与子优先级的描述

在CM3内核之外,STM32定义了自己配置优先级,即先分组,再配置抢占优先级和子优先级

  • 抢占优先级不同,会涉及到中断嵌套,抢占优先级高的会优先抢占优先级低的,优先得到执行。(注意:优先级数字越小,优先级越高)

  • 抢占优先级相同,不涉及到中断嵌套,子优先级不同,子优先级高的先响应。(例如:两个中断同时响应,这里就会先执行子优先级高的那个中断)

  • 抢占优先级和子优先级都相同,则比较它们的硬件中断编号,中断编号越小,优先级越高。(硬件中断编号从中断向量表当中查看)

NVIC 有一个专门的寄存器:中断优先级寄存器 NVIC_IPRx,用来配置外部中断的优先级,IPR 宽度为 8bit 但是STM32只用了4位来表达优先级,组别优先顺序(第0组优先级最强,第4组优先级最弱)

misc.h

/* 摘自stm32f10x.h */

typedef enum IRQn {
//Cortex-M3 处理器异常编号
NonMaskableInt_IRQn = -14,
MemoryManagement_IRQn = -12,
BusFault_IRQn = -11,
UsageFault_IRQn = -10,
SVCall_IRQn = -5,
DebugMonitor_IRQn = -4,
PendSV_IRQn = -2,
SysTick_IRQn = -1,
//STM32 外部中断编号
WWDG_IRQn = 0,
PVD_IRQn = 1,
TAMP_STAMP_IRQn = 2,

// 限于篇幅,中间部分代码省略,具体的可查看库文件 stm32f10x.h
DMA2_Channel2_IRQn = 57,
DMA2_Channel3_IRQn = 58,
DMA2_Channel4_5_IRQn = 59
} IRQn_Type;

1.4 NVIC相关函数

void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup);

void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct);

void NVIC_SetVectorTable(uint32_t NVIC_VectTab, uint32_t Offset);

void NVIC_SystemLPConfig(uint8_t LowPowerMode, FunctionalState NewState);

void SysTick_CLKSourceConfig(uint32_t SysTick_CLKSource);

1.5 NVIC初始化结构体

/* misc.h */

typedef struct{
  uint8_t NVIC_IRQChannel;                   
  uint8_t NVIC_IRQChannelPreemptionPriority;  
  uint8_t NVIC_IRQChannelSubPriority;        
  FunctionalState NVIC_IRQChannelCmd;         
} NVIC_InitTypeDef;

2 EXTI

2.1 EXTI是什么

EXTI(External interrupt/event controller)—外部中断/事件控制器

产生中断线路的目的是把输入信号输入到 NVIC,进一步会运行中断服务函数,实现功能,这样是 软件级的

而产生事件线路目的就是传输一个脉冲信号给其他外设使用,并且是电路级别的信号 传输,属于硬件级的。

2.2 EXTI初始化结构体

/* stm32f10x_exti.h */
typedef struct{
  uint32_t EXTI_Line;               
  EXTIMode_TypeDef EXTI_Mode;       
  EXTITrigger_TypeDef EXTI_Trigger; 
  FunctionalState EXTI_LineCmd;     
}EXTI_InitTypeDef;

3 用按键的电平跳变来产生中断

3.1 硬件配置

3.2 NVIC初始化

选择EXTI0_IRQn为NVIC的一个中断源,并配置优先级分组、抢占优先级、子优先级

void NVIC_Configuration(void){
  NVIC_InitTypeDef NVIC_InitStructure;
    
  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
  NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);
}

3.3 配置按键的GPIO口,用按键的电平跳变来产生中断

void KEY_GPIO_Config(void){
    GPIO_InitTypeDef GPIO_InitStructure;
    
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;    
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;   
    GPIO_Init(GPIOA, &GPIO_InitStructure);    
    
    GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0); 
}

3.4 EXTI初始化

void EXTI_Config(void){
    EXTI_InitTypeDef EXTI_InitStructure;
    EXTI_InitStructure.EXTI_Line = EXTI_Line0;
    EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
    EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
    EXTI_InitStructure.EXTI_LineCmd = ENABLE;
    EXTI_Init(&EXTI_InitStructure);
}

3.5 LED灯的GPIO口初始化

void LED_GPIO_Config(void){        
    GPIO_InitTypeDef GPIO_InitStructure;
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;    
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;   
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &GPIO_InitStructure);
}

3.6 编写中断服务函数

在这里编写

#include "stm32f10x_it.h"
#include "bsp_exti.h"

//省略
void EXTI0_IRQHandler(void)
{
    if(EXTI_GetITStatus(EXTI_Line0) != RESET) {
        GPIOB->ODR ^=0x01;
        EXTI_ClearITPendingBit(EXTI_Line0);   
    }  
}

3.7 main

#include "stm32f10x.h"  
#include "bsp_exti.h"

int main(void)
{
    NVIC_Configuration();
    KEY_GPIO_Config();
    EXTI_Config();
    LED_GPIO_Config();
    while(1);
}

3.8 实验现象

按下KEY1(PA0输出高电平),LED灯(PB0)会反转

4 串口发送完成产生中断

代码部分,NVIC初始化、LED灯的GPIO口初始化、main与上一小节类似,不再赘述

USART中断请求

这里只举TXNE(接受寄存器非空)中断这个例子,其他中断事件类似

4.1 硬件配置

4.2 USART初始化结构体

typedef struct {
uint32_t USART_BaudRate; // 波特率
uint16_t USART_WordLength; // 字长
uint16_t USART_StopBits; // 停止位
uint16_t USART_Parity; // 校验位
uint16_t USART_Mode; // USART 模式
uint16_t USART_HardwareFlowControl; // 硬件流控制
} USART_InitTypeDef;

4.3 串口配置

void USART_Config(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    USART_InitTypeDef USART_InitStructure;

    // 打开串口GPIO的时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    
    // 打开串口外设的时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);

    // 将USART Tx的GPIO配置为推挽复用模式
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    // 将USART Rx的GPIO配置为浮空输入模式
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    
    // 配置串口的工作参数
    USART_InitStructure.USART_BaudRate = 115200;
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;
    USART_InitStructure.USART_StopBits = USART_StopBits_1;
    USART_InitStructure.USART_Parity = USART_Parity_No ;
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
    USART_Init(USART1, &USART_InitStructure);
    
    // 串口中断优先级配置
    NVIC_Configuration();
    
    // 使能串口接收中断
    USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);    
    
    // 使能串口
    USART_Cmd(USART1, ENABLE);        
}

4.4 编写中断服务函数

void USART1_IRQHandler(void){
    if(USART_GetFlagStatus(USART1,USART_FLAG_RXNE) != RESET) {
        GPIOB->ODR ^=0x01;
        USART_ClearFlag(USART1,USART_FLAG_RXNE);   
    }  
}

4.5 通过MATLAB向stm32发送数据

stm32Serialport = serialport("COM5",115200,"Timeout",5)
write(stm32Serialport,1,"uint8");
delete(stm32Serialport)%注销系统之前已经打开的串口资源

4.6 实验现象

通过MATLAB向STM32发送数据,每发一次,灯反转一次

5 软件配置sysTick产生定时中断

5.1 SysTick寄存器

控制及状态寄存器 SysTick->CTRL

重装载值寄存器 SysTick->LOAD

当前值寄存器 SysTick->VAL

校准值寄存器 SysTick->CALIB

5.2 SysTick配置

// 这个 固件库函数 在 core_cm3.h中,只需调用即可
static __INLINE uint32_t SysTick_Config(uint32_t ticks)
{ 
  // reload 寄存器为24bit,最大值为2^24
    if (ticks > SysTick_LOAD_RELOAD_Msk)  return (1);
  
  // 配置 reload 寄存器的初始值    
  SysTick->LOAD  = (ticks & SysTick_LOAD_RELOAD_Msk) - 1;
    
    // 配置中断优先级为 1<<4-1 = 15,优先级为最低
  NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1); 
    
    // 配置 counter 计数器的值
  SysTick->VAL   = 0;
    
    // 配置systick 的时钟为 72M
    // 使能中断
    // 使能systick
  SysTick->CTRL  = SysTick_CTRL_CLKSOURCE_Msk | 
                   SysTick_CTRL_TICKINT_Msk   | 
                   SysTick_CTRL_ENABLE_Msk;                    
  return (0); 
}

5.3 编写中断服务函数

void SysTick_Handler(void){
    static uint16_t SysMsCount = 0;
    
    //每 1000ms 翻转一次 GPIOB GPIO_Pin_0 电平
    if(SysMsCount++ >= 1000){
        SysMsCount = 0;
    
        //1000ms 翻转一次电平
        GPIOB->ODR ^= ((uint32_t)0x01 << 0);
    }
}

5.4 main函数

#include "stm32f10x.h"  

int main(void)
{
    //LED_GPIO_Config    
    GPIO_InitTypeDef GPIO_InitStructure;
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;    
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;   
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &GPIO_InitStructure);

    /*
        uint32_t SystemCoreClock= SYSCLK_FREQ_72MHz; 
        配置 reload 寄存器的初始值为72M/1000 即每ms产生一次中断
    */
    SysTick_Config(SystemCoreClock/1000); 
       
    while(1);
}

5.5 实验现象

LED灯每隔1秒会反转一次,即一个闪烁周期为2秒

6 总结

中断配置,其实挺简单的

分为系统中断和外部中断

系统中断直接调用内核固件库函数配置中断优先级等(当然,core_cm3.h的函数也可以改),外部中断需要用户自己写NVIC_Configuration()函数,再去使能相应的中断

最后,不管是系统中断还是外部中断,都要在stm32f10x_it.h里写中断服务函数

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值