发新帖本帖赏金 100.00元(功能说明)我要提问
12下一页
返回列表
打印
[MM32生态]

【MM32+模块】系列:03、RGB灯控制

[复制链接]
1702|24
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
本帖最后由 xld0932 于 2022-3-29 19:55 编辑

#申请原创#   @21小跑堂

在上一篇中我们介绍了LED灯,它是一个单色的灯,比如红色、蓝色、黄色、或者绿色等等;而本篇中的RGB灯是一个彩色灯,一个RGB灯包含了红、绿、蓝这三元色,可以通过对红绿蓝亮度的比例调节来实现显示彩色的效果。

在淘宝上买了两款RGB灯:一种是RGB灯自身不带有驱动,而是通过调节RGB三个引脚的供电电压/电流来实现彩色显示的模块;另外一种则是RGB灯本身自带有驱动部分,只需要根据内置驱动输入相应的时序和数据即可驱动显示彩色效果的模块;分别如下图所示:

对于无内置驱动的RGB灯模块,我们可以通过像LED灯的控制方式来控制某一颜色的灯单独点亮,也可以通过单个PWM结合对数的控制方式,实现彩色呼吸灯的效果;当然如果要显示混合的炫彩效果,就需要3PWM分别来控制RGB这三个通道,但不管如何控制,结合环境光和实际的呈现效果,都不太满意,所以一般这种RGB灯要么是结合专业的驱动芯片一起工作,要么仅仅是当作三色灯来使用。示例程序如下所示:
void RGB_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;

    RCC_AHBPeriphClockCmd(RCC_AHBENR_GPIOB, 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(GPIOB, &GPIO_InitStructure);

    GPIO_WriteBit(GPIOB, GPIO_Pin_3, Bit_RESET);
    GPIO_WriteBit(GPIOB, GPIO_Pin_4, Bit_RESET);
    GPIO_WriteBit(GPIOB, GPIO_Pin_5, Bit_RESET);

    TASK_Append(TASK_ID_RGB, RGB_Handler, 250);
}

