Arduino CH32V003

OP #7893892
Lesenswert?

Im Moment experimentiere ich mit dem CH32V003 und diesem Framwork:
https://github.com/openwch/arduino_core_ch32?tab=readme-ov-file

und versuche hier mal ein paar Eigenheiten zu dokumentieren.

Hier gibt es eine Anleitung:
https://www.hackster.io/patrick-fitzgerald2/program-ch32v003-risc-v-with-arduino-ide-135f6f

Auf welche Pins wird der serielle Port "gemapped".

Angehängte Dateien:
OP #7893904
Lesenswert?

Hab's raus gekriegt:

1
/*
2
 * CH32V003F4P6 Serial Send Test
3
 * 
4
 * CH32V003F4P6 TX (output) : PD5
5
 * 
6
 * has to be connected to the Programming Dongle RX
7
 * 
8
 * Framework:
9
 * https://github.com/openwch/arduino_core_ch32
10
 */
11

12

13
void setup() {
14
  Serial.begin(115200);
15
}
16

17
uint8_t Counter = 0;
18

19
void loop() 
20
{
21
  Serial.println(Counter++);
22
  delay(100);
23
}
OP #7894207
Lesenswert?

Christoph M. schrieb:

Geschwindigkeit Pin-Toggle mit digitalWrite: 255kHz Alexander (alecxs) 18.06.2025 15:46 Ich wollte hier nur wissen: 233kHz oder so

1
 * Results:
2
 * 
3
 * within while loop  255kHz
4
 * without while loop 213kHz

Es wäre interessant zu sehen, wie schnell es maximal wird, wenn man direkt auf den Port schreibt.

OP #7894213
Lesenswert?

Oha, der Speicherverbrauch des Framworks scheint exorbitant:

1
/*
2
 * trigonometrie speed test
3
 */
4

5
void setup()
6
{
7
  Serial.begin(115200);
8
}
9

10
void loop()
11
{
12
  float sum=0;
13

14
  float x,y,z;
15
  
16
  Serial.println("start");
17
  for(uint32_t n=0;n<100000;n++)
18
  {
19
    x=sin(2*PI*n/1000);
20
    x=cos(2*PI*n/1000);
21
    sum+=sqrt(x*x+y*y);    
22
  }
23
  Serial.print("sum: ");
24
  Serial.print(sum);
25
}

/tmp/arduino_build_733415/CH32V003_trigonometrieSpeed.ino.elf section .text' will not fit in region FLASH'

.arduino15/packages/WCH/tools/riscv-none-embed-gcc/8.2.0/bin/../lib/gcc/riscv-none-embed/8.2.0/../../../../riscv-none-embed/bin/ld: region `FLASH' overflowed by 8680 bytes

OP #7894223
Lesenswert?

Beim Arduino Nano passt der obige "trigonometrie Speedtest" locker:

1
Der Sketch verwendet 2570 Bytes (8%) des Programmspeicherplatzes. Das Maximum sind 30720 Bytes.
2
Globale Variablen verwenden 200 Bytes (9%) des dynamischen Speichers, 1848 Bytes für lokale Variablen verbleiben. Das Maximum sind 2048 Bytes.
OP #7894391
Lesenswert?

Oha, der Speicherverbrauch des Framworks scheint exorbitant:

Der Grund dafür: Beitrag "Re: CH32V003, float wirklich langsam?"

Danke.

Auszug:

1
von Yalu X. (yalu) (Moderator)
2
14.04.2025 12:47
3
  
4
In der Mounriver-Toolchain für die CH32-Controller wird die generische
5
soft-fp der glibc verwendet. Diese ist in C implementiert und in
6
keinster Weise an spezifische CPU-Typen angepasst. Die AVR-FP-Routinen
7
hingegen sind in Assembler speziell für diesen CPU-Typ geschrieben.

Was mich dabei wundert: Die RISC-V Architektur ist ja ziemlich genau über die Abkürzungen der Erweiterung der Namensbezeichnung.

Die Bezeichnung von WCH ist RISCV-V2A

https://akizukidenshi.com/goodsaffix/CH32V003.pdf

was aber nicht mit der Namenskonvention der RISC-V Organisation übereinstimmt. Im Datenblatt wird dann auf die Bezeichnung des Cores als RV32EC verwiesen .

Da die RISC-V Architekur ja ein mittlerweile recht verbreiteter Standard ist, sollte man erwarten, dass es eine schnelle Assembler-Float-Lib dazu gibt.

#7894394
Lesenswert?

Christoph M. schrieb:

CH32V003_Arduino_toggleSpeedTest.png

Christoph M. schrieb:

Und ohne die while Schleife? Ist für die Geschwindigkeit.

Da es dir anscheinend auf Geschwindigkeit ankommt, ist die relativ langsame Funktion digitalWrite() doch eher ungeeignet. Wäre an deren Stelle das deutlich schlankere digitalWriteFast() nicht naheliegender?

p.s. Programmcode als PNG ist schon stark :-(

OP #7894401
Lesenswert?

Hier mal der Geschwindigkeitstest mit direktem Beschreiben von PC1.
Die Frequenz liegt bei knapp 6MHz.
Bei 48Mhz wäre das also 48/6/2=4 Zyklen pro Schreiben. Die Frage ist: Wieviel davon braucht der Sprung?

1
/*
2
 * CH32V003
3
 * 
4
 * Pin Toogle Speed Test
5
 * Results:
6
 * 
7
 * Toggle Speed: 168ns, ~5.95 MHz
8
 * 
9
 * 2025-06-19 mchris 
10
 * 
11
*/
12

13

14
#define RCC_APB2PCENR   (*(volatile uint32_t*)0x40021018) // APB2 Peripheral Clock Enable Register
15
// Controls the clock to APB2 peripherals.
16
// You must enable the clock for GPIOC (by setting bit 4, IOPCEN) before using any GPIOC functions.
17

18
#define GPIOC_CFGLR     (*(volatile uint32_t*)0x40011000) // GPIOC Port Configuration Low Register
19
// Configures the mode and configuration (input/output, push-pull/open-drain, etc.) for pins PC0-PC7.
20
// Each pin uses 4 bits: [MODE1:0 | CNF1:0]. For PC1, configure bits 7:4.
21

22
#define GPIOC_BSHR      (*(volatile uint32_t*)0x40011010) // GPIOC Bit Set/Reset Register
23
// Allows atomic setting or resetting of GPIOC output pins.
24
// Write '1' to bit n (0-15) to set PCn high; write '1' to bit n+16 (16-31) to set PCn low.
25
// Example: GPIOC_BSHR = (1 << 1);      // Set PC1 high
26
//          GPIOC_BSHR = (1 << (1+16)); // Set PC1 low
27

28
void setup()
29
{
30
  // Enable GPIOC clock
31
  RCC_APB2PCENR |= (1 << 4); // Set bit 4 (IOPCEN) to enable GPIOC peripheral clock
32

33
  // Configure PC1 as push-pull output, 50MHz
34
  GPIOC_CFGLR &= ~(0xF << 4); // Clear configuration bits for PC1 (bits 7:4)
35
  GPIOC_CFGLR |=  (0x3 << 4); // Set MODE1=11 (output 50MHz), CNF1=00 (push-pull)
36
}
37

38
void loop()
39
{
40
  // Blink PC1
41
  while (1) {
42
    GPIOC_BSHR = (1 << 1);      // Set PC1 high
43
    //delay(1000);
44
    GPIOC_BSHR = (1 << (1 + 16)); // Set PC1 low
45
    //delay(1000);
46
  }
47
}
Angehängte Dateien:
#7894464
Lesenswert?

... ich habe zwar einen ganz anderen Ansatz für die Verwendungsmöglichkeiten von CH32V003, habe aber in meinen Anleitungen zum Schluß auch die Möglichkeit der Programmierung unter Arduino behandelt und verwende in dieser Anleitung denselben (offiziellen) Core wie du, den ich um meine eigenen Programmiermöglichkeiten (unter Linux) erweitert hab.

Was ich beim Schreiben einer Anleitung vermisst habe, war ein typisches Pinoutmapping des Chips zur Arduino Programmierung und deshalb habe ich dann dieses hier im Anhang erstellt.

:-) vllt. kannst du das für deine Seite zur Programmierung von CH32V003 unter Arduino gebrauchen.

Gruß, Ralph

Angehängte Dateien:
OP #7894501
Lesenswert?

Benjamin K. (bentschie) 19.06.2025 07:42

Du könntest auch in der Schleife mehrere High/Low wechsel einbauen und dann siehst du auf dem Oszi die einzelnen zeiten etwas besser.

Eine sehr gute Idee :-) Ich meine 2 Cyclen pro Portschreiben zu sehen.

1
/*
2
   CH32V003
3

4
   Pin Toogle Speed Test
5
   Results:
6

7
   Toggle Speed: 82ns or 12.2MHz for ON/OFF
8

9
   2025-06-19 mchris
10

11
*/
12

13

14

15
#define GPIOC_BSHR      (*(volatile uint32_t*)0x40011010) // GPIOC Bit Set/Reset Register
16

17
void setup()
18
{
19
  pinMode(PC1, OUTPUT);
20
}
21

22
#define LEDON {GPIOC_BSHR = (1 << 1);}
23
#define LEDOFF {GPIOC_BSHR = (1 << (1 + 16));}
24

25
void loop()
26
{
27
  // Blink PC1
28
  while (1) {
29
    LEDON;
30
    LEDOFF;
31
    LEDON;
32
    LEDOFF;
33
  }
34
}
Moderator #7894590
Lesenswert?

Christoph M. schrieb:

Was mich dabei wundert: Die RISC-V Architektur ist ja ziemlich genau über die Abkürzungen der Erweiterung der Namensbezeichnung.

Die Bezeichnung von WCH ist RISCV-V2A

https://akizukidenshi.com/goodsaffix/CH32V003.pdf

was aber nicht mit der Namenskonvention der RISC-V Organisation übereinstimmt. Im Datenblatt wird dann auf die Bezeichnung des Cores als RV32EC verwiesen .

RV32EC ist der Name einer von der Berkeley-Universität bzw. RISC-V International entwickelten Befehlssatzarchitektur.

QingKe V2A ist der Name eines von Nanjing Qinheng Microelectronics entwickelten Prozessorkerns, der die RV32EC-Architektur implementiert.

CH32V003 ist der Name eines ebenfalls von Nanjing Qinheng Microelectronics unter der Marke WinChipHead (WCH) entwickelten Mikrocontrollers, der auf dem QingKe-V2A-Kern basiert.

#7895271
Lesenswert?

Christoph M. schrieb:

Oha, der Speicherverbrauch des Framworks scheint exorbitant:

Guckst du hier:

Beitrag "Re: CH32V003: Selbstbauprogrammer und "Getting started""

da hab ich einen Vergleich des Speicherbedarfs gemacht. Für dein Arduinoprogramm oben

Christoph M. schrieb:

for(uint32_t n=0;n<100000;n++) { x=sin(2PIn/1000); x=cos(2PIn/1000); sum+=sqrt(xx+yy); }

wird garantiert irgendwo die Math-Library eingebunden (des Sinus und Cosinus wegen) und wie ich weiß, ist diese Bibliothek schon außerhalb von Arduino so groß, dass Sinus und Cosinus auch außerhalb von Arduino nicht in ein reines C-Programm eingebunden werden kann, weil auch hier schon zu groß!

Persönliche Seite #7895420
Lesenswert?

Christoph M. schrieb:

1
> void loop()
2
> {
3
>   float sum=0;
4
> 
5
>   float x,y,z;
6
> 
7
>   Serial.println("start");
8
>   for(uint32_t n=0;n<100000;n++)
9
>   {
10
>     x=sin(2*PI*n/1000);
11
>     x=cos(2*PI*n/1000);
12
>     sum+=sqrt(x*x+y*y);
13
>   }
14
>   Serial.print("sum: ");
15
>   Serial.print(sum);
16
> }
17
>

Also man sollte sich schon genau überlegen, wie man einen Benchmark schreibt. GCC v15 optimiert das einfach zu sum = 0.

Auch wenn man den Code umschreibt zu

1
x=sin(2*PI*n/1000);
2
y=cos(2*PI*n/1000);

ist es nicht wirklich geschickt, weil man auf einen Blick sieht, dass die Berechnung gleichbedeutend mit "sum = 100000" ist. (Zumindest mit entspannter IEEE Interpretation, also rein arithmetisch).

OP #7895458
Lesenswert?

Johann L. (gjlayde) Benutzerseite

Also man sollte sich schon genau überlegen, wie man einen Benchmark schreibt. GCC v15 optimiert das einfach zu sum = 0.

Erstaunlich. Ich hätte nicht erwartet, dass der Compiler so gut optimieren kann.

ist es nicht wirklich geschickt, weil man auf einen Blick sieht, dass die Berechnung gleichbedeutend mit "sum = 100000" ist.

Inclusive der Rundungsfehler der FP32 Rechnung?

OP #7895461
Lesenswert?

Dieser Benchmark wäre besser:

1
void setup() {
2
  Serial.begin(115200);
3
}
4

5
void loop() {
6
  float sum = 0;
7
  float x, y;
8
  Serial.println("start");
9
  uint32_t start = micros();
10
  for (uint32_t n = 0; n < 10000; n++) {
11
    float r = random(0, 1000) * 1e-6; 
12
    x = sin(2 * PI * n / 1000 + r);
13
    y = cos(2 * PI * n / 1000 + r);
14
    sum += sqrt(x * x + y * y);
15
  }
16
  uint32_t dt = micros() - start;
17
  Serial.print("sum: ");
18
  Serial.println(sum, 6); // more decimals for float
19
  Serial.print("calculation time [us]: ");
20
  Serial.println(dt);
21
  Serial.println();
22
  delay(1000); // avoid flooding serial
23
}

Auf einem Arduino-Nano ergibt sich:

1
  
2
start  
3
sum: 10000.000000
4
calculation time [us]: 4457368
OP #7895464
Lesenswert?

Oje, sieht schlecht aus für das RiscV-Softwareframework:

1
Arduino Nano Atmega328
2
start  
3
sum: 10000.000000
4
calculation time [us]: 4.457.368
5

6
PiPico2 RiscV RV32IMACZ 
7
start
8
sum: 10000.000000
9
calculation time [us]:   742.654
10

11
PiPico2 ARM Mode
12
start
13
sum: 10000.000000
14
calculation time [us]:    87.584
15

16
Geschwindigkeitgewinn (Faktor) reltaiv zum Arduino Nano:
17
PiPico RiscV : ~6
18
PiPico ARM : ~50
Persönliche Seite #7895562
Lesenswert?

Christoph M. schrieb:

Johann L. (gjlayde) Benutzerseite

Also man sollte sich schon genau überlegen, wie man einen Benchmark schreibt. GCC v15 optimiert das einfach zu sum = 0.

Erstaunlich. Ich hätte nicht erwartet, dass der Compiler so gut optimieren kann.

y wird nicht initialisiert aber im Ausdruck für sum verwendet. sum hat folglich keinen definierten Wert, also kann man auch sum=0 nehmen.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren