2、NUCLEO-L476RG实验心得 (二)_LED闪闪亮续一 (按键、中断)
本帖最后由 wolfgang2015 于 2017-6-17 10:49 编辑一、实验开始之前二、实验过程0、上次实验回顾 上次实验中,我们通过STM32CubeMX创建了一个Keil工程,增加了少量代码就实现了点亮LED灯的试验目的;板子能按HAL_Delay(x)函数,在x=500时,按照这个时间间隔闪亮熄灭LED灯。回顾实验,有几个核心函数出现在试验中:1、GPIO口操作函数,HAL_GPIO_WritePin: HAL_GPIO_WritePin(LD2_GPIO_Port,LD2_Pin,GPIO_PIN_SET) HAL_GPIO_WritePin(LD2_GPIO_Port,LD2_Pin,GPIO_PIN_RESET); 上面两个函数将GPIO口设置为高电平、低电平实现了LED的点亮、熄灭,制造了LED的闪亮。
2、GPIO口引脚的相关定义,LD2_GPIO_Port,LD2_Pin,GPIO_PIN_SET、GPIO_PIN_RESET 这几个引脚的定义在{项目名称}\Inc\ mxconstants.h 文件中,定义内容如下:
#define LD2_Pin GPIO_PIN_5#define LD2_GPIO_Port GPIOA
GPIO_PIN_5的定义在 {项目名称}\Drivers\STM32L4xx_HAL_Driver\Inc\stm32l4xx_hal_gpio.h 文件中
.......#define GPIO_PIN_5 ((uint16_t)0x0020)/* Pin 5 selected */.......
GPIOA的定义在 {项目名称}\Drivers\STM32L4xx_HAL_Driver\Inc\stm32l4xx_hal.h 文件中
.........
#define PERIPH_BASE ((uint32_t)0x40000000) /*!< Peripheral base address */.........#define AHB2PERIPH_BASE (PERIPH_BASE + 0x08000000)........#define GPIOA_BASE (AHB2PERIPH_BASE + 0x0000).......#define GPIOA ((GPIO_TypeDef *) GPIOA_BASE).......
这里我们可以发现GPIOA是一个32位的寄存器,每个GPIO引脚都由这个积存器的bit来控制,而且这个寄存器的基地址由GPIOA_BASE 得来,GPIOA_BASE这个基地址是由AHB2PERIPH_BASE + 0x0000得来,AHB2PERIPH_BASE这个基地址是由 PERIPH_BASE +0x08000000得来;PERIPH_BASE这个基地址是由(uint32_t)0x40000000。这种用基址+偏移的方法来定义常量很不错,寄存器往往有一个固定的地址,如果编译、移植,如果偏移不变,只需要更换基址即可。
GPIO_TypeDef 结构体是在{项目名称}\Drivers\CMSIS\Device\ST\STM32L4xx\Include\stm32l476xx.h
typedef struct
{__IO uint32_t MODER; /*!< GPIO port mode register, Address offset: 0x00*/__IO uint32_t OTYPER; /*!< GPIO port output type register, Address offset: 0x04*/__IO uint32_t OSPEEDR; /*!< GPIO port output speed register,Address offset: 0x08*/__IO uint32_t PUPDR; /*!< GPIO port pull-up/pull-down register,Address offset: 0x0C*/__IO uint32_t IDR; /*!< GPIO port input data register, Address offset: 0x10*/__IO uint32_t ODR; /*!< GPIO port output data register,Address offset: 0x14*/__IO uint32_t BSRR; /*!< GPIO port bit set/resetregister,Address offset: 0x18*/__IO uint32_t LCKR; /*!< GPIO port configuration lock register,Address offset: 0x1C*/__IO uint32_t AFR; /*!< GPIO alternate function registers, Address offset: 0x20-0x24 */__IO uint32_t BRR; /*!< GPIO Bit Reset register,Address offset: 0x28*/__IO uint32_t ASCR; /*!< GPIO analog switch control register, Address offset: 0x2C*/} GPIO_TypeDef;
GPIO_PIN_SET、GPIO_PIN_RESET的定义在{项目名称}\Drivers\STM32L4xx_HAL_Driver\Inc\stm32l4xx_hal_gpio.h 文件中
.............typedef enum{GPIO_PIN_RESET = 0,GPIO_PIN_SET}GPIO_PinState;........................
定义GPIO_PIN_RESET 低电平,GPIO_PIN_SET 高电平。
3、亮与暗的时间间隔我们是用HAL_Delay(delay)函数来实现的,delay是在程序体中定义的一个变量值来定义的uint16_t delay = 500;HAL_Delay(uint32_tDelay) 是在{项目名称}\Drivers\STM32L4xx_HAL_Driver\Inc\stm32l4xx_hal.h 定义,在{项目名称} \Drivers\STM32L4xx_HAL_Driver\Src\stm32l4xx_hal.c的函数中HAL_GetTick()-tickstart< Delay,来实现的。
HAL_GetTick是在{项目名称}\Drivers\STM32L4xx_HAL_Driver\Inc\stm32l4xx_hal.h 定义,在{项目名称} \Drivers\STM32L4xx_HAL_Driver\Src\stm32l4xx_hal.c的函数中用uwTick实现的。查资料得到, HAL_IncTick(void) 中断函数是1ms进来一次,也就是说uwTick+1就代表1ms。结论是:HAL_Delay(delay)是闪亮500ms、熄灭延时500ms。
1、新实验设想 当我们知道了点灯的原理之后,我就就在想,能否对试验进行改进,再增加些已有的外设,比如,用按钮B1来控制等的闪亮延时长短,当检查到GPIO连接的B1发生按键时发起延时值得改变。除了事用HAL_GetTick()之外还有那些定义的时钟函数可以使用。使用CubeMX打开芯片引脚设置,观察B1所用的PC13 引脚设置:设置为GPIO_EXIT13:GPIO口启用外部中断13。
GPIO 类型设置为外部中断,在上升沿触发检测。
NVIC:嵌套向量中断控制器:NestedVectored Interrupt Controller (NVIC)在NVIC这里我们查询我们设置的中断向量:
启用GPIO的中断。在CubeMX设置好后,创建实验代码。
2、实验步骤先在main.h中增加定义延时的值:
enum Enum_Delay{Delay_512=512,Delay_256=256,
Delay_128=128,
Delay_64=64,
Delay_32=32,
Delay_16=16,
Delay_8=8,
Delay_4=4,
Delay_2=2,
Delay_1=1,
Delay_0=0
} ;
在main.c中引用main.h,并在USER CODEBEGIN 0处定义延时初始值;USER CODE BEGIN 1处初始化亮灯的值;USER CODE BEGIN 3处将循环中加入闪亮等的相关操作(继续沿用前面试验中LED亮灯的设置;USER CODE BEGIN 4 处增加按键中断的相应代码,即修改延时的值。
....../* USER CODE BEGIN Includes */#include "main.h"/* USER CODE END Includes */......./* USER CODE BEGIN 0 */static enum Enum_Delay delay =Delay_512;/* USER CODE END 0 */................../* USER CODE BEGIN 1 */uint8_t sta = ON; /* USER CODE END 1 */......................./* USER CODE BEGIN 3 */while(1){ LED(sta); sta =!sta; HAL_Delay(delay);}/* USER CODE END 3 */......................../* USER CODE BEGIN 4 */void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin){ switch(delay) { case Delay_512: delay=Delay_256; break; case Delay_256: delay=Delay_128; break; case Delay_128: delay=Delay_64; break; case Delay_64: delay=Delay_32; break; case Delay_32: delay=Delay_16; break; case Delay_16: delay=Delay_8; break; case Delay_8: delay=Delay_4; break; case Delay_4: delay=Delay_2; break; case Delay_2: delay=Delay_1; break; case Delay_1: delay=Delay_0; break; case Delay_0: delay=Delay_512; break; default: delay=Delay_512; }}/* USER CODE END 4 */
3、实验的观察结果当我们把程序下载到板子中后,按下Reset按钮重起系统,这时LED闪亮,我们按下LB按钮,闪亮频率加快,大约按下6~7下后,几乎看不出闪亮了;
通过实验发现 Delay_8到Delay_0LED几乎不闪,这是为什么呢?频率太高?还是((HAL_GetTick() - tickstart) < Delay)的时代码有缺陷呢?这里通过Debug是无法跟踪到的,F5按键的频率决定了程序响应速度,我们只好通过其它方式来验证;HAL_GetTick 调试中固定值为 0x08000FD0。看来还得引入其他方法来验证,这里暂且不表,等到后边的实验来验证。
三、实验后的心得这个实验,可以了解STM32l4xx_hal_gpio.h/.c中 HAL_GPIO_EXTI_Callback回调函数的定义和调用。通过__weak void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) 定义回调函数,在程序运行空间中定义具体的实现内容。GPIO回调函数这仅是众多回调函数的一个案例,虽则对功能模块的熟悉还有机会接触到更多的系统 回调函数。经常看见有人问如何来避免抖动,这里用检测中断来判断按键。这种方法是比较好的一种方法,几乎不会出现按键抖动情况。而且这里使用检测GPIO的上升沿来触发中断,根据电路图上按键接的是一个下拉电阻,松开按键的瞬间引发上拉电平。检测电平上拉这时候的上升沿往往自只有一个,只要是按钮或按键没有接触不良的情况,几乎都不会出现抖动现象,若出现就是你要更换按键了,鼠标的微动开关就是出现连击情况就是这样,你要跟换鼠标微动开关/鼠标了。到这里,刚拿到板子时的延时程序就还原了,诸位是否有收获呢?以后的实验我们需要外设了,Uart TTL电平转串口的芯片、SPI 液晶显示屏、IIC时钟模块或者IIC的Flash、做PWM实验的电机驱动模块及直流电机等等,或许还有其他我们自己制作的模块。
前文回顾
1、NUCLEO-L476RG试验(一)_LED闪闪亮(环境搭建与开发工具介绍)
后文链接
3、NUCLEO-L476RG实验心得(三)_Eclipse环境配置
4、NUCLEO-L476RG试验三_LED闪闪亮续(定时、串口) 不闪是因为超过了人眼能分辨的范围了。 creep 发表于 2015-11-24 23:14
不闪是因为超过了人眼能分辨的范围了。
哈哈,顺便做了扫屏测试 多谢分享 赞个,这教程写的,好像回到课堂上了 沐紫 发表于 2015-11-25 09:05
赞个,这教程写的,好像回到课堂上了
(*^_^*)
被沐紫mm,赞了太荣幸了~~ 呵呵,天天更新。 :loveliness:多谢分享。。。。 谢谢分享 很详细,跟羊脚印来的
页:
[1]