Hallo!
Ich versuche zur Zeit das interne I2C-Modul des dcPIC30F4011 zu nutzen
um mit diesem als Master ein kleines I2C-Bussystem aufzubauen. Um dieses
zu testen verwende ich einen pcf8574p (Remote 8-bit I/O expander for
I2C-bus) als Slave. Mit der Funktion void i2c_transmit_test() möchte ich
von µC den Slave ansprechen und daraufhin einmal 8 Bit Daten an diesen
Senden (Write). Die Variable "test" wird bei jedem Interrupt des
MI2C-Interrupts um eins hochgezählt. So wollte ich realisieren, dass die
Funktion zum Schreiben auf den Bus erst fortgesetzt wird, wenn dieser
Interrupt ausgelöst wird. Weiterhin habe ich im Programm habe ich ab und
zu mal den einen oder anderen Pin gesetzt/gelöscht (bei mir heißen diese
E0,E1,E2,....) um mir anzeigen zu lassen, ob der µC diese Stelle des
Codes durchläuft.
Meinen Quellcode könnt ihr hier sehen:
main.c:
I2CCONbits.SEN=1;//Generate start condition by setting the Start ENable bit (SSPCON2)
42
43
while(test<1&&timeout<t_tot)timeout++;//wait until MSSP module genrerated the interrupt at the end of the ninth clock cycle
44
if(timeout<t_tot)
45
{
46
E0=1;
47
E0=0;
48
timeout=0;
49
I2CTRN=0b01000000;//Adresse von slave und write bit (lsb) in register schreiben
50
51
while(test<2&&timeout<t_tot)timeout++;//wait until MSSP module genrerated the interrupt at the end of the ninth clock cycle
52
if(timeout<t_tot)
53
{
54
if(I2CSTATbits.TRSTAT==0)
55
{
56
E0=1;
57
E0=0;
58
timeout=0;
59
I2CTRN=0b10101010;
60
61
while(test<3&&timeout<t_tot)timeout++;
62
if(timeout<t_tot)
63
{
64
if(I2CSTATbits.ACKSTAT==0)
65
{
66
E0=1;
67
E0=0;
68
I2CCONbits.SEN=1;
69
}
70
}
71
}
72
}
73
}
74
E0=1;
75
E0=0;
76
if(timeout==50000)
77
{
78
E0=1;
79
E0=0;
80
}
81
}
82
}
83
84
voidcounter()
85
{
86
test++;
87
}
88
89
voidi2c_init()
90
{
91
//I2C-Bus-Initialisierung
92
I2CBRG=0x024;//Setting Baud Rate to 400kHz (I2CBRG = ((F_CY/F_SCL) - (F_CY/1111111)) - 1) mit F_SCL ) = 400kHz und F_CY = 4MHz
93
I2CCONbits.I2CEN=1;//Enable I2C
94
I2CCONbits.I2CSIDL=0;//Continue Module Operation in Idle Mode
95
I2CCONbits.SCLREL=0;//SCL Release Control Bit (when operating as I2C-Slave): Release SCL clock
96
I2CCONbits.IPMIEN=0;//IPMI mode not enabled
97
I2CCONbits.A10M=0;//7-bit slave address
98
I2CCONbits.DISSLW=0;//Slew Rate control enabled
99
I2CCONbits.SMEN=0;//Disable SMBus input threshold
100
I2CCONbits.GCEN=0;//General call address disabled
101
I2CCONbits.STREN=0;//Disable SOftware or release clock stretching
102
103
104
//I2C Interrupts
105
IFS0bits.MI2CIF=0;//I2C Bus collision flag clearen
106
IFS0bits.SI2CIF=0;//I2C transfer complete Interrupt flag clearen
107
108
IPC3bits.MI2CIP=1;//I2C Bus collision Interrupt priority
109
IPC3bits.SI2CIP=1;//I2C transfer complete interrupt priority
110
111
IEC0bits.MI2CIE=1;//I2C Bus collision interrupt enable bit
112
IEC0bits.SI2CIE=1;//I2C transfer complete interrupt enable bit
113
114
}
115
116
117
voidpic_init()
118
{
119
ADPCFG=0xffff;//Port B digital
120
TRISEbits.TRISE0=0;
121
TRISEbits.TRISE1=0;
122
TRISEbits.TRISE2=0;
123
TRISEbits.TRISE3=0;
124
TRISEbits.TRISE4=0;
125
TRISEbits.TRISE5=0;
126
LATEbits.LATE0=0;
127
LATEbits.LATE1=0;
128
LATEbits.LATE2=0;
129
LATEbits.LATE3=0;
130
LATEbits.LATE4=0;
131
LATEbits.LATE5=0;
132
133
TRISBbits.TRISB0=0;
134
TRISBbits.TRISB1=0;
135
TRISBbits.TRISB2=0;
136
LATBbits.LATB0=0;
137
LATBbits.LATB1=0;
138
LATBbits.LATB2=0;
139
}
Das ist jetzt nur der Teil des Quellcodes, den ich für mein Problem als
relevant betrachte. D.h. z.B. weitere Timer- und
Interruptinitialisierungen sind vorhanden, aber nicht hier mit drinnen.
Mein Problem kann ich leider dabei noch gar nicht so genau definieren.
Ich weiß, dass der µC auf jeden Fall noch nichts so wirklich auf den Bus
legt. Ich kann mit dem Oszi nicht erkennen, dass die Startbedingung
korrekt ausgeführt wird. Ich sehe nur, dass beim Funktionsaufruf SDA auf
low-Pegel gezogen wird und dort bleibt. SCL bleibt dauerhaft auf
High-Pegel. Es wird also auch nicht die Slave-Adresse auf den Bus
geschoben. Vor dem Funktionsaufruf haben SDA und SCL beide High-Pegel.
Ich denke mein Schaltungsaufbau wird nicht großartig relevant sein. Ich
habe außer dem Bus bis jetzt keine zusätzliche Peripherie angeschlossen
(außer PICkit 2). Der Bus wird mit zwei 5kOhm Pullups hochgezogen. Die
Stromversorgung erfolgt mit einem Labornetzgerät.
Funktionen wie z.B. Timer und Interrupts wurden bereits getestet und
funktionieren.
Wäre nett, wenn mal jmd. über meinen Code drüberschauen könnte. Vllt.
findet ihr ja nen kleinen Fehler in der Initialisierung oder der
Testfunktion des I2C-Buses, der mir einfach nicht auffällt.
Falls ich für eine AUskunft relevante Infos zu meinem Projekt vergessen
habe, möchte ich mich im Voraus dafür entschuldigen. Bitte gebt mir dann
Bescheid und ich werden alles nachliefern!
Besten Dank im Voraus.
Gruß Olli
Also ich habe mir deinen Code nicht genau angeschaut, aber ich würde dir
Vorschlagen es auch anders zu lösen, und zwar strukturierter.
Momentant hast du alles in eine Funktion gepackt, das mag am Anfang
vielleicht noch einigermaßen funktionieren (oder eben auch nicht)
spätestens wenn du ein zweites I2C Gerät ansprechen willst musst du
alles nochmal von Grund auf programmieren, da du nichts vom bisherigen
verwenden kannst.
Also packe Start, Stop, Send, Read, ... in seperate Funktionen.
Du du eh dein Programm mit einer while Schleife blockst, verstehe ich
auch den Umweg über den Interrupt nicht. Stattdessen kannst du auch
direkt das benötigte Flag in einer While Schleife pollen und nach einer
bestimmten Zeit abbrechen.
das würde ich aber eh erst gegen Ende versuchen zu implementieren, da es
das debuggen deutlich erschwert. Am Anfang wird es vermutlich oft
vorkommen, dass der Bus hängt, dann ist es immer sehr schön per debugger
zu sehen wo er nun hing.
Ich fand dieses Tutorial sehr hilfreich zum implementieren eigener I2C
Routinen, es ist gut erklärt und der Code gut struktuiert. Zudem siehst
du dort welche Flags du überhaupt überprüfen musst um zu sehen ob die
Übertragung, ... erfolgreich war oder nicht.
http://www.engscope.com/pic24-tutorial/10-2-i2c-basic-functions/
Danke für die Tips! Ich werd mal mit dem Erstellen separater Funktionen
für Start, Stop, usw. anfangen und dann das ganze nochmal Testen.
Den Umweg über den Interrupt habe ich aus folgendem Grund gemacht:
Ich wollte Testen, wie "tief" der µC in der I2C_transmit_test()-Funktion
geht. Das hätte ich mir dann an Hand des Wertes der Variable "test"
angeschaut. So weit bin ich allerdings noch gar nicht gekommen. Du hast
natürlich auf jeden Fall recht, dass es ein Umweg ist. Ich sollte
möglicherweise auch mehr mit dem Debugger arbeiten, leider kenn ich mich
mit dem bein PIC-µC (bzw. MPLAB X IDE) noch nicht so gut damit aus, aber
ich werd mich mal damit befassen.
Danke auch für den Tutorial-Link, werd ich mir auch mal ansehen.
Gruß Olli
Atlas schrieb:> Geschwindigkeit vom I2C-Bus von 400kHz auf 100kHz reduzieren.>> Siehe Datenblatt PCF8574P: 100 kHz I²C‑bus interface (Standard-mode> I²C-bus)
Habe die Geschwindigkeit beriets auf 100 kHz festgelegt. Habe an dieser
Stelle nur den Kommentar noch nicht geändert, mein Fehler.
Allerdings war/ist mein Problem ja mehr, dass die Bussteuerung an sich
schon nicht funktioniert hat.
Habe meinen Quellcode jetzt mal geändert, wie Frank M. vorgeschlagen
hat. Leider bin ich noch nicht dazu gekommen, ihn zu testen, da mein µC
sich gestern mit meinem PICkit 2 nicht mehr programmieren lassen wollte.
Heute wird aber ein neuer Versuch gestartet. Vielleicht hat jmd. von
euch Lust, sich den Code (nur die I2C-Funktionen) mal anzuschauen,
deswegen poste ich ihn jetzt einfach mal noch dazu:
1
#define T_MAX 50000
2
3
4
inti2c_start()
5
{
6
unsignedinttimeout=T_MAX;
7
8
I2CCONbits.SEN=1;//Generate start condition by setting the Start Enable bit (SSPCON2)
9
while(I2CCONbits.SEN&&timeout>0)timeout--;
10
return(timeout);
11
}
12
13
inti2c_restart()
14
{
15
unsignedinttimeout=T_MAX;
16
17
I2CCONbits.RSEN=1;//Generate start condition by setting the Start Enable bit (SSPCON2)
sieht soweit ganz gut aus.
Reset würde ich in Stop umbenennen, ganz einfach weil so der Befehl auch
in den Datenblättern genannt wird.
Dein Timeout ist etwas 'unsicher', da du ja nach keiner Zeit gehst es
sei denn du hast T_MAX zuvor auf deinen Prozessor, Taktfrequenz und
Operation hin berechnet, dass du weißt welche Zeit er wartet. Alternativ
kannst du Timer und eine globale Variable verwenden um Zeit zu messen.
Deine Überprüfung auf Erfolg oder Nichterfolg würde ich auch eher
geschachtelt machen anstatt einfach error hoch zu zählen, denn wenn
Start schon nicht erfolgreich ist, dann muss man ja auch nicht versuchen
was zu senden.