代做单片机毕业设计网站技术支持上海网站建设
文章目录
- 一、前言
 - 二、了解 JTAG 和 Ymodem 的工作原理
 - 2.1 环境准备
 - 2.2 Ymodem 协议工作原理
 - 2.3 固件分区准备
 
- 三、关键升级函数
 - 五、使用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,     
三、关键升级函数
// 定义文件名和文件大小变量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 测试
-  
设置

 -  
测试

 
