单片机开发—呼吸灯的三种实现方法

目录

  • 一、前言
    • 1、什么是呼吸灯
    • 2、如何实现呼吸灯
  • 二、利用for循环实现呼吸灯
  • 三、利用定时器实现呼吸灯
    • 1.利用定时器中断实现
    • 2.利用定时器输出PWM波实现
  • 四、总结


一、前言

提示:本文使用的芯片并非STM32系列,利用定时器实现呼吸灯是从寄存器层面讲解的,但是对于不同芯片以及是否使用库函数开发来说,基本原理是相同的。

1、什么是呼吸灯

顾名思义,呼吸灯是指灯能够像人的呼吸一样,实现由暗到亮或由亮到暗的变化,通常用于消息提示功能,或者作为系统正在运行的提示。

2、如何实现呼吸灯


其实无论哪种实现方法,基本思想都是通过脉冲宽度调制(PWM)实现,即通过调节占空比来对模拟信号电平进行数字编码。关于何为PWM,何为占空比,这里就不再赘述了,简单理解就是,占空比越高,LED两端电压越大,LED越亮。这里用一张图简单的介绍一下呼吸灯的实现原理。

人眼的分辨率为1秒24帧,即人眼看到的图像滞留时间为0.04s左右,就按40ms计算。也就是说,一个周期为40ms,人眼是看不出其中的亮灭变化的。但是为了让呼吸灯效果看起来更好,建议选则周期长度小于40ms,这里选则25ms。

二、利用for循环实现呼吸灯

利用for循环实现呼吸灯主要有两个关键变量,一个是周期T,一个是占空比的值t,他们的含义如下图所示:
单片机开发—呼吸灯的三种实现方法_第1张图片
图中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:本人也属于技术小白级别,本文如有写的不合适的地方,欢迎各位在评论区讨论。

你可能感兴趣的:(嵌入式开发,单片机,嵌入式硬件)