Hallo zusammen, ich versuche mich gerade an einem STM32F0 Discovery_Board und habe den ADC in Verwendung. Diesen calibriere ich zu Beginn und erhalte einen Wert von 66. Anschließend gibt mir mein ADC in der Main zyklisch Werte von Pa0 und Vref-Int aus. Danach möchte ich die Spannung an PA0 berechnen mit der Formel aus dem Reference Manual V=(3300V*V_ref_cal*ADC-Data)/(4095*V_ref_data) Die Spannung die ich messen möchte, liegt ca. bei 2.8V. Ich bekomme als Werte ungefähr ADC_Data=3949 und V_ref_data=2569. Mein Ergebnis liegt immer bei 74. Müsste allerdings bei 2800 liegen. Benutze ich eine falsche Formel? Stimmt der Calibration-Wert oder müsste dieser größer sein? Vielleicht sieht jemand meinen Fehler.. Danke für eure Hilfe
Main Code:
1 | while(1) |
2 | {
|
3 | |
4 | /* Test DMA1 TC flag */
|
5 | while((DMA_GetFlagStatus(DMA1_FLAG_TC1)) == RESET ); |
6 | /* Clear DMA TC flag */
|
7 | DMA_ClearFlag(DMA1_FLAG_TC1); |
8 | |
9 | |
10 | /* Convert Vref voltage value in mv */
|
11 | VrefIntVoltmv[x] = (uint32_t)((Buffer[0]* 3300* calib )/(4095*Buffer[1])); |
12 | ADC_StartOfConversion(ADC1); |
13 | |
14 | x++; |
15 | if(x==2000) |
16 | x=0; |
17 | |
18 | |
19 | }
|
20 | }
|
ADC_Konfig:
1 | /* ADC1 Configuration *******************************************************/
|
2 | /* ADCs DeInit */
|
3 | ADC_DeInit(ADC1); |
4 | |
5 | /* Configure the ADC1 in continous mode withe a resolutuion equal to 12 bits*/
|
6 | ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b; |
7 | ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; |
8 | ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_Rising; |
9 | ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T3_TRGO; |
10 | ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; |
11 | ADC_InitStructure.ADC_ScanDirection = ADC_ScanDirection_Upward; |
12 | ADC_Init(ADC1, &ADC_InitStructure); |
13 | |
14 | /* Convert the ADC1 Channel 0 with 1.5 Cycles as sampling time */
|
15 | ADC_ChannelConfig(ADC1, ADC_Channel_0 , ADC_SampleTime_1_5Cycles); |
16 | |
17 | /* Convert the ADC1 Vref with 1.5 Cycles as sampling time */
|
18 | ADC_ChannelConfig(ADC1, ADC_Channel_Vrefint , ADC_SampleTime_1_5Cycles); |
19 | |
20 | |
21 | /* ADC Calibration */
|
22 | calib=ADC_GetCalibrationFactor(ADC1); |
23 | |
24 | |
25 | |
26 | /* Enable the auto delay feature */
|
27 | // ADC_WaitModeCmd(ADC1, ENABLE);
|
28 | |
29 | /* Enable the Auto power off mode */
|
30 | // ADC_AutoPowerOffCmd(ADC1, ENABLE);
|
31 | |
32 | /* Enable ADCperipheral[PerIdx] */
|
33 | ADC_Cmd(ADC1, ENABLE); |
34 | ADC_VrefintCmd(ENABLE); |
35 | |
36 | /* Warten bis ADC enabled */
|
37 | while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_ADEN)); |
38 | |
39 | /* Enable ADC_DMA */
|
40 | ADC_DMACmd(ADC1, ENABLE); |
41 | |
42 | |
43 | |
44 | /* TIM3 starten */
|
45 | TIM_Cmd(TIM3, ENABLE); |
46 | |
47 | /* ADC1 regular Software Start Conv */
|
48 | ADC_StartOfConversion(ADC1); |
49 | }
|
Setzt Du für v_ref_cal den richtigen Wert ein? Der steht im Flash: Aus STM32F071 datasheet: 3.10.2 Internal voltage reference (VREFINT) The internal voltage reference (VREFINT) provides a stable (bandgap) voltage output for the ADC. VREFINT is internally connected to the ADC_IN17 input channel. The precise voltage of VREFINT is individually measured for each part by ST during production test and stored in the system memory area. It is accessible in read-only mode. Calibration value name Memory address ----------------------------------------------------------- VREFINT_CAL 0x1FFF F7BA - 0x1FFF F7BB Ich habe den Eindruck, dass im Reference manual Offset-calibration und VREF-calibration-factor etwas unglücklich zusammengemischt werden. Gruß, Stefan
V_ref_cal ---------- V_ref_data müssen einen Faktor von ca. 1.0 ergeben. Viele Grüße, Stefan
Stefan K. schrieb: > Setzt Du für v_ref_cal den richtigen Wert ein? Der steht im Flash: Laut dir nicht. Ich dachte immer, ich setze den Wert meiner Kalibrierung zu Beginn der ADC-Konfigurierung ein. So ist das aus dem Datenblatt für mich ersichtlich. Ich benutze allerdings einen STM32F051. In dem Reference Manual steht das nicht so deutlich. Das das so ist, dachte ich mir eigentlich auch schon. Habe allerdings noch keinen Wert für Vref_CAL gefunden, ausser eben den Wert, den ich nach der Kalibrierung erhalte. Danke schonmal für deinen Einwand, dann muss ich wohl noch weiter suchen...
Matthias H. schrieb: > Laut dir nicht. Ich dachte immer, ich setze den Wert meiner Kalibrierung > zu Beginn der ADC-Konfigurierung ein. So ist das aus dem Datenblatt für > mich ersichtlich. Ja, das dachte ich auch zuerst. Allerdings ist der Wert, der nach der ADC-Kalibration gelesen soll, ja anscheinend ein DC-Offset, erstens wegen seiner Größe (Bit 0..6, also 0 .. 127) und zweitens wegen dem Satz in 13.4.1: "The internal analog calibration is kept if the ADC is disabled (ADEN=0). When the ADC operating conditions change (VDDA changes are the main contributor to ADC offset variations and temperature change to a lesser extend), it is recommended to re-run a calibration cycle." Die Werte V_ref_cal und V_ref_data werden in der von Dir benutzten Formel aber als Gain-Korrektur verwendet. Meiner Meinung nach musst Du den Vref Channel messen und diesen Wert als V_ref_data in Deine Formel einsetzen. Für V_ref_cal wird der Wert aus dem Flash genommen. Dieser Wert wurde von ST für diesen speziellen Chip berechnet und stellt den individuellen Korrekturfaktor für die interne Referenzspannungsquelle dar. Viele Grüße, Sefan
Matthias H. schrieb: > In dem Reference Manual steht > das nicht so deutlich. Wer lessen kann ist klar im Vorteil. Im reference manual steht nichts, allerdings im Datenblatt: VREFINT_CAL 0x1FFFF7BA-0x1FFFF7BB Allerdings ist diese Variable nicht in der CooCox-Umgebung vorhanden.
Matthias H. schrieb: > Ich bekomme als > Werte ungefähr ADC_Data=3949 und V_ref_data=2569. 2569/4095 * 3.3V sind etwa 2V. Da kann also schon mal etwas nicht stimmen, denn das Bandgap Element liegt bei etwa 1.2V Welchen Wert kriegst du für calib? Edit: Du rufst ja auch schon ADC_GetCalibrationFactor auf. Je nach Interpretation des Reference Manuals könnte man zum Schluss gelangen, dass danach die Korrektur schon intern angewendet wird. (siehe ADCAL Abschnitt)
:
Bearbeitet durch User
Felix U. schrieb: > Matthias H. schrieb: >> Ich bekomme als >> Werte ungefähr ADC_Data=3949 und V_ref_data=2569. > > 2569/4095 * 3.3V sind etwa 2V. Da kann also schon mal etwas nicht > stimmen, denn das Bandgap Element liegt bei etwa 1.2V > Welchen Wert kriegst du für calib? Habe die SR erhöht und erhalte 1692 für Vref_Data. Bei 2,978 VDDA ergibt das 1,23 was relative genau ist. Daran liegt es nicht. Mir fehlt der Wert von V_REF_CAL
Felix U. schrieb: > Du rufst ja auch schon ADC_GetCalibrationFactor auf. Je nach > Interpretation des Reference Manuals könnte man zum Schluss gelangen, > dass danach die Korrektur schon intern angewendet wird. (siehe ADCAL > Abschnitt). Das denke ich auch. Der hier ermittelte Wert wird ja auch an keiner weiteren Stelle von Reference oder datasheet mehr erwähnt. Matthias H. schrieb: > Mir fehlt der Wert von V_REF_CAL Dessen Adresse sollte in einer der CMSIS Dateien definiert sein. Viele Grüße, Stefan
Felix U. schrieb: > Edit: > Du rufst ja auch schon ADC_GetCalibrationFactor auf. Je nach > Interpretation des Reference Manuals könnte man zum Schluss gelangen, > dass danach die Korrektur schon intern angewendet wird. (siehe ADCAL > Abschnitt) Nein. Mache ich es ohne Rechnung mit VrefInt komme ich auf Werte wie 3.1V anstatt 2.8V. Ist ja auch klar, da ich mit VDDA=3.3V rechne und nicht mit 2.978V Stefan K. schrieb: > Dessen Adresse sollte in einer der CMSIS Dateien definiert sein. Nein habe alles durchsucht. Ich habe es probiert mit: #define VREFINT_ADDR 0x1FFFF7BA uint16_t Vrefcal Vrefcal=*((uint16_t*)VREFINT_ADDR funktioniert immer noch nicht...
Matthias H. schrieb: > Nein. Mache ich es ohne Rechnung mit VrefInt komme ich auf Werte wie > 3.1V anstatt 2.8V. Ist ja auch klar, da ich mit VDDA=3.3V rechne und > nicht mit 2.978V Lösung: Rechne mit 2.978 * ADC_DATA/Fullscale, statt mit 3.3. Die Kalibrierung macht höchstens 0.1 V aus, das ist ja eine Ein-Punkt Korrektur, kann also gar nicht Offset und Linearitätsfehler berücksichtigen. Welchen Wert liest du denn an der Speicheradresse aus? Dann kann man da mal einen Plausibilitätscheck machen.
:
Bearbeitet durch User
Felix meinte die Offset-Korrektur. Hast Du einen jtag-Debugger dran? dann schau Dir doch mal den Memory-Bereich bei 0x1FFFF7BA an. Gruß, Stefan
Felix U. schrieb: > Lösung: Rechne mit 2.978 * ADC_DATA/Fullscale, statt mit 3.3. Die > Kalibrierung macht höchstens 0.1 V aus, das ist ja eine Ein-Punkt > Korrektur, kann also gar nicht Offset und Linearitätsfehler > berücksichtigen. Nein, in der späteren Anwendung kenne ich mein VDD nämlich nicht mehr. Ich habe es jetzt! Der Fehler war im Wert von VREF_CAL. Nachdem ich den Zugriff auf die Adresse hinbekommen habe, funktioniert alles einwandfrei. Danke für eure Hilfe!
Hallo Gemeinde, vielen Dank auch nochmal von mir für den wirklich nutzbringenden Hinweis, dass das in der ST-Doku erwähnte "VREFINT_CAL" nicht der Wert ist, welcher in der Standard Peripheral Library von ADC_GetCalibrationFactor() zurückgegeben wird - was mein Vorgänger ja ebenfalls glaubte. Stundenlang hab ich auch damit zugebracht herauszufinden, warum einfach keine meiner Berechnungen mit den zur Verfügung stehenden Werten zur richtigen Referenzspannung führen wollte. Mathe 3 hab ich zwar noch vor mir, aber so sehr bin ich nun doch nicht auf den Kopf gefallen XD Ziemlich schade von ST, nicht genau an der Stelle im Reference Manual, wo die Berechnungen der Referenzspannung aufgeführt ist, einfach nochmal darauf hinzuweisen, an welcher Adresse das "richtige" VREFINT_CAL zu finden ist. Ich benutze im übrigen einen STM32F030CC und mein Test-Code sieht (für die Nachwelt) wie folgt aus:
1 | // Configure ADC pins
|
2 | GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; |
3 | GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN; |
4 | GPIO_InitStructure.GPIO_OType = GPIO_OType_OD; |
5 | GPIO_Init(GPIOA, &GPIO_InitStructure); |
6 | |
7 | // Configure ADC
|
8 | ADC_DeInit(ADC1); |
9 | RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); |
10 | ADC_ClockModeConfig(ADC1, ADC_ClockMode_SynClkDiv4); |
11 | ADC_StructInit(&ADC_InitStructure); |
12 | ADC_Init(ADC1, &ADC_InitStructure); |
13 | |
14 | // Save the calibration factor for later temperature calculation
|
15 | adcCalibFactor = ADC_GetCalibrationFactor(ADC1); |
16 | |
17 | ADC_VrefintCmd(ENABLE); |
18 | ADC_Cmd(ADC1, ENABLE); |
19 | while (ADC_GetFlagStatus(ADC1, ADC_FLAG_ADRDY) != SET); |
20 | |
21 | // Get the applied reference voltage
|
22 | ADC_ChannelConfig(ADC1, ADC_Channel_Vrefint, ADC_SampleTime_239_5Cycles); |
23 | i = 5000; |
24 | while (i--) { |
25 | ADC_StartOfConversion(ADC1); |
26 | while (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) != SET); |
27 | adcRefVoltage += ADC_GetConversionValue(ADC1); |
28 | }
|
29 | adcRefVoltage /= 5000; |
30 | adcRefVoltage = 3.3 * (*(uint16_t *)(0x1ffff7ba)) / adcRefVoltage; |
31 | |
32 | // Clear all selected channels since there is no StdPeriph command for that
|
33 | ADC1->CHSELR = 0; |
34 | // Configure channel 6 for temperature conversion
|
35 | ADC_ChannelConfig(ADC1, ADC_Channel_6, ADC_SampleTime_239_5Cycles); |
Falls sich jemand darüber wundert, warum ich gleich 5000 Mal den ADC auslese: Es ist wie gesagt nur Test-Code und ich gebe den Spannungswert über ein UART aus. Dabei waren mir die teils sehr unschönen Schwankungen von einigen 10 mV bei einer einzelnen Konvertierung zu nervig anzusehen. Daher habe ich einfach ein bisschen rumexperimentiert und bin bei 5000 geblieben. Macht bei 48 MHz ja sowieso nur 2 ms aus und ist auch in meinem Fall absolut unkritisch :D Und noch ein Hinweis für all diejenigen, die es nicht schon bereits wussten: In der Standard Library setzte die Methode ADC_ChannelConfig() nicht vorher alle Channel zurück, sondern Oder-verknüpft das CHSELR-Register mit den übergebenen Channels. Dies kann dann zum Problem werden, wenn man so wie ich die ADC-Werte durch ein manuelles Auslösen in 5000 Konvertierungen mittelt und dabei versehentlich mehrere Channels aktiv waren. So werden dann nämlich reihum alle Kanäle einmal ausgelesen (entsprechend der Einstellung ADC_ScanDirection) und von all diesen Werten dann das Mittel genommen. Das könnte dann u.U. zu einer nervigen Fehlersuche führen. Gruß Arno
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.