Der RISC-V-Controller CH32V003          


Elektronik-Labor  Projekte  CH32V

Infos: https://wch-ic.com/products/CH32V003.html

Immer wieder einmal tauchen neue Controller auf, die mich neugierig machen. Diesmal ist es der CH32V003F4P6 im TSSOP20-Gehäuse mit einem 32 Bit RISC-V-Kern von WCH, der chinesischen Firma, die auch den bekannten USB-Wandler CH340 baut. Ich bedanke mich bei Michael Moske, der sich schon tief eingearbeitet hat und mich motiviert hat, auch einmal damit zu arbeiten.  Der Controller ist äußerst preiswert und sehr leistungsfähig. Er arbeitet mit 48 MHz, hat 16 kB Flash und 2 kB SRAM. Ich habe mir eine Entwicklungsplatine und den zugehörigen Programmieradapter von WCH besorgt und versuche nun die ersten Schritte.

Der Chip wird über eine einzelne Leitung SWDIO (1-wire serial debug interface) programmiert. Darüber hinaus werden GND und 3V3 verbunden, und bei Bedarf die serielle Schnittstelle mit RX und TX. Nach dem Programmieren kann die Platine auch über die USB-C-Buchse versorgt werden, ein weiterer Datenverkehr ist damit aber nicht möglich.

Für den Chip gibt es bereits eine Arduino-Boarderweiterung. Die habe ich zuerst ausprobiert.




void setup() {
  pinMode(PC1, OUTPUT);
  pinMode(PC2, OUTPUT);
}

void loop() {
  digitalWrite(PC1, 1);  
  digitalWrite(PC2, 0);  
  delay(1000);                      
  digitalWrite(PC1, 0);   
  digitalWrite(PC2, 1);  
  delay(1000);                     
}

Die Platine hat zwei blaue LEDs, die man an beliebige Ports anschließen kann. Deshalb habe ich einen kleinen Gegentaktblinker programmiert. Das funktioniert problemlos. Einige andere Dinge wie ADC und PWM haben bei mir jedoch noch nicht funktioniert. Deshalb arbeite ich jetzt lieber mit dem offiziellen C-Compiler im MountRiver Studio.



Das MounRiver Studio(MRS) ist frei verfügbar. Bei WCH findet man  das Datenblatt CH32V003DS0.PDF und umfangreiche Beispielprojekte CH32V003EVT.ZIP. Außerdem gibt es ein umfangreiches Referenz Manual https://www.wch-ic.com/downloads/CH32V003RM_PDF.html.

Zuerst habe ich das Beispielprojekt GPIO_Toggle geladen und einiges zum Test geändert. Die entscheidende Datei ist main.c im Verzeichnis User des Projekts.

void GPIO_Toggle_INIT(void)
{
    GPIO_InitTypeDef GPIO_InitStructure = {0}
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD ;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
    GPIO_Init(GPIOC, &GPIO_InitStructure);
}

int
main(void)
{
    GPIO_Toggle_INIT();
        while(1)
    {
        Delay_Ms(250);
        GPIO_WriteBit(GPIOC, GPIO_Pin_0, 1);
        Delay_Ms(250);
        GPIO_WriteBit(GPIOC, GPIO_Pin_0, 0);      
    }
}

Mit diesem Programm habe ich alle Ports einmal ausprobiert. PA1 und PA2 wollten nicht funktionieren. Aber dann habe ich verstanden, dass hier der Quarz angeschlossen ist und die Anschlüsse auf dem Entwicklungsboard nicht mit den Pfostensteckern verbunden sind, außer man lötet zwei zusätzliche Brücken ein. Ursprünglich wurde ein Pusch-Pull-Ausgang (GPIO_Mode_Out_PP) verwendet. Wenn ich mit der rechten Maustaste auf diese Einstellung klicke, bekomme ich mehr Informationen und kann auf Go to Definition klicken. Damit finde ich alternative Einstellungen in der Datrei ch32v00x_gpio.h:

