These super cheap (around $0.10 in bulk) RISC-V MCUs from WCH have been pretty popular recently. I recently had a need for a small, low power MCU and my usual choice of an Espressif module would have just been too power hungry - and also too large.
I’m using the smallest version of the CH32V003 - the CH32V003J4M6 - this is an 8 pin IC. It’s definitely a massive step down from what we normally have to play with. It’s got 16K of flash and only 2K of RAM. The CPU is pretty reasonable though - it’s RISC-V running at 48MHz - so not too shabby.
There are a lot of online guides to programming this chip - so I’m not going to do a deep technical dive. Here’s a good starting point as is this seies.
You don’t need very much to bring this chip up. It’s got its own built in voltage regulator - so you can feed it anything from 2.7~5.5V and it just needs a 100nF decoupling capacitor.
For our project, I took inspiration from this project - which is a coin cell powered mini game console. This was a good starting point as it included a buzzer. Here’s my schematic:
And the final PCB layout:
Despite me adding pin headers, we still ended up with a very small board: 16.3mm X 11.7mm.
I got the boards made up by PCBWay. I skipped ordering a stencil as there are very few components on the board, so not too hard to solder up by hand.
But I had forgotten how painful soldering under the microscope was.
Then I remembered - I have a PCB printing machine that also dispenses solder paste!
I was pretty amazed at how well this came out, especially considering the solder paste I used is almost three years past its best-before date!
Each board consists of:
- One CH32V003 MCU
- A small piezo buzzer driven by a transistor
My plan is/was to power these boards from a CR2032 coin cell battery. In standby mode the power consumption is very good - around 7-8µA.
I did find a bit of a problem with standby mode - with the device in standby, I couldn’t program it anymore - which is a bit of a pain when you are trying to develop. And even worse if your wakeup is not working. For a while, I thought I’d bricked it completely.
Fortunately there is a way to get back to a good known state. The wlink utility can be used to erase the flash even in standby mode.
$ ./wlink erase --method power-off --chip CH32V003
When playing music, the power consumption is a different story. When the MCU is in active mode, it’s a very reasonable 3.3mA. But as soon as we start playing audio, we spike up to 130mA.
I am using a very low mark-space ratio on my audio output (the buzzer is only on for a small fraction of the time), so we do only average 13-14mA, but the 130mA peaks are too much for a coin cell - as soon as we play audio, the device browns out.
My backup plan was to use one of these tiny Lithium cells. I did have to modify one of my charger boards for this cell - I don’t think pumping 1A into an 80mAh cell would end well…
With the LiPo, we get more than enough juice to power the board and play the music without issues.
But I really wanted to get this working with a coin cell. I modified one of the boards to use a much higher base resistor on the transistor - bumping this up to 10K.
With this, we got some much more reasonable results.
- Peak current: 56 milliamps (down from 130mA!)
- Average current: Just over 7 milliamps (down from 13-14mA)
This version works beautifully with the coin cell, plays all the way through, and is still plenty loud enough for my purposes.
My actual code for playing back the audio is very simple and naive. I’m just bit-banging the GPIO pin with the note frequency.
It doesn’t sound too bad for one of these tiny buzzers - it’s actually pretty impressive!
To actually get the audio data I put together a simple tool (well, my AI chum put it together…). This lets you export a track from a midi file in a simple format that the MCU can use.
This also contains a very simple 1-bit Sound FX generator which is quite fun.
All the code for the project is on GitHub.
And you can listen to the results here: