Forum: Mikrocontroller und Digitale Elektronik stm32f1 i2c als master


von Felix C. (felix_c13)


Lesenswert?

Hallo allerseits

Ich will mit dem ET-STM32F ARM Kit von http://www.ett.co.th/ einen 
Drucksensor über I2C auslesen.

Kurz zu meiner Clockkonfiguration:
HSE mit25MHz
SYSCLOCK = 72
HCLK = 72MHz
PCLK1 = 36MHz
PCLK2 = 72MHz


Meine Konfiguration:
RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);

    /* Reset I2Cx IP */
    //RCC_APB1PeriphResetCmd(RCC_APB1Periph_I2C1, ENABLE);

    /* Release reset signal of I2Cx IP */
    //RCC_APB1PeriphResetCmd(RCC_APB1Periph_I2C1, DISABLE);

    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7 | GPIO_Pin_6;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &GPIO_InitStructure);

    I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
    I2C_InitStructure.I2C_OwnAddress1 = 0x00;        //Eigene Adresse 
für Slave oder Multimaster betrieb, ansonsten irrelevant
    I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
    I2C_InitStructure.I2C_Ack = I2C_Ack_Disable;
    I2C_InitStructure.I2C_AcknowledgedAddress = 
I2C_AcknowledgedAddress_7bit;   //Adress-Modus, normalerweise 7-Bit
    I2C_InitStructure.I2C_ClockSpeed = 400000; 
//Taktgeschwindigkeit von SCL, hier 50kHz
    I2C_Init(I2C1, &I2C_InitStructure);

    I2C_ITConfig(I2C1, I2C_IT_EVT | I2C_IT_BUF, DISABLE);  //Keine 
Interrupts

    I2C_Cmd(I2C1, ENABLE);


Code im main:

Input: (I2C1,0x40,I2C_Direction_Transmitter)

void I2C_start(I2C_TypeDef* I2Cx, uint8_t address, uint8_t direction){
  // wait until I2C1 is not busy any more
  while(I2C_GetFlagStatus(I2Cx, I2C_FLAG_BUSY));

  // Send I2C1 START condition
  I2C_GenerateSTART(I2Cx, ENABLE);

  // wait for I2C1 EV5
  while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_MODE_SELECT));

  // Send slave Address for write
  I2C_Send7bitAddress(I2Cx, address, direction);

  /* wait for I2Cx EV6, check if
   * either Slave has acknowledged Master transmitter or
   * Master receiver mode, depending on the transmission
   * direction
   */
  if(direction == I2C_Direction_Transmitter){
    while(!I2C_CheckEvent(I2Cx, 
I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));  //Hier bleibt er hängen
  }
  else if(direction == I2C_Direction_Receiver){
    while(!I2C_CheckEvent(I2Cx, 
I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED));
  }
}


Hardware:
SCL und SDA sind jeweils mit einem 10k auf Masse gezogen.


Was funktioniert:
Die Register des I2C1 werden erfolgreich gesetzt. Ich arbeite mit Keil 
sehe dies also im Debugger. CCR etc. alles richtig..
Nach der Initalisierung liegen SCL und SDA auf 3.28V, habe leider nur 
ein Multimeter und kein Oszi.
Wenn ich die Startkondition machen will, funktioniert das auch alles.
Busy, MSL und SB werden gesetzt.
SCL fällt auf 0V SDA auf 0.13V was doch auch noch im Rahmen sein sollte 
oder?

Wenn ich jetzt die Adresse ins DR-Reg schiebe funzt das auch, allerdings 
krieg ich dann grad nen AF-Error.

Ich bin ehrlich komplett ratlos was falsch ist. Habe das ganze auch 
schon mit nem Arduino getestet und der Sensor funktioniert wunderbar.

Falls jemand auch Keil benutzt kann ich ihm gerne auch mein Projekt 
schicken.

Ich bin gerade echt am verzweifeln, bin da schon seit 3 Tagen dran, wenn 
jemand also iwie Zeit für ne Teamviewer/Sykpe Sitzung hätte oder 
irgendwelche Ideen hat, wäre ich schwer dankbar :)

Gruss und Dank

Felix

von Bernd M. (bernd_m)


Lesenswert?

SDA und SCL benötigen 4k7 Pullups (open drain)

von Felix C. (felix_c13)


Lesenswert?

Bernd M. schrieb:
> SDA und SCL benötigen 4k7 Pullups (open drain)

