Hallo zusammen,
ich bin aktuell dabei, mit einem STM32 und einem externen 16 bit ADC ein
Hamamatsu Mikro-Spektrometer auszulesen. Das klappt soweit auch ganz
gut, leider habe ich Probleme damit, die Daten aus dem STM
herauszubekommen. Geplant war, jeweils ein Spektrum einzulesen und
anschließend die Daten per U(S)ART zur weiteren Verarbeitung auszugeben.
Klingt eigentlich ganz simpel. Solange ich in der Hauptschleife des
Programms nichts per U(S)ART ausgebe, klappt das auch wunderbar, im
Debugger stehen auch plausible Werte im Array. Sobald ich jedoch die
Werte (ohne oder mit Delays davor oder danach) per U(S)ART ausgebe,
zerhackt es mir das Timing komplett und es kommt (logischerweise) nur
Rauschen im seriellen Terminal an, da nun nicht mehr das Spektrum
abgetastet wird, sondern nur Rauschen.
Ich bin momentan überfragt, bin mir aber sehr sicher, dass es ein ganz
offensichtlicher Fehler ist.
Anbei zwei Fotos vom Oszillogramm (einmal ohne UART Transmit und daher
mit mehr oder weniger übereinanderliegendem "Abtasttakt" und Spektrum,
so soll es aussehen) und einmal mit UART Transmit und komplett relativ
zum Spektrum verschobenen "Abtasttakt".
Ich habe mal alles in ein Github Repo geladen:
https://github.com/neitmedia/spectrodue
Ich habe das Projekt mit STM32CubeMX generiert und der U(S)ART
funktioniert ja auch einwandfrei, ich kann problemlos mit 1000000 Baud
Daten senden.
Ich zweifle also daran, dass da noch irgendein RCC aktiviert werden
muss, wenn die Übertragung als solches funktioniert. Das einzige Problem
ist wie gesagt dieses dann auftretende Timingproblem in der
Hauptschleife.
Die SPI-Kommunikation geht dann völlig kaputt.
ok sorry, habe ich missverstanden. Der UART ist offensichtlich ok.
Das nächste wonach man gucken sollte ist ob die SPI HAL Funktionen
irgendwie fortwährend Interrupts disablen.
uint16_t temp;
HAL_UART_Transmit(&huart1, &temp, 2, 1000);
Klappt bei mir auf der Blue Pill
Wichtig ist das "&" vor der Variable, sonst kommt bei mir auch nur
Bullshit raus.
Ich habe printf auf den U(S)ART umgebogen, hier:
PUTCHAR_PROTOTYPE
{
HAL_USART_Transmit(&husart1, (uint8_t *)&ch, 1, 0xFFFF);
return ch;
}
An sich funktioniert ja sowohl der U(S)ART als auch die
SPI-Kommunikation mit dem ADC. Ich kann sogar in der Initfunktion etwas
auf den USART schreiben und danach problemlos SPI Kommunikation machen,
nur wenn ich innerhalb der Hauptschleife zuerst paar GPIOs setze, dann
paar __nop()s ausführe und anschließend noch ein paar GPIOs setze, dann
SPI_Receive mache und anschließend den abgerufenen Wert per UART
ausgebe, zerhackt es mit das Timing komplett.
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
// Sende Startpuls an Spektrometerbaustein
HAL_GPIO_WritePin(ST_GPIO_Port, ST_Pin, GPIO_PIN_SET);
for (uint16_t i = 0; i < 10000; i++) {
__nop();
}
HAL_GPIO_WritePin(ST_GPIO_Port, ST_Pin, GPIO_PIN_RESET);
for (uint16_t i = 0; i < 10000; i++) {
__nop();
}
for (uint16_t i=0;i<288;i++) {
werte[i] = ADS8860_ReadValue();
}
HAL_Delay(10);
printf("%d\n", werte[150]); <-- mit dieser Zeile ist das Timing
kaputt, ohne diese Zeile klappt alles wunderbar. Siehe Oszillogramme.
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
Hm, sind die USARTS in der HAL interupt getrieben oder busy wait
getrieben? Wie liegen die Interruptprioritäten? An welcher Stelle wird
an USART4 geschrieben?
Was passiert, wenn Du das printf aufspaltest in
{
unsigned char aData[20];
sprintf(aData,"%d\n");
printf(aData);
}
sehr wahrscheinlich wird das Timing erst beim printf in die Hecken gehen
(der Test nur, um das Problem sicher auf den USART1 einzugrenzen).
Ich würde die Ausgabe von printf an die ITM statt an einen USART
ausgeben, das ist in jedem Fall wesentlich weniger invasiv.
Nico schrieb:> for (uint16_t i=0;i<288;i++) {> werte[i] = ADS8860_ReadValue();> }
Das sieht für mich nach der klassischen Aufgabe für einen
Timerinterrupt, oder, noch besser, wenn man die Pinwackelei in den Griff
bekommt, nach einem Job für DMA aus, gleich nachdem klar ist, das es
nicht zu einem Konflikt in den GPIOs kommt.
Ich wünschte, das HAL nicht so eine Plappertasche wäre, dann wäre es
etwas übersichtlicher. Mein Ansatz aber wäre, eine der beiden Aufgaben
deutlich in den Hintergrund zu schieben, oder automatisch zu bearbeiten.
USART müsste auf jeden Fall gehen, wenn sich die Pins nicht ins Gehege
kommen.
Nico schrieb:> printf("%d\n", werte[150]);
Du möchtest aber wirklich nur Zelle 150 ausgeben? printf kann übrigens
selber ganz schön dauern, wenn du da eine grosse Version benutzt.
Ruediger A. schrieb:> Wie liegen die Interruptprioritäten?
Außer EXTI0 finde ich keinen weiteren Interrupt, der zudem auch noch die
(default) höchste Priorität hat.
Matthias S. schrieb:> Ich wünschte, das HAL nicht so eine Plappertasche wäre, dann wäre es> etwas übersichtlicher.
Hinzu kommt, daß das ganze Programm wohl auch keine Struktur hat.
Erklärende Kommentare fehlen.
Ja, Timer-Interrupt und DMA wäre auf jeden Fall eine Idee für eine
spätere Implementation.
Mir gings jetzt erstmal darum, überhaupt ein Spektrum rauszubekommen. Es
ist auch erstmal völlig egal, wie lange es dauert, die Spektren
rauszuschreiben, hauptsache ich habe da mal was in der Hand.
Ich hatte es schonmal per Timer-Interrupt probiert, aber da ging
überhaupt nichts, da hat er erst gar keine SPI-Kommunikation gemacht,
wenn ich in der ISR versucht habe, das ADS8860_ReadValue() auszuführen.
Und wie gesagt, im Debugger stehen ja jetzt schonmal die richtigen
Werte, wäre nur schön, wenn ich die per UART irgendwie standardisiert
nach draußen bekäme.
Ja, das mit der 150 ist korrekt, das war nur zum Test. Selbst das klappt
ja nicht. Selbst wenn ich was statisches rausprinte, das Timing ist
sofort im Eimer.
Für den undokumentierten Code muss ich mich entschuldigen, man sieht
leider sehr, dass da bisher nur rumgetestet wurde.
Seriellen Buffer anlegen und dort rein schreiben.
Schreib- und LeseIndex entsprechend mitführen.
Denn Buffer dann Zeichenweise in der Hauptschleife OHNE HAL senden.
Immer wenn USART verfügbar ist das nächste Zeichen transmitten
Wenn nicht frei nicht transmitten und weiter in der loop.
Braucht 2 Zeilen welche man in der HAL Transmit findet.
Abfrage ob frei
Transfer des nächsten Zeichen
Und in der main-loop kein HAL_Delay(10) einbauen sondern die CPU pennen
legen, der SysTick wird in spätestens jede ms wecken.
Das Pennenlegen läßt sich dann Fallweise noch unterdrücken wenn 1ms nix
tuen unerwünscht ist.
Matthias S. schrieb:> void uart_put(char data){> while ((USART1->SR & USART_FLAG_TXE) == 0);> USART1->DR = data;> }
Auf die Art und Weise habe ich jetzt zumindest mal Daten rausgeplottet.
Sieht schon ganz gut aus - ich werde dennoch nicht ohne Interrupts/DMA
auskommen.
Vielen Dank allen, die sich hier beteiligt haben!
Nico schrieb:> Auf die Art und Weise habe ich jetzt zumindest mal Daten rausgeplottet.
Immerhin. Das ist einer der Gründe, warum ich HAL kein bisschen mag. Ich
hätte mein derzeitiges Projekt damit niemals hinbekommen, bei dem beide
USB Cores, TFT, ADCs und diverse andere Peripherie zusammen arbeiten
müssen.
Da ist mir die SPL doch deutlich lieber.