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)
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:
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.
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.
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.
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.
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):
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);
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
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.
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-none-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"?
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.
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?
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
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".