使用STM32基本定时器实现LED反转功能

引脚是什么为什么要初始化引脚?

     在嵌入式系统中,引脚是微控制器或微处理器上的物理引脚,用于连接外部设备、传感器或其他芯片。每个引脚都有特定的功能和用途,例如输入、输出、模拟输入、电源供应等。STM32F103C8T6引脚图:

初始化引脚的目的在于:

  1. 确定引脚的功能: 微控制器的引脚通常具有多个功能,比如输入、输出、模拟信号输入等。通过初始化,你可以明确引脚在特定时刻的功能。

  2. 配置引脚的电气特性: 引脚的电气特性包括输出速率、上拉/下拉电阻等。通过初始化,你可以设置引脚的这些特性,以适应特定的应用场景。

  3. 确保正确的电气状态: 初始化过程中,你可以设定引脚的初始状态,确保系统启动时引脚处于正确的电气状态。在代码中,通过 GPIO_SetBits(GPIOB, GPIO_Pin_5 | GPIO_Pin_1) 将引脚5和引脚1设置为高电平,关闭LED,可能是确保LED在初始化后处于关闭状态。

总的来说,初始化引脚是为了确保它们在系统运行时以正确的方式工作,并满足特定的应用需求。

GPIO和AIFO

GPIO(通用输入输出接口):是MCU与外部电路和设备连接的基本外设,也就是常说的端口或管脚。STM32一共有80个GPIO端口,其中的一些还可以把复用功能重新映射到其他的引脚,以实现优化管脚数目和配置母的。

AIFO (复用功能IO):某些GPIO除了通用功能外还可以设置为一些外设专用的功能。

STM32的八种GPIO模式

  1. 输入模式 (Input Mode):

  2. GPIO引脚被配置为输入,用于读取外部信号。
  3. 引脚电压将根据外部信号的状态变化而变化。
  4. 在此模式下,引脚的状态由外部电路决定。
  5. 输出模式 (Output Mode):

  6. GPIO引脚被配置为输出,可以向外部设备提供信号。
  7. 开发者可以通过相应的寄存器设置引脚的状态,例如将引脚设置为高电平或低电平。
  8. 推挽输出模式 (Push-Pull Output Mode):

  9. GPIO引脚被配置为推挽输出,可以提供高电平和低电平信号。
  10. 引脚可以主动推送(提供)高电平或拉低(提供)低电平。
  11. 适用于需要提供相对较大电流的场景,如驱动LED或驱动其他数字电路。
  12. 开漏输出模式 (Open-Drain Output Mode):

  13. GPIO引脚被配置为开漏输出,通常与外部上拉电阻结合使用。
  14. 在高电平状态下,引脚处于高阻态,而在低电平状态下,引脚将连接到地。
  15. 适用于需要实现电平的共享或与其他开漏设备连接的场景。
  16. 复用功能输入模式 (Alternate Function Input Mode):

  17. GPIO引脚被配置为某个外设的输入,例如UART、SPI或I2C。
  18. 引脚的功能由复用寄存器设置,以选择相应的外设功能。
  19. 复用功能输出模式 (Alternate Function Output Mode):

  20. GPIO引脚被配置为某个外设的输出。
  21. 类似于复用功能输入模式,引脚的功能由复用寄存器设置。
  22. 模拟输入模式 (Analog Mode):

  23. GPIO引脚被配置为模拟输入,用于连接模拟传感器或其他模拟设备。
  24. 在此模式下,引脚可以读取模拟电压值。
  25. 中断模式 (Interrupt Mode):

  26. GPIO引脚被配置为触发中断,当引脚状态变化时,可以触发中断服务程序。
  27. 适用于需要在引脚状态变化时执行特定操作的场景,如按钮按下触发中断

为什么判断时要使用 GPIO_ReadOutputDataBit(GPIOB, GPIO_Pin_5) == 0

在提供的代码中,GPIO_ReadOutputDataBit(GPIOB, GPIO_Pin_5) == 0 的条件语句检查引脚5(对应LEDR)的输出状态是否为低电平。

  1. GPIO_ReadOutputDataBit(GPIOB, GPIO_Pin_5): 这个函数是用于读取指定引脚的输出状态的函数。在这里,它读取GPIOB中引脚5(LEDR)的输出状态。

  2. == 0: 这是一个比较运算符,检查表达式左侧的值是否等于右侧的值。在这里,它检查引脚5的输出状态是否等于0,即低电平。

