Forum: Mikrocontroller und Digitale Elektronik Die I2C Schnittstelle eines PIC16F876A initialisieren


von Sebastian Bülow (Gast)


Angehängte Dateien:

Lesenswert?

Hallo!
Ich soll hier im Rahmen meiner Diplomarbeit einen PIC16F876A
programmieren. Ich komme allerdings überhaupt nicht weiter. Ich will
über die I^2C Schnittstelle des Controllerseinen EEPROM mit Daten
beschreiben. Es scheitert jedoch schon an der Initialisierung der
Schnittstelle. der PIC soll im MAster Mode arbeiten da ein
Rückschreiben vom EEPROM zum PIC nicht erwünscht ist
Hier ist mal der Code wie ich es probiert habe.



#include "pic168x76a.h"
#include "types_ksg1.h"

#fuses   HS,WDT,PUT,NOBROWNOUT,NOLVP,WRT_50%

#use delay(clock=4000000) // 4 MHz


void TransmitData(int);

void main(void)
{

int Adresse = 00110110;
int i;

//Setup Ports

TRISA = 0xff;    //All Ports are Outputs

TRISB = 0xC0;    //All Ports are Outputs  RB7/RB6 are Inputs

TRISC = 0xff;

// Setup Watchdog
    setup_wdt(WDT_288MS);



// Bits setzen:

//Slew rate control disabled for standard speed mode (100 kHz and 1
MHz)
//Disable SMBus specific inputs

SSPSTAT |=0x80;

//Bit3 I2C Master mode, clock = FOSC/(4 * (SSPADD + 1))
//Bit5 Enables the serial port and configures the SDA and SCL pins as
the serial port pins

SSPCON1 |=0x28;


// Bit rücksetzen
//Register&=~BIT;

// Bit toggeln
//Register^=BIT;

//for (i=0;i<6000000000;i++)
//{
//  PORTA = 0xff;
//  //PORTA = 0x00;
//}


TransmitData(Adresse);      //Daten senden


}



void TransmitData(Zieladresse)
{

// Start Enable Bit setzen SEN = 1
  SEN = 1;
  SSPBUF = Zieladresse;
while (BF)
{

}

}


Ich weiß wirklich nicht mehr weiter.
Ich bedanke mich schon einmal für die Hilfe.

Gruss Basti...

von Wolfram (Gast)


Lesenswert?

Hast du Pullups an SDA und SCL?
geh ans Oszi,und schau dir SCL an, vor dem Start sollte die Leitung
High sein. Nach Start Low.
Als nächstes kommt die Adresse. (9 Pulse auf der Leitung)
Wenn die Adresse stimmt bekommst du ein ACK vom EEPROM und kannst Daten
senden.
das letzte Datenbyte wird mit NACK bestätigt.
Wenn du ein Beispiel sehen willst für den Zugriff auf einen EEPROM bei
Winavr in den examples findest du eins.

von Sebastian Bülow (Gast)


Lesenswert?

Pullups habe ich dran. Beide Leitungen (SDA , SCL) sind vor dem Start
auf High. Nach dem Start geht der SCL tatsächlich auf LOW. Schon mal
ein gutes Zeichen.
Kann ich mit dem Oszi auch sehen wie die Adresse gesendet wird?
Es ist doch richtig dass ich die zu sendende Adresse einfach in das
SSPBUF Register schreibe mit dem Befehl
SSPBUF = Zieladresse;

von Sebastian Bülow (Gast)


Lesenswert?

Das Example habe ich leider nicht gefunden.
Das zweite Problem ist was ich habe, wenn ich starte gehen beide
Leitungen auf LOW. Sowohl SCL als auch SDA.

Gruss Basti...

von Wolfram (Gast)


Lesenswert?

Schau dir SDA auch mit an.
Ich kenne es von AVR aber es sollte analog beim PIC sein.
Im nächsten Schritt musst du noch angeben ob du lesen oder Schreiben
willst. Wahrscheinlich muss das mit der Zieladresse verknüpft werden
bevor es in das Datenregister geschrieben wird.
siehe Datenblatt.
Hast du die Modi und die Abfolge schon kapiert?

von Sebastian Bülow (Gast)


Lesenswert?

Laut Datenblatt muss ich das Start Enable Bit setzen mit SEN = 1;
Dann warten bis die Startbedingung erfüllt ist (SCL auf High und in SDA
ein Wechsel von High auf Low ) . Jetzt kann man die Daten in den SSPBUF
schreiben.
Wie ich die Wrtezeiten realisiere weiß ich auch nicht so richtig.

Gruss Basti...

von Wolfram (Gast)


Angehängte Dateien:

Lesenswert?

>Jetzt kann man die Daten in den SSPBUF schreiben
und das erste Byte was rüber geht gibt an welche Adresse und ob du
lesen oder schreiben willst.
nennt sich SLA+W /SLA+R schau ins Datenblatt wie man das auf einem Pic
erzeugt. Dann kommen die Datenbytes.
Anbei das schon erwähnte Beispiel, ist zwar für den AVR sollte aber
relativ gleich für den PIC sein.

von Sebastian Bülow (Gast)


Lesenswert?

Ja genau. Bei einem PIC ist es so, wenn das letzte Bit der Adresse eine
0 ist dann will man schreiben und bei einer 1 lesen.

Ich habe jetzt dasd Problem das wenn ich das Programm starte das beide
(SDA/SCL) auf null gehen was aber laut Datenblatt falsch ist. Sie
sollten auf 1 bleiben bis das SEN Bit gesetzt wird.

von PICcer (Gast)


Lesenswert?

Hallo,

>//Setup Ports
>
>TRISA = 0xff;    //All Ports are Outputs

Stimmt der Kommentar zum Code?
Wenn ja, dann stimmt der Code nicht... 0xff -> Input, 0x00 -> Output,

>TRISC = 0xff;

hier eventuell auch nochmal überprüfen ob I oder O gemeint ist.

von Wolfram (Gast)


Lesenswert?

