STM32 BootLoader 刷新项目 (五) 获取软件版本号-命令0x51

时间:2024-07-18 08:20:29

STM32 BootLoader 刷新项目 (五) 获取软件版本号-命令0x51

下面我们来讲解第一个指令,获取软件版本号命令-0x51.

在BootLoader中获取软件版本号的操作有多个重要的作用,具体如下:

  1. 版本管理

    • 识别当前版本:通过获取软件版本号,可以识别当前运行的固件版本。这对于调试、维护和升级都是至关重要的。
    • 追踪更新:版本号能够帮助开发团队和用户追踪软件的更新历史,确保每次升级都是有计划和可追溯的。
  2. 兼容性检查

    • 与硬件兼容性:在一些情况下,不同的软件版本可能对硬件有不同的要求。BootLoader可以根据版本号决定是否继续启动或提示用户进行升级。
    • 与其他软件模块兼容性:某些软件模块可能只有在特定版本的固件上才能正常运行。通过检查版本号,BootLoader可以确保所有模块的兼容性。
  3. 固件升级管理

    • 升级条件判断:在执行固件升级时,BootLoader可以根据当前版本号决定是否需要进行升级,或者是否需要强制进行某些重要升级。
    • 回滚机制:如果新的固件版本出现问题,BootLoader可以通过版本号识别和恢复到之前的稳定版本。
  4. 日志记录和故障诊断

    • 问题追踪:在记录系统日志和诊断故障时,版本号是一个关键参考信息。它可以帮助开发者快速定位问题所在版本,进而更有效地进行修复。
    • 客户支持:在提供客户支持时,通过获取软件版本号,可以更准确地了解客户正在使用的版本,从而提供更有针对性的支持和解决方案。
  5. 安全性

    • 固件验证:在某些安全敏感的应用中,BootLoader需要验证固件的完整性和来源。版本号可以作为验证过程的一部分,确保固件未被篡改或替换。
    • 防止回滚攻击:通过检查版本号,可以防止系统被降级到存在已知漏洞的老版本,从而提高系统的整体安全性。

总的来说,BootLoader中获取软件版本号的操作在版本管理、兼容性检查、升级管理、故障诊断和安全性等方面都起着至关重要的作用,是确保嵌入式系统稳定、可靠和安全运行的重要手段。

前面我们讲过了整个BootLoader的方案、架构以及协议,下面我们来具体讲解每一个命令该如何编程。

下面我们来讲解第一个指令,获取软件版本号命令-0x51.

主机发送 命令码 BootLoader回复 备注
BL_GET_VER 0x51 BootLoader版本号(1 byte) 从MCU中读BootLoader的版本号

下面是上位机的命令菜单:

image-20240713104433991

输入1,则通过上位机发送指令:发送的数据为0x05, 0x51, 0xe7, 0xe9, 0xab, 0x7c.

启动第一个字节,为接下来要发送数据的长度,即为5个字节,第二个字节为0x51,即为上面获取软件版本号的指令,后四位为前两位的CRC校验位。

image-20240714162041966

BL_GET_VER指令为获取软件版本号:

image-20240714102542277

下面是用Python写的上位机代码:

def decode_menu_command_code(command):
    ret_value = 0
    data_buf = []
    for i in range(255):
        data_buf.append(0)
    
    if(command  == 0 ):
        print("\n   Exiting...!")
        raise SystemExit
    elif(command == 1):
        print("\n   Command == > BL_GET_VER")
        COMMAND_BL_GET_VER_LEN              = 6
        data_buf[0] = COMMAND_BL_GET_VER_LEN-1 
        data_buf[1] = COMMAND_BL_GET_VER 
        crc32       = get_crc(data_buf,COMMAND_BL_GET_VER_LEN-4)
        crc32 = crc32 & 0xffffffff
        data_buf[2] = word_to_byte(crc32,1,1) 
        data_buf[3] = word_to_byte(crc32,2,1) 
        data_buf[4] = word_to_byte(crc32,3,1) 
        data_buf[5] = word_to_byte(crc32,4,1) 

        
        Write_to_serial_port(data_buf[0],1)
        for i in data_buf[1:COMMAND_BL_GET_VER_LEN]:
            Write_to_serial_port(i,COMMAND_BL_GET_VER_LEN-1)
        

        ret_value = read_bootloader_reply(data_buf[1])

通过读取上位机终端输入的“1”,知道是要获取软件版本号的命令,则按照规定先发送数据长度5,在发送命令指令0x51,在计算前两位的CRC值,总共6byte的数据发送给下位机。下位机收到数据按协议进行相应的解析。

下面通过bootloader_uart_read_data()函数,首先读出上位机发送过来的0x05,表示后续要接收5个字节的数据。后续则按5个字节的数据来接收数据。

