Clion开发STM32之OTA升级模块(最新完整版)

news2024/11/26 0:42:01

前言

  1. 程序分为上位机部分、BootLoader、App程序
  2. 上位机程序使用的是C#进行开发,目前只做成控制台部分
  3. 开发环境依然选择Clion
  4. 芯片采用的是stm32f103vet6
  5. 升级模块已和驱动层逻辑进行分离

BootLoader程序

Flash分区定义

头文件

#ifndef STM32F103VET6_PROJECT_APP_FLASH_CONF_H
#define STM32F103VET6_PROJECT_APP_FLASH_CONF_H

#include "stm32f1xx_hal.h"

#define SIZE_8B (8)
#define SIZE_256B (256)
#define SIZE_512B (512)
#define SIZE_1K   (1024)
#define SIZE_2K   (2048)
#define SIZE_3K   (3072)
#define SIZE_4K   (4092)
#define SYS_CONVERT(type, val) ((type)(val))
// 引导程序大小: 10KB
#define BOOT_AREA_SIZE (SIZE_1K*10)
#define BOOT_START_ADDR FLASH_BASE
// APP: 150KB (应用程序)
#define APP_AREA_SIZE (SIZE_1K*150)
#define APP_START_ADDR SYS_CONVERT(uint32_t,BOOT_START_ADDR+BOOT_AREA_SIZE)
// APP UPGRADE: 150KB (应用升级)
#define APP_UPGRADE_AREA_SIZE (SIZE_1K*150)
#define APP_UPGRADE_START_ADDR SYS_CONVERT(uint32_t,APP_START_ADDR+APP_AREA_SIZE)
// 配置区域: 50KB
#define CONF_AREA_SIZE (SIZE_1K*50)
#define CONF_START_ADDR SYS_CONVERT(uint32_t,APP_UPGRADE_START_ADDR+APP_UPGRADE_AREA_SIZE)
// 数据区域: 130KB
#define DATA_AREA_SIZE (SIZE_1K*130)
#define DATA_START_ADDR SYS_CONVERT(uint32_t,CONF_START_ADDR+CONF_AREA_SIZE)
// 引导数据配置信息:
#define BOOT_INF_ADDR SYS_CONVERT(uint32_t,DATA_START_ADDR+DATA_AREA_SIZE)
#endif //STM32F103VET6_PROJECT_APP_FLASH_CONF_H

固件信息读取

注意: 此处操作Flash驱动参考

头文件

#ifndef STM32F103VET6_PROJECT_OTA_BOOT_INFO_H
#define STM32F103VET6_PROJECT_OTA_BOOT_INFO_H

#include "stm32f1xx_hal.h"
// 定义升级标志位,确保唯一性
#define OTA_UPGRADE_FLAG (0x1389)
typedef struct {
    uint8_t app_version[6]; // 应用版本
    uint32_t app_new_version_flag; // 应用新版本标志位
    uint32_t app_upgrade_data_size; // 应用升级数据大小
} APP_Info_t;

void Load_App_Info(APP_Info_t *info);

void Update_App_Info(APP_Info_t *info);
void Jump_To_App(uint32_t run_addr);
void Jump_To_BootLoader(void);
#endif //STM32F103VET6_PROJECT_OTA_BOOT_INFO_H

源文件

#include "ota_boot_info.h"
#include "bsp_flash.h"
#include "app_flash_conf.h"

typedef void (*pFunction)(void);

/**
 * 加载APP信息
 * @param dst
 */
void Load_App_Info(APP_Info_t *info) {
    STMFLASH_Read_Base(BOOT_INF_ADDR, info, sizeof(APP_Info_t));
}

/**
 * 更新APP信息
 * @param dst
 */
void Update_App_Info(APP_Info_t *info) {
    STMFLASH_Write_Base(BOOT_INF_ADDR, info, sizeof(APP_Info_t));
}

void Jump_To_BootLoader(void) {
    __disable_irq();
    uint32_t JumpAddress;
    pFunction Jump_To_Application;
    JumpAddress = *(volatile uint32_t *) (0x00000004);
    Jump_To_Application = (pFunction) JumpAddress;
    __set_MSP(*(volatile uint32_t *) JumpAddress);
    __set_PSP(*(volatile uint32_t *) JumpAddress);
    Jump_To_Application();
}

void Jump_To_App(uint32_t run_addr) {
    __disable_irq();
    pFunction jump_app;
    jump_app = (pFunction) (*(volatile uint32_t *) (run_addr + 0x04));
    __set_MSP(*(volatile uint32_t *) run_addr);
    jump_app();
}

