STM32L4 I2C slave interrupt transmit

Gast #5553126
Lesenswert?

Hallo Forengemeinde,

seit ein paar Tagen beschäftige ich mich mit dem STM32L476 (Nucleo-64).
Jetzt möchte ich mit Hilfe der HAL-Treiber, welche ebenfalls neu für 
mich sind, einen I2C-Slave-Transmiter realisieren. Auch einen µC als 
I2C-Slave zu betreiben habe ich noch nicht gemacht.

Dafür habe ich mit STMCube die I2C Schnittstelle so eingerichtet:
1
/* I2C1 init function */
2
static void MX_I2C1_Init(void)
3
{
4

5
  hi2c1.Instance = I2C1;
6
  hi2c1.Init.Timing = 0x10909CEC;
7
  hi2c1.Init.OwnAddress1 = 8;
8
  hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
9
  hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
10
  hi2c1.Init.OwnAddress2 = 0;
11
  hi2c1.Init.OwnAddress2Masks = I2C_OA2_NOMASK;
12
  hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
13
  hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_ENABLE;
14
  if (HAL_I2C_Init(&hi2c1) != HAL_OK)
15
  {
16
    _Error_Handler(__FILE__, __LINE__);
17
  }
18

19
    /**Configure Analogue filter 
20
    */
21
  if (HAL_I2CEx_ConfigAnalogFilter(&hi2c1, I2C_ANALOGFILTER_ENABLE) != HAL_OK)
22
  {
23
    _Error_Handler(__FILE__, __LINE__);
24
  }
25

26
    /**Configure Digital filter 
27
    */
28
  if (HAL_I2CEx_ConfigDigitalFilter(&hi2c1, 0) != HAL_OK)
29
  {
30
    _Error_Handler(__FILE__, __LINE__);
31
  }
32

33
}
Interrupt Event Handler:
1
/**
2
* @brief This function handles I2C1 event interrupt.
3
*/
4
void I2C1_EV_IRQHandler(void)
5
{
6
  /* USER CODE BEGIN I2C1_EV_IRQn 0 */
7
  HAL_GPIO_TogglePin(GPIOA,D7_Pin);
8
  /* USER CODE END I2C1_EV_IRQn 0 */
9
  HAL_I2C_EV_IRQHandler(&hi2c1);
10
  /* USER CODE BEGIN I2C1_EV_IRQn 1 */
11

12
  /* USER CODE END I2C1_EV_IRQn 1 */
13
}
14

15
/**
16
* @brief This function handles I2C1 error interrupt.
17
*/
18
void I2C1_ER_IRQHandler(void)
19
{
20
  /* USER CODE BEGIN I2C1_ER_IRQn 0 */
21
  HAL_GPIO_TogglePin(GPIOB,D6_Pin);
22
  /* USER CODE END I2C1_ER_IRQn 0 */
23
  HAL_I2C_ER_IRQHandler(&hi2c1);
24
  /* USER CODE BEGIN I2C1_ER_IRQn 1 */
25

26
  /* USER CODE END I2C1_ER_IRQn 1 */
27
}
Für´s erste möchte ich die Beiden Pins D6 und D7 toggeln lassen sobald 
der I2C Slave von Master angesprochen wird. Wie ich festgestellt habe 
toggelt bei mir nichts. Auf dem Osszi kann ich sehen das der Slave mit 
dem Acknowledge bestätigt (Low).
Nun habe ich gedacht, dass danach der I2C-Interrupt ausgelöst wird, dem 
aber nicht so ist.
Jetzt zu meinen Fragen. Wann wird der I2C-Interrupt ausgelöst?
Bei der Funktion
1
HAL_StatusTypeDef HAL_I2C_Slave_Transmit_IT(I2C_HandleTypeDef *hi2c, uint8_t *pData, uint16_t Size)
 habe ich gedacht, dass diese dafür da ist. Aber wo packe ich die hin?
Ich stelle mir dem I2C-Interrupt wie einen Ext.-Interrupt vor der auf 
ein Ereignis wartet. Sehe ich das Richtig?

Danke im Voraus

Der Vollständigkeit halber,der Master (Arduino):
1
void loop() {
2

3
  delay(500);
4
  Wire.requestFrom(4, 30);
5
  while ( Wire.available() )
6
  {
7
    //c = (char)Wire.read();
8
    //
9
    buf[i] = (char)Wire.read(); //=c;(char)
10
    //Serial.print(c);
11
    Serial.print(buf[i]);
12
    i++;
13
  }
14
  
15
}
Gast #5553160
Lesenswert?

Es ist eigentlich immer besser die vorgesehene Callback Funktion zu 
nutzen.
In den Beispielen ist eine Kommunikation von 2 Boards als Master und 
Slave vorhanden.

