Guten Tag,
leider habe ich kein passendes, bereits offenes, Thema hierzu gefunden.
Zu meinem Problem.
Für meine Abschlussarbeit der Technikerschule entwickel ich ein Projekt
bei dem ich mehrere ADC-Kanäle auswerten will. Verwendet wird ein
ATMEGA8 im PDIP Gehäuse. An AREF ist ein Kondensator nach Masse
geschaltet (laut Datenblatt). An PORT D ist ein LCD Display
angeschlossen.
Funktion der entsprechenden Zeilen:
-es wird ein Sollwert (0-5V) ermittelt
-es werden nacheinander zwei weitere Werte ermittelt
Danach folgt die Verarbeitung und die Ausgabe an das LCD-Display.
Nun ist das Problem, dass, egal welchen Kanal ich im ADMUX Register
eingebe, nur der ADC Kanal 0 arbeitet.
Codeausschnitt:
[c]
//Sollwerterfassung
ADCSRA = (1<<ADEN) | (1<<ADPS2) | (1<<ADPS1)|(1<<ADSC);
ADMUX=0x11;
ADMUX=(0<<REFS1)|1<<REFS0);
while (ADCSRA & (1<<ADSC));
Sollwert = ADCW;
[c]
Also ob ich im ADMUX eine 11 oder eine 01 usw. schreibe hat keine
Auswirkung auf das Programm.Das heißt es wird immer Kanal 0, also ADMUX
= 0x00, erfasst und an das LCD Display ausgegeben.
Ich habe schon diverse unterschiedliche Schreibweisen der Adressen sowie
zwei ATMEGA 8 mit dem Programm getestet. Leider ohne Erfolg.
Hat hier jemand schon einmal ein ähnliches Problem gehabt / gelöst?
Im Augenblick komme ich nicht weiter.
Danke und viele Grüße
Stephan
Stephan Winckler schrieb:> ADMUX=0x11;> ADMUX=(0<<REFS1)|1<<REFS0);
Du schreibst also erstmal 0x11 ins ADMUX. Dann überschreibst du das mit
der 2ten Zeile.
Welche Technikerschule ist das denn? Etwa Beuth?
Ich würde gerne mal wissen, was für einen Abschluss man dann hat - und
warum bin ich so früh geboren, das ich nicht auch mit so einem
Sontagsvormittags-Picknick Projekt meinen Abschluss machen durfte. Das
ist ungerecht - allerdings wird mir angst und bang, wenn ich darüber
nachdenke, was für Controllerprogramme dann in meinem nächsten Auto
laufen.
Hey Matthias mal ganz ruhig!
Ich habe von der Programmierung wenig bis keine Ahnung und lerne das
auch nicht in der Schule!
Das ganze ist ein Projekt mit Praxisanteil und ausführlicher
Dokumentation, eine Gsamtentwicklung neben der Abendschule. Das
Programmieren ist nur ein winziger, unbedeutender Teil, der aber
trotzdem funktionieren muss!
Nun mal zu den hilfreichen (auch deiner Matthias) Antworten.
Erstmal danke.
Die Klammer habe ich drin, die ist nur auf dem Weg ins Forum verschütt
gegangen.
@ Matthias:
Um das Überschreiben zu umgehen, muss der Befehl also so aussehen?
ADMUX |= 0x11 | (1<<REFS0);
Oder raff ich da etwas nicht?
@egonotto:
Ich weiß, dass dieser Befehl nicht sein muss, habe es aber aus einem
Beispiel zur Problemlösung mit übernommen.
egonotto schrieb:> Stephan Winckler schrieb:>> ADMUX=(0<<REFS1)|1<<REFS0);>>> Da fehlt doch eine Klammer. Das Programm darf doch gar nicht übersetzt> werden.
Heutzutage haust du deinen Krempel (aka Krempel von anderen den du per
Copy&Paste geklaut hast) erstmal ins Forum, dann übersetzt du ihn. Sonst
kannst du ja nicht wissen ob er korrekt ist. Und der Compiler gibt keine
so schönen Lösungsvorschläge wie das Forum.
gruß cyblord
Und nochmal.
Danke für die Hilfe!
Ich habe in der Suche leider nichts Ähnliches gefunden.
@ cyblord:
Wodurch, kann ein Fehler der Art auftreten, das die Adressierung des
Kanals nicht übernommen wird? Hat Matthias Recht mit seiner Aussage,
dass das doppelte ADMUX die Adresse überschreibt und damit eine Änderung
unwirksam macht?
@loller:
Wie suche ich denn, deiner Meinung nach, nach einem Adressierungproblem
des ADC's in der Suche?
MfG
Stephan
Stephan Winckler schrieb:> ADMUX |= 0x11 | (1<<REFS0);>> Oder raff ich da etwas nicht?
Nein. Erstens ist Bit 4 ein reserviertes Bit im Mega8 und soll gar nicht
angefasst werden (Seite 205 im Datenblatt). Und zweitens solltest du es,
wenn schon denn schon, so schreiben:
1
ADMUX=(1<<REFS0)|(1<<MUX0);
Zugegeben, (1<<MUX0) schiebt gar nichts nach links, aber so ist es
einfach, auch mal MUX1 oder MUX2 anzusprechen, ohne im Datenblatt
rumzuwühlen.
@ Matthias:
Den Befehl (1<<MUX0) kannte ich in der Form noch nicht.
Ich habe mit einem Lernbuch die am Anfang genannte Adressierung
gelernt.
Jetzt machen auch die Tabellen im Datenblatt mehr Sinn :-)
@ loller:
Das Tutorial habe ich natürlich Hilfe gezogen. allerdings hat es mir,
aufgrund der oben geschriebenen "Lernanfänge", nicht sonderlich
geholfen, da mir die Adressierung wie von Matthias beschrieben,
unbekannt war.
Danke erstmal an alle.
Ich werde mich morgen früh da mal drauf stürzen und die andere (Volt
einzig richtige) Adressierung ausprobieren.
MfG
Stephan Winckler schrieb:> Ich habe mit einem Lernbuch die am Anfang genannte Adressierung> gelernt.
Nur als abschreckendes Beispiel - nenn dieses Buch mal bitte, damit wir
einen Shitstorm auslösen können, hehehe.
Das Buch heißt AVR. Autor : F. Schäffer. Verlag : Elektor.
Die Einstellungen des ADC des ATMEGA wird ca. ab Seite 158 sehr
detailliert beschrieben. Die Kanalauswahl scheint mir jetzt aber ein
wenig unglücklich.
MfG Stephan
Das Problem ist doch, dass es dabei nicht um die Funktion des Registers
geht, sondern grundsätzlich um Bitoperationen. Was der Register in
Endeffetk bewirkt ist doch dann eine ganz andere Geschichte. Darum brigt
doch hier ein Kapitel über den ADC gar nichts, wenn die Grundlagen nicht
sitzen.
gruß cyblord
unsigned int convertanalog(unsigned cha channel)
{
ADMUX=(1<<REFS1)|(1<<REFS0)|(channel & 0x0f);
.....
bla bla
.....
}
Aufruf:
adc0val=convertanalog(0);
Die Biteinstellung für ADMUX ist mit
ADMUX = (1<<REFS0) usw.
ADMUX = 0x00
beschrieben. Da es sich um ein Buch von 0 Wissen an handelt, meine ich,
sollte es so doch möglich sein.
Welcher Wert in das Register muss steht im Datenblatt. Also habe ich den
Wert für den entsprechenden Kanal abgelesen und eingegeben.
Bis zu dem Punkt ist mir das Buch sehr verständlich und hilfreich
gewesen. Hoffentlich komme ich noch dahinter, was ich da falsch
verstehe. :-)
@ Unwissender:
Diese Form habe ich auch schon gesehen. Welche Vorteile bringt sie mit?
Danke für eure Hilfe. Ich werde mich morgen weiter mit dem Thema
beschäftigen.
MfG
Stephan Winckler schrieb:> Hoffentlich komme ich noch dahinter, was ich da falsch> verstehe. :-)
Ach komm, so kompliziert ist das doch nicht.
i = 2;
i = 4;
Was ist nach diesen beiden Zeilen der Inhalt von i?
Stephan Winckler schrieb:> Eigentlich müsste es 4 sein.
Ganz genau.
Und nun schau dir noch mal die ADMUX-Zeilen an:
ADMUX=0x11;
ADMUX=(0<<REFS1)|1<<REFS0);
Was ist nun der Inhalt von ADMUX nach diesen beiden Zeilen?
Und was bedeutet das für die Kanalauswahl?
(dass 0x11 sowieso der falsche Wert ist, lassen wir mal außer Betracht)
Mich wundert (nicht wirklich), das in einem Elektor Buch reservierte
Bits beschrieben werden, normalerweise tut man das nicht.
Beim ADMUX ist ein Schreiben in einem Rutsch, also per ADMUX = blabla
ganz sinnvoll. Interessant wird es halt beim Starten des ADC über
ADCSRA.
Da ist verodern mit dem Originalwert des Registers interessant, vor
allem zum löschen des IRQ Flags und zum Starten des ADC:
1
// Setze Vorteiler und gib den ADC frei.
2
// Man beachte das vorbesetzen mit =
3
ADCSRA=(1<<ADEN)|(1<<ADPS2);
4
// nu wird der ADMUX besetzt
5
ADMUX=(1<<REFS0)|(1<<MUX0);
6
// und jetzt starten wir. Um ADCSRA nicht zu zermanschen, mit |=
7
ADCSRA|=(1<<ADSC);
Die Technik der Veroderns und VerANDens ist beim AVR gang und gäbe. Du
kannst so in C gezielt einzelne Bits löschen und setzen, im Prinzip das,
was die Assemblerbefehle CBI und SBI bzw. SBR und CBR auch machen.
Zum Löschen eines Bits nimmst du die Komplementärfunktion in C.
1
2
ADCSRA&=~(1<<ADFR);// als Beispiel mal das Freerun Bit löschen
Die Wellenlinie ~ ist die Komplementärfunktion und das &= ist dann das
VerANDen mit dem Ziel.
Diese Technik und die ausführliche Schreibweise führen dazu, das du dein
Program auch nach Jahren noch lesen kannst, ohne im Datenblatt
nachzulesen. Ausserdem musst du beim Wechsel auf einen anderen Mega
nicht immer wieder schauen, ob die Bits ihre Position geändert haben.
Moin nochmal,
danke Matthias für die Erklärungen. Eine Sache erschließt sich mir
allerdings noch nicht. Wenn ich mit bspw. (1<<MUX0) den ADC-Kanal 0
auswähle, müsste ich ja mit (1<<MUX1) usw die nächsten Kanäle ansteuern
können. In der datei im Anhang habe ich einmal die MUX-Tabelle aus dem
ATMEGA8 Datenblatt angehängt. Dort ist MUX3..0 beschrieben, aber 6 ADC
Kanäle.
Wie spreche ich denn dann die letzten beiden an?
MfG
Wobei ich hier bei den MUX Bits ausnahmsweise nicht auf die MUX
Bitbezeichnungen gehen würde, sondern das ganze so ansetzen würde
Am Beispiel zur 'Aktivierung' des Kanals 5
1
ADMUX=(1<<REFS0)|5;
der Vorteil dieser Schreibweise ist ziemlich offensichtlich. Im Code
steht für jeden lesbar, dass hier der Kanal 5 aktiviert wird.
Die MUX Bits sind genau so angeordnet, dass ihre binäre Repräsentierung
exakt der Kanalzahl entspricht. Und in diesem Fall ist mir diese bessere
Lesbarkeit das dann auch wert ausgenutzt zu werden.
Hat man in einem Programm mehere ADC Kanäle im Einsatz, dann eben zb so
1
#define ADC_COMMON (1<<RAFS0) // die allen ADC Messungen gemeinsamen Einstellungen
2
#define ADC_SOLL 0 // Kanal 0 ist der Sollwert
3
#define ADC_COMPARE_1 2 // Der erste Vergleichswert am ADC Kanal 2
4
#define ADC_COPMARE_2 4 // Der zweite Vergleichswert am ADC Kanal 4
5
6
...
7
8
9
10
ADMUX=ADC_COMMON|ADC_SOLL;// Sollwert messen
11
...
12
13
ADMUX=ADC_COMMON|ADC_COMPARE_1;// ersten Vergleichswert
14
...
15
16
ADMUX=ADC_COMMON|ADC_COMPARE_2;// zweiten Vergleichswert
17
...
(*)
bzw. dann eben die entsprechenden Varianten, wenn die ADC Funktionalität
in einer eigenen Funktion steckt.
Da hat man dann das beste aus 2 Welten. Auf der einen Seite eine leichte
Anpassbarkeit und auf der anderen Seite einen lesbaren,
selbsterklärenden Code.
(*) wobei es dann natürlich sinnlos ist, im Kommentar die Kanalnummer
noch einmal zu nennen. Steht ja eh schon im Makro und wurde hier im
Kommentar nur deshalb angeführt, damit man die Verständllichkeit des
Makros sieht.
Um nocheinmal auf das Buch zu sprechen zu kommen. Da steht als Beispiel
ADMUX = 0x01; drin. Das vierte Bit wird nicht angesprochen. Allerdings
wird so beschrieben, dass so die Kanalauswahl funktioniert.
Für welche Funktion ist das vierte Bit denn im ATMEGA8 denn vorgesehen?
Stephan Winckler schrieb:> Um nocheinmal auf das Buch zu sprechen zu kommen. Da steht als Beispiel>> ADMUX = 0x01; drin. Das vierte Bit wird nicht angesprochen. Allerdings> wird so beschrieben, dass so die Kanalauswahl funktioniert.> Für welche Funktion ist das vierte Bit denn im ATMEGA8 denn vorgesehen?
Du musst dir eines angewöhnen.
Deine absolute, uneingeschränkte, über jeden Zweifel erhabene, für dich
während der AVR-Programmierung einzig zuständige 'Bibel' ist das
Datenblatt des Prozessors. Dort stehen die Antworten für alle deine
Fragen, die sich um die Funktionsweise deines Prozessors und seiner
Steuerregister drehen.
Das kriegst du bei ATmel auf der Homepage.
Und ja. Du brauchst das Datenblatt. Ohne kannst du nicht vernünftig
programmieren. Kein Mensch kennt alle Bits auswendig. Was denkst du, wo
wir nachsehen, wenn du fragst, was Bit 4 im ADMUX Register macht?
Wenn wir im Datenblatt nachsehen, dann kannst du das genausogut auch
gleich selber machen.
Deine Bibel ist immer das Datenblatt und nicht irgendein windiges
Elektor Buch.
Und diese Zuweisung von Hex-Zahlen an ein Register, die vergisst du am
besten gleich wieder. Das sollte man ausdrucken und dem Autor solange um
die Ohren dreschen, bis nur noch Konfetti übrig bleiben.
Stephan Winckler schrieb:> Wenn ich mit bspw. (1<<MUX0) den ADC-Kanal 0> auswähle,
Falsch. Bei Kanal 0 sind sind alle Bits (MUX0...3) = 0
Kanal 1: (1<<MUX0)
Kanal 2: (1<<MUX1)
Kanal 3: (1<<MUX1) | (1<<MUX0)
Kanal 4: (1<<MUX2)
...
> Dort ist MUX3..0 beschrieben, aber 6 ADC Kanäle.
Der ATmega8 hat nur 6 Kanäle, nur das Register ist gleich wie bei den
Typen mit 8 Kanälen.
Gruß Dietrich
Stephan Winckler schrieb:> Für welche Funktion ist das vierte Bit denn im ATMEGA8 denn vorgesehen?
Schau mal im Datenblatt: da steht "-", was bedeutet soviel wie "nicht
benutzt" bzw. "keine Funktion". Beim Lesen liefert es "0".
Gruß Dietrich
Jetzt hab ichs! Die Auswahl der Kanäle erfolgt über über das Setzen der
Bits im MUX Register. und über die 4 MUX´s kann ich alle 8 (wenn
vorhanden) Kanäle anwählen.
Kanal 5 : (1<<MUX2) | (1<<MUX0)
Jetzt kanns weiter gehen.
@ Karl Heinz:
Das Datenblatt habe und verwende ich bereits soweit es meine
Möglichkeiten zulassen.
@ Dietrich und Karl heinz:
Eure Hinweise, auch die Schreibweise, sind großartig und haben mir auch
bei zukünftigen Problemen sehr geholfen!
Danke.
MfG
Stephan Winckler schrieb:> Um nocheinmal auf das Buch zu sprechen zu kommen. Da steht als Beispiel>> ADMUX = 0x01; drin.
In diesem Moment, und das ist das Problem, wird zwar der Eingangskanal
schon auf 1 gesetzt, aber auch REFS0 und REFS1 auf 0. Das ist nicht das,
was du möchtest.
Karl Heinz hat das sehr gut gelöst, indem er die gemeinsame Einstellung
(nämlich REFS0 auf 1) in den #define ADC_COMMON gepackt hat, und dann
den gewünschten Kanal mit dieser Einstellung verodert und in das ADMUX
schreibt.
Dietrich L. schrieb:>> Für welche Funktion ist das vierte Bit denn im ATMEGA8 denn vorgesehen?>> Schau mal im Datenblatt: da steht "-", was bedeutet soviel wie "nicht> benutzt" bzw. "keine Funktion". Beim Lesen liefert es "0".
Das ist typisch für so eine Prozessorfamilie, die Jungs behalten sich
vor, bei Nachfolgemodellen dieses Bit zu benutzen. Spassigerweise
benutzen die direkten Nachfolger Mega48/88 etc. dieses Bit immer noch
nicht. Also schön auf 0 lassen :-P
Nochmal zur Bitmanipulation. Besonders sinnvoll ist das natürlich für
I/O Ports, um einen Pin high oder low zu setzen:
1
#define LED_PORT PORTB
2
#define LED_DIR DDRB
3
#define LED_GREEN_PIN 5 // eine grüne LED an PB5 , Kathode am Pin, Anode an Plus
4
// Port init
5
LED_DIR=(1<<LED_GREEN_PIN)
6
7
// grüne LED an
8
LED_PORT&=~(1<<LED_GREEN_PIN);
9
// grüne LED aus
10
LED_PORT|=(1<<LED_GREEN_PIN);
Wenn du jemals auf die Idee kommen solltest, das die LED an einem
anderen Pin/Port besser aufgehoben ist, änderst du nur die #defines und
fertig.
Auch ein Prozessorwechsel oder eine Platinenänderung schockt dich dann
nicht mehr.
So nun habe ich die variante von Karl Heinz in das mein Programm
integriert.
Mit meinen Einstellungen sieht das wie folgt aus:
#define ADC_Einstellung (1<<REFS0) // interne Referenzspannung
für alle Messungen
#define ADC_Sollwert 2 // Wert von Sollwert an Kanal 2
#define ADC_Sensor_1 1 // Wert von Sensor 1 an Kanal 1
#define ADC_Sensor_2 0 // Wert von Sensor 2 an Kanal 0
und im Text:
[c] ADCSRA = (1<<ADEN) | (1<<ADPS2) | (1<<ADPS1); // ADC
Voreinstellungen
vornehmen
ADMUX = ADC_Einstellung | ADC_Sollwert; // REF-Spannung wählen,
Sollwertkanal einstellen
ADCSRA |= (1<<ADSC); //Einzelumwandlung
verODERt
while (ADCSRA & (1<<ADSC));
Sollwert = ADCW; [c]
Die Ausgabe des Sollwertes funktioniert tadelos.
Das gleiche Vorgehen verwende ich für die anderen Werte. Nun ist das
Problem, dass mir für die drei Kanäle die gleichen Werte zurück gegeben
werden.
Ich habe nun probiert mit
ADCW = 0;
nach jeder Wandlung den Wert zu überschreiben. Aber ich erhalte trotzdem
immer den Wert des Sollwertes. Auch wenn ich diesen verändere, ändert
sich der Wert für Sensor 1 und 2 mit.
Kann mir jemand veraten, wo mein Denkfehler liegt?
MfG
Stephan Winckler schrieb:> Das gleiche Vorgehen verwende ich für die anderen Werte. Nun ist das> Problem, dass mir für die drei Kanäle die gleichen Werte zurück gegeben> werden.
Beschreib nicht deinen Code, zeig ihn einfach. So umfangreich wird der
schon nicht sein. Es gibt noch hunderte andere Möglichkeiten, was da
alles passiert sein kann.
Stephan Winckler schrieb:> Kann mir jemand veraten, wo mein Denkfehler liegt?
ADCH und ADCL kann nur gelesen werden. Erkennt man im Datenblatt daran,
daß bei Read/write immer nur ein R bei den entsprechenden Registern
steht.
Ok, dass ich das Ausgaberegister nicht beschreiben kann, habe ich
verstanden.
@ Karl Heinz:
Den Code darf ich, wegen der Prüfung, die damit in Zusammenhang steht
nicht veröffentlichen. Hast du eine Mailadresse oder gibt es PN in
diesem Forum?
Ich habe des Weiteren mal nur die einzelnen Umwandlungen im Code
gelassen.
Wenn ich die Werte einzelnd abfrage stimmen Sie. Allerdings sobald es
zwei oder drei sind, dann wird der Wert immer noch in die anderen
Messungen übernommen.
Stephan W. schrieb:> Ok, dass ich das Ausgaberegister nicht beschreiben kann, habe ich> verstanden.>> @ Karl Heinz:>> Den Code darf ich, wegen der Prüfung, die damit in Zusammenhang steht> nicht veröffentlichen. Hast du eine Mailadresse oder gibt es PN in> diesem Forum?
Du meinst du darfst
1
intmain()
2
{
3
lcd_init();
4
5
while(1){
6
7
ADMUX=...
8
ADCSRA|=...
9
while(ADCSRA...)
10
;
11
Soll=ADCW;
12
13
ADMUX=...
14
ADCSRA|=...
15
while(ADCSRA...)
16
;
17
Ist1=ADCW;
18
19
ADMUX=...
20
ADCSRA|=...
21
while(ADCSRA...)
22
;
23
Ist2=ADCW;
24
25
zb
26
sprintf(buffer,"%04d %04d %04d",Soll,Ist1,Ist2);
27
lcd_gotoxy(0,0);
28
lcd_puts(buffer);
29
}
30
}
nicht veröffentlichen?
Na dann.
Man kann IMMER eine Aufgabe soweit abspecken, dass sie sich nur auf
einen Aspekt konzentriert und man trotzdem das rauskriegt, was man
wissen will.
Ok da hast du natürlich Recht. Es klang danach, dass ich den ganzen Code
einstellen soll und das geht leider nicht.
Die Abschnitte sehen wie folgt aus:
Die defines:
#define ADC_Einstellung (1<<REFS0)
#define ADC_Sollwert 2
#define ADC_Sensor_1 1
#define ADC_Sensor_2 0
Für den Sollwert:
ADCSRA = (1<<ADEN) | (1<<ADPS2) | (1<<ADPS1);
ADMUX = ADC_Einstellung | ADC_Sollwert;
ADCSRA |=(1<<ADSC); while (ADCSRA & (1<<ADSC));
Sollwert = ADCW;
Für Sensorwert 1:
ADCSRA = (1<<ADEN) | (1<<ADPS2) | (1<<ADPS1);
ADMUX = ADC_Einstellung | ADC_Sensor_1;
ADCSRA |= (1<<ADSC);
while (ADCSRA & (1<<ADSC));
Sensor_1 = ADCW;
Für Sensorwert 2:
ADCSRA = (1<<ADEN) | (1<<ADPS2) | (1<<ADPS1);
ADMUX = ADC_Einstellung | ADC_Sensor_2;
ADCSRA |= (1<<ADSC);
Sensor_2 = ADCW;
Ich habe den Fehler gefunden! Ich habe für Sensor 2 nicht gewartet, bis
die Umwandlunge fertig war!
while (ADCSRA & (1<<ADSC));
hat bei dem zweitem Sensor gefehlt. Dann hat der prozessor also den
Wert, der sich in ADCW befand genommen, da der aktuelle noch nicht
volag!
MfG
Stephan W. schrieb:> Ich habe den Fehler gefunden! Ich habe für Sensor 2 nicht gewartet, bis> die Umwandlunge fertig war!
Und .. du hast rausgefunden, warum es vernünftig ist, sich für
Teilaufgaben, überhaupt wenn es immer die gleiche Teilaufgabe ist, sich
Funktionen zu machen
1
uint16_tGetADC(uint8_tchannel)
2
{
3
ADMUX=ADC_COMMON|channel;
4
5
ADCSRA|=...
6
while(ADCSRA...)
7
;
8
9
returnADCW;
10
}
11
12
intmain()
13
{
14
lcd_init();
15
16
while(1){
17
18
Soll=GetADC(ADC_SOLL);
19
Ist1=GetADC(ADC_COMPARE_1);
20
Ist2=GetADC(ADC_COMPARE_2);
21
22
zb
23
sprintf(buffer,"%04d %04d %04d",Soll,Ist1,Ist2);
24
lcd_gotoxy(0,0);
25
lcd_puts(buffer);
26
}
27
}
Nicht nur wird das Programm kürzer. Es wird auch übersichtlicher und
wenn die Funktion für den einen Kanal funktioniert, funktioniert sie
auch für die anderen.