Wenn du mir Bits an den Kopf schmeisst bringt uns das beiden nichts, da
ich das vom AVR kenne. Wenn dann die I2C Zustände. Wichtig zwischen
deine Versuchen jeweils einen Reset,da I2C eine Statemachine ist wo du
nicht so ohne weiters von einem state in den anderen kommst. Gilt auch
für den eeprom!
Also nach der Startbedingung sagt der Mikrocontroller zu dir ok wenn er
den Bus in Beschlag genommen hat. Dann kommt ein SLA+W wo du die Adresse
des I2C Slaves angibst. als nächstes kommt das Kommando das an den Slave
geht. Alles sollte jeweils mit ACK vom Slave bestätigt werden. Deshalb
ist es wichtig das der Slave dranhängt.

von Sebastian Bülow (Gast)


Lesenswert?

ok. Einen Slave(EEPROM) habe ich noch nicht dranhängen. Daran könnt es
ja dann liegen das sich an den Zuständen nichts mehr ändert.
Ich glaube es ist aber auch schon falsch das beide Ausgänge auf Low
gehen oder kann das an dem fehlenden Slave liegen???

von Sebastian Bülow (Gast)


Lesenswert?

Ach ja und der Kommentar zum Code ist falsch. Habe es berichtigt

TRISA = 0xff;    //All Ports are Inputs

von Wolfram (Gast)


Lesenswert?

ohne Slave kommst du über die Adresse nicht heraus da der Slave diese
mit ACK bestätigen muss. Hast du das Datenblatt schon gelesen? Beim AVR
sind das über 25 Seiten zum i2c Interface. Frage dich folgendes : Wie
sieht eine Startbedingung auf dem i2c Bus aus? Solltest du dies nicht
beantworten können, wäre es Zeit da nochmal nachzuschauen.
Aber anscheinend hat sich ja noch ein PICcer eingefunden vielleicht
bekommst du den Code von ihm. Allerdings würde ich es bei einer
Diplomarbeit vorziehen es verstanden zu haben.

von Sebastian Bülow (Gast)


Lesenswert?

J habe ich gelesen. Sind auch bei dem PIC so um die 25 Seiten. Habe ich
ja schon gemeinet die Startbedingung ist das der SDA von High auf Low
geht während der SCL auf High bleibt. Das ist die Startbedingung. Nun
kann die Adresse in den SSPBUF geschrieben werden. Diese wird dann von
selbst gesendet. In dieser Zeit ist das BF Bit gersetzt welches im
statusregister ist. Ich versuche jetzt mal den Slave anzuschließen und
nochmal zu testen.

von Wolfram (Gast)


Lesenswert?

Mach mal weiter SCL kommt auch noch auf low...
Ich mach Schluss für heute, viel Spass noch
Beim Adresse senden nicht das letzte Bit vergessen

von Bernd Rüter (Gast)


Lesenswert?

Also,

PIC und I2C-Master...

da gibt es eine Abfolge im Datenblatt des PICs...

Ich habe es in PicBasic programmiert und es ist im Grunde so, daß jede
Aktion (z.B. SEN) eingeleitet wird und von der Hardware mit einem SSPIF
beantwortet wird. Der wird gelöscht und die nächste Aktion wird gesetzt
und SSPIF abgewartet. Das Warten auf den SSPIF habe ich mit einem
Timeout versehen...

Es klappt prima !

Als Hilfe (Krücke):

I2C_WR:
    I2C_ERR=1:SSPIF=0:SEN=1:Gosub Wait_SSPIF    ;Start-Condition
    IF SSPIF Then
        SSPIF=0:SSPBUF=I2C_ADR:Gosub Wait_SSPIF ;Adresse setzen &
Write-Modus
        If SSPIF Then
            SSPIF=0
            If ACKSTAT=0 THEN
                SSPBUF=Dummy:Gosub Wait_SSPIF   ;Byte schreiben
                If SSPIF Then
                    SSPIF=0:IF ACKSTAT=0 Then I2C_ERR=0
                Endif
            ENDIF
        Endif
    ENDIF
    PEN=1:Pause 1:Return                                ;Stop-Condition

von Sebastian Bülow (Gast)


Lesenswert?

Morgen!!!
Also muss ich quasi in einer Schleife immer nachfragen ob SSPIF gesetzt
ist und dann warten bis es wieder rückgesetzt wird bis ich die nächste
Aktion durchführen kann.
Ich habe es in C mal so realisiert

void TransmitData(Zieladresse)
{

// Start Enable Bit setzen SEN = 1
  SEN = 1;
while(SSPIF)
{
                                 //warten bis SSPIF rückgesetzt wird
}
  SSPBUF = Zieladresse;    // Puffer mit Adresse laden
while (BF)
{
                                 // BF ist während des sendens gesetzt
                                 // Warten bis BF null
                                 //dann nächste Aktion
}
//erneute Startbedingung

   SEN = 1;
.
.
.
.


return;
}

von Bernd Rüter (Gast)


Lesenswert?

Falsch!

SSPIF muß "von Hand" gelöscht werden, um dann von der Hardware wieder
gesetzt zu werden, wenn die nächste Aktion abgeschlossen ist.

von Sebastian Bülow (Gast)


Lesenswert?

Also dann eher so

void TransmitData(Zieladresse)
{

// Start Enable Bit setzen SEN = 1
  SEN = 1;
if(SSPIF)
{
  SSPIF = 0;
}

SSPBUF = Zieladresse;    //Zieladresse in Puffer laden
while (BF)
{}

return;
}

von Sebastian Bülow (Gast)


Lesenswert?

Hallo!!!

Ich bin echt am Ende! Jetzt gehen beide Leitungen für ca. 20 us auf Low
und dann gehen beide wieder hoch. Weiß einer was das für ein Phänomen
ist???

Gruss Basti...

von Wolfram (Gast)


Lesenswert?

Kannst du mal deinen Programmcode gut Dokumentiert zeigen?
Antwortet der EEPROM auf die Zieladresse mit ACK?
Oszidiagramm der gesamten Kommunikation?

von Wolfram (Gast)


