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 | void adc_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_t adc_read(void) |
46 | {
|
47 | uint16_t result = 0; |
48 | uint8_t x; |
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 | return result; |
62 | }
|
63 | |
64 | int main(void) |
65 | {
|
66 | uint16_t result = 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.
Danke für die Übersetzung ;-) Ich werde es gleich morgen bei meinem Projekt austesten und davon berichten.
1 | #include <avr/io.h> |
2 | #include <avr/interrupt.h> |
3 | #include <stdint.h> |
4 | |
5 | void adc_init(void) |
6 | {
|
7 | |
8 | uint16_t result = 0; |
9 | |
10 | |
11 | ADCSRA = ((1<<ADEN) | (1<<ADSC) | (0<<ADFR) |
12 | | (0<<ADIF) | (1<<ADPS2) | (0<<ADPS1) |
13 | | (1<<ADPS0)); |
14 | |
15 | ADMUX = ((1<<REFS1) | (0<<REFS0) | (0<<ADLAR) |
16 | | (0<<MUX3) | (0<<MUX2) | (0<<MUX1) |
17 | | (0<<MUX0)); |
18 | |
19 | // ADEN = ADC enable (eingeschaltet)
|
20 | // ADSC = Starten des Convertiervorganges
|
21 | // 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_t adc_read(void) |
50 | {
|
51 | uint16_t result = 0; |
52 | uint8_t x; |
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 | return result; |
67 | }
|
68 | |
69 | int main(void) |
70 | {
|
71 | #define ADC_referenz 3.2 //3.2 ist mal n Beispiel für mich
|
72 | #define maximalwert 1023
|
73 | uint16_t result = 0; |
74 | uint16_t ausgabe; |
75 | float bereichsbreite = 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
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.