void RGB_Handler(void)
{
    static uint8_t Index = 0;

    switch(Index)
    {
        case 0:
            GPIO_WriteBit(GPIOB, GPIO_Pin_3, Bit_RESET);
            GPIO_WriteBit(GPIOB, GPIO_Pin_4, Bit_RESET);
            GPIO_WriteBit(GPIOB, GPIO_Pin_5, Bit_RESET);
            break;

        case 1:
            GPIO_WriteBit(GPIOB, GPIO_Pin_3, Bit_SET);
            GPIO_WriteBit(GPIOB, GPIO_Pin_4, Bit_RESET);
            GPIO_WriteBit(GPIOB, GPIO_Pin_5, Bit_RESET);
            break;

        case 2:
            GPIO_WriteBit(GPIOB, GPIO_Pin_3, Bit_RESET);
            GPIO_WriteBit(GPIOB, GPIO_Pin_4, Bit_SET);
            GPIO_WriteBit(GPIOB, GPIO_Pin_5, Bit_RESET);
            break;

        case 3:
            GPIO_WriteBit(GPIOB, GPIO_Pin_3, Bit_RESET);
            GPIO_WriteBit(GPIOB, GPIO_Pin_4, Bit_RESET);
            GPIO_WriteBit(GPIOB, GPIO_Pin_5, Bit_SET);
            break;

        default:
            break;
    }

    Index = (Index + 1) % 4;
}
void RGB_Init(void)
{
    GPIO_InitTypeDef        GPIO_InitStructure;
    TIM_OCInitTypeDef       TIM_OCInitStructure;
    TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;

    RCC_APB2PeriphClockCmd(RCC_APB2ENR_TIM1, 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(TIM1, &TIM_TimeBaseStructure);

    TIM_OCStructInit(&TIM_OCInitStructure);
    TIM_OCInitStructure.TIM_OCMode          = TIM_OCMode_PWM1;
    TIM_OCInitStructure.TIM_OutputState     = TIM_OutputState_Enable;
    TIM_OCInitStructure.TIM_Pulse           = 0;
    TIM_OCInitStructure.TIM_OCPolarity      = TIM_OCPolarity_High;
    TIM_OCInitStructure.TIM_OCIdleState     = TIM_OCIdleState_Set;

    TIM_OC1Init(TIM1, &TIM_OCInitStructure);
    TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable);

    TIM_OC2Init(TIM1, &TIM_OCInitStructure);
    TIM_OC2PreloadConfig(TIM1, TIM_OCPreload_Enable);

    TIM_OC3Init(TIM1, &TIM_OCInitStructure);
    TIM_OC3PreloadConfig(TIM1, TIM_OCPreload_Enable);

    TIM_ARRPreloadConfig(TIM1, ENABLE);
    TIM_Cmd(TIM1, ENABLE);

    TIM_CtrlPWMOutputs(TIM1, ENABLE);

    RCC_AHBPeriphClockCmd(RCC_AHBENR_GPIOB, ENABLE);

    GPIO_PinAFConfig(GPIOB, GPIO_PinSource3, GPIO_AF_6);    /* TIM1_CH1 */
    GPIO_PinAFConfig(GPIOB, GPIO_PinSource4, GPIO_AF_6);    /* TIM1_CH2 */
    GPIO_PinAFConfig(GPIOB, GPIO_PinSource5, GPIO_AF_6);    /* TIM1_CH3 */

    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_AF_PP;
    GPIO_Init(GPIOB, &GPIO_InitStructure);

    TASK_Append(TASK_ID_RGB, RGB_Handler, 25);
}

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 RGB_Handler(void)
{
    static uint16_t Level = 1, Index = 0, MAX = 100.0;
    static uint8_t  State = 0;

    switch(Index)
    {
        case 0 : TIM_SetCompare1(TIM1, (uint32_t)LED_GetLOG(Level, MAX)); break;
        case 1 : TIM_SetCompare2(TIM1, (uint32_t)LED_GetLOG(Level, MAX)); break;
        case 2 : TIM_SetCompare3(TIM1, (uint32_t)LED_GetLOG(Level, MAX)); break;
        default: break;
    }

    if(State == 0)
    {
        if(Level >= MAX)
        {
            Level = MAX;
            State = 1;
        }
        else
        {
            Level++;
        }
    }
    else
    {
        if(Level <= 1)
        {
            Level = 1;
            State = 0;

            Index = (Index + 1) % 3;
        }
        else
        {
            Level--;
        }
    }
}

而对于内置驱动的RGB灯,一般常见的就是WS2812B,由于内置了驱动芯片,显示效果比直接通过PWM控制的效果好太多,能够完全显示出五彩缤纷的颜色;由于驱动芯片是可以级连的,这样就可以通过一根通讯控制线来实现对一串RGB灯珠或者一组灯珠的控制了,既节省了MCU资源,也减少了布线成本。本文主要就是通过MM32F0140芯片不同资源来实现对WS2812B RGB灯的控制及显示:
1、通过GPIO控制WS2812B RGB
2、通过PWM控制WS2812B RGB灯,及详述实现原理
3、通过SPI控制WS2812B RGB灯,及详述实现原理

我们使用的WS2812B驱动RGB灯珠是3.5V~5.5V供电的,正好我们MM32F0140芯片正常工作电压在2.0~5.5V之间都可以,但由于一个灯珠的R/G/B驱动电流就有12mA,我们接的RGB灯珠也比较多,这样整体电流功耗就会比较大,此时我们就需要外接一个5V的电源适配器来给整个系统供电了。

WS2812B RGB灯珠参数

WS2812B RGB连接方式

WS2812B RGB通讯协议