Lesenswert?

Nur so nebenbei, habe ich nach 10 sec. suchen auf der Microchip Seite
gefunden.
http://ww1.microchip.com/downloads/en/DeviceDoc/i2c.pdf

von Sebastian Bülow (Gast)


Lesenswert?

Hey!!!

Ja klar hier ist er. Der EEPROM antwortet bis jetzt noch nicht.

#include "pic168x76a.h"
#include "types_ksg1.h"

#fuses   HS,WDT,PUT,NOBROWNOUT,NOLVP,CPD,PROTECT,WRT_50%

#use delay(clock=4000000) // 4 MHz


void TransmitData(int);

void main(void)
{

int Adresse = 10100000;      // Adresse des EEPROMS, letzte Null-->
schreiben

//Setup Ports

TRISA = 0xff;    //All Ports are Inputs

TRISB = 0xC0;    //All Ports are Outputs  (RB7/RB6 are Inputs)

TRISC = 0x18;    //All Ports are Outputs (18 RC3/RC4 are Inputs)

// Setup Watchdog
//   setup_wdt(WDT_288MS);



// Bits setzen:


//Slew rate control disabled for standard speed mode (100 kHz and 1
MHz)
//Disable SMBus specific inputs

SSPSTAT |=0x80;


//Bit3 I2C Master mode, clock = FOSC/(4 * (SSPADD + 1))
//Bit5 Enables the serial port and configures the SDA and SCL pins as
the serial port pins

SSPCON1 |=0x28;


TransmitData(Adresse);      //Daten senden

return;
}



void TransmitData(Zieladresse)
{
//Interrupt Flag löschen

SSPIF =0;

// Start Enable Bit setzen SEN = 1

STATUS_RP0 = 1;
SEN = 1;
STATUS_RP0 = 0;

while(SSPIF = 0)
{}
SSPIF = 0;          // Wenn Bus mein ist dann SSPIF rücksetzen

SSPBUF = Zieladresse;  //Zieladresse in Puffer laden

while(BF)    // abwarten bis fertig gesendet
{}

while(SSPIF = 0)  // Warten bis senden abgeschlossen ist
{}
SSPIF = 0;  // nach Erfolgreichem senden SSPIF wieder löschen

//Datenbyte senden
SSPBUF = 11110000;    // Datenbyte in Puffer laden

while(SSPIF = 0)  // Warten bis senden abgeschlossen ist
{}
SSPIF = 0;  // nach Erfolgreichem senden SSPIF wieder löschen

// Bus wieder freigeben durch setzten des PEN Bits
STATUS_RP0 = 1;
PEN = 1;
STATUS_RP0 = 0;

while(SSPIF = 0)  // Warten bis Bus wieder freigegeben ist
{}
SSPIF = 0;  // Nach Freigabe Interrupt Flag wieder löschen

PEN = 1;                        // STOP Bit setzen

return;
}

von Wolfram (Gast)


Lesenswert?

int Adresse = 10100000;
Die Adresse des EEPROMS ist 10100000 dezimal? Das ist ja wohl nicht
ganz ernst gemeint...
Ich sehe im Moment nicht wo du du lesen oder schreiben als Richtung
angibst. Das muss im Zusammenhang mit der Adresse erfolgen.
TWI ist eine Stateengine da schaut man sich nach jedem Byte den Status
an und reagiert entsprechend!
Ich weiss nicht was du tun willst, also als erstes Start senden
2. Adresse des EEPROMs senden mit Richtung
3. Kommando an EEPROM <- Wo ist das?

Schau dir nochmal das Beispiel an es ist doch eigentlich ganz klar
gezeigt wie man mit einem EEPROM umgeht, auch wenn es ein anderer
Prozessor ist.

von Sebastian Bülow (Gast)


Lesenswert?

Diese Präsentation habe ich schon mal gelesen,. Mein Programm ist auch
stark daran orientiert.

Trotzdem Danke!!!!

von Sebastian Bülow (Gast)


Lesenswert?

Die Richtung (in diesem Fall schreiben) ist in dem letzten Bit der
Adressierung angegeben. Also 0 ist schreiben und 1 ist lesen.

von Bernd Rüter (Gast)


Lesenswert?

int Adresse = 10100000;

ist falsch !

dezimal 10100000 ist ...00100000 binär. Damit kann das EEPROM nicht
angesprochen werden.

von Sebastian Bülow (Gast)


Lesenswert?

Morgen!!!!

Bei dem EEPROM den ich benutze sind erst 5 Adressbits dann 2 Mode Bits
und dann das R/W Bit. Also in der Form AAAAAMMR
Dann ist ja bei mir die Adresse nur 10100. Das müsste doch gehen.

Gruss Basti...

von Wolfram (Gast)


Lesenswert?

Du hast anscheinend wirklich ein Verständnisproblem.
Die Adresse die du nach der Startbedingung sendest ist die ADRESSE DES
EEPROMBAUSTEINS AM I2C BUS. Die Speicheradresse auf die du IM EEPROM
zugreifen willst teilst du ihm erst später in der Kommandosequenz mit.
Es gibt einen Unterschied zwischen Geräteadresse und und
Speicheradresse im eeprom.

>Dann ist ja bei mir die Adresse nur 10100. Das müsste doch gehen
nur zur Sicherheit, in C bedeutet die Anweisung
int Adresse=10100;
Erstelle eine Variable vom Typ Integer und weise ihr den Wert
zehntausendeinhundert dezimal zu.
Wenn du von der Form AAAAAMMR sprichst ist das binär gemeint.

von Sebastian Bülow (Gast)


Angehängte Dateien:

Lesenswert?

Also laut dem Datenblatt kann ich gleich die Adresse des EEPROM's
angerben wo ich hinschreiben will

von Sebastian Bülow (Gast)


Lesenswert?

auf Seite 9 ist die Adressierung beschrieben. Oder steh ich jetzt total
auf dem schlauch???

von Wolfram (Gast)


Lesenswert?

Du hast es richtig verstanden
Aber lies mal weiter bis Seite 12 Abschnitt 5.2.3
Behandlung nach Reset

