单片机开发:三种实现呼吸灯的技术方案

目录

  • 一、前言
  • 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,他们的含义如下图所示:

    图中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:本人也属于技术小白级别,本文如有写的不合适的地方,欢迎各位在评论区讨论。

    物联沃分享整理
    物联沃-IOTWORD物联网 » 单片机开发:三种实现呼吸灯的技术方案

    发表评论