2020-09-23 07:50 AM
I have some questions about how to start I2C on LL.
Firstly, I rewrote the HAL code in LL and was a little surprised
Why does it take so much code to read the data?
virtual Status::statusType ReadByteArray(uint8* buffer, uint32 size) override {
if (state != Status::ready) {
return Status::busy;
}
state = Status::busy;
rxDataNeed = size;
rxDataPointer = buffer;
rxDataCounter = 0;
auto tickStart = System::GetTick();
while(!LL_I2C_IsActiveFlag_BUSY(i2cHandle)) {
if((System::GetTick() - tickStart) > timeout) {
state = Status::ready;
return Status::timeout;
}
}
LL_I2C_DisableBitPOS(i2cHandle);
LL_I2C_AcknowledgeNextData(i2cHandle, LL_I2C_ACK);
LL_I2C_GenerateStartCondition(i2cHandle);
while(!LL_I2C_IsActiveFlag_SB(i2cHandle)) {
if((System::GetTick() - tickStart) > timeout) {
LL_I2C_GenerateStopCondition(i2cHandle);
state = Status::ready;
return Status::timeout;
}
}
LL_I2C_TransmitData8(i2cHandle, GetReadAddress());
while(!LL_I2C_IsActiveFlag_ADDR(i2cHandle)) {
if((System::GetTick() - tickStart) > timeout) {
LL_I2C_GenerateStopCondition(i2cHandle);
state = Status::ready;
return Status::timeout;
}
}
switch (rxDataCounter < rxDataNeed) {
case 0:
LL_I2C_ClearFlag_ADDR(i2cHandle);
LL_I2C_GenerateStopCondition(i2cHandle);
break;
case 1:
LL_I2C_AcknowledgeNextData(i2cHandle, LL_I2C_NACK);
LL_I2C_ClearFlag_ADDR(i2cHandle);
LL_I2C_GenerateStopCondition(i2cHandle);
break;
case 2:
LL_I2C_AcknowledgeNextData(i2cHandle, LL_I2C_NACK);
LL_I2C_IsEnabledBitPOS(I2C_CR1_POS);
LL_I2C_ClearFlag_ADDR(i2cHandle);
break;
default:
LL_I2C_AcknowledgeNextData(i2cHandle, LL_I2C_ACK);
LL_I2C_ClearFlag_ADDR(i2cHandle);
break;
}
while (rxDataCounter < rxDataNeed) {
if ((rxDataNeed - rxDataCounter) <= 3) {
switch (rxDataNeed - rxDataCounter) {
case 1:
while(!LL_I2C_IsActiveFlag_RXNE(i2cHandle)) {
if((System::GetTick() - tickStart) > timeout) {
LL_I2C_GenerateStopCondition(i2cHandle);
state = Status::ready;
return Status::timeout;
}
}
*rxDataPointer = LL_I2C_ReceiveData8(i2cHandle);
rxDataPointer++;
rxDataCounter++;
break;
case 2:
while(!LL_I2C_IsActiveFlag_BTF(i2cHandle)) {
if((System::GetTick() - tickStart) > timeout) {
LL_I2C_GenerateStopCondition(i2cHandle);
state = Status::ready;
return Status::timeout;
}
}
LL_I2C_GenerateStopCondition(i2cHandle);
*rxDataPointer = LL_I2C_ReceiveData8(i2cHandle);
rxDataPointer++;
*rxDataPointer = LL_I2C_ReceiveData8(i2cHandle);
rxDataPointer++;
rxDataCounter += 2;
break;
default:
while(!LL_I2C_IsActiveFlag_BTF(i2cHandle)) {
if((System::GetTick() - tickStart) > timeout) {
LL_I2C_GenerateStopCondition(i2cHandle);
state = Status::ready;
return Status::timeout;
}
}
LL_I2C_AcknowledgeNextData(i2cHandle, LL_I2C_ACK);
*rxDataPointer = LL_I2C_ReceiveData8(i2cHandle);
rxDataPointer++;
rxDataCounter++;
while(!LL_I2C_IsActiveFlag_BTF(i2cHandle)) {
if((System::GetTick() - tickStart) > timeout) {
LL_I2C_GenerateStopCondition(i2cHandle);
state = Status::ready;
return Status::timeout;
}
}
LL_I2C_GenerateStopCondition(i2cHandle);
*rxDataPointer = LL_I2C_ReceiveData8(i2cHandle);
rxDataPointer++;
*rxDataPointer = LL_I2C_ReceiveData8(i2cHandle);
rxDataPointer++;
rxDataCounter += 2;
break;
}
} else {
while(!LL_I2C_IsActiveFlag_RXNE(i2cHandle)) {
if((System::GetTick() - tickStart) > timeout) {
LL_I2C_GenerateStopCondition(i2cHandle);
state = Status::ready;
return Status::timeout;
}
}
*rxDataPointer = LL_I2C_ReceiveData8(i2cHandle);
rxDataPointer++;
rxDataCounter++;
if (LL_I2C_IsActiveFlag_BTF(i2cHandle)) {
*rxDataPointer = LL_I2C_ReceiveData8(i2cHandle);
rxDataPointer++;
rxDataCounter++;
}
}
}
state = Status::ready;
return Status::ok;
}
Secondly, the amount of HAL code to work with interrupts was too large
I have not been able to port this code. Does anyone have working code to read and write using interrupts?
Unfortunately, the cube examples have code that only reads data
2020-09-23 09:32 AM
Why rewrite HAL if you're just going to do the same thing? I2C is more complicated than UART or SPI because of the two-way nature of the communication and the need to check for ACK/NACK on every byte, and if you want to check timeout conditions.
2020-09-23 02:28 PM
There is a lot of code because this I2C IP is poorly designed, and has special cases for 1 byte, 2 byte, and more than 2 byte messages. There are also places where it is absolutely necessary to inhibit all interruptions to guarantee the timings. I've never seen these complications with other MCU vendors.
2020-09-24 04:15 AM
I was able to rewrite other interfaces with greater efficiency. The code is smaller and faster. But I2C gives me a headache. Due to the fact that I have no idea how it works, I could not optimize the code
2020-10-31 08:41 AM
Study the description and block diagram in a reference manual. HAL code is a broken bloatware, don't waste time on studying it.