当前位置:   article > 正文

STM32CubeIDE开发(九), 定时器TIM与外部中断NVIC实践案例_cubeidex 配置定时器

cubeidex 配置定时器

目录

一、定时器功能

         1.1 定时器分类

        1.2 定时时间计算

         1.3 中断功能

 二、工程创建及配置

        2.1 cubeMX图形配置

        2.2 代码设计

三、添加定时器及中断功能

        3.1 GPIO外设中断

         3.2 TIM2通用定时器,轮询

         3.3 TIM3通用定时器,辅助lpuart1通信

         3.4  TIM4通用定时器,PWM输出

四、定时器及中断功能代码设计

        4.1 GPIO中断功能 

         4.2 TIM2的定时轮询功能

        4.3 TIM4的计数功能

        4.4 TIM4定时器支持PWM输出功能实现

          4.5 功能调用及实现

五、编译及测试

        5.1 编译程序及下载

         5.2 测试


一、定时器功能

         1.1 定时器分类

        STM32 的定时器分为高级定时器、 通用定时器 、基本定时器三种。

        这三个定时器成上下级的关系,即基本定时器有的功能通用定时器都有,而且还增加了向下、向上/向下计数器、PWM生成、输出比较、输入捕获等功能;而高级定时器又包含了通用定时器的所有功能,另外还增加了死区互补输出、刹车信号。

        例如本文采用的STM32L496VGT6芯片中,高级定时器(TIM1、TIM8)、 通用定时器(TIM2、TIM3、TIM4、TIM5、TIM15,另外TIM5、TIM15亦有些许差异) 、基本定时器(TIM6、TIM7、TIM16、TIM17),高级定时器、 通用定时器 、基本定时器在STM32CubeMX配置界面中是不一样的:

        高级定时器(TIM1):

        通用定时器(TIM2):

        或通用定时器(TIM5):

         基本定时器(TIM6):

         在通用定时器,每个定时器都有一个16位的自动加载递增/递减计数器,一个16位的预分频器和4个独立通道,每个通道可用于输入捕获、输出比较、PWM和单脉冲模式输出。而TIM1、TIM8高级定时,其通道更是支持到6个通道,在一些高级芯片中支持到的通道会更多。

        每个定时器都支持外部中断机制,还可以结合计数功能或时钟功能,实现轮询策略;每个定时器也支持独立的DMA请求机制。

        通用定时器可以利用GPIO引脚进行脉冲输出,在配置为比较输出、PWM输出功能时,捕获/比较寄存器TIMx_CCR被用作比较功能。

        1.2 定时时间计算

        定时器输出时间间隔(us)=(分频系数+1)*(计数周期+1)÷输入定时器时钟频率(MHz,APB*),如下图所示。

         1.3 中断功能

        通常STM32芯片支持所有GPIO设置为外部中断输入,外部IO可以由电平上升、电平下降、高低电平三种方式的中断触发或事件触发。定时器作为外设之一,同样支持到外部中断或事件触发。

 二、工程创建及配置

        2.1 cubeMX图形配置

        本文基于STM32L496VGT6芯片创建工程,创建时钟、开启lpuart1串口,以及三个LED灯和按键KEY,配置如下:

        【1】系统配置

         【2】RCC功能开启外部高速时钟(默认参数配置)及时钟树设置

         时钟树设置,STM32L496VGT6支持最大输出频率80MHz,本文直接拉满

         【3】lpuart1串口开启

         开启lpuart1中断功能及按开发板引脚说明调整引脚:

         【4】配置三个按键及LED

         完成基本配置后,生成输出代码(每个外设独立的.h/.c文件)

        2.2 代码设计

        【1】禁用syscalls.c源文件

         【2】在工程目录下,创建源文件夹ICore文件目录,

         【2】并在ICore目录下创建led、key、print、usart目录。

         【3】在print目录创建print.h和print.c源文件,将用来实现将printf标准函数输出映射到lpuart1串口的驱动程序。

        print.h

  1. #ifndef INC_RETARGET_H_
  2. #define INC_RETARGET_H_
  3. #include "stm32l4xx_hal.h"
  4. #include "stdio.h"//用于printf函数串口重映射
  5. #include <sys/stat.h>
  6. void ResetPrintInit(UART_HandleTypeDef *huart);
  7. int _isatty(int fd);
  8. int _write(int fd, char* ptr, int len);
  9. int _close(int fd);
  10. int _lseek(int fd, int ptr, int dir);
  11. int _read(int fd, char* ptr, int len);
  12. int _fstat(int fd, struct stat* st);
  13. #endif /* INC_RETARGET_H_ */

         print.c

  1. #include <_ansi.h>
  2. #include <_syslist.h>
  3. #include <errno.h>
  4. #include <sys/time.h>
  5. #include <sys/times.h>
  6. #include <limits.h>
  7. #include <signal.h>
  8. #include <stdint.h>
  9. #include <stdio.h>
  10. #include "print.h"
  11. #if !defined(OS_USE_SEMIHOSTING)
  12. #define STDIN_FILENO 0
  13. #define STDOUT_FILENO 1
  14. #define STDERR_FILENO 2
  15. UART_HandleTypeDef *gHuart;
  16. void ResetPrintInit(UART_HandleTypeDef *huart) {
  17. gHuart = huart;
  18. /* Disable I/O buffering for STDOUT stream, so that
  19. * chars are sent out as soon as they are printed. */
  20. setvbuf(stdout, NULL, _IONBF, 0);
  21. }
  22. int _isatty(int fd) {
  23. if (fd >= STDIN_FILENO && fd <= STDERR_FILENO)
  24. return 1;
  25. errno = EBADF;
  26. return 0;
  27. }
  28. int _write(int fd, char* ptr, int len) {
  29. HAL_StatusTypeDef hstatus;
  30. if (fd == STDOUT_FILENO || fd == STDERR_FILENO) {
  31. hstatus = HAL_UART_Transmit(gHuart, (uint8_t *) ptr, len, HAL_MAX_DELAY);
  32. if (hstatus == HAL_OK)
  33. return len;
  34. else
  35. return EIO;
  36. }
  37. errno = EBADF;
  38. return -1;
  39. }
  40. int _close(int fd) {
  41. if (fd >= STDIN_FILENO && fd <= STDERR_FILENO)
  42. return 0;
  43. errno = EBADF;
  44. return -1;
  45. }
  46. int _lseek(int fd, int ptr, int dir) {
  47. (void) fd;
  48. (void) ptr;
  49. (void) dir;
  50. errno = EBADF;
  51. return -1;
  52. }
  53. int _read(int fd, char* ptr, int len) {
  54. HAL_StatusTypeDef hstatus;
  55. if (fd == STDIN_FILENO) {
  56. hstatus = HAL_UART_Receive(gHuart, (uint8_t *) ptr, 1, HAL_MAX_DELAY);
  57. if (hstatus == HAL_OK)
  58. return 1;
  59. else
  60. return EIO;
  61. }
  62. errno = EBADF;
  63. return -1;
  64. }
  65. int _fstat(int fd, struct stat* st) {
  66. if (fd >= STDIN_FILENO && fd <= STDERR_FILENO) {
  67. st->st_mode = S_IFCHR;
  68. return 0;
  69. }
  70. errno = EBADF;
  71. return 0;
  72. }
  73. #endif //#if !defined(OS_USE_SEMIHOSTING)

        【3】在led目录创建led.h和led.c源文件,将用来实现LED等状态控制的驱动程序。

        led.h

  1. #ifndef LED_H_
  2. #define LED_H_
  3. #include "main.h"
  4. #include "gpio.h"
  5. void Toggle_led0();
  6. void Toggle_led1();
  7. void Toggle_led2();
  8. void set_led0_val(GPIO_PinState PinState);
  9. void set_led1_val(GPIO_PinState PinState);
  10. void set_led2_val(GPIO_PinState PinState);
  11. #endif /* LED_H_ */

        led.c

  1. #include "led.h"
  2. void Toggle_led0()
  3. {
  4. HAL_GPIO_TogglePin(LED0_GPIO_Port,LED0_Pin);
  5. }
  6. void Toggle_led1()
  7. {
  8. HAL_GPIO_TogglePin(LED1_GPIO_Port,LED1_Pin);
  9. }
  10. void Toggle_led2()
  11. {
  12. HAL_GPIO_TogglePin(LED2_GPIO_Port,LED2_Pin);
  13. }
  14. void set_led0_val(GPIO_PinState PinState)
  15. {
  16. HAL_GPIO_WritePin(LED0_GPIO_Port,LED0_Pin,PinState);
  17. };
  18. void set_led1_val(GPIO_PinState PinState)
  19. {
  20. HAL_GPIO_WritePin(LED1_GPIO_Port,LED1_Pin,PinState);
  21. };
  22. void set_led2_val(GPIO_PinState PinState)
  23. {
  24. HAL_GPIO_WritePin(LED2_GPIO_Port,LED2_Pin,PinState);
  25. };

        【4】在key目录创建key.h和key.c源文件,将用来实现KEY控制的驱动程序。

        key.h

  1. #ifndef KEY_H_
  2. #define KEY_H_
  3. #include "main.h"
  4. #include "gpio.h"
  5. GPIO_PinState get_key0_val();
  6. GPIO_PinState get_key1_val();
  7. GPIO_PinState get_key2_val();
  8. uint8_t KEY_0(void);
  9. uint8_t KEY_1(void);
  10. uint8_t KEY_2(void);
  11. #endif /* KEY_H_ */

        key.c

  1. #include "key.h"
  2. GPIO_PinState get_key0_val()
  3. {
  4. return HAL_GPIO_ReadPin(KEY0_GPIO_Port,KEY0_Pin);
  5. };
  6. GPIO_PinState get_key1_val()
  7. {
  8. return HAL_GPIO_ReadPin(KEY1_GPIO_Port,KEY1_Pin);
  9. };
  10. GPIO_PinState get_key2_val()
  11. {
  12. return HAL_GPIO_ReadPin(KEY2_GPIO_Port,KEY2_Pin);
  13. };
  14. uint8_t KEY_0(void)
  15. {
  16. uint8_t a;
  17. a=0;//如果未进入按键处理,则返回0
  18. if(HAL_GPIO_ReadPin(KEY0_GPIO_Port,KEY0_Pin)==GPIO_PIN_RESET){//读按键接口的电平
  19. HAL_Delay(20);//延时去抖动
  20. if(HAL_GPIO_ReadPin(KEY0_GPIO_Port,KEY0_Pin)==GPIO_PIN_RESET){ //读按键接口的电平
  21. a=1;//进入按键处理,返回1
  22. }
  23. }
  24. while(HAL_GPIO_ReadPin(KEY0_GPIO_Port,KEY0_Pin)==GPIO_PIN_RESET); //等待按键松开
  25. return a;
  26. }
  27. uint8_t KEY_1(void)
  28. {
  29. uint8_t a;
  30. a=0;//如果未进入按键处理,则返回0
  31. if(HAL_GPIO_ReadPin(KEY1_GPIO_Port,KEY1_Pin)==GPIO_PIN_RESET){//读按键接口的电平
  32. HAL_Delay(20);//延时去抖动
  33. if(HAL_GPIO_ReadPin(KEY1_GPIO_Port,KEY1_Pin)==GPIO_PIN_RESET){ //读按键接口的电平
  34. a=1;//进入按键处理,返回1
  35. }
  36. }
  37. while(HAL_GPIO_ReadPin(KEY1_GPIO_Port,KEY1_Pin)==GPIO_PIN_RESET); //等待按键松开
  38. return a;
  39. }
  40. uint8_t KEY_2(void)
  41. {
  42. uint8_t a;
  43. a=0;//如果未进入按键处理,则返回0
  44. if(HAL_GPIO_ReadPin(KEY2_GPIO_Port,KEY2_Pin)==GPIO_PIN_RESET){//读按键接口的电平
  45. HAL_Delay(20);//延时去抖动
  46. if(HAL_GPIO_ReadPin(KEY2_GPIO_Port,KEY2_Pin)==GPIO_PIN_RESET){ //读按键接口的电平
  47. a=1;//进入按键处理,返回1
  48. }
  49. }
  50. while(HAL_GPIO_ReadPin(KEY2_GPIO_Port,KEY2_Pin)==GPIO_PIN_RESET); //等待按键松开
  51. return a;
  52. }

        【4】在usart目录创建usart.h和usart.c源文件,将用来实现lpuart1驱动程序。

        usart.h

  1. #ifndef INC_USART_H_
  2. #define INC_USART_H_
  3. #include "stm32l4xx_hal.h" //HAL库文件声明
  4. #include <string.h>//用于字符串处理的库
  5. #include "../print/print.h"//用于printf函数串口重映射
  6. extern UART_HandleTypeDef hlpuart1;//声明LPUSART的HAL库结构体
  7. #define HLPUSART_REC_LEN 256//定义LPUSART最大接收字节数
  8. extern uint8_t HLPUSART_RX_BUF[HLPUSART_REC_LEN];//接收缓冲,最大HLPUSART_REC_LEN个字节.末字节为换行符
  9. extern uint16_t HLPUSART_RX_STA;//接收状态标记
  10. extern uint8_t HLPUSART_NewData;//当前串口中断接收的1个字节数据的缓存
  11. void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart);//串口中断回调函数声明
  12. #endif /* INC_USART_H_ */

        usart.c

  1. #include "usart.h"
  2. uint8_t HLPUSART_RX_BUF[HLPUSART_REC_LEN];//接收缓冲,最大HLPUSART_REC_LEN个字节.末字节为换行符
  3. /*
  4. * bit15:接收到回车(0x0d)时设置HLPUSART_RX_STA|=0x8000;
  5. * bit14:接收溢出标志,数据超出缓存长度时,设置HLPUSART_RX_STA|=0x4000;
  6. * bit13:预留
  7. * bit12:预留
  8. * bit11~0:接收到的有效字节数目(0~4095)
  9. */
  10. uint16_t HLPUSART_RX_STA=0;接收状态标记//bit15:接收完成标志,bit14:接收到回车(0x0d),bit13~0:接收到的有效字节数目
  11. uint8_t HLPUSART_NewData;//当前串口中断接收的1个字节数据的缓存
  12. void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)//串口中断回调函数
  13. {
  14. if(huart ==&hlpuart1)//判断中断来源(串口1:USB转串口)
  15. {
  16. if(HLPUSART_NewData==0x0d){//回车标记
  17. HLPUSART_RX_STA|=0x8000;//标记接到回车
  18. }else{
  19. if((HLPUSART_RX_STA&0X0FFF)<HLPUSART_REC_LEN){
  20. HLPUSART_RX_BUF[HLPUSART_RX_STA&0X0FFF]=HLPUSART_NewData; //将收到的数据放入数组
  21. HLPUSART_RX_STA++; //数据长度计数加1
  22. }else{
  23. HLPUSART_RX_STA|=0x4000;//数据超出缓存长度,标记溢出
  24. }
  25. }
  26. HAL_UART_Receive_IT(&hlpuart1,(uint8_t *)&HLPUSART_NewData,1); //再开启接收中断
  27. }
  28. }

        【5】在main.c文件中加入各驱动文件的头文件

  1. /* USER CODE BEGIN Includes */
  2. #include "../../ICore/key/key.h"
  3. #include "../../ICore/led/led.h"
  4. #include "../../ICore/print/print.h"
  5. #include "../../ICore/usart/usart.h"
  6. /* USER CODE END Includes */

        在主函数中,初始化lpuart1

  1. /* USER CODE BEGIN 2 */
  2. ResetPrintInit(&hlpuart1);
  3. HAL_UART_Receive_IT(&hlpuart1,(uint8_t *)&HLPUSART_NewData, 1); //再开启接收中断
  4. HLPUSART_RX_STA = 0;
  5. /* USER CODE END 2 */

        在主函数循环体中,实现lpuart1输入返回测试及按键时LED灯状态反转。

  1. /* USER CODE BEGIN WHILE */
  2. while (1)
  3. {
  4. if(HLPUSART_RX_STA&0xC000){//溢出或换行,重新开始
  5. printf("receive:%.*s\r\n",HLPUSART_RX_STA&0X0FFF, HLPUSART_RX_BUF);
  6. HLPUSART_RX_STA=0;//接收错误,重新开始
  7. HAL_Delay(100);//等待
  8. }
  9. if(KEY_0())
  10. {
  11. Toggle_led0();//LED0等状态反转一次
  12. }
  13. if(KEY_1())
  14. {
  15. Toggle_led1();//LED1等状态反转一次
  16. }
  17. if(KEY_2())
  18. {
  19. Toggle_led2();//LED2等状态反转一次
  20. }
  21. /* USER CODE END WHILE */

        【6】配置输出文件格式支持,然后编译代码

        【7】配置调试及下载支持

         运行下载烧写程序到开发板,完成基本功能开发。

三、添加定时器及中断功能

        3.1 GPIO外设中断

        打开.ioc进入cubeMX配置界面,将KEY0按钮调整为GPIO_EXTI类型

        并开启KEY0(PE11)的中断支持

         3.2 TIM2通用定时器,轮询

        添加TIM2通用定时器功能实现定时轮询功能

         并开启 TIM2的中断功能

         3.3 TIM3通用定时器,辅助lpuart1通信

        添加TIM3通用定时器功能实现间隔计时,辅助lpuart1串口在没有结束标记情况下实现超时(100ms)通知

         并开启TIM3的中断功能支持

         3.4  TIM4通用定时器,PWM输出

        添加TIM4通用定时器功能,开启其通道4对PWM输出功能支持,将KEY2(PD15)调整为TIM_CH4类型。PWM是定时器扩展出来的一个功能(本质上是使用一个比较计数器的功能),配置过程一般为选定定时器、复用GPIO口、选择通道(传入比较值)、使能相应系统时钟、设定相应的预分频、计数周期、PWM模式(有两种)、电平极性等。

        并开启TIM4的中断功能支持

         配置KEY2(PD15)GPIO引脚为交替推免GPIO模式,如下图。

         上述配置完成后输出生成代码。

四、定时器及中断功能代码设计

        4.1 GPIO中断功能 

        已经配置了KEY0(PE11)为GPIO_EXTI模式,并开启其中断支持,现需要重新实现stm32l4xx_hal_gpio.c文件中的HAL_GPIO_EXTI_Callback函数来实现GPIO外部中断回调功能。

         在main.c文件中,重新实现HAL_GPIO_EXTI_Callback函数,覆盖stm32l4xx_hal_gpio.c文件中HAL_GPIO_EXTI_Callback弱函数。

  1. /* USER CODE BEGIN 0 */
  2. void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
  3. {
  4. if(GPIO_Pin==KEY0_Pin)
  5. {
  6. static uint32_t key0_count = 0;
  7. printf("this is %lu count for HAL_GPIO_EXTI_Callback!\r\n",key0_count++);
  8. Toggle_led0();//LED0等状态反转一次
  9. }
  10. }
  11. /* USER CODE END 0 */

         4.2 TIM2的定时轮询功能

          已经配置了TIM2,并开启其中断支持,现需要重新实现stm32l4xx_hal_tim.c文件中的当计数溢出时调用的回调函数HAL_TIM_PeriodElapsedCallback。

         在mian.c文件中,重新实现HAL_TIM_PeriodElapsedCallback函数,覆盖stm32l4xx_hal_tim.c文件中的同名弱函数。

  1. /* USER CODE BEGIN 0 */
  2. void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
  3. {
  4. if(GPIO_Pin==KEY0_Pin)
  5. {
  6. static uint32_t key0_count = 0;
  7. printf("this is %lu count for HAL_GPIO_EXTI_Callback!\r\n",key0_count++);
  8. Toggle_led0();//LED0等状态反转一次
  9. }
  10. }
  11. void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
  12. {
  13. if(htim==&htim2)
  14. {
  15. static uint32_t key1_count = 0;
  16. printf("this is %lu count for HAL_TIM_PeriodElapsedCallback!\r\n",key1_count++);
  17. Toggle_led1();//LED1等状态反转一次
  18. }
  19. }
  20. /* USER CODE END 0 */

        4.3 TIM4的计数功能

       同样已经配置了TIM4,并开启其中断支持,现需要重新实现stm32l4xx_hal_tim.c文件中的当计数溢出时调用的回调函数HAL_TIM_PeriodElapsedCallback,在计数结束时,改写lpuart1的接收标记。

  1. /* USER CODE BEGIN 0 */
  2. void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
  3. {
  4. if(GPIO_Pin==KEY0_Pin)
  5. {
  6. static uint32_t key0_count = 0;
  7. printf("this is %lu count for HAL_GPIO_EXTI_Callback!\r\n",key0_count++);
  8. Toggle_led0();//LED0等状态反转一次
  9. }
  10. }
  11. void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
  12. {
  13. if(htim==&htim2)
  14. {
  15. static uint32_t key1_count = 0;
  16. printf("this is %lu count for HAL_TIM_PeriodElapsedCallback!\r\n",key1_count++);
  17. Toggle_led1();//LED1等状态反转一次
  18. }
  19. if(htim ==&htim3)//判断是否是定时器3中断(定时器到时表示一组字符串接收结束)
  20. {
  21. HLPUSART_RX_BUF[HLPUSART_RX_STA&0X0FFF]=0;//添加结束符
  22. HLPUSART_RX_STA|=0x8000;//接收标志位最高位置1表示接收完成
  23. __HAL_TIM_CLEAR_FLAG(&htim3,TIM_EVENTSOURCE_UPDATE );//清除TIM3更新中断标志
  24. __HAL_TIM_DISABLE(&htim3);//关闭定时器3
  25. }
  26. }
  27. /* USER CODE END 0 */

         同时在usart.h增加对TIM3的引入

  1. #ifndef INC_USART_H_
  2. #define INC_USART_H_
  3. #include "stm32l4xx_hal.h" //HAL库文件声明
  4. #include <string.h>//用于字符串处理的库
  5. #include "../print/print.h"//用于printf函数串口重映射
  6. extern TIM_HandleTypeDef htim3; //定时器3辅助串口接收数据
  7. extern UART_HandleTypeDef hlpuart1;//声明LPUSART的HAL库结构体
  8. #define HLPUSART_REC_LEN 256//定义LPUSART最大接收字节数
  9. extern uint8_t HLPUSART_RX_BUF[HLPUSART_REC_LEN];//接收缓冲,最大HLPUSART_REC_LEN个字节.末字节为换行符
  10. extern uint16_t HLPUSART_RX_STA;//接收状态标记
  11. extern uint8_t HLPUSART_NewData;//当前串口中断接收的1个字节数据的缓存
  12. void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart);//串口中断回调函数声明
  13. #endif /* INC_USART_H_ */

        在usart.c中串口回调函数的lpuart1接收实现方式,通过htim3(TIM3)计数周期通知来告知lpuart1结束数据接收。

  1. #include "usart.h"
  2. uint8_t HLPUSART_RX_BUF[HLPUSART_REC_LEN];//接收缓冲,最大HLPUSART_REC_LEN个字节.末字节为换行符
  3. /*
  4. * bit15:接收到回车(0x0d)时设置HLPUSART_RX_STA|=0x8000;
  5. * bit14:接收溢出标志,数据超出缓存长度时,设置HLPUSART_RX_STA|=0x4000;
  6. * bit13:预留
  7. * bit12:预留
  8. * bit11~0:接收到的有效字节数目(0~4095)
  9. */
  10. uint16_t HLPUSART_RX_STA=0;接收状态标记//bit15:接收完成标志,bit14:接收到回车(0x0d),bit13~0:接收到的有效字节数目
  11. uint8_t HLPUSART_NewData;//当前串口中断接收的1个字节数据的缓存
  12. void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)//串口中断回调函数
  13. {
  14. // if(huart ==&hlpuart1)//判断中断来源(串口1:USB转串口)
  15. // {
  16. // if(HLPUSART_NewData==0x0d){//回车标记
  17. // HLPUSART_RX_STA|=0x8000;//标记接到回车
  18. // }else{
  19. // if((HLPUSART_RX_STA&0X0FFF)<HLPUSART_REC_LEN){
  20. // HLPUSART_RX_BUF[HLPUSART_RX_STA&0X0FFF]=HLPUSART_NewData; //将收到的数据放入数组
  21. // HLPUSART_RX_STA++; //数据长度计数加1
  22. // }else{
  23. // HLPUSART_RX_STA|=0x4000;//数据超出缓存长度,标记溢出
  24. // }
  25. // }
  26. // HAL_UART_Receive_IT(&hlpuart1,(uint8_t *)&HLPUSART_NewData,1); //再开启接收中断
  27. // }
  28. //调整,通过htim3计数来通知接收数据结束
  29. if(huart ==&hlpuart1)//判断中断来源(串口lpuart1)//接收完的一批数据,还没有被处理,则不再接收其他数据
  30. {
  31. if(HLPUSART_RX_STA<HLPUSART_REC_LEN)//还可以接收数据
  32. {
  33. __HAL_TIM_SET_COUNTER(&htim3,0); //计数器清空
  34. if(HLPUSART_RX_STA==0) //使能定时器2的中断
  35. {
  36. __HAL_TIM_ENABLE(&htim3); //使能定时器3
  37. }
  38. HLPUSART_RX_BUF[HLPUSART_RX_STA++] = HLPUSART_NewData;//最新接收数据放入数组
  39. }else
  40. {
  41. HLPUSART_RX_STA|=0x8000;//强制标记接收完成
  42. }
  43. HAL_UART_Receive_IT(&hlpuart1,(uint8_t *)&HLPUSART_NewData,1); //再开启串口1接收中断
  44. }
  45. }

        4.4 TIM4定时器支持PWM输出功能实现

        TIM4定时器已经开启了通道4,向LED2(PD15)交替输出递减电压实现LED灯状态由亮变暗直至熄灭的循环,首选需要通过HAL_TIM_PWM_Start开启PWM功能,然后通过__HAL_TIM_SetCompare来设置占空比来实现输出信号调节。

        PWM输出工作过程:若配置脉冲计数器TIMx_CNT为向上计数,而重载寄存器TIMx_ARR被配置为N,即TIMx_CNT的当前计数值数值X在TIMxCLK时钟源的驱动下不断累加,当TIMx_CNT的数值X(对应下面代码的a)大于N(对应cubeMX配置的ARR值,即99)时,会重置TIMx_CNT数值为0重新计数 。

  1. HAL_TIM_PWM_Start(&htim4,TIM_CHANNEL_4);
  2. uint8_t a = ;
  3. while(1){
  4. __HAL_TIM_SetCompare(&htim4,TIM_CHANNEL_4,a);
  5. a++;//占空比值加1
  6. if(a>=99)a=0;
  7. HAL_Delay(10);
  8. }

          4.5 功能调用及实现

        在main.c函数中,初始化及启动TIM定时器

  1. /* USER CODE BEGIN 2 */
  2. ResetPrintInit(&hlpuart1);
  3. HAL_UART_Receive_IT(&hlpuart1,(uint8_t *)&HLPUSART_NewData, 1); //再开启接收中断
  4. HLPUSART_RX_STA = 0;
  5. HAL_TIM_Base_Start_IT(&htim2);//开启定时器2中断(必须开启才能进入中断处理回调函数)
  6. HAL_TIM_Base_Start_IT(&htim3);//开启定时器3中断(必须开启才能进入中断处理回调函数),辅助lpuart1接收数据
  7. HAL_TIM_PWM_Start(&htim4,TIM_CHANNEL_4);
  8. uint8_t memu = 0; //用来标记按键KEY2按下,来开启或关闭TIM4的PWM功能
  9. uint16_t a = 0; //占空比值
  10. /* USER CODE END 2 */

        在main函数循环中,实现如下:

  1. /* USER CODE BEGIN WHILE */
  2. while (1)
  3. {
  4. if(HLPUSART_RX_STA&0xC000){//溢出或换行,重新开始
  5. printf("receive:%.*s\r\n",HLPUSART_RX_STA&0X0FFF, HLPUSART_RX_BUF);
  6. HLPUSART_RX_STA=0;//接收错误,重新开始
  7. HAL_Delay(100);//等待
  8. }
  9. if(KEY_1())
  10. {
  11. }
  12. if(KEY_2())
  13. {
  14. memu=!memu;
  15. printf("TIM4_PWM is runing!\r\n");
  16. }
  17. if(memu)
  18. {
  19. __HAL_TIM_SetCompare(&htim4,TIM_CHANNEL_4,a);
  20. a++;//占空比值加1
  21. if(a>=99)
  22. {
  23. printf("TIM4_PWM finish cycle!\r\n");
  24. a=0;
  25. }
  26. HAL_Delay(10);//等待
  27. }
  28. /* USER CODE END WHILE */

五、编译及测试

        5.1 编译程序及下载

         5.2 测试

        【1】测试log输出

         【2】注释掉相关打印输出,测试lpuart1输入数据无回车时(lpuart1的HAL_UART_Receive_IT调用后,TIM3每100毫秒通知其结束接收),能成功收到返回数据,如下图。

         【3】LED等状态情况

        按键KEY0顺利切换LED0的状态,LED1灯周期切换状态(TIM2),按键KEY2开启TIM4的PWM输出功能,LED2灯周期由亮到灭切换状态(TIM4)。

        https://live.csdn.net/v/263027

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小舞很执着/article/detail/739180
推荐阅读
相关标签
  

闽ICP备14008679号