Forum: Mikrocontroller und Digitale Elektronik STM32L4 I2C slave interrupt transmit


von chrstm (Gast)


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
}

von chrstm (Gast)


Lesenswert?

Nachtrag:
Ich verwende Systemworkbech for STM32.

von pegel (Gast)


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/

von Harry L. (mysth)


Lesenswert?

Schau dir
HAL_I2C_SlaveRxCpltCallback(I2C_HandleTypeDef *hi2c)
an!

von chrstm (Gast)


Lesenswert?

pegel schrieb:
> 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/

Wo finde ich diese Beispiele?

Danke für die schnellen Antworten.

von Harry L. (mysth)


Lesenswert?

chrstm schrieb:
> Wo finde ich diese Beispiele?

Steht doch da:

chrstm schrieb:
>> STM32Cube_FW_L4_V1.12.0/Projects/NUCLEO-L476RG/Examples/I2C/I2C_TwoBoard

Such nach dem Ordner STM32Cube_FW_L4_V1.12.0 auf deiner HD.
Im Normalfall sollte der im workspace liegen.

von Christopher J. (christopher_j23)


Lesenswert?

Im CubeMX unter "Help"->"Updater Settings" findest du den Pfad

von Harry L. (mysth)


Lesenswert?

Christopher J. schrieb:
> Im CubeMX unter "Help"->"Updater Settings" findest du den Pfad

Damit du die Demo-Projekte direkt in deine IDE importieren kannst, 
sollte der Pfad identisch mit deinem workspace-Ordner sein.

Das macht vieles leichter.

: Bearbeitet durch User
von chrstm (Gast)


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

von pegel (Gast)


Lesenswert?

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.

von chrstm (Gast)


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
}

von Christopher J. (christopher_j23)


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.

von chrstm (Gast)


Lesenswert?

Christopher J. schrieb:
> Die Funktion HAL_I2C_SlaveTxCpltCallback wird nur aufgerufen wenn du die
> Interrupt- oder DMA-API für den I2C benutzt.

Benutze ich den Interrupt-I2C dann indem ich die Funktion
1
   HAL_I2C_EnableListen_IT(&hi2c1);
vor der Main-loop aufrufe?

von pegel (Gast)


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.

von chrstm (Gast)


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.

von Harry L. (mysth)


Lesenswert?

Zeig bitte den gesamten Code und nicht aus dem Kontext gerissene 
einzelne Zeilen!

von Harry L. (mysth)


Lesenswert?

chrstm schrieb:
> 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?

Im CubeMX den Interrupt eingeschaltet?

von chrstm (Gast)


Angehängte Dateien:

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.

von chrstm (Gast)


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****/

von chrstm (Gast)


Lesenswert?

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.

Kann mir jemand bitte dafür ein kleines Beispiel geben?

von Harry L. (mysth)


Lesenswert?


von chrstm (Gast)


Angehängte Dateien:

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)

von Kumar (Gast)


Lesenswert?

Hallo chrstm,

ich habe auch die gleiche Probleme.
Hast du schon die Lösung gefunden ?

von Tobias Z. (flntobi)


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

: Bearbeitet durch User
von Max G. (l0wside) Benutzerseite


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

von Peter D. (peda)


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.

von Max G. (l0wside) Benutzerseite


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.

: Bearbeitet durch User
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.