通过GPIO模拟时序控制RGB
结合WS2812B RGB通讯协议,我们使用GPIO来模拟数据的发送时序,是最简易的实现方式了;但它的缺点就是对芯片的依赖性太强,不兼具程序的移植性和可修改性,在前期调试驱动时序也需要大费功夫;但硬件平台一但确定下来,通过这种方式,也是一次性的投入,大多数也可以理解为51ARM通用的实现方式了。具体的实现代码如下所示(系统运行在72MHz的时钟频率):
void RGB_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;

    RCC_AHBPeriphClockCmd(RCC_AHBENR_GPIOB, ENABLE);

    GPIO_StructInit(&GPIO_InitStructure);
    GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_13;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_Out_PP;
    GPIO_Init(GPIOB, &GPIO_InitStructure);

    TASK_Append(TASK_ID_RGB, RGB_Handler, 500);
}

void WS2812B_Write0(void)
{
    GPIOB->BSRR = GPIO_Pin_13;  /* T0H 330ns */
    __nop(); __nop(); __nop(); __nop(); __nop(); __nop(); __nop(); __nop();

    GPIOB->BRR  = GPIO_Pin_13;  /* T0L 920ns */
    __nop(); __nop(); __nop(); __nop(); __nop(); __nop(); __nop(); __nop();
    __nop(); __nop(); __nop(); __nop(); __nop(); __nop(); __nop(); __nop();
    __nop(); __nop(); __nop(); __nop(); __nop(); __nop(); __nop(); __nop();
}

void WS2812B_Write1(void)
{
    GPIOB->BSRR = GPIO_Pin_13;  /* T1H 670ns */
    __nop(); __nop(); __nop(); __nop(); __nop(); __nop(); __nop(); __nop();
    __nop(); __nop(); __nop(); __nop(); __nop(); __nop(); __nop(); __nop();
    __nop(); __nop(); __nop(); __nop(); __nop(); __nop(); __nop(); __nop();

    GPIOB->BRR  = GPIO_Pin_13;  /* T1L 580ns */
    __nop(); __nop(); __nop(); __nop(); __nop(); __nop(); __nop(); __nop();
}

void WS2812B_SendData(uint8_t Data)
{
    for(uint8_t i = 0; i < 8; i++)
    {
        if(Data & (0x80 >> i))
        {
            WS2812B_Write1();
        }
        else
        {
            WS2812B_Write0();
        }
    }
}

void WS2812B_SendColor(uint8_t R, uint8_t G, uint8_t B)
{
    WS2812B_SendData(G);
    WS2812B_SendData(R);
    WS2812B_SendData(B);
}

通过TIMER PWM来控制RGB
结合WS2812B RGB通讯协议,我们看到不管是0码还是1码,一个码元的周期典型值是1.25us,唯一不同的就是0码和1码在这个周期内高低电平的占比不同;这样结合我们定时器的PWM比较输出功能,再结合DMA方式,就可以实现一个RGB数据序列了;在交流参数中指定了数据传输速度典型为800kHz,也就是对应了码元的1.25us的周期,这样我们在初始化定时器的时候,就可以直接将PWM输出频率固定在800kHz,然后通过控制CCR的值结合DMA来调整输出的占空比,实现0码和1码的时序。

我们示例程序中使用的是TIM2,它挂载在APB1总线上,根据系统初始化后APB1总线的时钟频率为72MHz,那需要800kHz的码元周期,则需要将定时器90分频,所以程序中设置的是890~8990个数,代表了90分频);而对于0码在码元周期中高电平时间为0.33us,所对应的CCR值通过计算约为240.33 * 90 / 1.25),1码在码元周期中高电平时间为0.66us,所应对的CCR值通过计算约为480.66 * 90 / 1.25),有了这些数据,我们就可以进行程序化和实现对WS2812B RGB灯珠的控制啦,示例程序如下:
uint32_t RGB_Buffer[100];

