返回列表 发新帖我要提问本帖赏金: 50.00元(功能说明)

[MM32生态] 【MM32+模块】系列:02、LED灯控制

[复制链接]
8014|6
 楼主| xld0932 发表于 2022-3-25 18:28 | 显示全部楼层 |阅读模式
本帖最后由 xld0932 于 2022-3-25 19:00 编辑

#申请原创#   @21小跑堂

在我的印象当中LED灯控制是接触MCU时的第一个外设实验了,因为其本身只需要通过GPIO端口输出高低电平就可以实现了,常规的原理图画法有很多,有VCCLED灯再串一个电阻连接到MCU的引脚上的,也有MCU引脚接LED灯再串一个电阻连接到GND上的,当然还有通过三极管等等电路来驱动的……

LED控制.png

在如上原理图中,串联的1k电阻是用于限制电流大小的;对于方式1MCU_PIN输出高电平时,LED处于熄灭的状态,输出低电平时,LED则处于点亮的状态;对于方式2中的MCU_PIN输出高电平时,LED处于点亮的状态,输出低电平时,LED则处于熄灭的状态;对于其它的方式,则需要根据实际电路设计来进行高低电平的输出控制了。

在淘宝上买了一个LED模块,带有红黄绿3LED灯;结合MM32F0140的核心板,我们今天实现LED的几个实验:
1LED闪烁实验
2LED流水灯实验
3PWM方式调节LED灯显示亮度
4、对数方式实现呼吸灯
LED模块.png

LED闪烁实验
LED灯闪烁就是通过时间间隔来控制LED灯点亮或者熄灭的操作,达到闪烁的效果,具体的初始化及功能实现代码如下所示:
  1. void LED_Init(void)
  2. {
  3.     GPIO_InitTypeDef GPIO_InitStructure;

  4.     RCC_AHBPeriphClockCmd(RCC_AHBENR_GPIOA, ENABLE);

  5.     GPIO_StructInit(&GPIO_InitStructure);
  6.     GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5;
  7.     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  8.     GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_Out_PP;
  9.     GPIO_Init(GPIOA, &GPIO_InitStructure);

  10.     GPIO_WriteBit(GPIOA, GPIO_Pin_3, Bit_RESET);
  11.     GPIO_WriteBit(GPIOA, GPIO_Pin_4, Bit_RESET);
  12.     GPIO_WriteBit(GPIOA, GPIO_Pin_5, Bit_RESET);

  13.     TASK_Append(TASK_ID_RYG, LED_Handler,  250);
  14. }


  15. void LED_Handler(void)
  16. {
  17.     bool PA3 = GPIO_ReadOutputDataBit(GPIOA, GPIO_Pin_3);
  18.     bool PA4 = GPIO_ReadOutputDataBit(GPIOA, GPIO_Pin_4);
  19.     bool PA5 = GPIO_ReadOutputDataBit(GPIOA, GPIO_Pin_5);

  20.     if(!PA3) GPIO_WriteBit(GPIOA, GPIO_Pin_3, Bit_SET);
  21.     else     GPIO_WriteBit(GPIOA, GPIO_Pin_3, Bit_RESET);

  22.     if(!PA4) GPIO_WriteBit(GPIOA, GPIO_Pin_4, Bit_SET);
  23.     else     GPIO_WriteBit(GPIOA, GPIO_Pin_4, Bit_RESET);

  24.     if(!PA5) GPIO_WriteBit(GPIOA, GPIO_Pin_5, Bit_SET);
  25.     else     GPIO_WriteBit(GPIOA, GPIO_Pin_5, Bit_RESET);
  26. }

LED闪烁实验效果
0325_1.gif

