Controlling WS2812B RGB LEDs with a WCH CH32V003 Microcontroller

Controlling WS2812B RGB LEDs with a WCH CH32V003 Microcontroller

Robby roboter @robby.roboter
Robby roboter @robby.roboter

In this blog post, we’ll explore a C program designed to control WS2812B RGB LEDs using a microcontroller. The code is structured to initialize the system, configure GPIOs, and send precise timing signals to the LEDs to display colors and animations.

Overview of the Code

The main.c file is the heart of our project, responsible for setting up the microcontroller and driving the WS2812B LEDs. Here’s a breakdown of its key components:

System Initialization

The program begins by initializing the system clock and configuring the GPIO pins. The system clock is set to 48MHz, which is crucial for achieving the precise timing required by the WS2812B protocol.

void SystemInit(void)
{
    // System initialization code
    RCC_AdjustHSICalibrationValue(0x10);
    SetSysClock();
}

GPIO Configuration

The GPIO pins are configured to output mode to control the LED data line. This setup is essential for sending the high and low signals that represent the binary data for the LEDs.

void WS2812B_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure = {0};
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD, ENABLE);
    GPIO_InitStructure.GPIO_Pin = LED_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(LED_PORT, &GPIO_InitStructure);
}

Sending Data to LEDs

The WS2812B_SendBit function is responsible for sending individual bits to the LEDs. It uses inline assembly to achieve precise timing, crucial for the WS2812B protocol.

void WS2812B_SendBit(uint8_t bit)
{
    if (bit)
    {
        // Bit "1": High for 0.66 µs, Low for 0.59 µs
        GPIOD->BSHR = LED_PIN;  // Set pin high
        __asm__ volatile (
            "li t0, 5 \n"        // Load the cycle count for high time
            "1: \n"              // Label for the loop start
            "addi t0, t0, -1 \n" // Decrement the cycle count
            "bnez t0, 1b \n"     // Branch to label 1 if the cycle count is not zero
        );

        GPIOD->BCR = LED_PIN;  // Set pin low
        __asm__ volatile (
            "li t0, 1 \n"        // Load the cycle count for low time
            "1: \n"              // Label for the loop start
            "addi t0, t0, -1 \n" // Decrement the cycle count
            "bnez t0, 1b \n"     // Branch to label 1 if the cycle count is not zero
        );
    }
    else
    {
        // Bit "0": High for 0.33 µs, Low for 0.92 µs
        GPIOD->BSHR = LED_PIN;  // Set pin high
        __asm__ volatile (
            "li t0, 4 \n"        // Load the cycle count for high time
            "1: \n"              // Label for the loop start
            "addi t0, t0, -1 \n" // Decrement the cycle count
            "bnez t0, 1b \n"     // Branch to label 1 if the cycle count is not zero
        );

        GPIOD->BCR = LED_PIN;  // Set pin low
        __asm__ volatile (
            "li t0, 1 \n"        // Load the cycle count for low time
            "1: \n"              // Label for the loop start
            "addi t0, t0, -1 \n" // Decrement the cycle count
            "bnez t0, 1b \n"     // Branch to label 1 if the cycle count is not zero
        );
    }
}

Color and Animation Functions

The program includes functions to send colors to the LEDs and create animations like rainbow and fade effects. These functions utilize the WS2812B_SendColor and WS2812B_FadeColors functions to control multiple LEDs.

void WS2812B_SendByte(uint8_t byte)
{
    for (int i = 0; i < 8; i++)
    {
        WS2812B_SendBit((byte << i) & 0x80);
    }
}

void WS2812B_SendColor(uint8_t red, uint8_t green, uint8_t blue)
{
    WS2812B_SendByte(green);
    WS2812B_SendByte(red);
    WS2812B_SendByte(blue);
}

void WS2812B_FadeColors(uint8_t num_leds) {
    uint8_t red, green, blue;
    int step = 5; // Adjust step size for smoother transitions

    // Fade from Red to Blue
    for (red = 255, blue = 0; red > 0; red -= step, blue += step)
    {
        WS2812B_SendColor(red, 0, blue);
        Delay_Ms(50); // Adjust delay for speed
    }

    // Fade from Blue to Green
    for (blue = 255, green = 0; blue > 0; blue -= step, green += step)
    {
        WS2812B_SendColor(0, green, blue);
        Delay_Ms(50);
    }

    // Fade from Green to Red
    for (green = 255, red = 0; green > 0; green -= step, red += step)
    {
        WS2812B_SendColor(red, green, 0);
        Delay_Ms(50);
    }
}

Main Function

The main function initializes the system and enters an infinite loop to continuously update the LED colors. It demonstrates basic color changes and animations.

int main(void)
{
    SystemInit();
    WS2812B_Init();
	Delay_Init();

    while (1)
    {
        WS2812B_FadeColors(NUM_LEDS);
    }
}

Conclusion

This main.c file provides a robust framework for controlling WS2812B RGB LEDs with a microcontroller. By leveraging precise timing and direct GPIO manipulation, it achieves the necessary control to create vibrant LED displays. Whether you’re building a simple LED strip or a complex light show, this code serves as a solid foundation for your projects.

Feel free to experiment with the timing and color functions to create your own unique LED animations!