STMSF通过PWM波实现三基色呼吸灯精编版.docx
- 文档编号:28045179
- 上传时间:2023-07-07
- 格式:DOCX
- 页数:20
- 大小:30.77KB
STMSF通过PWM波实现三基色呼吸灯精编版.docx
《STMSF通过PWM波实现三基色呼吸灯精编版.docx》由会员分享,可在线阅读,更多相关《STMSF通过PWM波实现三基色呼吸灯精编版.docx(20页珍藏版)》请在冰豆网上搜索。
STMSF通过PWM波实现三基色呼吸灯精编版
集团企业公司编码:
(LL3698-KKI1269-TM2483-LUI12689-ITT289-DQS58-MG198)
STMSF通过PWM波实现三基色呼吸灯
前段时间使用STM8S003F3实现了一个三基色灯的各种效果,故写一篇文章作为一个记录。
1综述
我们知道,要是的LED灯亮直接通电即可。
而要改变灯的亮度,我们有两种方法:
改变电流和PWM调光。
我们首先想到的就是改变它的驱动电流,因为LED的亮度是几乎和它的电流直接成正比关系。
然而用调正向电流的方法来调节亮度会产生一个问题:
在调亮度的同时也会改变它的光谱和色温,这样就会会产生色偏。
因为目前白光LED都是用蓝光LED加黄色荧光粉而产生,当正向电流减小时,蓝光LED亮度增加而黄色荧光粉的厚度并没有按比例减薄,从而使其光谱的主波长增长。
这个问题对于一般的照明是没有问题的,因为色温的变化量毕竟不是很大。
但是对电源来说当电流过小时会产生闪烁,除非电源的恒流范围很宽,完全可以从0到最大。
这样才没有问题。
简而言之,电流调光有色温变化和电源电流过小产生闪烁的问题。
曾经做过一个项目,用于某设备上需要非常非常平稳的调光,显然电流调光是无法实现。
同时像本文介绍的三基色调光有颜色要求的显然也不行。
因此我们使用PWM调光。
既然PWM调光可以避免上面的两个问题,为什么不直接都用PWM调光呢因为我们毕竟是做产品,要考虑成本问题。
使用PWM调光至少需要一颗能支持PWM的芯片(当然还有外围电路,但是电流调光也是有电路的。
我们也应该知道PWM信号也可以由脉冲发生器提供),另外它需要编写程序。
所以只有在需要的场合才使用PWM调光(使用PWM调光需要注意的问题是频率不能太低或者太高,推荐150-400Hz之间。
)。
PWM的优点如下:
●PWM调光就不会产生色偏,因为它总是工作在0或者最大两种状态。
●PWM的占空比很好控制,而且精度高●对电源没有影响,因为不会改变电源的工作条件,只是给电源开或者关。
2PWM波调光的原理
脉宽调制(PWM)是利用微处理器的数字输出来对模拟电路进行控制的的技术,广泛应用在从测量、通信到功率控制与变换及LED照明等许多领域中。
通过以数字方式控制模拟电路,可以大幅度降低系统的成本和功耗。
此外,许多微控制器和DSP已经在芯片上包含了PWM控制器,这使数字控制的实现变得更加容易了。
简言之,PWM是一种对模拟信号电平进行数字编码的方法。
通过高分辨率计数器的使用,方波的占空比被调制用来对一个具体模拟信号的电平进行编码。
PWM信号仍然是数字的,因为在给定的任何时刻,满幅值的直流供电要么完全有(ON),要么完全无(OFF)。
电压或电流源是以一种通(ON)或断(OFF)的重复脉冲序列被加到模拟负载上去的。
通的时候即是直流供电被加到负载上的时候,断的时候即是供电被断开的时候。
只要带宽足够,任何模拟值都可以使用PWM进行编码。
2.1占空比(DutyCycleorDutyRatio)
首先我们需要了解占空比,占空比的解释可以归纳为如下几种:
●在一串理想的脉冲序列中(如方波),正脉冲的持续时间与脉冲总周期的比值。
例如:
脉冲宽度1μs,信号周期4μs的脉冲序列占空比为0.25。
●在一段连续工作时间内脉冲占用的时间与总时间的比值。
●在周期型的现象中,现象发生的时间与总时间的比。
通俗一点讲就是电路释放能量的有效时间与总释放时间的比。
2.2调光比
然后我们需要了解调光比,调光比则是按下面的方法计算(Foper:
工作频率;Fpwm:
调光频率;):
调光比率=Foper/Fpwm,(其实也就是调光的最低有效占空比)比如Foper=100KHZ;Fpwm=200Hz,则调光比为:
100K/200=500;这个指标在很多驱动芯片的规格书里会说明的。
2.3PWM调光
2.3.1PWM调光原理
●若一个占空比为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(Alternatefunctionremappingbits,候补功能映射位)——我们继续看芯片资料
其中OPT2【选项字节(Optionbyte)编程】和NOPT2需要是相反的(可能是出于校验考虑),我们从《数据手册》中可以知道:
应用程序可直接向目标地址进行写操作。
所以我们直接对这两个地址进行写操作,那么数值是多少呢我们继续看《数据手册》,如下图所示
从上图中我们可以看到,我们将AFR1设置为1,将AFR0设置成1。
代码如下:
[cpp]
1./***************************************************************
2.*Function:
FLASH_Init
3.*Calls:
void
4.*CalledBy:
All_Config.c
5.*Input:
void
6.*OUTPUT:
void
7.*Return:
void
8.*DESCRIPTION:
1.设置管脚复用功能(AFR0要设置为1AFR1要设置为1)
9.2.eeprom每一次只能操作一个字节
10.*Others:
nothing
11.***************************************************************/
12.
13.volatileunsignedcharflash_OPT2@0x4803;
14.volatileunsignedcharflash_NOPT2@0x4804;
15.#defineFLASH_EOP0X04//FLASH_IAPSR中位,编程是否结束
16.#defineFLASH_DUL0X08//flashdataeeprom是否解锁标志位
17.voidFLASH_Init()
18.{
19.//第一步?
初始化EEPROM
20.while((FLASH->IAPSR&FLASH_DUL)==0X00)
21.{
22.
23.FLASH->DUKR=0XAE;//中文资料上说的和实际是相反的
24.FLASH->DUKR=0X56;
25._asm("NOP");
26.}
27.
28.
29.//第二步?
对OPT进行编程,首先需要如下操作:
开启opt编程
30.FLASH->CR2|=0X80;//OPT=1
31.FLASH->NCR2&=0X7F;//NOPT=0
32.
33.//第三步?
修改内存
34./***************************
35.1.修改参数,启用复用功能
36.2.OPT2和NOPT2要相反
37.****************************/
38.//修改OPT2
39.flash_OPT2=0X03;//00000011
40._asm("NOP");
41.while((FLASH->IAPSR&FLASH_EOP)==0);//等待操作完成?
42.//修改NOPT2
43.flash_NOPT2=~flash_OPT2;
44._asm("NOP");
45.while((FLASH->IAPSR&FLASH_EOP)==0);//等待操作完成
46.
47.//第四步?
对OPT进行编程,最后需要如下操作:
禁用opt编程
48.FLASH->CR2&=~0X80;//OPT=1
49.FLASH->NCR2|=0X80;//NOPT=0
50.}
这样,我们就完成了复用功能的“存储器地址分布重映射”。
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.7PWM模式可以看到,脉冲宽度调制(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.7TIM2产生PWM波的初始化程序
具体代码如下:
[cpp]
1./*************************************************
2.*Function:
TIM2_InitPwmCtrl
3.*Calls:
void
4.*CalledBy:
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.
15.voidTIM2_InitPwmCtrl()
16.{
17.CLK->PCKENR1|=CLK_PCKENR1_TIM2;//TIM2使能
18.
19./**********************************************************
20.1.预分频器
21.2.设置定时器的时钟(根据已经分频的主时钟来分频)
22.3.分频系数越大,周期越大,也就是频率越低
23.4.分频系数1~2^15,如果为5就是32分频(原来为16MHZ)
24.**********************************************************/
25.TIM2->PSCR=5;
26.
27.//选择TIM2通道1的工作模式(PWM2波的模式)
28.TIM2->CCMR1=0X68;//0110?
1000
29.TIM2->CCMR2=0X68;
30.TIM2->CCMR3=0X68;
31.
32./**********************************************************
33.1.自动装载寄存器(分高低位——也就是16位寄存器)
34.2.(每次就是上面分频后的时间,假设分频后是2us),每2us复位一次
35.定时器2,也就是说计数器每变化一次耗时2us,0到255经过255个2us
36.3.在这个工程中,我们认为255就是最亮(也就是在周期内都是高),
37.当然我们可以设置250,设置多少就看精细程度了
38.**********************************************************/
39.TIM2->ARRH=0;
40.TIM2->ARRL=255&0X0FF;
41.
42./**********************************************************
43.1.捕获/比较寄存器
44.2.设置亮度,这一位控制占空比
45.**********************************************************/
46.TIM2->CCR1H=0;
47.TIM2->CCR1L=0;
48.TIM2->CCR2H=0;
49.TIM2->CCR2L=0;
50.TIM2->CCR3H=0;
51.TIM2->CCR3L=0;
52.
53./**********************************************************
54.1.计数器使能
55.2.捕获/比较使能寄存器使能
56.**********************************************************/
57.TIM2->CR1|=TIM2_CR1_CEN;//使能?
计数器
58.TIM2->CCER1|=TIM2_CCER1_CC1E;//使能?
捕获/比较寄存器1
59.TIM2->CCER1|=TIM2_CCER1_CC2E;//使能?
捕获/比较寄存器2
60.TIM2->CCER2|=TIM2_CCER2_CC3E;//使能?
捕获/比较寄存器3
61.}
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
具体代码如下:
[cpp]
1./**************************************************************
2.
3.*Function:
SetCurLightShow
4.*Calls:
void
5.*CalledBy:
void
6.*Input:
u8Value_LED_Red接收到的Red的亮度值
7.u8Value_LED_Green接收到的Green的亮度值
8.u8Value_LED_Blue接收到的Blue的亮度值
9.u8Value_ChangeOnce上升/下降一次的程度
10.u8HoldTime_Min在最低亮度保持的时间
11.u8HoldTime_Max在最高亮度保持的时间
12.*OUTPUT:
void
13.*Return:
void
14.*DESCRIPTION:
1.接收参数,进行情况判断
15.2.保存接收的数据到全局变量中
16.3.进行2种特殊情况的处理
17.*Others:
nothing
18.**************************************************************/
19.
20.voidSetCurLightShow(u8Value_LED_Red,u8Value_LED_Green,u8Value_LED_Blue,
21.u8Value_ChangeOnce,u8HoldTime_Min,u8HoldTime_Max)
22.{
23.//获得各值,以备其他函数使用
24.Set_LightSet_Red=Value_LED_Red;
25.Set_LightSet_Green=Value_LED_Green;
26.Set_LightSet_Blue=Value_LED_Blue;
27.Set_LightSet_ChangeOnce=Value_ChangeOnce;
28.Set_LightSet_HoldTime_Min=HoldTime_Min;
29.Set_LightSet_HoldTime_Max=HoldTime_Max;
30.
31./**************************************
32.1.三个值为0的时候灯直接关闭
33.2.当改变为0的时候灯常量
34.3.除了这两种情况就是需要变化的了
35.***************************************/
36.if((0==Value_LED_Red)&&(0==Value_LED_Green)&&(0==Value_LED_Blue))
37.{
38.
39.TIM2->Red_CCRxL=0;
40.TIM2->Green_CCRxL=0;
41.TIM2->Blue_CCRxL=0;
42.gEnableChangeLED=0;
43.return;
44.}
45.
46.if(0==Value_ChangeOnce)
47.{
48.
49.TIM2->Red_CCRxL=Value_LED_Red;
50.TIM2->Green_CCRxL=Value_LED_Green;
51.TIM2->Blue_CCRxL=Value_LED_Blue;
52.gEnableChangeLED=0;
53.return;
54.}
55.
56.
57./*****************************************************
58.1.需要改变LED灯(用0x33是为了防止数值自己变为1或不为0的极
59.端情况)
60.2.我们默认是先向上的(当然也
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- STMSF 通过 PWM 实现 基色 呼吸 精编