cancel
Showing results for 
Search instead for 
Did you mean: 

I2C LL

Chuev.Vladimir
Associate III

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

4 REPLIES 4
TDK
Guru

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.

If you feel a post has answered your question, please click "Accept as Solution".
Nikita91
Lead II

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.

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

Study the description and block diagram in a reference manual. HAL code is a broken bloatware, don't waste time on studying it.