STM32Cube_FW_L4_V1.12.0/Projects/NUCLEO-L476RG/Examples/I2C/I2C_TwoBoard 
s_ComIT/
Gast #5555547
Lesenswert?

Hallo,

habe mir die verschieden Beispiele angesehen und auch eine Variante mit 
der HAL_I2C_Slave_Transmit Funktion zum laufen bekommen, allerdings ohne 
Callback Funktion --> invalide Daten.

Harry L. schrieb:
> Schau dir
> HAL_I2C_SlaveRxCpltCallback(I2C_HandleTypeDef *hi2c)
> an!

Wo finde ich Lesestoff oder Beispiele wie die Callback Treiber zu 
verwenden sind?
Mir ist nicht klar wann oder durch was z.B. die Funktion
1
 void HAL_I2C_SlaveTxCpltCallback(I2C_HandleTypeDef *hi2c)
 aufgerufen wird.

Meine Suchmaschine hat für mich noch nichts aufschlussreiches zu Tage 
gefördert "how to use HAL_I2C_SlaveTxCpl Callback".

Danke für eure Ratschläge und Tipps
Gast #5555554
Lesenswert?

pegel schrieb:
> Wenn du den Interrupt für I2C in CubeMX eingestellt hast,
> wird die Funktion, die als weak vorliegt aufgerufen.

ja habe ich gemacht (Häkchen sind gesetzt).

pegel schrieb:
> Du brauchst den Inhalt nur z.B. in der main.c selbst festlegen.

z.B. so?
1
void HAL_I2C_SlaveTxCpltCallback(I2C_HandleTypeDef *hi2c)
2
{
3
  /* Prevent unused argument(s) compilation warning */
4
  UNUSED(hi2c);
5
  HAL_I2C_Slave_Transmit(&hi2c1, aTxBuffer, 2,1000);
6
  /* NOTE : This function should not be modified, when the callback is needed,
7
            the HAL_I2C_SlaveRxCpltCallback could be implemented in the user file
8
   */
9
  HAL_GPIO_TogglePin(GPIOA, D7_Pin);
10
}
#5555559
Lesenswert?

chrstm schrieb:
> Mir ist nicht klar wann oder durch was z.B. die Funktion void
> HAL_I2C_SlaveTxCpltCallback(I2C_HandleTypeDef *hi2c)  aufgerufen wird.

chrstm schrieb:
> habe mir die verschieden Beispiele angesehen und auch eine Variante mit
> der HAL_I2C_Slave_Transmit Funktion zum laufen bekommen, allerdings ohne
> Callback Funktion --> invalide Daten.

Die Funktion HAL_I2C_SlaveTxCpltCallback wird nur aufgerufen wenn du die 
Interrupt- oder DMA-API für den I2C benutzt. HAL_I2C_Slave_Transmit ist 
aber eine ganz einfache blocking-API. Da ist der Zeitpunkt wenn der 
Transfer fertig ist logischerweise der, wenn die Funktion zurückkehrt 
und die Benutzung eines Callbacks somit Käse.
Gast #5555572
Lesenswert?

chrstm schrieb:
> Benutze ich den Interrupt-I2C dann indem ich die Funktion
> HAL_I2C_EnableListen_IT(&hi2c1);

Diese Funktion habe ich noch nie benutzt.

Sonst gilt für alle anderen mit Endung:

_IT         -> Interrupt
_DMA        -> wie der Name schon sagt
ohne Endung -> blockierend

das ist immer so, nicht nur bei I2C.
Gast #5555589
Lesenswert?

pegel schrieb:
> Diese Funktion habe ich noch nie benutzt.

Ok.

Wenn ich die Funktion
1
 HAL_I2C_Slave_Transmit_IT(&hi2c1, aTxBuffer, 23)
 in meiner Main Funktion benutzen möchte werden die Daten nicht 
übertragen. Ich gehe davon aus das ich noch irgend etwas aktivieren 
muss. Aber was?

Mit meinem Master frage ich nur Daten ab. Also weiß ich das der Slave 
nur die Daten senden muss / soll. Somit sollte ich doch den Interrupt im 
Slave registrieren und dann die Daten mit der Funktion 
HAL_I2C_Slave_Transmit_IT und der Variable aTxBuffer senden können. So 
ganz kann ich das was in der HAL_I2C_Slave_Transmit_IT steht nicht 
nachvollziehen.
Gast #5555622
Lesenswert?

Harry L. schrieb:
> Zeig bitte den gesamten Code und nicht aus dem Kontext gerissene
> einzelne Zeilen!

Damit ich erstmal irgend welche Daten übertragen bekommen habe ich 
zuerst ein mal diesen Code probiert.
1
 
