esp32 JTAG 串口 bootload升级

文章目录

  • 一、前言
  • [二、了解 JTAG 和 Ymodem 的工作原理](#二、了解 JTAG 和 Ymodem 的工作原理)
    • [2.1 环境准备](#2.1 环境准备)
    • [2.2 Ymodem 协议工作原理](#2.2 Ymodem 协议工作原理)
    • [2.3 固件分区准备](#2.3 固件分区准备)
  • 三、关键升级函数
  • [五、使用shell 测试](#五、使用shell 测试)

一、前言

如果使用 JTAG 串口 结合 Ymodem 协议 实现 ESP32 的固件升级,整体逻辑将围绕通过串口传输固件文件并将其烧录到指定的 Flash 分区。以下是完整的实现步骤和代码说明。为什么要使用ymodem ,因为要对文件进行校验,使得数据丢失可以重传,为什么保证固件的完整性,所以使用ymodem协议

二、了解 JTAG 和 Ymodem 的工作原理

2.1 环境准备

JTAG 接口工具:如 FT2232、J-Link、ESP32 自带的 UART-to-USB。

终端工具支持 Ymodem 协议:如 TeraTerm 或 Minicom。

ESP-IDF :开发 ESP32 所需的软件框架

参考上一篇文章 ymodem协议
https://blog.csdn.net/mayuxin1314/article/details/143990976

2.2 Ymodem 协议工作原理

  • 发送端:PC 端通过 Ymodem 协议将固件文件逐块传输到设备。
  • 接收端:ESP32 通过 UART 接口接收 Ymodem 数据并逐块校验。
  • 目标:将接收到的数据写入 OTA 分区。

2.3 固件分区准备

# ESP-IDF Partition Table
# Name,     Type,  SubType, Offset,   Size,      Flags
nvs,        data,  nvs,     0x9000,   200K,     
otadata,    data,  ota,     0x110000, 0x2000,
phy_init,   data,  phy,     0x112000, 0x1000,
factory,    app,   factory, 0x120000, 3M,      
ota_0,      app,   ota_0,   0x420000, 3M,      
ota_1,      app,   ota_1,   0x720000, 3M,      
font_data,  0x50,   0x22,    0xa20000, 2M,       
storage,    data,  littlefs,  0xc20000, 2M,    

三、关键升级函数

c 复制代码
// 定义文件名和文件大小变量

static esp_ota_handle_t ota_handle;
OtaUpdate_t xOtaUpdate_t;
// xLocaUpdate_Begin 函数:初始化 OTA 更新
static enum rym_code xLocaUpdate_Begin(RYM_t *pxRYM_Ag, uint8_t *buf, uint32_t len)
{
    printf("xLocaUpdate_Begin\r\n");
    const esp_partition_t *running_partition = esp_ota_get_running_partition();
    const esp_partition_t *ota_partition = esp_ota_get_next_update_partition(NULL);

    if (ota_partition == NULL)
    {
        ESP_LOGE(TAG, "未找到 OTA 分区");
        return RYM_ERR_CAN;
    }

    ESP_LOGI(TAG, "当前分区地址: 0x%08" PRIx32 ", OTA 分区地址: 0x%08" PRIx32,
             running_partition->address, ota_partition->address);

    parseYModemData(buf,xOtaUpdate_t.ucFilename, &xOtaUpdate_t.ulFilesize);
    ESP_LOGI(TAG, "固件名字 %s 固件内容长度: %" PRIu32 " 字节\r\n",xOtaUpdate_t.ucFilename, xOtaUpdate_t.ulFilesize);

    // 初始化 OTA 会话
    esp_err_t err = esp_ota_begin(ota_partition, xOtaUpdate_t.ulFilesize, &ota_handle);
    if (err != ESP_OK) {
        ESP_LOGE(TAG, "OTA 更新初始化失败: %s", esp_err_to_name(err));
        return RYM_ERR_CAN;
    }

    xOtaUpdate_t.bytes_written = 0; // 初始化已写入字节数
    return RYM_CODE_ACK;
}

// xLocaUpdate_Data 函数:写入固件数据
static enum rym_code xLocaUpdate_Data(RYM_t *pxRYM_Ag, uint8_t *buf, uint32_t len)
{
    // 检查剩余要写入的字节数
    uint32_t remaining_bytes = xOtaUpdate_t.ulFilesize - xOtaUpdate_t.bytes_written;

    // 如果接收的数据超过剩余的字节数,则只写入剩余部分
    if (len > remaining_bytes) {
        len = remaining_bytes;
    }

    // 写入数据到 OTA 分区
    esp_err_t err = esp_ota_write(ota_handle, buf, len);
    if (err != ESP_OK) {
        ESP_LOGE(TAG, "OTA 写入失败: %s", esp_err_to_name(err));
        return RYM_ERR_CAN;
    }

    // 更新已写入的字节数
    xOtaUpdate_t.bytes_written += len;
    ESP_LOGI(TAG, "写入了 %u 字节,总计写入 %u/%u 字节", (unsigned int)len, (unsigned int)xOtaUpdate_t.bytes_written, (unsigned int)xOtaUpdate_t.ulFilesize);

    return RYM_CODE_ACK;
}

// xLocaUpdate_End 函数:完成 OTA 更新
static enum rym_code xLocaUpdate_End(RYM_t *pxRYM_Ag, uint8_t *buf, uint32_t len)
{
    esp_err_t err = esp_ota_end(ota_handle);
    if (err != ESP_OK) {
        ESP_LOGE(TAG, "OTA 更新结束失败: %s", esp_err_to_name(err));
        return RYM_ERR_CAN;
    }
   // 获取当前 OTA 分区的 SHA-256 校验值
    const esp_partition_t *ota_partition = esp_ota_get_next_update_partition(NULL);
    uint8_t sha256_hash[32];
    err = esp_partition_get_sha256(ota_partition, sha256_hash);
    if (err != ESP_OK) {
        ESP_LOGE(TAG, "获取 SHA-256 校验失败: %s", esp_err_to_name(err));
        return RYM_ERR_CAN;
    }

    // 输出 SHA-256 校验结果供验证
    ESP_LOGI(TAG, "OTA 分区 SHA-256 校验值:");
    for (int i = 0; i < 32; i++) {
        printf("%02x", sha256_hash[i]);
    }
    printf("\n");

    // // 将新固件设置为引导分区
    // const esp_partition_t *ota_partition = esp_ota_get_next_update_partition(NULL);
    err = esp_ota_set_boot_partition(ota_partition);
    if (err != ESP_OK) {
        ESP_LOGE(TAG, "设置引导分区失败: %s", esp_err_to_name(err));
        return RYM_ERR_CAN;
    }

    ESP_LOGI(TAG, "OTA 更新完成,设备将使用新固件引导");

    // 触发设备重启以加载新固件
    esp_restart();

    return RYM_CODE_ACK;
}


int ulLocalUpdate()
{
    int err = 0;
    err = RYM_ReadFile(&xRYM_Ag, xLocaUpdate_Begin, xLocaUpdate_Data, xLocaUpdate_End);
    return err;
}

五、使用shell 测试

  • 设置

  • 测试

相关推荐
ThetaarSofVenice6 分钟前
Java从入门到放弃 之 泛型
java·开发语言
嘟嘟Listing14 分钟前
jenkins docker记录
java·运维·jenkins
伴野星辰15 分钟前
小乌龟TortoiseGit 安装和语言包选择
linux·运维·服务器
WHabcwu20 分钟前
统⼀异常处理
java·开发语言
zaim121 分钟前
计算机的错误计算(一百六十三)
java·c++·python·matlab·错数·等价算式
枫叶丹421 分钟前
【在Linux世界中追寻伟大的One Piece】多线程(一)
java·linux·运维
2401_8543910822 分钟前
Spring Boot OA:企业数字化转型的利器
java·spring boot·后端
残念ing23 分钟前
【Linux】—简单实现一个shell(myshell)
linux·运维·服务器
山山而川粤29 分钟前
废品买卖回收管理系统|Java|SSM|Vue| 前后端分离
java·开发语言·后端·学习·mysql
明月心95230 分钟前
linux mount nfs开机自动挂载远程目录
linux·运维·服务器