之后收到的数据是: 0x51, 0xe7, 0xe9, 0xab, 0x7c.

通过switch语句,判断0x51,则进入bootloader_handle_getver_cmd(bl_rx_buffer),来回去软件版本号的函数。

void  bootloader_uart_read_data(void)
{
    uint8_t rcv_len=0;

    printmsg_Host("BL_DEBUG_MSG: Receive CMD\n\r");
    while (1)
    {
    	memset(bl_rx_buffer, 0, 200);
    	//here we will read and decode the commands coming from host
    	//first read only one byte from the host , which is the "length" field of the command packet
    	HAL_UART_Receive(C_UART,bl_rx_buffer,1,HAL_MAX_DELAY);
    	rcv_len= bl_rx_buffer[0];
    	HAL_UART_Receive(C_UART,&bl_rx_buffer[1],rcv_len,HAL_MAX_DELAY);
    	switch(bl_rx_buffer[1])
    	{
    		case BL_GET_VER:
    			bootloader_handle_getver_cmd(bl_rx_buffer);
    			break;
            default:
            	printmsg("BL_DEBUG_MSG:Invalid command code received from host \n");
            	break;
    	}
    }

}

进入到bootloader_handle_getver_cmd()函数来获取软件版本号,首先计算排除后四位CRC数据前面收到数据的CRC值。如果计算出CRC的值,在和之前上位机发送的CRC,如果正确则进行软件版本号。

/*Helper function to handle BL_GET_VER command */
void bootloader_handle_getver_cmd(uint8_t *bl_rx_buffer)
{
	uint8_t bl_version;

    // 1) verify the checksum
	printmsg("BL_DEBUG_MSG:bootloader_handle_getver_cmd\n");

	//Total length of the command packet
	uint32_t command_packet_len = bl_rx_buffer[0]+1 ;

	//extract the CRC32 sent by the Host
	uint32_t host_crc = *((uint32_t * ) (bl_rx_buffer+command_packet_len - 4) ) ;

    if (! bootloader_verify_crc(&bl_rx_buffer[0],command_packet_len-4,host_crc))
    {
        printmsg("BL_DEBUG_MSG:checksum success !!\n");
        // checksum is correct..
        bootloader_send_ack(bl_rx_buffer[0], 1);
        bl_version = get_bootloader_version();
        printmsg("BL_DEBUG_MSG:BL_VER : %d %#x\n",bl_version,bl_version);
        bootloader_uart_write_data(&bl_version,1);

    }else
    {
        printmsg("BL_DEBUG_MSG:checksum fail !!\n");
        //checksum is wrong send nack
        bootloader_send_nack();
    }
}

下面则通过HAL库中计算CRC的值。

/* This verifies the CRC of the given buffer in pData. */
uint8_t bootloader_verify_crc (uint8_t *pData, uint32_t len, uint32_t crc_host)
{
    uint32_t uwCRCValue = 0xff;

    for (uint32_t i=0 ; i < len ; i++)
	{
        uint32_t i_data = pData[i];
        uwCRCValue = HAL_CRC_Accumulate(&hcrc, &i_data, 1);
	}

	 /* Reset CRC Calculation Unit */
    __HAL_CRC_DR_RESET(&hcrc);

	if( uwCRCValue == crc_host)
	{
		return VERIFY_CRC_SUCCESS;
	}

	return VERIFY_CRC_FAIL;
}

通过下面的函数返回软件版本号。

/* Just returns the macro value. */
uint8_t get_bootloader_version(void)
{
  return (uint8_t)BL_VERSION;
}

如果正确的传输,则发送ACK让上位机接收。

/* This function sends ACK if CRC matches along with "len to follow" */
void bootloader_send_ack(uint8_t command_code, uint8_t follow_len)
{
	 //here we send 2 byte.. first byte is ack and the second byte is len value
	uint8_t ack_buf[2];
	ack_buf[0] = BL_ACK;
	ack_buf[1] = follow_len;
	HAL_UART_Transmit(C_UART, ack_buf,2, HAL_MAX_DELAY);

}

如果错误的话,则传输NACK给上位机传输。

/* This function sends NACK */
void bootloader_send_nack(void)
{
	uint8_t nack = BL_NACK;
	HAL_UART_Transmit(C_UART, &nack, 1, HAL_MAX_DELAY);
}

下面则为执行命令1,0x51命令,则正确返回软件版本号。

image-20240714162041966

至此,获取软件版本号命令0x51的整个流程已经梳理完成。

往期文章请参考:

STM32 BootLoader 刷新项目 (一) STM32CubeMX UART串口通信工程搭建

STM32 BootLoader 刷新项目 (二) 方案介绍

STM32 BootLoader 刷新项目 (三) 程序框架搭建及刷新演示

STM32 BootLoader 刷新项目 (四) 通信协议