CH32V003
The 10 cent micro-controller.
Down the Rabbit hole I go..
The 10 cent micro-controller.
Down the Rabbit hole I go..
(Down load a PDF with some known issues and fixes here: List and some fixes.) Facts about these chips.. Cheap, yes. Reliable, not. It says I2C, but really if you want it, you have to bit bang it. Its buggy (read further), and its power hungry. If you want to use the higher speeds, you can at your own risk. I run mostly at 8-24mhz. But for a few cheep, NON safety projects, its an okay chip. My go to is still the ATtiny402 (for 8 pin). But I use many of the ATtiny 0, 1, 2 series. The ATtiny is proven and reliable.
The CH32v003 series:
For the pin test of the 8 pin CH32v003J4M6 and other future projects, look here. (I'll probably be doing most of my future projects with this chip or the 18 pin CH32v003A4M6)
Spent $12 ordering chips (the 8 pin) and a programmer (it also came with 5 free 20 pin F4P6's). Going to test out this RISC-V chip
The CH32V003 is a 32-bit RISC-V micro-controller featuring a QingKe RISC-V2A core up to 48MHz, 2KB of SRAM, and 16KB of flash memory. It has a wide operating voltage range (2.7V to 5.5V) and supports low-power modes like sleep and standby. Key peripherals include a 10-bit ADC, an op-amp comparator, a DMA controller, and standard communication interfaces like USART, I2C, and SPI. It may be faster then my ATtiny's, but I will use them still. The CH32V003 does not have an eeprom (which I used in the ATtiny often).
So I will be stepping in to a 32 bit controller from 8 bit.
11/18/2025:
I've had a little bit of time to play with the 8pin CH32v003J4M6 and the 20pin CH32v003f4p6. So far not to impressed with the datasheets. The info on the both is in one reference manual that is 188 pages long, but to see the physical pin out you have to open the 28 page datasheet.. WTF.. With the ATtiny lets say there 8 pin 402/202 (the only difference is 4k/2k of memory) which has its own separate datasheet from the other ATtiny's and is 544 pages long. Much more detail.. Lets say you want to use the ATtiny 806/1606 the 20 pin MCU (the only difference is memory 8k/16k) it has a datasheet that is 550 pages long. So the ATtiny is well documented.
Since I wanted to get started quickly, I used the Mounriver IDE (Eclipse with a different wrapper on it). I started with the J4M6 8pin chip. just to even get all 5 ports to toggle ( I don't use the SWIO programming port, If I need more pins I'll go to a bigger chip like the F4P6 or 16pin A4M6) I had to remap one of the pins because THEY HAD ITS DEFAULT MAP TO AN EXTERNAL OSCILLATOR! I can see that on a 20 pin or even 16 pin MCU, But not on an 8 pin that has an internal 48mHz clock (which I usually drop to 8mHz for low power). But for its price I like it. But if I need something serious or reliable. I use the ATtiny 0's 1's or 2's series and sometimes my go to ATtiny10. To fix the clock, I went into the file (and I have to do this to all projects that I don't use external oscillator): system_ch32v00x.c
#define SYSCLK_FREQ_8MHz_HSI 8000000 //I un-commented this one.
//#define SYSCLK_FREQ_24MHZ_HSI HSI_VALUE
//#define SYSCLK_FREQ_48MHZ_HSI 48000000
//#define SYSCLK_FREQ_8MHz_HSE 8000000
//#define SYSCLK_FREQ_24MHz_HSE HSE_VALUE
//#define SYSCLK_FREQ_48MHz_HSE 48000000 //(This was the default)
As of this date (11/18/25) I'm still trying to figure a few things out. I wrote some code to toggle pins on the F4P6 (20 pin), so I can test all the ports. The D1 pin is the SWIO programming port so I'm not going to touch it. But I could get most of the ports to toggle. BUT when trying them all at once, it had some serious weird effects. I'm sure its due to pins being tied to some other default function that caused it. Example. I tried to have all pins flash at the same time. But they would just stay on. Even though I had the ports turn on and off in the while loop. I thought maybe a clock issue. So I put a delay in the main func before the while loop. This worked.. Sort of.. Since I had two delays that I set both at 300ms on delay had a different time on it. Then I noticed it was identical to the delay in the MAIN section that was controlling the state that stayed on longer or shorter (I believe it was the off state). When I would change the delay that was NOT in the while loop, it would affect the off state..
Okay, Now I went down a huge rabbit hole here.. So I will shorten it. After remarking out all toggles and slowly adding them I found PD6 was having weird effects on all the other pins. When it went off all the others turned off. I tried the toggle multiple ways. But no luck. So I left PD6 commented out. So out of all the pins PD1 I don't use, PD6, PD7(hooked up to NRST), and PA2(defaulted to the external oscillator) do not work so far. PC5 works but is low power. I will probably have to do some more remapping on those pins.
11/20/2025 (Low Power testing and info):
After doing many tests.. One definite conclusion is THE CH32V003 IS NOT GOOD FOR LOW POWER. It is good if you have a decent amount of power. But compared to my go to MCU the ATtiny 0's 1's 2's series and the ATtiny10, the CH32V003 in lowest power mode uses 10uA, while the ATtiny's use .1uA . All you have to do is read the datasheets, but I also tested with my Bryman meter. The CH32V003 did test at 10.1uA, The ATtiny402 tested at .1uA, and the ATiny10 1.57uA. This puts the ATtiny series about 60-100 times lower current then the CH32V003. The tests were done with the code set to low power with 1 pin for wake up for 5 seconds, then go back to sleep. This will still not detour me from using the CH32V003 . The CH32V003 is (from what I have to pay and the quantities I buy) a realistic price of 40 cents each and my ATtiny402's were 61 cents each. Those prices are what I paid. I'm still doing much more testing on the CH32V003's. But so far I would still recommend them, but not for very low power project.
11/22/2025 (A4M6 16Pin speed tests, Interesting):
While testing my new A4M6 chips, that I just got, I did the usual
GPIO_WriteBit(GPIOC, GPIO_Pin_0, Bit_SET);
GPIO_WriteBit(GPIOC, GPIO_Pin_0, Bit_RESET);
It tested kind of slow, clock set at 48mHz in system_ch32v00x.c file. I only went about 1.19MHz (I'm sure multiple clock cycles for each). I looked at the GPIO_WriteBit func. Which showed: GPIOC->BSHR = GPIO_Pin_0; So I put the lines as:
GPIOC->BSHR = GPIO_Pin_0;
GPIOC->BCR = GPIO_Pin_0;
The speed jumped to 6 MHz!! Okay so if you want faster switching.. Don't use built in function. At setting the clock down to 8MHz with the GPIOC->BSHR = GPIO_Pin_0; it showed 1.14Mhz. About the same as using the default GPIO_WrightBit function at 48MHz clock.
12/4/2025 Problems found with CH32v003. While trying to set up the A4M6 (16pin version) for PWM, timer 1, I beat my head against the wall a lot. Timer 2, Not a problem. Then I decided to have this controller run my Christmas tree ornaments. But I wanted it to go to standby to drop it down to about 8uA, then wake and continue to save battery... Well first of all this chip is not one for low power. I get my ATtiny's much lower in power down mode (like a 1/2 of a uA). Now when I tried to get this CH32v003 to do the PWM on 2 ports (PC1/PA1), 2 separate timers, and some random flashing on another port (PD4). I wanted it to go to standby for about 3-4 seconds.. Got that figured out BUT THERE WAS A PROBLEM.. here is my log of what happened last night:
as of 12/3/25 Standby kind of works. I got it to wake, but it affect PD4 and somtimes
PC1 (pin 1) stays on, something to do with pwm not being shut down properly.
as of 12/3/25 1914hrs - Fixed the PD4 issue, it no longer dime. It was in GPIOConfig()
that had the issue, It had PC1 PP and not PD4. It still sometimes sleep with PC1 light, again
probably a timer issue and not shut down proper.
as of 12/3/25 2016hrs - Still TIM2 on PC1 sometimes shuts down and sometimes does not
after trying everything AI said (ai sucks) no luck. But still this is more energy efficient now.
as of 12/3/25 2042hrs - finally solved from chatgpt's 3rd try:
see function void PWM2_Stop_PC1_Safe(void) get this function here:
void PWM2_Stop_PC1_Safe(void)
{
/* 1. WAIT FOR PWM OUTPUT TO GO LOW
In PWM1 mode: output HIGH while CNT < CH2CVR.
*/
while (TIM2->CNT < TIM2->CH2CVR)
{
// wait until PWM output naturally reaches LOW
}
/* 2. STOP TIMER AND PWM OUTPUT */
TIM_CtrlPWMOutputs(TIM2, DISABLE);
TIM_Cmd(TIM2, DISABLE);
/* 3. DISABLE ALTERNATE FUNCTION ON PC1 (TIM2_CH2) */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
AFIO->PCFR1 &= ~(1 << 2); // disable AF for TIM2_CH2 on PC1
/* 4. SET PC1 AS NORMAL GPIO OUTPUT */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
GPIO_InitTypeDef gpio = {0};
gpio.GPIO_Pin = GPIO_Pin_1;
gpio.GPIO_Mode = GPIO_Mode_Out_PP;
gpio.GPIO_Speed = GPIO_Speed_10MHz;
GPIO_Init(GPIOC, &gpio);
/* 5. FORCE PC1 LOW */
GPIO_ResetBits(GPIOC, GPIO_Pin_1);
}
Here is the CH32v003F4P6 code that will toggle most of the pins on then off at separate times (to make sure one pin does not interfere like PD6):