cubeIDE开发, 定时器TIM与外部中断NVIC实践案例

一、定时器功能

         1.1 定时器分类

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

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

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

        高级定时器(TIM1):

cubeIDE开发, 定时器TIM与外部中断NVIC实践案例_第1张图片

        通用定时器(TIM2):

cubeIDE开发, 定时器TIM与外部中断NVIC实践案例_第2张图片

        或通用定时器(TIM5):

cubeIDE开发, 定时器TIM与外部中断NVIC实践案例_第3张图片

         基本定时器(TIM6):

cubeIDE开发, 定时器TIM与外部中断NVIC实践案例_第4张图片

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

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

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

        1.2 定时时间计算

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

cubeIDE开发, 定时器TIM与外部中断NVIC实践案例_第5张图片

         1.3 中断功能

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

cubeIDE开发, 定时器TIM与外部中断NVIC实践案例_第6张图片

 二、工程创建及配置

        2.1 cubeMX图形配置

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

        【1】系统配置

cubeIDE开发, 定时器TIM与外部中断NVIC实践案例_第7张图片

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

cubeIDE开发, 定时器TIM与外部中断NVIC实践案例_第8张图片

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

cubeIDE开发, 定时器TIM与外部中断NVIC实践案例_第9张图片

         【3】lpuart1串口开启

cubeIDE开发, 定时器TIM与外部中断NVIC实践案例_第10张图片

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

cubeIDE开发, 定时器TIM与外部中断NVIC实践案例_第11张图片

         【4】配置三个按键及LED

cubeIDE开发, 定时器TIM与外部中断NVIC实践案例_第12张图片

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

        2.2 代码设计

        【1】禁用syscalls.c源文件

cubeIDE开发, 定时器TIM与外部中断NVIC实践案例_第13张图片

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

cubeIDE开发, 定时器TIM与外部中断NVIC实践案例_第14张图片

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

cubeIDE开发, 定时器TIM与外部中断NVIC实践案例_第15张图片

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

        print.h

#ifndef INC_RETARGET_H_
#define INC_RETARGET_H_

#include "stm32l4xx_hal.h"
#include "stdio.h"//用于printf函数串口重映射
#include 

void ResetPrintInit(UART_HandleTypeDef  *huart);

int _isatty(int fd);
int _write(int fd, char* ptr, int len);
int _close(int fd);
int _lseek(int fd, int ptr, int dir);
int _read(int fd, char* ptr, int len);
int _fstat(int fd, struct stat* st);

#endif /* INC_RETARGET_H_ */

         print.c

#include <_ansi.h>
#include <_syslist.h>
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include "print.h"

#if !defined(OS_USE_SEMIHOSTING)
#define STDIN_FILENO  0
#define STDOUT_FILENO 1
#define STDERR_FILENO 2

UART_HandleTypeDef *gHuart;

void ResetPrintInit(UART_HandleTypeDef *huart)  {
  gHuart = huart;
  /* Disable I/O buffering for STDOUT  stream, so that
   * chars are sent out as soon as they are  printed. */
  setvbuf(stdout, NULL, _IONBF, 0);
}
int _isatty(int fd) {
  if (fd >= STDIN_FILENO && fd <=  STDERR_FILENO)
    return 1;
  errno = EBADF;
  return 0;
}
int _write(int fd, char* ptr, int len) {
  HAL_StatusTypeDef hstatus;
  if (fd == STDOUT_FILENO || fd ==  STDERR_FILENO) {
    hstatus = HAL_UART_Transmit(gHuart,  (uint8_t *) ptr, len, HAL_MAX_DELAY);
    if (hstatus == HAL_OK)
      return len;
    else
      return EIO;
  }
  errno = EBADF;
  return -1;
}
int _close(int fd) {
  if (fd >= STDIN_FILENO && fd <=  STDERR_FILENO)
    return 0;
  errno = EBADF;
  return -1;
}
int _lseek(int fd, int ptr, int dir) {
  (void) fd;
  (void) ptr;
  (void) dir;
  errno = EBADF;
  return -1;
}
int _read(int fd, char* ptr, int len) {
  HAL_StatusTypeDef hstatus;
  if (fd == STDIN_FILENO) {
    hstatus = HAL_UART_Receive(gHuart,  (uint8_t *) ptr, 1, HAL_MAX_DELAY);
    if (hstatus == HAL_OK)
      return 1;
    else
      return EIO;
  }
  errno = EBADF;
  return -1;
}
int _fstat(int fd, struct stat* st) {
  if (fd >= STDIN_FILENO && fd <=  STDERR_FILENO) {
    st->st_mode = S_IFCHR;
    return 0;
  }
  errno = EBADF;
  return 0;
}

#endif //#if !defined(OS_USE_SEMIHOSTING)

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

        led.h

#ifndef LED_H_
#define LED_H_
#include "main.h"
#include "gpio.h"

void Toggle_led0();
void Toggle_led1();
void Toggle_led2();

void set_led0_val(GPIO_PinState PinState);
void set_led1_val(GPIO_PinState PinState);
void set_led2_val(GPIO_PinState PinState);

#endif /* LED_H_ */

        led.c

#include "led.h"

void Toggle_led0()
{
	HAL_GPIO_TogglePin(LED0_GPIO_Port,LED0_Pin);
}

void Toggle_led1()
{
	HAL_GPIO_TogglePin(LED1_GPIO_Port,LED1_Pin);
}

void Toggle_led2()
{
	HAL_GPIO_TogglePin(LED2_GPIO_Port,LED2_Pin);
}

void set_led0_val(GPIO_PinState PinState)
{
	HAL_GPIO_WritePin(LED0_GPIO_Port,LED0_Pin,PinState);
};

void set_led1_val(GPIO_PinState PinState)
{
	HAL_GPIO_WritePin(LED1_GPIO_Port,LED1_Pin,PinState);
};

void set_led2_val(GPIO_PinState PinState)
{
	HAL_GPIO_WritePin(LED2_GPIO_Port,LED2_Pin,PinState);
};

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

        key.h

#ifndef KEY_H_
#define KEY_H_

#include "main.h"
#include "gpio.h"

GPIO_PinState get_key0_val();
GPIO_PinState get_key1_val();
GPIO_PinState get_key2_val();

uint8_t KEY_0(void);
uint8_t KEY_1(void);
uint8_t KEY_2(void);

#endif /* KEY_H_ */

        key.c

#include "key.h"

GPIO_PinState get_key0_val()
{
	return HAL_GPIO_ReadPin(KEY0_GPIO_Port,KEY0_Pin);
};

GPIO_PinState get_key1_val()
{
	return HAL_GPIO_ReadPin(KEY1_GPIO_Port,KEY1_Pin);
};

GPIO_PinState get_key2_val()
{
	return HAL_GPIO_ReadPin(KEY2_GPIO_Port,KEY2_Pin);
};

uint8_t KEY_0(void)
{
	uint8_t a;
	a=0;//如果未进入按键处理,则返回0
	if(HAL_GPIO_ReadPin(KEY0_GPIO_Port,KEY0_Pin)==GPIO_PIN_RESET){//读按键接口的电平
		HAL_Delay(20);//延时去抖动
		if(HAL_GPIO_ReadPin(KEY0_GPIO_Port,KEY0_Pin)==GPIO_PIN_RESET){ //读按键接口的电平
			a=1;//进入按键处理,返回1
		}
	}
	while(HAL_GPIO_ReadPin(KEY0_GPIO_Port,KEY0_Pin)==GPIO_PIN_RESET); //等待按键松开
	return a;
}

uint8_t KEY_1(void)
{
	uint8_t a;
	a=0;//如果未进入按键处理,则返回0
	if(HAL_GPIO_ReadPin(KEY1_GPIO_Port,KEY1_Pin)==GPIO_PIN_RESET){//读按键接口的电平
		HAL_Delay(20);//延时去抖动
		if(HAL_GPIO_ReadPin(KEY1_GPIO_Port,KEY1_Pin)==GPIO_PIN_RESET){ //读按键接口的电平
			a=1;//进入按键处理,返回1
		}
	}
	while(HAL_GPIO_ReadPin(KEY1_GPIO_Port,KEY1_Pin)==GPIO_PIN_RESET); //等待按键松开
	return a;
}