von Sebastian Bülow (Gast)


Lesenswert?

Aha muss ich dann erst so tun als will ich ein Byte auslesen ohne ein
ACK danach zu senden. Dann STOP senden und dann beginnen mit der
Adresse???

von Wolfram (Gast)


Lesenswert?

>dann beginnen mit der Adresse???
erst noch Start
aber so würde ich es verstehen

von Sebastian Bülow (Gast)


Lesenswert?

ja so habe ich das ja gemeint. Ok ist ein Versuch wert. Geb dann
Bescheid!

von Sebastian Bülow (Gast)


Lesenswert?

Wie sollte es auch anders sein. Es funktioniert immernoch nicht. Hier
nochmal der abgeänderte Quellcode.

#include "pic168x76a.h"
#include "types_ksg1.h"

#fuses   HS,WDT,PUT,NOBROWNOUT,NOLVP,CPD,PROTECT,WRT_50%

#use delay(clock=4000000) // 4 MHz


void TransmitData(int);

void main(void)
{

int Adresse = 00011011;    // Aufweckbedingung für den EEPROM

//Setup Ports

TRISA = 0xff;    //All Ports are Inputs

TRISB = 0x00;    //All Ports are Outputs

TRISC = 0x18;       //All Ports are Outputs (18 RC3/RC4 are Inputs)

// Setup Watchdog
   setup_wdt(WDT_288MS);



// Bits setzen:

SSPADD = 0x09;
//Slew rate control disabled for standard speed mode (100 kHz and 1
MHz)
//Disable SMBus specific inputs

SSPSTAT |=0x80;


//Bit3 I2C Master mode, clock = FOSC/(4 * (SSPADD + 1))
//Bit5 Enables the serial port and configures the SDA and SCL pins as
the serial port pins

SSPCON1 |=0x28;

// enable all interrupts
GIE = 1;


// Bit rücksetzen
//Register&=~BIT;

// Bit toggeln
//Register^=BIT;

//TransmitData(Adresse);      //Daten senden

return;
}



void TransmitData(AufweckAdresse)
{
int Zieladresse = 10100010;

// Start Enable Bit setzen SEN = 1
//STATUS_RP0 = 1;
SEN = 1;
//STATUS_RP0 = 0;

while(SSPIF=0)
{}
SSPIF = 0;         // Wenn Bus mein ist dann SSPIF rücksetzen
//STATUS_RP0 = 0;

SSPBUF = AufweckAdresse;  //Aufwecken des EEPROM's

while(SSPIF = 0)  // Warten bis senden abgeschlossen ist
{}
SSPIF = 0;     // nach Erfolgreichem senden SSPIF wieder löschen

PEN =1;            // STOP Bit

while(SSPIF = 0)  // Warten bis STOP Bedingung beendet
{}
SSPIF = 0;  // nach Erfolgreichem senden SSPIF wieder löschen

// Start Enable Bit setzen SEN = 1
//STATUS_RP0 = 1;
SEN = 1;
//STATUS_RP0 = 0;

// Zieladresse senden


SSPBUF = Zieladresse;     // Datenbyte in Puffer laden

while(SSPIF = 0)   // Warten bis senden abgeschlossen ist
{}
SSPIF = 0;   // nach Erfolgreichem senden SSPIF wieder löschen


//// Bus wieder freigeben durch setzten des PEN Bits
////STATUS_RP0 = 1;
PEN = 1;
////STATUS_RP0 = 0;
//
//while(SSPIF = 0)  // Warten bis Bus wieder freigegeben ist
//{}
////SSPIF = 0;         // Nach Freigabe Interrupt Flag wieder löschen

//PEN = 1;

return;

}

von Sebastian Bülow (Gast)


Lesenswert?

Die // Kommentar Zeichen vor dem Funktionsaufruf gehören natürlich weg!
Sorry...

von Wolfram (Gast)


Lesenswert?

Hi,
Es wurde zwar oben schonmal erwähnt, aber würdest du bitte mal angeben
was das heissen soll,
int Zieladresse = 10100010;
Wie kommst du auf diese Codierung , und welche Zahl ist das dezimal?
Oder mal zum Nachdenken, welche Bitbreite hat SSPBUF? Was ist die
größte Zahl die man dieser Bitbreite zuweisen kann (?) und welche Zahl
versuchst du in SSPBUF reinzuschreiben?

von Sebastian Bülow (Gast)


Lesenswert?

Die Adresse für das EEPROM soll sein 10100. Die nächsten 2 Bit sind für
den Modus. das heisst 01 bedeutet erst das low bit dann das high bit.
Das letzte Bit, also 0 bedeutet das man etwas auf den EEPROM schreiben
will.

von Sebastian Bülow (Gast)


Lesenswert?

In das SSPBUF Register passen acht Bit. Das sind doch nur acht Bit und
müsste doch somit unproblematisch sein. Oder?

von Wolfram (Gast)


Lesenswert?

Oder!
int Zieladresse = 10100010;
welche Zahl ist das deiner Meinung nach dezimal???

von Sebastian Bülow (Gast)


Lesenswert?

Dezimal an einem Stück wäre das 162

von Wolfram (Gast)


Lesenswert?

falsch, probiers nochmal

von Rahul (Gast)


Lesenswert?

doch!

von Bernd Rüter (Gast)


Lesenswert?

Endlich mal ein richtig schöner langer Thread über die falsche
Schreibweise von Zahlen gepaart mit einer gehörigen Portion Ignoranz.

von Rahul (Gast)


Lesenswert?

Naja, 0b10100010 wäre dezimal 162...

von Wolfram (Gast)


Lesenswert?

Och, jetzt hast du die Selbsterkenntnis verhindert...

von Rahul (Gast)


Lesenswert?

Schuldigung...
Lösungswort wäre wohl eher "Zahlenformate in C" gewesen?!

von Wolfram (Gast)


Lesenswert?

