cancel
Showing results for 
Search instead for 
Did you mean: 

Program stm32g474re from Bootloader via I2C

AZHUK.1
Associate II

Good afternoon.

I am trying to program STM32G47X through the built-in bootloader using a protocol I2C from another microcontroller.

Document 2606 on page 212 indicates the slave address 0b1010100x. I managed to get through only at 0b1010011x. Is this a bug in the documentation?

Document 4221 also has very little information. I enter bootloader mode by setting boot1 to 1 and releasing the reset.

//i2c 100kHz
 
HAL_StatusTypeDef status;
 
uint8_t tmp[2];
uint8_t dat[21];
uint8_t ACK;
 
	while(HAL_I2C_GetState(hi2c) != HAL_I2C_STATE_READY);
	status = HAL_I2C_Master_Transmit_IT(I2cHandle, 0xa6, dat, 1);
	while (HAL_I2C_GetState(hi2c) != HAL_I2C_STATE_READY) ;
	status = HAL_I2C_Master_Receive_IT(I2cHandle, 0xa7, &ACK, 1);
while(1)
	{
		tmp[0] = 1;
		tmp[1] = 0xfe;//XOR tmp[0]
		while (HAL_I2C_GetState(hi2c) != HAL_I2C_STATE_READY) ;
		status = HAL_I2C_Master_Transmit_IT(I2cHandle, 0xa6, tmp, 2);
		memset(dat, 0, sizeof(dat));
		while (HAL_I2C_GetState(hi2c) != HAL_I2C_STATE_READY) ;
		status = HAL_I2C_Master_Receive_IT(I2cHandle, 0xa7, &ACK, 1);
		if (ACK == 0x79)
		{			
			while (HAL_I2C_GetState(hi2c) != HAL_I2C_STATE_READY) ;
			status = HAL_I2C_Master_Receive_IT(I2cHandle, 0xa7, dat, 2);	
//I accept only zeros!
		}
		else if (ACK == 0x1f)//nack
		{
//Sometimes I get here
		}
		else if(ACK == 0x76)//busy
		{
		}
			else
		{
		}

Often, the slave controller presses the SCL line to the ground. And you have to wait up to 10 seconds.

Maybe someone has an example working?

I don’t understand how to receive data after the response ACK == 0x79

3 REPLIES 3
TDK
Guru

If you want to use I2C in blocking mode, use HAL_I2C_Master_Transmit instead of HAL_I2C_Master_Transmit_IT. The latter transfers asynchronously and you need to wait for completion before accessing the data. Which you do in most places, but not all.

> I don’t understand how to receive data after the response ACK == 0x79

Not sure what you mean. You receive data with HAL_I2C_Master_Receive.

From the app note:

Note: The host frame can be one of the following:

• Send Command frame: The host initiates communication as master transmitter, and sends two bytes to the device: command code + XOR.

• Wait for ACK/NACK frame: The host initiates an I2C communication as master receiver, and receives one byte from the device: ACK or NACK or BUSY.

• Receive Data frame: The host initiates an I2C communication as master receiver, and receives the response from the device. The number of received bytes depends on the command.

• Send Data frame: The host initiates an I2C communication as master transmitter, and sends the needed bytes to the device. The number of transmitted bytes depends on the command.

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

It seems to figure it out. The loader sends data by one byte

if (ACK == 0x79)
		{			
			status = HAL_I2C_Master_Receive_IT(I2cHandle, 0xa6, &dat[0], 1);
//https://www.st.com/resource/en/application_note/dm00072315-i2c-protocol-used-in-the-stm32-bootloader-stmicroelectronics.pdf
//page 11
//Byte 2: Bootloader version (0 < Version ≤ 255) (for example, 0x10 = Version 1.0)
                        while (HAL_I2C_GetState(I2cHandle) != HAL_I2C_STATE_READY) ;
                        status = HAL_I2C_Master_Receive_IT(I2cHandle, 0xa6, &dat[1], 1);	
//Byte 3: ACK
                        while (HAL_I2C_GetState(I2cHandle) != HAL_I2C_STATE_READY) ;
		}

Everything worked. I bring the code.

#define GET_CMD_COMMAND        0x00U  /*!< Get CMD command               */
#define GET_VER_COMMAND        0x01U  /*!< Get Version command           */
#define GET_ID_COMMAND         0x02U  /*!< Get ID command                */
#define RMEM_COMMAND           0x11U  /*!< Read Memory command           */
#define GO_COMMAND             0x21U  /*!< Go command                    */
#define WMEM_COMMAND           0x31U  /*!< Write Memory command          */
#define EMEM_COMMAND           0x44U  /*!< Erase Memory command          */
#define WP_COMMAND             0x63U  /*!< Write Protect command         */
#define WU_COMMAND             0x73U  /*!< Write Unprotect command       */
#define RP_COMMAND             0x82U  /*!< Readout Protect command       */
#define RU_COMMAND             0x92U  /*!< Readout Unprotect command     */
 
I2C_HandleTypeDef *I2cHandle;
 
void BL_Init(I2C_HandleTypeDef *hi2c)
{
	I2cHandle = hi2c;
}
 
HAL_StatusTypeDef BL_Transmit(uint8_t *pDat, uint16_t num)
{
	HAL_StatusTypeDef status;
	while (HAL_I2C_GetState(I2cHandle) != HAL_I2C_STATE_READY) ;
	status = HAL_I2C_Master_Transmit_IT(I2cHandle, 0xa6, pDat, num);
	while (HAL_I2C_GetState(I2cHandle) != HAL_I2C_STATE_READY) ;
	return status;
}
 
HAL_StatusTypeDef BL_Receive(uint8_t *pDat, uint16_t num)
{
	HAL_StatusTypeDef status;
	while (HAL_I2C_GetState(I2cHandle) != HAL_I2C_STATE_READY) ;
	status = HAL_I2C_Master_Receive_IT(I2cHandle, 0xa6, pDat, num);
	while (HAL_I2C_GetState(I2cHandle) != HAL_I2C_STATE_READY) ;
	return status;
}
 
uint8_t BL_GetVersion(void)
{
	HAL_StatusTypeDef status;
	uint8_t ACK;
	uint8_t cmd_frame[2];
	uint8_t version = 0x00U;
 
	cmd_frame[0] = GET_VER_COMMAND;
	cmd_frame[1] = GET_VER_COMMAND ^ 0xFFU;
	status = BL_Transmit(cmd_frame, 2);
	status = BL_Receive(&ACK, 1);
	if (ACK == 0x79)
	{			
		status = BL_Receive(&version, 1);
		status = BL_Receive(&ACK, 1);
		if (ACK == 0x79) return version;
	}
	return 0xff;
}
 
HAL_StatusTypeDef BL_ReadMemory(uint32_t address, uint16_t ***, uint8_t *pData)
{
	HAL_StatusTypeDef status;
	uint8_t ACK;
	uint8_t frame[5];
 
	frame[0] = RMEM_COMMAND;
	frame[1] = RMEM_COMMAND ^ 0xFFU;
	status = BL_Transmit(frame, 2);
	status = BL_Receive(&ACK, 1);
	if (ACK == 0x79)
	{			
		frame[0] = (uint8_t)((address >> 24) & 0xFFU);
		frame[1] = (uint8_t)((address >> 16) & 0xFFU);
		frame[2] = (uint8_t)((address >> 8) & 0xFFU);
		frame[3] = (uint8_t)(address & 0xFFU);
		frame[4] = xor_checksum(frame, 4U);
		status = BL_Transmit(frame, 5);
		ACK = 0;
		status = BL_Receive(&ACK, 1);
		if (ACK == 0x79)
		{
			frame[0] = (*** - 1U);
			frame[1] = (*** - 1U) ^ 0xFFU;
			status = BL_Transmit(frame, 2);
			ACK = 0;
			status = BL_Receive(&ACK, 1);
			if (ACK == 0x79)
			{
				status = BL_Receive(pData, ***);//data+ack
				ACK = 0;
			}
			else return HAL_ERROR;
		}
		else return HAL_ERROR;
	}
	else return HAL_ERROR;
	return HAL_OK;
}
 
HAL_StatusTypeDef BL_WriteMemory(uint32_t address, uint16_t ***, uint8_t *pData)
{
	HAL_StatusTypeDef status;
	uint8_t ACK=0;
	uint8_t frame[258];
	uint8_t checksum = xor_checksum(pData, ***) ^ (*** - 1U );
 
	frame[0] = WMEM_COMMAND;
	frame[1] = WMEM_COMMAND ^ 0xFFU;
	status = BL_Transmit(frame, 2);
	ACK = 0;
	status = BL_Receive(&ACK, 1);
	if (ACK == 0x79)
	{
		frame[0] = ((uint8_t)(address >> 24) & 0xFFU);
		frame[1] = ((uint8_t)(address >> 16) & 0xFFU);
		frame[2] = ((uint8_t)(address >> 8) & 0xFFU);
		frame[3] = ((uint8_t) address & 0xFFU);
		frame[4] = xor_checksum(frame, 4U);
		status = BL_Transmit(frame, 5);
		ACK = 0;
		status = BL_Receive(&ACK, 1);
		if (ACK == 0x79)
		{
			frame[0] = *** - 1U;
			memcpy(&frame[1], pData, ***);
			frame[*** + 1] = checksum;
			status = BL_Transmit(frame, (uint16_t) *** + 2);
			status = BL_Receive(&ACK, 1);
		}
		else return HAL_ERROR;
	}
	else return HAL_ERROR;
	return HAL_OK;
}
 
HAL_StatusTypeDef BL_EraseMemory(uint16_t nb, uint8_t code)
{
	HAL_StatusTypeDef status;
	uint8_t ACK;	
	uint8_t frame[3];
 
	frame[0] = EMEM_COMMAND;
	frame[1] = EMEM_COMMAND ^ 0xFFU;
	status = BL_Transmit(frame, 2);
	status = BL_Receive(&ACK, 1);
	if (ACK == 0x79)
	{
		frame[0] = (uint8_t)(nb >> 8) & 0xFFU;
		frame[1] = (uint8_t) nb & 0xFFU;
		frame[2] = frame[0] ^ frame[1];
		status = BL_Transmit(frame, 3);
		status = BL_Receive(&ACK, 1);
		if (ACK == 0x79)
		{
			if ((nb >> 4) != 0xFFF)
			{
				frame[0] = (uint8_t)(code >> 8) & 0xFFU;
				frame[1] = code & 0xFFU;
				frame[2] = frame[0] ^ frame[1];
				status = BL_Transmit(frame, 3);
				status = BL_Receive(&ACK, 1);
			}
		}
		else return HAL_ERROR;
	}
	else return HAL_ERROR;
	return HAL_OK;
 
}
 
static uint8_t xor_checksum(const uint8_t pData[], uint8_t len)
{
	uint8_t sum = *pData;
	for (uint8_t i = 1U; i < len; i++)
		sum ^= pData[i];
	return sum;
}

don't forget in the cube in i2c to tick off interrupts.

code for testing

uint8_t trsmt[256];
uint8_t recv[257];//add ack
HAL_StatusTypeDef status;
uint8_t tmp;
 
for (uint16_t i = 0; i < 256; i++)
		trsmt[i] = i;
 	BL_Init(&hi2c2);
	
	while (1)
	{
		
		tmp = BL_GetVersion();
		status = BL_EraseMemory(0xffff, 1);
		tmp = 0;
		tmp = BL_GetVersion();
		status = BL_ReadMemory(0x08000000, 256, recv);
		tmp = 0;
		tmp = BL_GetVersion();
		status = BL_WriteMemory(0x08000000, 256, trsmt);
		tmp = 0;
		tmp = BL_GetVersion();
		status = BL_ReadMemory(0x08000000, 256, recv);
	}