uint8_t KEY_2(void)
{
	uint8_t a;
	a=0;//如果未进入按键处理,则返回0
	if(HAL_GPIO_ReadPin(KEY2_GPIO_Port,KEY2_Pin)==GPIO_PIN_RESET){//读按键接口的电平
		HAL_Delay(20);//延时去抖动
		if(HAL_GPIO_ReadPin(KEY2_GPIO_Port,KEY2_Pin)==GPIO_PIN_RESET){ //读按键接口的电平
			a=1;//进入按键处理,返回1
		}
	}
	while(HAL_GPIO_ReadPin(KEY2_GPIO_Port,KEY2_Pin)==GPIO_PIN_RESET); //等待按键松开
	return a;
}

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

        usart.h

#ifndef INC_USART_H_
#define INC_USART_H_

#include "stm32l4xx_hal.h" //HAL库文件声明
#include //用于字符串处理的库
#include "../print/print.h"//用于printf函数串口重映射

extern UART_HandleTypeDef hlpuart1;//声明LPUSART的HAL库结构体

#define HLPUSART_REC_LEN  256//定义LPUSART最大接收字节数

extern uint8_t  HLPUSART_RX_BUF[HLPUSART_REC_LEN];//接收缓冲,最大HLPUSART_REC_LEN个字节.末字节为换行符
extern uint16_t HLPUSART_RX_STA;//接收状态标记
extern uint8_t HLPUSART_NewData;//当前串口中断接收的1个字节数据的缓存


void  HAL_UART_RxCpltCallback(UART_HandleTypeDef  *huart);//串口中断回调函数声明

#endif /* INC_USART_H_ */

        usart.c

#include "usart.h"

uint8_t  HLPUSART_RX_BUF[HLPUSART_REC_LEN];//接收缓冲,最大HLPUSART_REC_LEN个字节.末字节为换行符
/*
 * bit15:接收到回车(0x0d)时设置HLPUSART_RX_STA|=0x8000;
 * bit14:接收溢出标志,数据超出缓存长度时,设置HLPUSART_RX_STA|=0x4000;
 * bit13:预留
 * bit12:预留
 * bit11~0:接收到的有效字节数目(0~4095)
 */
uint16_t HLPUSART_RX_STA=0;接收状态标记//bit15:接收完成标志,bit14:接收到回车(0x0d),bit13~0:接收到的有效字节数目
uint8_t HLPUSART_NewData;//当前串口中断接收的1个字节数据的缓存

