Forum: Mikrocontroller und Digitale Elektronik STM32F103 I2C/TWI


von huehoet (Gast)


Lesenswert?

Hallo,

ich habe folgenden Code geschrieben:
1
void i2cInitialize(void)
2
{
3
  /* disable I2C */
4
  I2C1->CR1 &= ~(I2C_CR1_PE);
5
6
  /* enable I2C1 clock */
7
  RCC->APB1ENR |= RCC_APB1ENR_I2C1EN;
8
  /* reset I2C1 interface */
9
  RCC->APB1RSTR = RCC_APB1RSTR_I2C1RST;
10
11
  /* enable GPIOB and AFIO clock (port of I2C1) */
12
  RCC->APB2ENR |= RCC_APB2ENR_IOPBEN;
13
  RCC->APB2ENR |= RCC_APB2ENR_AFIOEN;
14
  /* I2C1 pins (PB6/7 or PB8/9) open drain alternate function (max. 10MHz) */
15
  GPIOB->CRL &= ~(GPIO_CRL_MODE6 | GPIO_CRL_CNF6 | GPIO_CRL_MODE7 | GPIO_CRL_CNF7);
16
  GPIOB->CRL |= (GPIO_CRL_MODE6_0 | GPIO_CRL_MODE6_1 | GPIO_CRL_CNF6_0) |
17
          (GPIO_CRL_MODE7_0 | GPIO_CRL_MODE7_1 | GPIO_CRL_CNF7_0);
18
19
  /* set I2C frequency to 8MHz */
20
  I2C1->CR2 &= ~(I2C_CR2_FREQ);
21
  I2C1->CR2 |= (I2C_CR2_FREQ_3);
22
23
  /* frequency for SCL (clk); SM mode, 100kHz */
24
  I2C1->CCR &= ~(I2C_CCR_FS | I2C_CCR_DUTY | I2C_CCR_CCR);
25
  I2C1->CCR |= 0x28;
26
27
  /* maximum rise time of SCL (S. 784, Ref. Man) */
28
  I2C1->TRISE &= ~(I2C_TRISE_TRISE);
29
  I2C1->TRISE |= 0x09;
30
31
  /* enable I2C */
32
  I2C1->CR1 |= I2C_CR1_PE;
33
}
34
35
uint8_t i2cStart(void)
36
{
37
  I2C1->CR1 |= I2C_CR1_START;
38
39
40
  while(!(I2C1->SR1 & I2C_SR1_SB) || (I2C1->SR2 & I2C_SR2_BUSY))
41
    ;
42
43
  return 0x01;
44
}
45
46
uint8_t i2cStop(void)
47
{
48
  I2C1->CR1 |= I2C_CR1_STOP;
49
50
  while(!(I2C1->SR1 & I2C_SR1_STOPF) || (I2C1->SR2 & I2C_SR2_BUSY))
51
    ;
52
53
  return 0x01;
54
}
55
56
uint8_t i2cWrite(uint8_t data)
57
{
58
  I2C1->DR = data;
59
60
  while(!(I2C1->SR1 & I2C_SR1_TXE) || (I2C1->SR2 & I2C_SR2_BUSY))
61
    ;
62
63
  if(I2C1->SR1 & I2C_SR1_BTF)
64
    return 0x01;
65
  else
66
    return 0x00;
67
}

Allerdings kommt, wenn ich das flashe, beim Empfänger nichts an.
Habe ich irgendetwas vergessen/falsch gemacht?

Danke!

von I2C Unwissender (Gast)


Lesenswert?

huehoet schrieb:
> Habe ich irgendetwas vergessen/falsch gemacht?

Ja, du hast vergessen das I2C Protokoll zu lesen  lernen 
verstehen. Schau in Datenblatt eines Slaves.

Unter anderem verlangt das Protokoll das Senden von
Start- und Stop-Bedingungen.

von huehoet (Gast)


Lesenswert?

I2C Unwissender schrieb:
> Unter anderem verlangt das Protokoll das Senden von
> Start- und Stop-Bedingungen.

Habe ich doch?
Vllt. habe ich zu vergessen, dass es im Programm so aufgerufen wird:
1
i2cInitialize();
2
i2cStart();
3
i2cWrite(address);
4
i2cWrite(data1);
5
i2cWrite(data2);
6
i2cStop();
Da sollte doch eigentlich über das Setzen der Start und Stop Bits die 
Bedingung automatisch gesendet werden oder nicht? So habe ich das 
eigentlich aus der Ref. Man. heraus verstanden.