Ok, lösen wir auf,
so du das richtig codiert hast( habe ich jetzt nicht
kontrolliert)entspricht die Bitfolge 10100010 binär dem Wert 162
dezimal oder 0xA2 hexadezimal.
Mit
int Zieladresse = 10100010;
weist du die Zahl 10100010 dezimal zu die mit Sicherheit nicht in das
erste Datenbyte(8bit) passt.
Mit
unsigned char Zieladresse = 0xA2;
wird da schon eher ein 1.Datenbyte draus.

von Sebastian Bülow (Gast)


Lesenswert?

Ah ok! Is natürlich eigentlich logisch. Da hab ich garnicht drangedacht
das darauf raus willst. Hab im Datenblatt nach der Lösung geschaut und
nicht in meinem C Buch. Aber der Fehler ist somit mal entfernt.

von Sebastian Bülow (Gast)


Lesenswert?

Ok! Die Schnittstelle reagiert schon mal. Gutes Zeichen. Allerdings kann
ioch in meinem Code die Zieladresse änddern wie ich will aber es kommt
immer die gleiche Zeichenfiolge raus. Warum das???

von Wolfram (Gast)


Lesenswert?

>Ok! Die Schnittstelle reagiert schon mal
Woran merkst du das? Bekommst du ein Ack?

>Allerdings kann ioch in meinem Code die Zieladresse änddern wie ich
>will aber es kommt immer die gleiche Zeichenfiolge raus. Warum das???

Keine Ahnung, du hast bis jetzt keinen Code gepostet in dem du einen
Lesezugriff machst.

Was mich interessieren würde: Was hast du eigentlich schon mit
Mikrocontrollern gemacht?

von Sebastian Bülow (Gast)


Lesenswert?

Jup ich bekomme ein ACK zurück. Ich meine das ich auf dem Oszi immer die
gleiche Zeichenfolge in Richtung EEPROM habe egal welche Adresse ich
rausschicke.

Es ist das erste mal das ich selber was mit uC mache. In der FH hatte
ich irgendwann eine Vorlesung über uC. Da ging es aber eher um den
Aufbau und Speicherverwaltung und weniger um die Programmierung.
Wenn wir programmiert haben dann in asm und jetzt muss ich in C

von Wolfram (Gast)


Lesenswert?

>Ich meine das ich auf dem Oszi immer die gleiche Zeichenfolge in
>Richtung EEPROM habe egal welche Adresse ich rausschicke.
auf SDA? Oszibild?

>Es ist das erste mal das ich selber was mit uC mache
Das merkt man etwas, ist halt schlecht dass du damit erst zur
Diplomarbeit anfängst.
>Wenn wir programmiert haben dann in asm und jetzt muss ich in C
Das sollte eigentlich bequemer sein, da du dich in C nicht mehr um die
Registeraufteilung kümmern musst. Das Ansprechen der Peripherie ist ja
das gleiche.
Wenn du den Rest noch nicht programmiert hast, mach das erstmal. Das
sollte Dir noch etwas Sicherheit in C und Mikrocontrollerprogrammierung
geben. Wenn z.B. noch eine Kommunikation zum PC sein soll dann mach erst
die serielle Schnittstelle. Die ist einfacher als ein I2C Protokoll.

von Sebastian Bülow (Gast)


Lesenswert?

Ja SDA ist auf dem Oszi immer gleich. Das Problem ist das meine Dipl
Arbeit nicht bei null anfängt, sondern in ein bestehendes System
integriert wird. Alle anderen Schnittstellen sind schon definiert. Der
Rest läuft über die UART des Controllers.
Ich wusste vor der Dipl Arbeit garnicht das es auf uC Programmierung
rausläuft. Mein Betreuer auch nicht. Es sollte eigentlich eine NanoNet
Funkstrecke werden. Naja ich hoffe ich kriege es hin.
es wird ja langsam aber sicher.
Ich finde ehrlich gesagt auch die Entwicklungsumgebung nicht so
optimal. Ich arbeite  hier mit dem MPLAB ICD2 von Microchip. Dieses
Teil stürzt die ganze Zeit ab und man muss immer voll umständlich
zwischen dem Programmierer und dem Debugger umschalten.

von Sebastian Bülow (Gast)


Lesenswert?

Jtzt funktioniert sie fast. Wenn ich step by step durchgehe kommen alle
Signale wie erwartet. Allerdings ist wohl noch ein Timing Problem drin.
Wenn ich es ganz normal durchlaufen lasse dann funktioniert es nicht.
Hier der Code


#include "pic168x76a.h"
#include "types_ksg1.h"

#fuses   HS,WDT,PUT,NOBROWNOUT,CPD,PROTECT,NOLVP,WRT_50%

#use delay(clock=4000000) // 4 MHz


void TransmitData(unsigned char);


void main(void)
{

unsigned char Adresse = 0x13;  // Aufweckbedingung für den EEPROM

//Setup Ports

TRISA = 0xff;    //All Ports are Inputs

TRISB = 0x00;    //All Ports are Outputs

TRISC = 0x18;  //All Ports are Outputs (18 RC3/RC4 are Inputs)

// Setup Watchdog
   setup_wdt(WDT_288MS);



// Bits setzen:

SSPADD = 0x09;
//Slew rate control disabled for standard speed mode (100 kHz and 1
MHz)
//Disable SMBus specific inputs

SSPSTAT |=0x80;


//Bit3 I2C Master mode, clock = FOSC/(4 * (SSPADD + 1))
//Bit5 Enables the serial port and configures the SDA and SCL pins as
the serial port pins

SSPCON1 |=0x28;

// Bit rücksetzen
//Register&=~BIT;

// Bit toggeln
//Register^=BIT;


TransmitData(Adresse);      //Daten senden

return;
}