2
void SystemClock_Config(void)
3
{
4

5
  RCC_OscInitTypeDef RCC_OscInitStruct;
6
  RCC_ClkInitTypeDef RCC_ClkInitStruct;
7
  RCC_PeriphCLKInitTypeDef PeriphClkInit;
8

9
    /**Configure LSE Drive Capability 
10
    */
11
  HAL_PWR_EnableBkUpAccess();
12

13
  __HAL_RCC_LSEDRIVE_CONFIG(RCC_LSEDRIVE_LOW);
14

15
    /**Initializes the CPU, AHB and APB busses clocks 
16
    */
17
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_LSE|RCC_OSCILLATORTYPE_MSI;
18
  RCC_OscInitStruct.LSEState = RCC_LSE_ON;
19
  RCC_OscInitStruct.MSIState = RCC_MSI_ON;
20
  RCC_OscInitStruct.MSICalibrationValue = 0;
21
  RCC_OscInitStruct.MSIClockRange = RCC_MSIRANGE_6;
22
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
23
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
24
  {
25
    _Error_Handler(__FILE__, __LINE__);
26
  }
27

28
    /**Initializes the CPU, AHB and APB busses clocks 
29
    */
30
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
31
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
32
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_MSI;
33
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV4;
34
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
35
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
36

37
  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK)
38
  {
39
    _Error_Handler(__FILE__, __LINE__);
40
  }
41

42
  PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_USART2|RCC_PERIPHCLK_I2C1;
43
  PeriphClkInit.Usart2ClockSelection = RCC_USART2CLKSOURCE_PCLK1;
44
  PeriphClkInit.I2c1ClockSelection = RCC_I2C1CLKSOURCE_PCLK1;
45
  if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
46
  {
47
    _Error_Handler(__FILE__, __LINE__);
48
  }
49

50
    /**Configure the main internal regulator output voltage 
51
    */
52
  if (HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1) != HAL_OK)
53
  {
54
    _Error_Handler(__FILE__, __LINE__);
55
  }
56

57
    /**Configure the Systick interrupt time 
58
    */
59
  HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000);
60

61
    /**Configure the Systick 
62
    */
63
  HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);
64

65
    /**Enable MSI Auto calibration 
66
    */
67
  HAL_RCCEx_EnableMSIPLLMode();
68

69
  /* SysTick_IRQn interrupt configuration */
70
  HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0);
71
}
72

73
/* I2C1 init function */
74
static void MX_I2C1_Init(void)
75
{
76

77
  hi2c1.Instance = I2C1;
78
  hi2c1.Init.Timing = 0x00000103;
79
  hi2c1.Init.OwnAddress1 = 8;
80
  hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
81
  hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
82
  hi2c1.Init.OwnAddress2 = 0;
83
  hi2c1.Init.OwnAddress2Masks = I2C_OA2_NOMASK;
84
  hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
85
  hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_ENABLE;
86
  if (HAL_I2C_Init(&hi2c1) != HAL_OK)
87
  {
88
    _Error_Handler(__FILE__, __LINE__);
89
  }
90

91
    /**Configure Analogue filter 
92
    */
93
  if (HAL_I2CEx_ConfigAnalogFilter(&hi2c1, I2C_ANALOGFILTER_ENABLE) != HAL_OK)
94
  {
95
    _Error_Handler(__FILE__, __LINE__);
96
  }
97

98
    /**Configure Digital filter 
99
    */
100
  if (HAL_I2CEx_ConfigDigitalFilter(&hi2c1, 0) != HAL_OK)
101
  {
102
    _Error_Handler(__FILE__, __LINE__);
103
  }
104

105
}

In der Main ist nicht wirklich was los ;-)
1
int main(void)
2
{
3
  /* USER CODE BEGIN 1 */
4

5
  /* USER CODE END 1 */
6

7
  /* MCU Configuration----------------------------------------------------------*/
8

9
  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
10
  HAL_Init();
11

12
  /* USER CODE BEGIN Init */
13

14
  /* USER CODE END Init */
15

16
  /* Configure the system clock */
17
  SystemClock_Config();
18

19
  /* USER CODE BEGIN SysInit */
20

21
  /* USER CODE END SysInit */
22

23
  /* Initialize all configured peripherals */
24
  MX_GPIO_Init();
25
  MX_USART2_UART_Init();
26
  MX_I2C1_Init();
27
  /* USER CODE BEGIN 2 */
28

29
  /* USER CODE END 2 */
30

31
  /* Infinite loop */
32
  /* USER CODE BEGIN WHILE */
33
  while (1)
34
  {
35

36
    if(HAL_I2C_Slave_Transmit(&hi2c1, (uint8_t*)aTxBuffer, TXBUFFERSIZE, 10000)!= HAL_OK)
37
      {
38
        /* Transfer error in transmission process */
39
       // Error_Handler();
40
      }
41
 }
42
}
Die Funktion
1
HAL_I2C_Slave_Transmit_IT(&hi2c1, (uint8_t*)aTxBuffer, TXBUFFERSIZE)
hatte ich mal in der while (1) schleife bringt aber nix.
Ich denke die ganze Zeit das ich doch prüfen muss ob ein Interrupt 
aufgetreten ist. Oder aber es muss doch eine Funktion geben die 
automatisch bei einem Interrupt aufgerufen wird, ähnlich wie bei 
externen Interrupts.

