[STM32] I2C - Master Mode(Polling)

Gast #2250266
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)
Gast #2250450
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?
Gast #2251872
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.
Gast #2253972
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.
#2281670
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
Gast #2384164
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

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren