wolfgang2015 发表于 2017-6-17 10:42:35

4、NUCLEO-L476RG试验三_LED闪闪亮续(定时、串口)

本帖最后由 wolfgang2015 于 2017-6-17 10:51 编辑

一、实验开始之前二、实验过程0、上次实验回顾   上次实验中,我们通过STM32CubeMX创建了通过按键捕获中断,实现LED变频闪亮的工程,通过不断按下用户键实现LED,让LED从延时512开始,逐次减半延时至0的规律循环。   回顾实验,有几个核心函数出现在试验中:1、按键功能并没有接其它元器件,只使用了MCU上GPIO的中断捕获功能,用到的中断回调函数 void HAL_GPIO_EXTI_Callback(uint16_tGPIO_Pin),在中断使能情况下,当捕获到了对应中断,进入中断回调函数中,执行中断代码逻辑。在HAL库中。
2、调用这个回调函数的函数为:void HAL_GPIO_EXTI_IRQHandler(uint16_tGPIO_Pin) 而, 这个函数是由void EXTI4_15_IRQHandler(void)中调用,EXTI4_15_IRQHandler是在启动汇编文件中的中断向量表中供ARM系统调用。当ARM系统产生中断而没有被屏蔽,立即会触发中断函数中的内容。 3、GPIO口的相关定义,B1_Pin、B1_GPIO_Port      
      GPIO_InitStruct.Pin = B1_Pin;   
   
                                 
这几个引脚的定义在{项目名称}\Inc\ mxconstants.h 文件中,定义内容如下:

      #define B1_Pin GPIO_PIN_13    #define B1_GPIO_Port GPIOC   
   
   
   
GPIO_PIN_13的定义在 {项目名称}\Drivers\STM32L4xx_HAL_Driver\Inc\stm32l4xx_hal_gpio.h 文件中

      .......    #define    GPIO_PIN_13                   ((uint16_t)0x2000U)/* Pin 13    selected*/    ......   
   
   
GPIOC的定义在 {项目名称}\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    GPIOC               ((GPIO_TypeDef *)    GPIOC_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 文件中
4、GPIO口的模式: 上升沿触发

      ......    GPIO_InitStruct.Mode=GPIO_MODE_IT_RISING;    ......   
   
   
GPIO_MODE_IT_RISING的定义在 {项目名称}\Drivers\STM32L4xx_HAL_Driver\Inc\stm32l4xx_hal_gpio.h 文件中

      ....    #defineGPIO_MODE_IT_RISING(0x10110000U)             /*!< External Interrupt Mode    with Rising edge trigger detection   */    ......   
   
   

5、外部中断初始化并中断使能。

      ......    HAL_NVIC_SetPriority(EXTI4_15_IRQn, 0,    0);    HAL_NVIC_EnableIRQ(EXTI4_15_IRQn);    ......   
   
   
中断的相关初始化和使能,在{项目名称}\Drivers\STM32L4xx_HAL_Driver\Inc\stm32l4xx_hal_cortex.h 文件中。
1、新实验设想 GPIO的输出、中断捕获功能原理熟悉之后,可以进一步设想,能否对实验进行改进,利用CPU的定时器作为参照灯或心跳灯,检测程序是否运行;并利用串口将按键后改变的延时值输出到上位机进行展示。 A)定时器设置: 使用CubeMX打开芯片引脚设置,将定时器1的时钟源设置为内部时钟。                                    

系统时钟频率设置为80MHz
在定时器功能中设置如下:设置预分频40000-1(0~39999),计数时间1000-1(0~1999),确定每次中断计数就是一个2000次计数,每次计数是1ms,。
定时器中断使能。
B)串口设置:   选择功能树上Usart2,为异步模式,VCP仅使用了Rx和Tx 并无流控,因此硬件流控为禁用:设定PIN脚 PA2为Usart_TX,PA3为Usart_RX。      
在串口功能中设置如下:
波特率:115200,数据长度,8bits,起停位1bit,其他的保持默认
USART2 中断使能
GPIO口及标签设置如上图设置完以上内容后,通过CubeMX创建试验代码。
CubeMX创建的实验代码有专门的章节介绍,这里介绍分析省略。

2、实验步骤A)定时器设置:定时器设置虚要外界一个LED或者蜂鸣器,通过GPIO F4口来进行控制先设置好定时器的回调函数,通过回调函数控制LED的闪亮和熄灭:在mxconstants.h 文件定义定时器的GPIO引脚,GPIO的相关设置参考前面的设置方法:

      ......    #define LDTimer_Pin GPIO_PIN_4    #define LDTimer_GPIO_Port GPIOF    .......   
   

在main.h设置定时器控制的LED相关控制函数

      ......    #define TIMERLED(STA)   (STA)?HAL_GPIO_WritePin(LDTimer_GPIO_Port,LDTimer_Pin,GPIO_PIN_SET):HAL_GPIO_WritePin(LDTimer_GPIO_Port,LDTimer_Pin,GPIO_PIN_RESET)    ......   
   
   