void TransmitData(AufweckAdresse)
{
unsigned char Zieladresse = 0xA2;

// Start Enable Bit setzen SEN = 1
STATUS_RP0 = 1;
SEN = 1;
STATUS_RP0 = 0;

while(SSPIF = 0) // Warten bis Aktion abgeschlossen und bestätigt wird
{}
SSPIF = 0;  // nach Erfolgreichem Abschluss SSPIF wieder löschen

SSPBUF = AufweckAdresse;  //Aufwecken des EEPROM's

while(SSPIF = 0)// Warten bis Aktion abgeschlossen und bestätigt wird
{}
SSPIF = 0;  // nach Erfolgreichem Abschluss SSPIF wieder löschen

STATUS_RP0 = 1;
PEN =1;
STATUS_RP0 = 0;

while(SSPIF = 0)// Warten bis Aktion abgeschlossen und bestätigt wird
{}
SSPIF = 0;  // nach Erfolgreichem Abschluss SSPIF wieder löschen

// Start Enable Bit setzen SEN = 1
STATUS_RP0 = 1;
SEN = 1;
STATUS_RP0 = 0;

// Zieladresse senden

SSPBUF = Zieladresse;     // Datenbyte in Puffer laden

while(SSPIF = 0)// Warten bis Aktion abgeschlossen und bestätigt wird
{}
SSPIF = 0;  // nach Erfolgreichem Abschluss SSPIF wieder löschen

// Datenbyte senden

SSPBUF = 0x56;

while(SSPIF = 0)// Warten bis Aktion abgeschlossen und bestätigt wird
{}
SSPIF = 0;  // nach Erfolgreichem Abschluss SSPIF wieder löschen

// Bus wieder freigeben durch setzten des PEN Bits

STATUS_RP0 = 1;
PEN = 1;
STATUS_RP0 = 0;

while(SSPIF = 0)// Warten bis Aktion abgeschlossen und bestätigt wird
{}
SSPIF = 0;  // nach Erfolgreichem Abschluss SSPIF wieder löschen

return;

}

von Bernd Rüter (Gast)


Lesenswert?

Warum setzt Du die STATUS_RP0-Bits ? C sollte doch das Bankumschalten
beim Adressieren von Bits beherrschen ! ;-)

von Sebastian Bülow (Gast)


Lesenswert?

Ich wollte es so ausführlich wie möglich machen. HAst schon Recht, es
funkt auch ohne.
Hast du für das Timing Problem eine Idee???

von Wolfram (Gast)


Lesenswert?

>while(SSPIF = 0)// Warten bis Aktion abgeschlossen und bestätigt wird
>{}
>SSPIF = 0;  // nach Erfolgreichem Abschluss SSPIF wieder löschen
>STATUS_RP0 = 1;
>PEN =1;
>STATUS_RP0 = 0;

bei sowas kommt keiner der den PIC nicht genau kennt mit
das sind keine Kommentare sondern Beschreibungen der C Anweisung
while(SSPIF = 0){} //Warten bis Zeichen übertragen wurde?
wäre ein Kommentar
Bei den folgenden Zeilen was macht dein Bitsetzen?
z.B. Sende Startbedingung
Das sind Kommentare bei denen jeder mitkommt und dies auch überprüfen
kann ob du richtig die Bits codiert hast, im anderen Fall muss man
erstmal erraten WAS du machen willst und du hast da schon einige Fehler
drin gehabt...
wenn du ihn nach dem Reset das erste mal ansprichst
Also Startbedingung+Datenbyte Sendest du danach eine Stopbedingung?
Was ich vermisse , wo überprüfst du auf ACK es könnte ja auch ein NACK
kommen. Also auch wenn ich nicht nachvollziehen kann was du genau tust,
in einem Fall wirst du mit Sicherheit hängenbleiben.

von Rahul (Gast)


Lesenswert?

>while(SSPIF = 0)

müsste da nicht while(SSPIF == 0) stehen?

von Wolfram (Gast)


Lesenswert?

mit Sicherheit, naja dann ist ja klar warum das step by step geht und am
Stück nicht...

von Sebastian Bülow (Gast)


Lesenswert?

So siehts jetzt aus. Aber es kommt kein ACK vom Slave zurück.


#include "pic168x76a.h"

#fuses   HS,WDT,PUT,NOBROWNOUT,CPD,PROTECT,NOLVP,WRT_50%

#use delay(clock=4000000) // 4 MHz


void TransmitData(unsigned char);


void main(void)
{

unsigned char Adresse = 0x13;  // Aufweckbedingung für den EEPROM

// Setup Watchdog
  setup_wdt(WDT_288MS);



//Setup Ports


TRISA = 0xff;    //All Ports are Inputs

TRISB = 0x00;    //All Ports are Outputs

TRISC = 0x18;  //All Ports are Outputs (18 RC3/RC4 are Inputs)




// Bits setzen:

SSPADD = 0x09;

//Slew rate control disabled for standard speed mode (100 kHz and 1
MHz)
//Disable SMBus specific inputs

  SSPSTAT |=0x80;


//Bit3 I2C Master mode, clock = FOSC/(4 * (SSPADD + 1))
//Bit5 Enables the serial port and configures the SDA and SCL pins as
the serial port pins

  SSPCON1 |=0x28;


  TransmitData(Adresse);      //Daten senden


}



void TransmitData(AufweckAdresse)
{
  unsigned char Zieladresse = 0xA2;


  // Start Enable Bit setzen SEN = 1

  SEN = 1;

while(SSPIF == 0)     // Warten bis Start Bedingung abgeschlossen
  {}
SSPIF = 0;     // nach Erfolgreichem Abschluss SSPIF wieder löschen


SSPBUF = AufweckAdresse;  //Aufwecken des EEPROM's

while(SSPIF == 0)     // Warten bis alle 8 Bit gesendet
{}
SSPIF = 0;  // nach Erfolgreichem Abschluss SSPIF wieder löschen

PEN =1;        // stop bedingung setzen


while(SSPIF == 0)  // warten bis pause bedingung abgeschlossen
{}
SSPIF = 0;  // nach erfolgreichem abschluss sspif wieder löschen


//start enable bit setzen sen = 1

SEN = 1;

// Zieladresse senden

SSPBUF = Zieladresse;     // Datenbyte in Puffer laden

while(ACKSTAT == 1)      // Auf ACK vom Slave warten
{}


while(SSPIF == 0)// Warten bis Aktion abgeschlossen und bestätigt wird
{}
SSPIF = 0;  // nach Erfolgreichem Abschluss SSPIF wieder löschen


// Datenbyte senden

SSPBUF = 0x56;

while(SSPIF == 0)// Warten bis Aktion abgeschlossen und bestätigt wird
{}
SSPIF = 0;  // nach Erfolgreichem Abschluss SSPIF wieder löschen

// Bus wieder freigeben durch setzten des PEN Bits

PEN = 1;


while(SSPIF == 0)// Warten bis Aktion abgeschlossen und bestätigt wird
{}
SSPIF = 0;  // nach Erfolgreichem Abschluss SSPIF wieder löschen

}

