Hallo,
nachdem ich I2C nun doch noch zum Laufen gebracht habe, sitze ich nun
wieder vor einem Problem: 1 | #define F_CPU 16000000UL
| 2 | #include <avr/io.h>
| 3 | #include <util/delay.h>
| 4 | #include <uart.h>
| 5 | #include <avr/interrupt.h>
| 6 | #include <i2cmaster.h>
| 7 |
| 8 | #define UART_BAUD_RATE 9600
| 9 |
| 10 | #define DS1307 0xD0
| 11 |
| 12 | unsigned char buffer;
| 13 | char seconds;
| 14 |
| 15 |
| 16 | char decToBcd(char val)
| 17 | {
| 18 | return ((val / 10 * 16) + (val % 10));
| 19 | }
| 20 |
| 21 | // Convert binary coded decimal to normal decimal numbers
| 22 | char bcdToDec(char val)
| 23 | {
| 24 | return ((val / 16 * 10) + (val % 16));
| 25 | }
| 26 |
| 27 | int main(void)
| 28 | {
| 29 | sei();
| 30 | DDRB = (1 << PB0);
| 31 |
| 32 |
| 33 | uart_init( UART_BAUD_SELECT(UART_BAUD_RATE,F_CPU) ); //uart initialisieren
| 34 | i2c_init(); //i2c initialisieren
| 35 |
| 36 | //schreibe CH-Bit
| 37 | i2c_start_wait(DS1307+I2C_WRITE);
| 38 | i2c_write(0x81);
| 39 | i2c_stop();
| 40 | //*schreibe CH Bit
| 41 |
| 42 | //CH Bit gesetzt?
| 43 | i2c_start_wait(DS1307+I2C_WRITE);
| 44 | i2c_write(0x00);
| 45 | i2c_rep_start(DS1307+I2C_READ);
| 46 | buffer = i2c_readNak();
| 47 | i2c_stop();
| 48 |
| 49 | if ((buffer & 0x80)) //Falls CH Bit gesetzt ==> löschen
| 50 | {
| 51 | i2c_start_wait(DS1307+I2C_WRITE);
| 52 | i2c_write(0x00);
| 53 | i2c_write((buffer & 0x7F));
| 54 | i2c_stop();
| 55 | uart_puts("CH Bit gelöscht\n");
| 56 | }
| 57 |
| 58 |
| 59 | while(1)
| 60 | {
| 61 | PORTB ^= ( 1 << PB0 ); // Toggle PB0
| 62 |
| 63 | i2c_start_wait(DS1307+I2C_WRITE);
| 64 | i2c_write(0x00); //Seconds Register
| 65 | i2c_rep_start(DS1307+I2C_READ);
| 66 | buffer = i2c_readNak(); // read seconds
| 67 | i2c_stop();
| 68 |
| 69 | seconds = bcdToDec((buffer & 0x7F));
| 70 |
| 71 | _delay_ms(500);
| 72 | }
| 73 | }
|
(1) Obwohl ich Anfang im Seconds Register zwangsweise das CH-Bit setze,
scheint das Programm nicht in die If-Abfrage reinzugehen, um das Bit
wieder zu löschen.
(2) In seconds sollen ja jetzt die Sekunden stehen. So wie ich Peter
Fleury's UART-Library verstanden habe, kann ich seconds ja nicht einfach
uart_puts(); übergeben. Kann ich das nun in einen String umwandeln?
Hoffe, ihr könnt mir einen Tipp geben.
Grüße
Dominik
Dominik Gebhardt schrieb:
> (2) In seconds sollen ja jetzt die Sekunden stehen. So wie ich Peter
> Fleury's UART-Library verstanden habe, kann ich seconds ja nicht einfach
> uart_puts(); übergeben. Kann ich das nun in einen String umwandeln?
FAQ gleich der erste Punkt
Die Überlegung, die dich zu diesem Code gebracht hat 1 | //schreibe CH-Bit
| 2 | i2c_start_wait(DS1307+I2C_WRITE);
| 3 | i2c_write(0x81);
| 4 | i2c_stop();
| 5 | //*schreibe CH Bit
|
ist mit unklar.
Nachdem du den Slave adressiert hast, ist das erste Byte, welches da auf
den Weg bringst, die Register Adresse. Das wäre bei dir 0x81. Das ist
aber nicht das, was du willst. Du willst ja das Register 0 adressieren
und dort die 0x81 (also BCD-01 plus gesetztes CH-Bit) einschreiben.
Also müsste das lauten
1 | //schreibe CH-Bit
| 2 | i2c_start_wait(DS1307+I2C_WRITE);
| 3 | i2c_write(0x00);
| 4 | i2c_write(0x81);
| 5 | i2c_stop();
| 6 | //*schreibe CH Bit
|
Karl Heinz Buchegger schrieb:
> Dominik Gebhardt schrieb:
>
>> (2) In seconds sollen ja jetzt die Sekunden stehen. So wie ich Peter
>> Fleury's UART-Library verstanden habe, kann ich seconds ja nicht einfach
>> uart_puts(); übergeben. Kann ich das nun in einen String umwandeln?
>
> FAQ gleich der erste Punkt
Obwohl.
Bei dir gehts viel einfacher. Genau das ist ja der springende Punkt,
warum die Uhr überhaupt in BCD zählt. Eben weil man dann ganz trivial
eine Textrepräsentierung der Zahl erzeugen kann, ohne viel rechnen zu
müssen.
1 | ....
| 2 |
| 3 | buffer = i2c_readNak(); // read seconds
| 4 | i2c_stop();
| 5 |
| 6 | char num[3];
| 7 | num[0] = (( buffer & 0x7F ) >> 4 ) + '0';
| 8 | num[1] = (( buffer & 0x0F ) + '0';
| 9 | num[2] = '\0';
| 10 |
| 11 | uart_puts( num );
| 12 | uart_puts( "\n" );
| 13 |
| 14 | ....
|
Asche auf mein Haupt!
Weiter unten immer darauf geachtet, dass ich erst die Adresse schreibe
und dann das Byte und dort vergessen; peinlich.
Danke dir, Karl Heinz. Funktioniert nun so, wie es soll.
Vielen Dank euch beiden für die sehr schnelle Hilfe! :)
Viele Grüße
Dominik
Edit: Gerade nochmal den die verlinkten FAQ angesehen. Mit itoa(); hatte
ich es probiert. Allerdings stand in meiner Variablen Müll drin,
weswegen ich es wieder verworfen hatte. Dann war ich ja doch auf der
richtigen Fährte und meine Umrechnung stimmt doch nicht.
Dominik Gebhardt schrieb:
> weswegen ich es wieder verworfen hatte. Dann war ich ja doch auf der
> richtigen Fährte und meine Umrechnung stimmt doch nicht.
Was mir als allererstes aufgefallen ist.
Wenn du in deinem Code mit 'Bytes' operierst, dann ist der Datentyp
dafür ein 'unsigned char' oder noch besser ein 'uint8_t'. Aber 'char'
ist nicht der Datentyp, den du für Arbeiten mit Bytes benutzen willst.
Bei 'char' ist nicht festgelegt, ob der ein Vorzeichen hat oder nicht.
Du willst kein Vorzeichen und zwar willst du das ganz sicher nicht.
Daher willst du dich auch nicht deinem Compilerbauer ausliefern, wie er
das für char festgelegt hat. An dieser Stelle bist du lieber explizit
und verlangst einen uint8_t
D.h. das hier 1 | char decToBcd(char val)
| 2 | {
| 3 | return ((val / 10 * 16) + (val % 10));
| 4 | }
|
ist besser als 1 | uint8_t decToBcd(uint8_t val)
| 2 | {
| 3 | return ((val / 10 * 16) + (val % 10));
| 4 | }
|
(und die Umkehrung genauso.)
Und genauso willst du einen
und keinen char. Denn Sekunden sind nun mal nicht negativ.
Dominik Gebhardt schrieb:
> Edit: Gerade nochmal den die verlinkten FAQ angesehen. Mit itoa(); hatte
> ich es probiert.
itoa für einen int oder int8_t. Also etwas mit Vorzeichen
utoa für einen unsigned int oder uint8_t. Also etwas ohne
Vorzeichen
Es obligt deiner Verantwortung, die richtige Funktion zu benutzen! itoa
bzw. utoa ist das wurscht! Die Funktion schnappt sich die übergebenen
Bytes und interpretiert sich diese Bytes als mit/ohne Vorzeichen, je
nach Funktion.
Dominik Gebhardt schrieb:
> Asche auf mein Haupt!
> Weiter unten immer darauf geachtet, dass ich erst die Adresse schreibe
> und dann das Byte und dort vergessen; peinlich.
Jetzt weißt du, warum man sich für alles mögliche Funktionen schreibt
:-)
Oh man, immer wenn ich denke, ich kann jetzt einen Schritt vorwärts
gehen, habe ich das Gefühl, dass ich doch wieder zwei zurück gehe. :D
> itoa für einen int oder int8_t. Also etwas mit Vorzeichen
> utoa für einen unsigned int oder uint8_t. Also etwas ohne
> Vorzeichen
Genau, hatte halt den Fehler gemacht char statt unsigned char zu
verwenden. Deswegen hatte ich noch itoa(); genutzt.
> Jetzt weißt du, warum man sich für alles mögliche Funktionen schreibt
> :-)
Damit man nicht einmal den Fehler, obwohl man es sonst immer richtig
gemacht hat. Gleich mal eine Funktion geschrieben, in der man explizit
Adresse, Adressregister und Inhalt übergeben muss. :)
Eine Frage zu deiner Berechnung habe ich aber gerade noch:
1 | char num[3];
| 2 | num[0] = (( buffer & 0x7F ) >> 4 ) + '0';
| 3 | num[1] = (( buffer & 0x0F ) + '0';
| 4 | num[2] = '\0';
|
Die Bitmasken sind klar und die Bitverschiebung bei num[0] ist auch
klar. Aber wozu das +'0'? Hat das eine ähnliche Funktion wie '\0'? ('\0'
bezeichnet ja das Stringende).
Grüße und nochmals Danke für die ganzen Informationen!
Hallo,
num[0] und num[1] enthält dann die Dezimalzahl, derer vorher in BCD
repräsentierten Zahl.
Somit enthält num[] einen C-String mit der BCD-Zahl als String.
Dominik Gebhardt schrieb:
> Die Bitmasken sind klar und die Bitverschiebung bei num[0] ist auch
> klar. Aber wozu das +'0'?
Weil am anderen Ende deiner UART ein Terminal sitzt. Und das will ASCII
Codes sehen, damit es was auf den Monitor hinpinselt.
Nach dem Ausmaskieren und Verschieben hast du bereits numerische Ziffern
von 0 bis 9. Die sind gut, kannst sie aber noch nicht gebrauchen. Damit
das Terminal das Zeichen '0' hinpinselt, musst du ihm den ASCII Code für
'0' schicken und nicht die Zahl 0. Damit es eine '1' hinpinselt, musst
du ihm den ASCII Code für '1' schicken und nicht die Zahl 1. Damit ....
Glücklicherweise sind im ASCII Code die Codes für die Ziffernzeichen
genau so angeordnet, dass sie hintereinander kommen. D.h. wenn du zum
ASCII Code von '0' noch 5 dazuzählst, kriegst du den ASCII Code des
Zeichens '5'.
Und da uns das zu fad ist, uns selber aus einer ASCII Tabelle
rauszusuchen, welches der ASCII Code für '0' ist (der wäre 0x30), lassen
wir das den Compiler machen. Der kann das genausogut, indem ich ihm
einfach das Zeichen hinschreibe und ich muss mich nicht mit
irgendwelchen Hex-Codes als ASCII Codes rumplagen. Wenn ich in c
char c;
den ASCII Code des Zeichens 'p' haben will, dann schreibe ich auch
c = 'p';
und nicht
c = 0x70;
oder gar
c = 112;
obwohl technisch gesehen alle 3 Varianten genau dasselbe machen. Für die
Maschine mag das dasselbe sein (nämlich ein immer gleiches Bitmuster in
den Character c zu laden), aber für mich als Mensch macht das einen
gewaltigen Unterschied. Bei c = 'p' sehe ich auf einen Blick was Sache
ist. Bei c = 112 seh ich erst mal gar nichts, sondern muss eine ASCII
Tabelle zu Hilfe nehmen, um zu wissen welchen ASCII Code ich da gerade
in c gespeichert habe.
In der EDV sind alles Zahlen! Auch Zeichen sind Zahlen. Wenn dein
Terminal ein 'A' auf den Bildschirm malt, dann deswegen, weil es ein
Byte mit dem Wert 0x41 bekommen hat und das laut ASCII Tabelle der Code
für 'A' ist.
Bis zu dem Punkt, dass in num[x] dann die einzelnen Zahlen für Zehner
und Einser stehen, hatte ich es verstanden. Aber dass das '0' also
ASCII-Code addiert wird und mann dann auf den ASCII-Code für z.B. 5
kommt, war mir nicht klar.
Vielen Dank für die asuführliche Erklärung und für eure Geduld mit mir!
Nun ist aber alles klar!
Konnte jetzt auch meine Berechnungsfunktion korrekt über den UART
ausgeben. Nun weiß ich auch, warum da vorher Murks drin stand. Deine
Berechnung für BCD ist aber durchaus eleganter und hier auch
einleuchtender als meins und werde es deshalb auch beibehalten. Wollte
nur wissen, ob ich jetzt alles richtig verstanden habe und es auch mit
dem alten Code zum Laufen bekomme. Dank eurer Hilfe ist dem jetzt so! :)
Vielen Dank und viele Grüße
Dominik
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
|