USB und ADC mit der STM32 Cube-IDE         


Elektronik-Labor  Projekte   


Besonders reizvoll an der BluePill-Platine ist die USB-Schnittstelle. Um sie mit der CubeIDE einzusetzen, bin ich erstmal wieder auf die Übersicht der Prozessor-Pins gegangen. In dem vorhandenen Projekt muss ich dazu nur auf BluePill1.ioc klicken. Unter Connectivity/USB kann ich dann DEVICE (FS) aktivieren, also ein Full-Speed USB-Gerät mit einer Bitrate von 12 MHz.



Im Abschnitte Middleware/USB_Device wähle ich dann die Communication Devide Class (Virtual COM Port). Middleware ist die hardwarenahe Software. Ich bin sehr dankbar, dass ich das ganze Zeug nicht selbst schreiben muss.



Zurück in der Pin-Übersicht finde ich nun die beiden USB-Leitungen an PA11 und PA12. Und weil ich gerade da bin, wähle ich auch noch den Pin PA0 als analogen Eingang ADC1_IN0 aus.




Es folgt noch ein Ausflug in die Clock Configuration. Zuerst werden da Probleme gemeldet. Die IDE bietet aber an, sie zu lösen. Wenn man sie lässt, gibt es ein längeres Geflacker mit allen möglichen Einstellungen. Und am Ende kommt heraus, dass die Quarzfrequenz 8 MHz mithilfe der PLL zu 48 MHz wird, was ja wohl für die USB-Signalverarbeitung nötig ist. Diese 48 MHz finde ich dann später am Pin PA8.



Ein Klick auf das weiße Zahnrad setzt wieder alle Einstellungen in den Code um. Was man dann noch selbst schreiben muss, ist sehr wenig. Ich hatte allerdings zuerst einige Mühe, die richtige Syntax herauszufinden, besonders was den AD-Wandler anging. Das Ziel war, ADC-Werte von PA0 zu lesen und über den USB zu senden. Die entscheidende Hilfe habe ich in  einem PDF bei STM gefunden: Description of STM32F1 HAL and Low-layer drivers, en.DM00154093.pdf. Entscheidende Tipps zum Umgang mit USB hatte ich schon von Leander bekommen:

USB Konfigurieren und senden

·        Im Reiter „Connectivity“ unter Kategorie „USB“ den Haken bei „Device (FS)“ setzen

·        Im Reiter „Middleware“ unter Kategorie „USB_DEVICE“ mit dem Dropdownmenü („Class For FS IP“ die Option „Communication Device Class (Virtual Port Com)“ auswählen

Der STM32 meldet sich dann später als COM-Port an und kann mit beliebiger Baudrate (9600 bis 11500 Baud erfolgreich getestet) geöffnet werden.

Befehlsliste:

·        void : CDC_Transmit_FS(Buf, Len);

Zum Senden von Strings (Char-Arrays) über den USB-Port.

Für Buf muss der zu sendende String als Integer gecasted übergeben werden (es kann auch ein Char-Array übergeben werden, was jedoch zu einer Warnung vom Compiler führt).

Für Len muss die Größe des Arrays übergeben werden.

Bsp.:

char hello[] = " Hello\r\n";
CDC_Transmit_FS((
uint8_t*) text, strlen(text));

Wichtig: Nach dem Befehl zum Senden werden die Daten an das Hardwaremodul übergeben und neue Übertragungen können erst nach dem vollständigen Senden entgegengenommen werden.

Nach jedem Flashen muss das Board einmal vom USB-Port getrennt werden!

Ob neue Daten gesendet werden können, ist nicht ganz einfach abzufragen. Am simpelsten ist das Hinzufügen von einer eigenen Funktion in der USB-Library im Projektordner:

·        Im Projektverzeichnis in der Datei „Core/USB_DEVICE/App/usbd_cdc_if.h“ zwischen
/*USER CODE BEGIN EXPORTED_FUNCTIONS*/“ und „/*USER CODE END EXPORTED_FUNCTIONS*/“ (ca. bei Zeile 110) folgende Funktionsdefinition einfügen:

 

/* USER CODE BEGIN EXPORTED_FUNCTIONS */

uint8_t CDC_getReady(void);

/* USER CODE END EXPORTED_FUNCTIONS */

 

·        Im dazugehörigen Sourcefile „Core/USB_DEVICE/App/usbd_cdc_if.c“ in einem User-Bereich folgenden Codeblock einfügen:

 

uint8_t CDC_getReady(void){

USBD_CDC_HandleTypeDef *hcdc = (USBD_CDC_HandleTypeDef*)hUsbDeviceFS.pClassData;

if (hcdc->TxState != 0){

return USBD_BUSY;

}else{

return USBD_OK;

}

}

 

Die Funktion gibt „USBD_BUSY“ (=1) zurück, wenn der Sendevorgang noch nicht beendet ist und „USBD_OK“ (=0), wenn die Hardwareschnittstelle bereit für neue Daten ist.

 Am Ende kam dieser einfache Code dabei heraus:

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  uint32_t ADCValue;
  char Buffer[] = "              ";
  while (1)
  {
     HAL_ADC_Start(&hadc1);
     if (HAL_ADC_PollForConversion(&hadc1, 10) == HAL_OK){
         ADCValue = HAL_ADC_GetValue(&hadc1);
     }
     HAL_ADC_Stop(&hadc1);
      sprintf(Buffer,"%d \r\n", ADCValue);
      if (CDC_getReady()==USBD_OK){
          CDC_Transmit_FS((uint8_t*) Buffer, strlen(Buffer));
      }
    //  HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13);
    //  HAL_Delay(200);
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}




Beim Kompilieren gibt es nun keinen Fehler und drei Warnungen. Nach dem Start des Debuggers (grüner Käfer) und Resume (F8) kann ich das USB-Kabel anstecken. Es wird eine virtuelle COM gebildet, bei mir COM10. Nun kann ich die serielle Console starten (Symbol im blauen Punkt). Im Fenster werden nun die Messwerte sichtbar. Wenn man den Debugger beendet, verschwindet auch die Console. Achtung, nach jedem Neustart muss der USB-Stecker neu verbunden werden, um die virtuelle COM zu starten.



Nun kann auch andere Software eingesetzt werden. Mit dem Seriellen Plotter der Arduino-IDE ergibt sich ein einfaches Einkanal-Oszilloskop mit durchlaufendem Bild. Die Baudrate kann beliebig eingestellt werden, BluePill akzeptiert alles. Das hier dargestellte Signal hatte eine Frequenz von 100 Hz. Daraus ergibt sich eine Abtastrate von rund 8 kHz ohne Zwischenspeicherung. Da ist sicher noch einiges zu machen.






Elektronik-Labor  Projekte