von Wolfram (Gast)


Lesenswert?

Danke, das liest sich schon wesentlich besser,
Codierungen wie: SSPADD = 0x09;
Solltest du angeben mit SSPADD = (1<<3)|(1<<1);
besser lesbar wird es wenn die Bitnamen von den C Headerfiles definiert
sind,dann steht da SSPADD = (1<<Bitname1)|(1<<Bitname2);
das verhindert Fehler und ist besser lesbar.
sind ACKSTAT,SSPIF,SEN Register der Seriellen Schnittstelle  oder Bits
im Register?
Ich vermute sehr es sind Bitnamen, wenn ja greifst du falsch zu.
um obiges Schema zu verwenden:
Registername=(1<<0); oder Registername=(1<<PEN)
Registername ist der Name des Registers in dem PEN steht.
analog die anderen...

von Sebastian Bülow (Gast)


Lesenswert?

Ja ACKSTAT, SSPIF, SEN und PEN sind Bits in den Registern. Ich versuch
es mal wie du es beschrieben hast. Danke!!!

von Sebastian Bülow (Gast)


Lesenswert?

Eine kleine Frage noch zu der Syntax

z.B.   SSPCON1 = (1<<SSPEN)|(1<<SSPM3)

steht die 1<< für setzen

tu ich dann mit 0<< ein Bit rücksetzen???

von Wolfram (Gast)


Lesenswert?

kontrollier noch in der Headerdatei ob die Bitpositionen stimmen
als in #include <pic...>
ist im include verzeichnis deines compilers
zu deiner Frage ,der Blick in ein C-Buch wäre anzuraten
1<<x bedeutet verschiebe 1 um x nach links
wenn du das mit dem Register oderst dann wird das jeweilige Bit
gesetzt

SSPCON1&=~(1<<SSPEN);
~ invertiert
also du hast eine 1 an der Position von sspen der rest sind Nullen
invertiert rest einsen an stelle SSPEN 0
dies wird mit dem Register geandet (UND) damit bleiben die restlichen
Bits unangetastet egal ob 0 oder 1 und das Bit wird gelöscht.

von Schoasch (Gast)


Lesenswert?

>>steht die 1<< für setzen
>>tu ich dann mit 0<< ein Bit rücksetzen???

mit 1<< Bitstelle schiebst du eigentlich nur eine 1 nach links um die
Nummer "Bitstelle". Wenn du 0<< Bitstelle schreibst, wird halt eine 0
an diese stelle geschoben--> somit "löschst" du ein Bit.
Ob das die ideale Schreibform für diesen Compiler ist, bin ich mir aber
nicht so ganz sicher.

Wieso hast du eigentlich den Watchdog aktiviert? dieser wird, wenn ich
mich nicht irre, nie zurückgesetzt und sollte somit immer einen Reset
verursachen oder etwa nicht?!

von Sebastian Bülow (Gast)


Lesenswert?

Den Watchdog Timer benötige ich noch für spätere Funktionen. Also in der
Software wo mein Teil implementiert wird ist der Watchdog in Gebrauch.
Für diese Funktion brauche ich ihn eigentlich nicht. Beim Debuggen muss
man ihn aber eh ausschalten

von Sebastian Bülow (Gast)


Lesenswert?

Die Bitpositionen stimmen mit den Registern im Datenblatt überein.

von Sebastian Bülow (Gast)


Lesenswert?

Aber so wie ich die Bits vorher gesetzt hatte konnte ich im
Prgrammablauf zusehen wie sie gesetzt bzw. rückgesetzt wurden. Dann
kann das doch nicht sooo falsch sein wenns klappt, oder???

von Wolfram (Gast)


Lesenswert?

Wenn dem wirklich so ist dann müssen in der .h Datei noch irgendwelche
Makros stehen. Schau dir mal den erzeugten Assemblercode an, denn
Assembler kannst du ja.
Wenn SSPIF ein Bit in einem Register ist würde ich erwarten das in der
.h Datei irgendwas ala
#define SSPIF Bitposition
steht.
also ein (1<<SSPIF) würde dann zu (1<<Bitpositon)
deine Zuweisung
SSPIF=0; wird zu Bitposition=0;
was Syntaktisch korrekt ist, da allerdings keine Zuweisung auf eine
Variable erfolgt wird der Optimierer dies rauswerfen.

von Sebastian (Gast)


Lesenswert?

Morgen!!!

so siet es in der .h Datei aus

#BYTE SSPCON2 = 0x91
#BIT  GCEN  =  0x91.7
#BIT  ACKSTAT= 0x91.6
#BIT  ACKDT =  0x91.5
#BIT  ACKEN =  0x91.4
#BIT  RCEN  =  0x91.3
#BIT  PEN   =  0x91.2
#BIT  RSEN  =  0x91.1
#BIT  SEN   =  0x91.0


#BYTE   PIR1   =  0x0C
#define POS_RCIF       5
#define POS_TXIF       4
#define POS_FERR       2
#define POS_TMR1IF     0
#define POS_RX9D       0
#BIT    PSPIF  =  0x0C.7
#BIT    ADIF   =  0x0C.6
#BIT    RCIF   =  0x0C.5
#BIT    TXIF   =  0x0C.4
#BIT    SSPIF  =  0x0C.3
#BIT    CCP1IF =  0x0C.2
#BIT    TMR2IF =  0x0C.1
#BIT    TMR1IF =  0x0C.0


