Forum: Mikrocontroller und Digitale Elektronik I2C BitBang/STM32: Mit UART-Ausgabe geht's, ohne nicht


von Dirk K. (dekoepi)


Lesenswert?

Moin Moin zusammen,

sehr komischer Effekt: Da ich den Hardware-I2C ums Verrecken nicht 
ordentlich zur Kommunikation mit meiner OV7670-Cam überreden kann, habe 
ich eine BitBang-Variante erstellt. Ordentlich mit Debug-Output via 
serieller Konsole, damit ich sehe, wo was klemmt. Als alles lief, wie es 
sollte, habe ich diese ganzen Kommentare entfernt - und probeweise durch 
Delay(1-50ms) ersetzt. Auch den emulierten I2C-Takt habe ich von rund 
360kHz auf 180, 90, 45 und weniger kHz gedrosselt. Es hilft nichts, ohne 
die Debug-Ausgabe kommt nur Müll (0 beim Read statt 128 / zweites Read 
anstatt 201) zurück.

Ersetze ich die Delay (2); wieder durch ein 
sprintf()->uart_tx()-Konstrukt, läuft es wie gewünscht. Was mache ich 
falsch?
1
#include "stm32f10x.h"
2
#include <stdbool.h>
3
#include <stdio.h>
4
#include "main.h"
5
6
// OV7670 i2c address
7
#define OV7670I2C 0x21  // write 0x42 (<<1), read 0x43 (<<1,+0x01)
8
9
// i2c bitbang pin defines
10
#define SCLH GPIOB->BSRR = GPIO_Pin_6
11
#define SCLL GPIOB->BRR = GPIO_Pin_6
12
#define SDAH GPIOB->BSRR = GPIO_Pin_7
13
#define SDAL GPIOB->BRR = GPIO_Pin_7
14
#define SCLread GPIOB->IDR & GPIO_Pin_6
15
#define SDAread GPIOB->IDR & GPIO_Pin_7
16
17
GPIO_InitTypeDef GPIO_InitStructure;
18
19
void main (void) {
20
21
  /* OV7670 connections
22
   * XCLK PB0 Min. 8 MHz for starting the cam; managed 9 MHz via TIM3 Channel 3 on PB0
23
   * VSYNC PB3 LOW marks start of frame; when HIGH then stop
24
   * HREF PB4 HIGH starts line; when LOW: line stop, don't read pixels
25
   * PCLK PB5 HIGH when pixel data valid
26
   * SCIO PB6 I2C1_SCL
27
   * SDIO PB7 I2C1_SDA
28
   * D0-D7 PB08-PB15 sample at rising edge of PCLK
29
   */
30
31
  SystemInit();
32
33
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
34
35
// I2C1 interface to talk to OV7670 - bitbang :(
36
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
37
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;
38
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
39
  GPIO_Init(GPIOB, &GPIO_InitStructure);
40
41
  // OV7670 data pins (PB8-PB15)
42
  GPIO_InitStructure.GPIO_Pin = (GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10 | GPIO_Pin_11 | GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15);
43
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
44
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
45
  GPIO_Init(GPIOB, &GPIO_InitStructure);
46
47
48
49
// Start talking to OV7670
50
  sprintf(text, "Starting communication with OV7670.\n");
51
  uart_tx(text);
52
53
  uint8_t tempvalue = i2c_read(OV7670I2C, 0x11);
54
55
  sprintf(text, "Reading COM11 from I2C target: [%i].\n", tempvalue);
56
  uart_tx(text);
57
58
  i2c_write(OV7670I2C, 0x11, (tempvalue | 0b01001001));  // COM11 prescaler for PCLK=9
59
  sprintf(text, "Writing COM11 prescaler to I2C target: [%i].\n", (tempvalue | 0b01001001));
60
  uart_tx(text);
61
62
  sprintf(text, "Reading COM11 again from I2C target: [%i].\n", i2c_read(OV7670I2C, 0x11));
63
  uart_tx(text);
64
}
65
66
// ----------------------------------------------------------------------------
67
68
69
// I2C support functions
70
71
void i2c_delay(void)
72
{
73
   // 72 MHz / 400 nops = 180KHz; 1600 ~= 45kHz
74
   for (int i=0; i<400; i++) {
75
     asm volatile ("nop");
76
   }
77
}
78
79
bool i2c_start(void)
80
{
81
  SDAH;
82
  SCLH;
83
  i2c_delay();
84
  if (!(SDAread)) return false;
85
  SDAL;
86
  i2c_delay();
87
  if (SDAread) return false;
88
  SDAL;
89
  i2c_delay();
90
  Delay (2);
91
  return true;
92
}
93
94
95
void i2c_stop(void)
96
{
97
  SCLL;
98
  i2c_delay();
99
  SDAL;
100
  i2c_delay();
101
  SCLH;
102
  i2c_delay();
103
  SDAH;
104
  i2c_delay();
105
  Delay (2);
106
}
107
108
109
void i2c_ack(void)
110
{
111
  SCLL;
112
  i2c_delay();
113
  SDAL;
114
  i2c_delay();
115
  SCLH;
116
  i2c_delay();
117
  SCLL;
118
  i2c_delay();
119
  Delay (2);
120
}
121
122
123
void i2c_nack(void)
124
{
125
  SCLL;
126
  i2c_delay();
127
  SDAH;
128
  i2c_delay();
129
  SCLH;
130
  i2c_delay();
131
  SCLL;
132
  i2c_delay();
133
  Delay (2);
134
}
135
136
137
bool i2c_waitack(void)
138
{
139
  SCLL;
140
  i2c_delay();
141
  SDAH;
142
  i2c_delay();
143
  SCLH;
144
  i2c_delay();
145
  if(SDAread)
146
  {
147
    SCLL;
148
    Delay (2);
149
  }
150
  SCLL;
151
  Delay (2);
152
}
153
154
155
void i2c_send(uint8_t data) {
156
  Delay (2);
157
158
    for(int i=0; i<8; i++) {
159
        SCLL;
160
        i2c_delay();
161
        if (data & 0x80)
162
          SDAH;
163
        else SDAL;
164
        data<<=1;
165
        i2c_delay();
166
        SCLH;
167
        i2c_delay();
168
    }
169
    SCLL;
170
}
171
172
uint8_t i2c_receive(void) {
173
  uint8_t tempresult = 0;
174
175
  SDAH;
176
  for (int i=0; i<8; i++) {
177
        tempresult<<=1;
178
        SCLL;
179
        i2c_delay();
180
        SCLH;
181
        i2c_delay();
182
        if(SDAread)
183
        {
184
          tempresult|=0x01;
185
        }
186
      }
187
  SCLL;
188
  Delay (2);
189
190
  return (tempresult);
191
}
192
193
void i2c_write(int i2c_target, uint8_t address, uint8_t data) {
194
    if(!i2c_start()) {
195
      return;
196
    }
197
    i2c_send(i2c_target << 1);
198
    if(!i2c_waitack()) {
199
      i2c_stop();
200
      return;
201
    }
202
  i2c_send(address);
203
    i2c_waitack();
204
    i2c_send(data);
205
    i2c_waitack();
206
    i2c_stop();
207
}
208
209
uint8_t i2c_read(int i2c_target, uint8_t address) {
210
  int tempresult = 0;
211
212
    bool retry = true;
213
    int times =5;
214
    while (retry) {
215
      if(!i2c_start()) {
216
        return false;
217
      }
218
      i2c_send(i2c_target << 1);
219
      if(!i2c_waitack()) {
220
        i2c_stop();
221
        times--;
222
        if (times == 0) return false;
223
      } else retry = false;
224
    }
225
  i2c_send(address);
226
    i2c_waitack();
227
    i2c_stop();
228
    i2c_start();
229
    i2c_send(((i2c_target << 1)+1));
230
    i2c_waitack();
231
232
    tempresult = i2c_receive();
233
    i2c_nack();
234
    i2c_stop();
235
  return (tempresult);
236
}

