CH23V003 and I2C in Platform IO using Arduino

Hi, I try to get my CH32V003F4P6 talk I2C
My Board is a “Home Made One”. Just all pins accessible via Header Pins. Programming is done via the SWDIO Pin. Using the pins as GPIO works like a charm.
I use PlatformIO with the recently added CH32V003 support, and I updated the WCH32 Platform today.

I wrote a simple I2C Scanner:

#include <Wire.h>

int address = 1;

void setup()
  Wire.begin(PC1, PC2);

void loop()

  if (address > 127)
    address = 1;

My PlatformIO look like this:

platform = ch32v
board = genericCH32V003F4P6
framework = arduino

I compile and upload my code to the CH32V003. No problems.
I use my oscilloscope to check if I get ANY “Action” on any pin indicating I2C activity. Neither PC1/PC2 show any pulses nor does any other pin.
All pins I checked are pulled up via 4k7 Ohm resistors.

What am I doing wrong?

Extremely sneaky. You wanna call

which corresponds to the uint32_t, uint32_t variant (for setting SDA, SCL)

However, when disassembling the generated binary inside the debugger, it matched a different function

disas setup
Dump of assembler code for function setup():
   0x000001f2 <+0>:	jal	t0,0x148 <__riscv_save_2>
   0x000001f6 <+4>:	li	a3,0
   0x000001f8 <+6>:	li	a2,1
   0x000001fa <+8>:	li	a1,3
   0x000001fc <+10>:	lui	a0,0x20000
   0x00000200 <+14>:	addi	a0,a0,296 # 0x20000128 <Wire>
   0x00000204 <+18>:	jal	0x7d4 <TwoWire::begin(int, bool, bool)>
   0x00000206 <+20>:	j	0x152 <__riscv_restore_2>
End of assembler dump.

Which is the last variant. So it calls it

void begin(int address = 3, bool generalCall = (bool) 4, bool NoStretchMode = false);


And this actually initializes the I2C peripheral in I2C Slave mode instead of master.

I’m not exactly sure why the last overload with 3 paramters was picked by the C++ compiler, but it did. The VSCode GUI confirms the same with a simple mouse hover over.

Revealing the bug right there.

So, the fix is simple: Either call


since the default pin mapping already is PC1, PC2 per pin map; or, force the right call with

Wire.begin((uint32_t) PC1, (uint32_t) PC2);

After that, the peripheral gets correctly initialized and there’s activtiy on the I2C bus.

This could be considered a bug in the Arduino core because if APIs that take pins always take them as uint32_t, then it should be #define PC1 (uint32_t)3, so automatically massage the code into calling the right overloads.

Hi, we are one step further. :slight_smile:

Following your advice produces a correct I2C address on the bus.
Exactly ONE. After that the bus remains silent although there should be constant activity since I wobble the addresses.

Then there might be a logic bug when handling a negative ACK or a timeout in the Wire library in the OpenWCH core. I can check that too, and report it if true.

Actually this seems to be a known problem also described in

So the OpenWCH core is on the hook to update its buggy code.

Thank you for the research… “All we can do is sit and wait” :slight_smile: