Forum: Mikrocontroller und Digitale Elektronik [STM32] I2C - Master Mode(Polling)


von Bernhard (Gast)


Lesenswert?

Hi,

ich möchte mit einem STM32F103RB Derivat ein I2C Device ansprechen. Der 
Mikrocontroller soll dabei als Master und das anzusprechende Device als 
Slave fungieren. Mein Code sieht folgendermaßen aus:

Initialisierung:
1
void init_i2c(void){
2
  RCC->APB1ENR |= RCC_APB1ENR_I2C1EN; //I2C1 Clock anknipsen 
3
  
4
  I2C1->CR1 &= I2C_CR1_PE; //I2C1 deaktivieren
5
6
  I2C1->CR1 &= I2C_CR1_SMBUS; //SMBus Mode = I2C
7
  I2C1->CR2 &= I2C_CR2_DMAEN; //DMA deaktivieren
8
  I2C1->CR2 &= I2C_CR2_ITBUFEN; //kein Interrupt bei TxE = 1 oder RXNE = 1
9
  I2C1->CR2 &= I2C_CR2_ITEVTEN; //Event Interrupt deaktivieren
10
  I2C1->CR2 &= I2C_CR2_ITERREN; //Error Interrupt deaktivieren
11
  I2C1->CR2 = ((I2C1->CR2 & ~I2C_CR2_FREQ) | (I2C_CR2_FREQ_3 ));   //setze FREQ auf 08h
12
  I2C1->CCR &= I2C_CCR_FS; //Standard Mode I2C    
13
  I2C1->CCR = 0x28;  //I2C Frequenz = 100kHz 
14
  I2C1->TRISE = 0x65; //Berechnung: siehe Datasheet
15
  I2C1->CR1 |= I2C_CR1_PE; //I2C1 aktivieren
16
17
  RCC->APB2ENR |= RCC_APB2ENR_IOPBEN; //GPIOB clock anknipsen
18
  RCC->APB2ENR |= RCC_APB2ENR_AFIOEN; //AFIO Clock anknipsen 
19
  GPIOB->CRL = (GPIOB->CRL & ~0xFF000000) | 0xFF000000; //PB6 und PB7 als Alternate Function Output Open Drain
20
21
}

Sende-Routine:
1
void send(){
2
  I2C1->CR1 |= I2C_CR1_START; //sende Start-bit
3
  while(!(I2C1->CR1 & I2C_SR1_SB)); //warte bis SB Flag gesetzt wurde
4
  I2C1->DR = 0x20;//schreibe Slave Adresse ins data register (Adresse fuers Schreiben = 32dez)
5
  while(!(I2C1->SR1 & I2C_SR1_ADDR)); //warte bis ADDR gesetzt wurde <- hier bleibt das Programm haengen
6
}

Und das Hauptprogramm:
1
int main(void){
2
  init_i2c(); 
3
  send();
4
5
  for(;;){
6
  }
7
}

Mir ist bewusst, dass die Sende-Routine nicht vollständig ist, doch um 
das Problem einzukreisen hab ich das Programm auf das notwendigste 
reduziert. Und zwar bleibt das Programm in dieser Zeile: 
"while(!(I2C1->SR1 & I2C_SR1_ADDR));" hängen. Ich hab mir dann mit dem 
Debugger die entsprechenden I2C1-Register angesehen und festgestellt, 
dass das BUSY Flag dauerhaft gesetzt ist. Wenn ich mit dem Oszi die 
Taktleitung ansehe, dann ist diese dauerhaft auf High. Da sollte ja 
eigentlich ein schöner Clock zu messen sein, oder?!

Hat jemand einen Tipp was das Problem sein könnte?

Bevor ichs vergesse: Sowohl SDA als auch SCL besitzen einen Pullup gegen 
VCC (also eigentlich sinds auf jeder Leitung zwei Pullups, masterseitig 
10k Ohm und slaveseitig 4k7 Ohm - ergibt also 3,19k Ohm)

von Bernhard (Gast)


Lesenswert?

Gerade ist mir aufgefallen, dass in der Initialisierungsroutine ein paar 
'~' gefehlt haben. Das hatte aber keine Auswirkungen, weil die 
entsprechenden Register zu Beginn sowieso mit 0 initialisiert waren.

