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 测试

  • 设置

  • 测试

相关推荐
报错小能手11 分钟前
linux学习笔记(19)进程间通讯——消息队列
linux·笔记·学习
Haooog31 分钟前
98.验证二叉搜索树(二叉树算法题)
java·数据结构·算法·leetcode·二叉树
武子康32 分钟前
Java-143 深入浅出 MongoDB NoSQL:MongoDB、Redis、HBase、Neo4j应用场景与对比
java·数据库·redis·mongodb·性能优化·nosql·hbase
jackaroo20201 小时前
后端_基于注解实现的请求限流
java
道可到1 小时前
百度面试真题 Java 面试通关笔记 04 |JMM 与 Happens-Before并发正确性的基石(面试可复述版)
java·后端·面试
liujing102329291 小时前
Day14_内核编译&安装
linux
飞快的蜗牛1 小时前
利用linux系统自带的cron 定时备份数据库,不需要写代码了
java·docker
进击的_鹏2 小时前
【Linux】vim的操作大全
linux·编辑器·vim
云动雨颤2 小时前
Linux卡在emergency mode怎么办?xfs_repair 命令轻松解决
linux·运维·服务器
亮子AI2 小时前
【Ubuntu】清理空间的几种方法
linux·运维·ubuntu