• 【STM32】STM32中断体系


    注意:

    我这里是标准库和HAL都混合一起使用,所以可能会有点混乱!!!!!!!!!!

    本文章的参考:

    NVIC:https://blog.51cto.com/wangjunlv/6021164?articleABtest=1

    中断初始化基本流程

    1. /**
    2. * 函 数:计数传感器初始化
    3. * 参 数:无
    4. * 返 回 值:无
    5. */
    6. void CountSensor_Init(void)
    7. {
    8. /*开启时钟*/
    9. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //开启GPIOB的时钟
    10. RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); //开启AFIO的时钟,外部中断必须开启AFIO的时钟
    11. /*GPIO初始化*/
    12. GPIO_InitTypeDef GPIO_InitStructure;
    13. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
    14. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;
    15. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    16. GPIO_Init(GPIOB, &GPIO_InitStructure); //将PB14引脚初始化为上拉输入
    17. /*AFIO选择中断引脚*/
    18. GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource14);//将外部中断的14号线映射到GPIOB,即选择PB14为外部中断引脚
    19. /*EXTI初始化*/
    20. EXTI_InitTypeDef EXTI_InitStructure; //定义结构体变量
    21. EXTI_InitStructure.EXTI_Line = EXTI_Line14; //选择配置外部中断的14号线
    22. EXTI_InitStructure.EXTI_LineCmd = ENABLE; //指定外部中断线使能
    23. EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; //指定外部中断线为中断模式
    24. EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; //指定外部中断线为下降沿触发
    25. EXTI_Init(&EXTI_InitStructure); //将结构体变量交给EXTI_Init,配置EXTI外设
    26. /*NVIC中断分组*/
    27. NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //配置NVIC为分组2
    28. //即抢占优先级范围:0~3,响应优先级范围:0~3
    29. //此分组配置在整个工程中仅需调用一次
    30. //若有多个中断,可以把此代码放在main函数内,while循环之前
    31. //若调用多次配置分组的代码,则后执行的配置会覆盖先执行的配置
    32. /*NVIC配置*/
    33. NVIC_InitTypeDef NVIC_InitStructure; //定义结构体变量
    34. NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn; //选择配置NVIC的EXTI15_10线
    35. NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //指定NVIC线路使能
    36. NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; //指定NVIC线路的抢占优先级为1
    37. NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //指定NVIC线路的响应优先级为1
    38. NVIC_Init(&NVIC_InitStructure); //将结构体变量交给NVIC_Init,配置NVIC外设
    39. }

    零:什么是中断

    1.中断的概念

    2.中断的作用和意义

    3.STM32 GPIO外部中断(EXIT)简图

    一、STM32的NVIC和起始代码中的ISP

    1.NVIC(嵌套向量中断控制器)

    (1)数据手册中相关部分浏览
    (2)地址映射时0地址映射到Flash或SRAM
    (3)中断向量表可以被人为重新映射,一般用来IAP中
    (4)STM32采用一维的中断向量表
    (5)中断优先级设置有点复杂,后面细说

    1.特征

    2.系统嘀嗒(SysTick)校准值寄存器

    3.中断向量表

    在每一个中断函数在startup_stm32f10x_md.s中都有进行定义

    2、起始代码中的ISR

    (1)其实代码中定义了一个Vector数组
    (2)WEAK声明的默认ISR
    (3)用户根据需要提供自己真正有用的ISR
    (4)中断要配置使能,ISR中要清挂起等,这一点和其他CPU一样

    3.NVIC工作原理

    二、STM32的外部中断(EXTI)

    EXIT处理和唤醒外部或者内部的设备中断

    EXTI是属于边沿触发。所以要指定是上升沿触发还是下降沿触发。

    1.EXTI 控制器的主要特性如下:

    2.外部中断/事件控制器框图

    3.外部中断(EXTI)与I/O 映像(AFIO的的中断)

    AFIO中与中断相关的寄存器有4个

    记得再使用之前一定要使能时钟!!!!!

    我们有4个AFIO的中断寄存器,则一个寄存器中应该可以处理16个引脚。

    最好使用引脚时,设置PA0,PA1....

    PA1和PB1和PC1不能同时使用

    使用AFIO中的EXTICRx寄存器进行操作

    1. /**
    2. * @brief Selects the GPIO pin used as EXTI Line.
    3. * @param GPIO_PortSource: selects the GPIO port to be used as source for EXTI lines.
    4. * This parameter can be GPIO_PortSourceGPIOx where x can be (A..G).
    5. * @param GPIO_PinSource: specifies the EXTI line to be configured.
    6. * This parameter can be GPIO_PinSourcex where x can be (0..15).
    7. * @retval None
    8. */
    9. void GPIO_EXTILineConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource)
    10. {
    11. uint32_t tmp = 0x00;
    12. /* Check the parameters */
    13. assert_param(IS_GPIO_EXTI_PORT_SOURCE(GPIO_PortSource));
    14. assert_param(IS_GPIO_PIN_SOURCE(GPIO_PinSource));
    15. tmp = ((uint32_t)0x0F) << (0x04 * (GPIO_PinSource & (uint8_t)0x03));
    16. AFIO->EXTICR[GPIO_PinSource >> 0x02] &= ~tmp;
    17. AFIO->EXTICR[GPIO_PinSource >> 0x02] |= (((uint32_t)GPIO_PortSource) << (0x04 * (GPIO_PinSource & (uint8_t)0x03)));
    18. }

    1.外部中断配置寄存器 1(AFIO_EXTICR1)

    2.外部中断配置寄存器 2(AFIO_EXTICR2)

    4.如何实际编程使用外部中断

    (1)时钟设置并打开相应GPIO模块时钟
    (2)将相应GPIO配置为浮空输入
    (3)NVIC设置
    (4)将外部中断线和配套的GPIO进行连接映射


    (5)外部中断线使能触发
    (6)准备好ISR,并在ISR处等待执行中断程序即可

    (7)记得!!!!!!!!!执行完ISR后记得将中断清除。

    5.外部中断 VS 外部事件中断

    外部中断:需要CPU,中断处理程序的参与---》使用NVIC

    外部事件中断:不需要CPU参与---》不使用NVIC

    我们一般使用外部中断,不使用外部事件中断,因为我们不知道该器件是否接受脉冲信号。

    6.相关寄存器

    1. 上升沿触发选择寄存器(EXTI_RTSR)

    2.下降沿触发选择寄存器(EXTI_FTSR)

    3.中断屏蔽寄存器(EXTI_IMR)

    4.挂起寄存器(EXTI_PR)

    7. 中断的使用(重点)

    8.HAL库中断回调处理机制介绍·

    三、标准库中NVIC(中断优先级)模块分析

    因为中断处理是在CPU内部进行的,所以没有单独的.c和.h文件

    NVIC在【misc.c】中

    1.抢占优先级(pre) VS 响应优先级(sub)

    当抢占优先级的不同时,抢占优先级小的先执行,然后在执行优先级大的。

    当抢占优先级相同时,次优先级小的不能抢占次优先级大的

    当相同抢占优先级的程序进行中断时,次优先级小的可以抢先执行,在执行次优先级大的。

    1. //PreemptionPriority:抢优先级
    2. uint8_t NVIC_IRQChannelPreemptionPriority; /*!< Specifies the pre-emption priority for the IRQ channel
    3. specified in NVIC_IRQChannel. This parameter can be a value
    4. between 0 and 15 as described in the table @ref NVIC_Priority_Table */
    5. //次优先级【同一个抢占级别中进行判断的】
    6. uint8_t NVIC_IRQChannelSubPriority; /*!< Specifies the subpriority level for the IRQ channel specified
    7. in NVIC_IRQChannel. This parameter can be a value
    8. between 0 and 15 as described in the table @ref NVIC_Priority_Table */

    2.中断优先级分组

    2.优先级组

    1. /**
    2. pre-emption:抢占优先级
    3. subpriority:次优先级
    4. */
    5. #define NVIC_PriorityGroup_0 ((uint32_t)0x700) /*!< 0 bits for pre-emption priority
    6. 4 bits for subpriority=》2的4次方*/
    7. #define NVIC_PriorityGroup_1 ((uint32_t)0x600) /*!< 1 bits for pre-emption priority
    8. 3 bits for subpriority=>2的3次方*/
    9. #define NVIC_PriorityGroup_2 ((uint32_t)0x500) /*!< 2 bits for pre-emption priority
    10. 2 bits for subpriority */
    11. #define NVIC_PriorityGroup_3 ((uint32_t)0x400) /*!< 3 bits for pre-emption priority
    12. 1 bits for subpriority */
    13. #define NVIC_PriorityGroup_4 ((uint32_t)0x300) /*!< 4 bits for pre-emption priority
    14. 0 bits for subpriority */

    3.启动方式

    RAM或者FLASH

    1. /**
    2. 启动方式:RAM或者FLASH
    3. 从0x0000 0000到0x080 0000之间的地址是用来设置映射关系
    4. 因为我们使用FLASH启动,所以将其映射到0x0800 0000
    5. */
    6. #define NVIC_VectTab_RAM ((uint32_t)0x20000000)
    7. #define NVIC_VectTab_FLASH ((uint32_t)0x08000000)
    8. #define IS_NVIC_VECTTAB(VECTTAB) (((VECTTAB) == NVIC_VectTab_RAM) || \
    9. ((VECTTAB) == NVIC_VectTab_FLASH))

    4.NVIC的使用

    注意点:

    再HAL库中,我们中断分组已经再HAL_Init()函数中设置了分组为2【因为分组2中,有4个抢占优先级,有4个响应优先级】

    5.函数

    下面这些函数都定义再stm32f1xx_hal_cortex中

    因为NVIC属于内核部分的内容,所以如果我们要查看相应的寄存器,只能去Cortex-M数据手册中进行查看。

    1.NVIC_PriorityGroupConfig:设置中断分组

    设置优先级分组字段(抢占优先级和子优先级)

    1. /**
    2. * @brief Configures the priority grouping: pre-emption priority and subpriority.
    3. * @param NVIC_PriorityGroup: specifies the priority grouping bits length.
    4. * This parameter can be one of the following values:
    5. * @arg NVIC_PriorityGroup_0: 0 bits for pre-emption priority
    6. * 4 bits for subpriority
    7. * @arg NVIC_PriorityGroup_1: 1 bits for pre-emption priority
    8. * 3 bits for subpriority
    9. * @arg NVIC_PriorityGroup_2: 2 bits for pre-emption priority
    10. * 2 bits for subpriority
    11. * @arg NVIC_PriorityGroup_3: 3 bits for pre-emption priority
    12. * 1 bits for subpriority
    13. * @arg NVIC_PriorityGroup_4: 4 bits for pre-emption priority
    14. * 0 bits for subpriority
    15. * @retval None
    16. */
    17. void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup)
    18. {
    19. /* Check the parameters */
    20. assert_param(IS_NVIC_PRIORITY_GROUP(NVIC_PriorityGroup));
    21. /* Set the PRIGROUP[10:8] bits according to NVIC_PriorityGroup value */
    22. SCB->AIRCR = AIRCR_VECTKEY_MASK | NVIC_PriorityGroup;
    23. }

    2.HAL_NVIC_SetPriority:优先级设置

    对抢占优先级和响应优先级的相关设置

    1. void HAL_NVIC_SetPriority(IRQn_Type IRQn, uint32_t PreemptPriority, uint32_t SubPriority)
    2. {
    3. uint32_t prioritygroup = 0x00U;
    4. /* Check the parameters */
    5. assert_param(IS_NVIC_SUB_PRIORITY(SubPriority));
    6. assert_param(IS_NVIC_PREEMPTION_PRIORITY(PreemptPriority));
    7. prioritygroup = NVIC_GetPriorityGrouping();
    8. NVIC_SetPriority(IRQn, NVIC_EncodePriority(prioritygroup, PreemptPriority, SubPriority));
    9. }

    3.NVIC_Init

    1. void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct)
    2. {
    3. uint32_t tmppriority = 0x00, tmppre = 0x00, tmpsub = 0x0F;
    4. /* Check the parameters */
    5. assert_param(IS_FUNCTIONAL_STATE(NVIC_InitStruct->NVIC_IRQChannelCmd));
    6. assert_param(IS_NVIC_PREEMPTION_PRIORITY(NVIC_InitStruct->NVIC_IRQChannelPreemptionPriority));
    7. assert_param(IS_NVIC_SUB_PRIORITY(NVIC_InitStruct->NVIC_IRQChannelSubPriority));
    8. if (NVIC_InitStruct->NVIC_IRQChannelCmd != DISABLE)
    9. {
    10. /* Compute the Corresponding IRQ Priority --------------------------------*/
    11. tmppriority = (0x700 - ((SCB->AIRCR) & (uint32_t)0x700))>> 0x08;
    12. tmppre = (0x4 - tmppriority);
    13. tmpsub = tmpsub >> tmppriority;
    14. tmppriority = (uint32_t)NVIC_InitStruct->NVIC_IRQChannelPreemptionPriority << tmppre;
    15. tmppriority |= NVIC_InitStruct->NVIC_IRQChannelSubPriority & tmpsub;
    16. tmppriority = tmppriority << 0x04;
    17. NVIC->IP[NVIC_InitStruct->NVIC_IRQChannel] = tmppriority;
    18. /* Enable the Selected IRQ Channels --------------------------------------*/
    19. NVIC->ISER[NVIC_InitStruct->NVIC_IRQChannel >> 0x05] =
    20. (uint32_t)0x01 << (NVIC_InitStruct->NVIC_IRQChannel & (uint8_t)0x1F);
    21. }
    22. else
    23. {
    24. /* Disable the Selected IRQ Channels -------------------------------------*/
    25. NVIC->ICER[NVIC_InitStruct->NVIC_IRQChannel >> 0x05] =
    26. (uint32_t)0x01 << (NVIC_InitStruct->NVIC_IRQChannel & (uint8_t)0x1F);
    27. }
    28. }

    4.NVIC_SetVectorTable

    设置中断向量表,看要设置到RAM还是FLASH

    1. void NVIC_SetVectorTable(uint32_t NVIC_VectTab, uint32_t Offset)
    2. {
    3. /* Check the parameters */
    4. assert_param(IS_NVIC_VECTTAB(NVIC_VectTab));
    5. assert_param(IS_NVIC_OFFSET(Offset));
    6. SCB->VTOR = NVIC_VectTab | (Offset & (uint32_t)0x1FFFFF80);
    7. }

    5.  HAL_NVIC_EnableIRQ

    使能NVIC

    1. void HAL_NVIC_EnableIRQ(IRQn_Type IRQn)
    2. {
    3. /* Check the parameters */
    4. assert_param(IS_NVIC_DEVICE_IRQ(IRQn));
    5. /* Enable interrupt */
    6. NVIC_EnableIRQ(IRQn);
    7. }

    四、标准库中外部中断模块分析

    1.EXTI_DeInit

    1. //完全去始化
    2. void EXTI_DeInit(void)
    3. {
    4. EXTI->IMR = 0x00000000;
    5. EXTI->EMR = 0x00000000;
    6. EXTI->RTSR = 0x00000000;
    7. EXTI->FTSR = 0x00000000;
    8. EXTI->PR = 0x000FFFFF;
    9. }

    2.EXTI_Init

    将我们设置好的结构体变量写入到寄存器中

    1. /**
    2. * @brief Initializes the EXTI peripheral according to the specified
    3. * parameters in the EXTI_InitStruct.
    4. * @param EXTI_InitStruct: pointer to a EXTI_InitTypeDef structure
    5. * that contains the configuration information for the EXTI peripheral.
    6. * @retval None
    7. */
    8. void EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct)
    9. {
    10. uint32_t tmp = 0;
    11. /* Check the parameters */
    12. assert_param(IS_EXTI_MODE(EXTI_InitStruct->EXTI_Mode));
    13. assert_param(IS_EXTI_TRIGGER(EXTI_InitStruct->EXTI_Trigger));
    14. assert_param(IS_EXTI_LINE(EXTI_InitStruct->EXTI_Line));
    15. assert_param(IS_FUNCTIONAL_STATE(EXTI_InitStruct->EXTI_LineCmd));
    16. tmp = (uint32_t)EXTI_BASE;
    17. if (EXTI_InitStruct->EXTI_LineCmd != DISABLE)//表示要使能
    18. {
    19. /* Clear EXTI line configuration */
    20. EXTI->IMR &= ~EXTI_InitStruct->EXTI_Line;
    21. EXTI->EMR &= ~EXTI_InitStruct->EXTI_Line;
    22. tmp += EXTI_InitStruct->EXTI_Mode;
    23. *(__IO uint32_t *) tmp |= EXTI_InitStruct->EXTI_Line;
    24. /* Clear Rising Falling edge configuration */
    25. EXTI->RTSR &= ~EXTI_InitStruct->EXTI_Line;
    26. EXTI->FTSR &= ~EXTI_InitStruct->EXTI_Line;
    27. /* Select the trigger for the selected external interrupts */
    28. if (EXTI_InitStruct->EXTI_Trigger == EXTI_Trigger_Rising_Falling)
    29. {
    30. /* Rising Falling edge */
    31. EXTI->RTSR |= EXTI_InitStruct->EXTI_Line;
    32. EXTI->FTSR |= EXTI_InitStruct->EXTI_Line;
    33. }
    34. else
    35. {
    36. tmp = (uint32_t)EXTI_BASE;
    37. tmp += EXTI_InitStruct->EXTI_Trigger;
    38. *(__IO uint32_t *) tmp |= EXTI_InitStruct->EXTI_Line;
    39. }
    40. }
    41. else
    42. {
    43. tmp += EXTI_InitStruct->EXTI_Mode;
    44. /* Disable the selected external lines */
    45. *(__IO uint32_t *) tmp &= ~EXTI_InitStruct->EXTI_Line;
    46. }
    47. }

    3.EXTI_StructInit

    如果用户不想自己设置中断值,则可以调用这个函数,这个函数中有默认值可以使用

    1. void EXTI_StructInit(EXTI_InitTypeDef* EXTI_InitStruct)
    2. {
    3. EXTI_InitStruct->EXTI_Line = EXTI_LINENONE;
    4. EXTI_InitStruct->EXTI_Mode = EXTI_Mode_Interrupt;
    5. EXTI_InitStruct->EXTI_Trigger = EXTI_Trigger_Falling;
    6. EXTI_InitStruct->EXTI_LineCmd = DISABLE;
    7. }

    4.EXTI_GenerateSWInterrupt

    软件中断,可以对寄存器中其中一位进行中断操作

    1. void EXTI_GenerateSWInterrupt(uint32_t EXTI_Line)
    2. {
    3. /* Check the parameters */
    4. assert_param(IS_EXTI_LINE(EXTI_Line));
    5. EXTI->SWIER |= EXTI_Line;
    6. }

    5.EXTI_GetFlagStatus

    共享中断:判断中断标志位是否真的置1了(判断是哪一个标志进行中断的)

    1. /**
    2. * @brief Checks whether the specified EXTI line flag is set or not.
    3. * @param EXTI_Line: specifies the EXTI line flag to check.
    4. * This parameter can be:
    5. * @arg EXTI_Linex: External interrupt line x where x(0..19)
    6. * @retval The new state of EXTI_Line (SET or RESET).
    7. */
    8. FlagStatus EXTI_GetFlagStatus(uint32_t EXTI_Line)
    9. {
    10. FlagStatus bitstatus = RESET;
    11. /* Check the parameters */
    12. assert_param(IS_GET_EXTI_LINE(EXTI_Line));
    13. if ((EXTI->PR & EXTI_Line) != (uint32_t)RESET)
    14. {
    15. bitstatus = SET;
    16. }
    17. else
    18. {
    19. bitstatus = RESET;
    20. }
    21. return bitstatus;
    22. }

    6.EXTI_ClearITPendingBit(中断挂起)

    中断进入后,就会有一个中断挂起

    1. /**
    2. * @brief Clears the EXTI's line pending bits.
    3. * @param EXTI_Line: specifies the EXTI lines to clear.
    4. * This parameter can be any combination of EXTI_Linex where x can be (0..19).
    5. * @retval None
    6. */
    7. void EXTI_ClearITPendingBit(uint32_t EXTI_Line)
    8. {
    9. /* Check the parameters */
    10. assert_param(IS_EXTI_LINE(EXTI_Line));
    11. EXTI->PR = EXTI_Line;
    12. }

    6.EXTI_ClearFlag

    清除中断

    1. void EXTI_ClearFlag(uint32_t EXTI_Line)
    2. {
    3. /* Check the parameters */
    4. assert_param(IS_EXTI_LINE(EXTI_Line));
    5. EXTI->PR = EXTI_Line;
    6. }

    五、GPIO外部中断程序的移植和调试

    1.建立工程模板

    STM32项目工程的搭建-CSDN博客

    2.查看官方示例代码

    以ARM3.0的按键示例程序作为参考,移植到PZ6806L开发板上。

    stm32f10x_it.c

    将我们在初始化文件(STM32F10x_md.c)中的中断函数进行覆盖重写。

    startup_stm32f10x_md.s

    这个文件中存放是有中断处理函数的定义

    我们如果想要执行(使用)这个中断,则将在md文件中将函数名字复制出来,然后再stm32f10x_it.c文件中使用

    stm32f10x_it.h

    我们上面再stm32f10x_it.c中重写了一个函数,则要再这个.h文件中进行声明

    main.c

    RCC_Configuragtion

    作用:将时钟设置为72MHZ,将相关的开关打开。

    GPIO_Configuration

    NVIC_Configuration

    NVIC中断优先级的初始化

    3.整个main函数的流程

    1)初始化RCC

    2)初始化GPIO

    3)初始化NVIC

    4)设置外部中断(将中断线和GPIO线连接起来)

    5)外部中断线的模式选择(我们一般选择外部中断,不使用外部事件中断)

    6)产生软件中断触发事件

    4.开始移植

    1.按键的接线问题

    这个程序的执行结果是:按下按键一(KEY1),使得LED16(也就是4*4矩阵显示屏上最后一排灯亮起)

    独立按键的JP1接到PB0-PB7

    LED8-LED16的J34接到PA0-PA7

    因为操作库函数的时候只能一位一位操作,所以我们只能选择其中一个引脚进行操作。

    我们选择KEY1和LED16进行操作,分别对应PB0和PA7

    2.关于中断几的选择

    由上面可知我们KEY1对应的是PB0,由0可知应该选择EXIT0

    确定好后,再stm32f10x.c和stm32f10x.h文件中进行重写和声明【我们可以再stm32f10x_md.c文件中查看应该调用哪一个函数】

    3.EXTI0_IRQHandler的移植

    注意点:本来我们应该再stm32f10x_it.c文件中去查找这个中断函数的初始化,但是普中科技的AMR3.0的官方代码将这个中断函数的初始化写再了exit.c文件中。

    stm32f10x_it.c
    1. */
    2. void EXTI0_IRQHandler(void)
    3. {
    4. //检测制定的EXTI线路触发
    5. if(EXTI_GetITStatus(EXTI_Line0)==1)
    6. {
    7. /*
    8. delay_ms(10);
    9. if(KEY1==0)
    10. {
    11. led2=0;
    12. }
    13. */
    14. }
    15. //清除EXTI线路挂起位
    16. EXTI_ClearITPendingBit(EXTI_Line0);
    17. }
    stm32f10x_it.h
    1. //声明自己写的EXIT0
    2. void EXTI0_IRQHandler(void);

    4.RCC_Configuration的移植

    1.RCC的使能
    1. //RCC的配置
    2. void RCC_Configuration(void){
    3. //因为起始代码中已经调用SystemInit将主时钟设置为72MHZ
    4. //所以我们这里RCC直接使能时钟就可以
    5. //使能GPIO端口
    6. //因为我们使用到的是PB0和PB8,所以只使用到GPIOB
    7. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|
    8. RCC_APB2Periph_AFIO,ENABLE);
    9. }
    2.GPIO_Configuration的移植
    1. //GPIO初始化
    2. void CPIO_Configuration(void){
    3. GPIO_InitTypeDef GPIO_InitStructure;
    4. //PB0 -- Key1 -- EXIT2 PA7 ---LED16
    5. //PB0 -- Key1 -- EXIT2【按键是输入获取】
    6. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
    7. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //配置浮空输入
    8. GPIO_Init(GPIOB, &GPIO_InitStructure);
    9. //PA7 ---LED16【LED的显示输出】
    10. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;
    11. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    12. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
    13. GPIO_Init(GPIOA, &GPIO_InitStructure);
    14. GPIO_WriteBit(GPIOA, GPIO_Pin_7, Bit_RESET); // 默认输出0让LED亮
    15. }
    3.NVIC优先级的移植

    NVIC_IRQChannel:在misc.h中定义

    1. void NVIC_Configuration(void)
    2. {
    3. NVIC_InitTypeDef NVIC_InitStructure;
    4. #ifdef VECT_TAB_RAM
    5. /* Set the Vector Table base location at 0x20000000 */
    6. NVIC_SetVectorTable(NVIC_VectTab_RAM, 0x0); //分配中断向量表
    7. #else /* VECT_TAB_FLASH */
    8. //表示从FLASH中启动
    9. /* Set the Vector Table base location at 0x08000000 */
    10. NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x0);
    11. #endif
    12. /* Configure one bit for preemption priority */
    13. // NVIC_PriorityGroup_1:2个抢占优先级,8个次优先级
    14. NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1); //设置中断优先级
    15. /* Enable the EXTI2 Interrupt */
    16. NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn; //中断通道
    17. NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //强占优先级
    18. NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;//次优先级
    19. NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //通道中断使能
    20. NVIC_Init(&NVIC_InitStructure);//初始化中断
    21. }
    4.main函数
    1. //函数声明
    2. //RCC的配置
    3. void RCC_Configuration(void);
    4. //GPIO初始化
    5. void GPIO_Configuration(void);
    6. void NVIC_Configuration(void);
    7. //全局变量定义
    8. EXTI_InitTypeDef EXTI_InitStructure;
    9. ErrorStatus HSEStatartUpStatus;
    10. int main(){
    11. //系统时钟配置
    12. RCC_Configuration();
    13. //NVIC配置
    14. NVIC_Configuration();
    15. //配置GPIO
    16. GPIO_Configuration();
    17. //将EXTI线0连接到PB0【处理按键中断】
    18. GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource0);
    19. /* Configure Key Button EXTI Line to generate an interrupt on falling edge */
    20. //配置按钮中断线触发方式
    21. EXTI_InitStructure.EXTI_Line = EXTI_Line0;
    22. EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
    23. EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; //下降沿触发
    24. EXTI_InitStructure.EXTI_LineCmd = ENABLE; //中断线使能
    25. EXTI_Init(&EXTI_InitStructure); //初始化中断
    26. /* Generate software interrupt: simulate a falling edge applied on Key Button EXTI line */
    27. // EXTI_GenerateSWInterrupt(EXTI_Line0); //EXTI_Line2中断允许 到此中断配置完成,可以写中断处理函数。
    28. while (1);
    29. return 0;
    30. }
    5.中断函数的处理(stm32f10x_it.c)

    因为我们要在中断函数中对LED进行点亮/熄灭,所以需要对LED进行读取和写入。

    所以要使用到我们的【GPIO_ReadInputDataBit】和【GPIO_WriteBit】

    1. void EXTI0_IRQHandler(void)
    2. {
    3. //检测制定的EXTI线路触发
    4. if(EXTI_GetITStatus(EXTI_Line0)==1)
    5. {
    6. //此时控制的是LED,LED对应的是PA0
    7. //我们想要对LED进行点亮/熄灭
    8. //则应该先将此时LED的状态位读取出来,然后在取反写进去
    9. GPIO_WriteBit(GPIOA,GPIO_Pin_0,(BitAction)((1-GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0))));
    10. }
    11. //清除EXTI线路挂起位
    12. EXTI_ClearITPendingBit(EXTI_Line0);
    13. }
    6.对LED的初始化

    在对GPIO初始化的时候一起将lED一起点亮

    1. // 默认输出1让LED亮
    2. GPIO_WriteBit(GPIOA, GPIO_Pin_7, Bit_SET);

    5.完整代码

    main.c

    1. #include "stm32f10x.h" // Device header
    2. /**
    3. 使用库函数对STM32进行GPIO的外部中断
    4. 按下KEY1对LED16进行点亮/熄灭
    5. */
    6. //函数声明
    7. //RCC的配置
    8. void RCC_Configuration(void);
    9. //GPIO初始化
    10. void GPIO_Configuration(void);
    11. void NVIC_Configuration(void);
    12. //全局变量定义
    13. EXTI_InitTypeDef EXTI_InitStructure;
    14. ErrorStatus HSEStatartUpStatus;
    15. int main(){
    16. //系统时钟配置
    17. RCC_Configuration();
    18. //NVIC配置
    19. NVIC_Configuration();
    20. //配置GPIO
    21. GPIO_Configuration();
    22. //将EXTI线0连接到PB0【处理按键中断】
    23. GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource0);
    24. /* Configure Key Button EXTI Line to generate an interrupt on falling edge */
    25. //配置按钮中断线触发方式
    26. EXTI_InitStructure.EXTI_Line = EXTI_Line0;
    27. EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
    28. EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; //下降沿触发
    29. EXTI_InitStructure.EXTI_LineCmd = ENABLE; //中断线使能
    30. EXTI_Init(&EXTI_InitStructure); //初始化中断
    31. /* Generate software interrupt: simulate a falling edge applied on Key Button EXTI line */
    32. //人为产生中断
    33. //如果不想要一进来就产生中断,则应该注释掉
    34. //EXTI_Line0中断允许 到此中断配置完成,可以写中断处理函数。
    35. EXTI_GenerateSWInterrupt(EXTI_Line0);
    36. while (1);
    37. return 0;
    38. }
    39. //RCC的配置
    40. void RCC_Configuration(void){
    41. //因为起始代码中已经调用SystemInit将主时钟设置为72MHZ
    42. //所以我们这里RCC直接使能时钟就可以
    43. //使能GPIO端口
    44. //因为我们使用到的是PB0和PB8,所以只使用到GPIOB
    45. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|
    46. RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO,ENABLE);
    47. }
    48. //GPIO初始化
    49. void CPIO_Gonfiguration(void){
    50. GPIO_InitTypeDef GPIO_InitStructure;
    51. //PB0 -- Key1 -- EXIT2 PA7 ---LED16
    52. //PB0 -- Key1 -- EXIT2【按键是输入获取】
    53. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
    54. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //配置浮空输入
    55. GPIO_Init(GPIOB, &GPIO_InitStructure);
    56. //PA7 ---LED16【LED的显示输出】
    57. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;
    58. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    59. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
    60. GPIO_Init(GPIOA, &GPIO_InitStructure);
    61. // 默认输出1让LED亮
    62. GPIO_WriteBit(GPIOA, GPIO_Pin_7, Bit_SET);
    63. }
    64. void NVIC_Configuration(void)
    65. {
    66. NVIC_InitTypeDef NVIC_InitStructure;
    67. #ifdef VECT_TAB_RAM
    68. /* Set the Vector Table base location at 0x20000000 */
    69. NVIC_SetVectorTable(NVIC_VectTab_RAM, 0x0); //分配中断向量表
    70. #else /* VECT_TAB_FLASH */
    71. //表示从FLASH中启动
    72. /* Set the Vector Table base location at 0x08000000 */
    73. NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x0);
    74. #endif
    75. /* Configure one bit for preemption priority */
    76. // NVIC_PriorityGroup_1:2个抢占优先级,8个次优先级
    77. NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1); //设置中断优先级
    78. /* Enable the EXTI2 Interrupt */
    79. NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn; //中断通道
    80. NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //强占优先级
    81. NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;//次优先级
    82. NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //通道中断使能
    83. NVIC_Init(&NVIC_InitStructure);//初始化中断
    84. }
    stm32f10x_it.c
    1. void EXTI0_IRQHandler(void)
    2. {
    3. //检测制定的EXTI线路触发
    4. if(EXTI_GetITStatus(EXTI_Line0)==1)
    5. {
    6. //此时控制的是LED,LED对应的是PA0
    7. //我们想要对LED进行点亮/熄灭
    8. //则应该先将此时LED的状态位读取出来,然后在取反写进去
    9. GPIO_WriteBit(GPIOA,GPIO_Pin_0,(BitAction)((1-GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0))));
    10. }
    11. //清除EXTI线路挂起位
    12. EXTI_ClearITPendingBit(EXTI_Line0);
    13. }

     六、通过外部中断控制一个灯亮灭

    1.分析IO应该设置为什么模式

    因为EXTI是边沿触发,所以如果想要EXTI则只能设置上升沿/下降沿触发。

    因为平时key处于低电平,如果我们想要控制,则需要给其高电平【上拉】

    2.步骤

    3.CubexMX配置

    EXTI引脚设置

    因为中断的触发点:就是通过引脚传入进入---》上升沿触发(高电平),下降沿触发(低电平)

    上升沿触发(从低电平到高电平)--》下拉输入

    下降沿触发(从高电平到低电平)--》上拉输入

    GPIO设置

    设置

    1)是输入???输出

    2)是什么输入(上拉输入/下拉输入/浮动输入/模拟输入)/输出(开漏输出/推挽输出)

    NVIC设置

    我们设置了哪一个EXTIx,就要中断相对应的Extix interupt

    4.生成的代码

    在stm32f1xx_it.c中有我们声明的EXTI3

    5.代码逻辑编写

    1. int main(void)
    2. {
    3. HAL_Init();
    4. SystemClock_Config();
    5. MX_GPIO_Init();
    6. while (1)
    7. {
    8. //先判断key是否被按下-->PA3
    9. if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_3)==GPIO_PIN_SET){//此时表示可能被按下
    10. //先进行软件消抖:延时一段时间再进行一次查看是否真的被按下
    11. HAL_Delay(5);
    12. if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_3)==GPIO_PIN_SET){//如果再一次进入说明是真的按键按下
    13. //下面这个while加上则表示按一次就亮,再按一次就灭
    14. while(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_3)==GPIO_PIN_SET);
    15. //则我们调整led的亮灭--->PB0
    16. HAL_GPIO_WritePin(GPIOB,GPIO_PIN_6,!HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_6));
    17. }
    18. }
    19. }
    20. }

    七、NVIC总结

    中断-NVIC与EXTI外设详解(超全面)-腾讯云开发者社区-腾讯云

    1.NVIC是什么?

    1)NVIC其实本质就是中断的优先级。分为【抢占优先级】和【响应优先级】。

    2)NVIC(嵌套向量中断):NVIC是Cortex-M3核心的一部分,关于它的资料不在《STM32的技术参考手册》中,应查阅ARM公司的《Cortex-M3技术参考手册》Cortex-M3的向量中断统一由NVIC管理。

    3)NVIC是内部外设(Cortex-M中的外设)

    2.NVIC的应用?

    1)NVIC是中断(EXTI)的主管。当我们使用到了外部或者内部的中断,都会使用到 NVIC来进行处理优先级。

    2)类似于ExTI,TIM,串口的中断都是要经过NVIC才可以对CPU产生中断。只有其他内核中断,才可以不经过NVIC。

    3.NVIC相关函数存放的位置

    Cortex-M3 内核的外设也比较多,但STM32并没有用到这么多内核外设对其进行了裁剪,STM32重要的内核外设用到的库函数放在了misc.c文件之中所以core_cm3.c文件用的较少。

    core_cm3.c:内核外设的驱动固件库  

    core_cm3.h:实现了内核(CPU)里面的外设的寄存器映射,还有很多关于内核外设的库函数。

    misc.h:NVIC_InitTypeDef结构体,以及库函数的参数和声明

    misc.c:NVIC(嵌套向量中断控制器)、SysTick(系统滴答定时器)相关函数

    4.中断向量表

    这个表存放在我们所使用的startup_stm32f103xb.s文件中【初始化文件】

    5.优先级分组,设置相关的优先级

    STM32 将中断分为 5 个组,组 0-4。该分组的设置是由 SCB->AIRCR 寄存器的 bit10~8 来定义的。

    数值越小,优先级越高

    这里我们使用到的函数:

    HAL_NVIC_SetPriorityGrouping---》设置分组

    HAL_NVIC_SetPriority--》设置优先级

    IRQn_Type IRQn:设置中断源

    不同的中断中断源不一样,且不可写错,即使写错了程序也不会报错,只会导致不响应中断。

    PreemptPriority:抢占优先级

    具体的值要根据优先级分组来确定。

    SubPriority:子优先级(响应优先级)

    具体的值要根据优先级分组来确定

    6.NVIC执行顺序

    八、EXTI总结

    中断-NVIC与EXTI外设详解(超全面)-腾讯云开发者社区-腾讯云

    1.EXTI的产生

    对于互联型产品(F107),外部中断/事件控制器由20个产生事件/中断请求的边沿检测器组成,对于其它产品(我们这里是F103),则有19个能产生事件/中断请求的边沿检测器。

    1)由外部条件触发例如按键触发(GPIO),触发产生上升沿、下降沿、双边沿【EXTI主要用于处理外部引脚状态变化引起的中断。】

    2)每个输入线可以独立地配置输入类型(脉冲或挂起)和对应的触发事件(上升沿或下降沿或者双边沿都触发)。每个输入线都可以独立地被屏蔽。挂起寄存器保持着状态线的中断请求

    2.GPIO和EXTI的映射

    16个中断线的不是每个中断都有独立的中断服务函数,IO口外部中断在中断向量表中只分配了7个中断向量,也就是只能使用7个中断服务函数

    3.中断处理函数 

    PA0--》EXTI0

    PA2--》EXTI2

    从表中可以看出,外部中断线5~9分配一个中断向量,共用一个服务函数。 外部中断线10~15分配一个中断向量,共用一个中断服务函数。

    对应的中断服务函数,直接去启动文件里面找以防写错。【startup_stm32f103xb.s】

    4.EXTI功能框图

    中断与事件的区别:

    https://www.cnblogs.com/engraver-lxw/p/7518958.html

    • 中断:需要CPU参与,需要调用软件的中断服务函数才能完成中断后产生的结果
    • 事件:靠脉冲发生器产生一个脉冲,进而由硬件自动完成这个事件产生的结果,当然相应的联动部件需要先设置好,触发TIM计时,AD转换等,事件不要软件的参与,降低了CPU的负荷,而且硬件速度快于软件速度

    5. EXTI的工作流程

    配置GPIO引脚:

    • 选择需要作为外部中断触发引脚的GPIO引脚。
    • 配置引脚为输入模式,选择上拉或下拉电阻。
    1. // 例如,配置引脚 PA0 作为外部中断触发引脚
    2. GPIO_InitTypeDef GPIO_InitStruct = {0};
    3. __HAL_RCC_GPIOA_CLK_ENABLE();
    4. GPIO_InitStruct.Pin = GPIO_PIN_0;
    5. GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
    6. GPIO_InitStruct.Pull = GPIO_PULLDOWN;
    7. HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    配置EXTI线:

    • 选择并配置外部中断线,将其连接到对应的GPIO引脚。
    • 配置中断触发条件,例如上升沿、下降沿、或者是双边沿触发。
    1. // 例如,配置 EXTI0 与 PA0 相连,并设置为上升沿触发
    2. HAL_NVIC_SetPriority(EXTI0_IRQn, 0, 0);
    3. HAL_NVIC_EnableIRQ(EXTI0_IRQn);
    4. HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_RESET);
    5. HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4);
    6. HAL_NVIC_SetPriority(EXTI0_IRQn, 0, 0);
    7. HAL_NVIC_EnableIRQ(EXTI0_IRQn);
    8. HAL_EXTI_GetHandle(&hexti0, EXTI_LINE_0);
    9. HAL_EXTI_RegisterCallback(&hexti0, HAL_EXTI_COMMON_CB_ID, UserButton_Callback);
    10. __HAL_GPIO_EXTI_GET_IT(GPIO_PIN_0);

    编写中断服务函数:

    • 中断服务函数是中断触发时执行的代码,处理实际的中断事件。【在it.c文件中写】
    • 在中断服务函数中,可能需要清除中断标志。
    1. // 例如,编写 EXTI0 中断服务函数
    2. void EXTI0_IRQHandler(void)
    3. {
    4. HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0);
    5. }
    6. void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
    7. {
    8. if (GPIO_Pin == GPIO_PIN_0)
    9. {
    10. // 处理中断事件
    11. }
    12. }

    启用中断:

    在主程序中,启用外部中断

    1. // 启用中断
    2. HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4);
    3. HAL_NVIC_SetPriority(EXTI0_IRQn, 0, 0);
    4. HAL_NVIC_EnableIRQ(EXTI0_IRQn);

  • 相关阅读:
    异常--自定义异常概述
    三类6种地图可视化软件测评,最好用的工具居然是它
    【论文阅读】半监督时序动作检测 Semi-Supervised Action Detection
    Java文件读取方式和效率性能对比
    AIGC时代 浪潮信息积极推动存储产品创新
    Elasticsearch集群搭建手册及配置详情(基于elasticsearch-8.5.2版本)
    【MySQL】聚合函数:汇总、分组数据
    算法训练(leetcode)第二十八天 | 509. 斐波那契数、70. 爬楼梯、746. 使用最小花费爬楼梯
    (web前端网页制作课作业)使用HTML+CSS制作非物质文化遗产专题网页设计与实现
    Kubernetes 基于 helm 安装 harbor
  • 原文地址:https://blog.csdn.net/m0_63077733/article/details/134042616