Forum: Mikrocontroller und Digitale Elektronik STM8 und ADC


von Ralph S. (jjflash)


Angehängte Dateien:

Lesenswert?

Im Rahmen der Erweiterung meiner "Steckbretthelferleins" wollte ich ein 
weiteres Modul hinzufügen das eine Art "Voltmeter" fürs Steckbrett ist. 
Mit einem STM32F030 hab ich das schon auf einem Steckbrett realisiert 
und funktioniert.

Jetzt dachte ich mir: Gut, das müßte ja auch mit einem STM8S103 
funktionieren (mit 10 statt 12 Bit Auflösung könnte ich gut leben). Nach 
einem schnellen Aufbau dachte ich schon, ich wäre am Ziel, aber nach 
einer Überprüfung der Genauigkeit war ich ernüchtert !

Also allen möglichen Ballast weggeworfen, nur die allernötigste Hardware 
(ohne Eingangsspannungsteiler), Software auf Mindestmaß reduziert.

Ergebnis: Ungenügende Genauigkeit.

Minimalschaltung und Minimalprogramm im Anhang, verwendeter Compiler 
SDCC v3.6.

Problem:

Unterhalb von 0,2V Eingangsspannung zuckt der ADC mit keinem einzigen 
Bit (insgesamt mit 3 verschiedenen STM8S103 und einem STM8S003 
getestet).

Von 0 .. 0,2V liefert der ADC den Wert 0. Danach nähert er sich dem 
wirklichen Spannungswert immer mehr an. Bei einer Eingangsspanunng von 
2,0V gibt es eine Abweichung von ca. 75mV. Erst die maximale 
Eingangsspannung von 3,3V wird richtig angegeben.

Das Referenzvoltmeter ist ein Fluke 45 (im Januar diesen Jahres frisch 
kalibriert).

Gerade im Bereich von 0,0 bis 1,0V bräuchte ich es genauer, ansonsten 
macht ein Eingangsspannungsteiler keinen Sinn.

Das Auslesen des ADC geschieht mit folgendem Code:
1
#define adc_init(ch)         {  ADC_CSR =  (ch);                  \
2
                                ADC_CR1 |= ADC_CR1_ADON; }
3
4
5
/* ---------------------------------------------------------
6
                           adc_read
7
     liest den 10-Bit ADC und gibt den Messwert als Argument
8
     zurueck (Werte 0..1023)
9
   --------------------------------------------------------- */
10
uint16_t adc_read(void)
11
{
12
  uint16_t  adcvalue;
13
14
  ADC_CR1 |= ADC_CR1_ADON;            // AD-Wandlung starten
15
  while (!(ADC_CSR & ADC_CSR_EOX));   // warten bis Conversion beendet
16
  delay_us(5);                        // Warten bis Wert gelesen werden kann
17
18
  adcvalue = (ADC_DRH << 2);          // die unteren 2 Bit
19
  adcvalue += ADC_DRL;                // die oberen 8 Bit
20
21
  return adcvalue;
22
}

Woran liegt der Fehler und / oder ist es sinnvoller, mein 
Steckbrettvoltmeter dann doch eher mit einem STM32F030 zu realisieren?

von pegel (Gast)


Lesenswert?

Es gibt eine Appnote dazu. Kennst du die?

http://www.st.com/resource/en/application_note/cd00186359.pdf

von Dr. Sommer (Gast)


Lesenswert?

Ralph S. schrieb:
> oder ist es sinnvoller, mein
> Steckbrettvoltmeter dann doch eher mit einem STM32F030 zu realisieren?
Da der Hardware-USB hat, könntest du dir da auch den CH340G sparen und 
das Helferlein noch kleiner bauen.

von Dr. Sommer (Gast)


Lesenswert?

Oh sorry, hat er gar nicht, das war der STM32F070.

von Ralph S. (jjflash)


Lesenswert?

Dr. Sommer schrieb:
> Da der Hardware-USB hat, könntest du dir da auch den CH340G sparen und
> das Helferlein noch kleiner bauen.

Der CH340 ist hier nur zum Überprüfen, das Helferlein wird ein OLED 
128x64 Display haben und keine serielle Schnittstelle.

pegel schrieb:
> Es gibt eine Appnote dazu. Kennst du die?
>
> http://www.st.com/resource/en/application_note/cd00186359.pdf

Das schau ich mir jetzt gerade im Moment an...

von Ralph S. (jjflash)