void  HAL_UART_RxCpltCallback(UART_HandleTypeDef  *huart)//串口中断回调函数
{
	if(huart ==&hlpuart1)//判断中断来源(串口1:USB转串口)
    {
		if(HLPUSART_NewData==0x0d){//回车标记
     	  HLPUSART_RX_STA|=0x8000;//标记接到回车
		}else{
			if((HLPUSART_RX_STA&0X0FFF)

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

/* USER CODE BEGIN Includes */
#include "../../ICore/key/key.h"
#include "../../ICore/led/led.h"
#include "../../ICore/print/print.h"
#include "../../ICore/usart/usart.h"
/* USER CODE END Includes */

        在主函数中,初始化lpuart1

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

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

 /* USER CODE BEGIN WHILE */
  while (1)
  {
	  if(HLPUSART_RX_STA&0xC000){//溢出或换行,重新开始
  		  printf("receive:%.*s\r\n",HLPUSART_RX_STA&0X0FFF, HLPUSART_RX_BUF);
  		  HLPUSART_RX_STA=0;//接收错误,重新开始
  		  HAL_Delay(100);//等待
  	  }
      if(KEY_0())
	  {
        Toggle_led0();//LED0等状态反转一次
	  }
  	  if(KEY_1())
	  {
        Toggle_led1();//LED1等状态反转一次
	  }
  	  if(KEY_2())
	  {
  		  Toggle_led2();//LED2等状态反转一次
	  }
    /* USER CODE END WHILE */

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

cubeIDE开发, 定时器TIM与外部中断NVIC实践案例_第16张图片

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

cubeIDE开发, 定时器TIM与外部中断NVIC实践案例_第17张图片

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

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

        3.1 GPIO外设中断

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

cubeIDE开发, 定时器TIM与外部中断NVIC实践案例_第18张图片

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

cubeIDE开发, 定时器TIM与外部中断NVIC实践案例_第19张图片

         3.2 TIM2通用定时器,轮询

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

cubeIDE开发, 定时器TIM与外部中断NVIC实践案例_第20张图片

         并开启 TIM2的中断功能

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

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

cubeIDE开发, 定时器TIM与外部中断NVIC实践案例_第21张图片

         并开启TIM3的中断功能支持

         3.4  TIM4通用定时器,PWM输出

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

cubeIDE开发, 定时器TIM与外部中断NVIC实践案例_第22张图片

        并开启TIM4的中断功能支持

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

cubeIDE开发, 定时器TIM与外部中断NVIC实践案例_第23张图片

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

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

        4.1 GPIO中断功能 

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

cubeIDE开发, 定时器TIM与外部中断NVIC实践案例_第24张图片

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

/* USER CODE BEGIN 0 */
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
	if(GPIO_Pin==KEY0_Pin)
	{
		static uint32_t key0_count = 0;
		printf("this is %lu count for HAL_GPIO_EXTI_Callback!\r\n",key0_count++);
		Toggle_led0();//LED0等状态反转一次
	}
}
/* USER CODE END 0 */

         4.2 TIM2的定时轮询功能

          已经配置了TIM2,并开启其中断支持,现需要重新实现stm32l4xx_hal_tim.c文件中的当计数溢出时调用的回调函数HAL_TIM_PeriodElapsedCallback。cubeIDE开发, 定时器TIM与外部中断NVIC实践案例_第25张图片

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

/* USER CODE BEGIN 0 */
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
	if(GPIO_Pin==KEY0_Pin)
	{
		static uint32_t key0_count = 0;
		printf("this is %lu count for HAL_GPIO_EXTI_Callback!\r\n",key0_count++);
		Toggle_led0();//LED0等状态反转一次
	}
}

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
	if(htim==&htim2)
	{
		static uint32_t key1_count = 0;
		printf("this is %lu count for HAL_TIM_PeriodElapsedCallback!\r\n",key1_count++);
		Toggle_led1();//LED1等状态反转一次
	}
}
/* USER CODE END 0 */

        4.3 TIM4的计数功能

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

/* USER CODE BEGIN 0 */
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
	if(GPIO_Pin==KEY0_Pin)
	{
		static uint32_t key0_count = 0;
		printf("this is %lu count for HAL_GPIO_EXTI_Callback!\r\n",key0_count++);
		Toggle_led0();//LED0等状态反转一次
	}
}

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
	if(htim==&htim2)
	{
		static uint32_t key1_count = 0;
		printf("this is %lu count for HAL_TIM_PeriodElapsedCallback!\r\n",key1_count++);
		Toggle_led1();//LED1等状态反转一次
	}
	if(htim ==&htim3)//判断是否是定时器3中断(定时器到时表示一组字符串接收结束)
    {
		HLPUSART_RX_BUF[HLPUSART_RX_STA&0X0FFF]=0;//添加结束符
		HLPUSART_RX_STA|=0x8000;//接收标志位最高位置1表示接收完成
		__HAL_TIM_CLEAR_FLAG(&htim3,TIM_EVENTSOURCE_UPDATE );//清除TIM3更新中断标志
		__HAL_TIM_DISABLE(&htim3);//关闭定时器3
    }
}
/* USER CODE END 0 */

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

#ifndef INC_USART_H_
#define INC_USART_H_