Dennoch hier der korrigierte Sourcecode:
1
void init_i2c(void){
2
  RCC->APB1ENR |= RCC_APB1ENR_I2C1EN; //I2C1 Clock anknipsen 
3
  
4
  I2C1->CR1 &= ~I2C_CR1_PE; //I2C1 deaktivieren
5
6
  I2C1->CR1 &= ~I2C_CR1_SMBUS; //SMBus Mode = I2C
7
  I2C1->CR2 &= ~I2C_CR2_DMAEN; //DMA deaktivieren
8
  I2C1->CR2 &= ~I2C_CR2_ITBUFEN; //kein Interrupt bei TxE = 1 oder RXNE = 1
9
  I2C1->CR2 &= ~I2C_CR2_ITEVTEN; //Event Interrupt deaktivieren
10
  I2C1->CR2 &= ~I2C_CR2_ITERREN; //Error Interrupt deaktivieren
11
  I2C1->CR2 = ((I2C1->CR2 & ~I2C_CR2_FREQ) | (I2C_CR2_FREQ_3 ));   //setze FREQ auf 08h
12
  I2C1->CCR &= ~I2C_CCR_FS; //Standard Mode I2C    
13
  I2C1->CCR = 0x28;  //I2C Frequenz = 100kHz 
14
  I2C1->TRISE = 0x65; //Berechnung: siehe Datasheet
15
  I2C1->CR1 |= I2C_CR1_PE; //I2C1 aktivieren
16
17
  RCC->APB2ENR |= RCC_APB2ENR_IOPBEN; //GPIOB clock anknipsen
18
  RCC->APB2ENR |= RCC_APB2ENR_AFIOEN; //AFIO Clock anknipsen 
19
  GPIOB->CRL = (GPIOB->CRL & ~0xFF000000) | 0xFF000000; //PB6 und PB7 als Alternate Function Output Open Drain
20
  
21
}

Sollte der I2C Master (also in meinem Fall der uC) nicht den Takt 
generieren? Also wenn, aus welchen Gründen auch immer, der Slave nicht 
auf die gesendete Adresse reagiert, dann müsste doch zumindest ein 
Taktsignal auf der SCL Leitung zu messen sein. Oder irre ich da?

von Matthias K. (matthiask)


Lesenswert?

AFIO braucht Du nicht freigeben. Nur wenn Du die alternativen Pins 
benutzt. Dann müsste aber noch eine Zuweisung erfolgen.

von Matthias K. (matthiask)


Lesenswert?

Oder: Versuchs doch mal testweise mit den Fkt. der STM FW-Lib. Dann 
bekommste hier auch mehr Hilfe.

von Bernhard (Gast)


Lesenswert?

Matthias K. schrieb:
> AFIO braucht Du nicht freigeben. Nur wenn Du die alternativen Pins
> benutzt. Dann müsste aber noch eine Zuweisung erfolgen.

Alles klar - war mir da nicht 100% sicher. Die FW-Library mag ich 
irgendwie nicht - ich würds viel lieber selbst ausprogrammieren.

von Sven Wagner (Gast)


Lesenswert?

Bernhard schrieb:
> Die FW-Library mag ich
> irgendwie nicht - ich würds viel lieber selbst ausprogrammieren.
Kannst ja trotzdem mal die FW-Lib verwenden und wenn's geht mit dem 
Debugger die Registerwerte anschauen.

Grüße
Sven

von Bernhard (Gast)


Lesenswert?

Die Idee ist eigentlich gar nicht mal so schlecht. Mal schauen, ob ich 
die FW-Library eingebunden bekomme.

von Lutz (Gast)


Lesenswert?

Zuerst solltest du mal alles überflüssige rausschmeißen. I2C_CR1 und 2 
haben als Resetwert 0. Wenn du vorher evtl. im Programm schon etwas 
verändert haben solltest, setz die Register einfach mit I2C->CR1 = 0; 
auf 0 und mach nicht x Löschbefehle für einzelne Bits. Dann wird es 
zumindest übersichtlicher.

von Bernhard (Gast)


Lesenswert?

Hallo,

hab es jetzt, wie vorgeschlagen, mit der Standard Library versucht. Ein 
bisschen weiter bin ich damit gekommen. Ich bekomme jetzt einen AF 
(acknowledge failure) nachdem ich die Slave Adresse an den Bus anlege.
Mein Programm sieht so aus:
1
#define I2C_Speed              100000
2
#define I2C1_SLAVE_ADDRESS7    0xA1
3
4
void GPIO_Configuration(void)
5
{
6
  GPIO_InitTypeDef  GPIO_InitStructure; 
7
8
  /* Configure I2C1 pins: SCL and SDA */
9
  GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_6 | GPIO_Pin_7;
10
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
11
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;
12
  GPIO_Init(GPIOB, &GPIO_InitStructure);
13
}
14
15
void I2C_Configuration(void)
16
{
17
  I2C_InitTypeDef  I2C_InitStructure; 
18
19
  /* I2C configuration */
20
  I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
21
  I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
22
  I2C_InitStructure.I2C_OwnAddress1 = I2C1_SLAVE_ADDRESS7;
23
  I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
24
  I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
25
  I2C_InitStructure.I2C_ClockSpeed = I2C_Speed;
26
  
27
  /* I2C Peripheral Enable */
28
  I2C_Cmd(I2C1, ENABLE);
29
  /* Apply I2C configuration after enabling it */
30
  I2C_Init(I2C1, &I2C_InitStructure);
31
}
32
33
int main(void)
34
{
35
36
  /* Enable peripheral clocks --------------------------------------------------*/
37
  /* GPIOB Periph clock enable */
38
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
39
  /* I2C1 Periph clock enable */
40
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);
41
42
  /* GPIOA Periph clock enable */
