News CH32V003 – Ressourcen und Beschnupperung der HAL


von Tam H. (Firma: Tamoggemon Holding k.s.) (tamhanna)


Angehängte Dateien:

Lesenswert?

Der 10-Cent-Mikrocontroller aus dem Hause WCH überzeugt durch eine vergleichsweise vollständige IDE und einen durchaus leistungsfähigen Kern auf Basis der quelloffenen RISC-V-Architektur. Im Vorgängerartikel hatten wir die Inbetriebnahme der Platine beschrieben - hier wollen wir einen kleinen Blick auf HAL und Dokumentation werfen, um mehr über das System zu erfahren.

(Bildquelle: Autor)

Dokumentation: GitHub!

Dass der Newsaffe dieser Webseite kein besonderer Freund von Git bzw. GitHub ist, dürfte bekannt sein. Trotzdem setzt man im Hause WCH konsequent auf GitHub, wenn es um die Befriedigung der Bedürfnisse westlicher Entwickler geht. Spezifischerweise dient die URL https://github.com/openwch/ch32v003 als „Einsprungpunkt“ in alles, was da in der WCH-Welt kreucht und fleucht.

Zweigeteiltes Datenblatt.

Im Pleistozän der Mikrocontroller-Entwicklung galt, dass 8-Bit-Kerne in einem Datenblatt komplett beschrieben sind. Spätestens seit dem Aufkommen umfangreicher ARM-Cores gilt, dass Halbleiter-Hersteller ihre Dokumentation zwecks besserer Erfassbarkeit in ein Hardware-Datenblatt und in ein Plattform-Referenz-Manual unterteilen, das mehr Informationen über den Aufbau von GPIO-Treiber und Co zur Verfügung stellt. WCH geht die Situation insofern einfacher an, als man die Dokumentation zwar in ein Datasheet und ein Reference Manual unterteilt, die beiden aber-im allgemeinen - „aufeinanderfolgend“ zu lesen sind. Beim Aufrufen der URLs https://github.com/openwch/ch32v003/blob/main/CH32V003DS0-EN.pdf und https://github.com/openwch/ch32v003/blob/main/CH32V003RM-EN.pdf gilt dabei, dass GitHub das Laden der Rich Text-Preview gerne verweigert - klicken Sie auf in der Abbildung grafisch hervorgehobenen Knopf, um einen Download-Prozess der PDF-Datei zu befehligen.

Bildquelle: Autor.

Statt Codegenerator: Beispiel-Repositorium

Man kann von MCC bzw. Cube halten, was immer man da will - außer Frage steht, dass grafische Generatoren die Inbetriebnahme der auf den Chips befindlichen Peripheriegeräte vereinfachen. Auch gilt allerdings, dass chinesische Halbleiterhersteller derzeit durch die Bank keine derartige Unterstützung anbieten - stattdessen geben Sie dem P. T. Entwickler einen mehr oder weniger umfangreiches Beispiel-Kompliment an die Hand, aus dem er dann durch Ausweidung die zum in Betrieb nehmen der vorliegenden Hardware benötigten Komponenten extrahiert. Im Fall unseres WCH CH32 ist dabei die URL https://github.com/openwch/ch32v003/blob/main/EVT/EXAM/ als Einsprungpunkt vorgesehen - wer sie öffnet, sieht wie in der Abbildung gezeigt eine Liste aller zur Verfügung stehenden Projektbeispiele.

Bildquelle: Autor.

Angemerkt sei in diesem Zusammenhang noch, dass die von MounRiver generierten Projektskelette sich eher an der Struktur von STMicroelectronics bzw. GigaDevice orientieren - Hardwaretreiber und sonstige HAL-Elemente sind Teil der Solution, und werden nicht wie in ESP 32-Projekten aus dem „globalen SDK“ zur Verfügung gestellt.

Experiment 1 - GPIO.

Als erstes en vivant angesehenes Codebeispiel will der Autor auf das unter der URL https://github.com/openwch/ch32v003/blob/main/EVT/EXAM/GPIO/GPIO_Toggle/User/main.c bereitstehende GPIO-Blinklicht zurückgreifen, das eine „anzuschließen“ Leuchtdiode periodisch zu blinken bringt. Am interessantesten ist dabei die Art der Initialisierung des Pins, weil wir hier ein in der Welt von WCH immer wieder anzutreffendes Designpattern erstmals sehen:

