exti.c
#include "exti.h"
/*
引脚说明
//KEY0 -- 按键PE4--灯PA6
KEY0 -- PE4
PE4 -- EXTI4
KEY1 -- PE3
*/
void Exti_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
EXTI_InitTypeDef EXTI_InitStruct;
NVIC_InitTypeDef NVIC_InitStruct;
//1、使能SYSCFG时钟: 使能这个时钟才能使用外部中断
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_4; //引脚4
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN; //输入
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP; //上拉
//2、 初始化IO口为输入。
GPIO_Init(GPIOE, &GPIO_InitStruct);
//3、设置IO口与中断线的映射关系。
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOE, EXTI_PinSource4);
EXTI_InitStruct.EXTI_Line = EXTI_Line4; //中断线4
EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt; //中断模式
EXTI_InitStruct.EXTI_Trigger= EXTI_Trigger_Falling; //下降沿
EXTI_InitStruct.EXTI_LineCmd= ENABLE; //使能中断线
//4、初始化线上中断,设置触发条件等。
EXTI_Init(&EXTI_InitStruct);
NVIC_InitStruct.NVIC_IRQChannel = EXTI4_IRQn; //选择通道
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 2; //抢占优先级
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 2; //响应优先级
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE; //通道使能
//5、配置中断分组(NVIC),并使能中断。
NVIC_Init(&NVIC_InitStruct);
}
//6、 编写中断服务函数。 这个函数在startup_stm32f40_41.s中查阅
//不需要自己调用,中断响应后,CPU自动去执行的函数
void EXTI4_IRQHandler(void)
{
if(EXTI_GetITStatus(EXTI_Line4) == SET) //判断中断线0是否置1
{
GPIO_ToggleBits(GPIOA, GPIO_Pin_6);//置1,灯亮
}
//7、清除中断标志位
EXTI_ClearITPendingBit(EXTI_Line4);
}
/*
外部中断PA0设置步骤
1、理解按键电路原理
按键KEY0连接PA0
当按键按下时, PA0电平为低电平(0)
当按键未按下时,PA0电平为高电平(1)
2、设置NVIC分组(记住,整个工程只能设置一次分组)
void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup)
3、使能SYSCFG时钟:
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);
4、 初始化IO口为输入。
GPIO_Init();
5、设置IO口与中断线的映射关系。
void SYSCFG_EXTILineConfig();
6、初始化线上中断,设置触发条件等。
EXTI_Init();
7、配置中断分组(NVIC),并使能中断。
NVIC_Init();
8、 编写中断服务函数。
EXTIx_IRQHandler();
9、清除中断标志位
EXTI_ClearITPendingBit();
*/
以上函数实现了连接按键控制led
exti.h
#ifndef __EXTI_H
#define __EXTI_H
#include "stm32f4xx.h"
void Exti_Init(void);
#endif
main.c
#include "stm32f4xx.h"
#include "led.h"
#include "key.h"
#include "exti.h"
//粗延时
void delayms(int n)
{
int i, j;
for(i=0; i<n; i++)
for(j=0; j<30000; j++);
}
//这是一个主函数
int main(void)
{
//NVIC分组 抢占优先级两位:0~3 响应优先级两位:0~3
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
Led_Init();
Exti_Init();
while(1)
{
delayms(1000);
}
return 0;
}
led.c 和led.h函数在上几篇,没有改动。
上面用的延时是粗延时,如果要精准延时,加上systick定时器。如下函数:
delay.c
#include "delay.h"
/*
Systick定时器是芯片外设
*/
u32 my_nus = 21; //1us计21个数
u32 my_nms = 21000; //1ms计21000个数
void Delay_Init(void)
{
//Systick频率 168/8 = 21MHZ
SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);
}
//微秒延时 1
//最大范围为798915us
void delay_us(u32 nus)
{
u32 temp = 0;
SysTick->LOAD = my_nus * nus - 1;// 重装寄存器 要计数的值
SysTick->VAL = 0; //当前值寄存器值为0
SysTick->CTRL |= (1<<0); //使能定时器
do
{
temp = SysTick->CTRL;
}while( temp&(1<<0) && !(temp&(1<<16)) ); //检测CTRL第十六是否为1
SysTick->CTRL &= ~(1<<0); //不使能定时器
}
//毫秒延时
//最大范围为798.915ms
void delay_ms(u32 nms)
{
u32 temp = 0;
SysTick->LOAD = my_nms * nms - 1;// 重装寄存器 要计数的值
SysTick->VAL = 0; //当前值寄存器值为0
SysTick->CTRL |= (1<<0); //使能定时器
do
{
temp = SysTick->CTRL;
}while( temp&(1<<0) && !(temp&(1<<16)) ); //检测CTRL第十六是否为1
SysTick->CTRL &= ~(1<<0); //不使能定时器
}
//秒延时
void delay_s(u32 ns)
{
u32 i;
for(i=0; i<ns; i++)
{
delay_ms(500);
delay_ms(500);
}
}
delay.h
#ifndef __DELAY_H
#define __DELAY_H
#include "stm32f4xx.h"
void Delay_Init(void);
void delay_us(u32 nus); //微秒延时
void delay_ms(u32 nms);
void delay_s(u32 ns);
#endif
SysTick 中断和 EXTI(外部中断)中断 区别
SysTick 中断和 EXTI(外部中断)中断是两种不同类型的中断,它们在功能和触发条件上有一些区别:
1. **SysTick 中断**:
- SysTick 定时器是 ARM Cortex-M 架构中的一个内置计时器,用于生成固定时间间隔的定时中断。
- SysTick 中断是基于定时器的,它提供了一个周期性触发的定时中断,可用于系统的定时和调度,例如实现延迟、定时任务等。
- SysTick 中断的间隔由加载到 SysTick 寄存器的值决定,因此可以通过调整该值来更改中断的触发频率。
- SysTick 中断通常用于实现系统的时间相关功能,如任务调度、定时器等。2. **EXTI 中断**:
- EXTI(外部中断)是由外部事件触发的中断,例如外部引脚的状态变化(上升沿、下降沿、或者双边沿触发)。
- EXTI 中断是基于硬件外部事件的,它允许外部事件触发处理器中断,从而执行相应的中断服务程序。
- EXTI 中断允许 MCU 对外部信号的变化做出快速响应,常用于处理外部事件,如按钮按下、传感器触发等。
- EXTI 中断的触发条件由外部事件的状态变化确定,可以通过配置 EXTI 寄存器来选择中断触发条件和外部引脚。总的来说,SysTick 中断是基于内置定时器的周期性定时中断,而 EXTI 中断是基于外部事件(例如外部引脚状态变化)的触发中断。它们分别用于不同的应用场景,SysTick 用于系统的定时和调度,而 EXTI 用于对外部事件的快速响应。
为什么用EXTI
代码就能实现按键控制led,为什么还要用exti?
通过使用 EXTI(外部中断)来处理按键事件,这种方法具有以下优点:
1. **实时性**: 使用外部中断可以实现按键按下时立即响应,无需等待主循环的轮询或其他延时操作。这可以确保在按键按下时系统能够立即执行相应的操作,提高了系统的实时性和响应速度。
2. **节省资源**: 通过使用外部中断,系统只有在按键按下时才会唤醒处理器执行中断服务程序,而在按键未按下时处于休眠或低功耗状态,可以有效节省处理器的能耗。
3. **避免忙等待**: 如果使用轮询或忙等待的方式来检测按键状态,可能会导致处理器在等待按键按下时持续消耗 CPU 时间,降低系统的效率和功耗表现。
综上所述,通过使用外部中断来处理按键事件可以提高系统的实时性,节省资源,并避免忙等待的情况发生,是一种较好的实现方式。
重要函数 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
//NVIC分组 抢占优先级两位:0~3 响应优先级两位:0~3
在嵌入式系统中,通常需要对中断或任务进行优先级排序,以确保系统对重要事件或任务的响应能力。这种优先级排序主要有两个方面:
1. 抢占优先级(Preemption Priority): 抢占优先级决定了当一个具有更高优先级的中断请求到来时,是否可以中断正在执行的低优先级中断或任务。更高的抢占优先级允许更快地中断正在执行的低优先级任务或中断。这使得系统能够在紧急情况下优先处理重要的任务。
2. 子优先级(Subpriority):在同一抢占优先级内,子优先级决定了在多个具有相同抢占优先级的中断请求中,哪一个会被优先执行。具有更高子优先级的中断会优先于具有较低子优先级的中断。这使得在同一优先级下,可以更细致地控制中断请求的执行顺序。
在一个系统中可能存在多个中断源,这些中断源可能在某些情况下会同时发生,或者在非常接近的时间内发生。为了合理地处理这些中断,需要对它们进行优先级排序。
通过使用两个优先级:抢占优先级和子优先级,可以确保系统在发生多个中断时能够按照预期的顺序进行处理。抢占优先级确保了当具有更高抢占优先级的中断请求到来时,能够立即中断正在执行的低优先级任务或中断;而子优先级则确保了在同一抢占优先级内,可以按照一定的顺序依次执行多个中断请求。
函数说明
/**
* @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
* @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
* @note When the NVIC_PriorityGroup_0 is selected, IRQ pre-emption is no more possible.
* The pending IRQ priority will be managed only by the subpriority.
* @retval None
*/
void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup)
{
/* Check the parameters */
assert_param(IS_NVIC_PRIORITY_GROUP(NVIC_PriorityGroup));
/* Set the PRIGROUP[10:8] bits according to NVIC_PriorityGroup value */
SCB->AIRCR = AIRCR_VECTKEY_MASK | NVIC_PriorityGroup;
}
使用哪个参数?
`NVIC_PriorityGroup_1` 和 `NVIC_PriorityGroup_2` 两个参数都定义了不同的优先级分组方式,其主要区别在于预抢占优先级和子优先级的分配方式不同。
- `NVIC_PriorityGroup_1`: 此参数指定了1位用于抢占优先级,3位用于子优先级。这意味着在系统中,有8个抢占优先级(2^1 = 2,共2个级别)和8个子优先级(2^3 = 8,共8个级别)可供分配。
- `NVIC_PriorityGroup_2`: 此参数指定了2位用于抢占优先级,2位用于子优先级。这意味着在系统中,有4个抢占优先级(2^2 = 4,共4个级别)和4个子优先级(2^2 = 4,共4个级别)可供分配。
所以,当选择使用哪个参数时,需要考虑系统的需求和实际情况:
- 如果系统需要更精细的优先级控制,即需要更多的子优先级来区分任务或中断的优先级,可以选择`NVIC_PriorityGroup_1`,因为它提供了更多的子优先级。
- 如果系统的需求对于预抢占优先级较为关键,而子优先级的区分度要求不高,可以选择`NVIC_PriorityGroup_2`,因为它提供了更多的预抢占优先级。在实际选择时,应根据系统的实际需求来决定,以满足对系统性能和响应时间的要求。
重要概念理解
什么是芯片外设
芯片外设(Peripheral)是指嵌入式系统中与主处理器核心(CPU)相连的辅助模块或硬件组件,它们扩展了芯片的功能,使其能够执行各种特定任务或处理外部事件。这些外设通常包括各种接口、控制器、传感器、存储设备等,用于连接外部设备或执行特定的功能。
芯片外设的功能包括但不限于以下几个方面:
1. **通信接口:** 包括串行通信接口(如UART、SPI、I2C)、以太网接口、USB接口等,用于与外部设备进行数据交换和通信。
2. **定时器和计数器:** 用于生成精确的时间间隔或进行定时操作,如控制定时器、PWM(脉冲宽度调制)输出等。
3. **模拟/数字转换器(ADC/DAC):** 用于将模拟信号转换为数字信号(ADC),或将数字信号转换为模拟信号(DAC),常用于传感器接口、信号采集等。
4. **存储器控制器:** 包括闪存控制器、SRAM控制器等,用于管理外部存储器的读写操作。
5. **中断控制器:** 用于管理中断请求,优先级分配和中断处理。
6. **GPIO(通用输入/输出):** 用于连接外部设备、传感器或执行控制操作的通用IO口。
7. **电源管理单元:** 控制芯片的供电、功耗管理和休眠模式等。
8. **加速器和协处理器:** 用于加速特定计算任务,如加密引擎、数字信号处理器等。
这些外设通常由硬件逻辑实现,与主处理器核心通过总线或专用接口连接。通过这些外设,芯片能够实现更多的功能,适应不同的应用场景,提高系统的性能和灵活性。
与外部设备有什么不同?
外部设备是指连接到嵌入式系统的独立硬件组件或模块,这些设备通常不在芯片内部,而是外部连接到芯片的引脚或接口上。外部设备可以是传感器、执行器、存储设备、显示器、键盘、鼠标、网络设备等,它们提供了与嵌入式系统进行交互的方式。
与芯片外设不同,外部设备通常是独立的单元,它们具有自己的处理器、控制逻辑和接口电路,通过各种物理接口(如USB、串口、并口等)连接到嵌入式系统。这些外部设备可以由不同的制造商生产,具有不同的规格和功能。
与外部设备相比,芯片外设是直接集成在嵌入式系统芯片内部的功能模块或硬件组件。它们与主处理器核心(CPU)连接在一起,通常通过内部总线或专用接口进行通信。外设由芯片厂商设计和实现,为了方便系统开发者使用,通常提供了软件接口(如寄存器映射、驱动程序等),使得开发者可以通过软件控制和配置外设功能。
总的来说,外部设备是独立的硬件单元,连接到嵌入式系统外部,而芯片外设是嵌入式系统内部集成的功能模块,与主处理器核心连接在一起。
README
每个模块分别学完了,接下来会做一些复盘和总结