因此,GPIO_ReadOutputDataBit(GPIOB, GPIO_Pin_5) == 0 的整个条件语句的含义是:如果LEDR(引脚5)的输出状态为低电平(0),则执行条件语句块内的代码。

为什么要设置中断?

在嵌入式系统中,中断是一种机制,用于实时响应硬件事件或异步事件。通过配置中断,系统可以在出现特定事件时立即执行相应的中断服务程序(ISR,Interrupt Service Routine),而不必通过轮询或其他方式不断地检查事件状态。配置中断的目的包括:

  1. 实时响应事件: 中断允许系统在发生特定事件时立即进行响应,而无需等待主程序轮询或定时检查。这对于需要快速响应的实时应用非常重要,如控制系统、通信系统等。

  2. 提高系统效率: 通过使用中断,系统可以在等待事件的同时执行其他任务,提高了系统的效率。相比于轮询方式,中断减少了对系统资源的浪费。

  3. 事件驱动编程: 配置中断允许采用事件驱动的编程模型,其中系统对外部事件作出响应,而不是通过持续轮询。这样的编程方式通常更加模块化和易于维护。

  4. 优先级管理: 中断允许设置不同中断的优先级,确保在系统中同时发生多个事件时能够按照优先级顺序处理。这对于确保关键事件的及时处理非常关键。

  5. 省电: 在某些情况下,通过中断响应事件,系统可以保持在低功耗状态,只有在需要时才唤醒主程序执行。这有助于节省能量。

在提供的代码中,配置中断的目的是为了处理键盘输入(KEY1、KEY2)和定时器中断(TIM6)。这样,当用户按下按键或者定时器溢出时,系统能够立即作出响应,而不必等待这些事件在主程序中被轮询检测到。

什么是NVIC中断控制器?

NVIC是一个总的中断控制器,不管是来自CM3内部的异常还是来自外设的中断,都将进入该控制器进行处理和逻辑控制。此外NVIC还通过优先级系统控制中断的嵌套和调度。

当中断输入脚被置为有效后,该中断就被“悬起”。所谓“悬起”,也就是等待、就绪的意思。即使后来中断源撤消了中断请求,已经被标记成悬起的中断也被记录下来。

当某中断的服务程序开始执行时,就称此中断进入了“活跃”状态,并且其悬起位会被硬件自动清除。在一个中断活跃后,直到其服务例程执行完毕,并且返回后,才能对该中断的新请求予以响应。

什么是外部中断_事件控制器 EXTI?

与NVIC不同,外部中断/事件控制器EXTI是STM32F103的一个外设,不属于CM3内核的范畴主要用于外部中断和事件的控制。
EXTI负责管理映射到GPIO引脚上的外中断和片内几个集成外设的中断 (PVD输出,RTC闹钟事件、USB唤醒事件、以太网唤醒事件),以及软件中断。其输出最终被映射到NVIC的相应通道因此,配置EXTI中断的过程必然包含对NVIC的配置

EXTI由20个产生事件/中断请求的边沿检测器组成,有以下几个特点:

每个输入线可以独立地配置输入类型(脉冲或挂起)和对应的触发事件(上升沿或下降沿或者双边沿都触发)。

每个输入线都可以独立地被屏蔽。

 挂起寄存器保持着状态线的中断请求

什么是中断挂起?

如果中断发生时,正在处理同级或高优先级异常,或者被掩蔽,则中断不能立即得到响应,此时中断被悬起。悬挂意味着等待而不是舍去,当优先级高的或者同等级先发生的中断完成后,被挂起的中断才会执行。中断的悬起状态可以通过“中断设置悬起寄存器(SETPEND)”和“中断悬起清除寄存器(CLRPEND)”来读取,还可以写它们来手工悬起中断。

什么是中断标志?

当进入中断后,中断标志位会自动被置1,代表着“正在执行中断服务函数中”。当我们结束终端服务函数之前,一定要在函数中清除中断标志位。我们可以这样理解:中断标志位是满足中断条件的象征,当我们配置好中断发生的条件,一旦遇到中断标志位有效,就说明该执行中断服务函数了。如果我们不手动的清除中断标志位,那么系统会默认中断条件一直满足那就会一直执行中断服务函数跳不出来了。

中断初始化代码详解

