Forum: Mikrocontroller und Digitale Elektronik Analog Wert mit ADC einlesen (LM35 Temperatursensor)


von Julius J. (juliusj)


Lesenswert?

Hallo,


der angehängte Code hat auf einem arduino328 (mit geänderten Registern) 
funtioniert um Analoge Werte mit dem ADC einzulesen.

Jetzt würde ich hiermit gerne den Temperatursensor LM35 einlesen, dieser 
gibt für 1 Grad Celsius 10ma Spannnung aus.

Aus mit unerfindlichen Gründen sendet der UART immer den Wert 0 zurück 
:(
Im Code wird iota verwendet um den unsinged int der zwischen 0 und 1023 
liegt in einen String umzuwandeln, zur Kontrolle ob ein Wert > 10 in der 
variable adc_value landet wird eine LED für eine Sekunda aktiviert. 
Diese geht aber nie an.

Das ganze läuft auf einem Atmega32, der Sensor Ausgang liegt an PD0 und 
hier kommen gerade 210mv an.

Neben der normalen Beschaltung sind:
Atmega -> Schaltung
AREF -> AVCC
GND -> GND
AVCC -> AVCC

angeschlossen.
Als Referenzspannung dient:

REFS1 REFS0
 0 1 AVCC with external capacitor at AREF pin

Bedeutet das das man einen Kondensator zwischen ATmega Pin 32 und +Vcc 
schaltet? Habe es bisher ohne Kondensator angeschlossen in der Hoffnung 
das auch ohne Glättung zumindest ein Wert herauskommt der nicht 0 ist.

Der Bluetooth Part funktioniert, wenn man adc_value = 300; vor itoa... 
setzt wird auch eine 300 angezeigt.

Hat jemand eine Idee was es sein kann?
1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3
#include <util/delay.h>
4
#include <string.h>
5
#include <stdlib.h>
6
7
8
#define BAUD 9600
9
10
11
#define MYUBRR ((uint16_t) ((F_CPU / ((BAUD) * 16.0)) + .5) - 1)
12
#define LED1 PD6
13
#define LED2 PD7
14
15
16
17
// contains what the bluetooth module received
18
uint8_t data;
19
20
21
void USART_Init(unsigned int ubrr) {
22
    // set baud rate
23
    UBRRH = (unsigned char)(ubrr>>8);
24
    UBRRL = (unsigned char)(ubrr);
25
    // enable receiver, transmitter and interrupts for rx/tx
26
    //UCSRB = (1<<RXEN) | (1<<TXEN) | (1<<RXCIE) | (1<< TXCIE);
27
    UCSRB = (1<<TXEN);
28
29
    // frame format: 8 bits data, 1 stop bit, no parity - these are the default settings for atmega168.
30
    // no change needed
31
    // could be changed here: //(0<<UPM00) not tested
32
    // UCSR0C = (1<<UCSZ00) | (1<<UCSZ01) | (0<<UPM00) | (0<<UPM01);
33
}
34
35
36
37
void USART_transmit(char c) {
38
    // the commented out test if we are ready to send should work too
39
    //while ((UCSR0A & (1 << UDRE0)) == 0) {};
40
    while ( !(UCSRA & (1<<UDRE)) ) {}
41
    UDR = c;
42
}
43
44
45
46
47
void send_string(char s[]) {
48
    int i =0;
49
50
    while (s[i] != 0x00) {
51
        USART_transmit(s[i]);
52
        i++;
53
    }
54
    USART_transmit('\r');
55
    USART_transmit('\n');
56
57
}
58
59
60
61
62
void InitADC(void) {
63
    // Select Vref=AVcc
64
    ADMUX |= ( 1 << REFS0);
65
    
66
    // old, clumsy code...to much at once
67
    //ADCSRA |= (1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0)|(1<<ADEN);    
68
    
69
    // ADEN = Enable ADC
70
    ADCSRA=(1<<ADEN);
71
72
    // prescalar FCPU/128 = 16000000 / 128 = 125khz
73
    ADCSRA=(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0);
74
}
75
76
77
uint16_t readADC(uint8_t ADCchannel) {
78
    
79
  // This is the code that selects the channel. AND out the entire mux area,
80
    // then OR in the desired analog channel.
81
    ADMUX &= ~((1<<MUX3) | (1<<MUX2) | (1<<MUX1) | (1<<MUX0));
82
    ADMUX |= ADCchannel;
83
    
84
    //ADMUX |= 0xb00000000;
85
    
86
    // single conversion mode
87
    ADCSRA |= (1<<ADSC);
88
89
    // ...if you block on ADSC rather than ADIF (wait while it's high rather than low) then it self clears (goes back to 0) so you don't need to manually clear it.
90
    // Wait for conversion to complete
91
    while(ADCSRA & (1<<ADSC));
92
   
93
    // second conversion, the first maybe off
94
    ADCSRA |= (1<<ADSC);
95
96
    while(ADCSRA & (1<<ADSC));
97
98
99
100
    return(ADC);
101
}
102
103
104
105
106
107
108
109
int main(void) {
110
    unsigned int adc_value;
111
    InitADC();
112
   
113
114
    DDRD |= (1 << LED1);
115
    DDRD |= (1 << LED2); 
116
    // led test
117
    PORTD |= (1 << LED1); // Turn on LED1
118
    _delay_ms(1000);
119
    PORTD &= ~(1 << LED1); // Turn off LED1
120
121
   
122
    // this gives a buffer overflow, the range 0...1023 does not fit into 3
123
    //char output[3];
124
    
125
    char output[7];
126
    
127
    USART_Init(MYUBRR);
128
    
129
    sei();
130
131
    while(1){
132
        adc_value = readADC(0);
133
134
        if (adc_value > 10) {
135
            PORTD |= (1 << LED2); // Turn on LED1
136
            _delay_ms(1000);
137
            PORTD &= ~(1 << LED2); // Turn off LED1
138
        }
139
140
141
142
        // !!!
143
        // use stdoul as replacement for itoa
144
        // convert integer to string
145
        itoa(adc_value, output, 10);
146
        
147
        // send value via bluetooth
148
        send_string(output);
149
150
        // (I KNOW; _delay_ms is bad) wait for the next conversion, 3 seconds
151
        _delay_ms(3000);
152
  
153
        // show some activity to know that the chip didnt hang
154
        PORTD |= (1 << LED1); // Turn on LED1
155
        _delay_ms(1000);
156
        PORTD &= ~(1 << LED1); // Turn off LED1
157
158
    }
159
}

: Bearbeitet durch User
von Holger L. (max5v)


Lesenswert?

Julius J. schrieb:
> Das ganze läuft auf einem Atmega32, der Sensor Ausgang liegt an PD0 und
> hier kommen gerade 210mv an.

Hast du dich nur vertippt oder meinst du wirklich PD0, eigentlich sollte 
ADC0 an PA0 liegen.

von aaasgdsa (Gast)


Lesenswert?

Julius J. schrieb:
> Jetzt würde ich hiermit gerne den Temperatursensor LM35 einlesen, dieser
> gibt für 1 Grad Celsius 10ma Spannnung aus.

Nein, der wirft Einheiten nicht wild durcheinander.

aaasgdsa

von eProfi (Gast)


Lesenswert?

> REFS1 REFS0
> 0 1 AVCC with external capacitor at AREF pin


> Bedeutet das das man einen Kondensator zwischen ATmega Pin 32 und
> +Vcc schaltet? Habe es bisher ohne Kondensator angeschlossen
> in der Hoffnung das auch ohne Glättung zumindest ein Wert
> herauskommt der nicht 0 ist.

Vorsicht! Das heißt, dass im µC das Pin AREF an AVCC mit einem FET 
geschaltet wird. Da braucht man keine externe Verbindung.
Wenn man eine externe Spannung an AREF anschließt, müssen die beiden 
Bits REFS (Reference Select) 00 sein! Genauer gesagt nur REFS0, das kann 
sich aber bei späteren Versionen ändern, da "10" reserved ist.

Siehe "Analog to Digital Converter Block Schematic Operation" im 
Datenblatt, dort sieht man, dass das REFS0 auf den FET geht.
Wenn REFS ungleich 0 ist, darf (und soll) nur ein Kondensator an AREF 
angeschlossen sein, sonst gibt es einen Kurzschluss.

von eProfi (Gast)


Lesenswert?

> Bedeutet das das man einen Kondensator zwischen ATmega Pin 32 und
> +Vcc schaltet?
Nicht nach Vcc, sondern zwischen AREF und AGND.


Nebenbei, richtig geschrieben: Bedeutet das, dass ...

von Julius J. (juliusj)


Lesenswert?

@Holger L. : Ups, ja das ist ein vertipper, Ist an PA0 angeschlossen.

Und die 10ma sind natürlich 10mV.

von Karl M. (Gast)


Lesenswert?

Hallo ein logischer Fehler ist Dir auch noch passiert.

macht man dies isoliert, so wird der ADC-Wert nicht gelesen.
1
// single conversion mode
2
    ADCSRA |= (1<<ADSC);
3
4
    // ...if you block on ADSC rather than ADIF (wait while it's high rather than low) then it self clears (goes back to 0) so you don't need to manually clear it.
5
    // Wait for conversion to complete
6
    while(ADCSRA & (1<<ADSC));
Also wirklich noch den ADC-Wert auslesen !

von Julius J. (juliusj)


Lesenswert?

Der Hinweis das man das nicht seperat machen kann war ausschlaggebend.
1
    // ALT:
2
    //ADCSRA=(1<<ADEN);
3
    //ADCSRA=(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0);
4
5
6
    // NEU
7
    // prescalar FCPU/128 = 16000000 / 128 = 125khz
8
    ADCSRA=(1<<ADEN)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0);