void RGB_Init(void)
{
    GPIO_InitTypeDef        GPIO_InitStructure;
    DMA_InitTypeDef         DMA_InitStructure;
    TIM_OCInitTypeDef       TIM_OCInitStructure;
    TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;

    memset(RGB_Buffer, 0, sizeof(RGB_Buffer));

    RCC_AHBPeriphClockCmd(RCC_AHBENR_DMA1, ENABLE);

    DMA_StructInit(&DMA_InitStructure);
    DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&TIM2->CCR1;
    DMA_InitStructure.DMA_MemoryBaseAddr     = (uint32_t)RGB_Buffer;
    DMA_InitStructure.DMA_DIR                = DMA_DIR_PeripheralDST;  
    DMA_InitStructure.DMA_BufferSize         = 24;
    DMA_InitStructure.DMA_PeripheralInc      = DMA_PeripheralInc_Disable;
    DMA_InitStructure.DMA_MemoryInc          = DMA_MemoryInc_Enable;
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;
    DMA_InitStructure.DMA_MemoryDataSize     = DMA_MemoryDataSize_Word;
    DMA_InitStructure.DMA_Mode               = DMA_Mode_Circular;
    DMA_InitStructure.DMA_Priority           = DMA_Priority_High;
    DMA_InitStructure.DMA_M2M                = DMA_M2M_Disable;
    DMA_Init(DMA1_Channel5, &DMA_InitStructure);

    DMA_Cmd(DMA1_Channel5, DISABLE);

    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            = 89;
    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        = 0;
    TIM_OCInitStructure.TIM_OCPolarity   = TIM_OCPolarity_High;
    TIM_OCInitStructure.TIM_OCIdleState  = TIM_OCIdleState_Set;

    TIM_OC1Init(TIM2, &TIM_OCInitStructure);
    TIM_OC1PreloadConfig(TIM2, TIM_OCPreload_Enable);

    TIM_ARRPreloadConfig(TIM2, DISABLE);

    TIM_DMACmd(TIM2, TIM_DMA_CC1, ENABLE);
    TIM_Cmd(TIM2, DISABLE);

    TIM_CtrlPWMOutputs(TIM2, ENABLE);

    RCC_AHBPeriphClockCmd(RCC_AHBENR_GPIOB, ENABLE);

    GPIO_PinAFConfig(GPIOB, GPIO_PinSource13, GPIO_AF_7);

    GPIO_StructInit(&GPIO_InitStructure);
    GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_13;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF_PP;
    GPIO_Init(GPIOB, &GPIO_InitStructure);

    TASK_Append(TASK_ID_RGB, RGB_Handler, 500);
}

void WS2812B_SendData(uint8_t Data, uint8_t Offset)
{
    for(uint8_t i = 0; i < 8; i++)
    {
        if(Data & (0x80 >> i))
        {
            RGB_Buffer[i + Offset] = 48;
        }
        else
        {
            RGB_Buffer[i + Offset] = 24;
        }
    }
}

void WS2812B_SendColor(uint8_t R, uint8_t G, uint8_t B)
{
    WS2812B_SendData(G, 0x00);
    WS2812B_SendData(R, 0x08);
    WS2812B_SendData(B, 0x10);

    DMA_Cmd(DMA1_Channel5, ENABLE);
    TIM_Cmd(TIM2, ENABLE);

    while(!DMA_GetFlagStatus(DMA1_FLAG_TC5));
    DMA_ClearFlag(DMA1_FLAG_TC5);

    TIM_Cmd(TIM2, DISABLE);
    DMA_Cmd(DMA1_Channel5, DISABLE);
}

通过SPIMOSI来控制RGB
除了上面的那种实现方式之外,我们还可以结合SPIMOSI这个输出引脚来达到对WS2812B RGB灯珠时序的控制,我们都知道SPI的数据发送的每一个比特位都是按照SPI SCK这个时钟频率进行了,我们MM32F0140这颗MCUSPI支持1~32位的用户自定义长度位进行数据发送,正好我们就结合了这一功能;