Im Anhang sind noch die Einstellungen vom CudeMx.
Angehängte Dateien:
Gast #5556415
Lesenswert?

Hallo nochmal,

ich habe heute noch einmal neu Angefangen.
Wenn ich die Grundeinstellungen mit StmCubeMX generiere kann ich mit dem 
Oszi sehen, dass die "Leseanfrage" vom Master gesendet wird und nicht 
abgebrochen wird (Slave bestätigt mit Ack nach der Adresse mit 
anschließendem Read). Übertragen wird nur High weil ich ja noch keine 
Anweisung für den Slave geschrieben habe.


pegel schrieb:
> Wenn du den Interrupt für I2C in CubeMX eingestellt hast,
> wird die Funktion, die als weak vorliegt aufgerufen.
>
> Du brauchst den Inhalt nur z.B. in der main.c selbst festlegen.

Die mit __weak gekennzeichneten Funktionen sind:
1
__weak void HAL_I2C_MspInit(I2C_HandleTypeDef *hi2c)
2
__weak void HAL_I2C_MspDeInit(I2C_HandleTypeDef *hi2c)
3
__weak void HAL_I2C_MasterTxCpltCallback(I2C_HandleTypeDef *hi2c)
4
__weak void HAL_I2C_MasterRxCpltCallback(I2C_HandleTypeDef *hi2c)
5
__weak void HAL_I2C_SlaveTxCpltCallback(I2C_HandleTypeDef *hi2c)
6
__weak void HAL_I2C_SlaveRxCpltCallback(I2C_HandleTypeDef *hi2c)
7
__weak void HAL_I2C_AddrCallback(I2C_HandleTypeDef *hi2c, uint8_t TransferDirection, uint16_t AddrMatchCode)
8
__weak void HAL_I2C_ListenCpltCallback(I2C_HandleTypeDef *hi2c)
9
__weak void HAL_I2C_MemTxCpltCallback(I2C_HandleTypeDef *hi2c)
10
__weak void HAL_I2C_MemRxCpltCallback(I2C_HandleTypeDef *hi2c)
11
__weak void HAL_I2C_ErrorCallback(I2C_HandleTypeDef *hi2c)
12
__weak void HAL_I2C_AbortCpltCallback(I2C_HandleTypeDef *hi2c)

Christopher J. schrieb:
> Die Funktion HAL_I2C_SlaveTxCpltCallback wird nur aufgerufen wenn du die
> Interrupt- oder DMA-API für den I2C benutzt. HAL_I2C_Slave_Transmit ist
> aber eine ganz einfache blocking-API. Da ist der Zeitpunkt wenn der
> Transfer fertig ist logischerweise der, wenn die Funktion zurückkehrt
> und die Benutzung eines Callbacks somit Käse.

Wenn ich mit den Callback-Funktionen arbeiten möchte habe ich doch das 
Problem, dass die Funktionen erst ausgeführt werden wenn die Übertragung 
fertig ist, wie ich Christopher J. verstanden habe.
Somit fallen für meine Slaveanwendung, alle RxCpltCallback und 
TxCpltCallback, richtig?
Nach meinem jetzigen Verständnis müsste ich doch in die 
HAL_I2C_ListenCpltCallback() die HAL_I2C_Slave_Transmit_IT() mit den zu 
sendenden reinpacken. Fürs erste wollte ich meinen Pin D6 toggeln 
lassen, ohne Erfolg. Wann wird die Funktion HAL_I2C_ListenCpltCallback() 
aufgerufen?
Hier noch der Programmcode:
1
/**
2
  ******************************************************************************
3
  * @file           : main.c
4
  * @brief          : Main program body
5
  ******************************************************************************
6
  ** This notice applies to any and all portions of this file
7
  * that are not between comment pairs USER CODE BEGIN and
8
  * USER CODE END. Other portions of this file, whether 
9
  * inserted by the user or by software development tools
10
  * are owned by their respective copyright owners.
11
  *
12
  * COPYRIGHT(c) 2018 STMicroelectronics
13
  *
14
  * Redistribution and use in source and binary forms, with or without modification,
15
  * are permitted provided that the following conditions are met:
16
  *   1. Redistributions of source code must retain the above copyright notice,
17
  *      this list of conditions and the following disclaimer.
18
  *   2. Redistributions in binary form must reproduce the above copyright notice,
19
  *      this list of conditions and the following disclaimer in the documentation
20
  *      and/or other materials provided with the distribution.
21
  *   3. Neither the name of STMicroelectronics nor the names of its contributors
22
  *      may be used to endorse or promote products derived from this software
23
  *      without specific prior written permission.
24
  *
25
  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
26
  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27
  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
28
  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
29
  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30
  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
31
  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
32
  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
33
  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
34
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35
  *
36
  ******************************************************************************
37
  */
