Ich versuche gerade, 4 ADC-Kanäle IN04-IN07 auf PA04-PA07 eines
STM32F103C8T6 einzulesen. Wenn ich mir das DMA-Zielarray in einer
Schleife im Programm anschaue, so springen die Werte auf ihren Plätzen.
(an PA4 habe ich ein Poti und das auf 0 gedreht). Ich würde ja erwarten,
daß nur der eine Wert am festen Platz modifiziert wird.
1
uint16_t adc_value[4];
2
static void MX_ADC1_Init(void)
3
{
4
5
ADC_ChannelConfTypeDef sConfig = {0};
6
7
/** Common config */
8
hadc1.Instance = ADC1;
9
hadc1.Init.ScanConvMode = ADC_SCAN_ENABLE;
10
hadc1.Init.ContinuousConvMode = ENABLE;
11
hadc1.Init.DiscontinuousConvMode = DISABLE;
12
hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
13
hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
14
hadc1.Init.NbrOfConversion = 4;
15
if (HAL_ADC_Init(&hadc1) != HAL_OK)
16
{
17
Error_Handler();
18
}
19
/** Configure Regular Channel */
20
sConfig.Channel = ADC_CHANNEL_4;
21
sConfig.Rank = ADC_REGULAR_RANK_1;
22
sConfig.SamplingTime = ADC_SAMPLETIME_1CYCLE_5;
23
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
24
{
25
Error_Handler();
26
}
27
/** Configure Regular Channel */
28
sConfig.Channel = ADC_CHANNEL_5;
29
sConfig.Rank = ADC_REGULAR_RANK_2;
30
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
31
{
32
Error_Handler();
33
}
34
/** Configure Regular Channel */
35
sConfig.Channel = ADC_CHANNEL_6;
36
sConfig.Rank = ADC_REGULAR_RANK_3;
37
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
38
{
39
Error_Handler();
40
}
41
/** Configure Regular Channel */
42
sConfig.Channel = ADC_CHANNEL_7;
43
sConfig.Rank = ADC_REGULAR_RANK_4;
44
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
Harry L. schrieb:> Schau dir das mal an:> https://www.youtube.com/watch?v=0jKtgP4OYvU
War heute schon meine Morgenlektüre. Und die Behauptung dort, WORD statt
HALF WORD sei erforderlich für 12bit ADC-Konversion, ist falsch. Ich
habe HALF WORD genommen und mein DMA-Buffer sind 4 16bit Worte.
Und nach diesem Video habe ich meine Anwedung auch erstellt. Habe jetzt
auch noch mal mit WORD statt HALFWORD probiert. Keine Änderung. Mit 2
blue pills (beides cheapo chinese) probiert.
Werde es jetzt noch mal auf einem STM32407G DISC1 probieren.
Hier ein Auszug aus meiner Library, ich verende keine HAL-Funktionen.
Mit "void STM32F1_ADC_init(void)" wird der ADC so konfiguriert, das er
kontinuierlich die ersten 3 (ist über ein define anpassbar) Channels
einliest und es über dem DMA im Ram ablegt.
Mit "uint16 STM32F1_ADC_getRegularGroupe1(uint8_fast channel)" kann man
jeweils den letzten gemessenen ADC-Wert abfragen.
Läuft komplett im Hintergrund, und dank DMA ohne Interrupt Routinen.
Die meisten STM32-ADCs haben 4+1 Register für die Ergebnisse, also
braucht man für max. 5 Kanäle kein DMA. Ob man die Daten aus dem RAM
oder aus den ADC-Registern abholt, ist doch relativ egal.
Danke an Christian(k.) für die Zurverfügungstellung seiner
Library-Routinen. Es ist sicher gut, wenn man alles auf Low-Level
versteht und nur damit kann man im Grunde das Funktionieren erzwingen.
Denn das Manual ist schließlich sowas wie die "Bibel".
Ich werde versuchen, zu einem späteren Zeitpunkt da mich noch mal
hineinzuknien, um es mit DMA zu lösen, weil das auch m.E. die
flexibelste Methode ist und man einige Sachen leichter machen kann wie
double buffering und Durchschnittswertbildung. Ich bin doch jetzt erst
mal @(bauformb)s Rat gefolgt und habe es mit diskreten
Konversionsaufrufen gelöst. Es ist wirklich manchmal zum Haareraufen,
wenn man dann z.B. auf ein Tutorial, wie dieses-
https://youtu.be/5l-b6lsubBE stößt und darin werden dann erst mal die
HAL_Routinen zerpflückt und noch einige Parameter, die MX erzeugt hat,
händisch geändert (wehe, man muß MX noch mal wegen was anderem
aufrufen). Zum Schluß - nachdem ich erst noch meine blue pill mit
STM32duino verifiziert habe (wieviel leichter kommt da ein
analogRead(analogPin) daher) - funktionierte es irgendwie, daß meine 4
Werte in den richtigen Töpfen landeten.
Danke jedenfalls noch mal.
1
/* USER CODE BEGIN 0 */
2
void ADC_Select_CH0(void) {
3
ADC_ChannelConfTypeDef sConfig = {0};
4
sConfig.Channel = ADC_CHANNEL_4;
5
sConfig.Rank = 1;
6
sConfig.SamplingTime = ADC_SAMPLETIME_28CYCLES_5;
7
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
8
{
9
Error_Handler();
10
}
11
}
12
/** Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time.
13
*/
14
void ADC_Select_CH1(void) {
15
ADC_ChannelConfTypeDef sConfig = {0};
16
sConfig.Channel = ADC_CHANNEL_5;
17
sConfig.Rank = 1;
18
sConfig.SamplingTime = ADC_SAMPLETIME_28CYCLES_5;
19
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
20
{
21
Error_Handler();
22
}
23
24
}
25
/** Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time.
26
*/
27
void ADC_Select_CH2(void) {
28
ADC_ChannelConfTypeDef sConfig = {0};
29
sConfig.Channel = ADC_CHANNEL_6;
30
sConfig.Rank = 1;
31
sConfig.SamplingTime = ADC_SAMPLETIME_28CYCLES_5;
32
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
33
{
34
Error_Handler();
35
}
36
37
}
38
/** Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time.
39
*/
40
void ADC_Select_CH3(void) {
41
ADC_ChannelConfTypeDef sConfig = {0};
42
sConfig.Channel = ADC_CHANNEL_7;
43
sConfig.Rank = 1;
44
sConfig.SamplingTime = ADC_SAMPLETIME_28CYCLES_5;
45
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
Genauso wenig wie du den sinnvollen Hinweis "Längeren Sourcecode nicht
im Text einfügen, sondern als Dateianhang" verstanden hast, hast du die
HAL und deren Benutzung verstanden, sonst würdest du so einen Blödsinn:
Christoph K. schrieb:> und darin werden dann erst mal die> HAL_Routinen zerpflückt und noch einige Parameter, die MX erzeugt hat,> händisch geändert (wehe, man muß MX noch mal wegen was anderem> aufrufen).
nicht schreiben.
CubeMX kannst du zu jeder Zeit deinen Code neu generieren lassen, ohne
deinen eigenen Code zu kompromittieren - vorausgesetzt, du hälst dich an
bestimmte Konventionen.
Harry L. schrieb:> Genauso wenig wie du den sinnvollen Hinweis "Längeren Sourcecode nicht> im Text einfügen, sondern als Dateianhang" verstanden hast, hast du die> HAL und deren Benutzung verstanden, sonst würdest du so einen Blödsinn:>
Gut, gebe ich zu. Sollte man nicht machen und lasse ich in Zukunft.
> Christoph K. schrieb:>> und darin werden dann erst mal die>> HAL_Routinen zerpflückt und noch einige Parameter, die MX erzeugt hat,>> händisch geändert (wehe, man muß MX noch mal wegen was anderem>> aufrufen).> nicht schreiben.>> CubeMX kannst du zu jeder Zeit deinen Code neu generieren lassen, ohne> deinen eigenen Code zu kompromittieren - vorausgesetzt, du hälst dich an> bestimmte Konventionen.
Zum Thema "Blödsinn": in dem Tutorial, auf das ich mich bezog, wurde
nachträglich händisch noch ein Parameter in der MX_ADC1_Init() geändert.
Und genau bei dieser neuen Codegenerierung wird dann diese Routine
wieder überschrieben. Nichts anderes meinte ich. Daß der Usercode
geschützt ist durch die Kommentarbereiche, ist klar.
Christoph K. schrieb:> Und genau bei dieser neuen Codegenerierung wird dann diese Routine> wieder überschrieben. Nichts anderes meinte ich.
Deswegen kann man die Parameter ja auch in CubeMX setzen, um zu
verhindern, dass sie überschrieben werden.
Rahul D. schrieb:> Christoph K. schrieb:>> Und genau bei dieser neuen Codegenerierung wird dann diese Routine>> wieder überschrieben. Nichts anderes meinte ich.>> Deswegen kann man die Parameter ja auch in CubeMX setzen, um zu> verhindern, dass sie überschrieben werden.
Aber es schien mir hier - so wurde es im Tutorial vorgeführt -, daß
zunächst die 4 eingegeben werden muß im MX, damit die 4
Channelinitialisierungen generiert werden. Die wurden dann in der Init
auskommentiert, daraus 4 einzelne gemacht und die 4 wurde dann wieder in
eine 1 geändert.
Christoph K. schrieb:> Aber es schien mir hier - so wurde es im Tutorial vorgeführt -, daß> zunächst die 4 eingegeben werden muß im MX, damit die 4> Channelinitialisierungen generiert werden. Die wurden dann in der Init> auskommentiert, daraus 4 einzelne gemacht und die 4 wurde dann wieder in> eine 1 geändert.
Frei nach Hulk: "Mickriges Tutorial"
Wenn darin Sachen gemacht werden, ohne sie zu erklären.
Oder manb sie unhinterfragt einfach so übernimmt.
Ich komme noch mal auf den low level code von christiankpunkt zurück,
nachdem ich mit allen Versuchen mit HAL und LL gescheitert bin. Ich
kriege es einfach nicht hin, 4 ADC-Werte an PA4-PA7 auszulesen. Habe die
Hardware (STM32F103C8T6, blue pill) noch mal mit STM32duino verifiziert.
Aber mit STM32CubeIDE klappt es einfach nicht. Ich bekomme immer 4
gleiche Werte. Jetzt habe ich Christians Code in ein
STM32CubeIDE-Projekt (main.c) eingebaut. Funktioniert aber immer noch
nicht. Immer 4 ähnliche Werte im Bereich um 2048. Sie müßten aber etwa
sowas ergeben (nur ein Eingang an Poti, die anderen 3 an GND):
Christoph K. schrieb:> Wie lade ich am besten das ganze Projekt als CubeIDE-Projekt hoch, daß> sich das vielleicht mal jemand ansehen kann? Als ZIP?
Ganzen Projekt-Ordner zippen, vorher ein Clean Project auslösen.
Wastl schrieb:> Christoph K. schrieb:>> Wie lade ich am besten das ganze Projekt als CubeIDE-Projekt hoch, daß>> sich das vielleicht mal jemand ansehen kann? Als ZIP?>> Ganzen Projekt-Ordner zippen, vorher ein Clean Project auslösen.
Danke. Hatte ich dann auch so gemacht und meinen Beitrag editiert :)
Christoph K. schrieb:> Ich komme noch mal auf den low level code von christiankpunkt zurück
STM32F1_DMA1_configChannel1ForADC1() und STM32F1_DMA1_startChannel1()
werden von STM32F1_ADC_init() aufgerufen.
Ruf die STM32F1_ADC_init() erst nach dem STM32F1_DMA_init() Funktionen
auf.
Bei STM32F1_ADC_getRegularGroupe1(...) hat der erste Channel den Index 0
Wenn du nicht die ADC0 bis 2 nimmst, must du den setSQR(...) Aufruf
anpassen, also für ADC4 bis 7:
setSQR(ADC1, REGULAR_GROUP_COUNT - 1, 4, 5, 6, 7, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0);
Und setze das define REGULAR_GROUP_COUNT auf 4
Christian schrieb:
...
> Bei STM32F1_ADC_getRegularGroupe1(...) hat der erste Channel den Index 0>> Wenn du nicht die ADC0 bis 2 nimmst, must du den setSQR(...) Aufruf> anpassen, also für ADC4 bis 7:> setSQR(ADC1, REGULAR_GROUP_COUNT - 1, 4, 5, 6, 7, 0, 0, 0, 0, 0, 0, 0,> 0, 0, 0, 0, 0);>> Und setze das define REGULAR_GROUP_COUNT auf 4
Danke. Das hat schon viel gebracht. Der DMA-Puffer (adc_value[]) sieht
nun gut aus. Die Rückgabewerte von STM32F1_ADC_getRegularGroupe1(...)
sind allerdings noch nicht in Ordnung. Habe es mit uint16_t und uint32_t
versucht.
Sehe gerade, daß in der STM32F1_ADC_init bereits ein
gemacht wird. Und außerhalb noch mal?
OK, läuft jetzt. Da gibt es ja schon den DMA-Puffer regularGroup1[]. Der
war nur static und im main nicht bekannt. Jetzt nehme ich den Puffer und
es funktioniert jetzt. Danke noch mal.
Und noch was zum Verständnis: das Ganze läuft ohne DMA-Interrupt,
richtig?
Kann man die Rate, mit der der DMA läuft, irgendwie steuern? Hängt ja
mit der ADC-Samplerate zusammen, nehme ich an.
Läuft ohne Interrupt.
Der DMA wird automatisch vom ADC getriggert, wenn der mit dem Sampling
fertig ist.
Mein Code ist halt sehr Hardware nah. Muss man dann natürlich ins
Datenblatt schauen um zu wissen was die Parameter machen.
Achja...ich vergass:
* Zip entpacken
* Im CubeIDE "Import existing projekt" und Verz. wählen
* .ioc-Datei öffen
* Code neu generieren
Danach sollte das vollständig sein.
Harry L. schrieb:> Achja...ich vergass:> * Zip entpacken> * Im CubeIDE "Import existing projekt" und Verz. wählen> * .ioc-Datei öffen> * Code neu generieren>> Danach sollte das vollständig sein.
Krieg beim starten des Debug launchers:
/Applications/STM32CubeIDE.app/Contents/Eclipse/plugins/com.st.stm32cube
.ide.mcu.externaltools.gnu-tools-for-stm32.10.3-2021.10.macos64_1.0.200.
202301161003/tools/bin/../lib/gcc/arm-none-eabi/10.3.1/../../../../arm-n
one-eabi/bin/ld: warning: cannot find entry symbol Reset_Handler;
defaulting to 0000000008000000
Liegt's vielleicht daran, daß ich ein ST32F103C8T6 habe und das .ioc für
R8Tx ist? Kann ich die MCU im .ioc umschalten, sozusagen "mitten im
Strom die Pferde wechseln"?
Christoph K. schrieb:> Kann ich die MCU im .ioc umschalten, sozusagen "mitten im> Strom die Pferde wechseln"?
Nein, das geht leider nicht.
Dann hilft wohl nur ein neues Projekt anzulegen, und alle Einstellungen
des ADC0 und TIM3 exakt zu übernehmen.
Die main.c solltest du verwenden können.
Das Einfachste wird es sein, aus dem .ioc-Projekt einen PDF-Report uzu
generieren.
Da sollte alles Notwendige enthalten sein.
PDF-Report. Ja, nützliche Sache. Kann man im Project-Manager ein Projekt
kopieren/duplizieren (so, wie es in anderen Baumansichten möglich ist) ?
Geht wohl nicht so einfach. Habe im Filesystem den ganzen Baum kopiert,
umbenannt und dann importiert.
Harry L. schrieb:> Christoph K. schrieb:>...> Diese Callbacks wirst du in der HAL überall finden.> Die wersden aus den Interrupts heraus aufgerufen.
Z.T. muß man die aber auch händisch dort eintragen oder nicht?
> Für 4 Kanäle sollte da auch 4 stehen.
Wo stand der denn jetzt noch? Ich hatte das schon gesehen und
korrigiert. Steht das jetzt in dem hochgeladenen Projekt noch so?
Christoph K. schrieb:> Harry L. schrieb:>> Christoph K. schrieb:>>...>>> Diese Callbacks wirst du in der HAL überall finden.>> Die wersden aus den Interrupts heraus aufgerufen.>> Z.T. muß man die aber auch händisch dort eintragen oder nicht?
Nein, muß man nicht.
Die Funktionen gibt es bereits mit einer __weak-Deklaration
Sobald du die Funktion selbst definierst, überschreibst du die.
Christoph K. schrieb:> Harry L. schrieb:>> Offensichtlich hast du versäumt, die anzahl der DMA-Zyklen anzupassen.>>> HAL_ADC_Start_DMA(&hadc1, (uint32_t*) &adc_buffer, 5);>> ^>>>> Für 4 Kanäle sollte da auch 4 stehen.>> Wo stand der denn jetzt noch? Ich hatte das schon gesehen und> korrigiert. Steht das jetzt in dem hochgeladenen Projekt noch so?
Direkt vor der Loop in main()
Harry L. schrieb:> Christoph K. schrieb:>> Harry L. schrieb:>>> Offensichtlich hast du versäumt, die anzahl der DMA-Zyklen anzupassen.>>>> HAL_ADC_Start_DMA(&hadc1, (uint32_t*) &adc_buffer, 5);>>> ^>>>>>> Für 4 Kanäle sollte da auch 4 stehen.>>>> Wo stand der denn jetzt noch? Ich hatte das schon gesehen und>> korrigiert. Steht das jetzt in dem hochgeladenen Projekt noch so?>> Direkt vor der Loop in main()
Entschuldige, ich habe jetzt mein eigenes Projekt noch mal runtergeladen
und im main.c vor der while() loop nachgeguckt: da steht
Harry L. schrieb:> Dein Timer und ADC waren noch falsch konfiguriert.
Der Fehler mit dem springenden DMA-Bufferplatz besteht weiter. Zumindest
im Debugger in der Expression View. Nein, auch im Programm selbst. Bei
allen weiteren Zyklen landet der ADC-Wert im adc_buffer[3].
1
while(1){
2
if(data_ready){
3
// do something with adc-data
4
if(adc_buffer[0]>100){
5
(bkpt)adc_buffer[1]=555;
6
}
7
data_ready=0;
8
}
9
10
}
Beim ersten Lauf stoppt das Programm bei bkpt. Danach kommt es nie mehr
dahin. Schon sehr merkwürdig. Wird ja wohl nicht an meinem BMP GDB
Debugger liegen?
Christoph K. schrieb:> Wird ja wohl nicht an meinem BMP GDB> Debugger liegen?
Mit Debugger mußt du nach jedem Stop neu reseten, sonst kommt die
Kanalzuordnung durcheinander.
Ich hab solche DMA Geschichten schon oft gemacht, allerdings nicht mit
HAL.Dort hab ich aber den DMA Interrupt ausgewertet und musste den DMA
Zähler und pointer für Wechselbuffer neu setzen. Weiss nicht ob dir das
der HAL abnimmt.
Grüsse
Gebhard R. schrieb:> Christoph K. schrieb:>> Wird ja wohl nicht an meinem BMP GDB>> Debugger liegen?>> Mit Debugger mußt du nach jedem Stop neu reseten, sonst kommt die> Kanalzuordnung durcheinander.> Ich hab solche DMA Geschichten schon oft gemacht, allerdings nicht mit> HAL.Dort hab ich aber den DMA Interrupt ausgewertet und musste den DMA> Zähler und pointer für Wechselbuffer neu setzen. Weiss nicht ob dir das> der HAL abnimmt.> Grüsse
Danke für den Hinweis. Was genau meinst Du mit "resetten".