单片机学习小组
直播中

算一挂

8年用户 887经验值
私信 关注

怎样通过PWM驱动扩展板上的冷光LED和暖光LED呢

调节LED灯光亮度的原理是什么?
怎样通过PWM驱动扩展板上的冷光LED和暖光LED呢?有哪些步骤?

回帖(1)

张帅

2022-2-15 10:39:37
PWM 实现调光

一、项目效果

预期(没有相关经验)

达到的目的(是实际能做到的)

通过 PWM 驱动扩展板上的冷光 LED 和暖光 LED,
并通过红外遥控器调节占空比来实现调节色温和调节亮度的功能。
这里也可以使用普通的,调节普通的led灯珠的亮度

  • 使用 CH+/-键设置灯光的色温
  • 使用+/-键设置灯光亮度
  • 使用 EQ 键控制灯光开启或者关闭
    (是基于成功做出,遥控器的实验)
  • 通过串口打印此时的亮度和色温百分比
二、原理说明

调光原理

学习物联网最经典的案例就是实现一款智能灯,
用户可以对其进行色温、亮度、颜色(下一章讲解)的控制,
色温就是指灯光的颜色是偏白(冷光源) 还是偏黄(暖光源) ,
单位是K(开尔文),色温值越高光源颜色就越白反之色温越低,光源颜色就越黄。
通常使用冷光灯和暖光灯配合的方式,通过调节各自的亮度来实现色温的任意调节。
调节灯光亮度的原理就是通过 PWM 的占空比来设置灯光亮暗,
调节灯光色温则是在当前亮度值(占空比)基础上再次进行比例的分配


这便是调光原理
硬件连接

开发板上使用的冷光灯色温为 6000K,暖光灯为 3000K,
上图这种情况下对应的色温值为 3900K,另外为了保证色温恒定在固定值,通常亮度不能设置为 0,
而是有一个保底的最小值,比如 10%。
还需要注意的是,为了大家方便理解今后不使用 K 作为色温单位,
而是像亮度那样使用百分比值,范围 0-100,值越大,灯光越白
开发板上的两颗高亮 LED 分别是暖光 LED 和冷光 LED

其中使用这两个引脚(PA15和PB5)的注意点
PA15上电后,该引脚默认是 JTAG 的 JTDI, 因此需要将其引脚重映射为 TIM2 的通道 1,
PB3 上电默认功能是 JTAG 的 JTDO,所以也需要 PB3 重映射为 TIM2 的通道 1,
这两个引脚的功能映射图

配置定时器 2 的通道 1/2 作为 PWM 输出的步骤



  • 重映射 TIM2 的 1/2 通道到 PA15 和 PB3 引脚,并初始化为复用输出
  • 初始化定时器,主要是设置定时器周期和重装载寄存器
  • 分别初始化定时器 2 的输出比较通道 1/2
总结

难点就在于将亮度和色温值最终转换为占空比的计算公式上。
程序说明

灯光初始化函数


/**
* 功能:初始灯光
* 参数:
*          brightness:初始化亮度 10-100
*          colortemp:初始化色温 0-100         
* 返回值:None
*/
void initLight(u8 brightness,u8 colortemp)
{
    GPIO_InitTypeDef GPIO_InitStructure;


    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA |RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO,ENABLE);


GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);    //禁止JTAG保留SWD
    GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2, ENABLE);      //设置JTAG为定时器2部分映射,只使用SWD模式


    /*设置冷光灯*/
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);


    /*设置暖光灯*/
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &GPIO_InitStructure);


    setLight(brightness,colortemp);
}
结合代码来理解这句话
灯光初始化函数实现了复用功能重映射,
并初始化冷光灯和暖光灯的控制引脚为复用输出。
设置灯光函数
/**
* 功能:设置灯光亮度和色温
* 参数:
*          brightness:亮度 10-100
*          colortemp:色温 0-100         
* 返回值:None
*/
void setLight(u8 brightness,u8 colortemp)
{
    /**
     * 无论是亮度还是色温,最终都体现在LED的亮度上
     * LED最终的亮度计算公式为:满占空比(重装载值) * 亮度百分比 * 色温百分比
     * 并且要保证冷光和暖光的色温比值之和为100%
     * */
    TIM_SetCompare1(TIM2,getPeriod(TIM2)*brightness/100*colortemp/100);         //设置冷光 对应PA15 TIM2_CH1
    TIM_SetCompare2(TIM2,getPeriod(TIM2)*brightness/100*(100-colortemp)/100);   //设置暖光 对应PB3  TIM2_CH2
}
为了方便理解, 没有把占空比计算公式进行合并。
我们通过 TIM_setCompare1()和 TIM_setCompare2()函数
对定时器 2 的捕获/比较寄存器 1/2 赋值来改变 PWM 占空比,
传入函数的第二个参数就是亮度-色温的计算公式,
在公式中先获取了重装载寄存器中的值(满占空比对应的计数值),
然后乘上亮度的百分比得到我们想要的亮度,最后在此基础上又将亮度值乘上色温百分比,
这里要注意,冷/暖光的色温比值之和是 100%,
这就保证了无论亮度值是多少,最终呈现出来的色温是不受影响的。
PWM 初始化函数