以下这段代码是用于配置嵌套向量中断控制器(NVIC)的初始化函数。在嵌入式系统中,NVIC用于管理中断,通过设置优先级和使能中断来控制中断的触发和处理。

  1. NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);: 设置优先级分组为优先级组1。这里的优先级分组影响了中断优先级的划分方式。

  2. 配置外部中断(EXTI)中断优先级:

    针对 EXTI0_IRQn,即KEY1的外部中断,设置主优先级为1,次优先级为1。针对 EXTI15_10_IRQn,即KEY2的外部中断,设置主优先级为1,次优先级为2。

  3. 配置定时器中断(TIM6)优先级:

    针对 TIM6_IRQn,设置主优先级为1,次优先级为0。

  4. NVIC_Config.NVIC_IRQChannelCmd = ENABLE; 使能相应中断。

  5. NVIC_Init(&NVIC_Config);对以上配置进行初始化,将配置应用到实际的中断控制器中。

总体而言,这段代码的目的是初始化中断控制器,设置不同中断的优先级和使能状态,以确保在系统运行时可以正确响应和处理各种中断。

void nvic_init(void)
{
	NVIC_InitTypeDef NVIC_Config;
	
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1); 	// 设置优先级分组为优先级组1
	
	// KEY1
	NVIC_Config.NVIC_IRQChannel = EXTI0_IRQn;
	NVIC_Config.NVIC_IRQChannelPreemptionPriority = 1; 	// 设置主优先级为1
	NVIC_Config.NVIC_IRQChannelSubPriority = 1; 		// 设置次优先级为1
	NVIC_Config.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init(&NVIC_Config);
	
	// KEY2
	NVIC_Config.NVIC_IRQChannel = EXTI15_10_IRQn;
	NVIC_Config.NVIC_IRQChannelPreemptionPriority = 1; 	// 设置主优先级为1
	NVIC_Config.NVIC_IRQChannelSubPriority = 2; 		// 设置次优先级为2
	NVIC_Config.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init(&NVIC_Config);
	
	// TIM6
	NVIC_Config.NVIC_IRQChannel = TIM6_IRQn;
	NVIC_Config.NVIC_IRQChannelPreemptionPriority = 1; 	// 设置主优先级为1
	NVIC_Config.NVIC_IRQChannelSubPriority = 0; 		// 设置次优先级为0
	NVIC_Config.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init(&NVIC_Config);
}

详细解释一下优先级顺序

KEY1外部中断(EXTI0_IRQn):              主优先级:1     次优先级:1

KEY2外部中断(EXTI15_10_IRQn):      主优先级:1     次优先级:2

定时器中断(TIM6_IRQn):                      主优先级:1     次优先级:0

这   里采用的优先级设置是简单的优先级分组方式,使用了一个主优先级和一个次优先级。在这种设置下,主优先级决定了各中断之间的相对优先级,而次优先级则用于区分相同主优先级的不同中断。

优先级顺序是:定时器中>KEY1外部中断>KEY2外部中断

这意味着在同时发生这三个中断时,系统会首先处理定时器中断,然后是KEY1外部中断,最后是KEY2外部中断。希望这次能够清晰地解释。

为什么要启用时钟?

启用时钟:

在嵌入式系统中,启用时钟是为了激活相应外设或功能模块的时钟。微控制器的不同功能模块(如GPIO、外部中断控制器、定时器等)都需要时钟信号来驱动其工作。因此,在使用这些功能之前,需要确保相应的时钟已经被启用。

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOC | RCC_APB2Periph_AFIO, ENABLE);

这行代码启用了GPIOA、GPIOC和复用引脚(AFIO)的时钟。GPIOA和GPIOC是用于配置按键引脚的GPIO端口,而AFIO是用于配置外部中断线的复用引脚。

为什么要配置外部中断线

外部中断线配置的目的是将实际的物理引脚与外部中断线关联起来,使得当引脚状态变化时能够触发相应的中断。在这里,主要是将按键的物理引脚与外部中断线关联。

    对于KEY1,GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0)将GPIOA的引脚0(即PA0)与外部中断线0关联起来。

     对于KEY2,GPIO_EXTILineConfig(GPIO_PortSourceGPIOC, GPIO_PinSource13)将GPIOC的引脚13(即PC13)与外部中断线13关联起来。

外部中断线配置的详细参数包括中断线的选择、中断模式(中断或事件模式)、触发方式(上升沿、下降沿等)等。配置外部中断线是为了让系统能够在按键状态变化时及时地产生中断。

