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

news2025/2/25 22:23:23

一、定时器功能

         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

#ifndef INC_RETARGET_H_
#define INC_RETARGET_H_

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

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 <errno.h>
#include <sys/time.h>
#include <sys/times.h>
#include <limits.h>
#include <signal.h>
#include <stdint.h>
#include <stdio.h>

#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 <string.h>//用于字符串处理的库
#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)<HLPUSART_REC_LEN){
				HLPUSART_RX_BUF[HLPUSART_RX_STA&0X0FFF]=HLPUSART_NewData; //将收到的数据放入数组
				HLPUSART_RX_STA++;  //数据长度计数加1
			}else{
				HLPUSART_RX_STA|=0x4000;//数据超出缓存长度,标记溢出
			}
        }
       HAL_UART_Receive_IT(&hlpuart1,(uint8_t *)&HLPUSART_NewData,1); //再开启接收中断
    }
}

        【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】配置输出文件格式支持,然后编译代码

        【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弱函数。

/* 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。

         在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 <string.h>//用于字符串处理的库
#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)<HLPUSART_REC_LEN){
//				HLPUSART_RX_BUF[HLPUSART_RX_STA&0X0FFF]=HLPUSART_NewData; //将收到的数据放入数组
//				HLPUSART_RX_STA++;  //数据长度计数加1
//			}else{
//				HLPUSART_RX_STA|=0x4000;//数据超出缓存长度,标记溢出
//			}
//        }
//       HAL_UART_Receive_IT(&hlpuart1,(uint8_t *)&HLPUSART_NewData,1); //再开启接收中断
//    }
    //调整,通过htim3计数来通知接收数据结束
	if(huart ==&hlpuart1)//判断中断来源(串口lpuart1)//接收完的一批数据,还没有被处理,则不再接收其他数据
	{
		if(HLPUSART_RX_STA<HLPUSART_REC_LEN)//还可以接收数据
		{
			__HAL_TIM_SET_COUNTER(&htim3,0); //计数器清空
			if(HLPUSART_RX_STA==0) //使能定时器2的中断
			{
				__HAL_TIM_ENABLE(&htim3); //使能定时器3
			}
			HLPUSART_RX_BUF[HLPUSART_RX_STA++] = HLPUSART_NewData;//最新接收数据放入数组
		}else
		{
			HLPUSART_RX_STA|=0x8000;//强制标记接收完成
		}

		HAL_UART_Receive_IT(&hlpuart1,(uint8_t *)&HLPUSART_NewData,1); //再开启串口1接收中断
	}
}

        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 编译程序及下载

         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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/89335.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

高并发编程之多线程锁和CallableFuture 接口

5 多线程锁 5.1 锁的八个问题演示 package com.xingchen.sync;import java.util.concurrent.TimeUnit;class Phone {public static synchronized void sendSMS() throws Exception {//停留4秒TimeUnit.SECONDS.sleep(4);System.out.println("------sendSMS");}publ…

AXI协议规范超详细中文总结版

link AXI协议规范中文翻译版 来源&#xff1a;https://github.com/lizhirui/AXI_spec_chinese 综述 本文参考分析整理总结了AMBA AXI and ACE Protocol Specification文档的AXI总线协议规范部分&#xff0c;错误之处欢迎指出。 AMBA AXI协议支持高性能高频的系统设计&#xff0…

【视觉高级篇】25 # 如何用法线贴图模拟真实物体表面

说明 【跟月影学可视化】学习笔记。 什么是法线贴图&#xff1f; 法线贴图就是在原物体的凹凸表面的每个点上均作法线&#xff0c;通过RGB颜色通道来标记法线的方向&#xff0c;你可以把它理解成与原凹凸表面平行的另一个不同的表面&#xff0c;但实际上它又只是一个光滑的平…

巧用 Chrome:网络知多少

开发者如数家珍的工具中&#xff0c;Chrome 想必是众多人心目中的白月光&#xff0c;倒也不是它有多么优秀&#xff0c;而是多亏同行浏览器们的衬托。其开源的内核 Chromium 也成就众多养家糊口的岗位&#xff0c;比如 Edge、Opera、QQ 浏览器、360 浏览器等等国内外一票浏览器…

物联网开发笔记(62)- 使用Micropython开发ESP32开发板之控制ILI9341 3.2寸TFT-LCD触摸屏进行LVGL图形化编程:环境搭建

一、目的 这一节我们学习如何使用我们的ESP32开发板来控制ILI9341 3.2寸TFT-LCD触摸屏进行LVGL图形化编程的第一步&#xff1a;环境搭建。 关键字&#xff1a;3.2寸SPI串口TFT液晶显示屏模块 ILI9341驱动 LCD触摸屏 240*320 LVGL图形化编程 XPT2046触摸屏芯片IC 二、环境 ESP…

实机安装CentOS7.9操作系统图文(保姆级)教程

一、制作启动U盘 1、下载Ventoy软件 去Ventoy官网下载Ventoy软件&#xff08;Download . Ventoy&#xff09;如下图界面 ​ 2、制作启动盘 选择合适的版本以及平台下载好之后&#xff0c;进行解压&#xff0c;解压出来之后进入文件夹&#xff0c;如下图左边所示&#xff0c…

Hive 之数据透视表

文章目录什么是数据透视表&#xff1f;创建数据源基于各产品在各个平台半年内的月销售额与汇总&#xff0c;制作数据透视表什么是数据透视表&#xff1f; 数据透视表是一种工具&#xff0c;用于帮助用户理解和分析大量数据。它通常是一个二维表格&#xff0c;可以让用户以不同…

java计算机毕业设计springboot+vue航空公司电子售票系统-机票预订系统

项目介绍 通篇文章的撰写基础是实际的应用需要,然后在架构系统之前全面复习大学所修习的相关知识以及网络提供的技术应用教程,以远程教育系统的实际应用需要出发,架构系统来改善现远程教育系统工作流程繁琐等问题。不仅如此以操作者的角度来说,该系统的架构能够对多媒体课程进…

手把手教你使用SpringBoot做一个员工管理系统【代码篇·下】

手把手教你使用SpringBoot做一个员工管理系统【代码篇下】1.增加员工实现2.修改员工信息3.删除员工4.404页面配置5.注销1.增加员工实现 新增添加员工的按钮&#xff1a; <h2><a class"btn btn-sm btn-success" th:href"{/addemp}">添加员工&…

0- LVGL移植基于野火STM32F429挑战者(LVGL8.2)

1-移植准备 LVGL8.2 野火STM32F429_v2开发板 因为ST在STM32F4之后所有的芯片都不在有标准库,因此本篇是基于HAL库的。同时现在有许多厂商都不在有标准库了,都是根据自己的开发环境进行一些基本芯片接口的配置。像NXP,ST等。 这里不过多介绍LVGL,既然看到这个文章,大多数是…

20221214英语学习

今日新词&#xff1a; minus prep.减去&#xff1b;&#xff08;温度&#xff09;零下 garlic n.【植】大蒜 linger v.停留&#xff0c;逗留&#xff1b;徘徊&#xff1b;继续留存&#xff0c;缓慢消失&#xff1b;苟延残喘 sarcastic adj.讽刺的, 嘲讽的, 挖苦的 data n.…

【LeetCode每日一题】——572.另一棵树的子树

文章目录一【题目类别】二【题目难度】三【题目编号】四【题目描述】五【题目示例】六【解题思路】七【题目提示】八【时间频度】九【代码实现】十【提交结果】一【题目类别】 树 二【题目难度】 简单 三【题目编号】 572.另一棵树的子树 四【题目描述】 给你两棵二叉树…

MySQL索引优化(二)

文章目录一、查询优化1. 索引失效&#xff08;1&#xff09;不满足最左前缀法则&#xff0c;索引失效&#xff08;2&#xff09;在索引列上做任何计算、函数操作&#xff0c;索引失效&#xff08;3&#xff09;存储引擎使用索引中范围条件右边的列&#xff0c;索引失效&#xf…

倪健中会长应邀出席首届世界数贸易博览会致辞:把杭州打造成全球数字贸易元宇宙之都

12月11日至14日&#xff0c;首届全球数字贸易博览会在浙江省杭州市盛大举办。博览会由浙江省人民政府和商务部联合主办&#xff0c;杭州市人民政府、浙江省商务厅和商务部贸发局共同承办&#xff0c;主题为“数字贸易商通全球”&#xff0c;爱尔兰为主宾国&#xff0c;北京、上…

大航海时代:葡萄牙、西班牙率先出发,英国为何成为最大赢家?

欧洲经历了长达千年的中世纪以后&#xff0c;忽然开始自我反省了。为啥&#xff1f;因为打了上千年&#xff0c;不仅社会没进步&#xff0c;反而因为各种瘟疫、战争&#xff0c;把人口搞掉了一大半。 这么玩下去&#xff0c;日耳曼人的各大分支&#xff0c;可能都要完犊子了&a…

基于java+springboot+mybatis+vue+mysql的智能热度分析和自媒体推送平台

项目介绍 前端页面&#xff1a; 功能&#xff1a;首页、文章信息、图片信息、视频信息、个人中心、后台管理 管理员后台管理页面&#xff1a; 功能&#xff1a;首页、个人中心、用户管理、文章类型管理、文章信息管理、图片类型管理、图片信息管理、视频类型管理、视频信息管…

数据工厂刷新PowerBI数据集2

前面已经介绍过数据工厂中刷新PowerBI数据集&#xff0c;我们先发起一个web请示获取了token&#xff0c;然后再把token传入到接口中从而刷新数据集。 但是&#xff0c;明明都是微软家的产品&#xff0c;竟然还需要先获取token?明明Power Apps、Power Automate里都不需要的啊&…

茶文化推广网站

开发工具(eclipse/idea/vscode等)&#xff1a; 数据库(sqlite/mysql/sqlserver等)&#xff1a; 功能模块(请用文字描述&#xff0c;至少200字)&#xff1a; 网型站前台&#xff1a;网站个介绍、帮助信总、茶文化、茶叶分享、讨论信总 管理功能&#xff1a; 1、管理网站介绍、帮…

刚做外贸,先做平台好还是独立站好?

作为亚马逊这样的平台卖家&#xff0c;依托平台完善的第三方服务和流量红利&#xff0c;很容易将产品卖到海外。如今&#xff0c;随着平台要求越来越严格&#xff0c;管理政策越来越多变&#xff0c;用户需求也越来越多样化和苛刻&#xff0c;卖家在平台上经营店铺的一些问题正…

[附源码]Python计算机毕业设计SSM基于Web的摄影爱好者交流社区(程序+LW)

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…