von Heiner (Gast)


Lesenswert?

Kompletten Sourecode mitteilen - den Boardregeln entsprechend!

von Max M. (maxmicr)


Lesenswert?

Wenn du einen Logic Analyzer oder ein Oszi hast, am besten damit mal die 
I2C Pins messen.

von huehoet (Gast)


Lesenswert?

Das ist eigentlich quasi der gesamte Source Code.

Dann eben noch die main.c:
[c]
#include "i2c.h"

int main(void)
{
    i2c_init();
    i2c_start();
    i2c_write(0x78); /* address */
/* ... hier werden nur weitere Werte genauso übertragen; dies sind aber 
magic numbers, die nicht weiterhelfen; es geht um die Ansteuerung eines 
OLED Displays, die Werte funktionieren mit genau dem gleichen Code nur 
anderem I2C mit einem AVR problemlos */
    i2c_stop();

    while(1)
        ;
}

von huehoet (Gast)


Lesenswert?

Max M. schrieb:
> Wenn du einen Logic Analyzer oder ein Oszi hast, am besten damit
> mal die
> I2C Pins messen.

Habe ich leider nicht. :/
SCL vom Slave ist dabei über einen 4.7kR Pull-Up auf 3.3V mit SCL vom 
STM32 verbunden, SDA genauso.

von huehoet (Gast)


Lesenswert?

Ich habe es jetzt noch einmal mit einem Arduino und Beispielcode, mit 
dem es vorher auch funktionierte, probiert und jetzt geht das Display 
irgendwie plötzlich nicht mehr.
Auch mit diesem I2C-Scanner Sketch für Arduino finde ich das Display 
nicht. Ich verstehe aber nicht warum. 5V müsste das Display doch 
eigentlich vertragen, hat es davor doch auch. Habt ihr noch irgendwelche 
Ideen, wie ich überprüfen kann, ob das Display hinüber ist oder wie ich 
das reparieren kann?
Ist ein 0.91" OLED Display mit SSD1306 Controller.
Ich wäre für jede Hilfe dankbar.

von huehoet (Gast)


Lesenswert?

Also der LM6206N3, der als Spannungsregler daraufsitzt, gibt konstant 
3.35V aus. Das müsste ja eigentlich soweit normal sein. Laut Datasheet 
kann der auch bis zu 6.5V ab, das dürfte es also nicht sein.
Aber was kann denn sonst kaputt sein? Die 5V sollten dann ja eigentlich 
nicht an dem LM6206N3 vorbeikommen, oder?

von Schnupfi (Gast)


Lesenswert?

>> Wenn du einen Logic Analyzer oder ein Oszi hast, am besten damit

>Habe ich leider nicht. :/

Dann aber schnell bestellen.
Für unter 10EUR gibt's Saleae Clones.
Wenn Du ihn jetzt nicht brauchst, dann beim nächsten Projekt.

von huehoet (Gast)


Lesenswert?

Da mache ich mich derart verrückt und dann ist es am OLED Display das:
die Lötstellen hatten irgendwie keine ordentliche Verbindung.
Einmal neugelötet und das Display geht wieder.
Wobei auch nur am ATmega. Am STM32F103 geht es immer noch nicht.

Schnupfi schrieb:
>>> Wenn du einen Logic Analyzer oder ein Oszi hast, am besten
> damit
>
>>Habe ich leider nicht. :/
>
> Dann aber schnell bestellen.
> Für unter 10EUR gibt's Saleae Clones.
> Wenn Du ihn jetzt nicht brauchst, dann beim nächsten Projekt.

Wo findest du das?
Mit meinem Multimeter kann ich aber übrigens weder an SCL noch an SDA 
eine Frequenz oder Spannung gegenüber GND messen.
Irgendetwas wird also wohl in dem obigen Code nicht stimmen.
Aber ich habe keine Ahnung, woran es liegen könnte. Ich habe eigentlich 
alle Register, die ich im Refman gefunden habe, entsprechend 
beschrieben.

von Stefan F. (Gast)


Lesenswert?

Wenn alle Stricke reißen, kannst du zur Not noch Bit-Banging machen, wie 
in diesem Projekt: http://stefanfrings.de/esp8266/WIFI-Kit-8-Test2.zip

