Forum: Mikrocontroller und Digitale Elektronik Probleme mit Protokollen UART/I2c


von Nils K. (nebukadnezar)


Lesenswert?

Ahoi liebe Forengemeinde,
ich versuche gerade, mir die Grundlagen der I2C Programmierung 
anzueignen. Habe mir zur  Hilfestellung folgendes Tutorial angeschaut

http://www.rn-wissen.de/index.php/TWI_Slave_mit_avr-gcc

Habe den Code soweit verstanden, an der Umsetzung hapert es noch. Der 
Slave läuft soweit. Laut implementierung  gibt er über UART im 
1/2-Sekunden Takt zehn Zahlen aus.
Die Implementierung des Masters lief leider nicht so gut. Es wird zwar 
alles kompiliert, nur tut sich leider nichts. Um den Master zu testen 
habe ich auch hier eine Ausgabe per UART eingefügt. Nun gibt mir das 
Hyperterminal leider überhaupt nichts aus, noch nicht einmal das 
Initialisieren der UART Schnittstelle (was laut Code per uart_puts 
bestätigt wird). Habe den gleichen Controller genutzt wie beim SLAVE, am 
µC kann es folglich nicht liegen. Bin ein wenig verzweifelt, da der UART 
Code des Masters identisch mit dem des Slave ist. Bei den Controllern 
handelt es sich um Atmega328p, Software ist AtmelStudio6
Über Hilfe würde ich mich sehr freuen!

Hier der Code:
1
// I2C-Master-Routinen von Peter Fleury verwenden
2
// siehe http://homepage.hispeed.ch/peterfleury/avr-software.html#libs
3
// Hier sind auch die Dateien: i2cmaster.h und twimaster.c zu finden, die benötigt werden
4
// Letztes Update des Codes 5. April 2010 durch HannoHupmann
5
6
#include <util/twi.h>       //enthält z.B. die Bezeichnungen für die Statuscodes in TWSR
7
#include <avr/interrupt.h>  //dient zur Behandlung der Interrupts
8
#include <stdint.h>       //definiert den Datentyp uint8_t
9
#include <avr/io.h>
10
#include "i2cmaster.h"
11
#include "uart.h"           //UART-Lib von P- Fleury
12
#include <stdlib.h>         //nötig für Zahlumwandlung mit itoa
13
#include <util/delay.h>
14
15
/* define CPU frequency in Mhz here if not defined in Makefile */
16
#ifndef F_CPU
17
#define F_CPU 8000000UL
18
#endif
19
#define BAUD 9600 //Baudrate
20
#define SLAVE_ADRESSE 0x50 //Die Slave-Adresse
21
22
uint8_t byte1 = 42;
23
uint8_t byte2 = 43;
24
uint8_t byte3 = 44;
25
26
int main(void)
27
{
28
  //Serielle Schnittstelle aktivieren
29
  uart_init((UART_BAUD_SELECT((BAUD),F_CPU)));
30
  uart_puts("I2C-Test\r\n");
31
  uart_puts("\r\n");
32
  uart_puts("\r\n"); //Leerzeile
33
34
  i2c_init();         // init I2C interface
35
36
while(1) {
37
  if(!(i2c_start(SLAVE_ADRESSE+I2C_WRITE))) //Slave bereit zum schreiben?
38
  {
39
    uart_puts("Slave bereit zum schreiben \r\n");
40
    i2c_write(0x00);  // Buffer Startadresse setzen
41
    i2c_write(byte1); // Drei Bytes schreiben...
42
    i2c_write(byte2);
43
    i2c_write(byte3);
44
    i2c_stop();       // Zugriff beenden
45
  }
46
  else
47
  {
48
    /* Hier könnte eine Fehlermeldung ausgegeben werden... */
49
    uart_puts("Slave nicht bereit zum schreiben \r\n");
50
  }
51
52
53
54
  if(!(i2c_start(SLAVE_ADRESSE+I2C_WRITE))) //Slave bereit zum lesen?
55
  {
56
    i2c_write(0x00); //Buffer Startadresse zum Auslesen
57
    i2c_rep_start(SLAVE_ADRESSE+I2C_READ); //Lesen beginnen
58
59
    byte1= i2c_readAck(); // Bytes lesen...
60
    byte2= i2c_readAck();
61
    byte3= i2c_readNak(); // letztes Byte lesen, darum kein ACK
62
    i2c_stop();           // Zugriff beenden
63
  }
64
  else
65
  {
66
    /* Hier könnte eine Fehlermeldung ausgegeben werden... */
67
  }
68
69
  if ((byte1 != 42)||(byte2 != 43)||(byte3 != 44))
70
  {
71
    /* Die Bytes wurden nicht korrekt übertragen und wieder gelesen! */
72
    uart_puts("Slave nicht bereit zum lesen \r\n");
73
    _delay_ms(500);
74
  }
75
}  
76
//  for(;;);
77
}