LED流水灯实验
LED灯模块上有3个灯,我们按照固定的方向依次点亮LED灯,不停的循环反复达到流水灯的效果,具体的初始化及功能实现代码如下所示:
  1. void LED_Init(void)
  2. {
  3.     GPIO_InitTypeDef GPIO_InitStructure;

  4.     RCC_AHBPeriphClockCmd(RCC_AHBENR_GPIOA, ENABLE);

  5.     GPIO_StructInit(&GPIO_InitStructure);
  6.     GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5;
  7.     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  8.     GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_Out_PP;
  9.     GPIO_Init(GPIOA, &GPIO_InitStructure);

  10.     GPIO_WriteBit(GPIOA, GPIO_Pin_3, Bit_RESET);
  11.     GPIO_WriteBit(GPIOA, GPIO_Pin_4, Bit_RESET);
  12.     GPIO_WriteBit(GPIOA, GPIO_Pin_5, Bit_RESET);

  13.     TASK_Append(TASK_ID_RYG, LED_Handler,  250);
  14. }

  15. void LED_Handler(void)
  16. {
  17.     static uint8_t Index = 0;

  18.     switch(Index)
  19.     {
  20.         case 0:
  21.             GPIO_WriteBit(GPIOA, GPIO_Pin_3, Bit_RESET);
  22.             GPIO_WriteBit(GPIOA, GPIO_Pin_4, Bit_RESET);
  23.             GPIO_WriteBit(GPIOA, GPIO_Pin_5, Bit_RESET);
  24.             break;

  25.         case 1:
  26.             GPIO_WriteBit(GPIOA, GPIO_Pin_3, Bit_SET);
  27.             GPIO_WriteBit(GPIOA, GPIO_Pin_4, Bit_RESET);
  28.             GPIO_WriteBit(GPIOA, GPIO_Pin_5, Bit_RESET);
  29.             break;

  30.         case 2:
  31.             GPIO_WriteBit(GPIOA, GPIO_Pin_3, Bit_RESET);
  32.             GPIO_WriteBit(GPIOA, GPIO_Pin_4, Bit_SET);
  33.             GPIO_WriteBit(GPIOA, GPIO_Pin_5, Bit_RESET);
  34.             break;

  35.         case 3:
  36.             GPIO_WriteBit(GPIOA, GPIO_Pin_3, Bit_RESET);
  37.             GPIO_WriteBit(GPIOA, GPIO_Pin_4, Bit_RESET);
  38.             GPIO_WriteBit(GPIOA, GPIO_Pin_5, Bit_SET);
  39.             break;
  40.     }

  41.     Index = (Index + 1) % 4;
  42. }

LED流水灯实验效果
0325_2.gif

