CH32V003 und Arduino           

von Michael Moske              

Elektronik-Labor  Projekte  CH32V


Es ist absolut erfreulich, dass der RISC-V Mikrocontroller CH32V003 (hier speziell der CH32V003F4P6) mit der Arduino IDE genutzt werden kann. Hierzu ist zunächst lediglich die Einrichtung des entsprechenden Boards notwendig. Es wird in den "Voreinstellungen" der IDE der folgende Eintrag eingefügt   =>   https://github.com/openwch/board_manager_files/raw/main/package_ch32v_index.json Wird die Arduino-IDE neu gestartet, so erscheint in der Boardverwaltung nach Eingabe von "ch32" der unten stehende Eintrag, den es natürlich zu installieren gilt.


Fürs Erste reicht das schon mal - man kann nun wie gewohnt Arduino-Programme schreiben und Bibliotheksfunktionen aufrufen, allerdings funktionieren ADC (analogRead, analogWrite) und PWM (Timer) noch nicht. Dafür ist noch die Änderung notwendig, die in dem folgenden Artikel beschrieben sind   =>   https://www.instructables.com/Arduino-Timer-Interrupts-for-the-CH32V003-WCH-MCU/ Darin steht die Passage "Board Manager version 1.0.4 will not work with this sketch because it requires the HardwareTimer function. You will need the latest updates available HERE."  Das führt zu folgendem Link   =>   https://github.com/openwch/arduino_core_ch32   mit der entsprechenden Zipdatei "arduino_core_ch32-main.zip". Wenn man dann den Inhalt der Zipdatei in Windows bei   =>   C:\Users\..Dein_Name...\AppData\Local\Arduino15\packages\WCH\hardware\ch32v\1.0.4   ersetzt, so sind nach einem erneuten IDE-Neustart alle Funktionen verfügbar - und man hat bei der Boardauswahl noch die Möglichkeit die Clock-Rate zu wählen.

Ich benutze die Arduino-Version 1.8.19 und vermute, dass die oben genannten Einträge auch für andere Versionen funktionieren. Auch verwende ich lediglich die "nackten" CH32V003-Chips, die ich auf kleine separate Adapterplatinen auflöte, ganz ohne jegliche Widerstände oder Kondensatoren. Solche Adapterplatinen sind vielfältig verfügbar bei z.B. Amazon, AliExpress, eBay ....



Es wird kein externer Quarz benötigt, da intern wahlweise 48 / 24 / 8 MHz zur Verfügung stehen und somit auch PA1 und PA2 frei sind für ADC. PWM funktioniert mit dem "HardwareTimer" an beliebigem Pin wie weiter unten im Besipiel beschrieben.



Hier eine Lösung für die PWM-Ausgabe   =>   CH32V_PWM_mit_Timer.ino.   Auf der Basis einer Interrupt-Funktion entsteht ein PWM-Signal an beliebigem Pin.

//***************************************
#include <HardwareTimer.h>
HardwareTimer myTimer(TIM2);

int myPort= PD4, myLED, time_span, i;

void timerHandler(void)
{ myTimer.pause();
digitalWrite(myPort,HIGH);
delayMicroseconds(time_span);
myTimer.setOverflow(5000-time_span, MICROSEC_FORMAT);
myTimer.resume();
digitalWrite(myPort, LOW);
}
void setup() {
Serial.begin(115200);
pinMode(myPort, OUTPUT);
myLED= LOW;
myTimer.attachInterrupt(timerHandler);
myTimer.resume();
}
void loop()
{ for(i=500;i<2500;i++) { time_span = i; delay(1); Serial.println(i);}
for(i=2500;i>500;i--) { time_span = i; delay(1); Serial.println(i);}
}
//***************************************


Wer etwas tiefer in die Funktionsweise dieser MCU eintauchen möchte, der geht zur Assembler-Programmierung über. Die RISC-V Maschinensprache ist schon etwas besonders, da sie insbesondere 3-Operandenbefehle beinhaltet. Weiterführende Literatur z.B. hier   =>   http://csl.snu.ac.kr/courses/4190.307/2020-1/riscv-spec-v2.2.pdf. Für die Assembler-Enthusiasten hier also ein Beispielprogramm mit einem Blinker   =>   CH32V003_Blinker.zip.   Die Datei CH32V003_Blinker.ino bleibt leer - das eigentliche Assemblerprogramm steht als xy.S im gleichen Verzeichnis. Den Registereintragungen liegt eine Datei   =>   CH32V003_reg1.asm   zugrunde aus folgendem Link   =>   https://github.com/sahasradal/CH32V003-Blinky-RISCV-assembly-language-code.

//***************************************
.equ R32_GPIOC_CFGLR,   0x40011000
.equ R32_GPIOC_BSHR, 0x40011010
.equ R32_RCC_APB2PCENR, 0x40021018

.global setup
.global loop

setup:
li t0,R32_RCC_APB2PCENR // load address of APB2PCENR register to t0 ,for enabling GPIO A,D,C peripherals
lw t1,0(t0) // load contents from peripheral register R32_RCC_APB2PCENR pointed by t0
li a1,(1<<4) // 1<<<<IOPC_EN
or t1,t1,a1 // or values
sw t1,0(t0) // store modified enable values in R32_RCC_APB2PCENR

li t0,R32_GPIOC_CFGLR // load pointer t0 with address of R32_GPIOC_CFGLR , GPIO configuration register
li t1,0x00030000 // set PORTC register for pin4 as push-pull output (PC4)
sw t1,0(t0) // store in R32_GPIOC_CFGLR

li t1,(1<<4) // value for setting pin4 (PC4)
li t2,(1<<(16+4)) // value for clearing pin4 (PC4)

li t0,R32_GPIOC_BSHR // R32_GPIOC_BSHR register sets and resets GPIOC pins, load address into pointer t0

loop:
PC4_ON:
sw t1,0(t0) // store t1 to R32_GPIOC_BSHR
call _delay // delay subroutine
PC4_OFF:
sw t2,0(t0) // store t2 to R32_GPIOC_BSHR
call _delay // delay subroutine
j loop // repeat

_delay: // delay routine
li a1,3000000 // load an arbitarary value 3000000 to t1 register for 1sec blink at 24MHz clock cycle
delay_loop:
addi a1,a1,-1 // subtract 1 from t1
bne a1,zero,delay_loop // if t1 not equal to 0 branch to label loop
ret // return to caller
//***************************************


Sollte aus irgendeinem Grund der Chip nicht mehr ansprechbar sein, z.B. durch eine Fehlprogrammierung des Pins PD1, der ja für die SWD-Übertragung benötigt wird, so gibt es für diese Notfälle eine Abhilfe. Dies ist im folgenden YouTube-Video bestens erklärt   =>   https://www.youtube.com/watch?v=RjOz39VffSU  Diese Rettungsfunktion ist in der WCH-Utility ziemlich versteckt bei "Target" zu finden => "Clear All Code Flash - By Power Off". Hier noch der Link zum Download der WCH-Utility, falls noch nicht vorhanden => https://www.wch.cn/download/file?id=418


Elektronik-Labor  Projekte  CH32V