【STM32+HAL】PWM呼吸灯实现

一、准备工作:

有关CUBEMX的初始化配置,参见我的另一篇blog:【STM32+HAL】CUBEMX初始化配置

二、所用工具:

1、芯片: STM32F407ZGT6

2、STM32CubeMx软件

3、IDE: MDK-Keil软件

4、STM32F4xxHAL库

三、实现功能:

1、控制LED一秒闪烁一次

2、实现串口通信:电脑控制pwm频率,串口输出pwm频率

3、按键控制呼吸灯暂停与继续

4、非线性呼吸灯实现

四、HAL库配置 (初始配置完成):

1、配置引脚

根据数据手册配置相应引脚的参数,此芯片PF10为LED灯,PB8,PB9,PA0为按键

2、设置优先级

为避免在按键中断中延时函数卡死,需改变Time base优先级 

3、开启定时器1

设置定时器频率为1HZ

定时器频率=168MHZ / (PSC+1) / (ARR+1)

4、开启PWM引脚

占空比先不填,到代码中实现

5、生成代码  

至此,CUBEMX配置完成 

五、KEIL填写代码:

1、串口重定向

#include "stdio.h"
int fputc(int ch, FILE *f)
{
    HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xffff);
    return ch;
}
int fgetc(FILE *f)
{
    uint8_t ch = 0;
    HAL_UART_Receive(&huart1, &ch, 1, 0xffff);
    return ch;
}

2、初始化

	HAL_TIM_Base_Start_IT(&htim1);//定时器1中断初始化
	HAL_TIM_PWM_Start (&htim14, TIM_CHANNEL_1);//Pwm初始化
	printf("Hello World\r\n");//串口输出测试
	HAL_UART_Receive_IT(&huart1, &aRxBuffer, 1);	//串口接收初始化

3、pwm函数,在while(1)中运行

void pwm(void)
{
	if(fxx==0){   //若为线性呼吸灯
		if(k&&flag){
			if(bri>=TIM14->ARR){
				dir=-1;
			 }
			  else if(bri<=0){
				dir=1;
			  }
			  bri+=dir;
		}
	}
	else if(fxx==1){   //若为非线性呼吸灯
		if(bri>=TIM14->ARR){
			dir=-1;
			i=0;
			flag_fxx=1;
		  }
		  else if(bri<=0){
			dir=1;
			flag_fxx=0;
		  }
		  i=(dir+i>=100||flag_fxx)?0:i+1;
		  bri+=dir+i;
	}
	TIM14->CCR1=bri;
	HAL_Delay(10);
}

4、接收数据函数

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
    UNUSED(huart);
if(huart==&huart2){
	if(Uart1_Rx_Cnt >= 255) {  
		Uart1_Rx_Cnt = 0;
		memset(RxBuffer,0x00,sizeof(RxBuffer));
		HAL_UART_Transmit(&huart1, (uint8_t *)"数据溢出", 10,0xFFFF); 	
	}
	else{
		RxBuffer[Uart1_Rx_Cnt++] = aRxBuffer; 
		if((RxBuffer[Uart1_Rx_Cnt-1] == 0x0A)&&(RxBuffer[Uart1_Rx_Cnt-2] == 0x0D)) {
			strcpy(rx_buf,RxBuffer); 
			pl=0;
			pl=atoi(rx_buf);
			htim14.Instance->PSC = (uint16_t )((double)84000000/(double)(htim14.Instance->ARR+1)/pl)+1;

            while(HAL_UART_GetState(&huart1) == HAL_UART_STATE_BUSY_TX); 
			Uart1_Rx_Cnt = 0;
			memset(RxBuffer,0x00,sizeof(RxBuffer)); 
		}
		flag=0;
	}
	HAL_UART_Receive_IT(&huart1, (uint8_t *)&aRxBuffer, 1); 
}
}

5、定时器中断

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim){
	if ( htim -> Instance == TIM1 ){
			HAL_GPIO_TogglePin(GPIOF,GPIO_PIN_10);
			duty=(double)(htim14.Instance->CCR1)/(double)(htim14.Instance->ARR+1);
			HZ=84000000/(htim14.Instance->ARR +1)/(htim14.Instance->PSC);
			printf ("HZ==%.0fhz      DuTy==%.1f%%\r\n \r\n",HZ,duty*100);
	}
}

6、按键中断

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)  
{
	if(HAL_GPIO_ReadPin(KEY_GPIO_Port,KEY_Pin) == GPIO_PIN_RESET){
		HAL_Delay(20); //延时消抖
		if(GPIO_Pin == KEY0_Pin){
			k=1-k; 
			flag=1-flag;
			printf("NO.%d\r\n",no++);
		}
	}
	else if(HAL_GPIO_ReadPin(KEY_GPIO_Port,KEY_Pin) == GPIO_PIN_RESET){
		HAL_Delay(20);
		if(GPIO_Pin == KEY1_Pin){
			fxx=1-fxx; 
			printf("NO.%d\r\n",no++);
		}
	}
}

7、主函数

while(1){	
    if(k&&flag==0){ 
        HAL_Delay(5);
		k=1;
		flag=1;
	}
	pwm();
}

完工

六、原理讲解:

参见

STM32CubeMX学习笔记(13)——PWM输出(呼吸灯)使用

【STM32】HAL库 STM32CubeMX教程七---PWM输出(呼吸灯)

PWM原理 PWM频率与占空比详解

七、源码提供:

【STM32+HAL】PWM呼吸灯实现