示例程序中使用的是SPI2MOSI引脚,SPI2也是工作在APB1总线上;在程序中,我们将SPI总线频率进行了16分频率,即SPI SCK的时候频率为2.25MHz,传输1位数据的时间约为0.45us,每3个位为一组,那一组位时间就为1.33us,那正好83BIT的数据组成了R/G/B这样的数据序列。在0码中要求了高电平的时间最大不超过0.47us,此时0码用3BIT二进制表示就是100b;而1码中要求了高电平的时间最大不超过1us,此时1码用3BIT二进制表示就是110b;通过80码或者1码,就组成了一个颜色的数据,发送324位的SPI数据,就可以驱动一个灯珠显示颜色了。示例程序如下所示:
void RGB_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    SPI_InitTypeDef  SPI_InitStructure;

    RCC_APB1PeriphClockCmd(RCC_APB1ENR_SPI2, ENABLE);

    SPI_StructInit(&SPI_InitStructure);
    SPI_InitStructure.SPI_Mode              = SPI_Mode_Master;
    SPI_InitStructure.SPI_DataSize          = SPI_DataSize_8b;
    SPI_InitStructure.SPI_DataWidth         = 24;
    SPI_InitStructure.SPI_CPOL              = SPI_CPOL_Low;
    SPI_InitStructure.SPI_CPHA              = SPI_CPHA_1Edge;
    SPI_InitStructure.SPI_NSS               = SPI_NSS_Soft;
    SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_16;
    SPI_InitStructure.SPI_FirstBit          = SPI_FirstBit_MSB;
    SPI_Init(SPI2, &SPI_InitStructure);

    SPI_Cmd(SPI2, ENABLE);

    RCC_AHBPeriphClockCmd(RCC_AHBENR_GPIOB, ENABLE);

    GPIO_PinAFConfig(GPIOB, GPIO_PinSource13, GPIO_AF_4);

    GPIO_StructInit(&GPIO_InitStructure);
    GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_13;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF_PP;
    GPIO_Init(GPIOB, &GPIO_InitStructure);

    SPI_BiDirectionalLineConfig(SPI2, SPI_Direction_Tx);

    TASK_Append(TASK_ID_RGB, RGB_Handler, 250);
}

void WS2812B_SendData(uint8_t Data)
{
    uint32_t Value = 0;

    for(uint8_t i = 0; i < 8; i++)
    {
        Value <<= 3;

        if((Data >> i) & 0x01)
        {
            Value |= 0x6;
        }
        else
        {
            Value |= 0x4;
        }
    }

    uint32_t TempH = ((Value >>  0) & 0x000000FF) << 16;
    uint32_t TempM = ((Value >>  8) & 0x000000FF) <<  8;
    uint32_t TempL = ((Value >> 16) & 0x000000FF) <<  0;

    SPI_SendData(SPI2, (TempH + TempM + TempL));

    for(uint8_t i = 0; i < 100; i++);
}

void WS2812B_SendColor(uint8_t R, uint8_t G, uint8_t B)
{
    WS2812B_SendData(G);
    WS2812B_SendData(R);
    WS2812B_SendData(B);
}

