Skip to main content
Chuev.Vladimir
Associate III
October 19, 2018
Question

How HAL I2C slave works?

  • October 19, 2018
  • 9 replies
  • 4689 views

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;
}

    This topic has been closed for replies.

    9 replies

    BWong
    Visitor II
    November 19, 2018

    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
    Visitor II
    November 19, 2018

    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.

    Liu0155
    Associate
    October 11, 2019

    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.

    RAnud.1
    Visitor II
    August 9, 2021

    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)

    DGarn
    Visitor II
    June 11, 2019

    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.

    Chuev.Vladimir
    Associate III
    June 11, 2019

    This is madness. Such things should be already inside HAL

    SJour.1
    Visitor II
    February 24, 2021

    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;

    gary239955
    Associate
    October 20, 2022

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

    gary239955
    Associate
    October 20, 2022

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