主入口方法逻辑

int main(void) {
    /* USER CODE BEGIN 1 */
    APP_Info_t app_info;
    Load_App_Info(&app_info);
    if (app_info.app_new_version_flag == OTA_UPGRADE_FLAG) {
        app_info.app_new_version_flag = 0x0;
        STMFLASH_Data_Copy(APP_UPGRADE_START_ADDR, APP_START_ADDR, app_info.app_upgrade_data_size);
        Update_App_Info(&app_info);
    }
    Jump_To_App(APP_START_ADDR);
    /* USER CODE END 1 */

    /* MCU Configuration--------------------------------------------------------*/

    /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
    HAL_Init();

    /* USER CODE BEGIN Init */

    /* USER CODE END Init */

    /* Configure the system clock */
    SystemClock_Config();

    /* USER CODE BEGIN SysInit */

    /* USER CODE END SysInit */

    /* Initialize all configured peripherals */
    MX_GPIO_Init();
    /* USER CODE BEGIN 2 */

    /* USER CODE END 2 */

    /* Infinite loop */
    /* USER CODE BEGIN WHILE */
    while (1) {
        /* USER CODE END WHILE */

        /* USER CODE BEGIN 3 */
    }
    /* USER CODE END 3 */
}

链接文件中对FLASH 限制大小(10KB)

链接文件中对FLASH 限制大小(10KB)

App程序

远程升级协议

头文件(upgrade_core.h)

#ifndef STM32_LIB_UPGRADE_CORE_H
#define STM32_LIB_UPGRADE_CORE_H

#include "sys_core.h"


#define OTA_UPGRADE_VERSION_LIN (6) /** @brief 升级版本长度*/

typedef struct {
    uint8_t app_version[OTA_UPGRADE_VERSION_LIN]; // 应用版本
    uint32_t app_new_version_flag; // 应用新版本标志位
    uint32_t app_upgrade_data_size; // 应用升级数据大小
} app_info_t;

/**
 * @brief 升级数据处理
 * @param data 接收到的数据
 * @param len 数据长度
 */
void device_upgrade_handle(u8 *data, u16 len);


extern void upgrade_resp(u8 status_code);

/**
 * 获取当前设备类型
 * @return 设备类型
 */
extern u8 get_cur_device_type(void);

/**
 * 获取当前版本(长度固定为6)
 * @return
 */
extern u8 *get_cur_version(void);

/**
 * 获取升级写入地址
 * @return
 */
extern u32 get_upgrade_write_addr(void);

/**
 * 写入bin 数据
 * @param addr
 * @param data
 * @param len
 */
extern void upgrade_write_bin(u32 addr, u8 *data, u16 len);

/**
 * 写入app 更新信息
 * @param data
 */
extern void upgrade_write_app_info(app_info_t *data);

/**
 *
 * @return 获取升级数据帧大小
 */
extern u32 get_upgrade_data_size(void);

#endif //STM32_LIB_UPGRADE_CORE_H


源文件(upgrade_core.c)

#include "upgrade_core.h"

#define upgrade_assert(condition, dst, resp_code) \
                        do{\
                            if (!(condition)) {\
                            dst = resp_code;\
                            goto end;\
                        }}while(0)
#define upgrade_assert_return(condition, resp_code)\
                        do{                       \
                        if(!(condition)){         \
                        return resp_code;         \
                        }                         \
                        }while(0)
typedef enum {
    UPGRADE_START_PACKET, /**@brief 开始包*/
    UPGRADE_DATA_PACKET, /**@brief 数据包*/
    UPGRADE_END_PACKET, /**@brief 结束包*/
} packet_type;
typedef enum {
    status_ok,/**@brief 正确*/
    packet_type_error,/**@brief 包类型错误*/
    param_error,/**@brief 参数错误*/
    data_len_error,/**@brief 数据长度错误*/
    device_type_error,/**@brief 设备类型错误*/
    upgrade_version_low, /**@brief 升级版本过低*/
    upgrade_packet_no_error,/**@brief 包号错误*/
    upgrade_data_not_complete, /** 升级数据不完整*/
    start_request_not_send, /**@brief 未发送开始请求*/

} resp_status_code;
typedef struct {
    u8 device_type;/** @brief 设备类型*/
    u8 upgrade_version[OTA_UPGRADE_VERSION_LIN]; /** @brief 升级版本*/
    u32 upgrade_file_size;  /** @brief 升级文件大小 */
} upgrade_info_t; /** @brief 升级帧信息*/
typedef struct {
    u8 current_packet_no;   /** @brief 当前包号*/
    u8 total_packet_num;   /** @brief 总包数*/
    u8 *upgrade_data;     /** @brief 升级数据*/
    u16 upgrade_data_size; /** @brief 升级数据大小(字节数)*/
} upgrade_frame_t; /** @brief 升级帧结构体*/


