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

This discussion is locked. Please start a new topic to ask your question.
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.