38
/* Includes ------------------------------------------------------------------*/
39
#include "main.h"
40
#include "stm32l4xx_hal.h"
41

42
/* USER CODE BEGIN Includes */
43

44
/* USER CODE END Includes */
45

46
/* Private variables ---------------------------------------------------------*/
47
I2C_HandleTypeDef hi2c1;
48

49
UART_HandleTypeDef huart2;
50

51
/* USER CODE BEGIN PV */
52
/* Private variables ---------------------------------------------------------*/
53

54
/* USER CODE END PV */
55

56
/* Private function prototypes -----------------------------------------------*/
57
void SystemClock_Config(void);
58
static void MX_GPIO_Init(void);
59
static void MX_USART2_UART_Init(void);
60
static void MX_I2C1_Init(void);
61

62
/* USER CODE BEGIN PFP */
63
/* Private function prototypes -----------------------------------------------*/
64

65
/* USER CODE END PFP */
66

67
/* USER CODE BEGIN 0 */
68

69
/* USER CODE END 0 */
70

71
/**
72
  * @brief  The application entry point.
73
  *
74
  * @retval None
75
  */
76
int main(void)
77
{
78
  /* USER CODE BEGIN 1 */
79

80
  /* USER CODE END 1 */
81

82
  /* MCU Configuration----------------------------------------------------------*/
83

84
  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
85
  HAL_Init();
86

87
  /* USER CODE BEGIN Init */
88

89
  /* USER CODE END Init */
90

91
  /* Configure the system clock */
92
  SystemClock_Config();
93

94
  /* USER CODE BEGIN SysInit */
95

96
  /* USER CODE END SysInit */
97

98
  /* Initialize all configured peripherals */
99
  MX_GPIO_Init();
100
  MX_USART2_UART_Init();
101
  MX_I2C1_Init();
102
  /* USER CODE BEGIN 2 */
103
  
104
  /* USER CODE END 2 */
105

106
  /* Infinite loop */
107
  /* USER CODE BEGIN WHILE */
108
  while (1)
109
  {
110
    HAL_GPIO_TogglePin(GPIOA,LD2_Pin);
111
       HAL_Delay(500);
112
  /* USER CODE END WHILE */
113

114
  /* USER CODE BEGIN 3 */
115

116
  }
117
  /* USER CODE END 3 */
118

119
}
120

121
/**
122
  * @brief System Clock Configuration
123
  * @retval None
124
  */
125
void SystemClock_Config(void)
126
{
127

128
  RCC_OscInitTypeDef RCC_OscInitStruct;
129
  RCC_ClkInitTypeDef RCC_ClkInitStruct;
130
  RCC_PeriphCLKInitTypeDef PeriphClkInit;
131

132
    /**Initializes the CPU, AHB and APB busses clocks 
133
    */
134
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
135
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
136
  RCC_OscInitStruct.HSICalibrationValue = 16;
137
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
138
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
139
  RCC_OscInitStruct.PLL.PLLM = 1;
140
  RCC_OscInitStruct.PLL.PLLN = 10;
141
  RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV7;
142
  RCC_OscInitStruct.PLL.PLLQ = RCC_PLLQ_DIV2;
143
  RCC_OscInitStruct.PLL.PLLR = RCC_PLLR_DIV2;
144
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
145
  {
146
    _Error_Handler(__FILE__, __LINE__);
147
  }
148

149
    /**Initializes the CPU, AHB and APB busses clocks 
150
    */
151
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
152
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
153
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
154
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
155
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
156
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
157

158
  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4) != HAL_OK)
159
  {
160
    _Error_Handler(__FILE__, __LINE__);
161
  }
162

163
  PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_USART2|RCC_PERIPHCLK_I2C1;
164
  PeriphClkInit.Usart2ClockSelection = RCC_USART2CLKSOURCE_PCLK1;
