Explorig the CH32V203 - FreeRTOS and NEOpixels.

by CanHobby in Circuits > Arduino

39 Views, 1 Favorites, 0 Comments

Explorig the CH32V203 - FreeRTOS and NEOpixels.

CH_Expl.png

Deeper exploration of the WCH CH32V203 highlighting various peripherals.

Hardware

203step.png

In this exploration episode we will be looking at a bigger brother of the tiny RISC-V MCU we discussed in our previous project. The CH32V203 is bigger, badder and faster. It features the same peripherals as the 003 but in greater quantity as well as enough memory to support freeRTOS. Our first hardware focus will be on using SPI to drive multiple NEOpixel strips. Our software exploration will use freeRTOS to produce various animations on the strips.

As with most of my explorations I release an incomplete teaser such as this present missive and keep adding to it - so check back often for recent updates.

The board I used to develop this project is pictured above and was purchased from AliExpress. It features the WCH32V203-C8T6 which has 64K Flash and 20K SRAM.

To attach the first NEOpixel strip we need to provide a source of 5V for the strip. I normally use a breadboard mountable USB power supply if I'm only debugging with a small NEO strip. I had no problem connecting the strip DataIn line directly to the MCU board. Strip #1 connects to the MOSI pin of SPI1 which is PA7.





Chinese clones available at CanHobby and eBay.

Programming

F5XN8DUM66HJVOW.png

A ".zip" of the complete Moun River project is available on my github.

Clock Frequency - We can run at full throttle (144 MHz) and still accommodate one of my main goals which is to run multiple NEOpixel strips from this board. Using the SPI is the obvious choice but these strips need a fairly specific base frequency of approximately 2.5 MHz. This can be determined from the nominal "ON" pulse width of 400 nS. 1 over 400 nSec gives us 2.5 MHz. Fortunately this number has a wide tolerance. The CH32V203 has an internal oscillator with a base frequency of 8MHz which can be stepped up to 144 MHz maximum. Considering that there is a hidden / 2 factor in the SPI clock, and the fact we are only concerned about the single pulse width rather than the whole cycle time.

144 MHz prescaled by 64 x ( / 4 ) = 434nS

We will use freeRTOS to handle multiple NEOpixel strips as well as multiple animation tasks, and the ubiquitous Blink routine just so we know the RTOS is running. Arduino users will find that tasks are just basically like having multiple sketches seemingly running at the same time. Just like a sketch they perform a "setup" function followed by an infinite loop. This structure kind of gives us a convenient form of the encapsulation that C++ could have given us.

Thus we will see the Blink Task calling GPIO_Toggle_INIT(); to setup the GPIOs for the Leds and the NEO1 task calling Init_SPIs( SPI1_msk ); to setup the SPI used for that strip. There are also other function called in the setup portion of each task.

Arduinites will do well to note that delay() is not permitted inside of a task. We need to use vTaskDelay( d ); instead. The "d" delay factor is not mSes - it is in ticks which in this case are 10 mSec.

We can see the details by looking at my source code in Init_SPIs.c attached below. We also find the Clear() function which is handy to brute force clear the full strip before starting the task. We would be wise to remember that these strips act as serial memories. If we upload a new version of our program that does not work we may see no change on the strip due to it's retaining the output from the previous version of our program.

In the absence of C++ (which is just too unwieldy to enable in Moun River) I have a struct called strip which we need to populate for each strip and it's up to 5 associated segments.

Once we have this done we are ready to let freeRTOS unleash a task to continuously display the NEOstrip buffer which would normally contain at least 1 segment. We do this by putting Compress5( &Neo1 ); in the endless loop of the task. Compress5 basically takes an array of 24 bit RGB values and encodes them into NEOpixel pulses, compressing 5 encoded bits into a 16 bit SPI data register.

Binary Semaphore: Both the display task and the animation task need to operate on the same memory structures. We need to use a Semaphore to make sure each display refresh and animation operate atomically to prevent flickering of the final NEOpixel display. We can see more details of this by examining the different task routines contained in the main.c code.