STM8S003F3通过PWM波实现三基色呼吸灯

发布者:CMOS最新更新时间:2017-11-14 来源: eefocus关键字:STM8S003F3  PWM波实  三基色呼吸灯 手机看文章 扫描二维码
随时随地手机看文章

前段时间使用STM8S003F3实现了一个三基色灯的各种效果,故写一篇文章作为一个记录。

1 综述

我们知道,要是的LED灯亮直接通电即可。而要改变灯的亮度,我们有两种方法:改变电流和PWM调光。

我们首先想到的就是改变它的驱动电流,因为LED的亮度是几乎和它的电流直接成正比关系。然而用调正向电流的方法来调节亮度会产生一个问题:在调亮度的同时也会改变它的光谱和色温,这样就会会产生色偏。因为目前白光LED都是用蓝光LED加黄色荧光粉而产生,当正向电流减小时,蓝光LED亮度增加而黄色荧光粉的厚度并没有按比例减薄,从而使其光谱的主波长增长。这个问题对于一般的照明是没有问题的,因为色温的变化量毕竟不是很大。但是对电源来说当电流过小时会产生闪烁,除非电源的恒流范围很宽,完全可以从0到最大。这样才没有问题。简而言之,电流调光有色温变化和电源电流过小产生闪烁的问题。曾经做过一个项目,用于某设备上需要非常非常平稳的调光,显然电流调光是无法实现。同时像本文介绍的三基色调光有颜色要求的显然也不行。因此我们使用PWM调光。
既然PWM调光可以避免上面的两个问题,为什么不直接都用PWM调光呢?因为我们毕竟是做产品,要考虑成本问题。使用PWM调光至少需要一颗能支持PWM的芯片(当然还有外围电路,但是电流调光也是有电路的。我们也应该知道PWM信号也可以由脉冲发生器提供),另外它需要编写程序。所以只有在需要的场合才使用PWM调光(使用PWM调光需要注意的问题是频率不能太低或者太高,推荐150-400Hz之间。)。PWM的优点如下:

● PWM调光就不会产生色偏,因为它总是工作在0或者最大两种状态。
● PWM的占空比很好控制,而且精度高
● 对电源没有影响,因为不会改变电源的工作条件,只是给电源开或者关。

2 PWM波调光的原理

脉宽调制(PWM)是利用微处理器的数字输出来对模拟电路进行控制的的技术,广泛应用在从测量、通信到功率控制与变换及LED照明等许多领域中。通过以数字方式控制模拟电路,可以大幅度降低系统的成本和功耗。此外,许多微控制器和DSP已经在芯片上包含了PWM控制器,这使数字控制的实现变得更加容易了。简言之,PWM是一种对模拟信号电平进行数字编码的方法。通过高分辨率计数器的使用,方波的占空比被调制用来对一个具体模拟信号的电平进行编码。PWM信号仍然是数字的,因为在给定的任何时刻,满幅值的直流供电要么完全有(ON),要么完全无(OFF)。电压或电流源是以一种通(ON)或断(OFF)的重复脉冲序列被加到模拟负载上去的。通的时候即是直流供电被加到负载上的时候,断的时候即是供电被断开的时候。只要带宽足够,任何模拟值都可以使用PWM进行编码。

2.1 占空比(Duty Cycle or Duty Ratio)

首先我们需要了解占空比,占空比的解释可以归纳为如下几种:
● 在一串理想的脉冲序列中(如方波),正脉冲的持续时间与脉冲总周期的比值。例如:脉冲宽度1μs,信号周期4μs的脉冲序列占空比为0.25。
● 在一段连续工作时间内脉冲占用的时间与总时间的比值。
● 在周期型的现象中,现象发生的时间与总时间的比。
通俗一点讲就是电路释放能量的有效时间与总释放时间的比。

2.2 调光比


然后我们需要了解调光比,调光比则是按下面的方法计算(Foper:工作频率;Fpwm:调光频率;):
调光比率 = Foper / Fpwm,(其实也就是调光的最低有效占空比)
比如Foper=100KHZ;Fpwm=200Hz,则调光比为:100K/200=500;这个指标在很多驱动芯片的规格书里会说明的。

2.3 PWM调光

2.3.1 PWM调光原理