1
void GPIO_Toggle_INIT(void)
2
{
3
    GPIO_InitTypeDef GPIO_InitStructure = {0};
4
5
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD, ENABLE);
6
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
7
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
8
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
9
    GPIO_Init(GPIOD, &GPIO_InitStructure);
10
}

Neben der Bereitstellung des Arbeitstakts ist die Struktur GPIO_InitTypeDef relevant. Peripheriegeräte werden in der Welt von WCH im allgemeinen insofern konfiguriert, als der Entwickler im ersten Schritt ein Konfigurations-Struct angelegt, in dem die diversen Eigenschaften untergebracht werden. Danach folgt ein Aufruf einer Anwendungsmethode, die - oft unter Nutzung eines globalen Referenzobjekts wie hier GPIOD - die in der Struktur befindlichen Attribute nach außen schreibt. Das eigentliche Blinken der GPIO-Pin-verbundenen Diode erfolgt dann nach folgendem, von GigaDevice und ST Microelectronics hinreichend bekannten Schema:

1
int main(void)
2
{
3
    u8 i = 0;
4
5
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
6
    Delay_Init();
7
    USART_Printf_Init(115200);
8
    printf("SystemClk:%d\r\n", SystemCoreClock);
9
10
    printf("GPIO Toggle TEST\r\n");
11
    GPIO_Toggle_INIT();
12
13
    while(1)
14
    {
15
        Delay_Ms(250);
16
        GPIO_WriteBit(GPIOD, GPIO_Pin_0, (i == 0) ? (i = Bit_SET) : (i = Bit_RESET));
17
    }
18
}

Interruptmethode im Fokus

Die Einrichtung der Interrupt-Engine ist bei den meisten Controllern ein Spezifikum - WCH stellt hierbei unter der URL https://github.com/openwch/ch32v003/blob/main/EVT/EXAM/EXTI/EXTI0/User/main.c ein weiteres Beispiel zur Verfügung, dessen Main-Routine vor allem aus dem Aufrufen der Delay-Funktion besteht:

1
int main(void)
2
{
3
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
4
    Delay_Init();
5
    USART_Printf_Init(115200);
6
    printf("SystemClk:%d\r\n", SystemCoreClock);
7
8
    printf("EXTI0 Test\r\n");
9
    EXTI0_INT_INIT();
10
11
    while(1)
12
    {
13
        Delay_Ms(1000);
14
        printf("Run at main\r\n");
15
    }
16
}

Die Verbindung zwischen den Interrupt-Quellen und den Interrupt-Linien erfolgt von Hand - hierzu sind insgesamt drei Konfigurations-Routinen erforderlich, die im Beispiel folgendermaßen konfiguriert werden:

1
void EXTI0_INT_INIT(void)
2
{
3
    GPIO_InitTypeDef GPIO_InitStructure = {0};
4
    EXTI_InitTypeDef EXTI_InitStructure = {0};
5
    NVIC_InitTypeDef NVIC_InitStructure = {0};
6
7
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO | RCC_APB2Periph_GPIOD, ENABLE);
8
9
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
10
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
11
    GPIO_Init(GPIOD, &GPIO_InitStructure);
12
13
    /* GPIOA ----> EXTI_Line0 */
14
    GPIO_EXTILineConfig(GPIO_PortSourceGPIOD, GPIO_PinSource0);
15
    EXTI_InitStructure.EXTI_Line = EXTI_Line0;
16
    EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
17
    EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
18
    EXTI_InitStructure.EXTI_LineCmd = ENABLE;
19
    EXTI_Init(&EXTI_InitStructure);
20
21
    NVIC_InitStructure.NVIC_IRQChannel = EXTI7_0_IRQn;
22
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
23
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
24
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
25
    NVIC_Init(&NVIC_InitStructure);
26
}

Zu guter letzt wollen wir noch ein Beispiel auf den Interrupt-Handler werfen, die nach folgendem Schema aufgebaut ist:

