USB und ADC mit der STM32 Cube-IDE
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.