typedef struct {
    u8 start_upgrade_flag;        /** @brief 开始升级标志位*/
    packet_type pkt_type;/** @brief 包类型*/
    upgrade_info_t base_info; /** @brief 基础信息*/
    upgrade_frame_t upgrade_info; /** @brief 升级信息*/
} upgrade_context_t;
static u8 cur_packet_no = 1;     // 当前升级包号
static __IO u32 rec_upgrade_data_size = 0;
static app_info_t app_info;
static upgrade_context_t context; /** @brief 升级上下文*/
/**
 * 数据解析
 * @param data 
 * @param len 
 * @return 
 */
static resp_status_code upgrade_data_parse_handle(u8 *data, u16 len);

/**
 * 数据处理
 * @param ctx 
 * @return 
 */
static resp_status_code upgrade_core_exec(upgrade_context_t *ctx);

// 版本比较
static int version_cmp(const u8 *v1, const u8 *v2);

// 数据转换
static inline u32 u8_to_u32(u8 *data);

void device_upgrade_handle(u8 *data, u16 len) {
    resp_status_code tmp_status;
    resp_status_code resp_status = status_ok;
    // 验证参数是否为空
    upgrade_assert(data != NULL, resp_status, param_error);
    // 数据解析处理
    tmp_status = upgrade_data_parse_handle(data, len);
    // 验证数据解析
    upgrade_assert(resp_status == tmp_status, resp_status, tmp_status);
    // 数据解析处理
    resp_status = upgrade_core_exec(&context);
    end:
    upgrade_resp(resp_status);
}

static resp_status_code upgrade_data_parse_handle(u8 *data, u16 len) {
    uint16_t idx = 0;
    switch (data[idx]) {
        case UPGRADE_START_PACKET: {
            upgrade_assert_return(len == 12, data_len_error); // 验证数据长度
            // 数据解析
            context.pkt_type = data[idx++];   // 包类型
            context.base_info.device_type = data[idx++];// 设备类型
            context.base_info.upgrade_file_size = u8_to_u32(data + idx); // 升级文件大小
            idx += 4;
            memcpy(context.base_info.upgrade_version, data + idx, OTA_UPGRADE_VERSION_LIN);

            break;
        }
        case UPGRADE_DATA_PACKET: {
            upgrade_assert_return(context.start_upgrade_flag, start_request_not_send); // 是否已经初始化
            upgrade_assert_return(len == get_upgrade_data_size() + 3, data_len_error); // 验证数据长度
            context.pkt_type = data[idx++];   // 包类型
            context.upgrade_info.total_packet_num = data[idx++]; // 总包数
            context.upgrade_info.current_packet_no = data[idx++]; // 当前包号
            context.upgrade_info.upgrade_data = data + idx; // 升级数据
            context.upgrade_info.upgrade_data_size = len - idx; // 升级数据大小(数据长度-前缀长度)
            break;
        }
        case UPGRADE_END_PACKET: {
            upgrade_assert_return(context.start_upgrade_flag, start_request_not_send); // 是否已经初始化
            upgrade_assert_return(len == 6, data_len_error); // 验证数据长度
            context.pkt_type = data[idx++];   // 包类型
            context.base_info.device_type = data[idx++];// 设备类型
            context.base_info.upgrade_file_size = u8_to_u32(data + idx); // 升级文件大小
            break;
        }
        default: {
            return packet_type_error;
        }

    }
    return status_ok;
}

/**
 * 核心逻辑执行
 * @param ctx 
 * @return 
 */
static resp_status_code upgrade_core_exec(upgrade_context_t *ctx) {
    switch (ctx->pkt_type) {
        case UPGRADE_START_PACKET: {
            // 解析之后校验
            // 设备类型校验
            upgrade_assert_return(get_cur_device_type() == ctx->base_info.device_type, device_type_error);
            // 版本校验
            upgrade_assert_return(version_cmp(ctx->base_info.upgrade_version, get_cur_version()) > 0,
                                  upgrade_version_low);
            // 开始更新标志位置位
            ctx->start_upgrade_flag = true;
            memset(&app_info, 0, sizeof(app_info_t));
            app_info.app_upgrade_data_size = ctx->base_info.upgrade_file_size;
            cur_packet_no = 1;
            rec_upgrade_data_size = 0;
            break;
        }
        case UPGRADE_DATA_PACKET: {
            // 升级包序号与当前需要的包序号是否对应
            upgrade_assert_return(cur_packet_no == ctx->upgrade_info.current_packet_no, upgrade_packet_no_error);
            // 获取升级数据地址
            u32 upgrade_data_addr = get_upgrade_write_addr();
            upgrade_write_bin(rec_upgrade_data_size + upgrade_data_addr,
                              ctx->upgrade_info.upgrade_data,
                              ctx->upgrade_info.upgrade_data_size);

            rec_upgrade_data_size = rec_upgrade_data_size + ctx->upgrade_info.upgrade_data_size;
            cur_packet_no += 1;
            break;
        }
        case UPGRADE_END_PACKET:
            upgrade_assert_return(app_info.app_upgrade_data_size == context.base_info.upgrade_file_size,
                                  upgrade_data_not_complete);
            memcpy(app_info.app_version, ctx->base_info.upgrade_version, OTA_UPGRADE_VERSION_LIN);
            app_info.app_new_version_flag = true;
            app_info.app_upgrade_data_size = rec_upgrade_data_size;
            upgrade_write_app_info(&app_info);


            rec_upgrade_data_size = 0;
            cur_packet_no = 1;
            break;
    }
    return status_ok;
}

/**
 *
 * @param v1
 * @param v2
 * @return 0 v1=v2 1 v1>v2 -1 v1<v2
 */
static int version_cmp(const u8 *v1, const u8 *v2) {
    for (int i = 0; i < OTA_UPGRADE_VERSION_LIN; ++i) {
        if (v1[i] > v2[i]) {
            return 1;
        } else if (v1[i] < v2[i]) {
            return -1;
        }
    }
    return 0;
}

static inline u32 u8_to_u32(u8 *data) {
    return (uint32_t) (data[0] << 24) + (uint32_t) (data[1] << 16) + (uint32_t) (data[2] << 8) + data[3];
}

升级协议接口实现

/*
 © Copyright (c) [scl]。保留所有权利。
     本文仅供个人学习和研究使用,禁止用于商业用途。

 */

#include "upgrade_core.h"
#include "app_gb_conf.h"
#include "bsp_flash.h"

#define OTA_UPGRADE_FLAG (0x1389)
u8 cur_version[] = {20, 23, 01, 01, 01, 01};

void upgrade_resp(u8 status_code) {
    os_log(&status_code, 1);
}

/**
 * 获取当前设备类型
 * @return 设备类型
 */
u8 get_cur_device_type(void) {
    return 1;
}

/**
 * 获取当前版本(长度固定为6)
 * @return
 */
u8 *get_cur_version(void) {
    return cur_version;
}

/**
 * 获取升级写入地址
 * @return
 */
u32 get_upgrade_write_addr(void) {
    return APP_UPGRADE_START_ADDR;
}

/**
 * 写入bin 数据
 * @param addr
 * @param data
 * @param len
 */
void upgrade_write_bin(u32 addr, u8 *data, u16 len) {
    STMFLASH_Write_Base(addr, data, len);
}

/**
 * 写入app 更新信息
 * @param data
 */
void upgrade_write_app_info(app_info_t *data) {
    data->app_new_version_flag = OTA_UPGRADE_FLAG;
    STMFLASH_Write_Base(BOOT_INF_ADDR, data, sizeof(app_info_t));
}

u32 get_upgrade_data_size(void) {
    return SIZE_1K;
}

测试(此处是模仿RT-Thread的写法)

/*
 © Copyright (c) [scl]。保留所有权利。
     本文仅供个人学习和研究使用,禁止用于商业用途。

 */

#include "app_gb_conf.h"
#include "upgrade_core.h"

static u8 dma_buf[SIZE_2K];

// 执行之前
static void invoke_before(void) {
    // todo 网络配置加载
}

// 执行
static void invoke(void) {
    com_init_set_baud(com1_cnf, 115200);
//    dma_init_set_direction(com1_rx_dma_cnf, DMA_PERIPH_TO_MEMORY);
    COM1_OpenIRQ(0, 0);
    com1_init();
}

// 执行之后
static void invoke_after(void) {
    HAL_UARTEx_ReceiveToIdle_IT(&com1_handle, dma_buf, SIZE_2K);
    os_ps("app upgrade demo123231123\r\n");
}