PWM方式调节LED灯显示亮度
上面我们是通过GPIO端口引脚直接输出高或者低电平来驱动LED灯点亮或熄灭的,在GPIO输出电平后一直维持着输出电平的状态,我们可以理解为这个时候输出占空比是100%或者0%LED灯一直处于最亮或者熄灭的状态;而PWM方式则是通过固定的一个频率,通过修改其占空比(高电平与低电平在一个周期内占用时间的比例)可以来控制LED灯的显示亮度,可以直观的理解之前是100%供电的,LED灯则是最亮的,PWM方式是断断续续的供电,这个时候LED灯显示就弱了,亮弱则是由PWM占空比来决定的。具体的初始化及功能实现代码如下所示:
  1. void LED_Init(void)
  2. {
  3.     GPIO_InitTypeDef        GPIO_InitStructure;
  4.     TIM_OCInitTypeDef       TIM_OCInitStructure;
  5.     TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;

  6.     RCC_APB1PeriphClockCmd(RCC_APB1ENR_TIM2, ENABLE);

  7.     TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);
  8.     TIM_TimeBaseStructure.TIM_Prescaler         = 0;
  9.     TIM_TimeBaseStructure.TIM_CounterMode       = TIM_CounterMode_Up;
  10.     TIM_TimeBaseStructure.TIM_Period            = 1000 - 1;
  11.     TIM_TimeBaseStructure.TIM_ClockDivision     = TIM_CKD_DIV1;
  12.     TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;
  13.     TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);

  14.     TIM_OCStructInit(&TIM_OCInitStructure);
  15.     TIM_OCInitStructure.TIM_OCMode          = TIM_OCMode_PWM1;
  16.     TIM_OCInitStructure.TIM_OutputState     = TIM_OutputState_Enable;
  17.     TIM_OCInitStructure.TIM_Pulse           = 500 - 1;
  18.     TIM_OCInitStructure.TIM_OCPolarity      = TIM_OCPolarity_High;
  19.     TIM_OCInitStructure.TIM_OCIdleState     = TIM_OCIdleState_Reset;

  20.     TIM_OC4Init(TIM2, &TIM_OCInitStructure);
  21.     TIM_OC4PreloadConfig(TIM2, TIM_OCPreload_Enable);

  22.     TIM_ARRPreloadConfig(TIM2, ENABLE);
  23.     TIM_Cmd(TIM2, ENABLE);

  24.     TIM_CtrlPWMOutputs(TIM2, ENABLE);

  25.     RCC_AHBPeriphClockCmd(RCC_AHBENR_GPIOA, ENABLE);

  26.     GPIO_PinAFConfig(GPIOA, GPIO_PinSource3, GPIO_AF_2);    /* TIM2_CH4  */

  27.     GPIO_StructInit(&GPIO_InitStructure);
  28.     GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_3;
  29.     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  30.     GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF_PP;
  31.     GPIO_Init(GPIOA, &GPIO_InitStructure);

  32.     TASK_Append(TASK_ID_RYG, LED_Handler, 10);
  33. }


  34. void LED_Handler(void)
  35. {
  36.     static uint16_t Value = 0, State = 0;

  37.     TIM_SetCompare4(TIM2, Value);

  38.     if(State == 0)
  39.     {
  40.         Value += 10;

  41.         if(Value >= 1000)
  42.         {
  43.             Value = 990;
  44.             State = 1;
  45.         }
  46.     }
  47.     else
  48.     {
  49.         if(Value > 10)
  50.         {
  51.             Value -= 10;
  52.         }
  53.         else
  54.         {
  55.             Value = 0;
  56.             State = 0;
  57.         }
  58.     }
  59. }

PWM方式调节LED灯显示亮度效果
0325_3.gif

对数方式实现呼吸灯
在上面通过直接修改PWM固定步长的占空比来实现的呼吸灯实验中,我们发现在占空比由小往大变化时,占空比在1%45%左右时,看到的变化最明显,但再往上变化时已经基本上看不出什么变化了,效果不是很好,甚至可以说没有达到呼吸灯应有的效果;

那么PWM占空比应该如何来控制才能够达到视觉感观上呼吸亮度的效果呢?通过尝试,或者有经验的网友已经知道了,通过对数的关系来调节PWM的占空比,呼吸灯的效果最佳;具体怎么操作呢?

