cancel
Showing results for 
Search instead for 
Did you mean: 

How HAL I2C slave works?

Chuev.Vladimir
Associate III

I'm trying to do I2C Slave but I totally don't understand how to implement it using HAL.

I have a good record of data in Slave.

But but when I use HAL_I2C_Slave_Sequential_Transmit_IT I get a HAL_BUSY error.

What should I do? I looked at the examples but it didn't help me.

enum class State { stop, waiting, getRegisterAddress, getData, sendData };
State transferState = State::stop;
 
uint8 registerAddress = 0;
 
uint8 transmitBuffer = 0;
uint8 receiveBuffer = 0;
 
 
bool(*registerReadEvent)(uint16 address, uint8 &data);
bool(*registerWriteEvent)(uint16 address, uint8 data);
 
 
 
void HAL_I2C_AddrCallback(I2C_HandleTypeDef *hi2c, uint8 direction, uint16 addrMatchCode) { 
	switch (direction) {
		case I2C_DIRECTION_TRANSMIT:
			transferState = State::getRegisterAddress;
			if (HAL_I2C_Slave_Sequential_Receive_IT(hi2c, &receiveBuffer, 1, I2C_FIRST_FRAME) != HAL_OK) {
				Error();
			}
		break;
 
		case I2C_DIRECTION_RECEIVE:
			transferState = State::sendData;
 
			if (!registerReadEvent(registerAddress, transmitBuffer)) {
				Error();
			}
 
			if (HAL_I2C_Slave_Sequential_Transmit_IT(hi2c, &transmitBuffer, 1, I2C_LAST_FRAME) != HAL_OK) {
				// Error here!!! (HAL_BUSY)
				Error();
			}
		break;
	}
}
 
 
 
void HAL_I2C_SlaveRxCpltCallback(I2C_HandleTypeDef *hi2c) {
	switch (transferState) {
		case State::getRegisterAddress:
			registerAddress = receiveBuffer;
			transferState = State::getData;
			if (HAL_I2C_Slave_Sequential_Receive_IT(hi2c, &receiveBuffer, 1, I2C_FIRST_FRAME) != HAL_OK) {
				Error();
			}
		break;
 
		case State::getData:
			if (!registerWriteEvent(registerAddress, receiveBuffer)) {
				Error();
			}
 
			if (HAL_I2C_Slave_Sequential_Receive_IT(hi2c, &receiveBuffer, 1, I2C_FIRST_FRAME) != HAL_OK) {
				Error();
			}
		break;
	}
}
 
 
 
void HAL_I2C_ListenCpltCallback(I2C_HandleTypeDef *hi2c) { 
	HAL_I2C_EnableListen_IT(hi2c); // Restart
}
 
 
 
void HAL_I2C_ErrorCallback(I2C_HandleTypeDef *hi2c) {
	if (HAL_I2C_GetError(hi2c) != HAL_I2C_ERROR_AF) {
		Error();
	}
}
 
 
 
 
 
 
 
void main() {
	static uint8 memoryArray[0xFF] = { 0 };
 
	registerReadEvent = [](uint16 address, uint8 &data) {
		data = memoryArray[address];
		return true;
	};
 
	registerWriteEvent = [](uint16 address, uint8 data) {
		memoryArray[address] = data;
		return true;
	};
 
 
	// Start Listen
	HAL_I2C_EnableListen_IT(&hi2c1);
	transferState = State::waiting;
}

9 REPLIES 9
BWong
Associate

My program is very similar to your ones. I use STM32F071 as a slave.

when the master device wants to read from STM32F071. it seems HAL_I2C_Slave_Sequential_Transmit_IT cannot send out any data at all. For Master device to write to STM32F071, HAL_I2C_Slave_Sequential_Receive_IT works witout issue.

BWong
Associate

Hi Cheuv

have just solved the problem by disabling the NOSTRETCH and ANALOG FLITER bits. And HAL_I2C_Slave_Sequential_Transmit_IT can work perfectly.

DGarn
Associate

This was driving me nuts, as I need NOSTRETCH. Finally solved it by loading the first byte of the slave transmission directly into the register before calling HAL_I2C_Slave_Sequential_Transmit_IT on the second byte of the outgoing buffer and the rest of the message. (Not sure if it matters, but I'm using an STMF301). That register needs to have something in it before SCL falls again after the ADDR is ACKed. (Which doesn't quite match the documentation, but for sure is how it acts.) Anyway, hope this saves someone the headache.

This is madness. Such things should be already inside HAL

Hi benny1

I use STM32F103RCT as slave, there is no problem receive data from master, but when master send "start+address+read bit" to slave, slave can not respond ACK then master stop the I2C BUS. I can not find the ANALOG FILTER bit, Could you please notice where to disable this bit, I want to try.

SJour.1
Associate

I went through all this nonesense, and to be clear about what " loading the first byte of the slave transmission directly into the register" means....

  1. case I2C_DIRECTION_RECEIVE:
  2. transferState = State::sendData;
  3.  
  4. if (!registerReadEvent(registerAddress, transmitBuffer)) {
  5. Error();
  6. }
  7.  

  hi2c->Instance->TXDR = transmitBuffer; // should work without this, but it does not

  1.  
  2. if (HAL_I2C_Slave_Sequential_Transmit_IT(hi2c, &transmitBuffer, 1, I2C_LAST_FRAME) != HAL_OK) {
  3. // Error here!!! (HAL_BUSY)
  4. Error();
  5. }
  6. break;

Hi Liu0155

I'm beginner researching with stm32f103c8t6, could you please help me by sharing references or code for to use STM32F103C8T6 as I2c slave device. I want to use it as I2C slave to ESP32 (which is I2c master)

gary239955
Associate II

I can confirm. No ACK until I set NOSTRETCH and ANFOFF. Then it works perfectly. I'm using ST32L072.

gary239955
Associate II

By the way, don't forget to turn off PE while you set those bits, then turn PE on again afterwards.