165
  PeriphClkInit.I2c1ClockSelection = RCC_I2C1CLKSOURCE_PCLK1;
166
  if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
167
  {
168
    _Error_Handler(__FILE__, __LINE__);
169
  }
170

171
    /**Configure the main internal regulator output voltage 
172
    */
173
  if (HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1) != HAL_OK)
174
  {
175
    _Error_Handler(__FILE__, __LINE__);
176
  }
177

178
    /**Configure the Systick interrupt time 
179
    */
180
  HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000);
181

182
    /**Configure the Systick 
183
    */
184
  HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);
185

186
  /* SysTick_IRQn interrupt configuration */
187
  HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0);
188
}
189

190
/* I2C1 init function */
191
static void MX_I2C1_Init(void)
192
{
193

194
  hi2c1.Instance = I2C1;
195
  hi2c1.Init.Timing = 0x10909CEC;
196
  hi2c1.Init.OwnAddress1 = 8;
197
  hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
198
  hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
199
  hi2c1.Init.OwnAddress2 = 0;
200
  hi2c1.Init.OwnAddress2Masks = I2C_OA2_NOMASK;
201
  hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_ENABLE;
202
  hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_ENABLE;
203
  if (HAL_I2C_Init(&hi2c1) != HAL_OK)
204
  {
205
    _Error_Handler(__FILE__, __LINE__);
206
  }
207

208
    /**Configure Analogue filter 
209
    */
210
  if (HAL_I2CEx_ConfigAnalogFilter(&hi2c1, I2C_ANALOGFILTER_ENABLE) != HAL_OK)
211
  {
212
    _Error_Handler(__FILE__, __LINE__);
213
  }
214

215
    /**Configure Digital filter 
216
    */
217
  if (HAL_I2CEx_ConfigDigitalFilter(&hi2c1, 0) != HAL_OK)
218
  {
219
    _Error_Handler(__FILE__, __LINE__);
220
  }
221

222
}
223

224
/* USART2 init function */
225
static void MX_USART2_UART_Init(void)
226
{
227

228
  huart2.Instance = USART2;
229
  huart2.Init.BaudRate = 115200;
230
  huart2.Init.WordLength = UART_WORDLENGTH_8B;
231
  huart2.Init.StopBits = UART_STOPBITS_1;
232
  huart2.Init.Parity = UART_PARITY_NONE;
233
  huart2.Init.Mode = UART_MODE_TX_RX;
234
  huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
235
  huart2.Init.OverSampling = UART_OVERSAMPLING_16;
236
  huart2.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
237
  huart2.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
238
  if (HAL_UART_Init(&huart2) != HAL_OK)
239
  {
240
    _Error_Handler(__FILE__, __LINE__);
241
  }
242

243
}
244

245
/** Configure pins as 
246
        * Analog 
247
        * Input 
248
        * Output
249
        * EVENT_OUT
250
        * EXTI
251
*/
252
static void MX_GPIO_Init(void)
253
{
254

255
  GPIO_InitTypeDef GPIO_InitStruct;
256

257
  /* GPIO Ports Clock Enable */
258
  __HAL_RCC_GPIOC_CLK_ENABLE();
259
  __HAL_RCC_GPIOH_CLK_ENABLE();
260
  __HAL_RCC_GPIOA_CLK_ENABLE();
261
  __HAL_RCC_GPIOB_CLK_ENABLE();
262

263
  /*Configure GPIO pin Output Level */
264
  HAL_GPIO_WritePin(LD2_GPIO_Port, LD2_Pin, GPIO_PIN_RESET);
265

266
  /*Configure GPIO pin Output Level */
267
  HAL_GPIO_WritePin(D6_GPIO_Port, D6_Pin, GPIO_PIN_RESET);
268

269
  /*Configure GPIO pin : B1_Pin */
270
  GPIO_InitStruct.Pin = B1_Pin;
271
  GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;
272
  GPIO_InitStruct.Pull = GPIO_NOPULL;
273
  HAL_GPIO_Init(B1_GPIO_Port, &GPIO_InitStruct);
274

275
  /*Configure GPIO pin : LD2_Pin */
276
  GPIO_InitStruct.Pin = LD2_Pin;
277
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
278
  GPIO_InitStruct.Pull = GPIO_NOPULL;
279
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
280
  HAL_GPIO_Init(LD2_GPIO_Port, &GPIO_InitStruct);
281

282
  /*Configure GPIO pin : D6_Pin */
283
  GPIO_InitStruct.Pin = D6_Pin;
284
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
285
  GPIO_InitStruct.Pull = GPIO_NOPULL;
286
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
287
  HAL_GPIO_Init(D6_GPIO_Port, &GPIO_InitStruct);
288

289
}
290