在下面的示例程序中,我们将PWM等分成1000份(0~999),在这1000份当中,我们设定有100个亮度级别,那最亮的那个级别100PWM的关系式为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占空比值了;具体的函数计算过程如下所示:
  1. void LED_Init(void)
  2. {
  3.     GPIO_InitTypeDef        GPIO_InitStructure;
  4.     TIM_OCInitTypeDef       TIM_OCInitStructure;
  5.     TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;

  6.     RCC_APB1PeriphClockCmd(RCC_APB1ENR_TIM2, ENABLE);

  7.     TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);
  8.     TIM_TimeBaseStructure.TIM_Prescaler         = 0;
  9.     TIM_TimeBaseStructure.TIM_CounterMode       = TIM_CounterMode_Up;
  10.     TIM_TimeBaseStructure.TIM_Period            = 1000 - 1;
  11.     TIM_TimeBaseStructure.TIM_ClockDivision     = TIM_CKD_DIV1;
  12.     TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;
  13.     TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);

  14.     TIM_OCStructInit(&TIM_OCInitStructure);
  15.     TIM_OCInitStructure.TIM_OCMode          = TIM_OCMode_PWM1;
  16.     TIM_OCInitStructure.TIM_OutputState     = TIM_OutputState_Enable;
  17.     TIM_OCInitStructure.TIM_Pulse           = 500 - 1;
  18.     TIM_OCInitStructure.TIM_OCPolarity      = TIM_OCPolarity_High;
  19.     TIM_OCInitStructure.TIM_OCIdleState     = TIM_OCIdleState_Reset;

  20.     TIM_OC4Init(TIM2, &TIM_OCInitStructure);
  21.     TIM_OC4PreloadConfig(TIM2, TIM_OCPreload_Enable);

  22.     TIM_ARRPreloadConfig(TIM2, ENABLE);
  23.     TIM_Cmd(TIM2, ENABLE);

  24.     TIM_CtrlPWMOutputs(TIM2, ENABLE);

  25.     RCC_AHBPeriphClockCmd(RCC_AHBENR_GPIOA, ENABLE);

  26.     GPIO_PinAFConfig(GPIOA, GPIO_PinSource3, GPIO_AF_2);    /* TIM2_CH4  */

  27.     GPIO_StructInit(&GPIO_InitStructure);
  28.     GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_3;
  29.     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  30.     GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF_PP;
  31.     GPIO_Init(GPIOA, &GPIO_InitStructure);

  32.     TASK_Append(TASK_ID_RYG, LED_Handler, 50);
  33. }

  34. double LED_GetLOG(double level, double max)
  35. {
  36.     double a = log10(999);

  37.     printf("\r\n%d(a) = %f", (uint32_t)level, a);

  38.     double b = level / (max / a);

  39.     printf("\r\n%d(b) = %f", (uint32_t)level, b);

  40.     double c = pow(10, b);

  41.     printf("\r\n%d(c) = %f", (uint32_t)level, c);

  42.     return c;
  43. }

  44. void LED_Handler(void)
  45. {
  46.     static double  Level = 1.0, MAX = 50.0;
  47.     static uint8_t State = 0;

  48.     if(State == 0)
  49.     {
  50.         if(Level >= MAX)
  51.         {
  52.             Level = MAX;
  53.             State = 1;
  54.         }
  55.         else
  56.         {
  57.             Level++;
  58.         }
  59.     }
  60.     else
  61.     {
  62.         if(Level <= 1)
  63.         {
  64.             Level = 1;
  65.             State = 0;
  66.         }
  67.         else
  68.         {
  69.             Level--;
  70.         }
  71.     }

  72.     TIM_SetCompare4(TIM2, (uint32_t)LED_GetLOG(Level, MAX));
  73. }

对数方式实现呼吸灯效果效果
0325_4.gif


打赏榜单

21小跑堂 打赏了 50.00 元 2022-03-29
理由:恭喜通过原创文章审核!请多多加油哦!

 楼主| xld0932 发表于 2022-4-4 10:02 | 显示全部楼层
对于实现LED呼吸灯效果,对数的方式效果相当不错,有兴趣的小伙伴可以尝试一下哈
foxsbig 发表于 2022-4-8 15:44 | 显示全部楼层
现在是不是有新活动了?
 楼主| xld0932 发表于 2022-4-8 16:38 | 显示全部楼层
foxsbig 发表于 2022-4-8 15:44
现在是不是有新活动了?

什么活动?
1908278102 发表于 2022-6-7 15:20 | 显示全部楼层
真好,这篇讲得很通透,好用
 楼主| xld0932 发表于 2022-6-7 16:22 | 显示全部楼层
1908278102 发表于 2022-6-7 15:20
真好,这篇讲得很通透,好用

sainuo598 发表于 2022-6-9 14:18 | 显示全部楼层
感谢分享
您需要登录后才可以回帖 登录 | 注册

本版积分规则

个人签名:King.Xu

77

主题

3023

帖子

38

粉丝
快速回复 在线客服 返回列表 返回顶部