STM32从零开始(六)外部中断

先说本质

看不懂可以先跳过,看完后边就懂了。
NVIC就是嵌套向量中断控制器。说白了,就是控制一大堆中断的东西。比如外部中断,或者发生异常以后怎么处理的中断。

 NonMaskableInt_IRQn         = -14,    /*!< 2 Non Maskable Interrupt                             */
  MemoryManagement_IRQn       = -12,    /*!< 4 Cortex-M3 Memory Management Interrupt              */
  BusFault_IRQn               = -11,    /*!< 5 Cortex-M3 Bus Fault Interrupt                      */
  UsageFault_IRQn             = -10,    /*!< 6 Cortex-M3 Usage Fault Interrupt                    */
  SVCall_IRQn                 = -5,     /*!< 11 Cortex-M3 SV Call Interrupt                       */
  DebugMonitor_IRQn           = -4,     /*!< 12 Cortex-M3 Debug Monitor Interrupt                 */
  PendSV_IRQn                 = -2,     /*!< 14 Cortex-M3 Pend SV Interrupt                       */
  SysTick_IRQn                = -1,     /*!< 15 Cortex-M3 System Tick Interrupt                   */

/******  STM32 specific Interrupt Numbers *********************************************************/
  WWDG_IRQn                   = 0,      /*!< Window WatchDog Interrupt                            */
  PVD_IRQn                    = 1,      /*!< PVD through EXTI Line detection Interrupt            */
  TAMPER_IRQn                 = 2,      /*!< Tamper Interrupt                                     */
  RTC_IRQn                    = 3,      /*!< RTC global Interrupt                                 */
  FLASH_IRQn                  = 4,      /*!< FLASH global Interrupt                               */
  RCC_IRQn                    = 5,      /*!< RCC global Interrupt                                 */
  EXTI0_IRQn                  = 6,      /*!< EXTI Line0 Interrupt                                 */
  EXTI1_IRQn                  = 7,      /*!< EXTI Line1 Interrupt                                 */
  EXTI2_IRQn                  = 8,      /*!< EXTI Line2 Interrupt                                 */
  EXTI3_IRQn                  = 9,      /*!< EXTI Line3 Interrupt                                 */
  EXTI4_IRQn                  = 10,     /*!< EXTI Line4 Interrupt                                 */
  DMA1_Channel1_IRQn          = 11,     /*!< DMA1 Channel 1 global Interrupt                      */
  DMA1_Channel2_IRQn          = 12,     /*!< DMA1 Channel 2 global Interrupt                      */
  DMA1_Channel3_IRQn          = 13,     /*!< DMA1 Channel 3 global Interrupt                      */
  DMA1_Channel4_IRQn          = 14,     /*!< DMA1 Channel 4 global Interrupt                      */
  DMA1_Channel5_IRQn          = 15,     /*!< DMA1 Channel 5 global Interrupt                      */
  DMA1_Channel6_IRQn          = 16,     /*!< DMA1 Channel 6 global Interrupt                      */
  DMA1_Channel7_IRQn          = 17,     /*!< DMA1 Channel 7 global Interrupt                      */

里边有几个优先级为负数,高于普通中断优先级,且优先级不可配置。
我们之前说的优先级设置,抢占优先级啥的,说的就是NVIC里边的这些哪个优先级高哪个优先级低,除了外部中断还有很多,千万不要以为只是设置外部中断来了先处理哪个。比如我正在外部中断,突然来了个优先级更高的,我就先处理他。这就是中断嵌套

NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn; 

这句其实就是让你选择你在配置NVIC里边的哪个东西,配置好了以后才能进行下一步。外部中断0只是NVIC里边的一个。stm32的中断贼多

详细过程

其实配置就4步:

  1. 设置中断优先级分组。

NVIC_Configuration()这个函数是咱们自己写的,就是用来配置NVIC的。NVIC这个东西是写在core_cm3内核里的。所以stm32的手册里没咋详细写。还是老样子,我们要定义一个东西的话,就先定义一个关于他的结构体。人家已经给定义好了,在misc.h里边。这个文件主要放这些杂七杂八的

typedef struct
{
  uint8_t NVIC_IRQChannel;                    /*!< Specifies the IRQ channel to be enabled or disabled.
                                                   This parameter can be a value of @ref IRQn_Type 
                                                   (For the complete STM32 Devices IRQ Channels list, please
                                                    refer to stm32f10x.h file) */
//这个是设置你在配置哪个中断口。这里是配置的EXTI0中断,
  uint8_t NVIC_IRQChannelPreemptionPriority;  /*!< Specifies the pre-emption priority for the IRQ channel
                                                   specified in NVIC_IRQChannel. This parameter can be a value
                                                   between 0 and 15 as described in the table @ref NVIC_Priority_Table */
//这个是设置主优先级
  uint8_t NVIC_IRQChannelSubPriority;         /*!< Specifies the subpriority level for the IRQ channel specified
                                                   in NVIC_IRQChannel. This parameter can be a value
                                                   between 0 and 15 as described in the table @ref NVIC_Priority_Table */
//这个就是次优先级
  FunctionalState NVIC_IRQChannelCmd;         /*!< Specifies whether the IRQ channel defined in NVIC_IRQChannel
                                                   will be enabled or disabled. 
                                                   This parameter can be set either to ENABLE or DISABLE */   
//这个是使能的,你要用了就把他配置成1,就可以用了。
} NVIC_InitTypeDef;

