2025-03-04 12:08 AM - last edited on 2025-03-04 1:40 AM by mƎALLEm
I could really need some help how to get a STM32F030T4P6 to act as a i2c slave and receive a byte of data
I've spend so much time in the past week trying to solve this problem, but no progress at all
The final code must act as four PCF8574 port expanders and save the received byte in a common array with one index for each of the four PCF8574 addresses.
Why?.... Because it is a very easy way of getting some data from Home Assistant, via EsPHome into the MCU for further usage
I need to use these 4 bytes to create some serial pulse trains thet will control some relay modules that is using a special protocol.
I created a simulator to generate the I2C traffic, and it sends a write to the 4 I2C addresses with a delay of 2 seconds between packages, and then sends a read to the same 4 i2c adresses every 2 seconds, and then it starts over again
Currently my code only listen on one I2C address 0x20, and I can see, with a logic analyzer, that the STM32 sends ack both after an read and a write from master (Simulator), so it seems like that part is ok
But I only get an ack after the data on the first I2C write after a reset from the slave.
But the slave keeps sending ack after the address matches
If I enable clock stretching on the slave, then the slave keeps pulling the clk-line down.
Disabling clock stretching gives the behavior above, where the slave seems to be waiting for something.
Here's my callback I'm using:
(It looks like this type of MCU needs to use a ISR read followed by a read from RXDR to reset the interrupt flag)
void HAL_I2C_SlaveRxCpltCallback(I2C_HandleTypeDef *hi2c)
{
uint32_t devAddr = hi2c->Instance->ISR;
// Extract the matched address (7 bits) from the ISR
devAddr = (devAddr >> 16) & 0x7F; // Shift and mask to get 7-bit address
// Update the global last received address
lastReceivedAddress = (uint8_t)devAddr;
// Read the received byte from the data register
uint8_t receivedByte = hi2c->Instance->RXDR;
// Store the received byte based on the matched address
if (lastReceivedAddress == I2C_ADDRESS_1)
{
dataArray[0] = receivedByte; // Store data for Address 1 (0x20)
}
else if (lastReceivedAddress == I2C_ADDRESS_2)
{
dataArray[1] = receivedByte; // Store data for Address 2 (0x21)
}
else if (lastReceivedAddress == I2C_ADDRESS_3)
{
dataArray[2] = receivedByte; // Store data for Address 3 (0x22)
}
else if (lastReceivedAddress == I2C_ADDRESS_4)
{
dataArray[3] = receivedByte; // Store data for Address 4 (0x23)
}
// Re-enable slave reception for the next byte
HAL_I2C_Slave_Receive_IT(hi2c, (uint8_t *)dataArray, 1);
}
And in main:
// Enable i2c listening for i2c number 1
if(HAL_I2C_EnableListen_IT(&hi2c1) != HAL_OK)
{
/* Transfer error in reception process */
Error_Handler();
}
// Start listening in slave mode for data reception
HAL_I2C_Slave_Receive_IT(&hi2c1, (uint8_t *)dataArray, 1);
I've tried all I can imagine to get this i2c slave code working, like using the sequential Rx callback instead, with all kind of combinations of the FIRST / LAST etc, but I get the same result
What am I missing?
Or have I completely misunderstood how to use I2C in HAL ?
Br Steen
2025-03-04 12:29 AM
Please see How to insert source code.
@SteenA wrote:The final code must act as four PCF8574 port expanders ...
I created a simulator to generate the I2C traffic
Have you tested that your simulator actually works with real PCF8574s ?
2025-03-04 12:43 AM
Have you enabled the I2C interrupts in I2C configuration, NVIC tab?
2025-03-04 1:38 AM
Yes the simulator is working perfect with a real PCF8574, both read and write.
The ports of the PCF8574 are physically correctly set and read
And checking with the logic analyzer the protocol is exactly as expected and as written in the datasheet for the PCF8574
So far it is seems like that the simulator is not the problem
2025-03-04 1:43 AM
After my current knowledge, I will say yes to the interrupt is correctly set in the NVIC
And as written my original post, the address match seems to work correctly and the STM32 slave is sending the correct ack back
But the next byte, the date, seems to get into some wait state after first data byte received.
But still according to the logic analyzer, the STM32 slave keeps responding to address match with an ack, and if I enable clock stretching then the slave keeps pulling the clock down to indicate that the Rx CPLT callback seems not to finish one way or another