呼吸灯,就是指电子产品上的LED灯的亮度随着时间由暗到亮逐渐增强,再由亮到暗逐渐衰减,有节奏感地一起一伏,就像是在呼吸一样,因而被广泛应用于手机、电脑等电子设备的指示灯中。在使用MCU开发相关的应用产品中也可以加入呼吸灯功能,增强用户的体验感,在本实验中将介绍如何使用MM32L0系列产品芯片做呼吸灯功能。
在平时应用中可以知道,MCU的GPIO输出高低电平变化可以实现LED灯的亮、灭两个过程,如果GPIO的电平一直维持高电平或者低电平,LED灯就处于长亮或长灭的状态,呼吸灯就是通过较高频率的电平变化来实现亮灭的切换,由于人的视觉暂留效应,肉眼无法迅速捕捉快速亮灭变化的过程,所以在视觉中一直出现一直亮或者灭的状态,通过调整占空比可以控制LED灯的亮度,给人视觉上一种灯光由暗到亮逐渐增强,然后又由亮到暗逐渐衰减。
正常的成年人的吸气呼气时间整个过程持续大约3秒时间,即吸气时间(LED灯亮度逐渐变亮)时间为1.5S,吸、呼气时间(LED灯亮度逐渐变暗)时间为1.5S。
亮度随着时间逐渐变强再衰减,可以用两种算数方式实现:半周期的正弦函数曲线和指数上升曲线及对称的下降沿曲线。
要控制 LED 灯达到呼吸灯的效果,实际上就是要控制 LED 灯的亮度拟合呼吸特性曲线,在本次实验中,将采用指数上升曲线及对称的下降沿曲线方式,用户如果对正弦方式感兴趣也可以进行尝试。在本次实验中,我们使用MM32L0xx输出较高频率的PWM信号,通过调制信号的占空比,控制LED灯的亮度。
生成指数方式的曲线图主要因素:
TIMPeriod:定时器的计数周期,它的值必须与PWM 表中的极大值相等(应用中赋值需要减 1),而 PWM表的极大值决定了控制的分辨率。例如极大值为 10时,PWM 占空比只有10个等级,精确到0.1,当极大值为1000 时,PWM 占空比1000个等级,精确到0.001。在本次实验中设置定时器的计数周期值为255+1,即PWM表中的极大值也是256。
TIM_Prescaler:定时器时钟分频因子,它控制定时器计数器 CNT计数加1所需要的时间,它的值太大会导致输出的单个PWM波周期过长,影响控制的动态特性。如控制LED灯时,该值太大会导致LED灯开关时间变长,闪烁明显。一般来说,该值越小越好。在本次实验中设置定时器时钟分频因子为1757+1,即对时钟1758分频。
PWM 表的点数:PWM表的点数即对拟合曲线的采样点数,即把LED灯的亮度分为0-255个等级,采样点越多,能更好地还原拟合曲线,采样点太少,可能会导致失真。在本次实验中设置PWM 表的点数为40。
Period_class:周期倍数,即 PWM 表中每个元素的循环次数,它影响拟合曲线的周期。
在本次实验中设置设置周期倍数为8。
本次实验程序直接从MM32L073的定时器PWM输出的例程的基础上修改得来,TIM3配置成向上计数,PWM通道输出也被配置成当计数器 CNT 的值小于输出比较寄存CCR1的值时,PWM通道输出低电平,点亮 LED 灯。在函数的最后还使能了定时器中断,每当定时器的一个计数周期完成时,产生中断,配合中断服务函数,即可切换CCR1 比较寄存器的值。
程序配置如下:
TIM3初始化配置
//LED亮度等级列表
uint8_t PWM_Wave[] = {1,1,2,2,3,4,6,8,10,14,19,25,33,44,59,80,107,143,191,255,255
,191,143,107,80,59,44,33,25,19,14,10,8,6,4,3,2,2,1,1};
TIM3的初始化、中断及PWM输出配置:
void TIM3_PWM_Init(u16 arr,u16 psc)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);//使能TIM3时钟
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE);//使能GPIOB时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4; //TIM3_CH1
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn; //TIM3中断源
NVIC_InitStructure.NVIC_IRQChannelPriority = 0x01; //设置中断优先级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
NVIC_Init(&NVIC_InitStructure); //初始化NVIC寄存器
TIM_TimeBaseStructure.TIM_Period = arr;//设置自动装载寄存器值
TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置预分频值
TIM_TimeBaseStructure.TIM_ClockDivision=0; //设置时钟分割
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;//设置计数模式
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //初始化TIM3寄存器
TIM_OCStructInit(&TIM_OCInitStructure);
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //配置PWM模式
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //输出使能
TIM_OCInitStructure.TIM_Pulse = 0; //设置初始脉冲宽度为0
TIM_OCInitStructure.TIM_OCPolarity=TIM_OCPolarity_Low;//小于CCR1值为低电平
TIM_OC1Init(TIM3, &TIM_OCInitStructure); //初始化TIM3_OC1寄存器
TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable); //预装载使能
TIM_ARRPreloadConfig(TIM3, ENABLE); //使能 TIM 重载寄存器 ARR
TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE); //使能TIM3的更新中断
TIM_Cmd(TIM3, ENABLE);//使能TIM3
}
中断服务函数:
void TIM3_IRQHandler(void)
{
static uint8_t a = 0;//PWM表的成员数,用于PWM查表
static uint8_t b = 0;//计算周期数
if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET)//判断更新中断标志位
{
b++;
if(b >= 8) //周期倍数
{
TIM3->CCR1 = PWM_Wave [a]; //修改定时器比较寄存器值
a++;//查表指向下一个成员变量
if( a >= 40) //查表查到最后一位,重新指向表头
{
a=0;
}
b=0; //重置周期计数标志
}
TIM_ClearITPendingBit (TIM3, TIM_IT_Update);
}
}
主程序:
int main(void)
{
TIM3_PWM_Init(255,1757);
while(1)
{
}
}
本次实验配置拟合曲线周期计算:
TIMPeriod=255+1;
TIM_Prescaler=1757+1;
PWM 表的点数(a)=40;
Fpwm = 48M / ((arr+1)*(psc+1))(单位:Hz)
定时器 update 事件周期,即定时器中断周期:t1= 1/ Fpwm = 9376us
每个 PWM 点的时间:t2= t1*8= 75008us
遍历PWM表的周期时间为:t3=t2*40= 3000320us
通过公式的计算可知本工程的配置可使得输出的拟合曲线周期约等于3秒,符合成年人的吸气呼气时间整个过程持续大约3秒时间。
利用GPIO模拟PWM波形设计呼吸灯功能:
void LedOnOff(uint16_t t,uint16_t i)
{
LED1_ON();
delay_us(i);
LED1_OFF();
delay_us(t-i);
}
int main(void)
{
int i,j;
LED_Init();
TIM3_PWM_Init(255,1757);
while(1)
{
for(i=0;i<100;i+=1){
for(j=0;j<47;j+=1)
{
LedOnOff(100,i);
}
}
for(i=100;i>0;i--) {
for(j=0;j<46;j+=1)
{
LedOnOff(100,i);
}
}
}
}
使用MM32L0系列MCU上述两种方式都可以实现呼吸灯功能,两种方法GPIO分别是PB4和PB5,download到MiniBoard中,可以对比测试两种实现方法的差异性,选择合适的呼吸灯实现方式。
来源: 灵动MM32