Forum: Mikrocontroller und Digitale Elektronik STMF4 ADC 5 Kanäle mit Regular Channel Group auslesen


von Johannes W. (sly_joey)


Angehängte Dateien:

Lesenswert?

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

  }

}

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

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
von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

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.

von Johannes W. (sly_joey)


Lesenswert?

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

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

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.

von Johannes W. (sly_joey)


Lesenswert?

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

von Johannes W. (sly_joey)


Lesenswert?

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);

  }

}

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

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
von Johannes W. (sly_joey)


Lesenswert?

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

von Stefan (Gast)


Lesenswert?

Wie groß ist configMINIMAL_STACK_SIZE?

Es gibt in freeRTOS eine Funktion, um den Stack zu überprüfen. Bau die 
mal ein.

Gruß, Stefan

von Johannes W. (sly_joey)


Lesenswert?

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

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

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?

von Johannes W. (sly_joey)


Lesenswert?

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

von Johannes W. (sly_joey)


Lesenswert?

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

von Johannes (Gast)


Lesenswert?

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

von Johannes (Gast)


Lesenswert?

PIN's sind natürlich:

PC1 (Ch11), PC2 (Ch12), PC3 (Ch13), PC4 (Ch14) und PC5 (Ch15)

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

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

von Johannes W. (sly_joey)


Lesenswert?

>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
Noch kein Account? Hier anmelden.