Posted on Leave a comment

CH32V003 driving WS2812B LEDs with SPI – Part 13

12 March 2025

Big news! New chips have dropped! Today I was able to order some of the very new and hitherto unobtanium CH32V002 and CH32V006 chips. Very similar to our dear friend the CH32V003, but with more SRAM (4 KB) and in the case of the -006, more pins and 8 KB SRAM. I also spied a new development board for the top-o-the-line CH32V317 chip. It’s got the -WC package, a 68 pin QFN. I’ll grab the -VC package (100 pins of LQFP100 goodness) as soon as they will sell me one.

Testing of the CH32V003 with the SPI-driven WS2812B LED continues, with promising results. So far, no faults have been detected. Having more working examples will help me figure out what is going on in the few cases where it consistently fails.

Let’s go back to some of the unfinished business from the last entry. I had been testing the new SVD files as processed by my Python script and converted into C language header files. I had noticed that some of the defined single-bit field bit masks did not correspond to any of the fields in the defined structures, so I wanted to go back and address that.

The simpler of the obvious things I should do is to include some additional commentary adjacent to these values so I at least know where they’re supposed to go. That worked as well as I would expect it to, and did not seem to introduce any difficulties to the process. Here are the single bit mask values for the PWR peripheral, mentioned previously:

// peripheral register single-bit values

#define PWR_PDDS    (1 << 1) // CTLR PDDS
#define PWR_PVDE    (1 << 4) // CTLR PVDE
#define PWR_PVDO    (1 << 2) // CSR PVDO
#define PWR_AWUEN   (1 << 1) // AWUCSR AWUEN

At least those comments will let me know where those values are supposed to be used.

Now if I can get it to emit bit field structure components for the registers that have only a single field, we should be done here (for now).

Let’s get those missing fields defined. We’re going to need every single one of them!

It seems my very vague recollection about ‘duplicate member’ errors was indeed what had been happening. Luckily for us, there are only a total of twelve name-space collisions in the whole system. Here is an example from the very first peripheral definition in the SVD, our old friend PWR:

union {
    uint32_t        AWUWR;  // 0x0C Automatic wake window comparison value register (PWR_AWUWR)
    struct {
        uint32_t    AWUWR:6;    // 0 AWU window value
    };
};

So both the register and the bit field within the register have the exact same name, as far as the SVD is concerned. I can take advantage of a pattern I have noticed in The Naming of Things here, in that register names often end in ‘R’, which I suppose stands for ‘Register’. That means that I can look for this pattern (register name is identical to field name and ends in ‘R’), and drop the trailing ‘R’ from the field name. Let’s see if that improves things at all.

It looks some fiddlin’, as my Python skills are rudimentary at best, but for the one case where the name substitution would be the correct solution to the duplicate member error, it seems to work OK. That leaves seven more cases to deal with.

A single instance where the register name and the single field within it were the same, and yet not ending in ‘R’, was again found in our friend PWR, the Auto-wakeup Crossover Factor Register (PWR_ AWUPSC). Here I elected to append the entirely arbitrary tag “_field” to the end of the field name. When referring to this setting, just read or write directly to the register instead of the field. Simpler that way.

That leaves six duplicates remaining. These fall into three groups. The first are the injected data readout registers for the ADC, all named IDATA (the RM names them ‘JDATA’). The second group are the ‘unique ID’ bit fields all named ‘U_ID’, and the third is just, I feel, bad naming in the flash controller, with a name collision between the similar Extended Key Register (FLASH_MODEKEYR) and BOOT Key Register (FLASH_BOOT_MODEKEYP), who both define their single, 32-bit wide fields with the same name: MODEKEYR.

And remember, this is just for the -003 chips. I’m sure there will be more issues like this when I eventually get to the ‘bigger’ chips with more peripherals.

I can only think of a couple of ways to deal with the ADC registers. I think the easiest and hopefully most reliable method would be to look for the known duplicate field ‘IDATA’, then amend it based on its uniquely named register, like this:

Register name   New field name
-------------   --------------
IDATAR1         IDATA1
IDATAR2         IDATA2
IDATAR3         IDATA3
IDATAR4         IDATA4

Hey, waddaya know? It worked! Can I get away with the same trick on the unique ID fields, as well? It seems that I can. Now we’re down to only one duplicate, at least on the -003: the ‘MODEKEYR’ field that somehow appears in both the extended key register and boot key register for the flash control peripheral. I think the only thing for this case is a special test, just for this particular combination.

And it solves the last remaining problem. However … as I was browsing through the script, I saw plenty of notes to Future Me, screaming and begging for help. There are still many things that need to be added and improved in this process. But for the moment, we can proceed.

Now you understand why I wanted to do the “simpler” assembly language version first. I’m eventually going to burrow even deeper than that, because of How Things Are. But now I should be able to hold up two examples of software, both ostensibly written in C, and pore over their disassembled guts and figure out why one works and the other one doesn’t.

Leave a Reply