1 简介

  • 本实验实现RGB灯的颜色渐变,从红慢慢过渡到绿,再慢慢过渡到蓝,再慢慢过渡到红。其原理是通过查表的方法给三个通道不同的pwm,从而改变颜色。

2 硬件

使用野火指南者开发板。

2.1 RGB

在这里插入图片描述

  • RGB灯由红蓝绿三个小灯构成,使用PWM控制是可以混合成256种不同的颜色。STM32的三个引脚需要选择具有定时器输出通道功能,而不是任意的GPIO口。本次实验使用PB5、PB0及PB1引脚,分别是定时器TIM3的通道2,3,4。
  • PB5对应红色R,PB0对应绿色G,PB1对应蓝色B。

3 软件设计

  • 首先我们需要知道三个通道的值对应的颜色。例如,红色为(255,0,0),绿色为(0,255,0),蓝色为(0,0,255)。知道值对应的颜色就可以对RGB进行颜色的控制。
    RGB颜色表查询网站:https://tool.oschina.net/commons?type=3

3.1 GPIO初始化

//初始化PB5和PE5为输出口.并使能这两个口的时钟		    
//LED IO初始化
void LED_Init(void) 
{
  	GPIO_InitTypeDef GPIO_InitStructure;
  	/* GPIO clock enable */
  	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_AFIO, ENABLE); 
  	/*IO设置*/
	GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3, ENABLE); 
  	/* 配置LED灯用到的引脚 */
	//红
  	GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_5 ;	
  	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;		    // 复用推挽输出
  	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  	GPIO_Init(GPIOB, &GPIO_InitStructure);
	//绿
	GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_0 ;
  	GPIO_Init(GPIOB, &GPIO_InitStructure);
	//蓝
	GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_1 ;
  	GPIO_Init(GPIOB, &GPIO_InitStructure);
}
  • 由于本实验直接使用定时器输出通道的脉冲信号控制RGB灯,此处代码把GPIO相关的引脚都配置成了复用推挽输出模式。
  • 由于红灯使用的引脚需要用到第二功能,本代码使用COLOR_GPIO_REMAP_FUN()进行了该引脚的功能重定义操作。
  • 红灯为B5引脚,绿灯为B0引脚,蓝灯为B1引脚。

3.2 TIM3定时器初始化

void TIM3_Int_Init(u16 arr,u16 psc)
{
  	TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
	TIM_OCInitTypeDef  TIM_OCInitStructure;	
	NVIC_InitTypeDef NVIC_InitStructure;

	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //时钟使能

	TIM_TimeBaseStructure.TIM_Period = arr-1; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值	 计数到5000为500ms
	TIM_TimeBaseStructure.TIM_Prescaler = psc-1; //设置用来作为TIMx时钟频率除数的预分频值  10Khz的计数频率  
	TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式
	TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
	
	/* PWM模式配置 */
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;	    				//配置为PWM模式1
  	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;	//使能输出
  	TIM_OCInitStructure.TIM_Pulse = 0;				 						  			//设置初始PWM脉冲宽度为0	
  	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;  	  //当定时器计数值小于CCR_Val时为低电平
	
	//RGB通道
  	TIM_OC2Init ( TIM3, &TIM_OCInitStructure );	
  	TIM_OC2PreloadConfig ( TIM3, TIM_OCPreload_Enable );						//使能预装载	

 	TIM_OC3Init ( TIM3, &TIM_OCInitStructure );	
  	TIM_OC3PreloadConfig ( TIM3, TIM_OCPreload_Enable );						//使能预装载	

  	TIM_OC4Init ( TIM3, &TIM_OCInitStructure );	
  	TIM_OC4PreloadConfig ( TIM3, TIM_OCPreload_Enable );						//使能预装载	
	
	TIM_ARRPreloadConfig(TIM3, ENABLE);
	
	TIM_Cmd(TIM3, ENABLE);  //使能TIMx外设
	TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE ); //使能指定的TIM3中断,允许更新中断

	NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;  //TIM3中断
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;  //先占优先级0级
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;  //从优先级3级
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
	NVIC_Init(&NVIC_InitStructure);  //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器			 
}

3.3 RGB渐变

uint16_t rgb_led_time=20;
const uint16_t R[] = {
	255, 226, 198, 170, 141, 113,  85,  56,  28,   0,
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	0,  28,  56,  85, 113, 141, 170, 198, 226, 255
};
const uint16_t G[] = {
	0,  28,  56,  85, 113, 141, 170, 198, 226, 255,
	255, 226, 198, 170, 141, 113,  85,  56,  28,   0,
	0, 0, 0, 0, 0, 0, 0, 0, 0,0
};
const uint16_t B[] = {
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	0,  28,  56,  85, 113, 141, 170, 198, 226, 255,
	255, 226, 198, 170, 141, 113,  85,  56,  28, 0
};
//定时器3中断服务程序
void TIM3_IRQHandler(void)   //TIM3中断
{
	static uint16_t pwm_index = 0;		//用于PWM查表
	static uint16_t amplitude_cnt = 0;	//持续时间
	if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET) //检查指定的TIM中断发生与否:TIM 中断源 
	{
		TIM_ClearITPendingBit(TIM3, TIM_IT_Update  );  //清除TIMx的中断待处理位:TIM 中断源 
		amplitude_cnt++;
				
		// 每个颜色持续的时间
		if(amplitude_cnt>rgb_led_time)		 						
		{		
			pwm_index++; //标志PWM表指向下一个元素
			//若PWM表已到达结尾,重新指向表头
			if( pwm_index >  (30-1))			
			{
				pwm_index=0;					
			}
				amplitude_cnt=0; //重置计数标志
		}
		else
		{	
			TIM3->CCR2 = R[pwm_index]; // PWM
			TIM3->CCR3 = G[pwm_index]; // PWM
			TIM3->CCR4 = B[pwm_index]; // PWM
		}	
	}
}
  • 要实现RGB灯渐变,需要通过在不同时刻给其三个通道不同的PWM值,从而产生不同的颜色。因此,我制定了一个含有30个元素的数组,分别为R[30],G[30],B[30]。每个时刻t的RGB灯的颜色为[R[t],G[t],B[t]],从而实现了RGB颜色从红慢慢过渡到绿,再慢慢过渡到蓝,再慢慢过渡到红。
  • RGB渐变由于需要控制其渐变周期,所以我将其写在TIM3定时器中断中。
  • 该算法的思路:假设设置定时器的周期为 t ms,R,G,B三张表拥有30个PWM数值,其每个数值会持续rgb_led_time个t,因此RGB循环一次的周期为T=(30 * rgb_led_time * t)/1000 s。若rgb_led_time=10,t=10ms,则RGB循环一次的周期为T=3s。

3.4 主函数

int main(void)
{		
	delay_init();	    	 //延时函数初始化	  
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); 	 //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
	uart_init(115200);	 //串口初始化为115200
 	LED_Init();			     //LED端口初始化
 	TIM3_Int_Init(1000,72);	 //不分频。PWM频率=72000000/72=1000Khz 1us*1000=1ms
  	while(1)
	{
	}	 
 }
Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