Anscheinend darf man auch den ADC nicht seperat aktivieren und danach 
den Vorteiler setzen.

Diese Änderung und diese readADC() Funktion:
1
uint16_t readADC(uint8_t ADCchannel) {
2
    //select ADC channel with safety mask
3
    ADMUX = (ADMUX & 0xF0) | (ADCchannel & 0x0F);
4
    //single conversion mode
5
    ADCSRA |= (1<<ADSC);
6
    //Wait for conversion to complete
7
    while(!(ADCSRA & (1<<ADIF)));
8
9
    ADCSRA |= (1<<ADSC);
10
    //Wait for conversion to complete
11
    while(!(ADCSRA & (1<<ADIF)));
12
13
14
    return ADC;
15
}

funktionieren.


Diese hier tut nichts:
1
uint16_t readADC_OLD(uint8_t ADCchannel) {
2
3
    ADMUX &= ~((1<<MUX3) | (1<<MUX2) | (1<<MUX1) | (1<<MUX0));
4
    ADMUX |= ADCchannel;
5
6
    // Wait for conversion to complete
7
    while(ADCSRA & (1<<ADSC));
8
9
    // second conversion, the first maybe off
10
    while(ADCSRA & (1<<ADSC));
11
12
13
14
    return(ADC);
15
}