1
void EXTI7_0_IRQHandler(void) __attribute__((interrupt("WCH-Interrupt-fast")));
2
void EXTI7_0_IRQHandler(void)
3
{
4
  if(EXTI_GetITStatus(EXTI_Line0)!=RESET)
5
  {
6
    printf("Run at EXTI\r\n");
7
    EXTI_ClearITPendingBit(EXTI_Line0);     /* Clear Flag */
8
  }
9
}

Interessant ist hier vor allem die Zeile void EXTI7_0_IRQHandler(void) attribute((interrupt("WCH-Interrupt-fast")));, die den C-Compilers darüber informiert, dass die jeweilige angesprochene Funktion als Interrupt-Handler vorgesehen ist. Wichtig ist außerdem noch der Aufruf von EXTI_ClearITPendingBit(EXTI_Line0); , um die Interruptengine nach der erfolgreichen Verarbeitung des eingegangenen Ereignisses wieder „scharf“ zu schalten.

ADC, I2C und Co

Das im letzten Artikel - siehe URL Beitrag "CH32V003 - Experimente mit dem Zehn Cent-Mikrocontroller" - vorgestellte Blockschaltbild bewies, dass der Chip durchaus umfangreiche Peripheriegeräte mitbringt.

Bildquelle: http://www.wch-ic.com/products/CH32V003.html

Ein interessantes Beispiel, das unter der URL https://github.com/openwch/ch32v003/blob/main/EVT/EXAM/ADC/Auto_Injection/User/main.c bereitsteht, demonstriert die Inbetriebnahme der ADC-Engine. Auch hier kommen Konfigurations-Structe zum Einsatz:

1
void ADC_Function_Init(void)
2
{
3
    ADC_InitTypeDef  ADC_InitStructure = {0};
4
    GPIO_InitTypeDef GPIO_InitStructure = {0};
5
6
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOD, ENABLE);
7
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
8
    RCC_ADCCLKConfig(RCC_PCLK2_Div8);
9
10
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
11
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
12
    GPIO_Init(GPIOC, &GPIO_InitStructure);
13
14
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
15
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
16
    GPIO_Init(GPIOD, &GPIO_InitStructure);
17
18
    ADC_DeInit(ADC1);
19
    ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
20
    ADC_InitStructure.ADC_ScanConvMode = DISABLE;
21
    ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
22
    ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
23
    ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
24
    ADC_InitStructure.ADC_NbrOfChannel = 1;
25
    ADC_Init(ADC1, &ADC_InitStructure);

Interessant ist außerdem, dass der CH32 eine Software-Kalibration mitbringt, die folgendermaßen zu aktivieren ist:

1
    ADC_RegularChannelConfig(ADC1, ADC_Channel_2, 1, ADC_SampleTime_241Cycles);
2
    ADC_InjectedChannelConfig(ADC1, ADC_Channel_3, 1, ADC_SampleTime_241Cycles);
3
    ADC_Calibration_Vol(ADC1, ADC_CALVOL_50PERCENT);
4
    ADC_AutoInjectedConvCmd(ADC1, ENABLE);
5
    ADC_Cmd(ADC1, ENABLE);
6
7
    ADC_ResetCalibration(ADC1);
8
    while(ADC_GetResetCalibrationStatus(ADC1));
9
    ADC_StartCalibration(ADC1);
10
    while(ADC_GetCalibrationStatus(ADC1));
11
}

Das eigentliche Auslesen erfolgt dann durch „Anstoßen“ des Konversionsprozesses:

1
u16 Get_ADC_Val(u8 ch)
2
{
3
    u16 val;
4
    ADC_SoftwareStartConvCmd(ADC1, ENABLE);
5
    while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC));
6
    val = ADC_GetConversionValue(ADC1);
7
    return val;
8
}

Die I2C-Engine unterstützt ihrerseits dabei sowohl den Master- als auch den Slave-Betrieb: das unter https://github.com/openwch/ch32v003/blob/main/EVT/EXAM/I2C/I2C_7bit_Mode/User/main.c stehende Beispiel illustriert die Vorgehensweise zur Initialisierung.

Ausblick und Zukunft.