演示效果
除了整体驱动显示之外,还做了在8*8RGB矩阵中显示了数字的演示,示例程序和演示效果如下所示:
void RGB_Handler(void)
{
    static uint8_t Index = 0;

    uint8_t Color[8][3] =
    {
        {255,   0,   0},
        {255, 255,   0},
        {255,   0, 255},
        {255, 255, 255},
        {  0, 255,   0},
        {  0,   0, 255},
        {  0, 255, 255},
        {128, 128, 128},
    };

    __disable_irq();

    for(uint8_t i = 0; i < 64; i++)
    {
        WS2812B_SendColor(Color[Index][0], Color[Index][1], Color[Index][2]);
    }

    Index = (Index + 1) % 8;

    __enable_irq();
}
uint8_t NUM_FONT[10][8] =
{
    {0x3E,0x22,0x22,0x22,0x22,0x22,0x3E,0x00},
    {0x04,0x0C,0x04,0x04,0x04,0x04,0x0E,0x00},
    {0x1C,0x22,0x02,0x04,0x08,0x10,0x3E,0x00},
    {0x1C,0x22,0x02,0x04,0x02,0x22,0x1C,0x00},
    {0x04,0x0C,0x14,0x24,0x3E,0x04,0x04,0x00},
    {0x3E,0x20,0x20,0x3E,0x02,0x02,0x3E,0x00},
    {0x3E,0x20,0x20,0x3E,0x22,0x22,0x3E,0x00},
    {0x3E,0x02,0x04,0x08,0x08,0x08,0x08,0x00},
    {0x3E,0x22,0x22,0x3E,0x22,0x22,0x3E,0x00},
    {0x3E,0x22,0x22,0x3E,0x02,0x02,0x3E,0x00},
};

void RGB_DrawNumber(uint8_t Index, uint8_t Value)
{
    uint8_t Color[8][3] =
    {
        {255,   0,   0},
        {255, 255,   0},
        {255,   0, 255},
        {255, 255, 255},
        {  0, 255,   0},
        {  0,   0, 255},
        {  0, 255, 255},
    };

    for(uint8_t i = 0; i < 8; i++)
    {
        for(uint8_t j = 0; j < 8; j++)
        {
            if(NUM_FONT[Value][i] & (0x80 >> j))
            {
                WS2812B_SendColor(Color[Index][0], Color[Index][1], Color[Index][2]);
            }
            else
            {
                WS2812B_SendColor(0, 0, 0);
            }
        }
    }
}

void RGB_Handler(void)
{
    static uint8_t Index = 0, Value = 0;

    __disable_irq();

    RGB_DrawNumber(Index, Value);

    Value = (Value + 1) % 10;

    if(Value == 0)
    {
        Index = (Index + 1) % 7;
    }

    __enable_irq();
}


附件
工程源代码: 03.RGB.zip (711.92 KB)
WS2812B RGB灯珠: 5050七彩幻彩透明贴片LED灯珠幻彩.PDF (1.41 MB)


使用特权

评论回复

打赏榜单

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

沙发
xld0932|  楼主 | 2022-4-4 10:00 | 只看该作者
带有WS2812内置驱动的RGB灯,通过GPIO、TIM PWM+DMA、SPI MOSI这三种方式做了配置例程,及配置说明

使用特权

评论回复
板凳
sparrow054| | 2022-4-6 14:27 | 只看该作者
这是要出系列么
不错

使用特权

评论回复
地板
xld0932|  楼主 | 2022-4-6 15:53 | 只看该作者
sparrow054 发表于 2022-4-6 14:27
这是要出系列么
不错

是的,从一些基础的开始,把市面上一些常用的模块都做一下分享

使用特权

评论回复
5
sparrow054| | 2022-4-8 15:49 | 只看该作者
xld0932 发表于 2022-4-6 15:53
是的,从一些基础的开始,把市面上一些常用的模块都做一下分享

很伟大的事情

使用特权

评论回复
6
blust5| | 2022-4-11 17:09 | 只看该作者
不错不错 坐等楼主系列越来越完善

使用特权

评论回复
7
yangxiaor520| | 2022-4-12 08:14 | 只看该作者
我晕一个灯也要做成模块

使用特权

评论回复
8
xld0932|  楼主 | 2022-4-12 10:04 | 只看该作者
yangxiaor520 发表于 2022-4-12 08:14
我晕一个灯也要做成模块

淘宝上买的哦,现在什么样的模块都有,很多前期验证都可以用模块来做

使用特权

评论回复
9
jinyi7016| | 2022-4-12 11:35 | 只看该作者
电源怎么处理的,不支持3.3V电源吧,而且逻辑电平是0.65VDD。