● 若一个占空比为10%的PWM输出,即10%的时间通,90%的时间断;
● 若一个占空比为50%的PWM输出,即50%的时间通,50%的时间断;
● 若一个占空比为90%的PWM输出,即90%的时间通,10%的时间断;

我们知道,人眼是有视觉暂留的,打个比方,人眼只能识别1us((这个比方没有任何科学依据,仅仅为了便于理解)内光子的数量从而判断亮暗,如果1us接收了1000个光子,那么我们就会认为是一个亮度,至于这1000个光子是在1us什么时候收到,是没有任何影响的,也就是说,在0.1us的时候收到和0.2us的时候收到是没有区别的,我们需要关心的只是数量。这就是为什么我们进行PWM调光的时候不能太慢(视觉暂留可以分辨)也不能太快(太快就没有区别了,就一直是最亮的)。这样就好理解了,占空比是10%,就相当于给它加了一个0.9V的电压(因为10%通电时间里电流产生的效果和0.9V加在周内的时候是一样的)。所以我们就可以通过占空比来条件亮度。

如果在50ms中,LED在这段时间中得到9V供电。如果在下一个50ms中将开关断开,灯泡得到的供电将为0V。如果在1秒钟内将此过程重复10次,灯泡将会点亮并象连接到了一个4.5V电池(9V的50%)上一样。这种情况下,占空比为50%,调制频率为10Hz(T=1/f  = 1/10 = 0.1S )。大多数负载(无论是电感性负载还是电容性负载)需要的调制频率高于10Hz。设想一下如果灯泡先接通5秒再断开5秒,然后再接通、再断开……。占空比仍然是50%,但灯泡在头5秒钟内将点亮,在下一个5秒钟内将熄灭。要让灯泡取得4.5V电压的供电效果,通断循环周期与负载对开关状态变化的响应时间相比必须足够短。要想取得调光灯(但保持点亮)的效果,必须提高调制频率。在其他PWM应用场合也有同样的要求。通常调制频率为1kHz到200kHz之间。

通过上面的介绍,我们就知道了PWM调光的原理,那么我们来看看我们这个项目的原理。

2.3.2 三基色呼吸灯原理

需求说明:我们需要设置一个灯,它具有常亮、长暗、快闪、慢闪、呼吸5钟效果,并且要求这几种状态是可以变化的。灯的颜色可以变化。

需求分析:灯的颜色可以变化——确定使用三基色灯。状态可以切换,我们使用串口调节灯的状态和灯的颜色(通过串口给单片机发送数据,然后将参数传给灯控制函数)。我们使用PWM调节灯的亮度,通过改变捕获/比较寄存器的值来改变占空比从而改变亮度。

数学建模:三个灯和一个灯的控制是一样的,由于我们使用的是PWM波调光所以灯只有两种状态:断和通。我们分析5种状态可以抽象成数学模型:暗、上升、亮、下降4钟状态(长暗就是一直暗,常亮就是一直亮,快闪就是100%占空比而且频率比较快,慢闪就是100%占空比而且频率比较慢、呼吸就是占空比最低为10%然后以10%逐渐上升)。然后我们确定需要输入的变量:Value_LED_Red(红色灯的亮度)、Value_LED_Green(绿色灯的亮度)、Value_LED_Blue(蓝灯的亮度)、Value_ChangeOnce(上升或下降的速度)、HoldTime_Min(在低电平状态的持续时间)、HoldTime_Max(在高电平状态的持续时间)。

3 实现过程

下面是TSSOP20封装的管脚图。

首先,我们要确定硬件管脚,但是事实上,因为我用的最多的就是TIM2和TIM4,因此我选用的TIM2_1(PC5,Red)、TIM2_2(PD3,Green)、TIM2_3(PD2,Blue),但是发现除了绿色以外都无法用PWM波控制,但是能用IO控制亮暗,后来查资料发现TIM2_1和TIM2_3早使用的时候必须给存储器地址分布重映射,也就是我们需要使用管脚的复用功能!我们通过看《数据手册》发现,使用TIM2只有一个管脚是复用功能,因此选择TIM2。但是我因为电路限制,所以还是用的上面所说的管脚(注意,TIM2_3有复用和不复用两种,我用的是复用)。这也没有什么影响,我们可以学习一下管脚的复用功能。

3.1 使用复用功能

我们首先看《数据手册》中关于管脚的描述(第一行是TSSOP20封装的管脚编号,第二行是UFQFPN20封装的管脚b)



从上面的图中我们可以看到,需要使用15、19管脚复用功能就需要设置AFR0和AFR1——使用复用功能就是设置AFR(Alternate function remapping bits,候补功能映射位)——我们继续看芯片资料


其中OPT2【选项字节(Option byte)编程 】和NOPT2需要是相反的(可能是出于校验考虑),我们从《数据手册》中可以知道: 应用程序可直接向目标地址进行写操作。所以我们直接对这两个地址进行写操作,那么数值是多少呢?我们继续看《数据手册》,如下图所示


从上图中我们可以看到,我们将AFR1设置为1,将AFR0设置成1。代码如下:


  1. /***************************************************************  

  2. *Function:  FLASH_Init 

  3. *Calls:     void 

  4. *Called By:     All_Config.c     

  5. *Input:     void 

  6. *OUTPUT:    void 

  7. *Return:    void 

  8. *DESCRIPTION:   1.设置管脚复用功能(AFR0要设置为1 AFR1 要设置为1) 

  9.                 2.eeprom 每一次只能操作一个字节 

  10. *Others:    nothing 

  11. ***************************************************************/  

  12. volatile unsigned char flash_OPT2 @0x4803;  

  13. volatile unsigned char flash_NOPT2 @0x4804;  

  14. #define FLASH_EOP  0X04  //FLASH_IAPSR 中位,编程是否结束  

  15. #define FLASH_DUL  0X08  //flash  data eeprom 是否解锁标志位  

  16. void FLASH_Init()  

  17. {  

  18.     //第一步  初始化EEPROM  

  19.     while( (FLASH->IAPSR & FLASH_DUL) == 0X00 )  

  20.     {  

  21.         FLASH->DUKR = 0XAE;     //中文资料上 说的和 实际是相反的  

  22.         FLASH->DUKR = 0X56;  

  23.         _asm("NOP");   

  24.     }  

  25.       

  26.     //第二步  对OPT进行编程,首先需要如下操作:开启opt编程  

  27.     FLASH->CR2 |= 0X80;     //OPT  = 1  

  28.     FLASH->NCR2 &= 0X7F;    //NOPT = 0  

  29.       

  30.     //第三步  修改内存  

  31.     /*************************** 

  32.     1.修改参数,启用复用功能 

  33.     2.OPT2 和 NOPT2要相反 

  34.     ****************************/  

  35.     //修改OPT2  

  36.     flash_OPT2 = 0X03;      // 0000 0011  

  37.     _asm("NOP");   

  38.     while( (FLASH->IAPSR & FLASH_EOP) == 0 );  //等待操作完成   

  39.     //修改NOPT2  

  40.     flash_NOPT2 = ~flash_OPT2;  

  41.     _asm("NOP");   

  42.     while( (FLASH->IAPSR & FLASH_EOP) == 0 );  //等待操作完成  

  43.      

  44.     //第四步  对OPT进行编程,最后需要如下操作:禁用opt编程  

  45.     FLASH->CR2 &= ~0X80;    //OPT  = 1  

  46.     FLASH->NCR2 |= 0X80;    //NOPT = 0  

  47. }  

这样,我们就完成了复用功能的“存储器地址分布重映射”。


3.2 初始化定时器

我们使用TIM2产生PWM波来控制三基色灯,所以,我们需要对TIM2进行初始化。

3.2.1 使能设置

首先无论使用什么,第一步就是使能,在《数据手册》的时钟控制中我们看到如下信息:


我们就可以确定使能TIM2的代码:CLK->PCKENR1 |= CLK_PCKENR1_TIM2;

3.2.2 设置频率

然后,TIM2的主频(决定着周期)是和单片机一样的(这个频率由时钟控制),我们可以进行分频(分频越多我们调节的就越精细),我们在《数据手册》“预分频器高8位”和“预分频器低8位”中可以看到:


我们就可以确定分频代码:TIM2-> PSCR = 5;其中上图所描述的更新事件我们这里就是计数器清0。

3.2.3 选择PWM波

我们查看《数据手册》的17.5.7 PWM模式可以看到,脉冲宽度调制(PWM)模式可以产生一个由TIM1_ARR寄存器确定频率、由TIM1_CCRi寄存器确定占空比的信号。PWM模式是捕获/比较模式寄存器1(TIM1_CCMR1)来控制的,我们选择PWM模式2、开启TIM1_CCR1寄存器的预装载功能、CC1通道被配置为输出(其余不变),我们可以从《数据手册》中看到:


我们就可以确定代码为:TIM2-> CCMR1 = 0X68;

3.2.4 设置PWM波的频率


在《参考手册》中可以看到,在PWM模式(模式1或模式2)下,TIM1_CNT和TIM1_CCRi始终在进行比较:
a.(依据计数器的计数方向)以确定是否符合TIM1_CCRi≤TIM1_CNT或者TIM1_CNT≤TIM1_CCRi(我们在TIM1_CR1中设置为向上计数、边沿对齐模式)。

b.根据TIM1_CR1寄存器中CMS位域的状态,定时器能够产生边沿对齐的PWM信号或中央对齐的PWM信号。

我们查看《数据手册》发现(可以参见——17.3.4  向上计数模式):


我们为了调光的均匀,将使得TIM2_ARR=255,根据上图,我们可以知道,最亮为255,最暗为0.255就是PWM波的频率(因为TIM1和TIMX的PWM功能是相同资料互用的,因此上图为TIM1的资料)。

3.2.5 初始化PWM波的亮度 

根据上面的内容我们知道占空比(也就是亮度)是TIM2_CCR决定的,我们初始化为零:TIM2-> CCR1H = 0;TIM2-> CCR1L = 0;

3.2.6 计数器使能、捕获比较寄存器使能

关于这两个使能我们可以自己查询《数据手册》,需要提一点的是TIMx_CCER1控制 比较/捕获寄存器1和比较/捕获寄存器2。TIMx_CCER2控制 比较/捕获寄存器3。

3.2.7 TIM2产生PWM波的初始化程序

具体代码如下:


  1. /*************************************************  

  2. *Function:  TIM2_InitPwmCtrl 

  3. *Calls:     void 

  4. *Called By:     All_Config.c     

  5. *Input:     void 

  6. *OUTPUT:    void 

  7. *Return:    void 

  8. *DESCRIPTION:   1.初始化与PWM相关的TIM2 

  9.                 2.TIMx_CCER1控制  比较/捕获寄存器1和 

  10.                   比较/捕获寄存器2 

  11.                 3.TIMx_CCER1控制  比较/捕获寄存器3 

  12. *Others:    nothing 

  13. *************************************************/  

  14. void TIM2_InitPwmCtrl()  

  15. {  

  16.     CLK->PCKENR1 |= CLK_PCKENR1_TIM2;   //TIM2 使能    

  17.       

  18.     /********************************************************** 

  19.     1.预分频器 

  20.     2.设置定时器的时钟(根据已经分频的主时钟来分频) 

  21.     3.分频系数越大,周期越大,也就是频率越低 

  22.     4.分频系数1 ~ 2^15,如果为5就是32分频(原来为16MHZ) 

  23.     **********************************************************/  

  24.     TIM2-> PSCR = 5;  

  25.       

  26.     //选择TIM2通道1的工作模式(PWM2波的模式)  

  27.     TIM2-> CCMR1 = 0X68;    //0110 1000  

  28.     TIM2-> CCMR2 = 0X68;  

  29.     TIM2-> CCMR3 = 0X68;  

  30.       

  31.     /********************************************************** 

  32.     1.自动装载寄存器(分高低位——也就是16位寄存器) 

  33.     2.(每次就是上面分频后的时间,假设分频后是2us),每2us复位一次 

  34.       定时器2,也就是说计数器每变化一次耗时2us,0到255经过255个2us 

  35.     3.在这个工程中,我们认为255就是最亮(也就是在周期内都是高), 

  36.       当然我们可以设置250,设置多少就看精细程度了 

  37.     **********************************************************/  

  38.     TIM2-> ARRH = 0;  

  39.     TIM2-> ARRL = 255 & 0X0FF;  

  40.       

  41.     /********************************************************** 

  42.     1.捕获/比较寄存器 

  43.     2.设置亮度,这一位控制占空比 

  44.     **********************************************************/  

  45.     TIM2-> CCR1H = 0;  

  46.     TIM2-> CCR1L = 0;   

  47.     TIM2-> CCR2H = 0;  

  48.     TIM2-> CCR2L = 0;   

  49.     TIM2-> CCR3H = 0;  

  50.     TIM2-> CCR3L = 0;   

  51.   

  52.     /********************************************************** 

  53.     1.计数器使能 

  54.     2.捕获/比较使能寄存器 使能 

  55.     **********************************************************/  

  56.     TIM2->CR1 |= TIM2_CR1_CEN;      //使能  计数器  

  57.     TIM2->CCER1 |= TIM2_CCER1_CC1E; //使能  捕获/比较寄存器1  

  58.     TIM2->CCER1 |= TIM2_CCER1_CC2E; //使能  捕获/比较寄存器2  

  59.     TIM2->CCER2 |= TIM2_CCER2_CC3E; //使能  捕获/比较寄存器3  

  60. }  



3.3 实现调光


初始化完成我们就需要进行调光了,我们调光的逻辑是这样的:

a.在UART中接收到调光的数据后调用“参数接收函数”
b.“参数接收函数”接收到数据后保存数据,并打开中断(我们选用TIM4)条件(我们用的是标志位来觉得是否调用“调光函数”)
c.TIM4调用“调光函数”

为什么我们不直接在UART中接收到参数后直接调用调光函数而非得让TIM4调用呢?

3.3.1 参数接收函数

上面已经说明,我们设计的时候会接收到6个参数,在这个函数里,我们需要做4件事

a.我们在“参数接收函数”中将这些参数赋值给全局变量(为什么我们不实用传参呢?因为我们用到中断没法传参)
b.如果R、D、G的值全为0,我们只需要将占空比全部设置为0即可,无需其他操作
c.如果Value_ChangeOnce为0,我们直接将占空比设置为输入的R、G、B值,无需其他操作
d.除去上面两种情况外,我们需要更改TIM4是否需要调用“调节函数”的标志位gEnableChangeLED

具体代码如下:


  1. /************************************************************** 

  2. *Function:  SetCurLightShow 

  3. *Calls:     void 

  4. *Called By:     void     

  5. *Input:     u8 Value_LED_Red    接收到的Red的亮度值 

  6.                 u8 Value_LED_Green  接收到的Green的亮度值 

  7.                 u8 Value_LED_Blue   接收到的Blue的亮度值 

  8.                 u8 Value_ChangeOnce 上升/下降一次的程度 

  9.                 u8 HoldTime_Min     在最低亮度保持的时间 

  10.                 u8 HoldTime_Max     在最高亮度保持的时间 

  11. *OUTPUT:    void 

  12. *Return:    void 

  13. *DESCRIPTION:   1.接收参数,进行情况判断 

  14.                 2.保存接收的数据到全局变量中 

  15.                 3.进行2种特殊情况的处理 

  16. *Others:    nothing 

  17. **************************************************************/  

  18. void SetCurLightShow(   u8 Value_LED_Red, u8 Value_LED_Green, u8 Value_LED_Blue,  

  19.                         u8 Value_ChangeOnce, u8 HoldTime_Min,  u8 HoldTime_Max)  

  20. {  

  21.     //获得各值,以备其他函数使用  

  22.     Set_LightSet_Red  = Value_LED_Red;  

  23.     Set_LightSet_Green= Value_LED_Green;  

  24.     Set_LightSet_Blue = Value_LED_Blue;  

  25.     Set_LightSet_ChangeOnce  = Value_ChangeOnce;  

  26.     Set_LightSet_HoldTime_Min = HoldTime_Min;  

  27.     Set_LightSet_HoldTime_Max = HoldTime_Max;  

  28.       

  29.     /************************************** 

  30.     1.三个值为0的时候灯直接关闭 

  31.     2.当改变为0的时候灯常量 

  32.     3.除了这两种情况就是需要变化的了 

  33.     ***************************************/  

  34.     if( (0 == Value_LED_Red) && (0 == Value_LED_Green) && (0 == Value_LED_Blue) )  

  35.     {  

  36.         TIM2-> Red_CCRxL   = 0;  

  37.         TIM2-> Green_CCRxL = 0;   

  38.         TIM2-> Blue_CCRxL  = 0;   

  39.         gEnableChangeLED = 0;  

  40.         return;  

  41.     }  

  42.     if( 0 == Value_ChangeOnce )  

  43.     {  

  44.         TIM2-> Red_CCRxL   = Value_LED_Red;  

  45.         TIM2-> Green_CCRxL = Value_LED_Green;  

  46.         TIM2-> Blue_CCRxL  = Value_LED_Blue;    

  47.         gEnableChangeLED = 0;  

  48.         return;  

  49.     }  

  50.       

  51.     /***************************************************** 

  52.     1.需要改变LED灯(用0x33是为了防止数值自己变为1或不为0的极 

  53.       端情况) 

  54.     2.我们默认是先向上的(当然也可以先向下) 

  55.     ******************************************************/  

  56.     gEnableChangeLED = 0x33;  

  57.     Light_Out_State = LIGHT_STATE_UP_OUT;  

  58.     Light_CurLevel_Percentage = 10;  

  59. }  


3.3.2 调光函数

这个函数是在TIM4中引用的,根据上面的函数可以知道,当引用这个函数的时候,我们已经将状态(Light_Out_State )设置为上升,当前亮度百分比(Light_CurLevel_Percentage )设置为10。我们将状态分为4类,每类的逻辑如下:


a.最低亮度显示状态(LIGHT_STATE_MIN_OUT):最值亮度保持时间(Light_Min_Max_HoldTime)自增,当Light_Min_Max_HoldTime==Set_LightSet_HoldTime_Min就说明最低亮度保持时间已经到了,然后就可以切换状态为下降,并改变亮度值。
b.最高亮度显示状态(LIGHT_STATE_MAX_OUT):最值亮度保持时间(Light_Min_Max_HoldTime)自增,当Light_Min_Max_HoldTime==Set_LightSet_HoldTime_Max就说明最高亮度保持时间已经到了,然后就可以切换状态为下降,并改变亮度值。
c.上升状态(LIGHT_STATE_UP_OUT):更改当前亮度百分比并更改亮度,当前亮度百分比大于100%就进入最大亮度状态。
d.下降状态(LIGHT_STATE_DOWN_OUT):更改当前亮度百分比并更改亮度,当前亮度百分比小于于10%就进入最低亮度状态。

除了3.3.1参数接收函数中的两种直接调节的状态以外,其余的,只要是需要调光的都首先处于LIGHT_STATE_UP_OUT状态。

在LIGHT_STATE_UP_OUT状态将根据Set_LightSet_ChangeOnce来调节亮度,当达到亮度最大以后,就进入LIGHT_STATE_MAX_OUT状态,此时为呼吸效果的上升。如果我们有最大亮度,那么亮度持续直到满足Set_LightSet_HoldTime_Max,然后进入LIGHT_STATE_DOWN_OUT状态,此时为呼吸的最大亮度保持状态;如果没有最大亮度将直接进入LIGHT_STATE_DOWN_OUT状态,此时没有持续的最大亮度。

在LIGHT_STATE_DOWN_OUT状态将根据Set_LightSet_ChangeOnce来调节亮度,当达到亮度最小以后,就进入LIGHT_STATE_MIN_OUT状态,此时为呼吸状态的下降。如果我们有最小亮度,那么亮度持续直到满足Set_LightSet_HoldTime_Min,然后进入LIGHT_STATE_UP_OUT状态,此时为呼吸的最小亮度保持状态;如果没有最大亮度将直接进入LIGHT_STATE_UP_OUT状态,此时没有持续的最低亮度。

随着Set_LightSet_ChangeOnce的编号,变化越来越快,当超过          且最大最小保持时间为0的时候就是闪亮。具体代码如下:


  1. /*************************************************  

  2. *Function:  TIM4_Updata_IRQHandler 

  3. *Calls:     void 

  4. *Called By:     中断函数     

  5. *Input:     void 

  6. *OUTPUT:    void 

  7. *Return:    void 

  8. *DESCRIPTION:   1.定时器函数,用来定时的调用呼吸灯的函数 

  9. *Others:    nothing 

  10. *************************************************/  

  11. void Pwm_BreatheCtrl()  

  12. {   

  13.     if( LIGHT_STATE_MIN_OUT == Light_Out_State )        //最低 亮度 显示状态  

  14.     {  

  15.         Light_Min_Max_HoldTime++;  

  16.         if( Light_Min_Max_HoldTime >=  Set_LightSet_HoldTime_Min )  

  17.         {  

  18.             Light_Min_Max_HoldTime = 0;  

  19.             Light_CurLevel_Percentage = 10;  

  20.             Light_Out_State = LIGHT_STATE_UP_OUT;  

  21.         }  

  22.           

  23.         //修改亮度  最低亮度为 亮度的 10%  

  24.         TIM2-> Red_CCRxL   = Set_LightSet_Red   / 10;   

  25.         TIM2-> Blue_CCRxL  = Set_LightSet_Blue  / 10;   

  26.         TIM2-> Green_CCRxL = Set_LightSet_Green / 10;   

  27.           

  28.         //修改完毕  退出  

  29.         return ;  

  30.     }  

  31.     else if( LIGHT_STATE_MAX_OUT == Light_Out_State )   //最高 亮度 显示状态  

  32.     {  

  33.         Light_Min_Max_HoldTime++;  

  34.         if( Light_Min_Max_HoldTime >=  Set_LightSet_HoldTime_Max )  

  35.         {  

  36.             Light_Min_Max_HoldTime = 0;  

  37.             Light_CurLevel_Percentage = 100;  

  38.             Light_Out_State = LIGHT_STATE_DOWN_OUT;  

  39.         }  

  40.     }  

  41.     else if( LIGHT_STATE_UP_OUT == Light_Out_State )   //上升 显示状态  

  42.     {  

  43.         Light_CurLevel_Percentage += Set_LightSet_ChangeOnce;  

  44.         if( Light_CurLevel_Percentage >= 100 )  

  45.         {  

  46.             Light_Min_Max_HoldTime = 0;  

  47.             Light_CurLevel_Percentage = 100;  

  48.             Light_Out_State = LIGHT_STATE_MAX_OUT;  

  49.         }  

  50.     }  

  51.     else ///LIGHT_STATE_DOWN_OUT                        //下降 显示状态  

  52.     {  

  53.         if( (Light_CurLevel_Percentage-10) <= Set_LightSet_ChangeOnce )  

  54.         {  

  55.             Light_Min_Max_HoldTime = 0;  

  56.             Light_CurLevel_Percentage = 10;  

  57.             Light_Out_State = LIGHT_STATE_MIN_OUT;  

  58.         }  

  59.         else  

  60.         {  

  61.             Light_CurLevel_Percentage -= Set_LightSet_ChangeOnce;  

  62.         }  

  63.     }  

  64.       

  65.     Light_CurLevel_Percentage_u16 = Light_CurLevel_Percentage; // 10 ~ 100  

  66.     TIM2-> Red_CCRxL   = (Set_LightSet_Red   * Light_CurLevel_Percentage_u16+50) / 100; //+50 为四舍五入  

  67.     TIM2-> Blue_CCRxL  = (Set_LightSet_Blue  * Light_CurLevel_Percentage_u16+50) / 100;  

  68.     TIM2-> Green_CCRxL = (Set_LightSet_Green * Light_CurLevel_Percentage_u16+50) / 100;  

  69. }  



4 结束语


至此,我们PWM波实现三基色呼吸灯已经写完了,这些代码可以直接使用在实际的项目中。相关代码可以移步下面的地址下载使用,欢迎大家和我一起学习和交流。


关键字:STM8S003F3  PWM波实  三基色呼吸灯 引用地址:STM8S003F3通过PWM波实现三基色呼吸灯

上一篇:矩阵键盘扫描程序实例
下一篇:小记-STM8S003F3端口复用和端口开漏

推荐阅读最新更新时间:2024-03-16 15:45

STM8S003F3 内部flash调试
这里做个记录,方便以后可以使用 查看文档手册,data eeprom的起始地址为0x004000,这个是用于存放一些例如版本号,ID,或者是标志位的这个来使用 示例代码: typedef enum { FLASH_MEMTYPE_PROG = (u8)0x00, /*! Program memory */ FLASH_MEMTYPE_DATA = (u8)0x01 /*! Data EEPROM memory */ } FLASH_MemType_TypeDef; int main() { u32 addr = 0x4000; CLK_HSIPrescalerConfig(CLK_PRE
[单片机]
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

更多每日新闻
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved