1、什么叫呼吸灯?
由亮到暗逐渐变化,很有节奏感地一起一伏,感觉好像人在呼吸,当手机收到消息,屏幕上的指示灯会渐变,比较显眼,能起到一个通知提醒的作用,其实这就是一个呼吸灯。
(备注:现在由于手机都在往全面屏发展,因此很多手机取消了呼吸灯这一功能,取而代之的是息屏显示,而这篇文章的目的也不是单纯地点亮一个呼吸灯,而是要了解背后的原理,知道什么是PWM,应该如何使用)
2、什么是PWM?
PWM:Pulse Width Modulation,脉冲宽度调制,是利用微处理器的数字输出来对模拟电路进行控制的一种非常有效的技术。
可以将PWM理解为:对脉冲信号的处理技术,这里的“处理”指就的是改变占空比,从而改变最后呈现的效果。
既然是针对的是脉冲信号,肯定会有以下这些概念(关于脉冲就是不断跳跃的波,而PWM脉冲信号是矩形波):
PWM的频率:是指1秒钟内,信号从高电平到低电平再回到高电平的次数,也就是说一秒钟PWM有多少个周期。
单位:Hz
一般人眼睛对于80Hz以上刷新频率则完全没有闪烁感(因人而异)。
频率太小的话看起来就会闪烁,那么我们平时见到的LED灯,当它的频率大于50Hz的时候,人眼就会产生视觉暂留效果,基本就看不到闪烁了,而是一个常亮的LED灯。
频率很高时,看不到闪烁,占空比越大,LED越亮(平均电压越大);频率很低时,可看到闪烁,占空比越大,LED越亮。
所以,在频率一定下,可以用不同占空比改变LED灯的亮度,使其达到一个呼吸灯的效果。
PWM的周期:T=1/f
如果频率为50Hz,也就是说一个周期是20ms,那么一秒钟就有50次PWM周期。
占空比:在一个周期内,高电平的时间占整个周期时间
duty=t1/T
单位:% (0%-100%)
脉宽时间:高电平时间。
PWM原理:
单片机的IO引脚输出的是数字信号,且只能输出1和0。
TTL电平中,高电平为5V,低电平为0V,但是我们想要输出不同的模拟电压,比如输出3.75V应该怎么操作?
这里就要用到PWM,通过改变IO引脚输出方波的占空比,从而得到不同的模拟电压值,理论上来讲,可以输出任意不大于最大电压值(即0~5V之间任意大小)的模拟电压。
模拟电压=最大电压*占空比,这里的模拟电压是平均值,占空比越大,则模拟电压也越大。
如何产生PWM波?
使用单片机中的定时器I/O口可以产生PWM波。
那么下面再介绍一下定时器,区别于SysTick滴答定时器(一般只用于系统时钟的定时)
基本定时器只具有定时功能;而高级定时器比通用定时器多了三相六步电机的接口以及刹车功能,而这里我们只需要输出PWM波,使用通用定时器就够了。
如何使用定时器输出PWM波?换个问法就是:如何配置定时器输出PWM波?
通用定时器可以利用GPIO引脚进行脉冲输出,但是并不是任意GPIO 都具有STM32 定时器的输出通道功能,这需要根据芯片的引脚功能选择具有定时器输出通道功能的引脚来控制RGB 灯。
与定时器输出PWM有关的库函数:
在本次实验中使用的是PB5复用功能(重映射),也就是定时器3的通道2功能,呼吸灯是红灯。
实验:通过PWM波实现单色呼吸灯(LED从亮渐灭,再从灭渐亮,以此循环)
下面讲解代码:
pwm.h
#ifndef __TIMER_H
#define __TIMER_H
#include "stm32f10x.h"
void TIM3_Config(void);
#endif
pwm.c
#include "pwm.h"
//定时器3配置
void TIM3_Config(void)
{
static GPIO_InitTypeDef GPIO_InitStructure;
static TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
static TIM_OCInitTypeDef TIM_OCInitStructure;
//1.使能定时器3时钟和GPIO时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE); //使能GPIO外设和AFIO复用功能模块时钟
//2.TIM3部分重映射
GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3, ENABLE); //Timer3部分重映射 TIM3_CH2->PB5
//3.配置GPIO引脚为复用功能
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_5;//选择GPIOB组的第0根引脚
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;//选择引脚为复用推挽功能
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;//选择引脚速度
GPIO_Init(GPIOB,&GPIO_InitStructure); //初始化GPIO
//4.配置定时器3的频率,包括:分频值、计数值等
TIM_TimeBaseStructure.TIM_Period=99; //计数值
TIM_TimeBaseStructure.TIM_Prescaler=0; //分频值
TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up; //向上计数
TIM_TimeBaseInit(TIM3,&TIM_TimeBaseStructure); //初始化定时器
//5.配置定时器3的通道2占空比
TIM_OCInitStructure.TIM_OCMode=TIM_OCMode_PWM2;
TIM_OCInitStructure.TIM_OutputState=TIM_OutputState_Enable;//打开或者关闭脉冲输出
TIM_OCInitStructure.TIM_Pulse = 50; //比较值
TIM_OCInitStructure.TIM_OCPolarity=TIM_OCPolarity_High; //有效电平设置为高状态
TIM_OC2Init(TIM3,&TIM_OCInitStructure);//初始化定时器通道2
//6.使能定时器3工作
TIM_Cmd(TIM3,ENABLE);
}
delay.h
#ifndef __DELAY_H
#define __DELAY_H
#include "stm32f10x.h"
void delay_us(uint32_t n);
void delay_ms(uint32_t n);
#endif
delay.c
//延时函数
void delay_ms(uint32_t n)
{
while(n--) //延时值
{
SysTick->CTRL=0; //关闭滴答定时器,从最初的状态开始
SysTick->LOAD=(720000*n)-1; //配置计数值为(720000)-1 ~ 0(递减)
SysTick->VAL=0; //清除当前计数值值
SysTick->CTRL=5; //使能定时器时钟
while((SysTick->CTRL & 0x10000)==0); //等待计数值归为0
}
SysTick->CTRL=0; //关闭滴答定时器
}
void delay_us(uint32_t n)
{
SysTick->CTRL = 0; // Disable SysTick,关闭系统定时器
SysTick->LOAD = (72*n)-1; // 配置计数值(72*n)-1 ~ 0
SysTick->VAL = 0; // Clear current value as well as count flag
SysTick->CTRL = 5; // Enable SysTick timer with processor clock
while ((SysTick->CTRL & 0x10000)==0);// Wait until count flag is set
SysTick->CTRL = 0; // Disable SysTick
}
main.c
#include "stm32f10x.h"
#include "pwm.h"
#include "delay.h"
int main(void)
{
int32_t i=0; //为了使定时器比较值发生变化使用,这样可以实现动态PWM波
//调用定时器3的配置函数
TIM3_Config();
//主函数循环
while(1)
{
//灯渐灭
for(i=20;i>=0;i--)
{
TIM_SetCompare2(TIM3,i);//赋值给定时器的比较值不断减1,
delay_ms(1);
}
//灯渐亮
for(i=0;i<=20;i++)
{
TIM_SetCompare2(TIM3,i);//赋值给定时器的比较值不断加1,
delay_ms(1);
}
}
}
这是实现呼吸灯所有需要自己的代码,中断为空,下面为实验效果图:
/*Arduino实现呼吸灯程序*/
int LED_PIN=9;
void setup(){
//setup不做任何处理
}
void loop(){
//LED渐亮
for(int fadeValue=0;fadeValue<=255;fadeValue+=5){
//输出PWM
analogWrite(LED_PIN,fadeValue);//fadeValue是脉冲宽度,即高电平的时间,也就是占空比
delay(30);
}
//LED渐灭
for(int fadeValue=255;fadeValue>=0;fadeValue-=5){
analogWrite(LED_PIN,fadeValue);
delay(30);
}
}
最后一个是使用Arduino UNO板实现呼吸灯,相比STM32这个Arduino呼吸灯程序非常地简单,但是点亮呼吸灯并不是最终目的,而是要通过呼吸灯学会用定时器输出PWM波,每个板子都有板子的适用场所,相比Arduino,可以使用32实现一些更加复杂的功能,比如通过PWM波还可以控制舵机方向、电机转速等。
由于本人也是刚开始学习STM32,属于新手一枚,在学习途中肯定会遇到很多疑问,所以自己建了一个STM32技术交流群,欢迎大家进群交流技术,彼此解答疑惑。
作者能力水平有限,文章难免存在错误和纰漏,请大佬不吝赐教,非常欢迎大家与小白杨进行技术交流,希望在此能遇到志同道合的朋友,一起学习技术。
版权声明:本文为CSDN博主「浅白杨」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_46406325/article/details/115320910
暂无评论