使用特权

评论回复
10
xld0932|  楼主 | 2022-4-12 20:10 | 只看该作者
jinyi7016 发表于 2022-4-12 11:35
电源怎么处理的,不支持3.3V电源吧,而且逻辑电平是0.65VDD。

MCU支持2.0V~5.5V的工作电压,在这个实验中MCU使用5V的供电电压,使用一个5V/4A的电源适配器给整个系统供电,这样可以提供足够的电流支持

使用特权

评论回复
11
cyk1| | 2022-4-13 09:55 | 只看该作者
不用32位,用51可以写吗?

使用特权

评论回复
12
xld0932|  楼主 | 2022-4-13 10:17 | 只看该作者
cyk1 发表于 2022-4-13 09:55
不用32位,用51可以写吗?

51单片机只可以用GPIO的方式来驱动,一般的51单片机没有TIM PWM功能和硬件SPI功能

使用特权

评论回复
13
cyk1| | 2022-4-13 10:21 | 只看该作者
本帖最后由 cyk1 于 2022-4-13 10:22 编辑

可以指导一下,51怎么用GPIO驱动呀!谢谢大佬!

使用特权

评论回复
14
xld0932|  楼主 | 2022-4-13 10:36 | 只看该作者
cyk1 发表于 2022-4-13 10:21
可以指导一下,51怎么用GPIO驱动呀!谢谢大佬!

参考这个帖子中GPIO实现的小节,主要就是要控制时序时间,51的NOP延时和MM32的时间是不一样的,这个取决于时钟频率;这个需要实际调试下,实现代码类似;

使用特权

评论回复
15
xld0932|  楼主 | 2022-4-14 13:31 | 只看该作者
#申请原创#   @21小跑堂

正点原子曾发布了一个RGB LED模块,在这个模块上就使用了特定的驱动芯片:P9813三路恒流256级彩灯驱动芯片。使用专用的驱动芯片再来驱动RGB灯的时候,效果要比3路PWM来驱动显示效果好太多了,就不是一个级别的;

P9813使用两线串行通讯方式,包含CLK时钟线和DATA数据线;但P9813的工作电压在5V~6.5V之间,正好MM32F0140支持宽压的输入范围,在5V与之相匹配;参考芯片数据手册中的通讯协议及时序完成对RGB LED的驱动显示,如下:



配合MM32F0140核心板的驱动演示程序:
#define RGB_CLK_H() GPIO_WriteBit(GPIOB, GPIO_Pin_10, Bit_SET)
#define RGB_CLK_L() GPIO_WriteBit(GPIOB, GPIO_Pin_10, Bit_RESET)

#define RGB_DAT_H() GPIO_WriteBit(GPIOB, GPIO_Pin_11, Bit_SET)
#define RGB_DAT_L() GPIO_WriteBit(GPIOB, GPIO_Pin_11, Bit_RESET)


/*******************************************************************************
* [url=home.php?mod=space&uid=247401]@brief[/url]      
* @param      
* @retval      
* [url=home.php?mod=space&uid=93590]@Attention[/url]   
*******************************************************************************/
void RGB_DelayNOP(void)
{
    for(uint8_t i = 0; i < 200; i++)
    {
        __nop();
    }
}


/*******************************************************************************
* [url=home.php?mod=space&uid=247401]@brief[/url]      
* @param      
* @retval      
* [url=home.php?mod=space&uid=93590]@Attention[/url]   
*******************************************************************************/
void RGB_SendStartFrame(void)
{
    RGB_DAT_L();

    for(uint8_t i = 0; i < 32; i++)
    {
        RGB_CLK_L(); RGB_DelayNOP();
        RGB_CLK_H(); RGB_DelayNOP();        
    }
}


/*******************************************************************************
* @brief      
* @param      
* @retval      
* @attention   
*******************************************************************************/
uint8_t RGB_Encode(uint8_t Data)
{
    return (((~Data) & 0xC0) >> 6);
}


