本帖最后由 xld0932 于 2022-3-25 19:00 编辑
#申请原创# @21小跑堂
在我的印象当中LED灯控制是接触MCU时的第一个外设实验了,因为其本身只需要通过GPIO端口输出高低电平就可以实现了,常规的原理图画法有很多,有VCC接LED灯再串一个电阻连接到MCU的引脚上的,也有MCU引脚接LED灯再串一个电阻连接到GND上的,当然还有通过三极管等等电路来驱动的……
在如上原理图中,串联的1k电阻是用于限制电流大小的;对于方式1中MCU_PIN输出高电平时,LED处于熄灭的状态,输出低电平时,LED则处于点亮的状态;对于方式2中的MCU_PIN输出高电平时,LED处于点亮的状态,输出低电平时,LED则处于熄灭的状态;对于其它的方式,则需要根据实际电路设计来进行高低电平的输出控制了。
在淘宝上买了一个LED模块,带有红黄绿3个LED灯;结合MM32F0140的核心板,我们今天实现LED的几个实验: 1、LED闪烁实验 2、LED流水灯实验 3、PWM方式调节LED灯显示亮度 4、对数方式实现呼吸灯
LED闪烁实验 LED灯闪烁就是通过时间间隔来控制LED灯点亮或者熄灭的操作,达到闪烁的效果,具体的初始化及功能实现代码如下所示: void LED_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHBPeriphClockCmd(RCC_AHBENR_GPIOA, ENABLE);
GPIO_StructInit(&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_WriteBit(GPIOA, GPIO_Pin_3, Bit_RESET);
GPIO_WriteBit(GPIOA, GPIO_Pin_4, Bit_RESET);
GPIO_WriteBit(GPIOA, GPIO_Pin_5, Bit_RESET);
TASK_Append(TASK_ID_RYG, LED_Handler, 250);
}
void LED_Handler(void)
{
bool PA3 = GPIO_ReadOutputDataBit(GPIOA, GPIO_Pin_3);
bool PA4 = GPIO_ReadOutputDataBit(GPIOA, GPIO_Pin_4);
bool PA5 = GPIO_ReadOutputDataBit(GPIOA, GPIO_Pin_5);
if(!PA3) GPIO_WriteBit(GPIOA, GPIO_Pin_3, Bit_SET);
else GPIO_WriteBit(GPIOA, GPIO_Pin_3, Bit_RESET);
if(!PA4) GPIO_WriteBit(GPIOA, GPIO_Pin_4, Bit_SET);
else GPIO_WriteBit(GPIOA, GPIO_Pin_4, Bit_RESET);
if(!PA5) GPIO_WriteBit(GPIOA, GPIO_Pin_5, Bit_SET);
else GPIO_WriteBit(GPIOA, GPIO_Pin_5, Bit_RESET);
}
LED闪烁实验效果
LED流水灯实验 LED灯模块上有3个灯,我们按照固定的方向依次点亮LED灯,不停的循环反复达到流水灯的效果,具体的初始化及功能实现代码如下所示: void LED_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHBPeriphClockCmd(RCC_AHBENR_GPIOA, ENABLE);
GPIO_StructInit(&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_WriteBit(GPIOA, GPIO_Pin_3, Bit_RESET);
GPIO_WriteBit(GPIOA, GPIO_Pin_4, Bit_RESET);
GPIO_WriteBit(GPIOA, GPIO_Pin_5, Bit_RESET);
TASK_Append(TASK_ID_RYG, LED_Handler, 250);
}
void LED_Handler(void)
{
static uint8_t Index = 0;
switch(Index)
{
case 0:
GPIO_WriteBit(GPIOA, GPIO_Pin_3, Bit_RESET);
GPIO_WriteBit(GPIOA, GPIO_Pin_4, Bit_RESET);
GPIO_WriteBit(GPIOA, GPIO_Pin_5, Bit_RESET);
break;
case 1:
GPIO_WriteBit(GPIOA, GPIO_Pin_3, Bit_SET);
GPIO_WriteBit(GPIOA, GPIO_Pin_4, Bit_RESET);
GPIO_WriteBit(GPIOA, GPIO_Pin_5, Bit_RESET);
break;
case 2:
GPIO_WriteBit(GPIOA, GPIO_Pin_3, Bit_RESET);
GPIO_WriteBit(GPIOA, GPIO_Pin_4, Bit_SET);
GPIO_WriteBit(GPIOA, GPIO_Pin_5, Bit_RESET);
break;
case 3:
GPIO_WriteBit(GPIOA, GPIO_Pin_3, Bit_RESET);
GPIO_WriteBit(GPIOA, GPIO_Pin_4, Bit_RESET);
GPIO_WriteBit(GPIOA, GPIO_Pin_5, Bit_SET);
break;
}
Index = (Index + 1) % 4;
}
LED流水灯实验效果
PWM方式调节LED灯显示亮度 上面我们是通过GPIO端口引脚直接输出高或者低电平来驱动LED灯点亮或熄灭的,在GPIO输出电平后一直维持着输出电平的状态,我们可以理解为这个时候输出占空比是100%或者0%;LED灯一直处于最亮或者熄灭的状态;而PWM方式则是通过固定的一个频率,通过修改其占空比(高电平与低电平在一个周期内占用时间的比例)可以来控制LED灯的显示亮度,可以直观的理解之前是100%供电的,LED灯则是最亮的,PWM方式是断断续续的供电,这个时候LED灯显示就弱了,亮弱则是由PWM占空比来决定的。具体的初始化及功能实现代码如下所示: void LED_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
RCC_APB1PeriphClockCmd(RCC_APB1ENR_TIM2, ENABLE);
TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);
TIM_TimeBaseStructure.TIM_Prescaler = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseStructure.TIM_Period = 1000 - 1;
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
TIM_OCStructInit(&TIM_OCInitStructure);
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = 500 - 1;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Reset;
TIM_OC4Init(TIM2, &TIM_OCInitStructure);
TIM_OC4PreloadConfig(TIM2, TIM_OCPreload_Enable);
TIM_ARRPreloadConfig(TIM2, ENABLE);
TIM_Cmd(TIM2, ENABLE);
TIM_CtrlPWMOutputs(TIM2, ENABLE);
RCC_AHBPeriphClockCmd(RCC_AHBENR_GPIOA, ENABLE);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource3, GPIO_AF_2); /* TIM2_CH4 */
GPIO_StructInit(&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
TASK_Append(TASK_ID_RYG, LED_Handler, 10);
}
void LED_Handler(void)
{
static uint16_t Value = 0, State = 0;
TIM_SetCompare4(TIM2, Value);
if(State == 0)
{
Value += 10;
if(Value >= 1000)
{
Value = 990;
State = 1;
}
}
else
{
if(Value > 10)
{
Value -= 10;
}
else
{
Value = 0;
State = 0;
}
}
}
PWM方式调节LED灯显示亮度效果
对数方式实现呼吸灯 在上面通过直接修改PWM固定步长的占空比来实现的呼吸灯实验中,我们发现在占空比由小往大变化时,占空比在1%到45%左右时,看到的变化最明显,但再往上变化时已经基本上看不出什么变化了,效果不是很好,甚至可以说没有达到呼吸灯应有的效果;
那么PWM占空比应该如何来控制才能够达到视觉感观上呼吸亮度的效果呢?通过尝试,或者有经验的网友已经知道了,通过对数的关系来调节PWM的占空比,呼吸灯的效果最佳;具体怎么操作呢?
在下面的示例程序中,我们将PWM等分成1000份(0~999),在这1000份当中,我们设定有100个亮度级别,那最亮的那个级别100和PWM的关系式为100 = x*log10(999),x为一个固定的常数,是需要我们求出来的:x = 100 / log10(99);在计算出这个x之后,我们再将100这个等级替换成0~99,结合刚刚的常数x,求出log10(n)这个数值,再通过反LOG的计算方式求出n的数值:如果 m = log10(n) ,那么则有 n = pow(10, m);这个n就是我们应该设定的PWM占空比值了;具体的函数计算过程如下所示: void LED_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
RCC_APB1PeriphClockCmd(RCC_APB1ENR_TIM2, ENABLE);
TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);
TIM_TimeBaseStructure.TIM_Prescaler = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseStructure.TIM_Period = 1000 - 1;
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
TIM_OCStructInit(&TIM_OCInitStructure);
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = 500 - 1;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Reset;
TIM_OC4Init(TIM2, &TIM_OCInitStructure);
TIM_OC4PreloadConfig(TIM2, TIM_OCPreload_Enable);
TIM_ARRPreloadConfig(TIM2, ENABLE);
TIM_Cmd(TIM2, ENABLE);
TIM_CtrlPWMOutputs(TIM2, ENABLE);
RCC_AHBPeriphClockCmd(RCC_AHBENR_GPIOA, ENABLE);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource3, GPIO_AF_2); /* TIM2_CH4 */
GPIO_StructInit(&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
TASK_Append(TASK_ID_RYG, LED_Handler, 50);
}
double LED_GetLOG(double level, double max)
{
double a = log10(999);
printf("\r\n%d(a) = %f", (uint32_t)level, a);
double b = level / (max / a);
printf("\r\n%d(b) = %f", (uint32_t)level, b);
double c = pow(10, b);
printf("\r\n%d(c) = %f", (uint32_t)level, c);
return c;
}
void LED_Handler(void)
{
static double Level = 1.0, MAX = 50.0;
static uint8_t State = 0;
if(State == 0)
{
if(Level >= MAX)
{
Level = MAX;
State = 1;
}
else
{
Level++;
}
}
else
{
if(Level <= 1)
{
Level = 1;
State = 0;
}
else
{
Level--;
}
}
TIM_SetCompare4(TIM2, (uint32_t)LED_GetLOG(Level, MAX));
}
对数方式实现呼吸灯效果效果
|