2024-08-14 02:00 PM - edited 2024-08-14 02:05 PM
Hello, I try to transmit data via I2C bus using LL drivers, but it doesn't work properly. No data is transfered properly.
Here is my I2C initialisation generated with CubeMX:
LL_I2C_InitTypeDef I2C_InitStruct = {0};
LL_GPIO_InitTypeDef GPIO_InitStruct = {0};
LL_RCC_SetI2CClockSource(LL_RCC_I2C1_CLKSOURCE_PCLK1);
LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOB);
/**I2C1 GPIO Configuration
PB6 ------> I2C1_SCL
PB7 ------> I2C1_SDA
*/
GPIO_InitStruct.Pin = LL_GPIO_PIN_6|LL_GPIO_PIN_7;
GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_OPENDRAIN;
GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
GPIO_InitStruct.Alternate = LL_GPIO_AF_4;
LL_GPIO_Init(GPIOB, &GPIO_InitStruct);
/* Peripheral clock enable */
LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_I2C1);
/* USER CODE BEGIN I2C1_Init 1 */
/* USER CODE END I2C1_Init 1 */
/** I2C Initialization
*/
LL_I2C_EnableAutoEndMode(I2C1);
LL_I2C_DisableOwnAddress2(I2C1);
LL_I2C_DisableGeneralCall(I2C1);
LL_I2C_EnableClockStretching(I2C1);
I2C_InitStruct.PeripheralMode = LL_I2C_MODE_I2C;
I2C_InitStruct.Timing = 0x00702991;
I2C_InitStruct.AnalogFilter = LL_I2C_ANALOGFILTER_ENABLE;
I2C_InitStruct.DigitalFilter = 0;
I2C_InitStruct.OwnAddress1 = 0;
I2C_InitStruct.TypeAcknowledge = LL_I2C_ACK;
I2C_InitStruct.OwnAddrSize = LL_I2C_OWNADDRESS1_7BIT;
LL_I2C_Init(I2C1, &I2C_InitStruct);
LL_I2C_SetOwnAddress2(I2C1, 0, LL_I2C_OWNADDRESS2_NOMASK);
And here is data transmission, only the beginning, because I can't even transmit an address:
// 1. Set ACK configuration
LL_I2C_AcknowledgeNextData(I2C1, LL_I2C_ACK);
// 2. start condition
LL_I2C_GenerateStartCondition(I2C1);
// 3. check start bit flag
while (!LL_I2C_IsActiveFlag_BUSY(I2C1))
{
osDelay(1);
if (cnt++ > ms) return -1;
}
// 3. write device address (WRITE)
LL_I2C_TransmitData8(I2C1, (slaveAddr << 1) | 0x00);
// 4. wait address sent
while (!LL_I2C_IsActiveFlag_ADDR(I2C1))
{
osDelay(1);
if (cnt++ > ms) return -1;
}
// 5. clear ADDR flag
LL_I2C_ClearFlag_ADDR(I2C1_I2C);
...
It hangs at position 4, because wrong address was transmitted.
Here is a diagram from the logic analyzer:
As you can see, empty byte was transmitted, but slaveAddr was not 0.
I have already checked everything, tried different variants of I2C commands, but without any success.
Does LL actualy work with I2C?
Solved! Go to Solution.
2024-08-15 01:27 AM
Hello @AKise
The slave address cannot be transmitted using the API LL_I2C_TransmitData8.
it is handled using the API LL_I2C_HandleTransfer.
You can refer to the I2C LL example available on Github.
The snipped code below is a simplified function (taken from the mentioned example) that can manage I2C transmit using LL API.
void Handle_I2C_Master(void)
{
/* (1) Initiate a Start condition to the Slave device ***********************/
/* Master Generate Start condition for a write request : */
/* - to the Slave with a 7-Bit SLAVE_OWN_ADDRESS */
/* - with a auto stop condition generation when transmit all bytes */
LL_I2C_HandleTransfer(I2C1, SLAVE_OWN_ADDRESS, LL_I2C_ADDRSLAVE_7BIT, ubNbDataToTransmit, LL_I2C_MODE_AUTOEND, LL_I2C_GENERATE_START_WRITE);
/* (2) Loop until end of transfer received (STOP flag raised) ***************/
/* Loop until STOP flag is raised */
while (!LL_I2C_IsActiveFlag_STOP(I2C1))
{
/* (2.1) Transmit data (TXIS flag raised) *********************************/
/* Check TXIS flag value in ISR register */
if (LL_I2C_IsActiveFlag_TXIS(I2C1))
{
/* Write data in Transmit Data register.
TXIS flag is cleared by writing data in TXDR register */
LL_I2C_TransmitData8(I2C1, (*pTransmitBuffer++));
}
}
/* (3) Clear pending flags, Data consistency are checking into Slave process */
/* End of I2C_SlaveReceiver_MasterTransmitter Process */
LL_I2C_ClearFlag_STOP(I2C1);
}
2024-08-14 03:24 PM - edited 2024-08-14 03:25 PM
> LL_I2C_TransmitData8(I2C1, (slaveAddr << 1) | 0x00)
The slave address is not sent to DR, but to CR2. Look at HAL_I2C_Master_Transmit or the reference manual for inspiration.
Consider using direct register access rather than LL. It needlessly obfuscates the code.
2024-08-15 01:27 AM
Hello @AKise
The slave address cannot be transmitted using the API LL_I2C_TransmitData8.
it is handled using the API LL_I2C_HandleTransfer.
You can refer to the I2C LL example available on Github.
The snipped code below is a simplified function (taken from the mentioned example) that can manage I2C transmit using LL API.
void Handle_I2C_Master(void)
{
/* (1) Initiate a Start condition to the Slave device ***********************/
/* Master Generate Start condition for a write request : */
/* - to the Slave with a 7-Bit SLAVE_OWN_ADDRESS */
/* - with a auto stop condition generation when transmit all bytes */
LL_I2C_HandleTransfer(I2C1, SLAVE_OWN_ADDRESS, LL_I2C_ADDRSLAVE_7BIT, ubNbDataToTransmit, LL_I2C_MODE_AUTOEND, LL_I2C_GENERATE_START_WRITE);
/* (2) Loop until end of transfer received (STOP flag raised) ***************/
/* Loop until STOP flag is raised */
while (!LL_I2C_IsActiveFlag_STOP(I2C1))
{
/* (2.1) Transmit data (TXIS flag raised) *********************************/
/* Check TXIS flag value in ISR register */
if (LL_I2C_IsActiveFlag_TXIS(I2C1))
{
/* Write data in Transmit Data register.
TXIS flag is cleared by writing data in TXDR register */
LL_I2C_TransmitData8(I2C1, (*pTransmitBuffer++));
}
}
/* (3) Clear pending flags, Data consistency are checking into Slave process */
/* End of I2C_SlaveReceiver_MasterTransmitter Process */
LL_I2C_ClearFlag_STOP(I2C1);
}