Ich würde mich an deiner Stelle aber lieber in das Problem reinfuchsen.

Leider kann ich Dir nicht helfen, ich habe Hardware-TWI bisher nur auf 
AVR genutzt.

von Schnupfi (Gast)


Lesenswert?

huehoet schrieb:
> Schnupfi schrieb:
>>>> Wenn du einen Logic Analyzer oder ein Oszi hast, am besten
>> damit
>>
>>>Habe ich leider nicht. :/
>>
>> Dann aber schnell bestellen.
>> Für unter 10EUR gibt's Saleae Clones.
>> Wenn Du ihn jetzt nicht brauchst, dann beim nächsten Projekt.
>
> Wo findest du das?


Wer zieht Dir eigentlich morgens die Hosen an?

"USB Logic Analyzer Device Set USB Cable 24MHz 8CH 24MHz"

Beitrag #5399873 wurde vom Autor gelöscht.
von Lutz (Gast)


Lesenswert?

Und wenn du dazu sigrok nutzt (was wirklich gut ist!), kommst du auch 
nicht wegen Softwarepiraterie in die Hölle.

von huehoet (Gast)


Lesenswert?

Leider hat sich das Problem auch immer noch nicht gelöst.

Allerdings komme ich dem Problem vllt. langsam näher:
die Statusbits werden irgendwie nicht gesetzt, so z.B. das Bit SB bleibt 
irgendwie immer 0.
Das müsste doch eigentlich auf 1 gehen, sobald das Startsignal generiert 
wurde - und das sollte ja eigentlich passieren.
Weiß denn keiner, wo der Fehler liegt? Irgendwer wird doch schon einmal 
mit einem STM32F103 I2C betrieben haben, oder nicht?

von huehoet (Gast)


Lesenswert?

Ich habe jetzt auch noch einmal mit einem Debugger mir alle Register 
angesehen und bin zu folgendem Ergebnis gekommen:
I2C1->SR1: 0x00
I2C1->SR2: 0x02 (busy)
I2C1->CR1: 0x501 (ack, start, enable)
I2C1->CR2: 0x24 (frequency at 36MHz (SYSCLK=72MHz))
RCC->APB1ENR: 0x200000 (I2C1EN)
GPIOB->CRL: 0xff484444 (PB6/7 af, od, 50mhz)
AFIO: MAPR: 0x00 (PB6/7 for I2C1)

Das heißt aber vermutlich, dass der I2C-Bus einfach irgendwie nicht 
aktiv ist, obwohl alle Bits eigentlich gesetzt sind. Ich habe es jetzt 
auch mehrfach mit der Ref. Man. und der Standard-Lib abgeglichen und es 
entspricht eigentlich genau den Vorgaben.
Was mir noch oben als Fehler aufgefallen ist und was ich ausgessert 
habe, dass ich den I2C-Reset nicht zurückgesetzt habe und dass in CR2 
die falsche Frequenz geschrieben wurde.
Das macht aber irgendwie absolut keinen Unterschied.

von huehoet (Gast)


Lesenswert?

Was vermutlich eher zutrifft, ist, dass das BUSY Bit die ganze Zeit 
gesetzt ist.
Ich habe es jetzt einmal mit einem Timeout probiert, wenn BUSY direkt 
nach der Initialisierung gesetzt ist, dass ich dann einfach noch einmal 
die Initialisierung wiederhole. Aber irgendwie ist es direkt nach der 
Initialisierung wieder gesetzt.
Ich habe jetzt auch einmal I2C2 ausprobiert, ebenfalls kein Erfolg.

von huehoet (Gast)


Lesenswert?

Ich habe es jetzt noch einmal getestet und pull-up sowie das Display 
entfernt und die Pins offen gelassen. Und schon wird SB gesetzt, genauso 
wie START auf 0 gesetzt wird und sich der µC im Master-Mode befindet 
(erkennbar an MSL).
Allerdings bleibt BUSY dabei die ganze Zeit gesetzt. Das verstehe ich 
irgendwie nicht.

von huehoet (Gast)


Lesenswert?

