Liebes Forenteam. Ich hab lange die OffTopics durchsucht, und mich
gewundert warum so etwas noch anscheinend nicht besprochen wurde. (bitte
um Korrektion falls ich falsch liege)
Meine Aufgabe ist es mit dem ATmega8 einen 0-5V Wert einzulesen. Ich
habe eine VCC & REF Spannung von 5V. Wenn ich nun meine 0-5V Werte
Digitalisier.
Stimmt es das wie ich in Erfahrung gebracht habe das der Digitale wert
0001
in Volt umgerechnet (5/1024)V entspricht?
(1024 weil es ein 10 BIT ADC ist)
Am Ausgang soll möglichst der Eingangswert digital in Millivolt
ausgegeben werden. (z.B. an Port D)
Mir fehlt ein bisschen das Wissen rundherum, stimmt es wenn ich in diese
Richtung arbeite das 1 Bit 5/1024 V ist und so weiterrechne?
Quellcode:
#include <avr/io.h>
#include <avr/interrupt.h>
#include <stdint.h>
int main(void)
{
PORTB = 0;
DDRB = 0x00; //Port B als eingang
if( (PINB & (1<<1))) //Alles startet indem an PB1 ein High-Signal kommt
{
DDRD = 0xFF; //Port D als Ausgabe-Port deffiniert
ADCSRA = (1<<ADEN | 1<<ADSC | 0<<ADFR | 0<<ADIF | 1<<ADPS2 | 0<<ADPS1 |
1<<ADPS0);
ADMUX = (1<<REFS1 | 0<<REFS0 | 0<<ADLAR | 0<<MUX3 | 0<<MUX2 | 0<<MUX1 |
0<<MUX0);
//ADEN = ADC enable (eingeschaltet)
//ADSC = Starten des Convertiervorganges
//ADFR = Die Convertierung soll nicht von alleine beginnen sondern
// manuell -> 0
//ADIF = Interrupt Flag ausgeschaltet
//ADIE = Interrupt Enable ausgeschaltet
//
// CLK 12000000
// TFmin = ------ = ------------- = 60
// 200kHz 200000
//
// CLK 12000000
// TFmax = ------ = ------------- = 24
// 200kHz 50000
//-> TF = 32;
PORTD = ADCL;
}
}
Lob, dass du wenigstens versucht hast zu suchen ;-)
Besser ist deine Frage entweder im Forum µC & Elektronik oder im Forum
GCC aufgehoben, da sie nicht Offtopic sondern Ontopic ist.
Lob auch, dass du bereits eigene Ideen in Form von Code bei bringst
Der Code hat noch Probleme.
Es fehlt eine Hauptarbeitsschleife. Im Moment wird exakt 1x PB1
abgefragt und dann macht der µC kurz was mit ADC (if Bedingung ist wahr)
oder einfach nix mehr (retirn aus main()). Kurz ist hier wirklich kurz
bei x MHz Takt!
In dem Arbeitsfall initialisierst du den ADC (Zuweisungen an ADCSRA und
ADMUX). Dann schnappst dir direkt den Wert (ADCL lesen) ohne zu prüfen,
ob der ADC bereits einen Spannungswert digitalisiert hat. Das macht man
z.B. nach dem Tutorial anders:
http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial#ADC_.28Analog_Digital_Converter.29
Dann gibst du den digitalen Wert an den Port D aus. Das kann man machen,
wenn man am Port z.B. ein Widerstandsnetzwerk zur Umwandlung der
digitalen Portzustände in analoge Spannungswerte installiert hat, also
einen DAC gebaut hat. Wie steht auch im
http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial#DAC_.28Digital_Analog_Converter.29Tutorial:
Stefan "stefb" B. wrote:
> Besser ist deine Frage entweder im Forum µC & Elektronik oder im Forum> GCC aufgehoben, da sie nicht Offtopic sondern Ontopic ist.
Genau, daher habe ich das jetzt auch mal verschoben.
danke dir. ich hab mal das formale geändert:
#include <avr/io.h>
#include <avr/interrupt.h>
#include <stdint.h>
int main(void)
{
int result;
int x;
for(;;)
{
PORTB = 0;
DDRB = 0x00; //Port B als eingang
if( (PINB & (1<<1))) //Alles startet indem an PB1 ein High-Signal kommt
{
DDRD = 0xFF; //Port D als Ausgabe-Port deffiniert
ADCSRA = (1<<ADEN | 1<<ADSC | 0<<ADFR | 0<<ADIF | 1<<ADPS2 | 0<<ADPS1 |
1<<ADPS0);
ADMUX = (1<<REFS1 | 0<<REFS0 | 0<<ADLAR | 0<<MUX3 | 0<<MUX2 | 0<<MUX1 |
0<<MUX0);
//ADEN = ADC enable (eingeschaltet)
//ADSC = Starten des Convertiervorganges
//ADFR = Die Convertierung soll nicht von alleine beginnen sondern
// manuell -> 0
//ADIF = Interrupt Flag ausgeschaltet
//ADIE = Interrupt Enable ausgeschaltet
//
// CLK 12000000
// TFmin = ------ = ------------- = 60
// 200kHz 200000
//
// CLK 12000000
// TFmax = ------ = ------------- = 24
// 200kHz 50000
while ( ADCSRA & (1<<ADSC) )
{
; // auf Abschluss der Konvertierung warten
}
result = ADCL; // ADCW muss einmal gelesen werden,
result += (ADCH<<8);// sonst wird Ergebnis der nächsten Wandlung
// nicht übernommen.
result = 0; // nun kanns losgehen
for( x=0; x<4; x++ ) //Zählschleife
{
ADCSRA = (1<<ADSC); // starten der Wandlung
while ( ADCSRA & (1<<ADSC) )
{
; // auf Abschluss der Konvertierung warten
}
result += ADCL; // Wandlungsergebnisse aufaddieren
result += (ADCH<<8);
}
result/=4;
}
}
}
ne frage....ich muss die digitalen werte an z.B. Port D (das was
auskommentiert ist) ausgeben.
in result steht ja ein int wert oder?
wie bekomm ich diesen result wert am besten raus?
lg mario
Ich erlaube mir mal, das mit der Formatierung lesbarer zu gestalten.
Sieht gut aus.
1
#include<avr/io.h>
2
#include<avr/interrupt.h>
3
#include<stdint.h>
4
5
voidadc_init(void)
6
{
7
ADCSRA=((1<<ADEN)|(1<<ADSC)|(0<<ADFR)
8
|(0<<ADIF)|(1<<ADPS2)|(0<<ADPS1)
9
|(1<<ADPS0));
10
11
ADMUX=((1<<REFS1)|(0<<REFS0)|(0<<ADLAR)
12
|(0<<MUX3)|(0<<MUX2)|(0<<MUX1)
13
|(0<<MUX0));
14
15
// ADEN = ADC enable (eingeschaltet)
16
// ADSC = Starten des Convertiervorganges
17
// ADFR = Die Convertierung soll nicht von alleine beginnen sondern
18
// manuell -> 0
19
// ADIF = Interrupt Flag ausgeschaltet
20
// ADIE = Interrupt Enable ausgeschaltet
21
//
22
// CLK 12000000
23
// TFmin = ------ = ------------ = 60
24
// 200kHz 200000
25
//
26
// CLK 12000000
27
// TFmax = ------ = ------------ = 24
28
// 200kHz 50000
29
30
// ADC muss einmal eingelesen werden, sonst
31
// wird das Ergebnis der nächsten Wandlung
32
// nicht übernommen
33
while((ADCSRA&(1<<ADSC)))
34
;// auf Abschluss der Konvertierung warten
35
result=ADCW;
36
}
37
38
// Beim Setzen der Anzahl
39
// Uverflow in result in beachten
40
// Bei der Division
41
// Bei Potenzen von 2 kann statt / ein Rechtsschieben benutzt werden
42
// Ganzzahlhdivision! => Keine Nachkommastellen
43
#define ANZAHL_MESSUNGEN 4
44
45
uint16_tadc_read(void)
46
{
47
uint16_tresult=0;
48
uint8_tx;
49
50
// Mittelwert aus ANZAHL_MESSUNGEN Messungen bilden
51
for(x=0;x<ANZAHL_MESSUNGEN;x++)
52
{
53
ADCSRA|=(1<<ADSC);// Starten der Wandlung
54
while((ADCSRA&(1<<ADSC)))
55
;// auf Abschluss der Konvertierung warten
56
result+=ADCW;// Wandlungsergebnisse aufaddieren
57
}
58
ADCSRA&=~(1<<ADEN);// ADC deaktivieren
59
result/=x;
60
61
returnresult;
62
}
63
64
intmain(void)
65
{
66
uint16_tresult=0;
67
68
// Einmalige Hardware-Einstellungen
69
PORTB=0;// Pullups Port B aus
70
DDRB=0x00;// Port B als Eingang
71
DDRD=0xFF;// Port D als Ausgabe-Port definiert
72
// Pegel Port D implizit alle LOW
73
74
// ADV einmalig konfigurieren
75
adc_init();
76
77
for(;;)
78
{
79
// Alles startet wenn an PB1 ein High-Signal kommt
80
if((PINB&(1<<PB1)))
81
{
82
// ADC starten, Mittelwert bilden, ADC stoppen
83
result=adc_read();
84
}
85
}
86
}
> in result steht ja ein int wert oder?
Beim Aufsummieren von vier Messwerten a 10-Bit kommst du noch mit
einem int aus, wenn der int Typ 16-Bit hat (hat er auf dem AVR).
Sicherer wird es, wenn du explizit die Bitanzahl vorschreibst und einen
Overflow an der 15-Bit Grenze ausschliesst.
=> Datentyp uint16_t (s.o.) der geht sicher bis 64 aufsummierte
Messungen
> ich muss die digitalen werte an z.B. Port D ausgeben.> wie bekomm ich diesen result wert am besten raus?
Ich verstehe nicht genau, was deine Aufgabe ist.
Angenommen du misst genau 4 Volt am ADC. Was soll die Ausgabe sein (eine
Spannung von 4000 mV? Die digitale Zahl 4000?) und wie machst du die
Anzeige?
>> Am Ausgang soll möglichst der Eingangswert digital in Millivolt>> ausgegeben werden. (z.B. an Port D)
Multimeter an einem Pin?
>> PORTD = result;
10 (8) LEDs für einen 10 (8) Bit Wert? 10 Bits braucht 8 Pins z.B von
einem kompletten Port plus 2 andere Pins
Ganz anders? Zwei Siebensegmentanzeigen? Ein LCD?
Also bei der Formatierung hab ich Augen gemacht =)
Der ADC-Wert wird direkt noch am Atmega8 so bearbeitet, dass eine 7
Segment Anzeige angesteuert werden kann. Wir arbeiten mit ner
Referenzspannung in der Gegend von so 3,9 €...sowas in der Richtung.
Jah wir werden einen ganzen Port (8B) und zusätzlich von iwo 2B
ausborgen um 10B rausführen zu können.. .
Beim Programm ist es so wies jetzt ist denk ich alles richtig. Meine
Frage davor war einfach wie ich nun am besten den int_result Wert auf 10
Bits umwandle.
lg mario
ps: danke ;-)
M4rio wrote:
> Also bei der Formatierung hab ich Augen gemacht =)
Die Forensoftware kann das schon schön darstellen. Und wenn man dann den
Sourcecode noch logisch strukturiert (Funktionen, Prozeduren), schärft
das den Blick auf's Wesentliche.
> Der ADC-Wert wird direkt noch am Atmega8 so bearbeitet, dass eine 7> Segment Anzeige angesteuert werden kann.
Machbar. Zu Siebensegmentanzeigen gibt es einiges in der Artikelsammlung
in der Codesammlung. Da kann man einiges lernen.
> Ja wir werden einen ganzen Port (8B) und zusätzlich von iwo 2B> ausborgen um 10B rausführen zu können.. .
Da kannst du schon mehr draus machen:
Eine Anzeige für die 0en und 1en des digitalen Wertes. Das rattert ganz
gut besonders in den niederwertigen Stellen.
So eine Art LED-Leiste wie bei manchen Stereoanlagen die
Lautstärkeanzeige. Vielleicht mit 2-3 roten LEDs für Spitzenwerte oder
eine Hold-Funktion, d.h. die LED vom letzten Spitzenwert 1-2s gehalten.
> Wir arbeiten mit ner> Referenzspannung in der Gegend von so 3,9 €...sowas in der Richtung.
Verstehe ich nicht.
Genau soetwas, wie du mit den LEDs angespielt hast meine ich. Diese
Ausgänge sollen in eine 7-S-Anzeige geführt werden.
z.B. das wenn ich im ADC-Eingang 3 V habe, dass am ausgang am besten
300mV ausgegeben werden.
also sagen wir mal 100101100 ( = 300)
In meiner "result"-Variable steht nun mein ADC-Wert.
Diesen Wert muss ich wie in dem Fall auf 9 Port rausbringen.
Problem:
1 B != 1mV
Dazu haben wir beim Atmega8 jah eine "Referenzspannung".
Unsere wird so bei 3V liegen.
Also 3 V = 10B = 1024
........> 1 B = 3V / 1024
.
Vl. ist meine Frage schlecht vormuliert, ich hab nur die Problemstellung
das ich am Ausgang nicht nur LEDs leuchten lassen muss, sondern das
wirklich zuordbare V-Werte rauskommen.
z.B. 0000000001 = 1mV
0000001000 = 8mV
usw
Sorry ich habe nochnicht herraußen wie ich nun meine result Variable so
anpasse das ich dann den Ausgangsports mV-Werte zuweisen kann : /
ps: danke für die gute, aufwändige Hilfe!
Laaannnngsam verstehe ich.
Es ist gut, wie du das im letzten Artikel erklärst. Ich war der Ansicht,
du willst physikalische "300 mV" am µC ausgeben.
Das ist nicht so, du willst eine Anzeige haben, und zwar eine Anzeige
auf Binärzahlbasis, deren Wert du direkt als mV interpretieren kannst.
Also...
Bei einen ADC Referenz von 3 V und einem 10-Bit ADC () entspricht der
ADC-Messwert 1023 ("Vollausschlag") der gemessenen Spannung 3V und der
ADC-Messwert 0 der gemessenen Spannung 0V.
Eingang | ADC-Result | Rechenwert | Ausgabe
--------------------------------------------
3V 1023 1024 3000 "mV"
1,5V 511 512 1500
0V 0 1 0
Zunächst ohne den Trick mit dem Rechenwert.
3V (= ADC-Referenz) am Eingang entsprechen dem ADC-Result 1023.
Und bei einem ADC-Result von 1023 soll 3000 ausgegeben werden.
Also die Formel für die Ausgabe ist dann (Dreisatz):
(ADC-Ref. V * 1000 mV/V)
ausgabe = ------------------------ * ADC-Result
1023
Das Unschöne ist die Division durch 1023 statt durch eine Zweierpotenz.
Die nächste Zweierpotenz wäre 1024. Und da kommt der Rechenwert ins
Spiel:
(ADC-Ref. V * 1000 mV/V)
ausgabe = ------------------------ * (ADC-Result+1)
1024
Wenn du durchgängig mit Ganzzahlen rechnen willst, lohnt sich eine
Betrachtung, wie man geschickt rechnet ohne durch Abschneiden oder
Überläufe ungenau zu werden.
Das Abschneiden kann man minimieren, in dem man die Division am Schluss
macht.
ausgabe = ((x*1000)*(y+1))/1024
^^^^^^^^^^^^^^
Dabei muss man aber auf Überläufe achten. Dieser ^^^^ markierte
Zwischenwert kann grösser 16 Bit werden, 28-Bit um genau zu sein.
28-Bit passen in eine Variable vom Typ long oder unsigned long. Man kann
den Compiler zwingen mit unsigned long zu rechnen, in dem man die Formel
z.B. so schreibt:
#define ADC_referenz 3UL
#define mV_pro_Volt 1000
uint16_t ausgabe;
uint16_t ADC_result;
ausgabe = ((ADC_referenz * mV_pro_Volt) * (ADC_result+1)) / 1024;
Und zum Schluss noch die Division durch 1024 durch ein Rechtsschiften
ersetzen (oder auf die Optimierung des Compilers vertrauen):
ausgabe = ((ADC_referenz * mV_pro_Volt) * (ADC_result+1)) >> 10;
Nachsehen ob es passt, wenn 1,5V am Eingang anliegen:
ausgabe = ((3UL*1000) * (511+1)) >> 10 = 1500 mV bei einem ADC-Messwert
von 511... passt.
Lieber Stefan,
die Formel für den ADC-Wert lautet immer noch
Warum das so ist (und nicht durch 1023 und auch nicht ADC+1), wurde hier
im Forum schon zig mal durchgekaut. ADC kann dabei Werte von 0...1023
annehmen. Ich bin erschüttert, dass ein in meinen Augen erfahrener und
fachkundiger Forenteilnehmer wie Du so etwas schreibt...;-) Du gaukelst
mit dem "Rechentrick" vor, dass der ADC die Referenz messen kann, was
aber nicht der Fall ist. Wenn 3000 mV gemessen werden sollen, dann muss
eine höhere Referenzspannung benutzt werden.
Ich kann es verkraften. Aber Danke für die schnelle Korrektur.
Ich mache mich gleich schlau, warum das so ist.
PS:
Ich bin bestimmt kein Fachkundiger, ich mache ganz was anderes ;-)
Ah, klar. Durch den +1 "Offset", den ich benutze, um an die obere Grenze
zu kommen, beeinflusse ich gleichzeitig auch die untere Grenze (0V
Eingang => nicht 0 mV sondern (3*1000*(0+1))/1024...
Also M4rio, das unseelige +1 beim ADC_result weglassen und die
Referenzspannung eine Bereichsbreite höher wählen (3*1024/1034 = 3,003
V).
http://www.mikrocontroller.net/articles/AVR-Tutorial:_ADC#Umrechnung_des_ADC_Wertes_in_eine_Spannung
Wenn man keine solche Referenzspannung hat oder basteln will, kann man
z.B. die interne Referenzspannung des µC nehmen. Dann verschenkt man
allerdings etwas an Auflösung, weil die Bereichsbreite (Spannung pro
Bit) grösser wird.
@Stefan B:
Ich meinte mit "fachkundig" eigentlich eher, dass Du hier bisher
eigentlich i.d.R. qualifizierte Dinge von Dir gegeben hast, was schon
auf eine gewisse "Fachkunde" schließen lässt.
// ADFR = Die Convertierung soll nicht von alleine beginnen sondern
22
// manuell -> 0
23
// ADIF = Interrupt Flag ausgeschaltet
24
// ADIE = Interrupt Enable ausgeschaltet
25
//
26
// CLK 12000000
27
// TFmin = ------ = ------------ = 60
28
// 200kHz 200000
29
//
30
// CLK 12000000
31
// TFmax = ------ = ------------ = 24
32
// 200kHz 50000
33
34
// ADC muss einmal eingelesen werden, sonst
35
// wird das Ergebnis der nächsten Wandlung
36
// nicht übernommen
37
while((ADCSRA&(1<<ADSC)))
38
;// auf Abschluss der Konvertierung warten
39
result=ADCW;
40
}
41
42
// Beim Setzen der Anzahl
43
// Uverflow in result in beachten
44
// Bei der Division
45
// Bei Potenzen von 2 kann statt / ein Rechtsschieben benutzt werden
46
// Ganzzahlhdivision! => Keine Nachkommastellen
47
#define ANZAHL_MESSUNGEN 4
48
49
uint16_tadc_read(void)
50
{
51
uint16_tresult=0;
52
uint8_tx;
53
54
// Mittelwert aus ANZAHL_MESSUNGEN Messungen bilden
55
for(x=0;x<ANZAHL_MESSUNGEN;x++)
56
{
57
ADCSRA|=(1<<ADSC);// Starten der Wandlung
58
while((ADCSRA&(1<<ADSC)))
59
;// auf Abschluss der Konvertierung warten
60
result+=ADCW;// Wandlungsergebnisse aufaddieren
61
}
62
63
ADCSRA&=~(1<<ADEN);// ADC deaktivieren
64
65
result/=x;
66
returnresult;
67
}
68
69
intmain(void)
70
{
71
#define ADC_referenz 3.2 //3.2 ist mal n Beispiel für mich
72
#define maximalwert 1023
73
uint16_tresult=0;
74
uint16_tausgabe;
75
floatbereichsbreite=ADC_referenz/(maximalwert+1);
76
bereichsbreite*=1000;//von V auf mV
77
78
79
// Einmalige Hardware-Einstellungen
80
PORTB=0;// Pullups Port B aus
81
PORTD=0;
82
DDRB=0x03;// Ersten 2 Bits Ausgänge, rest Eingänge
83
DDRD=0xFF;// Port D als Ausgabe-Port definiert
84
// Pegel Port D implizit alle LOW
85
86
// ADV einmalig konfigurieren
87
adc_init();
88
89
for(;;)
90
{
91
// Alles startet wenn an PB2 ein High-Signal kommt
92
if((PINB&(1<<PB2)))
93
{
94
// ADC starten, Mittelwert bilden, ADC stoppen
95
result=adc_read();
96
97
98
ausgabe=result*bereichsbreite;
99
100
PORTD=ausgabe&0b0011111111;//Die ersten 8 Bits auf PORTD
101
PORTB=(ausgabe>>8);//Ich hol mir die letzten 2 Bit vor und auf PORTB
102
}
103
104
105
}
106
}
Danke schonmal. Ich hab eure Vorschläge in das Programm eingearbeitet.
Ich Verwende nun die ersten 2 Bits vom Port B als die Ausgabeports für
die 9. und 10. stelle von meinem ADC-Wert.
Meine Messung startet nun auch nichtmehr wenn auf PB1 ein 1 ist sondern
an PB2.
Frage:
Du hast oben im ADC_read die Zeile
ADCSRA &= ~(1<<ADEN); // ADC deaktivieren
Mir ist klar, dass die Tilde den Wert der drin ist umdreht...
Aber wenn ich 1 mal Umwandle....ihn Ausschalte...
und dann wieder etwas umwandeln will..
ist der ADEN immer noch auf 0.
Es steht nur
ADCSRA |= (1<<ADSC); // Starten der Wandlung
muss ich nicht am Anfang von ADC_read
ADCSRA &= (1<<ADEN);
haben?
lg M4ario
Ps.: Danke hat mir sehr sehr weitergeholfen
hey. kannstu dir das nochmal als letztes anschauen was ich da anfragte?
du schaltest oben nach der 1. umwandlung. den ADC aus:
1
ADCSRA&=~(1<<ADEN);// ADC deaktivieren
wenn ich 2 mal umwandle schaltest du ihm nimma ein?
ich würde oben in adc_read einfach das gleiche nochmal schreiben,
aber ich denke mir du wirst schon einen grund haben oO
sri. falls ich das falsch sehe