von Dirk K. (dekoepi)


Lesenswert?

Ich konnte es etwas eingrenzen. Die ganzen Delay() konnte ich entfernen. 
Kritisch scheint nur die i2c_send()-Funktion zu sein:
1
Starting communication with OV7670.
2
i2c_send: [66].
3
i2c_send: [17].
4
i2c_send: [67].
5
Reading COM11 from I2C target: [128].
6
i2c_send: [66].
7
i2c_send: [17].
8
i2c_send: [137].
9
Writing COM11 prescaler to I2C target: [137].
10
i2c_send: [66].
11
i2c_send: [17].
12
i2c_send: [67].
13
Reading COM11 again from I2C target: [137].

Also die Funktion hier:
1
void i2c_send(uint8_t data) {
2
  sprintf(text, "i2c_send: [%i].\n", data);
3
  uart_tx(text);
4
//  Delay(100);
5
6
    for(int i=0; i<8; i++) {
7
        SCLL;
8
        i2c_delay();
9
        if (data & 0x80)
10
          SDAH;
11
        else SDAL;
12
        data<<=1;
13
        i2c_delay();
14
        SCLH;
15
        i2c_delay();
16
    }
17
    SCLL;
18
    i2c_delay();
19
}

Egal, mit welchen Werten im Delay ich versuche, die Textausgabe zu 
ersetzen, es klappt nicht. Probiert habe ich zwischen 1 und 100ms.

Irgendwelche Ideen? :)

von Klaus (Gast)


Lesenswert?

Ich sehe nirgendwo, daß du nachdem du SCL losgelassen hast, überprüfst 
ob die Leitung wirklich high geworden ist. SCL kann von beiden Seiten 
auf low getrieben werden, nennt man Clock Stretching. Insbesondere beim 
ACK ist das wichtig.

MfG Klaus

von Dirk K. (dekoepi)


Lesenswert?

Hallo Klaus,

Danke für den Vorschlag!

Ich habe den Busy-Check "while (!SCLread) {};" anstatt der Ausgabe drin 
gehabt, ändert leider nichts. Auch der Test auf der SDA-Leitung war 
erfolglos.

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.