18-I2C_LoLevel-Library (STM32F4)

-diese Library dient zum benutzen der I2C-Schnittstelle im Master-Mode

-die I2C-Pins die benutzt werden sollen, müssen im C-File eingetragen werden
(im H-File kann die I2C-Clock-Frq eingestellt werden)

-auf der Hardware dürfen die zwei externen Pull-Up Widerstände (je 4k7) an SCL und SDA nicht vergessen werden, sonst funktioniert das ganze nicht.

-die Library kann als LoLevel-Funktion die Schnittstelle initialisieren und Daten zu einem Slave senden und von einem Slave empfangen.

-es gibt Funktionen um ein einzelnes Datenbyte zu senden/empfangen und Funktionen um mehrere Bytes zu senden/empfangen. In dem Fall müßen die Daten vor dem senden in einem Array stehen bzw. stehen nach dem empfangen in einem Array.

-Die Funktion “UB_I2C1_WriteCMD” sendet nur ein einzelnes Byte an den Slave.

-es gibt auch noch eine “Pausenfunktion” was einfach nur ein Zähler auf 0 ist um z.B. für langsame I2C-Devives eine Wartefunktion einfügen zu können.

-es gibt 3 identische Librarys, getrennt für I2C1, I2C2 und I2C3

-im Beispiel wurde I2C1 benutzt mit dieser Pinbelegung :

1
2
SCL an PB6
SDA an PB7

Voraussetzungen :

1
2
Benutzte Module der CooCox-IDE : GPIO, I2C
Benutzte Librarys : keine

Funktionen (für I2C1) :

1
2
3
4
5
6
7
8
9
void UB_I2C1_Init(void);                                                     // zum initialisieren der I2C-Schnittstelle
int16_t UB_I2C1_ReadByte(uint8_t slave_adr, uint8_t adr);                    // um ein Byte zu lesen
int16_t UB_I2C1_WriteByte(uint8_t slave_adr, uint8_t adr, uint8_t wert);     // um ein Byte zu schreiben
int16_t UB_I2C1_ReadMultiByte(uint8_t slave_adr, uint8_t adr, uint8_t cnt);  // um mehrere Bytes zu lesen
int16_t UB_I2C1_WriteMultiByte(uint8_t slave_adr, uint8_t adr, uint8_t cnt); // um mehrere Bytes zu schreiben
int16_t UB_I2C1_WriteCMD(uint8_t slave_adr, uint8_t cmd);                    // ein einzelnes Byte senden
int16_t UB_I2C1_ReadByte16(uint8_t slave_adr, uint16_t adr);                 // 16bit adresse auslesen
int16_t UB_I2C1_WriteByte16(uint8_t slave_adr, uint16_t adr, uint8_t wert);  // 16bit adresse beschreiben
void UB_I2C1_Delay(volatile uint32_t nCount);                                // Pausenfunktion

Beispiel :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
//--------------------------------------------------------------
// File     : main.c
// Datum    : 07.03.2013
// Version  : 1.0
// Autor    : UB
// EMail    : mc-4u(@)t-online.de
// Web      : www.mikrocontroller-4u.de
// CPU      : STM32F4
// IDE      : CooCox CoIDE 1.7.0
// Module   : CMSIS_BOOT, M4_CMSIS_CORE
// Funktion : Demo der I2C-LoLevel-Library
// Hinweis  : Diese zwei Files muessen auf 8MHz stehen
//              "cmsis_boot/stm32f4xx.h"
//              "cmsis_boot/system_stm32f4xx.c"
//--------------------------------------------------------------
 
#include "main.h"
#include "stm32_ub_i2c1.h"
 
int main(void)
{
  int16_t wert;
 
  SystemInit(); // Quarz Einstellungen aktivieren
 
  UB_I2C1_Init(); // Init der I2C1-Schnittstelle
 
  // ein Byte vom I2C-Slave mit Adr 0xA0
  // aus Register-Adr 0x01 auslesen
  wert=UB_I2C1_ReadByte(0xA0,0x01);
  if(wert<0) {
    // Fehler
  }
 
  // den Wert 0x12 zum I2C-Slave mit Adr 0xC0
  // in Register-Adr 0x02 speichern
  wert=UB_I2C1_WriteByte(0xC0,0x02,0x12);
  if(wert<0) {
    // Fehler
  }
 
  UB_I2C1_Delay(400); // kleine Pause nach dem schreiben
 
  while(1)
  {
 
  }
}