von Karl M. (Gast)


Lesenswert?

Julius J. schrieb:
> Diese hier tut nichts:uint16_t readADC_OLD(uint8_t ADCchannel) {
>
>     ADMUX &= ~((1<<MUX3) | (1<<MUX2) | (1<<MUX1) | (1<<MUX0));
>     ADMUX |= ADCchannel;
>
>     // Wait for conversion to complete
>     while(ADCSRA & (1<<ADSC));
>
>     // second conversion, the first maybe off
>     while(ADCSRA & (1<<ADSC));
>
>     return(ADC);
> }

Weil Du nicht weist, was passiert. Warum schrieb ich schon.

von Karl M. (Gast)


Lesenswert?

Das ist auch nicht richtig:
1
while(!(ADCSRA & (1<<ADIF)));
Warum ? steht im Datenblatt unter den Flags.

Aus dem Datenblatt eines Attiny85:
• Bit 4 – ADIF: ADC Interrupt Flag
This bit is set when an ADC conversion completes and the data registers 
are updated. The ADC Conversion Complete Interrupt is executed if the 
ADIE bit and the I-bit in SREG are set. ADIF is cleared by hardware when
executing the corresponding interrupt handling vector. Alternatively, 
ADIF is cleared by writing a logical one to the flag. Beware that if 
doing a Read-Modify-Write on ADCSRA, a pending interrupt can be 
disabled. This also applies if the SBI and CBI instructions are used.

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.