Lesenswert?

Zitat aus der App-Note:
1
Offset and gain errors can easily be calibrated by the application firmware. First, apply zero 
2
volts to the ADC input and perform a conversion, then the conversion result represents the 
3
zero offset error. Then perform a gain adjustment. A subsequent offset error calibration may 
4
be required. A useful method for offset and gain calibration is the least square method 
5
(which calculates the smallest error in all the used range)

Wenn es nur mal so "easily" wäre ! Das kann man dann kalibrieren, wenn 
anstelle von 0 Volt eine Spannung "detektiert" wird und man die dann 
schlicht abziehen würde. Das funktioniert aber nicht, wenn der ADC 
schlicht schon gar nicht loslegt und mittels der Referenz kann man an 
diesem Controller auch nicht "spielen" weil die Referenzanschlüsse nicht 
aufgelegt sind.

Oder (was ich schon eher glaube) ich bin nur zu doof die App-Note 
richtig zu lesen !

von Axel S. (a-za-z0-9)


Lesenswert?

Ralph S. schrieb:
1
> adcvalue = (ADC_DRH << 2);          // die unteren 2 Bit
2
> adcvalue += ADC_DRL;                // die oberen 8 Bit

Das stimmt so nicht wirklich. Also mindestens die Kommentare sind 
falsch.

Ich lese das Datenblatt so, daß der Default left-aligned ist 
(ADC_CR2:ALIGN = 0). Die 8 MSB stehen dann in ADC_DRH und die beiden LSB 
in ADC_DRL. Dann ist das, was du programmiert hast, im Prinzip richtig, 
vorausgesetzt, der Compiler macht das Linksschieben auf allen 16 Bit und 
nicht nur auf den 8 Bit aus ADC_DRH. Im Zweifel würde ich das explizit 
formulieren:
1
adcvalue  = (uint16_t)ADC_DRH << 2;
2
adcvalue += ADC_DRL;

oder alternativ
1
adcvalue   = ADC_DRH;
2
adcvalue <<= 2;
3
adcvalue  += ADC_DRL;

von Ralph S. (jjflash)


Lesenswert?

... schön wärs gewesen wenn ich nur n dummen Schiebefehler gemacht hab. 
Der Kommentar ist leider falsch (aber das Schieben richtig). Muß heißen, 
die oberen 2 Bits und die unteren 8 Bits (des 10-Bit Wertes).

Wenn der Compiler hier nicht richtig casten würde, dann würde ich nie 
einen ADC - Result von 1023 haben können (aber ich habs dennoch auch mit 
der alternativen Formulierung versucht, gleiches Problem).

Hab das jetzt erst einmal auf die Seite gelegt und werkel parallel dazu 
am gleichen Helferlein jedoch mit STM32 ... (natürlich wird mich das 
hier jedoch nicht loslassen).

von Martin B. (ratazong)


Lesenswert?

Hallo,


ich kenn das Datenblatt nicht, aber vielleicht liegt es an ADC_DRL.

Was steht denn in den oberen bits von ADC_DRL ?

vielleicht die oberen bits von ADC_DRL vorher explizit nullen

adcvalue   = ADC_DRH;
adcvalue <<= 2;
adcvalue  += (ADC_DRL & 3);

von Ralph S. (jjflash)


Lesenswert?

Das Zusammenschieben des Wertes ist nicht das Problem:

Bis 0,2V liefert der ADC den Wert 0 !!!!

Das zeigt auch die (überflüssiger Weisse gemachte) getrennte Ausgabe von 
ADC_DRH und ADC_DRL.

Bis 0,2V steht in den Registern 0 drin !

von neuer PIC Freund (Gast)


Angehängte Dateien:

Lesenswert?

