Forum: Mikrocontroller und Digitale Elektronik Einrichtung I2C-Bus mit dcPIC30F4011


von Olli (Gast)


Lesenswert?

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:
1
/****************************************************************************
2
  INCLUDE FILES
3
*****************************************************************************/
4
#include <p30f4011.h>
5
#include "init.h"
6
7
8
/****************************************************************************
9
  CONFIGURATION BITS
10
*****************************************************************************/
11
// DSPIC30F4011 Configuration Bit Settings
12
13
#include <xc.h>
14
15
// FOSC
16
#pragma config FPR = XT_PLL8          // Primary Oscillator Mode (XT w/PLL 16x)
17
18
#pragma config FOS = PRI               // Oscillator Source (Primary Oscillator)
19
#pragma config FCKSMEN = CSW_FSCM_OFF  // Clock Switching and Monitor (Sw Disabled, Mon Disabled)
20
21
// FWDT
22
#pragma config WDT = WDT_OFF            // Watchdog Timer (Disabled)
23
24
// FGS
25
#pragma config GWRP = GWRP_OFF          // General Code Segment Write Protect (Disabled)
26
#pragma config GCP = CODE_PROT_OFF      // General Segment Code Protection (Disabled)
27
28
/****************************************************************************
29
  Funktionsdeklarationen
30
*****************************************************************************/
31
void short_pause(void);
32
void long_pause(void);
33
void test_rb(void);
34
void pic_init(void);
35
void Timer_init(void);
36
void i2c_init(void);
37
void i2c_transmit_test(void);
38
void counter(void);
39
40
41
int main(void) {
42
43
    long_pause();
44
    i2c_transmit_test();
45
46
47
    while(1)
48
    {
49
    }
50
    return 0;
51
52
}
53
54
/****************************************************************************
55
  Interrupt Capture MI2C
56
*****************************************************************************/
57
58
 void __attribute__((__interrupt__,__no_auto_psv__)) _MI2CInterrupt(void)
59
  {
60
        counter();
61
62
        IFS0bits.MI2CIF=0;
63
    return;
64
  }
65
66
 /****************************************************************************
67
  Interrupt Capture SI2C
68
*****************************************************************************/
69
70
 void __attribute__((__interrupt__,__no_auto_psv__)) _SI2CInterrupt(void)
71
  {
72
73
  IFS0bits.SI2CIF=0;
74
    return;
75
  }

init.h:
1
#include <p30f4011.h>
2
3
#define E0      LATEbits.LATE0
4
#define E1      LATEbits.LATE1
5
#define E2      LATEbits.LATE2
6
#define E3      LATEbits.LATE3
7
#define E4      LATEbits.LATE4
8
#define E5      LATEbits.LATE5
9
10
#define B0      LATBbits.LATB0
11
#define B1      LATBbits.LATB1
12
#define B2      LATBbits.LATB2

myfunctions.c:
1
/****************************************************************************
2
 Globale Variablen deklarieren
3
*****************************************************************************/
4
5
unsigned int test;
6
7
//-----------------------------------------------------------------------------------------------------------//
8
//***** Funktion zum kurzen Pausieren des Programmablaufs
9
//-----------------------------------------------------------------------------------------------------------//
10
void short_pause()
11
{
12
    unsigned long i;
13
    for(i=0; i<1000; i++);
14
}
15
16
//-----------------------------------------------------------------------------------------------------------//
17
//***** Funktion zum längeren Pausieren des Programmablaufs
18
//-----------------------------------------------------------------------------------------------------------//
19
void long_pause()
20
{
21
    unsigned long i;
22
    for(i=0; i<50000; i++);
23
}
24
25
//-----------------------------------------------------------------------------------------------------------//
26
//***** Funktion zum Test des I2C-Bus durch Senden von 8 Bit Daten an I/O-Expander mit Adresse 0100000
27
//-----------------------------------------------------------------------------------------------------------//
28
29
void i2c_transmit_test()
30
{
31
    E0 = 1;
32
    E0 = 0;
33
    if(I2CSTATbits.P==0)                                                            //Bus in idle-Zustand?
34
    {
35
        unsigned long timeout = 0;
36
        unsigned long t_tot = 50000;
37
        test = 0;
38
        E0 = 1;
39
        E0 = 0;
40
41
        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
void counter()
85
{
86
    test++;
87
}
88
89
void i2c_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
void pic_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

von Frank M. (frank_m35)


Lesenswert?

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/

von Olli (Gast)


Lesenswert?

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

von Atlas (Gast)


Lesenswert?

Geschwindigkeit vom I2C-Bus von 400kHz auf 100kHz reduzieren.

Siehe Datenblatt PCF8574P: 100 kHz I²C‑bus interface (Standard-mode 
I²C-bus)

von Olli (Gast)


Lesenswert?

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
int i2c_start()
5
{
6
        unsigned int timeout = 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
int i2c_restart()
14
{
15
        unsigned int timeout = T_MAX;
16
17
        I2CCONbits.RSEN = 1;                                                         //Generate start condition by setting the Start Enable bit (SSPCON2)
18
        while( I2CCONbits.RSEN && timeout > 0)  timeout--;
19
20
        return(timeout);
21
}
22
23
int i2c_reset()
24
{
25
        unsigned int timeout = T_MAX;
26
27
        I2CCONbits.PEN = 1;
28
        while( I2CCONbits.RSEN && timeout > 0)  timeout--;
29
30
        I2CCONbits.RCEN = 0;
31
        IFS0bits.MI2CIF = 0;
32
        I2CSTATbits.IWCOL = 0;
33
        I2CSTATbits.BCL = 0;
34
35
        return(timeout);
36
}
37
38
int i2c_send_byte(int data)
39
{
40
    unsigned int timeout = T_MAX;
41
42
    I2CTRN = data;
43
    while(I2CSTATbits.TRSTAT && timeout > 0) timeout--;
44
    if(I2CSTATbits.ACKSTAT)
45
    {
46
        i2c_reset();
47
        return(-1);
48
    }
49
    return(timeout);
50
}
51
52
//-----------------------------------------------------------------------------------------------------------//
53
//***** Funktion zum Test des I2C-Bus durch Senden von 8 Bit Daten an Slave
54
//-----------------------------------------------------------------------------------------------------------//
55
56
int i2c_write(int add, int data)
57
{
58
    int error = 0;
59
60
    if(i2c_start() < 1) error++;
61
    if(i2c_send_byte(add) < 1) error++;
62
    if(i2c_send_byte(data) < 1) error++;
63
    if(i2c_reset() < 1) error++;
64
65
    return(error);
66
}

Gruß Olli

von Frank M. (frank_m35)


Lesenswert?

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.

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.