定义定时器回调函数中的内容:

      .......    static uint8_t staTimer = ON;    ......    void    HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {    TIMERLED(staTimer);    staTimer    = !staTimer;    }    ......   
   


在main()函数中主循环里设置启动定时器:   
      .......    //TIME启动;             HAL_TIM_Base_Start_IT(&htim1);    ......   
   



B)串口设置:先设置串口的发送数据缓冲区和接收缓冲区在main.h 文件定义发送和接收缓冲区的大小:

      ......    #define RxBufferSize64    #define TxBufferSize64    .......   
   

在main.c文件中定义发送和接收缓冲区的数组:   
      ......    static uint8_t Rx_Buffer    = { 0 };    static uint8_t Tx_Buffer    = { 0 };    .......   
   

在非DMA方式下实现串口通信时需要设置以下内容:在Usart2的初始化函数(MX_USART2_UART_Init)中,设置中断和数据接收缓冲区   
      ......    HAL_UART_Receive_IT(&huart2,Rx_Buffer,RxBufferSize);    .......   
   

在Main.c文件中,设置好串口中断回调函数:   
      ........    void    HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart){if    (huart==&huart2){       //printf("Rx: %s \n",Rx_Buffer);//设置好Printf方式输出后可去掉注释符号       .........//捕获中断后,将接收到的缓冲数据,进行数据处理的函数(这里略)       HAL_UART_Receive_IT(&huart2,Rx_Buffer,RxBufferSize);    }    }    .......   
   

在Main.c文件中,设置好发送数据函数   
      ........    static uint8_t *TxBuffer;    static uint32_t StrN; //设置动态字符串长度值    ........    void SendWithOutDMA_Len(void) {           TxBuffer = (uint8_t *) malloc(StrN * sizeof(uint8_t));           .......//组织TxBuffer 将发送的字符串拼接起来;        HAL_UART_Transmit(&huart2,    TxBuffer, StrN, 0xff);           free(TxBuffer);    }    .......   
   

将 SendWithOutDMA_Len();函数放到需要发送数据的功能下便可实现串口发送数据;这里将函数放在按键响应函数中:   
      ........    void KeyPass(void) {    ........           //按键发送数据        SendWithOutDMA_Len();    ......    }    .......   
   




通过Printf函数进行串口输出设置:      
      ........    /* USER CODE BEGIN Includes */    .......    #ifdef __GNUC__       /* With GCC, small printf (option LD Linker->Libraries->Small    printf          set to 'Yes') calls __io_putchar() */       #define PUTCHAR_PROTOTYPE int __io_putchar(int ch)    #else       #define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)    #endif /* __GNUC__ */    ......    /* USER CODE END Includes */    .......   
    /* USER CODE BEGIN 4 */    ........    /* retarget the C library printf    function to the USART */    PUTCHAR_PROTOTYPE    {       /* Place your implementation of fputc here */       /* e.g. write a character to the EVAL_COM1 and Loop until the end of    transmission */       HAL_UART_Transmit(&huart2, (uint8_t *)&ch, 1, 0xFFFF);        return ch;    }    .......    /* USER CODE END 4 */   
   
   
   
   

经过以上设置之后,即可用printf函数向串口输出字符串了printf("HelloSTM32L476RC! \n"); 3、实验的观察结果A)定时器:外接的LED(注意串接一个分压电阻)每一秒钟闪亮一次;B)串口:可通过SendWithOutDMA_Len()函数向串口输出信息;也可通过Printf("xxxxxxx")函数向串口输出信息;三、实验后的心得
    这个实验,可以了解到定时器回调函数、串口回调函数的使用方式,在串口发送数据的实验中,还体验到串口不同的数据发送方式(使用中断方式方式发送和接收数据,使用Printf函数发送数据)。


此前以为发了次文章,后来检查文章时才发现忘发此帖了(补一个)界此,基于Nucleo系列板卡的试验基础环境搭建好了,之后回基于这些基本LED、按键、中断、串口、定时器的基本功能做一系列的试验,下一个系列是低功耗系列
前文回顾:
   1、NUCLEO-L476RG试验(一)_LED闪闪亮(环境搭建与开发工具介绍)   2、NUCLEO-L476RG试验(二)_LED闪闪亮续一(按键、中断)
   3、NUCLEO-L476RG实验心得(三)_Eclipse环境配置

后文连接:
   1、ST线下培训(05-23成都站)STM32L476低功耗设计(一)

黑溱郎 发表于 2017-6-27 08:03:19

学习学习。。。。。。。

a2331 发表于 2018-10-15 16:05:52

谢谢大佬
页: [1]
查看完整版本: 4、NUCLEO-L476RG试验三_LED闪闪亮续(定时、串口)