以下是基于STM32PWM呼吸实现方法: 1. 首先需要在STM32上配置TIM1的CH1通道为PWM输出模式,并设置PWM的周期和占空比。 2. 然后需要通过按键中断来控制PWM占空比的增加和减少。具体实现方法如下: ```c // 定义按键的GPIO引脚和中断优先级 #define KEY0_GPIO_PIN GPIO_PIN_0 #define KEY0_GPIO_PORT GPIOA #define KEY0_EXTI_IRQn EXTI0_IRQn #define KEY0_IRQ_PRIORITY 2 #define KEY1_GPIO_PIN GPIO_PIN_13 #define KEY1_GPIO_PORT GPIOC #define KEY1_EXTI_IRQn EXTI15_10_IRQn #define KEY1_IRQ_PRIORITY 2 // 定义PWM的周期和占空比 #define PWM_PERIOD 1000 #define PWM_MIN_DUTY_CYCLE 0 #define PWM_MAX_DUTY_CYCLE 900 // 定义PWM的当前占空比 uint16_t pwm_duty_cycle = 0; // 定义按键的状态和计数器 uint8_t key0_state = 0; uint8_t key1_state = 0; uint16_t key0_counter = 0; uint16_t key1_counter = 0; // 定义PWM输出的GPIO引脚 #define PWM_GPIO_PIN GPIO_PIN_8 #define PWM_GPIO_PORT GPIOA // 初始化PWM输出的GPIO引脚 HAL_GPIO_Init(PWM_GPIO_PORT, &(GPIO_InitTypeDef){ .Pin = PWM_GPIO_PIN, .Mode = GPIO_MODE_AF_PP, .Pull = GPIO_NOPULL, .Speed = GPIO_SPEED_FREQ_HIGH, .Alternate = GPIO_AF1_TIM1 }); // 初始化TIM1的CH1通道为PWM输出模式 HAL_TIM_PWM_Init(&(TIM_HandleTypeDef){ .Instance = TIM1 }); HAL_TIM_PWM_ConfigChannel(&(TIM_HandleTypeDef){ .Instance = TIM1 }, &(TIM_OC_InitTypeDef){ .OCMode = TIM_OCMODE_PWM1, .Pulse = pwm_duty_cycle, .OCPolarity = TIM_OCPOLARITY_HIGH }, TIM_CHANNEL_1); // 初始化按键的GPIO引脚和中断 HAL_GPIO_Init(KEY0_GPIO_PORT, &(GPIO_InitTypeDef){ .Pin = KEY0_GPIO_PIN, .Mode = GPIO_MODE_IT_FALLING, .Pull = GPIO_PULLUP, .Speed = GPIO_SPEED_FREQ_HIGH }); HAL_NVIC_SetPriority(KEY0_EXTI_IRQn, KEY0_IRQ_PRIORITY, 0); HAL_NVIC_EnableIRQ(KEY0_EXTI_IRQn); HAL_GPIO_Init(KEY1_GPIO_PORT, &(GPIO_InitTypeDef){ .Pin = KEY1_GPIO_PIN, .Mode = GPIO_MODE_IT_FALLING, .Pull = GPIO_PULLUP, .Speed = GPIO_SPEED_FREQ_HIGH }); HAL_NVIC_SetPriority(KEY1_EXTI_IRQn, KEY1_IRQ_PRIORITY, 0); HAL_NVIC_EnableIRQ(KEY1_EXTI_IRQn); // 在while循环中不断更新PWM占空比 while (1) { // 更新PWM占空比 __HAL_TIM_SET_COMPARE(&(TIM_HandleTypeDef){ .Instance = TIM1 }, TIM_CHANNEL_1, pwm_duty_cycle); // 控制LED2的闪烁 HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_0); HAL_Delay(500); } // KEY0的中断处理函数 void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) { if (GPIO_Pin == KEY0_GPIO_PIN) { // 检测按键状态 if (HAL_GPIO_ReadPin(KEY0_GPIO_PORT, KEY0_GPIO_PIN) == GPIO_PIN_RESET) { key0_counter++; if (key0_counter >= 1000) { key0_state = 1; key0_counter = 0; } } else { key0_counter = 0; if (key0_state == 1) { // 增加PWM占空比 pwm_duty_cycle += 100; if (pwm_duty_cycle > PWM_MAX_DUTY_CYCLE) { pwm_duty_cycle = PWM_MAX_DUTY_CYCLE; } key0_state = 0; } } } } // KEY1的中断处理函数 void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) { if (GPIO_Pin == KEY1_GPIO_PIN) { // 检测按键状态 if (HAL_GPIO_ReadPin(KEY1_GPIO_PORT, KEY1_GPIO_PIN) == GPIO_PIN_RESET) { key1_counter++; if (key1_counter >= 1000) { key1_state = 1; key1_counter = 0; } } else { key1_counter = 0; if (key1_state == 1) { // 减少PWM占空比 pwm_duty_cycle -= 100; if (pwm_duty_cycle < PWM_MIN_DUTY_CYCLE) { pwm_duty_cycle = PWM_MIN_DUTY_CYCLE; } key1_state = 0; } } } } ``` 3. 最后需要控制LED1的亮度呼吸效果。具体实现方法如下: ```c // 在while循环中不断更新PWM占空比 while (1) { // 更新PWM占空比 __HAL_TIM_SET_COMPARE(&(TIM_HandleTypeDef){ .Instance = TIM1 }, TIM_CHANNEL_1, pwm_duty_cycle); // 控制LED1的呼吸效果 for (int i = 0; i < 100; i++) { __HAL_TIM_SET_COMPARE(&(TIM_HandleTypeDef){ .Instance = TIM1 }, TIM_CHANNEL_1, i * 9); HAL_Delay(10); } for (int i = 100; i >= 0; i--) { __HAL_TIM_SET_COMPARE(&(TIM_HandleTypeDef){ .Instance = TIM1 }, TIM_CHANNEL_1, i * 9); HAL_Delay(10); } } ```
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值