PWM 实现调光——测试pwn的功能

本文介绍了一种使用PWM技术实现LED灯亮度和色温调节的方法。通过红外遥控器控制灯光的色温和亮度,采用STM32的定时器产生PWM信号。文章详细解释了PWM的工作原理及其在智能照明系统中的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

PWM 实现调光

一、项目效果

预期(没有相关经验)

在这里插入图片描述

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

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

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

二、原理说明

调光原理

学习物联网最经典的案例就是实现一款智能灯,
用户可以对其进行色温、亮度、颜色(下一章讲解)的控制,
色温就是指灯光的颜色是偏白(冷光源) 还是偏黄(暖光源) ,
单位是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 输出的步骤

  1. 重映射 TIM2 的 1/2 通道到 PA15 和 PB3 引脚,并初始化为复用输出
  2. 初始化定时器,主要是设置定时器周期和重装载寄存器
  3. 分别初始化定时器 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%。
在用户逻辑循环中不断的判断此时红外键值,并根据键值做出相应的灯光控制动作。

最终效果

在这里插入图片描述

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值