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

 在我的印象当中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的几个实验:

1LED闪烁实验

2LED流水灯实验

3PWM方式调节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));
}

对数方式实现呼吸灯效果效果

---------------------
作者:xld0932
链接:https://bbs.21ic.com/icview-3208044-1-1.html
来源:21ic.com
此文章已获得原创/原创奖标签,著作权归21ic所有,任何人未经允许禁止转载。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值