Hallo,
ich bin ein Anfänger in Sachen µC und beschäfitge mich gerade mit dem
ADC Wandler.
Simpler Versuchsaufbau:
Attiny44 an ADC0 ein 10k Poti als Spannungsteiler (max. VCC), um dort
eine Spannung drauf zu geben.
Als Referenzspannung wurde VCC gewählt (geht beim Attiny44). Wenn ich
mit dem Multimeter messe, liegt am Attiny44 VCC=4,73V an. Diese werden
aus 12V mit einem 7805 erzeugt.
Ich habe nun ein einfaches Programm mit dem angepassten Sourcecode aus
dem Tutorial geschrieben (Mittelwertbildung aus 4 Ergebnissen).
Die Funktion habe ich readADCWert genannt.
Nun rechne ich diese in mV um und prüfe testweise, ob bei einer Spannung
< 2500mV auch eine LED angeht.
Nun geht diese LED aber schon bei gemessenen 2,37V an. Liegt das daran,
dass die Referenzspannung nur gemessen 4,73V ist? Oder warum ist der ADC
"so ungenau"? Wenn ich mit dem Taschenrechner rechne und nehme anstelle
von 5V V_Ref nur 4,73V, käme ich auf 2,365V, ws den gemessenen 2,37V
sehr Nahe kommt.
Sourcecode:
1 | #include <stdint.h>
|
2 | #include <avr/io.h>
|
3 | #include <avr/interrupt.h>
|
4 | #include <util/delay.h>
|
5 | #include <inttypes.h>
|
6 | #ifndef F_CPU
|
7 | #define F_CPU 8000000UL // processor clock frequency 8Mhz
|
8 | #endif
|
9 | #define Referenzspannung 5 // 5 Volt Referenzspannung
|
10 | #define LED PA1 // Pin LED active low
|
11 |
|
12 | int main(void){
|
13 | //variablen für das Programm
|
14 | uint16_t ADCWert; // ADC-Wert in ADC Skalierung 0...1024
|
15 | uint16_t ADCWertmV; // ADC-Wert in milli Volt
|
16 |
|
17 | uint16_t readADCWert (uint8_t mux); // Liest den ADC-Wert ein und gibt den Wert zurück
|
18 |
|
19 | DDRA = 0x00;
|
20 | DDRA |= (1 < <DDA1);
|
21 | PORTA |= (1 << LED);
|
22 | ADCWert = readADCWert (0); // liest den eingestellten ADC-Wert ein an ADC0 = PA0 ein
|
23 | ADCWertmV = ( (uint32_t) ADCWert*Referenzspannung*1000)/1024; // Umrechnung des ADC-Wertes in mV (Faktor 1000)
|
24 | if (ADCWertmV >= 2500){
|
25 | PORTA &= ~( 1 << LED ); // LED einschalten
|
26 | for (y=0; y < 10; y++)
|
27 | {
|
28 | _delay_ms(100);
|
29 | }
|
30 | PORTA |= ( 1 << LED ); // LED einschalten
|
31 | }
|
32 | }
|
33 |
|
34 | // Funktion readADCWert
|
35 |
|
36 | int16_t readADCWert (uint8_t mux) {
|
37 |
|
38 | uint8_t i; // Schleifenzähler
|
39 | uint16_t result;
|
40 |
|
41 | ADMUX = mux;
|
42 | ADMUX &= (~(1<<REFS1) | (1<<REFS0)); // VCC als Ref
|
43 |
|
44 | ADCSRA = (1<<ADEN) | (1<<ADPS1) | (1<<ADPS2); // ADC wird aktiviert, Vorteiler wird eingestellt
|
45 |
|
46 | /* nach Aktivieren des ADC wird ein "Dummy-Readout" empfohlen, man liest
|
47 | also einen Wert und verwirft diesen, um den ADC "warmlaufen zu lassen" */
|
48 |
|
49 | ADCSRA |= ( 1 << ADSC ); // startet eine ADC-Wandlung
|
50 | while ( ADCSRA & (1<<ADSC) ) {
|
51 | ; // auf Abschluss der Konvertierung warten,
|
52 | }
|
53 | result = ADCW; // ADCW muss einmal gelesen werden, sonst wird Ergebnis der nächsten Wandlung nicht übernommen.
|
54 |
|
55 | /* Eigentliche Messung */
|
56 | result = 0; // Dummy Readout Messwert verwerfen und result zu 0 setzen für neue Messung
|
57 |
|
58 | for( i=0; i<4; i++ ){ // Schleife für 4 aufeinanderfolgende Werte die in result zunächst aufaddiert werden
|
59 | ADCSRA |= (1<<ADSC); // eine Wandlung "single conversion" startet
|
60 | while ( ADCSRA & (1<<ADSC) ){
|
61 | ; // auf Abschluss der Konvertierung warten
|
62 | }
|
63 | result += ADCW;
|
64 | }
|
65 | ADCSRA &= ~(1<<ADEN); // ADC deaktivieren
|
66 | result /= 4; // Summe durch 4 teilen = arithm. Mittelwert
|
67 |
|
68 | return result;
|
69 | }
|
Achja, noch eine Frage: Ich habe gemerkt, dass es ohne diesen cast gar
nicht geht:
1 | ADCWertmV = ( (uint32_t) ADCWert*Referenzspannung*1000)/1024;
|
Ich hatte Anfangs die Variable ADCWertMV als uint32_t deklariert ohne
cast. Das ging aber nicht. Kann mir das jemand erklären?