#include "stm32l4xx_hal.h" //HAL库文件声明
#include //用于字符串处理的库
#include "../print/print.h"//用于printf函数串口重映射

extern TIM_HandleTypeDef htim3;	//定时器3辅助串口接收数据

extern UART_HandleTypeDef hlpuart1;//声明LPUSART的HAL库结构体

#define HLPUSART_REC_LEN  256//定义LPUSART最大接收字节数

extern uint8_t  HLPUSART_RX_BUF[HLPUSART_REC_LEN];//接收缓冲,最大HLPUSART_REC_LEN个字节.末字节为换行符
extern uint16_t HLPUSART_RX_STA;//接收状态标记
extern uint8_t HLPUSART_NewData;//当前串口中断接收的1个字节数据的缓存


void  HAL_UART_RxCpltCallback(UART_HandleTypeDef  *huart);//串口中断回调函数声明

#endif /* INC_USART_H_ */

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

#include "usart.h"

uint8_t  HLPUSART_RX_BUF[HLPUSART_REC_LEN];//接收缓冲,最大HLPUSART_REC_LEN个字节.末字节为换行符
/*
 * bit15:接收到回车(0x0d)时设置HLPUSART_RX_STA|=0x8000;
 * bit14:接收溢出标志,数据超出缓存长度时,设置HLPUSART_RX_STA|=0x4000;
 * bit13:预留
 * bit12:预留
 * bit11~0:接收到的有效字节数目(0~4095)
 */
uint16_t HLPUSART_RX_STA=0;接收状态标记//bit15:接收完成标志,bit14:接收到回车(0x0d),bit13~0:接收到的有效字节数目
uint8_t HLPUSART_NewData;//当前串口中断接收的1个字节数据的缓存

void  HAL_UART_RxCpltCallback(UART_HandleTypeDef  *huart)//串口中断回调函数
{
//	if(huart ==&hlpuart1)//判断中断来源(串口1:USB转串口)
//    {
//		if(HLPUSART_NewData==0x0d){//回车标记
//     	  HLPUSART_RX_STA|=0x8000;//标记接到回车
//		}else{
//			if((HLPUSART_RX_STA&0X0FFF)

        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重新计数 。

HAL_TIM_PWM_Start(&htim4,TIM_CHANNEL_4);
uint8_t a = ;
while(1){
    __HAL_TIM_SetCompare(&htim4,TIM_CHANNEL_4,a);
  	a++;//占空比值加1
  	if(a>=99)a=0;
    HAL_Delay(10);
}

          4.5 功能调用及实现

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

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

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

 /* USER CODE BEGIN WHILE */
  while (1)
  {
	  if(HLPUSART_RX_STA&0xC000){//溢出或换行,重新开始
  		  printf("receive:%.*s\r\n",HLPUSART_RX_STA&0X0FFF, HLPUSART_RX_BUF);
  		  HLPUSART_RX_STA=0;//接收错误,重新开始
  		  HAL_Delay(100);//等待
  	  }
  	  if(KEY_1())
	  {
	  }
  	  if(KEY_2())
	  {
  		  memu=!memu;
  		  printf("TIM4_PWM is runing!\r\n");
	  }
  	  if(memu)
  	  {
  		  __HAL_TIM_SetCompare(&htim4,TIM_CHANNEL_4,a);
  		  a++;//占空比值加1
  		  if(a>=99)
  		  {
  			  printf("TIM4_PWM finish cycle!\r\n");
  			  a=0;
  		  }
  		  HAL_Delay(10);//等待
  	  }
    /* USER CODE END WHILE */

五、编译及测试

        5.1 编译程序及下载

cubeIDE开发, 定时器TIM与外部中断NVIC实践案例_第26张图片

         5.2 测试

        【1】测试log输出

cubeIDE开发, 定时器TIM与外部中断NVIC实践案例_第27张图片

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

cubeIDE开发, 定时器TIM与外部中断NVIC实践案例_第28张图片

         【3】LED等状态情况

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

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

你可能感兴趣的:(STM32开发案例,stm32,cubeIDE,cubeMX,stm32Cube,定时器)