von Karl H. (kbuchegg)


Lesenswert?

> #include "uart.h"           //UART-Lib von P- Fleury

Die UART Lib vom Fleury Peter ist interrupt getrieben.
Wenn du also keinen sei() machst, dann laufen auch keine Interrupts und 
dein Programm gibt solange in den internen Puffer aus, bis dieser voll 
ist. Danach hängt dann alles.
Ein sei() VOR dem ersten uart_puts() behebt dieses.

von Nils K. (nebukadnezar)


Lesenswert?

Vielen Dank für die schnelle Antwort! Leider ist das problem noch nicht 
ganz behoben. Wenn ich den µC resette, empfange ich drei Zeichen, das 
wars leider auch schon. Diese sind auch nicht das erwartete, sondern 
eher kryptisch. Das Problem hatte ich beim Slave jedoch auch, war aber 
nach 1-2 Sekunden weg.
Bin ein wenig mit meinem Latein am Ende :(

von Karl H. (kbuchegg)


Lesenswert?

Nils K. schrieb:
> Vielen Dank für die schnelle Antwort! Leider ist das problem noch nicht
> ganz behoben.

Dann speck erst mal ab.
1
int main(void)
2
{
3
  uart_init((UART_BAUD_SELECT((BAUD),F_CPU)));
4
5
  sei();
6
  while( 1 ) {
7
    uart_puts("Hello world\r\n");
8
  }
9
}

Solange das nicht einwandfrei funktioniert, brauchst du I2C noch nicht 
mit dazu nehmen.

> Wenn ich den µC resette, empfange ich drei Zeichen, das
> wars leider auch schon. Diese sind auch nicht das erwartete, sondern
> eher kryptisch.

"kryptische" zeichen sind meistens ein sicheres Indiz, dass das hier
1
#ifndef F_CPU
2
#define F_CPU 8000000UL
3
#endif
nicht der Realität entspricht. Dein µC läuft nicht mit 8Mhz.

von Nils K. (nebukadnezar)


Lesenswert?

Den Tipp mit dem reduzieren des Codes habe ich umgesetzt, kam jetzt 
durchgehend was bei mir anb, leider immernoch Schrott.
Was die Frequenz angeht, war ich mir sehr sicher, dass Frequenz und 
Baudrate stimmen, da es ja beim Slave auch funktionier hat. Um sicher zu 
gehen, habe ich den Code vom Slave nochmal raufgespielt. Dieser geht 
plötzlich auch nicht mehr.
Da ich noch gerne Anfängerfehler mache, hier der Ablauf, wie ich 
Übersetzte, vielleciht ist da ja der Wurm drin:

Programm erstelle ich mit  AVRStudio6 und kompiliere es. Zum übertragen 
nutze ich das myAVR Progtool. Hexfile ausfählen, EEprom in Ruhe lassen, 
brennen. Funktioniert auch immer ohne Fehlermeldung. Bei den Fusebist 
deaktiviere ich "divide clock by 8" um auf 8MHz zu kommen.
Zur Taktfrequent: Auf einen externen Oszillator muss ich verzichten, da 
der Controller in ein Bluetoothmodul (BCA8-BTM) eingebettet ist.

von Eumel (Gast)


Lesenswert?

Nils K. schrieb:
> Den Tipp mit dem reduzieren des Codes habe ich umgesetzt, kam jetzt
> durchgehend was bei mir anb, leider immernoch Schrott.
> Was die Frequenz angeht, war ich mir sehr sicher, dass Frequenz und
> Baudrate stimmen, da es ja beim Slave auch funktionier hat. Um sicher zu
> gehen, habe ich den Code vom Slave nochmal raufgespielt. Dieser geht
> plötzlich auch nicht mehr.
> Da ich noch gerne Anfängerfehler mache, hier der Ablauf, wie ich
> Übersetzte, vielleciht ist da ja der Wurm drin:
>
> Programm erstelle ich mit  AVRStudio6 und kompiliere es. Zum übertragen
> nutze ich das myAVR Progtool. Hexfile ausfählen, EEprom in Ruhe lassen,
> brennen. Funktioniert auch immer ohne Fehlermeldung. Bei den Fusebist
> deaktiviere ich "divide clock by 8" um auf 8MHz zu kommen.
> Zur Taktfrequent: Auf einen externen Oszillator muss ich verzichten, da
> der Controller in ein Bluetoothmodul (BCA8-BTM) eingebettet ist.

Also läuft dein Controller mit dem internen RC Taktgeber? Das geht nicht 
gut, du brauchst einen Quarz!

von Malte S. (maltest)


Lesenswert?

UART und interner Oszillator ist so eine Sache. Acuh wenn du ihn 
kalibrierst, bleibt die Temperaturabhängigkeit. Zuverlässig ist was 
anderes.

von Nils K. (nebukadnezar)


Lesenswert?

Okay, werde mich drum kümmern. Danke!

von Nils K. (nebukadnezar)


Lesenswert?

Der Port für die externe Clock (XTAl1/ PB6) ist leider durch eine LED 
belegt. Jetzt funktionieren sowohl das SLAVE als auch das "Hello World" 
Programm. Scheint tatsächlich an der Temperatur gelegen zu haben. Beim 
Master Programm wird die UART-Initmeldung "I2C-Test" wiedergegeben. Das 
wars dann aber auch.  Im Moment habe ich noch keinen I2C Verbindung 
hergestellt, müsste also eine Fehlermeldung erscheinen.
Zum I2C bzw TWI habe ich noch eine Frage:
wenn ich einen Master und einen Slave habe (mehr brauch ich nicht), sind 
dann die Pullupwiderstände nötig?

von Jörg E. (jackfritt)


Lesenswert?

9600 baud sollten auch ohne quarz machbar sein.Höhere baudraten werden 
kritischer.

von Karl H. (kbuchegg)


Lesenswert?

3% sind 3%. Egal bei welcher Baudrate.

von Jörg E. (jackfritt)


Lesenswert?

Du hast Recht sorry nehme alles zurück.

von Nils K. (nebukadnezar)


Lesenswert?

Tatsächlich funktioniert es gerade mit 1MHz Takt und einer Baudrate von 
4800, bei 8MHz und höheren baud jedoch nicht. Ist ja auch nur für 
Testzwecke, TWI ist weitaus störender gerade :)

