on
2021-11-16
02:00 AM
- edited on
2024-05-21
07:19 AM
by
Laurids_PETERSE
In this article we will see how to create an I2C target device with interrupts using the STM32CubeMX and STM32Cube HAL Library. All the I2C target communication will be handled by firmware.
Adding an I2C target to an application can be a bit tricky due to the complexity of the I2C protocol. In this article we will show you how to add asynchronous I2C target code using the STM32cubeIDE tool for I2C configuration and then include the necessary code to handle the I2C target asynchronous mode using interrupts. For this article, a Total Phase Aardvark is used as the I2C controller.
The pinout of the Aardvark:
Nucleo-G070RB:
Connections:
The software Control Center from Total Phase will be used to setup and control the master I2C communications with the STM32 I2C target code.
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#define COUNTOF(__BUFFER__) (sizeof(__BUFFER__) / sizeof(*(__BUFFER__)))
/* Size of Transmission buffer */
#define TXBUFFERSIZE (COUNTOF(aTxBuffer))
/* Size of Reception buffer */
#define RXBUFFERSIZE TXBUFFERSIZE
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
__IO uint32_t Transfer_Direction = 0;
__IO uint32_t Xfer_Complete = 0;
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* Buffer used for transmission */
uint8_t aTxBuffer[4];
/* Buffer used for reception */
uint8_t aRxBuffer[4];
/* USER CODE END PD */
/* USER CODE BEGIN Init */
aRxBuffer[0]=0x00;
aRxBuffer[1]=0x00;
aRxBuffer[2]=0x00;
aRxBuffer[3]=0x00;
aTxBuffer[0]=0xAA;
aTxBuffer[1]=0xBB;
aTxBuffer[2]=0xCC;
aTxBuffer[3]=0xDD;
/* USER CODE END Init */
…
/* USER CODE BEGIN 2 */
if(HAL_I2C_EnableListen_IT(&hi2c1) != HAL_OK)
{
/* Transfer error in reception process */
Error_Handler();
}
/* USER CODE END 2 */
/* USER CODE BEGIN WHILE */
while (1)
{
if (Xfer_Complete ==1)
{
HAL_Delay(1);
/*##- Put I2C peripheral in listen mode process ###########################*/
if(HAL_I2C_EnableListen_IT(&hi2c1) != HAL_OK)
{
/* Transfer error in reception process */
Error_Handler();
}
Xfer_Complete =0;
}
/* USER CODE END WHILE */
…
In these functions, the call back functions are added and manage the slave transmit or slave receive part of the code.
/* USER CODE BEGIN 4 */
/**
* @brief Tx Transfer completed callback.
* I2cHandle: I2C handle.
* @note This example shows a simple way to report end of IT Tx transfer, and
* you can add your own implementation.
* @retval None
*/
void HAL_I2C_SlaveTxCpltCallback(I2C_HandleTypeDef *I2cHandle)
{
/* Toggle LED4: Transfer in transmission process is correct */
Xfer_Complete = 1;
aTxBuffer[0]++;
aTxBuffer[1]++;
aTxBuffer[2]++;
aTxBuffer[3]++;
}
/**
* @brief Rx Transfer completed callback.
* I2cHandle: I2C handle
* @note This example shows a simple way to report end of IT Rx transfer, and
* you can add your own implementation.
* @retval None
*/
void HAL_I2C_SlaveRxCpltCallback(I2C_HandleTypeDef *I2cHandle)
{
/* Toggle LED4: Transfer in reception process is correct */
Xfer_Complete = 1;
aRxBuffer[0]=0x00;
aRxBuffer[1]=0x00;
aRxBuffer[2]=0x00;
aRxBuffer[3]=0x00;
}
/**
* @brief Slave Address Match callback.
* hi2c Pointer to a I2C_HandleTypeDef structure that contains
* the configuration information for the specified I2C.
* TransferDirection: Master request Transfer Direction (Write/Read), value of @ref I2C_XferOptions_definition
* AddrMatchCode: Address Match Code
* @retval None
*/
void HAL_I2C_AddrCallback(I2C_HandleTypeDef *hi2c, uint8_t TransferDirection, uint16_t AddrMatchCode)
{
Transfer_Direction = TransferDirection;
if (Transfer_Direction != 0)
{
/*##- Start the transmission process #####################################*/
/* While the I2C in reception process, user can transmit data through
"aTxBuffer" buffer */
if (HAL_I2C_Slave_Seq_Transmit_IT(&hi2c1, (uint8_t *)aTxBuffer, TXBUFFERSIZE, I2C_FIRST_AND_LAST_FRAME) != HAL_OK)
{
/* Transfer error in transmission process */
Error_Handler();
}
}
else
{
/*##- Put I2C peripheral in reception process ###########################*/
if (HAL_I2C_Slave_Seq_Receive_IT(&hi2c1, (uint8_t *)aRxBuffer, RXBUFFERSIZE, I2C_FIRST_AND_LAST_FRAME) != HAL_OK)
{
/* Transfer error in reception process */
Error_Handler();
}
}
}
/**
* @brief Listen Complete callback.
* hi2c Pointer to a I2C_HandleTypeDef structure that contains
* the configuration information for the specified I2C.
* @retval None
*/
void HAL_I2C_ListenCpltCallback(I2C_HandleTypeDef *hi2c)
{
}
/**
* @brief I2C error callbacks.
* I2cHandle: I2C handle
* @note This example shows a simple way to report transfer error, and you can
* add your own implementation.
* @retval None
*/
void HAL_I2C_ErrorCallback(I2C_HandleTypeDef *I2cHandle)
{
/** Error_Handler() function is called when error occurs.
* 1- When Slave doesn't acknowledge its address, Master restarts communication.
* 2- When Master doesn't acknowledge the last data transferred, Slave doesn't care in this example.
*/
if (HAL_I2C_GetError(I2cHandle) != HAL_I2C_ERROR_AF)
{
Error_Handler();
}
}
/* USER CODE END 4 */
Hello, I am woking on STM32G030F6P6 microcontroller.
I am trying to receive data from master using above code (STM32G030F6P6 in slave mode)
HAL_I2C_Slave_Seq_Transmit_IT function is working properly.
But HAL_I2C_Slave_Seq_Receive_IT function not receiving data ( ie. not updating aRxBuffer ) ( program goes into HAL_I2C_AddrCallback routine)
Can some please help with this problem?
I was having the same issue as SMali.4 where it seemed like aRxBuffer was not receiving any data. I was trying to debug print the data in HAL_I2C_AddrCallback(). But I found that it does work correctly if I print the contents of aRxBuffer in HAL_I2C_SlaveRxCpltCallback() instead.
I had an issue that sequential call to i2cdetect -y 1 made I2C SCL line remain low.
This is because HAL_I2C_AddrCallback does not reset Xfer_Complete variable, so main function goes to infinite loop without listening for I2C events.
Issue fixed adding Xfer_Complete = 1; at the end of HAL_I2C_AddrCallback
The Example didn't work properly on my device. After many hours of debugging I found the solution. The Code which is stored within the HAL_I2C_AddrCallback have to be within the main to prevent timing errors.
Within the Private defines I create two "unit_8" which are used as boolean within the following code:
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* Buffer used for transmission */
uint8_t aTxBuffer[3];
/* Buffer used for reception */
uint8_t aRxBuffer[1];
uint8_t getMasterInput = 0;
uint8_t getTransferDirection = 0;
/* USER CODE END PD */
Within the Main my Code is:
/* USER CODE BEGIN WHILE */
while (1) {
if (getMasterInput == 1) {
getMasterInput = 0;
if (getTransferDirection == 0) {
if (HAL_I2C_Slave_Seq_Transmit_IT(&hi2c1, (uint8_t*) aTxBuffer,
TXBUFFERSIZE, I2C_NEXT_FRAME) != HAL_OK)
//if (HAL_I2C_Slave_Transmit(&hi2c1, (uint8_t *)aRxBuffer, TXBUFFERSIZE) != HAL_OK)
// if (HAL_OK != HAL_OK)
{
Error_Handler();
}
} else {
if (HAL_I2C_Slave_Seq_Receive_IT(&hi2c1, (uint8_t*) aRxBuffer,
RXBUFFERSIZE, I2C_NEXT_FRAME) != HAL_OK)
//if (HAL_OK != HAL_OK)
{
Error_Handler();
} else {
HAL_Delay(1 * RXBUFFERSIZE);
}
}
GPIOG->BSRR = DBG_PIN_Pin;
}
HAL_GPIO_WritePin(GPIOG, DBG_PIN_Pin, GPIO_PIN_RESET);
if (Xfer_Complete == 1) {
// HAL_Delay(1);
/*##- Put I2C peripheral in listen mode proces ###########################*/
if (HAL_I2C_EnableListen_IT(&hi2c1) != HAL_OK) {
/* Transfer error in reception process */
Error_Handler();
}
Xfer_Complete = 0;
}
/* USER CODE END WHILE */
Therefor my HAL_I2C_AddrCallback function looks like that:
void HAL_I2C_AddrCallback(I2C_HandleTypeDef *hi2c, uint8_t TransferDirection,
uint16_t AddrMatchCode) {
getMasterInput = 1;
getTransferDirection = TransferDirection;
}
With this changes to the Example from ST my device works properly fine.
Hello Laura, thanks for your comment.
I'm trying to connect an STM32 as a slave in I2C to an RPI.
So I tried to follow the given documentation. When I apply your modifications, I get several errors.
First of all, TransferDirection is not recognized if I put it in the main. It was originally in HAL_I2C_AddrCallback. I didn't quite understand if you were moving the whole function or just its contents.
If it's just the content, then you replace the HAL_I2C_AddrCallback function with the one you propose below after copying the first content into main, is that right?
Then, as for your second piece of code, you say what is in my main is: ...
Do you add that to main, replace it, or put it in /* USER CODE BEGIN WHILE */ instead?
For me, putting it in /* USER CODE BEGIN WHILE */ I get errors for GPIOG->BSRR = DBG_PIN_Pin; with DBG_PIN_Pin and GPIOG not declared.
I thank you a lot in advance for the time you can give me
Thomas
Hello.
Can some one send the full Master code to communicate with the slave ??
Thanks ram