Hier die Library zum Download :

ub_stm32f4_i2c_v103

Hier der komplette CooCox-Projektordner zum Download :

Demo_18_I2C_LoLevel

29 Antworten auf 18-I2C_LoLevel-Library (STM32F4)

  1. Slevin sagt:

    Hi,

    erstmal vielen Dank für deine Lib, die bei mir sehr gut funktioniert.

    Ich verwende dein I2C Besipiel und würde gerne 2 Bytes empfangen.
    Leider habe ich es bisher nicht zum laufen gebracht.
    Wie müsste ich deinen Code anpassen, damit ich das 2. Byte empfangen kann:

    Danke!
    MfG

    • admin_ub sagt:

      Hi Slevin, lad dir mal die Version 1.1 von der Library runter. Da hab ich zwei neue Funktionen eingefügt. Mit denen kann man mehrere Bytes senden/empfangen. In deinem Fall zum empfangen von zwei Bytes würde ein Beispiel so aussehen :

      UB_I2C1_Init();
      // 2 Bytes vom I2C-Slave mit Adr 0xA0
      // ab Register-Adr 0x01 auslesen
      check=UB_I2C1_ReadMultiByte(0xA0,0x01,2);
      // wenn check==0 dann stehen die zwei Byte
      // in dem Array "I2C1_DATA"
      

      probier das mal aus und schreib ob es funktioniert
      dann mach ich die Änderung auch beim STM32F429

  2. Slevin sagt:

    Hi, leider funktioniert die Funktion UB_I2C1_ReadMultiByte bei mir (teilweise) nicht. Der Slave (Beschleunigungssensor ADXL345) hängt sich beim lesen der Bytes bzw. bei der Übermittlung der Slave-Adresse (0xA6) auf – kurz bevor ich die Bytes lesen möchte.
    Ich habe zusätzlich eine Stop-Sequenz vor der zweiten Start-Sequenz eingefügt (laut Datenblatt).
    Ein einzelnes Byte kann ich problemlos lesen, nur zwei klappen irgendwie nicht.

    • admin_ub sagt:

      lass mal die zusätzliche “STOP-Sequenz” weg, und probier es nochmal … und schau mal um Debugger nach welchen Rückgabewert die Funktion liefert. Da sieht man wo sie rausfliegt.

      • Slevin sagt:

        es klappt!!! Ich habe testweise das Auslesen der Register in den IRQHandler geschoben und nun funktioniert es. Irgendwie scheint der Timer die I2C Kommunikation zu stören, das muss ich nochmal genauer anschauen.
        Also, deine I2C Funktionen laufen einwandfrei.
        Vielen Dank nochmals!

  3. Slevin sagt:

    Hi, könnte man die I2C Schnittstelle auch mit DMA realisieren?

    • Joerg B. sagt:

      Klar kann man das, sie StdLibs Beispiel 2 Boards

  4. Ilyas sagt:

    Hallo,

    ich bin noch recht neu auf dem Gebiet der Mikrocontroller und habe seit kurzem mit dem stm32f4 zu tun.
    Ich möchte mit I2C arbeiten, als IDE verwende ich KeilµVision 4 (Laborrechner unserer Uni).
    Leider bekomme ich es nicht zum Laufen, über Hilfe freue ich mich sehr.

    Grüße, Ilyas

    • admin_ub sagt:

      deine Fehlerbeschreibung “leider bekomme ich es nicht zum laufen” ist zu kurz. Lese dich in die Funktionsweise vom I2C-Protokoll ein, messe die Signale nach, vergleiche den Ist-Zustand mit dem Soll-Zustand und schreib dann nochmal was genau nicht funktioniert wie es soll.

      • Ilyas sagt:

        Hallo,
        entschuldige, dass es bissl unglücklich (und zu kurz) formuliert habe, mein Fehler.
        Die Funktionsweise des I2C ist mir im Grunde klar. Ich möchte das dicovery-board einem I2c-fähigen AD-Wandler verbinden (ADS7830 von Texas Instruments).
        Wie die Anfrage vom Discoveryboard an den ADC aussehen soll ist mir klar. Leider bin ich mit der Vielzahl der Einstellmöglichkeiten am STM32F4 überfordert.
        Ich habe mir dein Beispiel angesehen, bekomme es aber in meiner IDE (Keil µVision 4) nicht zum laufen.

        Beste Grüße, Ilyas

        • admin_ub sagt:

          schon wieder “bekomm es nicht zum laufen” !! du musst am F4 gar nichts “einstellen” das macht die Library alles für dich und die ist nicht compilerspezifisch.

  5. Ilyas sagt:

    Guten Abend,

    habe mir in diesem Zusammenhang auch die Standard Peripherals Library von ST zum STM32F4 angeschaut. Beim Versuch die dort enthaltenen Beispiele zu nutzen stoße ich auf das Problem, dass während des Linkens Fehler auftauchen wie bespielsweise:
    “.\STM32F40_41xxx\STM32F40_41xxx.axf: Error: L6218E: Undefined symbol LCD_Clear (referred from main.o).”
    Im entsprechenden readme des Beispielprojekts wird darauf hingewiesen, dass 3 .c-files “Add the following files in the project source list” dem Projekt hinzugefügt werden müssen. Jedoch behebt dies den Fehler nicht.
    Mein Board ist das STM32F407VGT6.
    Vielen Dank für eure Hilfe.

    Grüße, Ilyas

    • admin_ub sagt:

      sorry, für linkerfehler bin ich nicht zuständig.

  6. Ilyas sagt:

    Guten Tag,

    arbeite jetzt mit der CoIDE und deinem Beispielcode (18-I2C_LoLevel).
    Ich finde die Kommentare und erläuterungen im Code sehr hilfreich, vielen Dank hierfür.
    Zunächst will ich mich am Audio-DAC (Cirrus CS43L22) versuchen.
    Dieser befindet sich an I2C1 (PB6 bzw. PB9). Von außen habe ich 2x 4,7 kOhm Widerstände mit +3V verbunden, da auf dem Discovery-Board selbst nur für I2C3 Pullups vorhanden sind.
    In “stm32_ub_i2c1.h” habe ich für SCL PB6 und für SDA PB9 ausgewählt.
    Wenn ich nun beispielsweise die Lautstärke für Kanal A (Master A Vol) schreiben möchte, erhalte ich als Rückgabewert der UB_I2C1_WriteByte eine -2.

    Aufruf: UB_I2C1_WriteByte(0×94,0×20,0×00)
    0×94 ist die Adresse des Audio-DAC, 0×20 die Register-Adresse für die Lautstärke von Kanal A und 0×00 der zu schreibende Wert für die Lautstärke.

    Was habe ich vergessen zu beachten?
    Grüße, Ilyas

    • Ilyas sagt:

      Fehler gefunden:
      Man muss den Audio-DAC noch einschalten. Mit dem Schaltplan des Discovery-Boards wird ersichtlich, dass RESET/ per 10k Widerstand mit Masse verbunden ist…

      Ich hoffe, dass diese Information auch für andere User hilfreich ist.

      Grüße, Ilyas

  7. Michi sagt:

    Hallo,
    kurze Verständnisfrage, meine Main:
    int main(void)
    {
    int16_t wert;
    int16_t wert2;

    SystemInit(); // Quarz Einstellungen aktivieren
    UB_I2C3_Init(); // Init der I2C1-Schnittstelle

    wert=UB_I2C3_WriteByte(0×88,0×00,0×02);
    if(wert<0) {
    // Fehler
    }
    UB_I2C3_Delay(400); // kleine Pause nach dem schreiben

    wert2=UB_I2C3_ReadByte(0×88,0×00);
    if(wert2<0) {
    // Fehler
    }

    UB_I2C3_Delay(400);
    while(1)
    {
    }
    }
    Also isch Schreibe einen Wert 0×02 in ein Register und lese ihn dann wieder aus.
    Müsste dann nicht dieser Wert in der Variable wert2 stehen? Bei mir steht da nämlich nur 1 drinnen egal was ich mache ….
    Danke
    Gruß Michi

    • admin_ub sagt:

      was für einen Slave hast du den angeschlossen ? Event. ist die Adresse 0×00 eine Read-Only.

      • Michi sagt:

        Hallo,
        Als Slave habe ich einen TDA7439 (Digitale Klangregelung):
        http://www.st.com/web/en/resource/technical/document/datasheet/CD00004906.pdf
        angeschlossen… Eine Read Only Adresse kann es nicht sein!
        Die 4,7k Ohm Wid. auf 5V sind auch vorhanden.

        • admin_ub sagt:

          im Datasheet steht nichts von READ also vermutlich geht das gar nicht. Wenn der Rückgabewert der Write Funktion =0 ist, dann hat das IC mit einem ACK geantwortet und alles sollte gut sein. Du kannst zum Spass ja mal eine andere Slave adresse senden z.B. 0×90 dann sollte eine Fehler zurueckgemeldet werden.

          • Michi sagt:

            Danke für deine Hilfe!
            Hast recht bei einer anderen Addresse kommt ein Fehler.

  8. Benedikt sagt:

    Hallo,
    leider habe ich nun schon länger Probleme mit der I2C Schnittstelle. Ich habe dein Beispiele als Master und als Slave übernommen und diese aufeinander geschalten. Also die I2C1 Schnittstelle als Master auf die I2C2 Schnittstelle als Slave. Aber ich bekomme immer -1 zurück. Auch bei meinen Test mit einem MPU6050 kam immer nur -1 zurück.
    Kann mir hier jemand weiterhelfen?
    Vielen Dank

    • admin_ub sagt:

      Hast du die Beispiel 1:1 übernommen oder was abgeändert ?
      Sind die externen 4k7 Pull-Up Widerstände in SCL und SDA eingelötet ?
      Ich denke doch du hast ZWEI STM32F4 benutzt, einer als Master und einer als Slave richtig ? Eine CPU kann nicht Master und Slave gleichzeitig sein (zumindest nicht mit meinen Librarys)

      • Benedikt sagt:

        Ich habe nur den Master das richtige Byte schreiben lassen. Sonst habe ich nichts geändert (UB_I2C1_WriteByte(0×70,0×00,0×12);).
        Ich habe 2 STMF4 und die Widerstände sind auch vorhanden. Habe auch schon versucht die internen zu nehmen. Habe PB8 mit PB8 und PB7 mit PB7 verbunden. Leider ohne Erfolg. LED bleibt Rot und return Value ist -1.

        • admin_ub sagt:

          hast du auch GND von beiden Boards verbunden ?
          Hast du die Änderung für PB8 in beiden C-Files (Master und Slave) gemacht ?.Laufen beide Boards mit der richtigen Frequenz ?. Zur Not wirst du mit dem Oszi Fehler suchen müssen. Die Librarys funktionieren (hab ich beide getestet)

          • Benedikt sagt:

            Ja, habe ich alles gemacht. Hatte heute die Möglichkeit mit einem Oszi zu messen. Der Master scheint richtig zu arbeiten. Nur antwortet der Slave nicht.

          • admin_ub sagt:

            sorry, da kann ich nicht helfen. Event. sendet der Master schon Daten bevor der Slave bereit ist.
            Mach mal eine Pause von 1sec bevor das Byte zum Slave gesendet wird. Und schalte den Slave zuerst ein.

  9. sadok sagt:

    hallo alle zuzamen ..konnte vlt jemanden mir helfen !!!!
    ich versuche mehere byte von meine Sensor zu lesen aber irgenwie klappt es nicht mit diese funktion :( :(
    UB_I2C2_ReadMultiByte(uint8_t slave_adr, uint8_t adr, uint8_t cnt)!!!

    ich kann nur eine byte lesen mit das andere funktion

    bitte kann mir jemanden helfen ??

    • admin_ub sagt:

      was für ein “Sensor” ist das ? Und welche Daten willst du auslesen ?

  10. Christian sagt:

    Hallo,

    ich wollte diese Funktionen heute abend mal testen. da ich schon recht viel mit I2C gemacht habe frage ich mich allerdings, wie hier NAK und ACk gehandhabt werden, bzw das Einfügen von Stop Bits? Das ist bei jedem Baustein anders, daher macht es auch wenig Sinn I2C auf High-Level zu schreiben. Wichtig sind nur die Timeouts, da mit jedem beliebigen Busfehler gerechnet werden muss.

    Ich wollte das heute abend mal mit einem 24LC32 von Microchip testen.


Wie hat Dir dieser Artikel gefallen?

1 Stern2 Sterne3 Sterne4 Sterne5 Sterne (Noch keine Bewertungen)
Loading...

4 Kommentare zu 18-I2C_LoLevel-Library (STM32F4)

  1. Tades sagt:

    Hallo,
    wollte die Libraries für stm32f4xxx und Beispiel code runterladen, und die sind leider nicht mehr verfügbar. Kann ich die noch irgendwie kriegen?
    Danke im voraus

    Tades

    • admin_mb sagt:

      Hallo Tades,
      ich habe die fehlenden Dateien erneut hochgeladen.
      Jetzt sollte der Download funktionieren;)
      LG, Manfred

  2. Tades sagt:

    Hallo admin_mb,
    erstmal danke schön weil ich Libraries jetzt downloaden konnte. Ich habe aber ein Problem und würde mich über Vorschläge sehr freuen.
    Ich versuche einen byte aus dem Drucksensor SDP600 (Link:https://www.mouser.com/datasheet/2/682/Sensirion_Differential_Pressure_Sensors_SDP600Seri-1109144.pdf) zu lesen. Der sensor wird als slave benutz und Das STM32F407VG ist der Master. Die externen Pull Up Widerstände sind eingeschlossen und ich benutze die Pins 6 und 7 wie in deinem Beispiel. Leider bleibe ich hängen in der folgenden Weil Schleife und bekomme dann immer -1 zurück (Fehler):

    1
    2
    3
    4
    
     I2C_GenerateSTART(I2C1, ENABLE);
      //timeout=I2C1_TIMEOUT;
      while (!I2C_GetFlagStatus(I2C1, I2C_FLAG_SB)) {
        if(timeout!=0) timeout--; else return(P_I2C1_timeout(-1));

    der Sensor ist noch in guter zustand und die Schaltung is richtig. Das Weiss ich weil ich das ganze an Arduino angeschlossen habe und da kriege ich schon mal vernünftige Werte zurück.
    Ich habe leider kein Oszi zum messen und visualisieren des CLK (pin6) und Data(Pin7) signal des STM32F407. Ich konnte aber mein VDD signal mit einem hand Multimeter messen und dieser liegt bei 2,9 V. Kann das ein Problem sein? Hat jemand ein Vorschlag für mich?
    Gruß
    Tades

    • admin_mb sagt:

      Hallo Tades,
      leider kann es viele Ursachen haben wieso die besagte While Schleife einen Fehler zurückliefert;(
      Bei welcher Funktion ist das eigentlich? UB_I2C1_ReadByte() oder UB_I2C1_WriteByte()?

      Laut Datenblatt ist die Default I2C Adresse = 64. Kopiere mal folgende Funktion in dein Main.c und rufe sie auf, um zu prüfen, ob das mit der I2C Adresse stimmt.

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      
      //--------------------------------------------------------------
      // I2C Port Scan
      //--------------------------------------------------------------
      uint8_t UB_I2C_PortScan()
      {
      	uint8_t start=0x01;
      	uint8_t ende=0x7F;
      	uint8_t slave_adr=start;
      	uint8_t port=0;
      	uint16_t ret;
      	uint8_t adr=0b00000000;
       
      	do
      	{
      		ret = UB_I2C1_WriteByte(slave_adr,adr,0xff);
       
      		if (ret == 0)
      			port = slave_adr; // I2C Port gefunden!!!
      		else
      			slave_adr++; // naechste Adresse testen
       
      	} while (ret!=0 && slave_adr<=ende);
       
      	return port;
      }

      Viel Erfolg,
      Manfred

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.