/* Configuration Mode enumeration */
typedef enum
{
    GPIO_Mode_AIN = 0x0,
    GPIO_Mode_IN_FLOATING = 0x04,
    GPIO_Mode_IPD = 0x28,
    GPIO_Mode_IPU = 0x48,
    GPIO_Mode_Out_OD = 0x14,
    GPIO_Mode_Out_PP = 0x10,
    GPIO_Mode_AF_OD = 0x1C,
    GPIO_Mode_AF_PP = 0x18
} GPIOMode_TypeDef;

Die meisten verstehe ich sofort: Verwendung als analoger Eingang, offener Eingang, mit Pulldown, mit Pullup, Ausgang Open Drain, Ausgang Push-Pull. Bei den Optionen mit AF musste ich erst herumsuchen. Es steht für Alternative Funktion, also keine Verwendung als normaler Port. Wenn ich z.B. einen Ausgang TX für serielle Ausgaben verwende, kann ich angeben, ob es Push-Pull oder Open Drain sein soll.

Als nächstes habe ich das Projekt Get_CLK getestet. Es dient dazu, den Systemtakt an PC4 nach außen zu legen. Hier finde ich auch gleich ein Beispiel für die Port-Einstellung GPIO_Mode_AF_PP.

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_30MHz;
    GPIO_Init(GPIOC, &GPIO_InitStructure);

    /* MCO Output GPIOC 4 */
    RCC_MCOConfig( RCC_MCO_SYSCLK );
//    RCC_MCOConfig( RCC_MCO_HSI );
//    RCC_MCOConfig( RCC_MCO_HSE );
//    RCC_MCOConfig( RCC_MCO_PLLCLK );

Das Board verwendet einen Quarz mit 24 MHz. Die Frequenz wird intern über eine PLL verdoppelt. Nun kann ich mir den Takt von 48 MHz genauer ansehen. Der Port wurde auf die höchste Flankensteilheit für Ausgaben bis 30 MHz eingestellt. Ein richtiges Rechtecksignal kann also nicht mehr rauskommen, mein Oszilloskop bis 60 MHz könnte es auch nur verschliffen anzeigen. Aber man sieht trotzdem ein interessantes Detail.



Jede zweite Schwingung erscheint doppelt und in der Phase leicht verschoben. Das ist ein Phasenzittern der PLL. Wenn man eine Frequenz nur um den Faktor 2 erhöhen will, kann man sich ein sehr einfaches PLL-Schleifenfilter leisten. Aber das Ergebnis ist dann eine gewisse Frequenzmodulation im Takt der Ausgangsfrequenz 24 MHz. Wenn das einmal stören sollte, kann man den Takt durch zwei, vier oder 8 usw. teilen, dann ist das Signal wieder sauber. Auch jetzt schon zeigt der Frequenzzähler die korrekte Frequenz von 48 MHz mit einer Abweichung von deutlich unter 1 kHz.

Auf der Suche nach einer Einstellung des Systemtakts bin ich auf die Datei  system_ch32v00x.c im User-Verzeichnis des Projekts gestoßen. Damit konnte ich verschiedene Einstellungen testen. HSI steht für den internen RC-Oszillator und HSE für den Quarzoszillator. Der RC-Oszillator hat eine Abweichung bis 100 kHz bei 24 MHz, also bei meinem Exemplar nur 0,4%, Das ist immer noch genau genug für eine serielle Schnittstelle

/* File Name          : system_ch32v00x.c
/*
* Uncomment the line corresponding to the desired System clock (SYSCLK) frequency (after
* reset the HSI is used as SYSCLK source).
* If none of the define below is enabled, the HSI is used as System clock source.
*/

//#define SYSCLK_FREQ_8MHz_HSI    8000000
//#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




Elektronik-Labor  Projekte  CH32V