Frei nach RM0016
1
static void adc_init(void)
2
{
3
    // write reset state
4
    ADC1->CSR = 0;
5
    ADC1->CR1 = 0;
6
    ADC1->CR2 = 0;
7
    ADC1->CR3 = 0;
8
    ADC1->TDRH = 0;
9
    ADC1->TDRL = 0;
10
    ADC1->HTRH = 0xFF;
11
    ADC1->HTRL = 0x03;
12
    ADC1->LTRH = 0;
13
    ADC1->LTRL = 0;
14
    ADC1->AWSRH = 0;
15
    ADC1->AWSRL = 0;
16
    ADC1->AWCRH = 0;
17
    ADC1->AWCRL = 0;
18
    
19
    // ADC clock 1 ... 4 MHz
20
    ADC1->CR1 = (ADC1->CR1 & ~ADC1_CR1_SPSEL) | (0x4 << 4); // 0x4 -> fmaster/8
21
    
22
    // disable schmitt trigger of used channels
23
    /* ToDO */
24
    
25
    // change default alignment
26
    //ADC1->CR2 |= ADC1_CR2_ALIGN;
27
}
28
29
static uint16_t adc_read(uint8_t channel)
30
{
31
    uint16_t adc = 0;
32
    uint8_t current_channel;
33
    
34
    // wake up adc
35
    if (!(ADC1->CR1 & ADC1_CR1_ADON))
36
    {
37
        ADC1->CR1 |= ADC1_CR1_ADON;
38
    }
39
    
40
    // set channel
41
    current_channel = ADC1->CSR & ADC1_CSR_CH;
42
    if (channel != current_channel)
43
    {
44
        ADC1->CSR |= (channel & ADC1_CSR_CH);
45
    }
46
    
47
    // start conversion
48
    ADC1->CSR &= ~(ADC1_CSR_EOC);
49
    ADC1->CR1 |= ADC1_CR1_ADON;
50
    
51
    // wait end of conversion
52
    while (!(ADC1->CSR & ADC1_CSR_EOC))
53
    {
54
        __asm__ ("nop");            // possible breakpoint
55
    }
56
    
57
    // check consistency
58
    if (ADC1->CR3 & ADC1_CR3_OVR)
59
    {
60
        return 0x8000;
61
    }
62
    
63
    // read out data
64
    if (ADC1->CR2 & ADC1_CR2_ALIGN)
65
    {
66
        // right aligned
67
        adc = ADC1->DRL;
68
        adc |= (ADC1->DRH << 8);
69
    }
70
    else
71
    {
72
        // left aligned
73
        adc = (ADC1->DRH << 2);
74
        adc |= ADC1->DRL;
75
    }
76
    
77
    return adc;
78
}
79
80
81
MAINLOOP:
82
    adc = adc_read(3);      // channel 3 == PD2
83
    
84
    uart_send(0x55);        // 'U'
85
    uart_send(adc >> 8);
86
    uart_send(adc & 0xFF);
87
    uart_send(0x55);
88
    
89
    goto MAINLOOP;

Betrieben bei 3.3V, angesteuert mit Dreiecksignal, welches über 4k7 auf 
PD2 wirkt. V_max(signal) > 3.3V, V_min(signal) < 0.0V, daher das 
clipping.

von Ralph S. (jjflash)


Lesenswert?

Hey.... das werde ich doch glatt ausprobieren, allerdings wohl nach 
Weihnachten, die Partnerin ist da und wenn ich jetzt das Elektronikzeugs 
wieder auspacke, dann bekommt sie einen Anfall.

lach, sie sagt immer "Was du nur an den scharzen Käferlein findest, 
die eh kein Mensch mehr sehen kann".

von neuer PIC Freund (Gast)


Lesenswert?

Da fehlt eine Maskierung beim setChannel
1
    // set channel
2
    channel &= ADC1_CSR_CH;
3
    current_channel = ADC1->CSR & ADC1_CSR_CH;
4
    if (channel != current_channel)
5
    {
6
        ADC1->CSR = (ADC1->CSR & ~(ADC1_CSR_CH)) | channel;
7
    }

von (º°)·´¯`·.¸¸.·´¯`·.¸¸.·´¯`·.¸¸.·´¯`·.¸¸.·´¯`·.¸¸.· (Gast)


Lesenswert?

Ein STM8S208RBT6 auf einem Evalbord ist bzgl. der ADs voellig 
unauffaellig.
Vermutlich machst einfach was flasch.

von Ralph S. (jjflash)


Lesenswert?

Schmunzeln muss, ich geh mal schwer davon aus, dass ich etwas falsch 
mache, auf einem STM8S105 ist es auch unauffällig. Ich werde nach 
Weihnachten den Tip von Pic-Freund ausprobieren und ich werde eine 
Prototypenplatine ätzen, vor allem mit sauberen Masseleitungen und nicht 
auf dem Steckbrett. Wenn das dann nicht hilft, gebe ich auf und 
realisiere das endgültig mit einem STM32F030 (vor allem funktioniert 
hier schon die komplette Software und ist sozusagen fertig).

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.