void RxEventCallback_USART1(uint16_t size) {
    device_upgrade_handle(dma_buf, size);
    HAL_UARTEx_ReceiveToIdle_IT(&com1_handle, dma_buf, SIZE_2K);


}


void ErrorCallback_USART1() {
    os_ps("ErrorCallback_USART1\r\n");
    HAL_UARTEx_ReceiveToIdle_DMA(&com1_handle, dma_buf, SIZE_2K);

}

CMD_EXPORT(app_upgrade, invoke_before, invoke, invoke_after);

上位机程序(采用的C#)

OtaPacketHelper帮助类

using CommonLib.BaseConvert;
using CommonLib.Basic;

namespace ModbusConsole.Upgrade;

/// <summary>
/// 包帮助类
/// </summary>
public static class OtaPacketHelper
{
    /// <summary>
    /// 构建起始包请求
    /// </summary>
    /// <param name="deviceType"></param>
    /// <param name="upgradePacketSize"></param>
    /// <param name="version"></param>
    /// <returns></returns>
    public static byte[] BuildOtaStartPacketRequest(byte deviceType, uint upgradePacketSize, string version)
    {
        var versionData = CommonSoftBasicHelper.HexStringToBytes(version);
        var upgradeByteNum = ByteTransConvertHelper.ConvertToByte(upgradePacketSize);
        var request = new OtaPacket.OtaStartPacketRequest
        {
            DeviceType = deviceType,
            PacketType = (byte)OtaPacket.OtaPacketTypeEnum.StartPacket,
        };
        unsafe
        {
            CommonUnsafeBasicHelper.ArrayToPointer(request.DeviceVersion, versionData,
                OtaPacket.Define.UpgradePacketVersionNum);
            CommonUnsafeBasicHelper.ArrayToPointer(request.UpgradeDataPacketSize, upgradeByteNum,
                upgradeByteNum.Length);

            var pktLen = sizeof(OtaPacket.OtaStartPacketRequest);
            var dstBuf = new byte[pktLen];

            if (CommonUnsafeBasicHelper.PointerToArray(request.buf, dstBuf, dstBuf.Length))
            {
                return dstBuf;
            }
        }

        return Array.Empty<byte>();
    }
    /// <summary>
    /// 构建结束包请求
    /// </summary>
    /// <param name="deviceType"></param>
    /// <param name="upgradePacketSize"></param>
    /// <returns></returns>
    public static byte[] BuildOtaEndPacketRequest(byte deviceType, uint upgradePacketSize)
    {
        var upgradeByteNum = ByteTransConvertHelper.ConvertToByte(upgradePacketSize);
        var request = new OtaPacket.OtaEndPacketRequest
        {
            DeviceType = deviceType,
            PacketType = (byte)OtaPacket.OtaPacketTypeEnum.EndPacket,
        };
        unsafe
        {
            CommonUnsafeBasicHelper.ArrayToPointer(request.UpgradeDataPacketSize, upgradeByteNum,upgradeByteNum.Length);
            var pktLen = sizeof(OtaPacket.OtaEndPacketRequest);
            var dstBuf = new byte[pktLen];

            if (CommonUnsafeBasicHelper.PointerToArray(request.buf, dstBuf, dstBuf.Length))
            {
                return dstBuf;
            }
        }

        return Array.Empty<byte>();
    }
    

    /// <summary>
    /// 构建数据包
    /// </summary>
    /// <param name="curPacketNo"></param>
    /// <param name="totalPacketNo"></param>
    /// <param name="data"></param>
    /// <returns></returns>
    public static byte[] BuildOtaDataPacketRequest(byte curPacketNo, byte totalPacketNo, byte[] data)
    {
        var request = new OtaPacket.OtaDataPacketRequest()
        {
            PacketType = (byte)OtaPacket.OtaPacketTypeEnum.DataPacket,
            NowPackNum = curPacketNo,
            TotalPackNum = totalPacketNo
        };
        unsafe
        {
            CommonUnsafeBasicHelper.ArrayToPointer(request.UpgradeData, data,
                Math.Min(data.Length, OtaPacket.Define.UpgradeDataPacketNum));
            var pktLen = sizeof(OtaPacket.OtaDataPacketRequest);
            var dstBuf = new byte[pktLen];
            if (CommonUnsafeBasicHelper.PointerToArray(request.buf, dstBuf, dstBuf.Length))
            {
                return dstBuf;
            }
        }

        return Array.Empty<byte>();
    }
    
}

OtaPacket包类

using System.Runtime.InteropServices;

namespace ModbusConsole.Upgrade;

/// <summary>
/// 远程升级包
/// </summary>
public class OtaPacket
{
    public class Define
    {
        public const int UpgradeDataPacketNum = 1024;
        public const int UpgradePacketVersionNum = 6;
        public const int UpgradePacketSize = 4;
    }

    public enum OtaPacketTypeEnum : byte
    {
        StartPacket = 0x00,
        DataPacket = 0x01,
        EndPacket = 0x02,
    }

    /// <summary>
    /// OTA 升级信息请求包
    /// </summary>
    [StructLayout(LayoutKind.Explicit, Size = Define.UpgradePacketVersionNum + Define.UpgradePacketSize + 2)]
    public unsafe struct OtaStartPacketRequest
    {
        [FieldOffset(0)] public fixed byte buf[Define.UpgradePacketVersionNum + Define.UpgradePacketSize + 2];

        /// <summary>
        /// 包类型
        /// </summary>
        [FieldOffset(0)] public byte PacketType;

        /// <summary>
        /// 设备类型
        /// </summary>
        [FieldOffset(1)] public byte DeviceType;

        /// <summary>
        /// 升级数据包大小
        /// </summary>
        [FieldOffset(2)] public fixed byte UpgradeDataPacketSize[Define.UpgradePacketSize];

        /// <summary>
        /// 设备版本号
        /// </summary>
        [FieldOffset(2 + Define.UpgradePacketSize)]
        public fixed byte DeviceVersion[Define.UpgradePacketVersionNum];
    }
    /// <summary>
    /// OTA 升级结束包
    /// </summary>
    [StructLayout(LayoutKind.Explicit, Size =  Define.UpgradePacketSize + 2)]
    public unsafe struct OtaEndPacketRequest
    {
        [FieldOffset(0)] public fixed byte buf[ Define.UpgradePacketSize + 2];
        /// <summary>
        /// 包类型
        /// </summary>
        [FieldOffset(0)] public byte PacketType;
        /// <summary>
        /// 设备类型
        /// </summary>
        [FieldOffset(1)] public byte DeviceType;
        /// <summary>
        /// 升级数据包大小
        /// </summary>
        [FieldOffset(2)] public fixed byte UpgradeDataPacketSize[Define.UpgradePacketSize];
    }


    /// <summary>
    /// OTA 数据请求包
    /// </summary>
    [StructLayout(LayoutKind.Explicit, Size = Define.UpgradeDataPacketNum + 3)]
    public unsafe struct OtaDataPacketRequest
    {
        [FieldOffset(0)] public fixed byte buf[Define.UpgradeDataPacketNum + 3];

        /// <summary>
        /// 包类型
        /// </summary>
        [FieldOffset(0)] public byte PacketType;

        [FieldOffset(1)] public byte TotalPackNum;
        [FieldOffset(2)] public byte NowPackNum;
        [FieldOffset(3)] public fixed byte UpgradeData[Define.UpgradeDataPacketNum];
    }

    [StructLayout(LayoutKind.Explicit, Size = 1)]
    public unsafe struct OtaPackResponse
    {
        [FieldOffset(0)] public fixed byte buf[1];

        /// <summary>
        /// 执行结果 0xFE时执行开机成功 0xFD时开启失败 0xFC表示当前已处于开启状态
        /// </summary>
        [FieldOffset(0)] public byte resp_code;
    }
}

主入口函数

using System.IO.Ports;
using CommonLib.Basic;
using Modbus.Device;
using ModbusConsole.Helper;
using ModbusConsole.Upgrade;

namespace ModbusConsole;

public static class Program
{
    public static void Main(string[] args)
    {
        var binFile =
            @"E:\scl\gitee\stm32f103vet6-project\cmake-build-debug\stm32f103vet6-project.bin";
        var binData = CommonFileBasicHelper.GetFileSize(binFile);
        Console.WriteLine($"bin data size: {binData}");
        var packetData = OtaPacketHelper.BuildOtaStartPacketRequest(0x01, (uint)binData, "202301020101");
        Console.WriteLine(packetData.ToHexString());
        var binDataList = CommonFileBasicHelper.LoadBinFile(binFile,1024);
        var client = new SerialPort("COM17",115200);
        client.Open();
        client.Write(packetData, 0, packetData.Length);
        Thread.Sleep(500);
        var status = client.ReadByte();
        Console.WriteLine(status);
        for (var i = 0; i < binDataList.Count; i++)
        {
            var upgradeData =
                OtaPacketHelper.BuildOtaDataPacketRequest((byte)(i + 1), (byte)binDataList.Count, binDataList[i]);
            client.Write(upgradeData, 0, upgradeData.Length);
            var result = client.ReadByte();
            Console.WriteLine($"result: {result:X2}");
            Thread.Sleep(50);
        }

        packetData = OtaPacketHelper.BuildOtaEndPacketRequest(0x1, (uint)binData);
        client.Write(packetData, 0, packetData.Length);
         status = client.ReadByte();
        Console.WriteLine($"result: {status:X2}");
        Console.Read();
    }

  
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/586021.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

图论-图的基本概念与数据结构

图的基本概念 无向图 边是没有方向的&#xff0c;也就是双向的 结点 V { v 1 , v 2 , . . . , v 7 } \mathcal{V} \{ v_1,v_2,...,v_7\} V{v1​,v2​,...,v7​} 边 ε { e 1 , 2 , e 1 , 3 , . . . , e 6 , 7 } \varepsilon \{e_{1,2},e_{1,3},...,e_{6,7}\} ε{e1,2​…

【面试】计算机网络面试题

计算机网络面试题一 简述OSI七层协议 OSI七层协议包括&#xff1a;物理层&#xff0c;数据链路层&#xff0c;网络层&#xff0c;运输层&#xff0c;会话层&#xff0c;表示层&#xff0c; 应用层 简述TCP/IP五层协议 TCP/IP五层协议包括&#xff1a;物理层&#xff0c;数据…

IntelliJ IDEA使用Alibaba Java Coding Guidelines编码规约扫描插件

代码规范和编码规约扫描插件使用 为什么要有代码规范&#xff1f;1.代码规范插件2.idea插件安装3.插件使用介绍编码规约扫描使用编码规约扫描结果 4.扫描结果严重级别BlockerCriticalMajor 5.《阿里巴巴Java开发手册&#xff08;终极版&#xff09;》 为什么要有代码规范&#…

HTTPS协议深入理解

博主简介&#xff1a;想进大厂的打工人博主主页&#xff1a;xyk:所属专栏: JavaEE初阶 目录 文章目录 一、HTTPS协议的由来及概念 二、加密是什么 三、HTTPS的工作流程 3.1 使用对称密钥 3.2 引入非对称加密 3.3 中间人攻击 3.4 引入证书 一、HTTPS协议的由来及概念 HTTPS 也是…

【chatGPT4结对编程】chatGPT4教我做图像分类

开始接触深度学习 大语言模型火了之后&#xff0c;我也想过是否要加入深度学习的行业当中来&#xff0c;一开始的想法就是AI大模型肯定会被各大厂垄断&#xff0c;我们作为普通应用型软件工程师直接调用api就完事&#xff0c;另外对自己的学历也自卑(刚刚够线的二本&#xff0…

2.4. 封装与访问控制

封装&#xff08;Encapsulation&#xff09;是面向对象编程的一个核心概念&#xff0c;它意味着将数据&#xff08;属性&#xff09;和方法&#xff08;操作数据的函数&#xff09;捆绑在一起&#xff0c;形成一个类&#xff08;Class&#xff09;。封装的目的是将数据和操作数…

C++插件管理类(下)——实际项目(阉割版)

文章目录 一、背景二、代码结构三、两个CMakeLists.txt3.1 父目录3.2 子目录src 四、代码实例4.1 main.cpp4.2 Plugin.h4.3 Plugin.cpp4.4 Comm.h4.5 calc.cpp 五、 踩坑点 一、背景 请参考C插件管理类(上) 二、代码结构 三、两个CMakeLists.txt 3.1 父目录 #设置cmake的最…

stackqueue的模拟实现

stack模拟&#xff1a; stack的源代码&#xff1a; stack的全部源代码就这些。 stack的代码少&#xff0c;原因在于采用了适配器模式&#xff0c;所谓适配器&#xff0c;以电器为例&#xff0c;每个电器都有电源适配器&#xff0c;中国的家用电源为220V的交流电&#xff0c;但是…

3d虚拟主播形象能提升提升企业销售额

随着科技的不断进步和发展&#xff0c;虚拟人形象正在被广泛地应用于商业宣传中。3D虚拟人形象是指采用计算机图形学、人工智能等技术&#xff0c;模拟真实人类形象的虚拟形象。相比于传统产品营销方式&#xff0c;采用3D虚拟人形象进行产品交互讲解对提升企业销售额具有很多优…

JavaWeb12(实现基础分页模糊查询的分页)

目录 一. 效果预览 ​编辑 二. 实现基本分页 2.1 分页sql --每页3条 取第二页 --由于伪列不能作用与大于符号也不能作用于between....and --因此需要将伪列----->名列 2.2 万能公式 2.3 首页&上一页&下一页实现 ②前端代码 2.4 末页实现&优化 ①底层代…

目标检测复盘 -- 6. YOLOv4

Backbone YOLOv4的骨干是CSPDarknet53 CSP结构的作用&#xff1a;1. 增强CNN的学习能力 2. 移出计算瓶颈 3. 减少内存开销 CSP首先将输入的特征层分成两个部分&#xff0c;这里以densenet为例&#xff0c;part2分支经过denseblock后&#xff0c;进过一个transition&#xff0c…

Redis之高可用方案浅析

在工程项目中&#xff0c;系统应用的高可用性越来越重要&#xff0c;业主越来越重视。其实高可用可以分为应用层高可用和数据层高可用&#xff0c;数据层高可用中常见的有关系型数据库mysql的高可用、非关系型NoSQl数据库redis的高可用等&#xff0c;下面聊聊典型的NoSQL数据库…

C# 事件和委托的区别并说明

1.区别 事件是基于委托的&#xff0c;为委托提供了一个发布/订阅机制。可以说事件是一种特殊的委托&#xff0c;他的调用和委托是一样的。 事件的声明 public event 委托类型 事件名称 通常事件的命名以事件名称Event来命名。如public event delegate NotifyEvent; 事件和委…

C++ Primer Plus 第二章习题

目录 复习题 1.C程序的模块叫什么&#xff1f; 2.#include 预处理器编译指令的用处&#xff1f; 3.using namespace std; 该语句是干什么用的&#xff1f; 4.什么语句可以打印一个语句"hello,world"&#xff0c;然后重新换行&#xff1f; 5.什么语句可以用来创…

桂院校园导航 导入 与 配置教程

将 静态项目/云开发项目 文件夹下最新版本的 文件夹下的 项目 的整个文件夹 复制到项目路径下&#xff08;比如 D:\WeChatProjects&#xff09;&#xff0c;强烈建议不要直接扔在桌面上 云开发项目 需开通 云开发 功能&#xff08;首月免费&#xff0c;次月19.9&#xff09;&am…

【论文阅读笔记】CRFL: Certifiably Robust Federated Learning against Backdoor Attacks

个人阅读笔记&#xff0c;如有错误欢迎指出! 会议&#xff1a;PMLR 2021[2106.08283] CRFL: Certifiably Robust Federated Learning against Backdoor Attacks (arxiv.org) 问题&#xff1a; 现有的防御算法缺乏健壮性 创新&#xff1a; 证明了所提出框架得稳定性 通过马尔…

Generative AI 新世界 | 走进文生图(Text-to-Image)领域

在之前的四篇 “Generative AI 新世界” 中&#xff0c;我们带领大家一起探索了生成式 AI&#xff08;Generative AI&#xff09;&#xff0c;以及大型语言模型&#xff08;LLMs&#xff09;的全新世界概览。并在文本生成&#xff08;Text Generation&#xff09;领域做了一些概…

javascript基础十:说说你对Javascript中this对象的理解

一、定义 函数的 this 关键字在 JavaScript 中的表现略有不同&#xff0c;此外&#xff0c;在严格模式和非严格模式之间也会有一些差别 在绝大多数情况下&#xff0c;函数的调用方式决定了 this 的值&#xff08;运行时绑定&#xff09; this 关键字是函数运行时自动生成的一…

进程控制与进程调度 —— 时间片轮转调度算法(C++版)

目录 实验一 进程控制与进程调度 一、实验目的 二、实验内容 三、数据结构及符号说明 四、运行环境说明 五、代码段 六、 效果展示 实验一 进程控制与进程调度 备注&#xff1a;大二&#xff08;下&#xff09;操作系统实验一 一、实验目的 掌握进程状态的转变、…

多次调用java.awt.Toolkit.getDefaultToolkit方法获得是同一个单例的Toolkit

多次调用java.awt.Toolkit.getDefaultToolkit方法获得是同一个单例的Toolkit java.awt.Toolkit.getDefaultToolkit()import java.awt.Toolkit;public class 多次调用Toolkit的getDefaultToolkit方法获得是同一个单例的Toolkit {static public void main (String...arguments)t…