huehoet schrieb:
> Ich habe es jetzt noch einmal getestet und pull-up sowie das
> Display
> entfernt und die Pins offen gelassen. Und schon wird SB gesetzt, genauso
> wie START auf 0 gesetzt wird und sich der µC im Master-Mode befindet
> (erkennbar an MSL).
> Allerdings bleibt BUSY dabei die ganze Zeit gesetzt. Das verstehe ich
> irgendwie nicht.

Als Anmerkung noch:
ich habe am Display einmal nachgemessen und zwischen SCL/GND und SDA/GND 
jweils 250kOhm messen können, zwischen SCL/VCC und SDA/VCC besteht 
allerdings keinerlei Verbindung. Kann sich das jemand erklären? Weil 
eigentlich braucht I2C ja Pull-Ups und keine Pull-Downs.

von huehoet (Gast)


Lesenswert?

huehoet schrieb:
> Ich habe es jetzt noch einmal getestet und pull-up sowie das
> Display
> entfernt und die Pins offen gelassen. Und schon wird SB gesetzt, genauso
> wie START auf 0 gesetzt wird und sich der µC im Master-Mode befindet
> (erkennbar an MSL).
> Allerdings bleibt BUSY dabei die ganze Zeit gesetzt. Das verstehe ich
> irgendwie nicht.

Ich habe das noch einmal genauer untersucht:
1
while((I2C2->SR2 & I2C_SR2_BUSY) || (I2C2->CR1 & I2C_CR1_START))
2
    ;
Hier, direkt nach der Initialisierung habe ich jetzt kein gesetztes 
BUSY-Flag, sondern nur BERR und STOPF, also eine empfangene 
STOP-condition.
Sobald ich jedoch wieder die Pull-Ups anschließe, erhalte ich mein 
BUSY-flag und ich lande in der loop. Neuinitialisierung nach einer 
bestimmten Zeit in der Schleife bringt auch nichts.
Allerdings: sobald ich das START-bit setze, schaltet das BUSY-flag um 
und wird auch nicht mehr gecleared. Dafür wird das STOPF-flag gecleart.
Aber wie gesagt, das alles nur bei offenen Pins.
Allerdings konnte ich jetzt auch irgendwie dabei nicht noch einmal 
reproduzieren, dass SB gesetzt wird. Auch START in CR1 bleibt jetzt 
gesetzt.

Es ist doch zum Verzweifeln. :|

von I2C Unwissender (Gast)


Lesenswert?

huehoet schrieb:
> Als Anmerkung noch:

Du siehst schon, dir antwortet wohl kaum einer da du schwarze
Magie betreibst.

huehoet schrieb:
> zwischen SCL/VCC und SDA/VCC besteht
> allerdings keinerlei Verbindung.

Warum sollte das der Fall sein? Ich sehen keinen Grund dafür.

Du kannst an einem Halbleiter-Eingang keine Aussagekräftigen
Widerstandsmessungen machen. Allerhöchstens könntest du bei
einem Widerstand nahe null Ohm aussagen dass der Eingang
defekt ist.

von huehoet (Gast)


Lesenswert?

I2C Unwissender schrieb:
> huehoet schrieb:
>> Als Anmerkung noch:
>
> Du siehst schon, dir antwortet wohl kaum einer da du schwarze
> Magie betreibst.

?
Das verstehe ich nicht.

> huehoet schrieb:
>> zwischen SCL/VCC und SDA/VCC besteht
>> allerdings keinerlei Verbindung.
>
> Warum sollte das der Fall sein? Ich sehen keinen Grund dafür.
>
> Du kannst an einem Halbleiter-Eingang keine Aussagekräftigen
> Widerstandsmessungen machen. Allerhöchstens könntest du bei
> einem Widerstand nahe null Ohm aussagen dass der Eingang
> defekt ist.

huehoet schrieb:
> am Display

Ich messe zwischen den Anschlusspins des Displays um entscheiden zu 
können, ob da nicht sogar vllt. schon ein Pullup vorhanden ist. Aber mir 
fällt auch gerade auf, dass das eigentlich nichts zu bedeuten hat, da es 
mit einem AVR ja funktionierte. Auch mit Pull-Ups.
Keine Angst, ich kam nicht auf die Idee, VCC/GND gegenüber SCL/SDA am µC 
zu vermessen.

von I2C Unwissender (Gast)


Lesenswert?

huehoet schrieb:
> huehoet schrieb:
>> am Display

Meinst du das Display ist kein Halbleiter? Was sonst?