Sind alle definiert. Aslo kann ich sie doch ala SEN = 1; ansprechen.

von Bernd Rüter (Gast)


Lesenswert?

Ich würde mich jetzt nicht so an den Bitschiebereien aufhängen. Die
Schreibweise mit den Pfeilen finde ich äußerst unübersichtlich.
Ich arbeite mit PicBasic und auch da ist die Schreibweise mit dem Punkt
gebräuchlich und besser lesbar. Aber jeder hat da so eine Vorlieben.

Ich sehe eher noch Probleme im Programm!
Mein I2C-Code arbeitet schon seit über 12 Monaten in Tausenden von
Steuerungen und wurde der Empfehlung von Microchip nachempfunden.
Jeder Schritt, den der Master machen muß, wird durch ein gesetztes
SSPIF-Bit abgeschlossen, das man dann wieder löscht und sich die
Status-Bits ansieht, ob alles OK war.

Es gibt doch tonnenweise C-Code, der die I2C-Schnittstelle bedient.

von Sebastian (Gast)


Lesenswert?

Das mache ich ja. Immer warten bis eine Aktion erledigt ist indem man
das SSPIF Bit beobachtet. Sobald es gesetzt wird kann man es wieder
löschen und weiter machen mit der nächsten Anweisung. Ich habe
irgendwie keinen brauchbaren code gefunden für den PIC16F876A

von Bernd Rüter (Gast)


Lesenswert?

Ich glaube mal, daß das I2C-Modul in allen PICs gleich ist. Da ist der
uC-Typ eher egal.

von Sebastian (Gast)


Lesenswert?

Ja schon, das ist egal aber ich habe trotzdem keine Programme gefunden
und wenn dann in Assembler

von Sebastian (Gast)


Lesenswert?

so siehts jetzt aus. Wenn ich es schritt für schritt durchgehe dann
klappts. Wenn ich es aber laufen lasse funkts nicht???




#include "pic168x76a.h"
#include "types_ksg1.h"

#fuses   HS,WDT,PUT,NOBROWNOUT,CPD,PROTECT,NOLVP,WRT_50%

#use delay(clock=4000000) // 4 MHz


void TransmitData(unsigned char);


void main(void)
{

  unsigned char Adresse;   // Aufweckbedingung für den EEPROM

  Adresse = 0x13;

  // Setup Watchdog
    setup_wdt(WDT_288MS);



  //Setup Ports


  TRISA = 0xff;    //All Ports are Inputs

  TRISB = 0x00;    //All Ports are Outputs

  TRISC = 0x18;  //All Ports are Outputs (18 RC3/RC4 are Inputs)




  // Bits setzen:

  SSPADD = 0x09;

  //Slew rate control disabled for standard speed mode (100 kHz and 1
MHz)
  //Disable SMBus specific inputs

  SSPSTAT |=0x80;


  //Bit3 I2C Master mode, clock = FOSC/(4 * (SSPADD + 1))
  //Bit5 Enables the serial port and configures the SDA and SCL pins as
the serial port pins

  SSPCON1 |=0x28;


  TransmitData(Adresse);      //Daten senden


}



void TransmitData(AufweckAdresse)
{
  unsigned char Zieladresse = 0xA2;


  // Start Enable Bit setzen SEN = 1

  SEN = 1;

  while(SSPIF == 0)// Warten bis Start Bedingung abgeschlossen
  {}
  SSPIF = 0;// nach Erfolgreichem Abschluss SSPIF wieder löschen


  SSPBUF = AufweckAdresse;//Aufwecken des EEPROM's

  while(SSPIF == 0)  // Warten bis alle 8 Bit gesendet
  {}
  SSPIF = 0;// nach Erfolgreichem Abschluss SSPIF wieder löschen

  PEN =1;        // stop bedingung setzen


  while(SSPIF == 0)// warten bis pause bedingung abgeschlossen
  {}
  SSPIF = 0;// nach erfolgreichem abschluss sspif wieder löschen


  //start enable bit setzen sen = 1

  SEN = 1;

  // Zieladresse senden

  SSPBUF = Zieladresse; // Datenbyte in Puffer laden

//  while(ACKSTAT == 1)    // Auf ACK vom Slave warten
//  {}

  while(SSPIF == 0)// Warten bis Aktion abgeschlossen und bestätigt
wird
  {}
  SSPIF = 0;// nach Erfolgreichem Abschluss SSPIF wieder löschen


  // Datenbyte senden

  SSPBUF = 0x56;

  while(SSPIF == 0)// Warten bis Aktion abgeschlossen und bestätigt
wird
  {}
  SSPIF = 0;// nach Erfolgreichem Abschluss SSPIF wieder löschen


  // Bus wieder freigeben durch setzten des PEN Bits

  PEN = 1;


  while(SSPIF == 0)// Warten bis Aktion abgeschlossen und bestätigt
wird
  {}
  SSPIF = 0;// nach Erfolgreichem Abschluss SSPIF wieder löschen

}

von Bernd Rüter (Gast)


Lesenswert?

Hä ?

Wie oft wird denn jetzt die Adresse gesendet ?
Und gleich danach wieder Stop... und wieder Adresse und dann etwas
Daten...

Als Slave würde ich mich da auch vera***** vorkommen. ;-)

von Sebastian (Gast)


Lesenswert?

Na das erste sit um den PIC wieder zu aktivieren. So steht das im
Datenblatt das man einen Lesewunsch senden soll ohne ein ACK zu senden.
Dann geht es weiter mi der riochtigen Zieladresse. worauf  auch gleich
die Daten folgen.

von Basti (Gast)



Lesenswert?

Für alle die jetzt der Code interessiert die können ihn mal im Anhang
bewundern. Aus Pin RB5 kann man Man´chester codierte Bits senden und
die I2C funkt auch!
Die meisten Fehler waren in den Voreinstellungen des Compilers und
nicht im Code.

Danke für die rege Beteiligung und Hilfe

Gruss Basti...

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.