Wieso?

Die sind doch nur dafür da, die Leitungen "default" auf High zu setzen, 
damit sie per open-drain wieder runter gezogen werden.

Egal habs jetzt mal ausprobiert und funzt immer noch net. Nebenbei das 
Arduino Mega benutzt auch 10k auf SCL/SDA.

Ergänzung:

Da PB6/PB7 auf einem RS232-Modul verbunden sind(was ja eigentlich egal 
sein sollte) habe ich die Pins jetzt mal auf PB8/PB geremapt, was aber 
auch nich half.

Einziger Unterschied Low liegt jetzt bei 0.05V, High bei 3.28V unter 
Nutzung von 4K7.

: Bearbeitet durch User
von Bernd M. (bernd_m)


Lesenswert?

Felix C. schrieb:
> SCL und SDA sind jeweils mit einem 10k auf Masse gezogen.

Felix C. schrieb:
> Wieso?
>
> Die sind doch nur dafür da, die Leitungen "default" auf High zu setzen,
> damit sie per open-drain wieder runter gezogen werden.

Die Pullup sind Arbeitswiderstände, ohne die der I2C nicht funktioniert.
Die 4k7 sind ein Erfahrungswert, er kann irgendwo zwischen 1k und 100k 
liegen, je nach Buseigenschaften. Die Pullup kann man auch exakt 
berechnen.

http://www.nxp.com/documents/user_manual/UM10204.pdf

Und noch eine übersichtliche Darstellung des I2c:
https://de.wikipedia.org/wiki/I%C2%B2C

von Felix C. (felix_c13)


Lesenswert?

Bernd M. schrieb:
> Die Pullup sind Arbeitswiderstände, ohne die der I2C nicht funktioniert.
> Die 4k7 sind ein Erfahrungswert, er kann irgendwo zwischen 1k und 100k
> liegen, je nach Buseigenschaften. Die Pullup kann man auch exakt
> berechnen.

Danke für den Einwand, auch wenn er mir kein Stück weiterhilft, da ich 
ja bereits schrieb, dass das System bereits mit 10k funktioniert hat. 
4k7 wurden wie bereits beschrieben ebenfalls ausprobiert.


Falls jemand ein Keil Projekt oder Beispielcode hat, für egal was für 
eine I2C Anwendung wäre ich extrem dankbar. Da ich soqieso nicht über 
das Addressieren des Sensors hinauskomme, könnte ich die Adresse ja 
schnell anpassen.

: Bearbeitet durch User
von Bernd M. (bernd_m)


Lesenswert?

Felix C. schrieb:
> Hardware:
> SCL und SDA sind jeweils mit einem 10k auf Masse gezogen.

Aber das ein Pullup gegen VCC geht und nicht gegen Masse, darin stimmen 
wir überein?

Ich habe hier einen STM32L151 als I2C Master im polling an laufen, ist 
zwar ein anderer uC aber alles sehr ähnlich:

Der Init Struct sieht folgendermaßen aus:
1
static const I2C_InitTypeDef i2c_master_InitStructure =
2
{
3
   .I2C_ClockSpeed = 100000,
4
   .I2C_Mode = I2C_Mode_I2C,
5
   .I2C_DutyCycle = I2C_DutyCycle_2,
6
   .I2C_OwnAddress1 = I2C_MASTER_ADR,
7
   .I2C_Ack = I2C_Ack_Enable,
8
   .I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit
9
};

