内容纲要
6讲 中断应用概览、EXTI中断外部事件控制器、SysTick系统定时器
- 中断类型
- 控制中断的寄存器---NVIC
NVIC 是Arm 厂商定义的,对于不同的芯片使用Arm 的内核,不同的公司会根据需求对NVIC 进行裁剪
- 当中断到来时,先比较优先级分组,然后再比较优先级。就可以用4位来表示80多种甚至更多的中断优先级
- 如果组优先级和子优先级一致的话,那么就对比硬件优先级。
- 中断编程的顺序
中断优先级 对内核 和外设 都有效,并非内核优先级大于外设的。
- 系统内核优先级只有下图的可以编程
中断服务函数尽量写在 "stm32f4xx_it.c" 文件中,当然只是建议,因为便于管理。
如果中断函数名写错了,那么在启动文件的中断向量表会执行默认函数,当然你也可以写如果中断未执行应该怎么做。
上图 B . 汇编语言表示死循环,程序会停在这里。
8讲 EXTI---外部 中断/事件 控制器 (External interrupt / event controller)
- EXTI 功能框图
- 与门
- 或门
- 定义EXTI 结构体成员
这四个结构体成员对应如下的寄存器
关于EXTI_Line 我的理解是,0-15代表GPIOx的接口引脚,,另外7种代表7个可配置内核的中断
/** @defgroup EXTI_Lines * @{ */ #define EXTI_Line0 ((uint32_t)0x00001) /*!< External interrupt line 0 */ #define EXTI_Line1 ((uint32_t)0x00002) /*!< External interrupt line 1 */ #define EXTI_Line2 ((uint32_t)0x00004) /*!< External interrupt line 2 */ #define EXTI_Line3 ((uint32_t)0x00008) /*!< External interrupt line 3 */ #define EXTI_Line4 ((uint32_t)0x00010) /*!< External interrupt line 4 */ #define EXTI_Line5 ((uint32_t)0x00020) /*!< External interrupt line 5 */ #define EXTI_Line6 ((uint32_t)0x00040) /*!< External interrupt line 6 */ #define EXTI_Line7 ((uint32_t)0x00080) /*!< External interrupt line 7 */ #define EXTI_Line8 ((uint32_t)0x00100) /*!< External interrupt line 8 */ #define EXTI_Line9 ((uint32_t)0x00200) /*!< External interrupt line 9 */ #define EXTI_Line10 ((uint32_t)0x00400) /*!< External interrupt line 10 */ #define EXTI_Line11 ((uint32_t)0x00800) /*!< External interrupt line 11 */ #define EXTI_Line12 ((uint32_t)0x01000) /*!< External interrupt line 12 */ #define EXTI_Line13 ((uint32_t)0x02000) /*!< External interrupt line 13 */ #define EXTI_Line14 ((uint32_t)0x04000) /*!< External interrupt line 14 */ #define EXTI_Line15 ((uint32_t)0x08000) /*!< External interrupt line 15 */ #define EXTI_Line16 ((uint32_t)0x10000) /*!< External interrupt line 16 Connected to the PVD Output */ #define EXTI_Line17 ((uint32_t)0x20000) /*!< External interrupt line 17 Connected to the RTC Alarm event */ #define EXTI_Line18 ((uint32_t)0x40000) /*!< External interrupt line 18 Connected to the USB OTG FS Wakeup from suspend event */ #define EXTI_Line19 ((uint32_t)0x80000) /*!< External interrupt line 19 Connected to the Ethernet Wakeup event */ #define EXTI_Line20 ((uint32_t)0x00100000) /*!< External interrupt line 20 Connected to the USB OTG HS (configured in FS) Wakeup event */ #define EXTI_Line21 ((uint32_t)0x00200000) /*!< External interrupt line 21 Connected to the RTC Tamper and Time Stamp events */ #define EXTI_Line22 ((uint32_t)0x00400000) /*!< External interrupt line 22 Connected to the RTC Wakeup event */ #define EXTI_Line23 ((uint32_t)0x00800000) /*!< External interrupt line 23 Connected to the LPTIM Wakeup event */
Q: 如果PA0 和PB0 同时连到EXTI 中断源上,那么同一时间中断信号来的时候谁先执行?
S:可以通过读取GPIO 的IDR 寄存器,来判断谁先执行,总有一个先后顺序
//代码如下,头疼,找问题点花了我好长一段时间。
main.c
#include "stm32f4xx.h"
#include "bsp_led.h"
#include "./exti/bsp_exti.h"
int main(void)
{
/* 在这里添加你自己的程序 */
LED_GPIO_Config();
EXTI_Key_Config();
while(1){};
}
bsp_led.c
#include "./LED/bsp_led.h"
/**
* @brief 初始化控制LED的IO
* @param 无
* @retval 无
*/
void LED_GPIO_Config(void)
{
/*定义一个GPIO_InitTypeDef类型的结构体*/
GPIO_InitTypeDef GPIO_InitStructure;
/*开启LED相关的GPIO外设时钟*/
RCC_AHB1PeriphClockCmd ( LED1_GPIO_CLK|
LED2_GPIO_CLK|
LED3_GPIO_CLK, ENABLE);
/*选择要控制的GPIO引脚*/
GPIO_InitStructure.GPIO_Pin = LED1_PIN;
/*设置引脚模式为输出模式*/
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
/*设置引脚的输出类型为推挽输出*/
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
/*设置引脚为上拉模式*/
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
/*设置引脚速率为2MHz */
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
/*调用库函数,使用上面配置的GPIO_InitStructure初始化GPIO*/
GPIO_Init(LED1_GPIO_PORT, &GPIO_InitStructure);
/*选择要控制的GPIO引脚*/
GPIO_InitStructure.GPIO_Pin = LED2_PIN;
GPIO_Init(LED2_GPIO_PORT, &GPIO_InitStructure);
/*选择要控制的GPIO引脚*/
GPIO_InitStructure.GPIO_Pin = LED3_PIN;
GPIO_Init(LED3_GPIO_PORT, &GPIO_InitStructure);
/*关闭RGB灯*/
LED_RGBOFF;
}
/*********************************************END OF FILE**********************/
bsp_led.h
#ifndef __LED_H
#define __LED_H
#include "stm32f4xx.h"
//引脚定义
/*******************************************************/
//R 红色灯
#define LED1_PIN GPIO_Pin_6
#define LED1_GPIO_PORT GPIOF
#define LED1_GPIO_CLK RCC_AHB1Periph_GPIOF
//G 绿色灯
#define LED2_PIN GPIO_Pin_7
#define LED2_GPIO_PORT GPIOF
#define LED2_GPIO_CLK RCC_AHB1Periph_GPIOF
//B 蓝色灯
#define LED3_PIN GPIO_Pin_8
#define LED3_GPIO_PORT GPIOF
#define LED3_GPIO_CLK RCC_AHB1Periph_GPIOF
/************************************************************/
/** 控制LED灯亮灭的宏,
* LED低电平亮,设置ON=0,OFF=1
* 若LED高电平亮,把宏设置成ON=1 ,OFF=0 即可
*/
#define ON 0
#define OFF 1
/* 带参宏,可以像内联函数一样使用 */
#define LED1(a) if (a) \
GPIO_SetBits(LED1_GPIO_PORT,LED1_PIN);\
else \
GPIO_ResetBits(LED1_GPIO_PORT,LED1_PIN)
#define LED2(a) if (a) \
GPIO_SetBits(LED2_GPIO_PORT,LED2_PIN);\
else \
GPIO_ResetBits(LED2_GPIO_PORT,LED2_PIN)
#define LED3(a) if (a) \
GPIO_SetBits(LED3_GPIO_PORT,LED3_PIN);\
else \
GPIO_ResetBits(LED3_GPIO_PORT,LED3_PIN)
/* 直接操作寄存器的方法控制IO */
#define digitalHi(p,i) {p->BSRRL=i;} //设置为高电平
#define digitalLo(p,i) {p->BSRRH=i;} //输出低电平
#define digitalToggle(p,i) {p->ODR ^=i;} //输出反转状态 //按位异或,当位分别位1与0 是,取得结果为1
/* 定义控制IO的宏 */
#define LED1_TOGGLE digitalToggle(LED1_GPIO_PORT,LED1_PIN)
#define LED1_OFF digitalHi(LED1_GPIO_PORT,LED1_PIN)
#define LED1_ON digitalLo(LED1_GPIO_PORT,LED1_PIN)
#define LED2_TOGGLE digitalToggle(LED2_GPIO_PORT,LED2_PIN)
#define LED2_OFF digitalHi(LED2_GPIO_PORT,LED2_PIN)
#define LED2_ON digitalLo(LED2_GPIO_PORT,LED2_PIN)
#define LED3_TOGGLE digitalToggle(LED3_GPIO_PORT,LED3_PIN)
#define LED3_OFF digitalHi(LED3_GPIO_PORT,LED3_PIN)
#define LED3_ON digitalLo(LED3_GPIO_PORT,LED3_PIN)
/* 基本混色,后面高级用法使用PWM可混出全彩颜色,且效果更好 */
//红
#define LED_RED \
LED1_ON;\
LED2_OFF;\
LED3_OFF
//绿
#define LED_GREEN \
LED1_OFF;\
LED2_ON;\
LED3_OFF
//蓝
#define LED_BLUE \
LED1_OFF;\
LED2_OFF;\
LED3_ON
//黄(红+绿)
#define LED_YELLOW \
LED1_ON;\
LED2_ON;\
LED3_OFF
//紫(红+蓝)
#define LED_PURPLE \
LED1_ON;\
LED2_OFF;\
LED3_ON
//青(绿+蓝)
#define LED_CYAN \
LED1_OFF;\
LED2_ON;\
LED3_ON
//白(红+绿+蓝)
#define LED_WHITE \
LED1_ON;\
LED2_ON;\
LED3_ON
//黑(全部关闭)
#define LED_RGBOFF \
LED1_OFF;\
LED2_OFF;\
LED3_OFF
void LED_GPIO_Config(void);
#endif /* BSP_LED_H */
bsp_exti.c
#include "./exti/bsp_exti.h"
//3.配置NVIC //嵌套向量中断控制器
static void NVIC_Configuration(void)
{
//设置优先级组为1
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
//void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct);
//创建结构体
NVIC_InitTypeDef NVIC_InitStruct;
/************* KEY1 **************************/
//配置中断源
NVIC_InitStruct.NVIC_IRQChannel = KEY1_INT_EXTI_IRQ;
//配置抢占优先级
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;
//配置子优先级
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;
//使能中断通道
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
//初始化
NVIC_Init(&NVIC_InitStruct);
/************* KEY1 END **************************/
/****************** KEY2 *******************/
NVIC_InitStruct.NVIC_IRQChannel = KEY2_INT_EXTI_IRQ;
NVIC_Init(&NVIC_InitStruct);
/****************** KEY2 END *******************/
}
void EXTI_Key_Config(void)
{
//1.初始化需要连接到EXTI 线的GPIO
RCC_AHB1PeriphClockCmd(KEY1_INT_GPIO_CLK | KEY2_INT_GPIO_CLK, ENABLE);
//设置按键1引脚
GPIO_InitTypeDef InitGPIOstruct;
InitGPIOstruct.GPIO_Mode = GPIO_Mode_IN;
InitGPIOstruct.GPIO_OType = GPIO_OType_PP;
InitGPIOstruct.GPIO_Pin = KEY1_INT_GPIO_PIN;
InitGPIOstruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
InitGPIOstruct.GPIO_Speed = GPIO_Low_Speed;
GPIO_Init(KEY1_INT_GPIO_PORT,&InitGPIOstruct);
//设置按键2引脚
InitGPIOstruct.GPIO_Pin = KEY2_INT_GPIO_PIN;
GPIO_Init(KEY2_INT_GPIO_PORT,&InitGPIOstruct);
//2.初始化EXTI
/****************** KEY1 ************************/
//使能SYSCFG 时钟,该时钟位于APB2 外设总线下。
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG,ENABLE); //开启系统配置(SYSCFG) 时钟 目的是未来用得到 EXTICR4
//使GPIO 引脚添加到EXTI中断源上
//void SYSCFG_EXTILineConfig(uint8_t EXTI_PortSourceGPIOx, uint8_t EXTI_PinSourcex);
SYSCFG_EXTILineConfig(KEY1_INT_EXTI_PORTSOURCE, KEY1_INT_EXTI_PINSOURCE); //Q: 既然这里配置了引脚?为什么下面的结构体也要配置?
EXTI_InitTypeDef EXTI_Struct;
EXTI_Struct.EXTI_Line = KEY1_INT_GPIO_PIN;
EXTI_Struct.EXTI_Mode = EXTI_Mode_Interrupt; //配置成中断模式, (而不是事件模式)
EXTI_Struct.EXTI_LineCmd = ENABLE;
EXTI_Struct.EXTI_Trigger = EXTI_Trigger_Rising; //配置上升沿触发。 (上升沿/下降沿/都选)
EXTI_Init(&EXTI_Struct);
/****************** KEY1 END ************************/
/****************** KEY2 ************************/
SYSCFG_EXTILineConfig(KEY2_INT_EXTI_PORTSOURCE, KEY2_INT_EXTI_PINSOURCE);
EXTI_Struct.EXTI_Line = KEY2_INT_GPIO_PIN;
EXTI_Struct.EXTI_Mode = EXTI_Mode_Interrupt; //配置成中断模式, (而不是事件模式)
EXTI_Struct.EXTI_LineCmd = ENABLE;
EXTI_Struct.EXTI_Trigger = EXTI_Trigger_Falling; //配置下降沿沿触发//区别于上,用于测试。 (上升沿/下降沿/都选)
EXTI_Init(&EXTI_Struct);
/****************** KEY2 END ************************/
//3.配置NVIC
NVIC_Configuration();
}
//4.编写中断服务函数
/*中断服务函数在 "stm32f4xx_it.c" 中 */
bsp_exti.h
#ifndef BSP_EXTI_H
#define BSP_EXTI_H
#include "stm32f4xx.h"
/****************引脚定义****************************/
#define KEY1_INT_GPIO_PORT GPIOA
#define KEY1_INT_GPIO_CLK RCC_AHB1Periph_GPIOA
#define KEY1_INT_GPIO_PIN GPIO_Pin_0
#define KEY1_INT_EXTI_PORTSOURCE EXTI_PortSourceGPIOA
#define KEY1_INT_EXTI_PINSOURCE EXTI_PinSource0
#define KEY1_INT_EXTI_LINE EXTI_Line0
#define KEY1_INT_EXTI_IRQ EXTI0_IRQn
#define KEY1_IRQHandler EXTI0_IRQHandler
#define KEY2_INT_GPIO_PORT GPIOC
#define KEY2_INT_GPIO_CLK RCC_AHB1Periph_GPIOC
#define KEY2_INT_GPIO_PIN GPIO_Pin_13
#define KEY2_INT_EXTI_PORTSOURCE EXTI_PortSourceGPIOC
#define KEY2_INT_EXTI_PINSOURCE EXTI_PinSource13
#define KEY2_INT_EXTI_LINE EXTI_Line13
#define KEY2_INT_EXTI_IRQ EXTI15_10_IRQn
#define KEY2_IRQHandler EXTI15_10_IRQHandler
/**************************************************/
void EXTI_Key_Config(void);
//void NVIC_Configuration(void);
#endif /*BSP_EXTI_H*/
stm32f4xx_it.c
/**
******************************************************************************
* @file Project/STM32F4xx_StdPeriph_Templates/stm32f4xx_it.c
* @author MCD Application Team
* @version V1.8.0
* @date 04-November-2016
* @brief Main Interrupt Service Routines.
* This file provides template for all exceptions handler and
* peripherals interrupt service routine.
******************************************************************************
* @attention
*
* <h2><center>© COPYRIGHT 2016 STMicroelectronics</center></h2>
*
* Licensed under MCD-ST Liberty SW License Agreement V2, (the "License");
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.st.com/software_license_agreement_liberty_v2
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
******************************************************************************
*/
/* Includes ------------------------------------------------------------------*/
#include "stm32f4xx_it.h"
#include "./exti/bsp_exti.h"
#include "bsp_led.h"
/** @addtogroup Template_Project
* @{
*/
/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
/* Private function prototypes -----------------------------------------------*/
/* Private functions ---------------------------------------------------------*/
/******************************************************************************/
/* Cortex-M4 Processor Exceptions Handlers */
/******************************************************************************/
/**
* @brief This function handles NMI exception.
* @param None
* @retval None
*/
void NMI_Handler(void)
{
}
/**
* @brief This function handles Hard Fault exception.
* @param None
* @retval None
*/
void HardFault_Handler(void)
{
/* Go to infinite loop when Hard Fault exception occurs */
while (1)
{
}
}
/**
* @brief This function handles Memory Manage exception.
* @param None
* @retval None
*/
void MemManage_Handler(void)
{
/* Go to infinite loop when Memory Manage exception occurs */
while (1)
{
}
}
/**
* @brief This function handles Bus Fault exception.
* @param None
* @retval None
*/
void BusFault_Handler(void)
{
/* Go to infinite loop when Bus Fault exception occurs */
while (1)
{
}
}
/**
* @brief This function handles Usage Fault exception.
* @param None
* @retval None
*/
void UsageFault_Handler(void)
{
/* Go to infinite loop when Usage Fault exception occurs */
while (1)
{
}
}
/**
* @brief This function handles SVCall exception.
* @param None
* @retval None
*/
void SVC_Handler(void)
{
}
/**
* @brief This function handles Debug Monitor exception.
* @param None
* @retval None
*/
void DebugMon_Handler(void)
{
}
/**
* @brief This function handles PendSVC exception.
* @param None
* @retval None
*/
void PendSV_Handler(void)
{
}
/**
* @brief This function handles SysTick Handler.
* @param None
* @retval None
*/
void SysTick_Handler(void)
{
}
void EXTI0_IRQHandler(void) // EXTI0_IRQHandler 是宏定义,真实的函数名不是它,真正的函数名在启动文件中,TIP:中断函数都在中断向量表里面可以找到
{
//ITStatus EXTI_GetITStatus(uint32_t EXTI_Line);
if(EXTI_GetITStatus(KEY1_INT_EXTI_LINE) != RESET)
{
LED1_TOGGLE;
EXTI_ClearITPendingBit(KEY1_INT_EXTI_LINE); //清除中断标志位,Q:EXTI_ClearITPendingBit() 和 EXTI_ClearFlag() 函数的区别?
}
}
void EXTI15_10_IRQHandler(void)
{
//ITStatus EXTI_GetITStatus(uint32_t EXTI_Line);
if(EXTI_GetITStatus(KEY2_INT_EXTI_LINE) != RESET)
{
LED2_TOGGLE;
EXTI_ClearITPendingBit(KEY2_INT_EXTI_LINE);
}
}
/******************************************************************************/
/* STM32F4xx Peripherals Interrupt Handlers */
/* Add here the Interrupt Handler for the used peripheral(s) (PPP), for the */
/* available peripheral interrupt handler's name please refer to the startup */
/* file (startup_stm32f4xx.s). */
/******************************************************************************/
/**
* @brief This function handles PPP interrupt request.
* @param None
* @retval None
*/
/*void PPP_IRQHandler(void)
{
}*/
/**
* @}
*/
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
注意:细心。。。。这个BUG我找了半天。。由于是宏定义,编译器又不报错。。。
10讲 系统定时器 SysTick
-
SysTick 功能框图
-
重装载寄存器------24位
-
当前数值寄存器----读这个数会表示当前计数器这个值
- SysTick 控制及状态寄存器
STK_CTRL 控制及状态寄存器
SysTick 唯一的固件库函数
- 此处 1UL << __NVIC_PRIO_BITS 这个宏定义代表4,此处是1 左移 4位,上图的这行代码代表将优先级数字置为最高(即优先级最低)。(上一章节有讲,优先级由4个二进制数组成,优先级数字越高,则优先级越低)
SysTick 例程
实验要求:让SysTick 产生1s 的定时,让LED亮灭。