Hey zusammen, bin neu hier im Forum und wollte gleich mal euer Wissen auf die Probe stellen ;-) Hab mich in den letzten Wochen mit dem STM32F4 Discovery Board ein wenig angefreundet. Nun möchte ich die Spannungswerte von 5 Sensoren über einen ADC (ADC1) einlesen und als Wert auf dem LCD ausgeben. Da die drei vorhandenen ADC's ja die Möglichkeit besitzen mit Hilfe einer erzeugten "Regular Group" mehrere analoge Eingänge zu nutzen habe ich diese Variante versucht umzusetzen. Allerdings habe ich wohl irgendwo einen Fehler eingebaut. Zwar erhalte ich aktuell einen Wert auf dem Display, der ist aber viel höher als erwartet. Außerdem wird er nicht aktualisiert. An den 5 initialisierten PIN's (PC1-PC5) liegen Spannungen von 0-3 V an. Vor allem verstehe ich noch nicht so ganz wie die Werte der 5 Channels automatisch nacheinander (kontinuierlich) ausgelesen und in Variablen geschrieben werden. Bei Driller & Co habe ich natürlich auch schon gesucht allerdings gibt es dort nur Beispiele für das Auslesen von einem Kanal oder nur Beispiele für STMF1xx. Vielleicht könntet ihr einem Neuling in Sachen µC etwas Hilfe leisten und mal über meinen Code schauen? Wäre nett. Gruß Johannes #include "main.h" int main(void) { SystemInit(); // Quarz Einstellungen aktivieren //Initialisierung der Ports Initclock_GPIO(); InitGPIOC(); //Initialisierung des ADC's Init_ADC1(); ... } //-------------------------------------------------------------- // Initialisierung der GPIO PORTS //-------------------------------------------------------------- void Initclock_GPIO (void) { RCC_HSEConfig(RCC_HSE_ON); while(!RCC_WaitForHSEStartUp()); } // //Initialisierung von GPIOC // void InitGPIOC(void) { //Input definieren für fünf analoge Eingänge PC1(IN11), PC2(IN12), PC3(IN13), PC4(IN14), PC5(IN15) RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE); GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN ; GPIO_Init(GPIOC, &GPIO_InitStructure); } //-------------------------------------------------------------- //Initialisierung des ADC's //-------------------------------------------------------------- void Init_ADC1(void) { ADC_DeInit(); //Reset aller ADC Parameter RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); ADC_CommonInitTypeDef ADC_CommonInitStructure; ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent; ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div8; ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled; ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_5Cycles; ADC_CommonInit(&ADC_CommonInitStructure); ADC_InitTypeDef ADC_InitStructure; ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b; ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConvEdge_None; ADC_InitStructure.ADC_ExternalTrigConv = 0; ADC_InitStructure.ADC_NbrOfConversion = 5; ADC_InitStructure.ADC_ScanConvMode = ENABLE; ADC_AutoInjectedConvCmd(ADC1, DISABLE); ADC_Init(ADC1, &ADC_InitStructure); //Kanäle werden manuell der Regular Group (bis 16 Kanäle) zugefügt (ADCx, Kanal, Reihenfolge zum Durchlaufen,Sample Time) ADC_RegularChannelConfig(ADC1, ADC_Channel_11 ,1 , ADC_SampleTime_15Cycles); //Wählt Kanal 11 aus (PC1) ADC_RegularChannelConfig(ADC1, ADC_Channel_12 ,2 , ADC_SampleTime_15Cycles); //Wählt Kanal 12 aus (PC2) ADC_RegularChannelConfig(ADC1, ADC_Channel_13 ,3 , ADC_SampleTime_15Cycles); //Wählt Kanal 13 aus (PC3) ADC_RegularChannelConfig(ADC1, ADC_Channel_14 ,4 , ADC_SampleTime_15Cycles); //Wählt Kanal 14 aus (PC4) ADC_RegularChannelConfig(ADC1, ADC_Channel_15 ,5 , ADC_SampleTime_15Cycles); //Wählt Kanal 15 aus (PC5) ADC_Cmd(ADC1, ENABLE); //Aktiviert Wandler ADC1 ADC_ContinuousModeCmd(ADC1,ENABLE); ADC_SoftwareStartConv(ADC1); //Triggert erste Wandlung } //-------------------------------------------------------------- // Task 6: ADC Value auslesen //-------------------------------------------------------------- void TSK_ADCValue(void *pvParameters) { while(1) { //Lesen und Ausgeben der Spannung an Sensor #1 ADC_Result1 = ADC_GetConversionValue(ADC1); sprintf(ADC_OutputVoltage,"Voltage sensor #1 = %5d",ADC_Result1); GUI_DispStringAt(ADC_OutputVoltage, 50, 40); //Gleicher Code für die weiteren vier Eingänge } }
Richtig sinnvoll ist es im Scan Mode eigentlich nur, wenn du die DMA benutzt. Dann werden kontinuierlich die Werte des ADC geordnet in einen Struktur geschrieben, hier mal für 4 Kanäle (PA4 ist nur bedingt zu benutzen, weil hier WS des Audiochips angeschlossen ist):
1 | typedef struct ADCValues |
2 | {
|
3 | uint16_t channel0; |
4 | uint16_t channel1; |
5 | uint16_t channel2; |
6 | uint16_t channel3; |
7 | uint16_t spare; |
8 | } ADCValues_t; |
9 | // hier die globale Variable
|
10 | // der DMA Stream schreibt hier rein
|
11 | volatile ADCValues_t ADCRead; |
12 | // ich lasse mal die GPIO Init weg, das hast du ja schon
|
13 | void ADCInit(void) { |
14 | ADC_CommonInitTypeDef ADC_CommonInitStructure; |
15 | ADC_InitTypeDef ADC_InitStructure; |
16 | DMA_InitTypeDef DMA_InitStructure; |
17 | |
18 | RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE); |
19 | //RCC_ADCCLKConfig(RCC_PCLK2_Div8);
|
20 | RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE); |
21 | /* ADC1 configuration
|
22 | * 4 channel continuous
|
23 | */
|
24 | //ADC_DeInit(ADC1);
|
25 | ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent; |
26 | ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div8; |
27 | ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled; |
28 | ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_20Cycles; |
29 | ADC_CommonInit(&ADC_CommonInitStructure); |
30 | |
31 | ADC_StructInit(&ADC_InitStructure); |
32 | ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b; |
33 | ADC_InitStructure.ADC_ScanConvMode = ENABLE; |
34 | ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; |
35 | ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None; |
36 | ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T3_TRGO; |
37 | ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; |
38 | ADC_InitStructure.ADC_NbrOfConversion = 4; //< hier sinds 4 Kanäle |
39 | /* Now do the setup */
|
40 | ADC_Init(ADC1, &ADC_InitStructure); |
41 | /* Enable ADC1 and configure channels very slow for this example*/
|
42 | ADC_RegularChannelConfig(ADC1,ADC_Channel_0,1,ADC_SampleTime_144Cycles); |
43 | ADC_RegularChannelConfig(ADC1,ADC_Channel_1,2,ADC_SampleTime_144Cycles); |
44 | ADC_RegularChannelConfig(ADC1,ADC_Channel_2,3,ADC_SampleTime_144Cycles); |
45 | ADC_RegularChannelConfig(ADC1,ADC_Channel_3,4,ADC_SampleTime_144Cycles); |
46 | |
47 | /* DMA1 channel1 configuration ----------------------------------------------*/
|
48 | DMA_DeInit(DMA2_Stream0); // Clear everything we had |
49 | DMA_StructInit(&DMA_InitStructure); |
50 | DMA_InitStructure.DMA_Channel = DMA_Channel_0; |
51 | DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&(ADC1->DR); // source struct |
52 | DMA_InitStructure.DMA_Memory0BaseAddr = (u32)&ADCRead.channel0; // result memory location |
53 | DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory; // from Peripheral to RAM |
54 | DMA_InitStructure.DMA_BufferSize = 4; /// Note that we have 4 Channels |
55 | DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; // Source stays same address |
56 | DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; // Destination will be incremented |
57 | DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; // 16 bit results |
58 | DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; // yeah same size -> 16 bit |
59 | DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; // Wrap around the destination address |
60 | DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; // |
61 | DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single; |
62 | DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; |
63 | DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable; |
64 | DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull; |
65 | DMA_Init(DMA2_Stream0, &DMA_InitStructure); // Init the stuff finally |
66 | DMA_Cmd(DMA2_Stream0,ENABLE); |
67 | while(DMA_GetCmdStatus(DMA2_Stream0)==DISABLE){}; |
68 | ADC_DMACmd(ADC1, ENABLE); |
69 | ADC_Cmd(ADC1, ENABLE); |
70 | ADC_DMARequestAfterLastTransferCmd(ADC1, ENABLE); |
71 | ADC_ContinuousModeCmd(ADC1,ENABLE); |
72 | ADC_SoftwareStartConv(ADC1); |
73 | }
|
74 | // so , fertig und ADC und DMA laufen jetzt und schreiben brav in die Struct
|
75 | //
|
76 | // Spass in der Hauptschleife - Blauer Knopf schaltet blaue LED
|
77 | |
78 | while(1) { |
79 | if (ADCRead.channel0 > 0x0080) { |
80 | GPIO_SetBits(GPIOD, GPIO_Pin_15); |
81 | } else { |
82 | GPIO_ResetBits(GPIOD, GPIO_Pin_15); |
83 | }
|
84 | }
|
Beachte, das du dir nicht beliebig DMA Stream und Kanal aussuchen kannst, sondern an die Tabelle im Reference Manual gebunden bist. DMA2 Stream 0 Kanal 0 ist nun mal ADC1.
:
Bearbeitet durch User
Noch eins: Ich schicke mir zu Testzwecken AD Kanal 0 und 1 auf den Cirrus Chip um sie mir anzuhören - das hätte ich besser nicht machen sollen. Es knistert und rauscht und ist nicht sehr beeindruckend. Das kann ein guter 10-bit Wandler besser als der STM32 12-bitter. Für sehr genaue Ergebnisse ist der Wandler nicht so dolle.
Super, viele Dank schon mal für deine Hilfe :-) Werde es wohl dann einmal mit der DMA versuchen. Ist ja eigentlich auch nicht viel mehr Arbeit die noch mit reinzuziehen. Falls wer noch andere Vorschläge oder Lösungsansätze hat, die sind natürlich auch gerne gesehen :P Gruß Johannes
Johannes W. schrieb: > Falls wer noch andere Vorschläge oder Lösungsansätze hat, die sind > natürlich auch gerne gesehen :P OK, hier ist noch einer - der wird dir aber auch nicht so gefallen und kostet ein wenig Rechenzeit. Du aktivierst den 'ADC-Fertig' Interrupt und speicherst im IRQ Handler den Wert aus dem DR Register in die entsprechende Variable. Ist aber eigentlich Unsinn, denn die DMA kostet eben keine Rechenzeit und verhaspelt sich auch nicht in den Kanälen, was beim IRQ Ansatz passieren kann, wenn du nicht schon genau weisst, welcher Kanal denn nun gerade gewandelt wurde oder du das Kanal Register abfragst. Die DMA läuft gut mit dem ADC (leider nicht mit dem S***ss I2S :-(), und ST selber empfiehlt dieses Vorgehen. STM32 hat eben leider nur ein Ergebnis Register für den ADC vorgesehen.
Okok du hast mich überzeugt :-) Werde in den nächsten Tagen mal die DMA mit einbinden. Anscheinend ist das ja wirklich die einzige sinnvolle Lösung. Vielen Dank nochmals für die Infos. Gruß Johannes
Also ich hab es nun endlich geschafft die DMA einzubinden. Auch werden wie gewünscht alle fünf Werte auf dem Bildschirm ausgegeben. Hab bisher nur an einem Kanal eine Spannung anliegen die circa zwischen 0 und 3 V variiert. Das Problem nun: Kanal 1 gibt gewandelte Werte aus von circa 200 bis 3700 (plausibel) Kanal 2-5 müssten theoretisch alle den Wert 4095 ausgeben da keine Spannung anliegt. Allerdings zeigen Kanal 1,2 und 3 die gleichen Werte an die sich auch gleichzeitig verändern. Kanal 4 und 5 sind wie zu erwarten bei 4095. Und noch ein Problem: Das ganze kommt zu einem Stack Overflow der ganz willkürlich auftritt. Das Programm springt aus der While-Schleife in der die gewandelten Werte in Variablen geschrieben werden in die Funktion "void vApplicationStackOverflowHook". Hab das ganze etwas "entzerrt" indem ich kurze Delays zwischen den einzelnen Schritten eingebracht hab. Dadurch kommt es nicht mehr so häufig zum Absturz. Muss die Frequenz zur AD Wandlung speziell angepasst werden? Das mach ich doch eigentlich schon mit dem Prescaler oder? Wäre supernett wenn sich nochmals jemand die Mühe machen könnte über meinen Code zu schauen. #include "main.h" //Struktur zum Auslesen der fünf Sensoren typedef struct ADCValues{ uint16_t channel_11 ; uint16_t channel_12 ; uint16_t channel_13 ; uint16_t channel_14 ; uint16_t channel_15 ; } ADCValues_t; //DMA Stream schreibt hier rein volatile ADCValues_t ADCRead; //Variablen in denen ausgegebene Spannung der Sensoren gespeichert wird static uint16_t ADC_Result_1 = 0; static uint16_t ADC_Result_2 = 0; static uint16_t ADC_Result_3 = 0; static uint16_t ADC_Result_4 = 0; static uint16_t ADC_Result_5 = 0; //-------------------------------------------------------------- // Einsprungpunkt des Programms //-------------------------------------------------------------- int main(void) { SystemInit(); //Initialisierung der Ports Initclock_GPIO(); InitGPIOA(); InitGPIOC(); InitGPIOG(); //Initialisierung des ADC1 und DMA2 Init_ADC1(); // Start Task, generiert alle anderen Tasks, löscht sich im Anschluss selbst xTaskCreate( TSK_StartJob, ( const char * ) "startjob", 20*configMINIMAL_STACK_SIZE, NULL, 1, &xstartjob ); // FreeRTOS Scheduler starten vTaskStartScheduler(); // wird nie erreicht!! while(1) { } } //-------------------------------------------------------------- // Initialisierung der GPIO PORTS //-------------------------------------------------------------- void int()...... //-------------------------------------------------------------- //Initialisierung des ADC's //-------------------------------------------------------------- void Init_ADC1(void) { ADC_DeInit(); //Resetet alle ADC Einstellungen RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); ADC_CommonInitTypeDef ADC_CommonInitStructure; ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent; ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div8; ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled; ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_20Cycles; ADC_CommonInit(&ADC_CommonInitStructure); ADC_InitTypeDef ADC_InitStructure; ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b; //Eingangsspannung in 12bit Wert mit max. Wert von 4096 ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; //Kontinuierliche Auswertung aller Regular-Kanäle (in Schleife) ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //wird _left _right bündig in 16 Bit Register abgespeichert ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConvEdge_None; //Signal welches die Wandlung auslösen soll ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T3_TRGO; ADC_InitStructure.ADC_NbrOfConversion = 5; //Anzahl der Kanäle in der "Regular Channel Groupe" ADC_InitStructure.ADC_ScanConvMode = ENABLE; // ENABLE: Mehrere Kanäle werden im Scan-Modus gewandelt ADC_AutoInjectedConvCmd(ADC1, DISABLE); //ENABLE: Nach abgearbeiteter Regular Group wird automatisch Injected Group angetriggert ADC_Init(ADC1, &ADC_InitStructure); //Kanäle werden manuell der Regular Group (bis 16 Kanäle) zugefügt (ADCx, Kanal, Reihenfolge zum Durchlaufen,Sample Time) ADC_RegularChannelConfig(ADC1, ADC_Channel_11 ,1 , ADC_SampleTime_144Cycles); //Wählt Kanal 11 aus (PC1) ADC_RegularChannelConfig(ADC1, ADC_Channel_12 ,2 , ADC_SampleTime_144Cycles); //Wählt Kanal 12 aus (PC2) ADC_RegularChannelConfig(ADC1, ADC_Channel_13 ,3 , ADC_SampleTime_144Cycles); //Wählt Kanal 13 aus (PC3) ADC_RegularChannelConfig(ADC1, ADC_Channel_14 ,4 , ADC_SampleTime_144Cycles); //Wählt Kanal 14 aus (PC4) ADC_RegularChannelConfig(ADC1, ADC_Channel_15 ,5 , ADC_SampleTime_144Cycles); //Wählt Kanal 15 aus (PC5) DMA_DeInit(DMA2_Stream0); //Resetet alle DMA Einstellungen RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE); DMA_InitTypeDef DMA_InitStructure; DMA_StructInit(&DMA_InitStructure); DMA_InitStructure.DMA_Channel = DMA_Channel_0; DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&(ADC1->DR); // source struct DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t) &ADCRead; // result memory location DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory; // from Peripheral to RAM DMA_InitStructure.DMA_BufferSize = 5; // 5 vorhandene Kanäle mit CNY 70 Sensor DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; // Source stays same address DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; // Destination will be incremented DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; // 16 bit results DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; // yeah same size -> 16 bit DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; // Wrap around the destination address DMA_InitStructure.DMA_Priority = DMA_Priority_High; // DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single; DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable; DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull; DMA_Init(DMA2_Stream0, &DMA_InitStructure); // Init the stuff finally DMA_Cmd(DMA2_Stream0,ENABLE); //Aktiviert DMA while(DMA_GetCmdStatus(DMA2_Stream0)==DISABLE){}; ADC_DMACmd(ADC1, ENABLE); ADC_Cmd(ADC1, ENABLE); //Aktiviert Wandler ADC1 ADC_DMARequestAfterLastTransferCmd(ADC1, ENABLE); ADC_ContinuousModeCmd(ADC1,ENABLE); ADC_SoftwareStartConv(ADC1); //Triggert erste Wandlung } //-------------------------------------------------------------- // Task 6: ADC Value auslesen //-------------------------------------------------------------- void TSK_ADCValue(void *pvParameters) { char buffer[40]; while(1) { ADC_Result_1 = ADCRead.channel_11; sprintf(buffer,"Voltage Sensor #1 = %5d",ADC_Result_1); GUI_DispStringAt(buffer, 50, 80); ADC_Result_1 = 0; vTaskDelay(100); ADC_Result_2 = ADCRead.channel_12; sprintf(buffer,"Voltage Sensor #2 = %5d",ADC_Result_2); GUI_DispStringAt(buffer, 50, 100); ADC_Result_2 = 0; vTaskDelay(100); ADC_Result_3 = ADCRead.channel_13; sprintf(buffer,"Voltage Sensor #3 = %5d",ADC_Result_3); GUI_DispStringAt(buffer, 50, 120); ADC_Result_3 = 0; vTaskDelay(100); ADC_Result_4 = ADCRead.channel_14; sprintf(buffer,"Voltage Sensor #4 = %5d",ADC_Result_4); GUI_DispStringAt(buffer, 50, 140); ADC_Result_4 = 0; vTaskDelay(100); ADC_Result_5 = ADCRead.channel_15; sprintf(buffer,"Voltage Sensor #5 = %5d",ADC_Result_5); GUI_DispStringAt(buffer, 50, 160); ADC_Result_5 = 0; vTaskDelay(100); } }
Ich programmiere ohne RTOS und habe den AD Wandler schon tagelang laufen lassen, ohne das es zu einem Stacküberlauf kam.Geht ja auch nicht, denn nur die paar RAM Zellen des AD Structs werden von der DMA beschrieben. Ich kann mir nur vorstellen (keine Ahnung von RTOS), das das Betriebssystem nicht genug abarbeiten kann, während es mit dem Programm vollgestopft wird. sprintf ist vermutlich auch nicht gerade das schnellste Unterprogramm aller Zeiten. Informiere dich bei RTOS darüber, ob es evtl. Konflikte mit der DMA geben kann und ob das ein Blocking- oder Nonblocking 'vTaskDelay' ist. Das 'Mitziehen' von Nachbarkanälen hingegen ist bei allen AD Wandlern mit Mux im Eingang ein bekanntes Problem. Der S&H Kondensator hält seine Ladung eben beim Umschalten auf einen offenen Eingang und wird dann fröhlich auch auf diesem Kanal gemessen. Verhindern kannst du das nur, indem die AD Eingänge niederohmig angesteuert werden und in der Lage sind, den S&H Kondensator zügig auf ihr Level zu bringen. Code postet du übrigens am besten eingeschlossen in die Codetags mit eckigen Klammern und 'c'.
:
Bearbeitet durch User
Alles klar. Vielen Dank für deine schnelle Antwort. Das mit dem Posten des Code's ist mir leider erst zu spät aufgefallen :p Ich melde mich wieder sobald es was neues gibt. Gruß Johannes
Wie groß ist configMINIMAL_STACK_SIZE? Es gibt in freeRTOS eine Funktion, um den Stack zu überprüfen. Bau die mal ein. Gruß, Stefan
Aktuelle Einstellungen sind wie folgt:
1 | #define configCHECK_FOR_STACK_OVERFLOW 2 // Variante wie der Stack Overflow überprüft wird --> Methode 2 (siehe freertos.org API)
|
1 | #define configMINIMAL_STACK_SIZE ( ( unsigned short ) 130 ) // kleinste erlaubte Stackgröße
|
Werde mein Glück mal morgen mit der genannten Funktion versuchen. Das 'vTaskDelay(x)' ist meines Wissens ein Blocking-Delay. Allerdings läuft der Tasks ohne die eingebauten Delays nicht einmal durch und hängt sich gleich nach dem Flashen/Starten des Programmes auf. Das mit der niederohmigen Ansteuerung ist auch noch ein guter Tipp. Allerdings hab ich grade einmal einen 4,7 kOhm Widerstand als Spannungsteiler verbaut. Evtl. baue ich die Schaltung auch noch etwas um. Gruß Johannes
Johannes W. schrieb: > Allerdings hab ich grade einmal einen 4,7 kOhm Widerstand als > Spannungsteiler verbaut. Evtl. baue ich die Schaltung auch noch etwas > um. Für mein derzeitiges Projekt mit Audio benutze ich OpAmps der Kopfhörer-Verstärker Sorte und geh mit den Ausgängen direkt auf den F407. Das ist evtl. übertrieben, aber dadurch habe ich zwischen Kanal 0 und Kanal 1 eine ausgezeichnete Kanaltrennung. (Und ja, ich weiss, das 12-bit nicht gut genug sind - ist nur zum grundsätzlichen Testen). Johannes W. schrieb: > Allerdings > läuft der Tasks ohne die eingebauten Delays nicht einmal durch Das klingt aber komisch. Dieses GUI Dings wird evtl. völlig überfordert?
Also hab jetzt glaube ich endlich die "Lösung" für mein Problem gefunden. Hab mir mal Stefan's Denkanstoß zu Herzen genommen. Im FreeRTOSConfig.h ist ja die minimale stack size festgelegt.
1 | #define configMINIMAL_STACK_SIZE ( ( unsigned short ) 128 )
|
Hab sie bei der Erstellung des Tasks:
1 | xTaskCreate( TSK_ADCValue,( const char * ) "ow", configMINIMAL_STACK_SIZE+256, NULL, 1, NULL ); |
einfach ein wenig vergrößert und mal eine Variable mitzählen lassen. Bis jetzt ist der Task bereits 10000 mal durchgelaufen ohne das er sich "aufgehängt" hat oder es zu anderen Problemen gekommen ist. Ich denke das Problem mit dem 'Mitziehen' der Nachbarkanäle kann ich mit Matthias's Vorschlag beheben und einfach die AD Eingänge niederohmig ansteuern. Trotzdem habe ich noch eine Frage. Die Stack Size ist doch nichts anderes als der bereitgestellte Speicher für lokal definierte Variablen und Adressen oder? Welche Nachteile hat man dann von einer höheren Stack Size? Sind die Nachteile daraus überhaupt spürbar? Danke nochmal an alle die mich auf den "richtigen Weg" gebracht haben ;-) Gruß Johannes
Nachtrag: Und die vTaskDelays die ich vorher im Task anbringen musste damit dieser überhaupt gelaufen ist konnte ich von je 100 ms auf 10 ms reduzieren. Evtl. kann ich sie sogar ganz entfallen lassen bei entsprechender Anpassung (was ja eigentlich auch Sinn der ganzen Sache ist) Gruß Johannes
Hi zusammen, hab jetzt mal wieder endlich etwas Zeit gefunden mein kleines Projekt weiterzuführen. Hab allerdings immernoch das Problem, dass meine fünf Kanäle unterschiedliche Werte auf dem Display ausgeben. Ohne dass ein Signal an den fünf Pins anliegt: Wert1: toggelt so bei 3000 Wert2: toggelt so bei 2500 Wert3: toggelt so bei 2100 Wert4: relativ stabil bei 4090 Wert5: relativ stabil bei 4060 Also sind aktuell nur die Werte 4 und 5 realistisch da ja keine Spannung am ADC anliegt. Kann mir jemand sagen was die Ursache hierfür sein kann? Als Pins habe ich PC1 (Ch1), PC2 (Ch2), PC3 (Ch3), PC4 (Ch4) und PC5 (Ch5) ausgewählt. Die sind laut Datasheet alle für ADC1 zu verwenden. Kann das Problem an einer falschen Wahl der PINs liegen oder an einer vergessenen/fehlerhaften Initialisierung? Wäre nett wenn Ihr eine Idee habt oder das gleiche Problem schon einmal hattet. Gruß Johannes
PIN's sind natürlich: PC1 (Ch11), PC2 (Ch12), PC3 (Ch13), PC4 (Ch14) und PC5 (Ch15)
Johannes schrieb: > Als Pins habe ich PC1 (Ch1), PC2 (Ch2), PC3 (Ch3), PC4 (Ch4) und PC5 > (Ch5) ausgewählt. Die sind laut Datasheet alle für ADC1 zu verwenden. Die kannst du als Inputports benutzen, aber PC0 = ADC1->Ch10 oder ADC2->Ch10 oder ADC3->Ch10 PC1 = ADC1->Ch11 " ADC2->Ch11 oder ADC3->Ch11 d.h. du musst Channel 10 bis Channel 14 abfragen. usw. Beachte dabei, das auf PC0 auch die PowerOn LED fürs USB-OTG liegt, auf PC3 liegt DOUT vom Mikrofon. Fünf Kanäle am Stück ohne Doppelbelegung sind mit dem Discovery F4 nicht drin. Am besten ist noch PA0-PA3, wobei auf PA0 der blaue Button liegt, der auch gleich zum AD Test dienen kann, da er den Eingang auf + zieht beim Drücken. Hier die F4 Disco Pinbelegung zum Ausdrucken, gemacht von U.B.: http://mikrocontroller.bplaced.net/wordpress/wp-content/uploads/2013/03/Pinbelegung_v101.html
>Beachte dabei, das auf PC0 auch die PowerOn LED fürs USB-OTG liegt, auf >PC3 liegt DOUT vom Mikrofon. Ja genau das ist mir eben gestern auch aufgefallen. Auf Grund der Übersichtlichkeit und der einfacheren Definition wollte ich eben 5 PINs in Folge machen. Na gut dann werde ich mir wohl mal "komplett freie" Ports suchen und es mit denen versuchen. Verwunderlich ist eben, dass zwei Kanäle korrekte Werte anzeigen und drei nicht. Hab gestern mal zum Test nacheinander GND auf die fünf Eingänge gelegt. Bei zwei Ports ist der ausgegebene digitale Wert ohne Beeinflussung der Nachbarkanäle auf Null gesprungen. Bei den drei restlichen Kanälen hat es die Nachbarkanäle mit nach unten gezogen. Hab mit die Pinbelegung bereits aus dem Ref Manual bzw. dem Datasheet geholt aber deine Tabelle ist wesentlich übersichtlicher. Danke nochmals für deine Hilfe ich melde mich sobald ich Neuigkeiten zu berichten habe :-)
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.