Forum: Mikrocontroller und Digitale Elektronik I2C Master mit STM32F


von Mike R. (thesealion)


Lesenswert?

Hallo,

ich versuche zur Zeit mit einem Cortex-M3 (STM32F102RB) mit einem X9258 
Poti zu kommunizieren. Leider klappt das nicht und vor allem weiß ich 
nicht ob die Hardware I2C Schnittstelle von ST das überhaupt kann.
Das Problem ist da Protokoll des Potis. Das möchte etwas in der Form:

Start, Adresse (ohne read/write), Befehl (vom Master), Daten (vom 
Slave), Ende

haben, also ein umschalten der Übertragungsrichtung während der 
Kommunikation. Hab ich die Möglichkeit das zu tun bisher einfach nur 
übersehen oder können die STM32F.. Chips das tatsächlich nicht?

Gruß Mike

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Mike S. schrieb:
> Das Problem ist da Protokoll des Potis.
> ... ein umschalten der Übertragungsrichtung während der Kommunikation.
Du weißt schon, wie I²C funktioniert?
Es wird niemals eine '1' ausgegeben. Eine '1' ergibt sich über einen 
Pullup automatisch, wenn keiner eine '0' ausgibt...

Deshalb ist jeder Pin (SCL und SDA) immer gleichzeitig Aus- und Eingang 
(und das muss er sein, denn bei I²C kann/könnte ein Slvae den Takt 
selbständig verlängern!!!).

von Johannes V. (Gast)


Lesenswert?

Funktioniert bei meinem 103RB sehr gut.

Ohne ein Datenblatt gelesen zu haben, würde deine I2C Kommunikation so 
ablaufen:

Master started, Master schickt Addresse mit Read, Slave ACKed, Master 
schickt Befehl, Slave ACKed, Master stopt

