Hallo,
ich kämpfe nun schon seit geraumer Zeit mit dem I2C-Modul des STM32
unter Verwendung der Standard-Lib.
Mir ist aufgefallen, dass bei TX nach einem
1
I2C_GenerateSTOP(I2C2,ENABLE);
direkt ein
1
I2C_GenerateSTOP(I2C2,DISABLE);
eingefügt werden muss, falls dies nach dem letzten gesendeten Byte
erfolgt. Macht man dies nicht, so wird direkt nach dem nächsten Start,
was man sendet ein Stop gesendet.
Bei RX wird Stop vor Empfang des letzten Bytes gesetzt, es ist kein
DISABLE notwendig, alles funktioniert.
Weiß jemand woran das liegt, bzw. hat jemand die gleichen Erfahrungen
gemacht?
Viele Grüße!
Servus,
welchen stm32 hast du genau???
Generell ist der i2c bus beim stm32f1x etwas vermurkst. Ich empfehle
dafür folgende Appnote: AN2824. Weiterhin empfehle ich dir DMA zu
nutzen, weil man sonst bei den zeitkritischen bus ohne ein LA/Oszi
aufgeschmissen ist!
Außerdem würde ich mir in der Appnote genannten I2CRoutines.c mal
anschauen. Diese hält sich strickt an das Reference Manual/AN2824.
Damit müssest du schnell etwas zum blinken bringen :)
PS: Pullup bei 400khz und 3,3V sollte 1k und bei 5V 1,7k betragen!
mfg
Hallo,
ich hab nen STM32F103. Das mit dem vermurkst hab ich auch schon
festgestellt...
Danke für den Tipp mit der Appnote, werd mir die mal angucken.
Also einmal senden geht. Falls ich danach noch was mit dem I2C machen
will, so geht nix mehr. Auf dem Oszi sieht man wie nach dem Start direkt
ein Stop gesendet wird und das wars dann.
Falls ich z.B. ein Register eines Slaves lesen will, also:
Addresse+W, Registeradresse, RepStart, Addresse+R, Registerinhalt, ... ,
Nack, Stop
So kann ich auch danach dasselbe nochmal machen, ohne dass irgendwas
hängt.
Hab glaub ich meinen Fehler gefunden.
Man sollte sich peinlichst genau an das Diagramm auf Seite 752 des
Reference Manual (RM0008) halten.
Man sollte immer beim Senden bei einem TXE-Flag Daten in das
Sende-Register laden und nur falls zusätzlich ein BTF-Flag auftritt, ein
Stop senden.
Habs damit doch nicht gelöst, aber folgendes festgestellt.
Der Fehler tritt nur bei exakt 100 kHz auf.
Stell ich eine Bus-Frequenz von 99 kHz, 101 kHz oder 400 kHz ein, so
funktioniert alles wunderbar.
Hat jemand schonmal etwas ähnliches festgestellt?
Servus,
also ohne dir zu nahe zu treten, bitte benutze die DMA. Sonst empfehle
ich dir den "Diller STM32 Tutorial", da hat er es ganz elegant gelösst.
Beim stm32 sollte man die Flags sofort nach dem Eintritt in den Interupt
löschen, da der nächste Interrupt zur Störung führt. Wo ist es bei dir.
Nicht gut. Auch in der Appnote/.c Datei wird geschildert, dass zum Teil
ein Delay erstellt werden muss, da sonst alles sich aufhängt.
Mein Tipp:
-DMA nutzen,
-STM32 Tutorial
-Timeout erstellen
hier siehst du den Vorteil:
http://letanphuc.net/2014/06/stm32-mpu6050-dma-i2c/
Ich dachte anfangs auch scheiß auf DMA, aber net beim i2c bus!!! Der
Code halbiert sich. Garantiert!
mfg
aSma>> schrieb:> Servus,> also ohne dir zu nahe zu treten, bitte benutze die DMA. Sonst empfehle> ich dir den "Diller STM32 Tutorial", da hat er es ganz elegant gelösst.>> Beim stm32 sollte man die Flags sofort nach dem Eintritt in den Interupt> löschen, da der nächste Interrupt zur Störung führt. Wo ist es bei dir.> Nicht gut. Auch in der Appnote/.c Datei wird geschildert, dass zum Teil> ein Delay erstellt werden muss, da sonst alles sich aufhängt.>> Mein Tipp:> -DMA nutzen,> -STM32 Tutorial> -Timeout erstellen>> hier siehst du den Vorteil:> http://letanphuc.net/2014/06/stm32-mpu6050-dma-i2c/>> Ich dachte anfangs auch scheiß auf DMA, aber net beim i2c bus!!! Der> Code halbiert sich. Garantiert!>> mfg
Danke für die konstruktive Kritik!
Hab mir das mit DMA mal angeschaut, sieht wirklich wesentlich einfacher
aus. Werde das bei Gelegenheit dann so umsetzen.
Viele Grüße!
Habs nun in DMA umgebaut.
Ich hab den Code aus dem genannten STM32-Tutorial, bzw. leicht
angepasst. Ich brauch Funktionen um einmal in ein Slave-Register zu
schreiben (Adr+W, 2 Bytes) und zu lesen (Adr+W, Reg-Adr., Adr+R, Daten).
Der Code aus dem Tutorial hat im Prinzip letzteres fertig implementiert
gehabt.
Habs nun so erweitert, dass auch reines schreiben gehen soll.
Nur ist mir da was komisches aufgefallen. Bei der Schreibfunktion will
ich insgesamt 2 Bytes schreiben. Es werden bei:
1
DMA_SetCurrDataCounter(DMA1_Channel4,2);
aber nur 1 Byte gesendet, also Adr+W und 1 Byte.
Setz ich:
1
DMA_SetCurrDataCounter(DMA1_Channel4,3);
so werden Adr+W und 2 Bytes gesendet. Weiß jemand woran das liegt??
Das würde sich ja mit dem Lesebefehl widersprechen (dieser funktioniert
wie er soll).
Hier noch der Code (die Init hab ich mal wegelassen, hier wurde einfach
ergänzt, dass durch DMA1_Channel4 IRQs ausgelöst werden können):
Slave-Zugriffsfunktionen:
1
/*
2
* Schreibt Daten in ein Register des Slaves.
3
* Zuerst wird die Addresse+W geschickt, dann die Registeraddresse,
Hab eine Erklärung für das verloren gegangen Byte gefunden.
DMA löst einen IRQ aus, falls alles an die Peripherie übergeben wurde,
was logischerweise vor dem letzten über I2C versendeten Byte der Fall
ist.
mh schrieb:> Hab eine Erklärung für das verloren gegangen Byte gefunden.> DMA löst einen IRQ aus, falls alles an die Peripherie übergeben wurde,> was logischerweise vor dem letzten über I2C versendeten Byte der Fall> ist.
Ja, also die slave_addr zählt man nicht mit. Nur, bei dir "Registerdata
und Data".
mh schrieb:> void i2c2_write_register(uint8_t address, uint8_t registeraddress,> uint8_t data)
Ich würde das lieber als Buffer versenden oder wenigstens als Pointer.