huehoet schrieb:
> Keine Angst, ich kam nicht auf die Idee, VCC/GND gegenüber SCL/SDA am µC
> zu vermessen.

Schon die Ausdrucksweise und die Gedanken dahinter lassen
daran denken

I2C Unwissender schrieb:
> da du schwarze Magie betreibst.

von huehoet (Gast)


Lesenswert?

I2C Unwissender schrieb:
> huehoet schrieb:
>> huehoet schrieb:
>>> am Display
>
> Meinst du das Display ist kein Halbleiter? Was sonst?

Die Anschlusspins. Wäre ein Pull-Up auf der Display-Platine, so wäre 
dies vor dem Halbleiter.
Das ist aber auch vollkommen egal. Mir geht es um I2C und nicht um den 
Widerstand.

> I2C Unwissender schrieb:
>> da du schwarze Magie betreibst.

Wie genau kommst du auf diese Idee?

von Stefan G. (Firma: Herr) (stefg)


Lesenswert?

Hallo
Das hier:

I2C1->CR2: 0x24 (frequency at 36MHz (SYSCLK=72MHz))

heisst doch hoffentlich nicht dass der Bus auf 36MHz getaktet ist?
Als I2C Master musst Du den Takt auf ca. 250kBit einstellen - schau was 
das Display kann...

von huehoet (Gast)


Lesenswert?

Stefan G. schrieb:
> Hallo
> Das hier:
>
> I2C1->CR2: 0x24 (frequency at 36MHz (SYSCLK=72MHz))
>
> heisst doch hoffentlich nicht dass der Bus auf 36MHz getaktet ist?
> Als I2C Master musst Du den Takt auf ca. 250kBit einstellen - schau was
> das Display kann...

Es geht gerade erst einmal darum, dass die I2C-Communication überhaupt 
funktioniert und die Bits von der Hardware passend gesetzt werden (siehe 
z.B. das SB-bit).
Und nein, das ist der Takt von APB1. Das Maximum da ist 36MHz, ich habe 
es aber auch schon mit 8MHz ausprobiert, keine Veränderung.

Siehe Ausgangspost ist der Bus auf 100kHz getaktet:
>  /* frequency for SCL (clk); SM mode, 100kHz */
>  I2C1->CCR &= ~(I2C_CCR_FS | I2C_CCR_DUTY | I2C_CCR_CCR);
>  I2C1->CCR |= 0x28;


Noch etwas:
Wenn ich den Workaround aus dem Errata Sheet unter 2.13.7 verwende, dann 
kriege ich es hin, dass die Bits nach dem Versenden der Start condition 
richtig gesetzt werden (SB high, START cleared, MSL high). Aber es wird 
schon wieder nicht das BUSY-flag gecleared, sodass das schon wieder auf 
1 ist und bleibt. Es kann doch nicht sein, dass ich diesen Workaround 
nach jedem Byte oder jeder Condition anwenden muss, oder? Kennt da noch 
jemand eine andere Lösung?

von Stefan F. (Gast)


Lesenswert?

Hallo huehoet,

ich bin gerade in einer ganz ähnlichen Situation wie du, ich versuche 
mich auch gerade zum ersten mal an der I2C Schnittstelle des STM32. Also 
eins muss ich mal mal sagen: Bei AVR ist das deutlich einfacher.

Die Prozedur im Referenzhandbuch wurde durch die Application Note AN2824 
ergänzt. Leider widersprechen sich die beiden Dokumente ein wenig, und 
in der AN wird ein Byte zu wenig ausgelesen. Die zahlreichen Tutorials 
im Internet machen es noch komplizierter, wie sie so wie du ständig das 
Busy Flag abfragen, was aber weder im Referenzhandbuch noch in der AN 
empfohlen wird.

Jedenfalls habe ich es nun endlich erfolgreich hinbekommen, und zwar 
indem ich primär nach dem Referenzhandbuch vorgegangen bin und dann die 
Workarounds aus der AN hinzugefügt habe.

Das Ergebnis habe ich hier veröffentlicht:
Beitrag "Re: STM3103RBT6 ist mein I²C Master mit CMSIS so korrekt?"

Ich glaube es ist eine gute Idee, meine Code mit deinem zu vergleichen. 
Ich habe aber gerade keinen Nerv mehr dazu, muss mal was anderes machen. 
Programmieren strengt an.

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.