/*******************************************************************************
* @brief      
* @param      
* @retval      
* @attention   
*******************************************************************************/
void RGB_SendData(uint32_t Data)
{         
    for(uint8_t i = 0; i < 32; i++)
    {
        if(Data & 0x80000000) RGB_DAT_H();
        else                  RGB_DAT_L();

        Data <<= 1;

        RGB_CLK_L(); RGB_DelayNOP();
        RGB_CLK_H(); RGB_DelayNOP();        
    }        
}


/*******************************************************************************
* @brief      
* @param      
* @retval      
* @attention   
*******************************************************************************/
void RGB_Dislpay(uint8_t R, uint8_t G, uint8_t B)
{
    uint32_t Data = 0;

    Data |= (uint32_t)0x03 << 30;          /* 前两位'1'位标志位 */
    Data |= (uint32_t)RGB_Encode(B) << 28;
    Data |= (uint32_t)RGB_Encode(G) << 26;
    Data |= (uint32_t)RGB_Encode(R) << 24;
    Data |= (uint32_t)B << 0x10;
    Data |= (uint32_t)G << 0x08;
    Data |= (uint32_t)R << 0x00;

    RGB_SendStartFrame();
    RGB_SendData(Data);
    RGB_SendData(Data);
}


/*******************************************************************************
* @brief      
* @param      
* @retval      
* @attention   
*******************************************************************************/
void RGB_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;

    RCC_AHBPeriphClockCmd(RCC_AHBENR_GPIOB, ENABLE);

    GPIO_StructInit(&GPIO_InitStructure);
    GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_10 | GPIO_Pin_11;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_Out_PP;
    GPIO_Init(GPIOB, &GPIO_InitStructure);

    GPIO_WriteBit(GPIOB, GPIO_Pin_10, Bit_RESET);
    GPIO_WriteBit(GPIOB, GPIO_Pin_11, Bit_RESET);

    TASK_Append(TASK_ID_RGB, RGB_Handler, 250);
}


/*******************************************************************************
* @brief      
* @param      
* @retval      
* @attention   
*******************************************************************************/
void RGB_Handler(void)
{
    uint8_t RGB_TABLE[][3] =
    {
        {  0,   0,   0},
        {255,   0,   0},
        {  0, 255,   0},
        {  0,   0, 255},
        {255, 255,   0},
        {  0, 255, 255},
    };

    static uint8_t Index = 0;

    RGB_Dislpay(RGB_TABLE[Index][0], RGB_TABLE[Index][1], RGB_TABLE[Index][2]);

    Index = (Index + 1) % 6;
}


原理图: ATK-RGB LED-V1.2.pdf (138.75 KB)
使用手册: ATK-RGB LED模块用户手册_V1.0.pdf (446.05 KB)
数据手册: P9813_手册说明(中文版).pdf (638.32 KB)
更新的软件工程源代码: 03.RGB.zip (712.32 KB)


使用特权

评论回复
16
麻花油条| | 2022-4-26 15:13 | 只看该作者
资料挺全的,楼主厉害了

使用特权

评论回复
17
xld0932|  楼主 | 2022-4-26 18:14 | 只看该作者
麻花油条 发表于 2022-4-26 15:13
资料挺全的,楼主厉害了

使用特权

评论回复
18
chenci2013| | 2022-5-16 10:42 | 只看该作者
这个不错。  

使用特权

评论回复
19
xld0932|  楼主 | 2022-5-16 10:43 | 只看该作者

使用特权

评论回复
20
sanxingnote7| | 2022-5-16 11:28 | 只看该作者
MM32很给力呢。  

使用特权

评论回复
发新帖 本帖赏金 100.00元(功能说明)我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

认证:上海灵动微电子股份有限公司资深现场应用工程师
简介:诚信·承诺·创新·合作

67

主题

2993

帖子

29

粉丝