Hello everyone, in this post we will control one (or more) WS2812 LEDs from a Linux UserSpace application, communicating with Cortex M33 through RPMSG.
Required materials:
Environment:
This is the block diagram of the application:
CORTEX M33 SIDE.
From WS2812 LED datasheet, we can see the timing must be precise, that is the reason we will use the Cortex M33 (Real time applications):
As we can see, the timing just accept tolerance of 150ns, that is the reason is preferred to use the Cortex M33 instead of control with Linux that is in charge of other tasks.
The function that we will implement to send Zero and One is:
void WS2812B_send_bit(uint8_t bit)
{
if (bit)
{
// Send "1"
RGPIO_PinWrite(WS2812B_LED_RGPIO, WS2812B_LED_RGPIO_PIN, 1);
for (int i = 0; i < HIGH_ONE_CYCLES; i++) __asm__("NOP");
RGPIO_PinWrite(WS2812B_LED_RGPIO, WS2812B_LED_RGPIO_PIN, 0);
for (int i = 0; i < LOW_ONE_CYCLES; i++) __asm__("NOP");
}
else
{
// Send "0"
RGPIO_PinWrite(WS2812B_LED_RGPIO, WS2812B_LED_RGPIO_PIN, 1);
for (int i = 0; i < HIGH_ZERO_CYCLES; i++) __asm__("NOP");
RGPIO_PinWrite(WS2812B_LED_RGPIO, WS2812B_LED_RGPIO_PIN, 0);
for (int i = 0; i < LOW_ZERO_CYCLES; i++) __asm__("NOP");
}
}
Testing and measuring the toggle timing with the RGPIO driver of Cortex M33, we could find the best number of NOP instructions to adjust with the required timing according to datasheet, and the results with core running at 200MHz are:
Parameter |
Number of NOP times |
HIGH_ONE_CYCLES |
22 (T1H: 700 ns) |
LOW_ONE_CYCLES |
12 (T1L: 600 ns) |
LOW_ZERO_CYCLES |
20 (T0L: 800 ns) |
HIGH_ZERO_CYCLES |
6 (T0H: 350 ns) |
Zero:
One:
Taking in mind this information, we can start to develop our application. From Cortex M33, we created a little protocol, that will wait for instructions to do.
For example, the expected string to fill the RPMSG buffer is:
Index |
Purpose |
Example |
app_buf[0] |
Command ('c' or 't') |
'c' = store color in buffer |
app_buf[1] |
Red component (0–255) |
0xFF |
app_buf[2] |
Green component (0–255) |
0x00 |
app_buf[3] |
Blue component (0–255) |
0x80 |
app_buf[4] |
LED index (target LED position) |
e.g. 0, 1, 2 |
Commands:
'c' "Color store"
This command stores the RGB color value into a local buffer, targeting a specific LED (app_buf[4]).
It doesn't update the LED strip immediately, it just prepares the data.
Example of RPMSG received buffer from Cortex A (Linux):
app_buf = { 'c', 0xFF, 0x00, 0x00, 2 }; // Store red color for LED 2
't' "Transfer"
This command sends the full color buffer to the WS2812 strip, updating the LED colors.
The RGB values in the buffer are ignored, it simply triggers an update based on previously stored data.
Example of RPMSG received buffer from Cortex A (Linux):
app_buf = { 't', 0, 0, 0, 0 }; // Transfer the buffered colors to the LED strip
You can modify the code to add your custom commands like change number of LEDs on the strip, or change the GPIO, etc. Currently, the number of WS2812 LEDs supported is defined in software by NUM_RGB_LEDS. As default is 8 LEDs.
I will attach the full code for Cortex M33 firmware, it was tested with arm-gnu-toolchain-13.3.rel1-x86_64-arm-none-eabi and SDK 2_15, SDK 2_16 and SDK 24.
In this example, the EXP_GPIO_IO24 of FRDM-IMX93 was used to control the LED Strip.
CORTEX A55 SIDE.
For Cortex A55 (Linux side), we developed an userspace application, that will create the structure for the buffer to send to the Cortex M33 and send through RPMSG using the imx_rpmsg_tty included on the NXP BSP. You can find the userspace application attached.
As we know, the imx_rpmsg_tty creates the tty channel called ttyRPMSG31 under /dev. So, this userspace application communicates directly with the device /dev/ttyRPMSG31 to send the buffer with the required structure for Cortex M33.
EXAMPLE OF USAGE.
STEP 1.
Compile firmware for Cortex M33 and store in FRDM-IMX93 under /lib/firmware to use with remoteproc:
STEP 2.
Compile or cross-compile the userspace application (attached.)
To compile on the platform:
root@imx93evk:~# gcc wsled.c -o wsled
root@imx93evk:~# ls
wsled wsled.c
Also, we can copy the wsled under /bin to practicity.
root@imx93evk:~# wsled
Error: -c <red> <green> <blue> is required.
WS2812
Usage:
wsled -c <red> <green> <blue> [-n <led_position>] Load the color of exact LED to the buffer
wsled -t Shows the colors to the LEDs
STEP 3.
To run the entire example:
boot the board and load the firmware for cortex M33 with remoteproc:
root@imx93evk:~# cd /sys/class/remoteproc/remoteproc0
root@imx93evk:/sys/class/remoteproc/remoteproc0# echo ws2812_m33.elf > firmware
root@imx93evk:/sys/class/remoteproc/remoteproc0# echo start > state
Then, we will receive this from Cortex M33 terminal:
RPMSG String Communication Channel FreeRTOS RTOS
M33 clock is at: 200000000 Hz
Nameservice sent, ready for incoming messages from Cortex-A55! WSLED
Load the imx_rpmsg_tty module in Linux to initialize RPMSG:
root@imx93evk:~# modprobe imx_rpmsg_tty
Then, we will have the hello world message from Cortex A (Linux side), but with our buffer format in Cortex M33 console:
CMD: h [1]:0x65 [2]:0x6c [3]:0x6c [4]: 111 LEN = 12
Finally, we are ready change colors of the LEDs:
Load the buffer (LED 1 = RED, LED 2 = Green, LED 3 = Blue).
root@imx93evk:~# wsled -c 0x55 0x00 0x00 -n 1
root@imx93evk:~# wsled -c 0x00 0x55 0x00 -n 2
root@imx93evk:~# wsled -c 0x00 0x00 0x55 -n 3
Show colors on LEDs.
root@imx93evk:~# wsled -t