News CH32V003 – Ressourcen und Beschnupperung der HAL


Announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
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 ;)

Dieser Beitrag kann nur von angemeldeten Benutzern beantwortet werden. 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.