2022-01-18 05:06 AM
Hello,
I have a custom STM32F439VITx board running at 60MHz clocked from the HSI. I have three devices on I2C3, two of which function correctly, the third, an mb85rc64 non-volatile memory, fails on both read and write operations.
The I2C is configured like this:
void MX_I2C3_Init(void)
{
hi2c3.Instance = I2C3;
hi2c3.Init.ClockSpeed = 100000;
hi2c3.Init.DutyCycle = I2C_DUTYCYCLE_2;
hi2c3.Init.OwnAddress1 = 0;
hi2c3.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
hi2c3.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
hi2c3.Init.OwnAddress2 = 0;
hi2c3.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
hi2c3.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
if (HAL_I2C_Init(&hi2c3) != HAL_OK)
{
Error_Handler();
}
/** Configure Analogue filter
*/
if (HAL_I2CEx_ConfigAnalogFilter(&hi2c3, I2C_ANALOGFILTER_ENABLE) != HAL_OK)
{
Error_Handler();
}
/** Configure Digital filter
*/
if (HAL_I2CEx_ConfigDigitalFilter(&hi2c3, 0) != HAL_OK)
{
Error_Handler();
}
}
I'm sending the I2C command like this:
static const uint8_t DevAddress = 0b10100000; // preshifted
static const uint16_t MemAddress = 0;
static const uint16_t MemAddSize = I2C_MEMADD_SIZE_16BIT;
static const uint16_t Size = 1;
uint8_t Data[Size];
static const uint32_t Timeout = 1000;
Data[0] = 1;
HAL_StatusTypeDef hal = HAL_I2C_Mem_Write(&hi2c3, DevAddress, MemAddress, MemAddSize, Data, Size, Timeout);
Tracing through the source, HAL_I2C_Mem_Write() gets as far as calling I2C_WaitOnMasterAddressFlagUntilTimeout without any problem:
HAL_StatusTypeDef HAL_I2C_Mem_Write(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint16_t MemAddress, uint16_t MemAddSize, uint8_t *pData, uint16_t Size, uint32_t Timeout)
{
/* Init tickstart for timeout management*/
uint32_t tickstart = HAL_GetTick();
/* Check the parameters */
assert_param(IS_I2C_MEMADD_SIZE(MemAddSize));
if (hi2c->State == HAL_I2C_STATE_READY)
{
/* Wait until BUSY flag is reset */
if (I2C_WaitOnFlagUntilTimeout(hi2c, I2C_FLAG_BUSY, SET, I2C_TIMEOUT_BUSY_FLAG, tickstart) != HAL_OK)
{
return HAL_BUSY;
}
/* Process Locked */
__HAL_LOCK(hi2c);
/* Check if the I2C is already enabled */
if ((hi2c->Instance->CR1 & I2C_CR1_PE) != I2C_CR1_PE)
{
/* Enable I2C peripheral */
__HAL_I2C_ENABLE(hi2c);
}
/* Disable Pos */
CLEAR_BIT(hi2c->Instance->CR1, I2C_CR1_POS);
hi2c->State = HAL_I2C_STATE_BUSY_TX;
hi2c->Mode = HAL_I2C_MODE_MEM;
hi2c->ErrorCode = HAL_I2C_ERROR_NONE;
/* Prepare transfer parameters */
hi2c->pBuffPtr = pData;
hi2c->XferCount = Size;
hi2c->XferSize = hi2c->XferCount;
hi2c->XferOptions = I2C_NO_OPTION_FRAME;
/* Send Slave Address and Memory Address */
if (I2C_RequestMemoryWrite(hi2c, DevAddress, MemAddress, MemAddSize, Timeout, tickstart) != HAL_OK)
I2C_RequestMemoryWrite gets as far as calling I2C_WaitOnMasterAddressFlagUntilTimeout without anything suspsicious looking happening:
static HAL_StatusTypeDef I2C_RequestMemoryWrite(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint16_t MemAddress, uint16_t MemAddSize, uint32_t Timeout, uint32_t Tickstart)
{
/* Generate Start */
SET_BIT(hi2c->Instance->CR1, I2C_CR1_START);
/* Wait until SB flag is set */
if (I2C_WaitOnFlagUntilTimeout(hi2c, I2C_FLAG_SB, RESET, Timeout, Tickstart) != HAL_OK)
{
if (READ_BIT(hi2c->Instance->CR1, I2C_CR1_START) == I2C_CR1_START)
{
hi2c->ErrorCode = HAL_I2C_WRONG_START;
}
return HAL_TIMEOUT;
}
/* Send slave address */
hi2c->Instance->DR = I2C_7BIT_ADD_WRITE(DevAddress);
/* Wait until ADDR flag is set */
if (I2C_WaitOnMasterAddressFlagUntilTimeout(hi2c, I2C_FLAG_ADDR, Timeout, Tickstart) != HAL_OK)
I2C_WaitOnMasterAddressFlagUntilTimeout then immediately enters the if (__HAL_I2C_GET_FLAG(hi2c, I2C_FLAG_AF) == SET) condition and fails.
static HAL_StatusTypeDef I2C_WaitOnMasterAddressFlagUntilTimeout(I2C_HandleTypeDef *hi2c, uint32_t Flag, uint32_t Timeout, uint32_t Tickstart)
{
while (__HAL_I2C_GET_FLAG(hi2c, Flag) == RESET)
{
if (__HAL_I2C_GET_FLAG(hi2c, I2C_FLAG_AF) == SET)
{
/* Generate Stop */
SET_BIT(hi2c->Instance->CR1, I2C_CR1_STOP);
/* Clear AF Flag */
__HAL_I2C_CLEAR_FLAG(hi2c, I2C_FLAG_AF);
hi2c->PreviousState = I2C_STATE_NONE;
hi2c->State = HAL_I2C_STATE_READY;
hi2c->Mode = HAL_I2C_MODE_NONE;
hi2c->ErrorCode |= HAL_I2C_ERROR_AF;
/* Process Unlocked */
__HAL_UNLOCK(hi2c);
return HAL_ERROR;
}
Which looks to me to be a failure to receive the address ACK.
So much for the code. the data sheet tells me that to write 1 to address 0 I should send this:
S10100000A00000000A00000000A00000001AP
Where S and P are the start and stop conditions set by me and A the acknowledge set by the mb85rc64 and with a possibly suspsicious footnote "it is not necessary to take a period for internal write operation cycles from the buffer to the memory after the stop condition is generated." which I'm assuming isn't relevant since I'm not getting as far as actually writing into the memory.
The oscilloscope for the failed transaction looks like this:
The clock signal seems possibly a little grubby, but other than that is there anything obviously causing WaitOnMasterAddressFlag to fail?
Solved! Go to Solution.
2022-01-19 04:14 AM
This is a bit of a shot-in-the dark, but since we're into an electrical problem and given that the chip connections seem fine I was looking through the data sheet:
and it seems like I'm exceeding some of the specs (my rise times Tr on SCL/SDA seem to be on the order of 2uS rather than the 0.3uS required by the spec, the start condition hold seems a bit border-line too).
Is it possible that my pullups are too high resistance and this is causing some sort of problem?
2022-01-19 04:53 AM
I've tried all sorts of frequencies, and even tried blasting through all possible I2C addresses one at a time, and its quite likely that this picture was taken during one of the lower-frequency runs. Nothing I've tried has made any difference so far.
My main worry was that I had somehow messed up the I2C initialisation, although realistically that was quite unlikely since the other two devices on the same bus work fine.
2022-01-19 05:51 AM
2022-01-19 07:04 AM
Seems possible. Using a lower clock rate would fix it, if it's an issue. Note that the 0.3us is for 400 kHz, and you appear to be using 100 kHz. I guess the scope trace isn't perfect.