void exti_key_init(void)
{
	GPIO_InitTypeDef GPIO_Config;
	EXTI_InitTypeDef EXTI_Config;
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOC | RCC_APB2Periph_AFIO, ENABLE); 	// 使能GPIOA、GPIOC和复用引脚时钟
	
	// KEY1
	GPIO_Config.GPIO_Pin = GPIO_Pin_0; 						// 配置第0个引脚
	GPIO_Config.GPIO_Mode = GPIO_Mode_IN_FLOATING; 			// 配置引脚为浮空输入
	GPIO_Init(GPIOA, &GPIO_Config); 						// 设置PA0为浮空输入模式
	
	// KEY2
	GPIO_Config.GPIO_Pin = GPIO_Pin_13; 					// 配置第0个引脚
	GPIO_Config.GPIO_Mode = GPIO_Mode_IN_FLOATING; 			// 配置引脚为浮空输入
	GPIO_Init(GPIOC, &GPIO_Config); 						// 设置PA0为浮空输入模式
	
	// KEY1
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0); // 使用PA0作为EXTI0输入源,PA0连接KEY1
	EXTI_Config.EXTI_Line = EXTI_Line0;							// 设置外部中断线0,对应KEY1的PA0
	EXTI_Config.EXTI_Mode = EXTI_Mode_Interrupt; 				// 设置为中断模式,除此之外还有事件模式
	EXTI_Config.EXTI_Trigger = EXTI_Trigger_Rising; 			// 上升沿触发
	EXTI_Config.EXTI_LineCmd = ENABLE;
	EXTI_Init(&EXTI_Config);
	
	// KEY2
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOC, GPIO_PinSource13);	// 使用PC13作为EXTI13输入源,PC13连接KEY2
	EXTI_Config.EXTI_Line = EXTI_Line13;							// 设置外部中断线13,对应KEY2的PC13
	EXTI_Config.EXTI_Mode = EXTI_Mode_Interrupt; 					// 设置为中断模式,除此之外还有事件模式
	EXTI_Config.EXTI_Trigger = EXTI_Trigger_Falling; 				// 下降沿触发
	EXTI_Config.EXTI_LineCmd = ENABLE;
	EXTI_Init(&EXTI_Config);
}

什么是中断的通道

NVIC_Config.NVIC_IRQChannel = TIM6_IRQn;

    这行代码是在配置中断控制器(NVIC – Nested Vectored Interrupt Controller)的相关设置,具体是在配置 TIM6 定时器的中断通道。

    具体而言,NVIC_Config.NVIC_IRQChannel = TIM6_IRQn; 这一行代码设置了中断通道为 TIM6 定时器中断。在这里,TIM6_IRQn 表示 TIM6 定时器的中断通道。

 NVIC: Nested Vectored Interrupt Controller,中文称为嵌套向量中断控制器。它是用于管理中断的硬件模块。

 NVIC_IRQChannel: 表示中断通道,即中断的来源。在这里,它被设置为 TIM6 定时器中断。

 TIM6_IRQn: 表示 TIM6 定时器的中断通道编号。这个编号是根据具体的微控制器型号和型号的,不同型号的 STM32 微控制器可能具有不同的中断通道编号。

这行代码的作用是告诉中断控制器,当 TIM6 定时器触发中断时,执行与该中断通道相关的中断服务程序(Interrupt Service Routine,ISR)。在程序的其他地方,你可能会编写一个中断服务程序来处理 TIM6 定时器中断时需要执行的操作。

NVIC_Init(&NVIC_Config)的作用是什么?

VIC_Init(&NVIC_Config); 是用来更新中断向量表的,确保中断发生时跳转到正确的中断处理函数数。当我们定义了一个中断结构体的时,我们需要把这个中断向量表更新,以便我们使用的时候可以找到。

中断向量表的第一个元素是堆栈顶(Stack Top),之后是一系列的中断向量。每个中断向量都是一个32位的地址,指向相应中断处理函数的入口地址。当中断发生时,处理器会自动跳转到相应中断向量的地址执行中断服务程序。

在STM32中,中断向量表的地址是固定的,通常是Flash存储器的起始地址。启动时,处理器会将中断向量表的地址加载到中断向量表寄存器中。在运行时,如果发生中断,处理器会自动从中断向量表中获取相应中断向量的地址,然后跳转到中断处理函数的入口执行相应的中断服务程序。

在STM32标准外设库(Standard Peripheral Library)或者Cube HAL库中,中断向量表的具体实现是由库提供的,用户一般无需手动修改中断向量表。用户只需要配置中断处理函数,然后库会将这些函数的地址填充到中断向量表中。

void TIM6_IRQHandler(void)命名是在哪里定义的?

作者:代码破碎真君

物联沃分享整理
物联沃-IOTWORD物联网 » 使用STM32基本定时器实现LED反转功能

发表评论