Guten Abend,
Ich sitze nun seit ein paar Stunden an einem Problem und ich finde auch
im Netz keine Hilfe die ich entweder verstehe oder die mir wirklich
weiter hilft.
Und zwar habe ich eine kleine Schaltung aufgebaut die einen Atmega8, ein
RTC-Modul (RV 8564-C2) und ein LCD Display (HD44780 Controller)
beinhaltet.
Nun wollte ich ganz klassisch ersteinmal die Sekunden aus dem RTC-Modul
auslesen (TWI) und auf dem LCD-Display ausgeben lassen.
Nun genau da hakt es schon.
Laut Datenblatt besteht das Byte des RTCs ,welches die Sekunden
beinhaltet, aus dem oberstem Bit (Welches ausgelöscht werden muss, da es
nur als Statusbit genutzt wird) und den 7 weiteren Bits worin die
Sekunden im BCD-Format gespeichert sind.
Nun klappt es logischerweise nicht, den im TWDR ausgelesenen Wert ohne
umweg auf das LCD-Display zu senden, da das Display die Zeichen ja im
Ascii-Format braucht. (Nicht dass ich es nicht zumindest versucht hätte)
Die TWI/I2C verbindung scheint offentsichtlich zu funktioneren, da das
auf dem LCD sichtbar werdende Kauderwelsch sich im Sekundentakt
verändert.
Das heisst für mich ja soweit ich muss das oberste Bit löschen und das
ausgelesene Byte danach in Ascii-Format umwandeln.
Nun habe ich schon so einiges gelesen wie "Rechne den Wert einfach + 48"
bis hin zu ellenlangen Codes die die gewünschte Umwandlung vornehmen...
Nichts...
Ich habe es schon über kleine Umwege versucht, den BCD-Wert erst in
Integer umzurechnen und dann mittels itoa() an das LCD zu senden... auch
nichts...(liegt aber sehr wahrscheinlich an meinem fehlenden Wissen)
Das hier ist der Code den ich bisher habe und der (nach meiner Sicht)
zumindest bis zum i2c_stop() funktioniert:
1
#include<avr/io.h>
2
#include<util/delay.h>
3
#include"lcd.h"
4
#include"twimaster.c"
5
#include<avr/interrupt.h>
6
intmain(void)
7
{
8
9
sei();
10
TWCR=(1<<TWIE);
11
lcd_init();
12
i2c_init();
13
while(1){
14
i2c_start(0b10100010);
15
i2c_write(0x02);
16
i2c_rep_start(0b10100011);
17
i2c_readNak();
18
i2c_stop();
19
20
charWert=TWDR;
21
Wert=Wert&0b01111111;
22
23
lcd_string_xy(0,0,Buffer);//Das "Buffer" ist noch restbestand- nicht beachten.
24
}
25
26
}
Erste kleine Frage vorweg:
Mache ich das mit folgendem Code richtig wenn ich das oberste Bit
ausblenden möchte?
1
Wert=Wert&0b01111111;
Zweite Frage:
Wie zum Teufel bekomm ich den aus dem RTC-Modul gelesenen Wert auf das
LCD-Display?
Ich bin mir sicher, dass irgendjemand mir jetzt eine verbale Ohrfeige
verpasst weil ich einen leichten weg nicht beachtet habe und komplett
aus der Spur bin. Allerdings bin ich Anfänger in den Künsten der
uC-Programmierung( in C# im allgemeinen auch) und bastel mir ein kleines
Projekt damit ich das ganze besser lernen kann. ( Ich brauche ein Ziel
um die Motivation des lernens zu halten ;-) )
Ich danke schonmal für die möglichen guten Antworten ;-)
Gruß
Christian
Hallo,
Christian O. schrieb:> Mache ich das mit folgendem Code richtig wenn ich das oberste Bit> ausblenden möchte?> Wert = Wert & 0b01111111;
Das stimmt schon so, allerdings ist der Rest immer noch in BCD-Codiert.
Wenn die Sekunden BCD-codiert sind und sich in Wert das Sekundenbyte
befindet, kannst Du das folgendermaßen auflösen:
[c]
unsigned char SekundenString[3];
SekundenString[0] = ((Wert & 0x70) >> 4) + 0x30;
// Zehner-Stelle auswerten
// (Wert & 0x70) wertet die Bits 6, 5 und 4 aus (und blendet Bit-7
aus)
// >> 4 schiebt die Bits auf die Pos. 2, 1 und 0
// + 0x30 gibt den Offset für ein Zeichen im ASCII-Format dazu
SekundenString[1] = (Wert & 0x0F) + 0x30;
// Einer-Stelle auswerten
SekundenString[2] = 0x00; // String-Ende
[\c]
HTH, Robert.
Robert W. schrieb:> Das stimmt schon so, allerdings ist der Rest immer noch in BCD-Codiert.>> Wenn die Sekunden BCD-codiert sind und sich in Wert das Sekundenbyte> befindet, kannst Du das folgendermaßen auflösen:>> [c]> unsigned char SekundenString[3];>> SekundenString[0] = ((Wert & 0x70) >> 4) + 0x30;> // Zehner-Stelle auswerten> // (Wert & 0x70) wertet die Bits 6, 5 und 4 aus (und blendet Bit-7> aus)> // >> 4 schiebt die Bits auf die Pos. 2, 1 und 0> // + 0x30 gibt den Offset für ein Zeichen im ASCII-Format dazu> SekundenString[1] = (Wert & 0x0F) + 0x30;> // Einer-Stelle auswerten> SekundenString[2] = 0x00; // String-Ende> [\c]>> HTH, Robert.
Oh gott ja.. jetzt sehe ich auch woran mein Weiterkommen gescheitert
ist. Ich bin die ganze zeit davon ausgegangen dass die einer und zehner
stellen zusammen in allen 7 bits sind. Also 0101001 --> 41... das ist
aber ja nicht BCD...
Ach Mist 3 Stunden um alle falschen Ecken gedacht.
Ich werde heute Abend dann versuchen diesen Lösungsansatz an mein
Programm anzupassen und werde dann Bericht erstatten.
Eins ist klar.. Nachtschichten schlagen stark aufs Gemüt. ;-)
>0101001 --> 41..
Wenn die Binärzahl linksbündig ist (sind ja nur 7 bit), dann steht da
nicht 41 sondern 29,
wenn das dann der BCDcode ist, so sind die Zehner eben 2, die Einer eben
9.
Schau Dir doch nochmals die Codierung an. Binär, hex, bcd, dezimal was
es sonst noch gibt.
Nabend Leute,
vielen Dank für eure Hilfe, dass hat schonmal wunderbar geklappt.
Nun hab ich das nächste (aber etwas kleinere Problem).
Ich habe die ausgelesenen Sekunden nun so verarbeitet, dass ich sie
bereits auf dem Display anzeigen und laufen lassen kann.
Nun wollte ich aber das Wort "Sekunden:" vorne anhängen. Also suchte ich
nochmal nach der Funktion um Zeichenketten und Variablen in einem String
zu verketten und habe mich erstmal mit strcat() veruscht:
1
unsignedcharBCD10;
2
unsignedcharBCD1;
3
charstring;
4
charstringsek1[20];
5
charstringsek10[20];
6
BCD10=((TWDR&0x70)>>4);
7
BCD1=(TWDR&0x0F);
8
string="Sekunden:";
9
itoa(BCD1,stringsek1,10);
10
itoa(BCD10,stringsek10,10);
11
strcat(string,stringsek10);
12
strcat(string,stringsek1);
13
lcd_string_xy(0,0,string);
Nun.. die Funktion verrichtet ihre Arbeit soweit das Zunächst auch
"Sekunden: 46" auf dem Display steht (46 ist nur ein Beispiel).
Allerdings hängt diese Funktion nun in jedem Zyklus die neue Sekunde
hinten dran. Sodass der String sich immer weiter verlängert.
"Sekunden: 46474849..".
Ich dachte mit der Zeile
1
string="Sekunden:";
Gebe ich dem String wieder nur den Wert "Sekunden:"
Gruß
Der Vollständigkeithalber möchte ich noch hinzufügen dass der oben
genannte Code sich in einer while-schleife befindet. Deswegen der immer
wiederkehrende Zyklus.
Mein Ziel ist wie bestimmt zu erkennen ist, das ich das Datum und die
Uhrzeit auf dem LCD wiedergeben möchte.
Das heisst dass ich irgendwie einen String zusammensetzen und ans LCD
senden muss der wie folgt aussieht:
varWoTag "den" vatTag "." varMonat "." varJahr " " varStunde ":" var
Minute
(alles mit var sind die sich verändernden Werte und alles in " sind die
festen Strings)
Gruß
Hallo,
zur Stringverarbeitung solltest Du folgenden Artikel lesen:
http://www.mikrocontroller.net/articles/String-Verarbeitung_in_C
In Deiner initialisierung von "string" (char string;) fehlt die
Arrayangabe -> char string[20];
Wenn Du den String wieder nur den Wert "Sekunden\0" geben willst, kannst
Du die Funktion strcpy verwenden. Also statt:
string = "Sekunden:";
einfach:
strcpy(string, "Sekunden: ");
verwenden.
Übrigens die Initialisierungen der Variablen stringsek1 und stringsek10
sind reichlich groß dimensioniert, diese sollten letztlich immer nur ein
Zeichen und das Stringabschlußzeichen '\0' enthalten. Hier sollte der
Wert [2] statt [20] ausreichen:
char stringsek1[2];
char stringsek10[2];
BG,
Robert.
Christian O. schrieb:> Das heisst dass ich irgendwie einen String zusammensetzen und ans LCD> senden muss der wie folgt aussieht:>> varWoTag "den" vatTag "." varMonat "." varJahr " " varStunde ":" var> Minute
sprintf ist dein Freund.
Christian O. schrieb:> Nun.. die Funktion verrichtet ihre Arbeit
wobei das so ziemlich die komplzierste Variante ist, die ich mir
vorstellen kann.
Wenn du sprintf, wie von PeDa vorgeschlagen benutzen kannst, dann zb so
1
unsignedcharBCD;
2
charstring[20];
3
4
BCD=((TWDR&0x70)>>4)+(TWDR&0x0F);
5
sprintf(string,"Sekunden: %02u",BCD);
6
lcd_string_xy(0,0,string);
Allerdings erhebt sich auch die Frage, wozu man mit dem Text 'Sekunden'
rummachen muss. AUf dem LCD sieht sowieso kein Mensch, ob du jetzt nur
eine einzige Ausgabe machst, oder deren 2.
1
unsignedcharBCD;
2
charstring[3];
3
4
BCD=((TWDR&0x70)>>4)+(TWDR&0x0F);
5
sprintf(string,"%02u",BCD);
6
lcd_string_xy(0,0,"Sekunden: ");
7
lcd_string_xy(10,0,string);
was sich in einem verringerten Speicherverbrauch für den temporären
String niederschlägt.
Ist man soweit dann kann man sich fragen, wozu man jetzt eigentlich den
sprintf noch braucht. denn irgendwie ist das etwas witzlos, zuerst die
beiden BCD Ziffern zu einer Zahl zusammenzusetzen, nur damit sprintf die
dann wieder auseinanderdröselt. Das eine ist die Umkehrung vom anderen.
Und aus einer einzelnen Ziffer das entsprechende Zeichen zu machen, ist
aber eine leichte Übung.
An dieser Stelle wird man sich überlegen, dass man eine Funktion
bräuchte, die keinen kompletten String ausgibt, sondern nur ein
einzelnes Zeichen.
Die Funktion wirst du wohl vorrätig haben, dann diese Funktionalität
wird ja von lcd_string_xy auch gebraucht. Tatsächlich ist ja die Ausgabe
eines Strings nichts anderes als in einer Schleife jedes einzelne
Zeichen des Strings auszugeben.
Dann vereinfacht sich der Code zu
1
lcd_string_xy(0,0,"Sekunden: ");
2
3
lcd_char(((TWDR&0x70)>>4)+'0');
4
lcd_char((TWDR&0x0F)+'0');
Wenn man möchte, könte man sich die Bitoperationen noch ein wenig
'schöner' gestalten, indem man sie in einem Makro versteckt.
1
#define BCD_HIGH(x) ( ((x) & 0x70) >> 4 )
2
#define BCD_LOW(x) ( (x) & 0x0F )
3
4
....
5
6
lcd_string_xy(0,0,"Sekunden: ");
7
8
lcd_char(BCD_HIGH(TWDR));
9
lcd_char(BCD_LOW(TWDR));
und da kann man doch jetzt wirklich nicht mehr meckern. Das ist kurz,
einfach, übersichtlich und verständlich.
Karl Heinz schrieb:> Dann vereinfacht sich der Code zu lcd_string_xy( 0, 0, "Sekunden: ");>> lcd_char( ((TWDR & 0x70) >> 4) + '0' );> lcd_char( (TWDR & 0x0F) + '0' );>> Wenn man möchte, könte man sich die Bitoperationen noch ein wenig> 'schöner' gestalten, indem man sie in einem Makro versteckt.#define> BCD_HIGH(x) ( ((x) & 0x70) >> 4 )> #define BCD_LOW(x) ( (x) & 0x0F )>> ....>> lcd_string_xy( 0, 0, "Sekunden: ");>> lcd_char( BCD_HIGH( TWDR ) );> lcd_char( BCD_LOW( TWDR ) );>> und da kann man doch jetzt wirklich nicht mehr meckern. Das ist kurz,> einfach, übersichtlich und verständlich.
darüber habe ich auch die ganze Zeit gegrübelt ob man nicht Platz spren
könnte wenn sprintf nicht mehr genutzt wird !
Dein Vorschlag "& 0x70) >> 4 sowie & 0x0F)" war ja auch schon in bcd2int
im Uhrprogramm der CT, ich wollte das fast wieder ausbuddeln!
Und dieses einfach mit char('0') zu addieren die logische Konsequenz um
die sprintf eben nicht mehr zu nutzen.
Aber keine Ahnung was das im MC spart im m1284p bis m2560 ist mir der
Platz nie eng geworden, im m328p schon eher.
Joachim B. schrieb:> Aber keine Ahnung was das im MC spart im m1284p bis m2560 ist mir der> Platz nie eng geworden, im m328p schon eher.
Mir gehst auch in erster Linie gar nicht so sehr um den verbrauchten
Platz. Ich verwende in meinen Programmen auch sprintf, weil es eine
einfache Sache ist und ich die Formatieroptionen ausnutzen kann.
Mir geht es vielmehr darum, dem TO zu zeigen, dass sein
unübersichtlicher Codewust auch durch ein bischen Nachdenken und dem
Nichtbestehen auf bestimmten Funktionen wie zb itoa, viel einfacher
formuliert werden kann, was dann ja auch der Fehlervermeidung zu gute
kommt.
Das andere ist das hier
> Mein Ziel ist wie bestimmt zu erkennen ist, das ich das Datum und> die Uhrzeit auf dem LCD wiedergeben möchte.> Das heisst dass ich irgendwie einen String zusammensetzen und> ans LCD senden muss
Nein, das heisst es eben nicht. Selbst wenn da mehrere Informationen
auszugeben sind, bedeutet das nicht, dass man das zuerst alles in einem
einzigen String zusammenfassen muss. Ganz im Gegenteil wird man das
nicht tun, sondern nur die Dinge am LCD updaten, die sich tatsächlich
verändert haben. Das Datum verändert sich für 24 Stunden nicht. Daher
gibt es überhaupt keinen Grund, das in jeder Sekunde wieder neu
hinzuschreiben.
Versteift man sich aber darauf, zuerst im µC einen kompletten String
zusammenzubauen, dann ist dieser Weg kontraproduktiv, weil er von der
einfachen Lösung, einfach mit mehreren Ausgabeanweisungen die Dinge nach
und nach (und mglw. nur bei Bedarf) auszugeben, ablenkt. Wobei natürlich
dieses 'nach und nach' nur aus µC besteht. Für uns Menschen ist das
immer noch viel zu schnell. Selbst 'nach und nach' klatscht der µC das
alles einzeln so schnell hin, dass wir da nicht mehr mitkommen. Für uns
erscheint 'die Ausgabe' auf einen Schlag, selbst dann wenn da mehrere
Funktionsaufrufe involviert sind.
Karl Heinz schrieb:> Versteift man sich aber darauf, zuerst im µC einen kompletten String> zusammenzubauen, dann ist dieser Weg kontraproduktiv, weil er von der> einfachen Lösung, einfach mit mehreren Ausgabeanweisungen die Dinge nach> und nach (und mglw. nur bei Bedarf) auszugeben, ablenkt. Wobei natürlich> dieses 'nach und nach' nur aus µC besteht. Für uns Menschen ist das> immer noch viel zu schnell.
und trotzdem mache ich es auch so den string zusammenzubauen.
1. für die LCD Ausgabe
2. für die serielle Ausgabe
aber ich mache das nicht permanent im main loop sondern genau 3x pro
Sekunde wenn ein LCD update fällig wird, Timer count 10ms bei 0, wird
von 33 rückwärts gezählt if (lcd_update) lcd_update--;
im Main loop if (!lcd_update) {String basteln, ausgeben, lcd_update=33;}
klar reicht 1x pro Sekunde aber dann läuft eine Anzeige eventuell nicht
so flüssig oder springt.
Mit LCD update alle 330ms finde ich einen brauchbaren Kompromiss.