初始化定时器2输出比较通道1来生成PWM 对应冷光
/**
* 功能:初始化定时器2输出比较通道1来生成PWM 对应冷光
* 参数:
*      duty:设置PWM输出的占空比
* 返回值:None
*/
void initTIM2OC1(u16 duty)
{
TIM_OCInitTypeDef  TIM_OCInitStructure;    //定义输出比较初始化结构体


TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;   //设置PWM1模式
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //输出使能
TIM_OCInitStructure.TIM_Pulse = duty;   //设置捕获比较寄存器的值
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;   //设置有效电平为高电平


TIM_OC1Init(TIM2, &TIM_OCInitStructure);    //生效初始化设置


TIM_OC1PreloadConfig(TIM2, TIM_OCPreload_Enable);   //使能输出比较预装载,即更改完输入捕获寄存器的值要等到更新事件发生才生效,该行加不加无所谓
}
初始化定时器2输出比较通道2来生成PWM 对应暖光
/**
* 功能:初始化定时器2输出比较通道2来生成PWM 对应暖光
* 参数:
*      duty:设置PWM输出的占空比
* 返回值:None
*/
void initTIM2OC2(u16 duty)
{
TIM_OCInitTypeDef  TIM_OCInitStructure;    //定义输出比较初始化结构体


TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;   //设置PWM1模式
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //输出使能
TIM_OCInitStructure.TIM_Pulse = duty;   //设置捕获比较寄存器的值
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;   //设置有效电平为高电平


TIM_OC2Init(TIM2, &TIM_OCInitStructure);    //生效初始化设置


TIM_OC2PreloadConfig(TIM2, TIM_OCPreload_Enable);   //使能输出比较预装载,即更改完输入捕获寄存器的值要等到更新事件发生才生效,该行加不加无所谓
}
主函数


int main(void)
{
u8 light_power = 1; //默认开机
s8 brightness = 100; //亮度
s8 colortemp = 50; //色温


/*初始化各外设*/
initSysTick();                          
initLED();
initUART();
initNVIC(NVIC_PriorityGroup_2);


/*初始化NEC*/
initNEC();
    /*定时器4时钟周期10us,中断周期200ms,使能更新中断和捕获通道3中断*/
    initTIMx(TIM4,719,19999,TIM_IT_Update|TIM_IT_CC3,ENABLE);
    /*设置下降沿触发捕获*/
    initTIM4IC3(TIM_ICPolarity_Falling);  


/*设置定时器2时钟为10us,1KHz*/
initTIMx(TIM2,719,99,TIM_IT_Update,DISABLE);
initTIM2OC1(50);
initTIM2OC2(50);
/*亮度10%,色温50%*/
initLight(100,50);  
while(1)
    {
if(NEC_DataStructure.CmdCode_H == 0x45)     //CH-键按下
{
NEC_DataStructure.CmdCode_H = 0;     //清除键值
colortemp -= 10;
if(colortemp<0)   
{
colortemp = 0;
}
setLight(brightness,colortemp);
}else if(NEC_DataStructure.CmdCode_H == 0x47) //CH+键按下
{
NEC_DataStructure.CmdCode_H = 0;       //清除键值
colortemp += 10;
if(colortemp>100)   
{
colortemp = 100;
}
setLight(brightness,colortemp);
}else if(NEC_DataStructure.CmdCode_H == 0x07) //-键按下      
{
NEC_DataStructure.CmdCode_H = 0;       //清除键值
brightness -= 10;
if(brightness<10)   
{
brightness = 10;                       //保底值
}
setLight(brightness,colortemp);
}else if(NEC_DataStructure.CmdCode_H == 0x15) //+键按下
{
NEC_DataStructure.CmdCode_H = 0;       //清除键值
brightness += 10;
if(brightness>100)   
{
brightness = 100;                       
}
setLight(brightness,colortemp);
}else if(NEC_DataStructure.CmdCode_H == 0x09) //EQ键按下
{
NEC_DataStructure.CmdCode_H = 0;       //清除键值
light_power = !light_power;   //开关机状态取反
if(light_power == 0)
{
setLight(0,0);   //关灯
}else
{
setLight(brightness,colortemp);       //开灯
}   
}else
{


}


printf("brightness is :%d%%n",brightness);
printf("color temperature is :%d%%n",colortemp);
toggleLED();
Delay_ms(100);
    }
}
代码很长,主要实现的是
定时器 2 设置的时钟周期为 10us,则 1KHz(1ms)需要计数 100 个。
然后初始化 PWM 并设置灯光开机默认最大亮度,色温 50%。
在用户逻辑循环中不断的判断此时红外键值,并根据键值做出相应的灯光控制动作。
最终效果

举报

更多回帖

发帖
×
20
完善资料,
赚取积分