Ich habe zwei getrennte Funktionen zum Schreiben und Lesen. Die sind 
dicht an die STM lib angelehnt, ohne Aufrufe in die STM lib. Ich poste 
die beiden Funktionen, weil ich nicht die Muße habe, deinen Code, der 
augenscheinlich nicht der verwendete Code ist, im Zusammenhang mit der 
STM lib zu analysieren:
1
/* -------------------------------------------------------------------------- */
2
uint8 i2c_writeBuffer (uint8 i2cAdr, uint8 * buffer, uint16 length)
3
{
4
   uint16 idx;
5
   uint16 tmpSR1;
6
   uint8 retVal;
7
8
   retVal = 1;
9
10
   /* generate START condition */
11
   SET_BIT(I2C_MASTER->CR1, I2C_CR1_START);
12
13
   /* wait for START event */
14
   while(READ_BIT(I2C_MASTER->SR1, I2C_SR1_SB) == 0);
15
16
   /* send I2C address for write - bit0 = 0 */
17
   i2cAdr &= (uint8)~((uint8)I2C_OAR1_ADD0);
18
   I2C_MASTER->DR = i2cAdr;
19
20
   /* wait until address was sent */
21
   do {
22
      tmpSR1 = I2C_MASTER->SR1;
23
   }
24
   while((tmpSR1 & (I2C_SR1_ADDR | I2C_SR1_AF)) == 0);
25
26
   if (tmpSR1 & I2C_SR1_ADDR) {
27
      /* Clear ADDR Flag by reading SR1 then SR2 registers (SR1 have already been read) */
28
        (void)I2C_MASTER->SR2;
29
30
        idx = 0;
31
        while(idx < length) {
32
           /* send data byte */
33
           I2C_MASTER->DR = buffer[idx];
34
           idx++;
35
36
           /* wait until data was sent */
37
           while(READ_BIT(I2C_MASTER->SR1, I2C_SR1_BTF) == 0);
38
        }
39
40
        /* generate STOP condition */
41
        SET_BIT(I2C_MASTER->CR1, I2C_CR1_STOP);
42
43
        retVal = 0;
44
   }
45
46
   return retVal;
47
}
48
49
/* -------------------------------------------------------------------------- */
50
uint8 i2c_readBuffer (uint8 i2cAdr, uint8 * buffer, uint16 length)
51
{
52
   uint16 idx;
53
   uint16 tmpSR1;
54
   uint8 retVal;
55
56
   retVal = 1;
57
58
   /* generate START condition */
59
   SET_BIT(I2C_MASTER->CR1, I2C_CR1_START);
60
61
   /* wait for START event */
62
   while(READ_BIT(I2C_MASTER->SR1, I2C_SR1_SB) == 0);
63
64
   /* send I2C address for read - bit0 = 1 */
65
   i2cAdr |= I2C_OAR1_ADD0;
66
   I2C_MASTER->DR = i2cAdr;
67
68
   /* wait until address was sent */
69
   do {
70
      tmpSR1 = I2C_MASTER->SR1;
71
   }
72
   while((tmpSR1 & (I2C_SR1_ADDR | I2C_SR1_AF)) == 0);
73
74
75
   if (length == 1) {
76
      /* generate last byte and STOP condition */
77
      CLEAR_BIT(I2C_MASTER->CR1, I2C_CR1_ACK);
78
   }
79
   else {
80
      SET_BIT(I2C_MASTER->CR1, I2C_CR1_ACK);
81
   }
82
83
   if (tmpSR1 & I2C_SR1_ADDR) {
84
      /* Clear ADDR Flag by reading SR1 then SR2 registers (SR1 have already been read) */
85
        (void)I2C_MASTER->SR2;
86
87
        idx = 0;
88
        while(idx < length) {
89
           /* wait until data was received */
90
           while(READ_BIT(I2C_MASTER->SR1, I2C_SR1_RXNE) == 0);
91
92
           /* read data byte */
93
           buffer[idx] = I2C_MASTER->DR;
94
           idx++;
95
96
           if (idx >= length - 1) {
97
              /* generate last byte and STOP condition */
98
              CLEAR_BIT(I2C_MASTER->CR1, I2C_CR1_ACK);
99
              SET_BIT(I2C_MASTER->CR1, I2C_CR1_STOP);
100
           }
101
        }
102
103
        retVal = 0;
104
   }
105
106
   return retVal;
107
}

Ist Folgendes in Deinem Code berücksichtigt, sollte eigentlich durch die 
STM lib geschehen:
"Note: Reading I2C_SR2 after reading I2C_SR1 clears the ADDR flag, even 
if the ADDR flag was set after reading I2C_SR1. Consequently, I2C_SR2 
must be read only when ADDR is found set in I2C_SR1 or when the STOPF 
bit is cleared."

Läßt Du dir Registerinhalte des I2C anzeigen? Beim Durchsteppen ist das 
ein Problem. Der Debugger liest das SR2 als erster, dann kommt dein 
Programm und der Inhalt ist bereits verändert.

Evtl. ist dein I2C Slave mittlerweile in einem undefinierten Zustand, 
ich hatte das mit dem DS3231. Eine mögliche Lösung ist hier zu finden:
http://www.forward.com.au/pfod/ArduinoProgramming/I2C_ClearBus/index.html

Zur Analyse was wirklich auf dem I2C geschiet ist ein LogicAnalyzer von 
Vorteil.

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.