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
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!!!).
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 | }
|
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
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.