我们看具体的步骤:

void NVIC_Configuration(void)
{
  NVIC_InitTypeDef NVIC_InitStructure;
  


  /* Set the Vector Table base location at 0x08000000 */ 
  NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x0);   
//这一句是设置你的代码从哪启动,是从ram还是flash
  /* Configure one bit for preemption priority */
  // 2个抢占优先级
  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);  
  //这个是设置抢占优先级的,看上一篇文章就行了
  /* Enable the EXTI0 Interrupt */
  //开始配置
  NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn; 
  //外部中断线0
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority =  0;  //抢占优先级0(最高)
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;//次优先级(最高)
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;  
  //使能
  NVIC_Init(&NVIC_InitStructure);
  //初始化
}

就是这么简单

  1. 配置时钟和GPIO

这个是老生常谈了,很简单就两个函数,一个rcc,一个gpio。
注意,如果a0做了中断,那么bcdef的0就不行,因为只能有一个。

void RCC_Configuration(void)
{
	// 起始代码中已经调用SystemInit将主时钟设置为72M
	// 所以这里RCC就直接使能时钟就行了
  //gpioa0接按键,gpiob1接灯
  /* Enable Key Button GPIO Port, GPIO_LED and AFIO clock */
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA |RCC_APB2Periph_GPIOB| RCC_APB2Periph_AFIO, ENABLE);
}

void GPIO_Configuration(void)
{
  GPIO_InitTypeDef GPIO_InitStructure;
  //gpioa0接按键,gpiob1接灯
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;       //推挽输出
  GPIO_Init(GPIOB, &GPIO_InitStructure);
  GPIO_WriteBit(GPIOB, GPIO_Pin_1, Bit_SET);			// 默认输出0让LED亮

  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;	  //配置浮空输入
  GPIO_Init(GPIOA, &GPIO_InitStructure);
}

GPIO的配置,点灯的那个设置成推挽输出,中断的那个设置成浮空输入

  1. 配置中断

我是在主函数里配置的,配置完直接用。所以先调用这三句,让别的工作都准备好

  RCC_Configuration(); 
 
  NVIC_Configuration();	
    
  GPIO_Configuration(); 

一样的套路,先配置结构体,然后直接把他放进初始化函数里就行

int main(void)
{
RCC_Configuration(); 
NVIC_Configuration();	
GPIO_Configuration(); 



	GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource0);
	//把哪个口的哪个线连接起来,意思就是在中断线中选一根给他连起来
	EXTI_InitStructure.EXTI_Line = EXTI_Line0;
	//选定0号线
	EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
	//中断模式
	EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
	//下降沿 
	EXTI_InitStructure.EXTI_LineCmd = ENABLE; 
	//使能
  EXTI_Init(&EXTI_InitStructure);
  //初始化
	EXTI_GenerateSWInterrupt(EXTI_Line0);
	//人工设置出一次中断,主要为了试一试。
	while(1);
	return 0;
}
  1. 最重要的一步来了。大家可能好奇,中断处理在哪呢?怎么在exti和msic里边都没有。这个其实在STM32从零开始(六)外部中断_第1张图片

这里。这个文件相当于是自己创建的,因为在startup里边他定义了一堆中断向量指向的中断处理程序。但是里边啥也没有,就一个死循环。我们用到哪个中断处理程序,就在it这个文件里自己写一个函数,把他之前的就相当于自动覆盖掉了

*主中断服务例程。
*此文件为所有异常处理程序和提供模板
外围设备中断服务程序。

大家明白了吧,这个文件是没有的,得你自己写。不过一般网上都有模板,套就完了。官方例程里也有。

void EXTI0_IRQHandler(void)
{
	if(EXTI_GetITStatus(EXTI_Line0) != RESET)
	{
		GPIO_WriteBit(GPIOB, GPIO_Pin_1, (BitAction)((1-GPIO_ReadOutputDataBit(GPIOB, GPIO_Pin_1))));
		EXTI_ClearITPendingBit(EXTI_Line0);
		//delayInterrupt();
	}
}

我在里边写了个外部中断0的处理程序。很简单,就是先检测一下中断是否真的发生,也就是中断标志位是否挂起,然后把灯口取反,让他达到相反的状态,最后清除标志位。一定要记得清除,不然他的感觉就是一直在中断。

就这四步。非常简单。四步走即可。
————————————————————————————

注意事项

1.一个中断线只能有一个中断口,意思就是如果你A0接的中断,那么你B0啥的就不能接中断,没意义。想接搞到B1上去。
2.这么多各种各样的函数名别写错
3.最后几个中断线系统内部用了,咱们别用。

你可能感兴趣的:(c,嵌入式)