43
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
44
45
  /* I2C configuration */
46
  I2C_Configuration();
47
48
   /* GPIO configuration */
49
  GPIO_Configuration();
50
51
  /* Send STRAT condition */
52
  I2C_GenerateSTART(I2C1, ENABLE);
53
54
  /* Test on EV5 and clear it */
55
  while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT));  
56
57
  I2C_Send7bitAddress(I2C1, 0x20, I2C_Direction_Transmitter);
58
  
59
  /* Test on EV6 and clear it */
60
  while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));
61
62
  for(;;){
63
  }
64
}

Da ich keinerlei Erfahrung mit der Library hab, hab ich mich fast 1:1 an 
das Demoprogramm gehalten. Was ich jedoch am Sourcecode nicht ganz 
verstehe: Warum muss ich im Master Mode die eigene Slave Adresse 
(I2C1_SLAVE_ADDRESS7) festlegen? Ich dachte, das muss man nur im Slave 
Mode machen?

Mein Problem deckt sich mit dem was ich hier[1] gefunden habe, bis auf 
die Tatsache, dass ich ein anderes STM32 Derivat einsetze.

[1] 
https://my.st.com/public/STe2ecommunities/mcu/Lists/cortex_mx_stm32/DispForm.aspx?ID=17515&Source=/public/STe2ecommunities/mcu/Tags.aspx?tags=C

Ich hoffe, jemand hat Erfahrung mit der Lib und kann mir einen Tipp 
geben.

von Lutz (Gast)


Lesenswert?

Bernhard schrieb:
> /* I2C configuration */
>   I2C_Configuration();
>
>    /* GPIO configuration */
>   GPIO_Configuration();

Sollte die Reihenfolge nicht andersrum sein?

von Bernhard (Gast)


Lesenswert?

Ich hab mich da an das Posting von A.K. gehalten:

Beitrag "Re: Stm32 und I2C"

von Lutz (Gast)


Lesenswert?

9.1.4 Alternate functions (AF)
It is necessary to program the Port Bit Configuration Register before 
using a default alternate function.

von Bernhard (Gast)


Lesenswert?

So hatte ich es ursprünglich, doch auch damit hat's nicht funktioniert.

von Lutz (Gast)


Lesenswert?

Bernhard schrieb:
> /* I2C Peripheral Enable */
>   I2C_Cmd(I2C1, ENABLE);
>   /* Apply I2C configuration after enabling it */
>   I2C_Init(I2C1, &I2C_InitStructure);

Die Reihenfolge muß auch gedreht werden, sonst läuft der I2C ja mit den 
Resetwerten los.

von Sylvia H. (sandy)


Lesenswert?

Hallo Bernhard,
hast du inzwischen das Problem gelöst? Bei mir ist es nämlich ganz genau 
das gleiche:
Ich schicke die 7-bit-Adresse, und als Antowrt kommt anstatt EV6 immer 
nur ein AF.
Komischerweise hatte das gleiche Programm ein paar Stunden vorher ohne 
Probleme funktioniert, aber auf einmal baff, ohne das irgendetwas 
geändert wurde ........
nur noch AF und das nun seit 2 Tagen.
Irgendeine Lösung?
Gruß
Sylvia

von Sylvia H. (sandy)


Lesenswert?

noch ein kurzer Nachtrag:
ich benutze den STM32f103RCT6

von Paul (Gast)


Lesenswert?

Hallo,

ich hab genau das selbe Problem mit meinem I2C Port am STM32VL 
Discovery, wie es hier beschrieben ist mit der Firmware-Lib. Die 
Pull-Ups sind gesetzt, Master-Seite 10k und Slave-Seite 4,7k.

Gibt es schon eine Lösung zu diesem Problem? Wäre cool wenn es einer von 
euch posten könnte.

mfg

Paul

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.