291
/* USER CODE BEGIN 4 */
292
void HAL_I2C_ListenCpltCallback(I2C_HandleTypeDef *hi2c1)
293
{
294
  /* Toggle LED2: Transfer in reception process is correct */
295
  HAL_GPIO_TogglePin(GPIOB,D6_Pin);
296
}
297
/* USER CODE END 4 */
298

299
/**
300
  * @brief  This function is executed in case of error occurrence.
301
  * @param  file: The file name as string.
302
  * @param  line: The line in file as a number.
303
  * @retval None
304
  */
305
void _Error_Handler(char *file, int line)
306
{
307
  /* USER CODE BEGIN Error_Handler_Debug */
308
  /* User can add his own implementation to report the HAL error return state */
309
  while(1)
310
  {
311
    HAL_GPIO_TogglePin(GPIOB,D6_Pin);
312
    HAL_Delay(500);
313
  }
314
  /* USER CODE END Error_Handler_Debug */
315
}
316

317
#ifdef  USE_FULL_ASSERT
318
/**
319
  * @brief  Reports the name of the source file and the source line number
320
  *         where the assert_param error has occurred.
321
  * @param  file: pointer to the source file name
322
  * @param  line: assert_param error line source number
323
  * @retval None
324
  */
325
void assert_failed(uint8_t* file, uint32_t line)
326
{ 
327
  /* USER CODE BEGIN 6 */
328
  /* User can add his own implementation to report the file name and line number,
329
     tex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
330
  /* USER CODE END 6 */
331
}
332
#endif /* USE_FULL_ASSERT */
333

334
/**
335
  * @}
336
  */
337

338
/**
339
  * @}
340
  */
341

342
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
Gast #5556535
Lesenswert?

Danke Harry L.
im Anhang habe ich mal ein Bild von meinem Oszi.
Da sieht man was der Master (ESP8266) an den STM sendet.
Mir stellt sich die Frage wie ich die Daten vom STM an den Master 
zuverlässig senden kann. Sollte ich dann nicht eine Callback verwenden 
die nur die Adresse ausliest und dann sende ich mit er Funktion z.B. 
HAL_I2C_Slave_Transmit oder  HAL_I2C_Slave_Transmit_IT die Daten an den 
Master? (Die Daten sind immer unterschiedlich und werden vom Master nur 
weitergeleitet)
Angehängte Dateien:
#6441255
Lesenswert?

Hallo Zusammen,

ist zwar schon etwas verstaubt hier, aber ich denke eine Lösung ist für 
den Ein oder Anderen noch interessant:

Um als I2C Slave mit einem stm32 interrupt-basiert zu lesen oder zu 
schreiben stehen folgende Interrupt-Callbacks zur Verfügung:

Wird aufgerufen, wenn eine RX/TX Übertragung beendet ist:
1
void HAL_I2C_SlaveTxCpltCallback(I2C_HandleTypeDef *hi2c);
2
void HAL_I2C_SlaveRxCpltCallback(I2C_HandleTypeDef *hi2c);

Wird aufgerufen, wenn die Adresse empfangen wurde (also nach dem ersten 
Byte):
1
void HAL_I2C_AddrCallback(I2C_HandleTypeDef *hi2c, uint8_t TransferDirection, uint16_t AddrMatchCode);

Wird aufgerufen, nach dem eine Übertragung vollständig abgeschlossen ist 
und das Ende des Listen-Zustands erreicht ist:
1
void HAL_I2C_ListenCpltCallback(I2C_HandleTypeDef *hi2c);

Außerdem benötigt man folgenden Funktionen:

Funktionen um bei der nächsten Möglichkeit zu lesen oder zu senden:
1
HAL_StatusTypeDef HAL_I2C_Slave_Transmit_IT(I2C_HandleTypeDef *hi2c, uint8_t *pData, uint16_t Size);
2
HAL_StatusTypeDef HAL_I2C_Slave_Receive_IT(I2C_HandleTypeDef *hi2c, uint8_t *pData, uint16_t Size);

Funktion um die Interrupts scharf zu schalten:
1
HAL_StatusTypeDef HAL_I2C_EnableListen_IT(I2C_HandleTypeDef *hi2c);

Die obigen Funktionen gibt es dann auch noch als Sequential Variante 
(z.B. HAL_I2C_Slave_Sequential_Transmit_IT) um die Übertragung mehrere 
Nachrichten ohne Masterwechsel zu ermöglichen.

Für das empfangen von Daten bedeutet das, dass folgenden 
Aufrufreihenfolge nötig ist
1. HAL_I2C_EnableListen_IT
2. Der Master sendet die Nachricht
3. HAL_I2C_AddrCallback kommt: Dort HAL_I2C_Slave_Receive_IT aufrufen
4. HAL_I2C_SlaveRxCpltCallback
5. HAL_I2C_ListenCpltCallback

