🎀 文章作者:二土电子
🐸 期待大家一起学习交流!
一、前言
提示:本文使用的芯片并非STM32系列,利用定时器实现呼吸灯是从寄存器层面讲解的,但是对于不同芯片以及是否使用库函数开发来说,基本原理是相同的。
1、什么是呼吸灯
顾名思义,呼吸灯是指灯能够像人的呼吸一样,实现由暗到亮或由亮到暗的变化,通常用于消息提示功能,或者作为系统正在运行的提示。
2、如何实现呼吸灯
其实无论哪种实现方法,基本思想都是通过脉冲宽度调制(PWM)实现,即通过调节占空比来对模拟信号电平进行数字编码。关于何为PWM,何为占空比,这里就不再赘述了,简单理解就是,占空比越高,LED两端电压越大,LED越亮。这里用一张图简单的介绍一下呼吸灯的实现原理。
人眼的分辨率为1秒24帧,即人眼看到的图像滞留时间为0.04s左右,就按40ms计算。也就是说,一个周期为40ms,人眼是看不出其中的亮灭变化的。但是为了让呼吸灯效果看起来更好,建议选则周期长度小于40ms,这里选则25ms。
二、利用for循环实现呼吸灯
利用for循环实现呼吸灯主要有两个关键变量,一个是周期T,一个是占空比的值t,他们的含义如下图所示:
图中T为一个周期(脉冲宽度),t为占空比。
利用for循环实现呼吸灯的程序如下:
int main(void)
{
uint32 T = 1600; // 周期(脉冲宽度)
uint32 i=0,m=0,n=0,t=0;
PT0OES_D9 = 1; // 输出使能
PT0DAT_D9 = 1; // 灭
while (1)
{
for (i=0;i<T;i++)
{
PT0DAT_D9 = 0; // 亮
for (m=0;m<t;m++);
PT0DAT_D9 = 1; // 灭
for (n=0;n<T-t;n++);
t++;
if (t >= T)
{
for (i=0;i<T;i++)
{
PT0DAT_D9 = 0; // 亮
for (m=0;m<t;m++);
PT0DAT_D9 = 1; // 灭
for (n=0;n<T-t;n++);
t--;
}
}
}
}
}
这里程序逻辑比较容易理解,就不再赘述,有需要讨论的小伙伴可以留言讨论。虽然利用for循环能够很简单的实现呼吸灯,但是这种方法是利用for循环来控制亮灭时间,时间控制并不精确。
三、利用定时器实现呼吸灯
1.利用定时器中断实现
利用定时器中断实现呼吸灯的程序如下:
uint32 count= 0;
uint32 flag = 0;
uint32 t = 0;
int main(void)
{
Osc_Setup(); // 初始化系统时钟
EnableGlobalInterrupt(); // 使能全局中断
PT0OES_D9 = 1; // 输出使能
PT0DAT_D9 = 1; // 灭
TMR1_TCR_CRST = 1; // 复位定时器计数值寄存器和预分频计数值寄存器
while (TMR1_TCR_CRST); // 等待复位完成
TMR1_PR_PR = 49; // 设置预分频系数
TMR1_MCR_MR0I = 1; // 产生匹配中断
TMR1_MCR_MR0R = 1; // 产生计数器复位
TMR1_MCR_MR0S = 0; // 计数器不停止计数
TMR1_MR0 = 250; // 设置匹配值
TMR1_IR = 0xffffffff; // 清除匹配中断标志位
NVIC_ISER_TMR1 = 1;
TMR1_TCR_CEN = 1; //定时器定时,捕捉功能启动
while (1)
{
if (t <= count)
{
PT0DAT_D9 = 1; // 灭
}
else if (t > count)
{
PT0DAT_D9 = 0; // 亮
}
}
}
void __attribute__((isr)) ISR_TMR1(void) // 匹配中断服务函数
{
count = count + 1;
if (count >= 100 && flag <= 100) // 由暗到亮
{
count = 0;
t = t + 1;
flag = flag + 1;
}
if (count >= 100 && flag > 100) // 由亮到暗
{
count = 0;
t = t - 1;
flag = flag + 1;
}
if (flag > 200)
{
count = 0;
t = 0;
flag = 0;
}
TMR1_IR = 0xffffffff;
}
配置TMR1来实现呼吸灯功能,配置TMR1时预分频系数设置为49,匹配值设置为250,配置产生匹配中断,中断后产生计数器复位,计数器不停止计数,然后开启定时器1。这里需要注意的是要配置中断后产生计数器复位,否则计数器会等到计数到最大值后才清零,此时的现象是LED依然可是像呼吸灯一样由暗变亮,再由亮变暗,但是期间会不断闪烁。
假设进入匹配中断的时间为t,系统主时钟频率为Fsys,预分频系数为PR,匹配值为n,那么进入一次匹配中断的时间t= ((PR + 1)/Fsys)*n。因为开头提到过,本程序设置的脉冲宽度为25ms,系统主时钟频率为50MHz,所以这里将预分频系数设置为49和匹配值设置为250,计算后可知,每0.25ms进入一次匹配中断,进入100次更改一次占空比,即25ms更改一次,总的脉冲宽度为25ms。如果想更改脉冲宽度只需要调整count的值即可。
2.利用定时器输出PWM波实现
相比于前两种方法来说,初学单片机的人更加熟悉的是利用定时器输出PWM波来实现呼吸灯,因为正点原子或普中科技等等教程中都有详细介绍,这里就不再赘述了。
四、总结
虽然利用for循环能够比较简单地实现呼吸灯,但是其时间控制没有利用定时器实现准确,建议使用定时器来实现呼吸灯功能。
当然,上面给出的程序存在不好的地方,在中断中执行了太多语句,这是平时开发需要注意的。可以只在中断中保留count自加操作,其他放在主函数地while(1)中进行,这样可以减少在中断中执行的程序。
PS:本人也属于技术小白级别,本文如有写的不合适的地方,欢迎各位在评论区讨论。