Außer Frage steht, dass das Fehlen eines grafischen Konfigurationseditors die Arbeit mit dem CH32 ein wenig erschwert. Andererseits gilt, dass der Preis - in Stückzahlen bei Anforderung beim Verkaufsteam werden die 10 Cent erreicht - ausreicht, um den Aufwand zu rechtfertigen. Der Autor hofft, dass diese Experimente „Anstoß“ für eigene Versuche sind.

In eigener Sache.

Der Newsaffe möchte diese Gelegenheit nutzen, um sich von der Leserschaft für dieses Jahr zu verabschieden. Ich wünsche einen guten und vor allem gesunden (Stichworte: Streit/nervliche Belastung re Feierplanung, Glatteis, Feuerwerk und Alkohol) Rutsch und hoffe, euch alle im neuen Jahr in alter Stärke wieder zu sehen.


: Bearbeitet durch NewsPoster
von Matthias 🟠. (homa)


Lesenswert?

Frohes Neues!

von Tam H. (Firma: Tamoggemon Holding k.s.) (tamhanna)


Lesenswert?

Matthias 🟠. schrieb:
> Frohes Neues!

Danke dir, mein Freund. Dir auch alles Gute!

von Fred F. (fred08151)


Lesenswert?

Tam H. schrieb:
> Der 10-Cent-Mikrocontroller aus dem Hause WCH ...
Der kostet aber mehr als 10 Cent ;)

von Lothar L. (lole)


Lesenswert?

Hallo,
gibt es inzwischen schon Neuigkeiten, Erfahrungen? Ich habe mir ein 
Entwicklungsboard zugelegt und ein bisschen rumgespielt. Mein Interesse 
an den Chips haben die SOP8 geweckt. Das mit den 10 Cent kommt da schon 
fast hin. Mindestmenge 50 Stück. Der F4P6 sowohl auf dem Board, als auch 
ohne Quarz auf einem Breadbord machen das, was versprochen wird. Beim 
J4M6(SOP8) schaffe ich nicht mal eine LED anzusteuern. Programmieren 
funktioniert, debuggen nicht. Keine Antwort vom Port 3333. Ich habe 
verschiedene GPIOs probiert, es blinkt leider gar nichts. Dem MCRL Pin 
zu aktivieren ist auch nicht ganz ohne. Standardmäßig ist der 
deaktiviert. Habe ich auch erst gemerkt, als ich einen Reset per Taste 
gebraucht habe. Wäre schön, hier im Forum mehr von der Familie zu lesen.
Grüße Lothar

von Blake S. (blake_s)


Lesenswert?

Lothar L. schrieb:
> At the
> J4M6(SOP8) I can't even control an LED . Program
> working, not debugging. No response from port 3333. I have
> Tried different GPIOs, unfortunately nothing flashes. The MCRL pin
> to activate is also not entirely without. By default the
> disabled. I only noticed that when I did a reset with a button
> needed. Would be nice to read more about the family here in the forum.

Greetings from the UK, I have also been experimenting with these chips. 
I have had the same experience. The F4P6 works great. The J4M6 is 
problematic.

I have managed to flash the J4M6 using the WCH Link-e and Mounriver 
Studio.
Rx-Tx pin 8
Tx-Rx Pin 1
Rst - PA2 pin 3
Swio - also tie to pin 8
3.3v and G

I can only flash these chips once and I am still trying to successfully 
program them. I can get a program running, but its behaving badly.

von Lothar L. (lole)


Angehängte Dateien:

Lesenswert?

Ich bin inzwischen etwas weiter gekommen. Alle fünf verfügbaren GPIOs 
sind jetzt ansprechbar. In MounRiver, ich nutze V1.84, muss vor jedem 
Flashen die Konfiguration aufgerufen werden, der "Target Mode" und bei 
"Clear Code Flash" by Power Off ausgewählt werden. NRST gibt es beim 
J4M6 nicht. Das klappt auch beim "Debuggen". Dafür darf dann aber die 
Debug-UART nicht aktiviert sein, die nutzt dummerweise auch Pin8. Zum 
Entwickeln und Testen besser einen größeren Chip benutzen. Das Demoboard 
mit dem F4P6 ist bestens geeignet.
Grüße, auch nach UK

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.