Master started, Master schickt Addresse mit Write, Slave ACKed, Master 
empfängt byte (Ack oder nack, je nachdem ob es das letzte byte ist), 
Master stopt.
1
I2Cslot X9258(Addresse, Port);
2
3
unsigned char befehl[2] = { 1, 'bla' };
4
unsigned char antwort[2] = { 1, 0x00 };
5
6
if(X9258.send(befehl) != 0)
7
printf("Fehler!");
8
if(X9258.recv(antwor) != 0)
9
printf("Fehler!");
1
class I2Cslot{
2
  unsigned char address;    // holds address of this slot
3
  unsigned char port;      // number of this slots port
4
5
public:
6
  /*  Constructor to create slots for devices  */
7
  I2Cslot(unsigned char address, unsigned char port);
8
  /*  initializes all active I2C ports  */
9
  static void init();
10
  /*  send a number of bytes with on this slot, number has to be in the first spot of the string pMsg points at */
11
  /*  returns 0 if no error occured of the transaction  */
12
  int send(unsigned char *pMsg);
13
  /*  receive a number of bytes with on this slot, number has to be in the first spot of the string pMsg points at  */
14
  /*  returns 0 if no error occured of the transaction  */
15
  int recv(unsigned char *pMsg);
16
  
17
  /* same as regular functions, but busy lopping instead of switching the task */
18
  int send_sync(unsigned char *pMsg);
19
  int recv_sync(unsigned char *pMsg);
20
};
1
#include "STM32_I2C.h"
2
#include "STM32_DriverInterna.h"
3
#include <stm32f10x.h>
4
#ifdef DEBUG
5
#include <stdio.h>
6
#endif
7
8
#define Core__Frequency 72000000
9
10
#define I2C_NO_MUTEX
11
#define I2C1__active  //port0.0 SDA1, port0.1 SCL1
12
//#define I2C2__active  //port0.10 SDA2, port0.11 SCL2
13
14
15
16
17
I2Cslot::I2Cslot(unsigned char address, unsigned char port)
18
{
19
  this->address = address;
20
  this->port = port - 1;
21
}
22
23
extern "C" {
24
25
void I2C1_EV_IRQHandler(void);
26
void I2C2_EV_IRQHandler(void);
27
28
void I2C1_ER_IRQHandler(void);
29
void I2C2_ER_IRQHandler(void);
30
31
32
struct {
33
  unsigned char *pMsg;
34
  volatile char error;
35
  unsigned char count;
36
  unsigned char address;
37
#ifndef I2C_NO_MUTEX
38
  CTL_MUTEX_t mutex;
39
#endif
40
}I2C[1];
41
}
42
43
void I2Cslot::init()
44
{
45
#ifdef I2C1__active
46
  I2C1->CR2 = (3 << 8) | 0x24;        // set APB clock 36MHz and  [enable IRQs : Error, Buffer, Event I2C1->CR2 = (7 << 8)]
47
  I2C1->CCR = (1 << 15)|(1<<14)|27;   // enable fast mode 19:9 
48
    I2C1->TRISE = 11;
49
  I2C1->CR1 = 1;                      //I2C enable
50
#ifndef I2C_NO_MUTEX
51
  ctl_mutex_init(&I2C[0].mutex);
52
#endif
53
  ctl_set_isr(31, 10, I2C1_EV_IRQHandler, 0);
54
  ctl_unmask_isr(31);
55
    ctl_set_isr(32, 10, I2C1_ER_IRQHandler, 0);
56
  ctl_unmask_isr(32);
57
#endif
58
#ifdef I2C2__active
59
  I2C2->CR2 = (7 << 8) | 0x24;    // set APB clock 36MHz and enable IRQs : Error, Buffer, Event
60
  I2C2->CCR = (1 << 15)|(1<<14)|23;      // enable fast mode 19:9 
61
    I2C2->TRISE = 11;
62
  I2C2->CR1 = 1;                  //I2C enable
63
  ctl_mutex_init(&I2C[1].mutex);
64
  ctl_set_isr(33, 10, I2C2_EV_IRQHandler, 0);
65
  ctl_unmask_isr(31);
66
    ctl_set_isr(34, 10, I2C2_ER_IRQHandler, 0);
67
  ctl_unmask_isr(32);
68
#endif
69
}
70
71
72
int I2Cslot::send(unsigned char *pMsg)
73
{
74
#ifdef DEBUG
75
  printf("I2C: %d Bytes to %X\n", pMsg[0], address);
76
#endif
77
#ifndef I2C_NO_MUTEX
78
  ctl_mutex_lock(&I2C[port].mutex, CTL_TIMEOUT_NONE, 0);
79
#endif
80
  I2C[port].pMsg = pMsg;  //adjust pointer to msg
81
  I2C[port].error = 1;  //initialize error
82
  I2C[port].count = 1;  //initialize counter
83
  I2C[port].address = address;
84
  switch(port)
85
  {
86
#ifdef I2C1__active
87
  case 0:
88
    I2C1->CR1 |= 0x100;    //generate start condition
89
    WaitStatus_I2C1;    //wait for hardware to complete the operation
90
    break;
91
#endif
92
#ifdef I2C2__active
93
  case 1:
94
    I2C2->CR2 |= 0x100;    //generate start condition
95
    WaitStatus_I2C2;    //wait for hardware to complete the operation
96
    break;
97
#endif
98
  default:
99
    while(1);        //trying to access inactive port
100
  }
101
#ifdef DEBUG
102
  if(I2C[port].error)
103
    printf("I2Cerror: %X", I2C[port].error);
104
#endif
105
#ifndef I2C_NO_MUTEX
106
  ctl_mutex_unlock(&I2C[port].mutex);
107
#endif
108
  return I2C[port].error;
109
}
110
111
int I2Cslot::recv(unsigned char *pMsg)
112
{
113
  address++;
114
  int temp = send(pMsg);
115
  address--;
116
  return (temp);
117
}
118
119
int I2Cslot::send_sync(unsigned char *pMsg)
120
{
121
#ifdef DEBUG
122
  printf("I2C%d: %d Bytes to %X\n", port, pMsg[0], address);
123
#endif
124
#ifndef I2C_NO_MUTEX
125
  ctl_mutex_lock(&I2C[port].mutex, CTL_TIMEOUT_NONE, 0);
126
#endif
127
  I2C[port].pMsg = pMsg;  //adjust pointer to msg
128
  I2C[port].error = 1;  //initialize error
129
  I2C[port].count = 1;  //initialize counter
130
  I2C[port].address = address;
131
  switch(port)
132
  {
133
#ifdef I2C1__active
134
  case 0:
135
    I2C1->CR1 = 0x101;    //generate start condition
136
    break;
137
#endif
138
#ifdef I2C2__active
139
  case 1:
140
    I2C2->CR1 = 0x101;    //generate start condition    
141
    break;
142
#endif
143
  default:
144
    while(1);        //trying to access inactive port
145
  }
146
  
147
  
148
    while(I2C[port].error == 1)
149
      ;
150
  
151
 
152
#ifdef DEBUG
153
  if(I2C[port].error)
154
    printf("I2C%derror: %X", port, I2C[port].error);
155
#endif
156
#ifndef I2C_NO_MUTEX     
157
  ctl_mutex_unlock(&I2C[port].mutex);
158
#endif
159
  return I2C[port].error;
160
}
161
162
int I2Cslot::recv_sync(unsigned char *pMsg)
163
{
164
  address++;
165
  int temp = send_sync(pMsg);
166
  address--;
167
  return (temp);
168
}
169
170
extern "C" {
171
#ifdef I2C1__active
172
void I2C1_EV_IRQHandler (void)
173
{
174
    ctl_enter_isr();
175
176
    static int status2 = 0;
177
  int status1 = I2C1->SR1;
178
    
179
180
    if(status1 & 0x01)                  // start bit was send
181
    {
182
        I2C1->DR = I2C[0].address;      // first transmit the address
183
    }
184
    else if( status1 & 0x02 )           // address transmitted and ACK received
185
    {
186
        status2 = I2C1->SR2;            // read 2nd status register to clear it
187
        if( ~status2 & 0x04 )           // Receiver mode, determine ACK return
188
        {
189
            if( I2C[0].pMsg[0] > 1 )    // there's more than one byte to receive, set ACK return bit
190
                I2C1->CR1 = 0x401;
191
            else
192
                I2C1->CR1 = 0x201;      // else there is only one byte to be received, set the STOP bit
193
        }
194
    }
195
196
    if ( status1 & 0x84 )               // if BTF or TxE bit is set we need to write/read a new byte
197
    {
198
        if( status2 & 0x04 )            // write if in transmitter mode
199
        {
200
            if( I2C[0].count <= I2C[0].pMsg[0] )
201
            {                           // if not all bytes have been transmitted yet, keep on pushing
202
                I2C1->DR = I2C[0].pMsg[I2C[0].count];
203
                I2C[0].count++;
204
            }
205
            else                        // the last byte was transmitted, shut down the interface
206
            {
207
                I2C[0].error = 0;       // we done
208
                I2C1->CR1 = 0x201;      // set stop bit
209
                StatusSet_I2C1;
210
            }
211
        }
212
        else                            // else in receiver mode
213
        {                               // read the byte just received
214
            I2C[0].pMsg[I2C[0].count] = I2C1->DR;
215
            I2C[0].count++;
216
            if( I2C[0].count == I2C[0].pMsg[0] )
217
                I2C1->CR1 = 0x201;      // clear the ACK bit and Set the STOP bit for the next byte to be received
218
            else if( I2C[0].count > I2C[0].pMsg[0] )
219
            {
220
                I2C[0].error = 0;       // we done
221
                StatusSet_I2C1;
222
            }
223
        }
224
    } 
225
226
    ctl_exit_isr();
227
}
228
229
void I2C1_ER_IRQHandler(void)
230
{
231
    ctl_enter_isr();
232
    
233
    int err = I2C1->SR1;    
234
    I2C1->SR1 = 0;          // write a zero to clear all flags
235
    if( err & 0x400 )       // NACK received
236
    {
237
        I2C1->CR1 = 0x201;  // set a stop
238
        I2C[0].error = 0xFE;
239
        StatusSet_I2C1;
240
    }
241
    if( err & 0x200 )       // arbitration lost
242
    {
243
        I2C[0].error = 0xFF;
244
        StatusSet_I2C1;
245
    }
246
    
247
    if( I2C[0].count > I2C[0].pMsg[0] ) // indicates that the transfer was completed but a NACK on last byte? possible :/
248
        while(1);
249
    
250
    ctl_exit_isr();
251
}
252
#endif
253
}

von Johannes V. (johannes_v)


Lesenswert?

Ok,

es hat mich wirklich stutzig gemacht und du hast Recht.
Anscheinend muss tatsächlich zwischen write und read während dem 
Transfer gewechselt werden und die Slave Addresse hat keinen Einfluss 
auf die Datenrichtung.

Als ich mich mit dem STM I2C auseinandergesetzt habe ist mir eine 
ähnliche Funktion auch nicht untergekommen. Verübeln kann man es ihnen 
aber auch nicht, ist schließlich absoluter Humbug und entspricht nicht 
dem Standard.

Unter Umständen wirsd du nicht drum rumkommen ein Software I2C zu 
schreiben um das Poti zu verwenden, da die Hardware Statusmaschienen 
sich logischerweise nur nach dem R/W Bit der Addresse richten.

Viel Glück

von Mike R. (thesealion)


Lesenswert?

Ok, danke für die Info. Dann habe ich das Datenblatt ja wohl doch 
richtig gelesen.
Das mit dem zweiten Start kenne ich auch von solchen Devices wie EEROMs, 
aber die Potis haben halt eine total "eigene" Interpretation :-(

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.