Achtung: Vor jeder Übertragung muss dann wieder HAL_I2C_EnableListen_IT 
aufgerufen werden. Es bietet sich an, das im ListenCpltCallback zu 
machen.

Gruß Tobi
Persönliche Seite #6450145
Lesenswert?

Tobias, vielen Dank! Das hat mir mindestens einen Tag Raten erspart.

Ich möchte noch den Hinweis ergänzen, dass mein STM32L152RE erst dann 
als Slave funktionierte, als ich den Clock Stretching Mode deaktiviert 
habe:
1
hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_ENABLE;
Vorher zog er die ganze Zeit SCL nach unten, und ich wunderte mich, 
warum da kein STOP kommt.

Vielleicht hilft es ja dem einen oder anderen. Mir hat dein Hinweis 
jedenfalls sehr geholfen. Auch wenn der Ursprungsthread uralt ist.

Max
#6450779
Lesenswert?

Max G. schrieb:
> Ich möchte noch den Hinweis ergänzen, dass mein STM32L152RE erst dann
> als Slave funktionierte, als ich den Clock Stretching Mode deaktiviert
> habe:

Dann machst Du was falsch. Dieser Mode ist wichtig, um den Master zu 
synchronisieren. Ansonsten überfährt der Master einfach den Slave und es 
gibt Datensalat.

Max G. schrieb:
> Vorher zog er die ganze Zeit SCL nach unten, und ich wunderte mich,
> warum da kein STOP kommt.

Der zieht nur solange auf Low, bis er den Interrupt bearbeitet und das 
Interruptflag rückgesetzt hat. Vermutlich hast Du das Rücksetzen 
vergessen.
Persönliche Seite #6451474
Lesenswert?

In der Zwischenzeit habe ich daran weitergemacht. Weil ich nur schnell 
eine Lösung brauchte und das von mir sonst verwendete ChibiOS keinen I2C 
Slave unterstützt, habe ich diesmal CubeMX verwendet. Langsam verstehe 
ich, warum das Ding in Verruf gekommen ist. Und das geht schon damit 
los, dass sich der generierte Code erst dann übersetzen lässt, wenn man 
TrueStudio noch mal erzählt hat, welcher Prozessor eigentlich verwendet 
wird. Genau das Gleiche hatte ich vorher schon CubeMX erzählt.

Die Doku, was welche Funktion macht, ist auch eher überschaubar. Manches 
kann man sich ja zusammenreimen, anderes bleibt rätselhaft (was macht 
HAL_I2C_Mem_Read() bzw. ..._IT()?)

Die Idee hinter Clock Stretch ist mir schon klar (den I2C-Treiber der 
Gegenstelle habe ich zu Fuß geschrieben). Ich habe aber den Vorteil in 
diesem Projekt, beide Seiten zu programmieren, kann also dafür sorgen, 
dass Clock Stretch nicht gebraucht wird.

Im Detail hatte und habe ich folgende Probleme:
* Der generierte Code (oder der Treiber?) hat irgendwo in 
I2C_SlaveReceive_RXNE() statt
1
CurrentState != HAL_I2C_STATE_BUSY_TX_LISTEN
ein
1
CurrentState != HAL_I2C_STATE_BUSY_RX_LISTEN
stehen, was zu seltsamen Effekten führt. Hat mich ein paar Stunden 
gekostet.
* Wenn der Master mehr Daten schickt, als der Slave erwartet, wäre eine 
sinnvolle Reaktion ein NACK. Der generierte Code schaltet aber einfach 
in I2C_SlaveReceive_RXNE() den Interrupt stumm und macht gar nix mehr. 
Der Master sieht also ein ACK und schickt fröhlich weiter, es wird aber 
nicht mehr eingelesen. Daraufhin läuft natürlich der Eingangspuffer (DR) 
voll, und der zugehörige Interrupt läuft Amok.
* Wenn man das umbaut, indem statt des zurückgesetzten Interrupts 
einfach das ACK-Bit zurückgesetzt wird, kommt auch wunderbar ein NACK. 
Nur bräuchte ich dann spätestens bei der nächsten START-Condition einen 
Interrupt, um das ACK-Bit wieder zu setzen. Der kommt aber nicht, wenn 
das ACK-Bit zurückgesetzt ist.

Das Ganze ist nur ein Testsystem für ein bestimmtes Produkt und muss nur 
in diesem Kontext funktionieren, insofern ist es mir egal. Aber 
Begeisterung für CubeMX hat diese Orgie bei mir nicht ausgelöst.

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