von Nils K. (nebukadnezar)


Lesenswert?

Bin ziemlich sicher, dass das Problem an folgender Stelle hängen bleibt.
1
if(!(i2c_start(SLAVE_ADRESSE+I2C_WRITE))) //Slave bereit zum schreiben?

Laut twimaster.c sehen die Funktionen folgendermaßen aus:
1
unsigned char i2c_start(unsigned char address)
2
{
3
    uint8_t   twst;
4
5
  // send START condition
6
  TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN);
7
8
  // wait until transmission completed
9
  while(!(TWCR & (1<<TWINT)));
10
11
  // check value of TWI Status Register. Mask prescaler bits.
12
  twst = TW_STATUS & 0xF8;
13
  if ( (twst != TW_START) && (twst != TW_REP_START)) return 1;
14
15
  // send device address
16
  TWDR = address;
17
  TWCR = (1<<TWINT) | (1<<TWEN);
18
19
  // wail until transmission completed and ACK/NACK has been received
20
  while(!(TWCR & (1<<TWINT)));
21
22
  // check value of TWI Status Register. Mask prescaler bits.
23
  twst = TW_STATUS & 0xF8;
24
  if ( (twst != TW_MT_SLA_ACK) && (twst != TW_MR_SLA_ACK) ) return 1;
25
26
  return 0;
27
28
}/* i2c_start */
1
unsigned char i2c_write( unsigned char data )
2
{  
3
    uint8_t   twst;
4
    
5
  // send data to the previously addressed device
6
  TWDR = data;
7
  TWCR = (1<<TWINT) | (1<<TWEN);
8
9
  // wait until transmission completed
10
  while(!(TWCR & (1<<TWINT)));
11
12
  // check value of TWI Status Register. Mask prescaler bits
13
  twst = TW_STATUS & 0xF8;
14
  if( twst != TW_MT_DATA_ACK) return 1;
15
  return 0;
16
17
}/* i2c_write */
Mir ist nicht ganz klar, was dieser Ausdruck bewirkt: 
"i2c_start(SLAVE_ADRESSE+I2C_WRITE)"
SlAVE_ADRESSE erklärt sich selbst, aber warum eine Funktion addieren?

von Georg G. (df2au)


Lesenswert?

Nils K. schrieb:
> SlAVE_ADRESSE erklärt sich selbst, aber warum eine Funktion addieren?

Weil in "C" auch GROSS-klein-Schreibung beachtet wird.
Es gibt die Funktion i2c_write und die Konstante I2C_WRITE. Das ist ein 
wenig ungeschickt vom Programmierer, aber legal.

von Jörg E. (jackfritt)


Lesenswert?

Das Problem liegt meist darin das der Bus hängt. Ich habe in meine 
Routinen deswegen einen Abbruch nach zeit eingebaut und probiere es 
später nochmal.
Einfach bei den while noch eine zeit mit aufnehmen.

Addieren zur Slave Adresse, weil damit lesen oder schreiben ausgewählt 
wird.
Lies dich nochmal in das Thema ein bzw. den Code.

Gruß,
Jörg

von Nils K. (nebukadnezar)


Lesenswert?

Das erklärt einiges, danke! Werd wohl doch nochmal das Datenblatt lesen 
müssen.

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
Noch kein Account? Hier anmelden.