ESP32 串口升级
一、开启串口,并配置好数据读写接口
#define RX2_BUF_SIZE (1024)
#define TX2_BUF_SIZE (512)
#define TXD2_PIN (GPIO_NUM_12)
#define RXD2_PIN (GPIO_NUM_13)
#define UART_RTS_PIN (GPIO_NUM_2)int uart_init_115200(void)
{uart_config_t uart_config = {.baud_rate = 115200,.data_bits = UART_DATA_8_BITS,.parity = UART_PARITY_DISABLE,.stop_bits = UART_STOP_BITS_1,.flow_ctrl = UART_HW_FLOWCTRL_DISABLE,.rx_flow_ctrl_thresh = 122,.source_clk = UART_SCLK_APB,};ESP_LOGI("RS485", "Start RS485 application test and configure UART.");// Install UART driver (we don't need an event queue here)// In this example we don't even use a buffer for sending data.ESP_ERROR_CHECK(uart_driver_install(UART_NUM_2, RX2_BUF_SIZE * 2,0, 0, NULL, 0));// Configure UART parametersESP_ERROR_CHECK(uart_param_config(UART_NUM_2, &uart_config));// Set UART pins as per KConfig settingsESP_ERROR_CHECK(uart_set_pin(UART_NUM_2, TXD2_PIN, RXD2_PIN, UART_RTS_PIN, UART_PIN_NO_CHANGE));// Set RS485 half duplex modeESP_ERROR_CHECK(uart_set_mode(UART_NUM_2, UART_MODE_RS485_HALF_DUPLEX));// Set read timeout of UART TOUT featureESP_ERROR_CHECK(uart_set_rx_timeout(UART_NUM_2, 3));return 0;
}int rs485_write(void *src, size_t size)
{
// gpio_set_level(UART_RTS_PIN,1);
// ss_delay_ms(1);int ret = uart_write_bytes(UART_NUM_2,src,size);
// ss_delay_ms( size + 1 );
// gpio_set_level(UART_RTS_PIN,0);return ret;
}int rs485_read(void* buf, uint32_t length)
{
// gpio_set_level(UART_RTS_PIN,0);
// ss_delay_ms(1); int ret = uart_read_bytes(UART_NUM_2,buf, length, 20);
// ss_delay_ms(length + 10 );return ret;
}
二、配置串口升级uart_update.h文件
#ifndef _UART_UPDATE_H_
#define _UART_UPDATE_H_#define PACKET_SEQNO_INDEX (1)
#define PACKET_SEQNO_COMP_INDEX (2)#define PACKET_HEADER (3)
#define PACKET_TRAILER (2)
#define PACKET_OVERHEAD (PACKET_HEADER + PACKET_TRAILER)
#define PACKET_SIZE (128)
#define PACKET_1K_SIZE (1024)#define FILE_NAME_LENGTH (256)
#define FILE_SIZE_LENGTH (16)#define SOH (0x01) /* start of 128-byte data packet */
#define STX (0x02) /* start of 1024-byte data packet */
#define EOT (0x04) /* end of transmission */
#define ACK (0x06) /* acknowledge */
#define NAK (0x15) /* negative acknowledge */
#define CA (0x18) /* two of these in succession aborts transfer */
#define CRC16 (0x43) /* 'C' == 0x43, request 16-bit CRC */#define ABORT1 (0x41) /* 'A' == 0x41, abort by user */
#define ABORT2 (0x61) /* 'a' == 0x61, abort by user */#define NAK_TIMEOUT (0x100000)
#define MAX_ERRORS (5)#define IS_AF(c) ((c >= 'A') && (c <= 'F'))
#define IS_af(c) ((c >= 'a') && (c <= 'f'))
#define IS_09(c) ((c >= '0') && (c <= '9'))
#define ISVALIDHEX(c) IS_AF(c) || IS_af(c) || IS_09(c)
#define ISVALIDDEC(c) IS_09(c)
#define CONVERTDEC(c) (c - '0')
#define CONVERTHEX_alpha(c) (IS_AF(c) ? (c - 'A'+10) : (c - 'a'+10))
#define CONVERTHEX(c) (IS_09(c) ? (c - '0') : CONVERTHEX_alpha(c))extern int Ymodem_Receive ();#define ss_delay_ms(time) vTaskDelay(time / portTICK_PERIOD_MS);#endif /* _UART_UPDATE_H_ */
三、配置串口升级uart_update.c 文件,需要的头文件自己添加,替换#include "include.h"即可
/** ******************************************************************************* @file uart_update.c * @author wst* @version V1.0.0* @date 19-08-2022* @brief Main program body******************************************************************************* * 主要功能是串口升级,需要配合串口使用,与OTA升级流程类似,只是获取的方式不一样**/#include "include.h" unsigned char FileName[200];/*** @brief Send a byte* @param c: Character* @retval 0: Byte sent*/
static unsigned int Send_Byte (unsigned char c)
{ss_rs485_write(&c,1);debug_print("---%d -",c);return 0;
}
static unsigned int Send_2Byte (unsigned char c1,unsigned char c2)
{char c_str[2];c_str[0] = c1;c_str[1] = c2;ss_rs485_write(c_str,2);debug_print("---%02X - %02X----",c_str[0],c_str[1]);return 0;
}/*** @brief Update CRC16 for input byte* @param CRC input value * @param input byte* @retval Updated CRC value*/
unsigned short UpdateCRC16(unsigned short crcIn, unsigned char byte)
{unsigned int crc = crcIn;unsigned int in = byte|0x100;do{crc <<= 1;in <<= 1;if(in&0x100){++crc;}if(crc&0x10000){crc ^= 0x1021;}} while(!(in&0x10000));return (crc&0xffffu);
}/*** @brief Cal CRC16 for YModem Packet* @param data* @param length* @retval CRC value*/
unsigned short Cal_CRC16(const unsigned char* data, unsigned int size)
{unsigned int crc = 0;const unsigned char* dataEnd = data+size;while(data<dataEnd){crc = UpdateCRC16(crc,*data++);}crc = UpdateCRC16(crc,0);crc = UpdateCRC16(crc,0);return (crc&0xffffu);
}/*** @brief Convert a string to an integer* @param inputstr: The string to be converted* @param intnum: The intger value* @retval 1: Correct* 0: Error*/
unsigned int String2Int(unsigned char *inputstr, int *intnum)
{unsigned int i = 0, res = 0;unsigned int val = 0;if (inputstr[0] == '0' && (inputstr[1] == 'x' || inputstr[1] == 'X')){if (inputstr[2] == '\0'){return 0;}for (i = 2; i < 11; i++){if (inputstr[i] == '\0'){*intnum = val;/* return 1; */res = 1;break;}if (ISVALIDHEX(inputstr[i])){val = (val << 4) + CONVERTHEX(inputstr[i]);}else{/* return 0, Invalid input */res = 0;break;}}/* over 8 digit hex --invalid */if (i >= 11){res = 0;}}else /* max 10-digit decimal input */{for (i = 0;i < 11;i++){if (inputstr[i] == '\0'){*intnum = val;/* return 1 */res = 1;break;}else if ((inputstr[i] == 'k' || inputstr[i] == 'K') && (i > 0)){val = val << 10;*intnum = val;res = 1;break;}else if ((inputstr[i] == 'm' || inputstr[i] == 'M') && (i > 0)){val = val << 20;*intnum = val;res = 1;break;}else if (ISVALIDDEC(inputstr[i])){val = val * 10 + CONVERTDEC(inputstr[i]);}else{/* return 0, Invalid input */res = 0;break;}}/* Over 10 digit decimal --invalid */if (i >= 11){res = 0;}}return res;
}/*** @brief Receive a packet from sender* @param data 数据缓存* @param length * @param timeout* * @retval 0: 正常返回* -1: 接收数据头错误* -2:补码、校验错误* -3:发送端中断数据* 4:接收完成* 255: 数据为空*/
static int Receive_Packet (unsigned char *data, int *length, unsigned int timeout)
{unsigned short i, packet_size, computedcrc;char name_dat[200]={0};*length = 0;unsigned char test_num = 0,recv_delay_num=0;char c=0x43;static char first_read_flag=1;if(first_read_flag==1) //第一次,启动下载,获取固件名,大小{ first_read_flag=2; ss_rs485_read(data, 133); //清空串口内部缓存memset(data,0,133);ss_delay_ms(10);while((strstr(name_dat,".bin")==NULL) && (strstr(name_dat,".Bin")==NULL) && (strstr(name_dat,".BIN")==NULL)){ss_rs485_write(&c,1); //开始需要发送 “C” 启动下载ss_delay_ms(80);memset(data,0,134);ss_rs485_read( data, 133);// for(i=0;i<133;i++)// printf("%02x ",data[i]);// printf("\n ");if(data[0] != 0){if((data[0]!=SOH)&&(data[0]!=STX)&&(data[0]!=EOT)&&(data[0]!=CA)&&(data[0]!=ABORT1)&&(data[0]!=ABORT2)) //判断数据头是否正确{return -1;}for(i=0;i<128;i++){name_dat[i] = data[i+3];}printf("---->%s \n",name_dat);}test_num++;if(test_num >=4) //连续 n 次不正确,判断为数据为空 return 255; //不进行升级}} else if(first_read_flag == 2)//第 2--n次,获取固件包{first_read_flag=0; ss_delay_ms(10);Send_2Byte(ACK,CRC16); memset(data,0,PACKET_1K_SIZE + PACKET_OVERHEAD);ss_delay_ms(120);while(ss_rs485_read( data, PACKET_1K_SIZE + PACKET_OVERHEAD)<=0){Send_2Byte(0x00,0x00); //故障情况下使用ss_delay_ms(120);recv_delay_num++;if(recv_delay_num>=3)break;}// for(i=0;i<PACKET_1K_SIZE + PACKET_OVERHEAD;i++)// printf("%02x ",data[i]);// printf("\n ");if((data[0]!=SOH)&&(data[0]!=STX)&&(data[0]!=EOT)&&(data[0]!=CA)&&(data[0]!=ABORT1)&&(data[0]!=ABORT2))//判断数据头错误{return -1; }}else{Send_Byte(ACK);memset(data,0,PACKET_1K_SIZE + PACKET_OVERHEAD);ss_delay_ms(100);ss_rs485_read( data, PACKET_1K_SIZE + PACKET_OVERHEAD);// for(i=0;i<PACKET_1K_SIZE + PACKET_OVERHEAD;i++)// printf("%02x ",data[i]);// printf("\n ");if((data[0]!=SOH)&&(data[0]!=STX)&&(data[0]!=EOT)&&(data[0]!=CA)&&(data[0]!=ABORT1)&&(data[0]!=ABORT2))//判断数据头错误{return -1; } }switch (data[0]){case SOH: //接收 128B 包packet_size = PACKET_SIZE;// //检测序号补码是是否正确if (data[PACKET_SEQNO_INDEX] != ((data[PACKET_SEQNO_COMP_INDEX] ^ 0xff) & 0xff)){return -2; //补码错误}computedcrc = Cal_CRC16(&data[PACKET_HEADER], (unsigned int )packet_size);//计算CRC16码if (computedcrc != (unsigned short)((data[packet_size+3]<<8) | data[packet_size+4]))//检测CRC16校验码{return -2;//校验错误}*length = packet_size;return 0; //成功接收数据case STX: //接收 1KB 包packet_size = PACKET_1K_SIZE;//检测序号补码是是否正确if (data[PACKET_SEQNO_INDEX] != ((data[PACKET_SEQNO_COMP_INDEX] ^ 0xff) & 0xff)){return -2;//补码错误}computedcrc = Cal_CRC16(&data[PACKET_HEADER], (unsigned int )packet_size);//计算CRC16码if (computedcrc != (unsigned short)((data[packet_size+3]<<8) | data[packet_size+4]))//检测CRC16校验码{return -2;//校验错误}*length = packet_size;return 0;case EOT:return 4; //升级完成,结束数据case CA:if ((ss_rs485_read(&c, 1) >= 1) && (c == CA)){*length = -1;return 0;}else{return -2;}case ABORT1:return -3;//中断数据case ABORT2:return -3;//中断数据default: return -1;//数据头错误}
}//异常处理,连接http服务器失败等异常
static void __attribute__((noreturn)) task_fatal_error()
{ESP_LOGE("UART_UPDATE", "Exiting task due to fatal error...");while (1) {ss_delay_ms(1000);}
}/*** @brief Receive a file using the ymodem protocol* @param buf: Address of the first byte* @retval The size of the file* * @retval 0: 正常返回* -1: 接收数据头错误* -2:补码、校验错误* -3:发送端中断数据* 4:接收完成* 255: 数据为空*/int Ymodem_Receive()
{ unsigned char packet_data[PACKET_1K_SIZE + PACKET_OVERHEAD], file_size[FILE_SIZE_LENGTH], *file_ptr, *buf_ptr;unsigned char buf[1024];int i, packet_length, session_done, file_done, packets_received, errors, session_begin, size = 0;esp_err_t err;/* update handle : set by esp_ota_begin(), must be freed via esp_ota_end() */esp_ota_handle_t update_handle = 0 ;const esp_partition_t *update_partition = NULL;const esp_partition_t *configured = esp_ota_get_boot_partition();const esp_partition_t *running = esp_ota_get_running_partition();if (configured != running) {ESP_LOGI("UART_UPDATE", "Configured OTA boot partition at offset 0x%08x, but running from offset 0x%08x",configured->address, running->address);ESP_LOGI("UART_UPDATE", "(This can happen if either the OTA boot data or preferred boot image become corrupted somehow.)");}ESP_LOGI("UART_UPDATE", "Running partition type %d subtype %d (offset 0x%08x)",running->type, running->subtype, running->address);update_partition = esp_ota_get_next_update_partition(NULL);ESP_LOGI("UART_UPDATE", "Writing to partition subtype %d at offset 0x%x",update_partition->subtype, update_partition->address);assert(update_partition != NULL);for (session_done = 0, errors = 0, session_begin = 0; ;)//死循环直至文件数据包全部发送完成{for (packets_received = 0, file_done = 0, buf_ptr = buf; ;){ switch (Receive_Packet(packet_data, &packet_length, NAK_TIMEOUT)){case 0://成功接收到1K{errors = 0;switch (packet_length) // packet_length = 0, -1, 128, 1024{/* Abort by sender */case -1: //接收失败Send_Byte(ACK); //回复return -1;/* End of transmission */case 0:Send_Byte(ACK);//回复file_done = 1;break;/* Normal packet */default: //接收成功if ((packet_data[PACKET_SEQNO_INDEX] & 0xff) != (packets_received & 0xff)){//序号00(文件名)Send_Byte(NAK); }else{if (packets_received == 0)//文件名(首包){/* Filename packet */if (packet_data[PACKET_HEADER] != 0)//文件名字{/* Filename packet has valid data */for (i = 0, file_ptr = packet_data + PACKET_HEADER; (*file_ptr != 0) && (i < FILE_NAME_LENGTH);){FileName[i++] = *file_ptr++;//保存文件名}FileName[i++] = '\0';//字符串形式for (i = 0, file_ptr ++; (*file_ptr != ' ') && (i < (FILE_SIZE_LENGTH - 1));){file_size[i++] = *file_ptr++;//文件大小}file_size[i++] = '\0';String2Int(file_size, &size);//Convert a string to an integerdebug_print("uart update : file name :%s , size :%d \n",FileName,size);/* Test the size of the image to be sent *//* Image size is greater than Flash size */if (size > 0x100000)//1MB{/* End session */Send_2Byte(CA,CA); debug_print("case size:\n");return -1;}err = esp_ota_begin(update_partition, OTA_WITH_SEQUENTIAL_WRITES, &update_handle);if (err != ESP_OK) {ESP_LOGE("UART_UPDATE", "esp_ota_begin failed (%s)", esp_err_to_name(err));esp_ota_abort(update_handle);task_fatal_error();}ESP_LOGI("UART_UPDATE", "esp_ota_begin succeeded");
// ss_delay_ms(10);// Send_Byte(ACK);// Send_Byte(CRC16);}/* Filename packet is empty, end session */else{Send_Byte(ACK);file_done = 1;session_done = 1;break;}}/* Data packet */else //文件信息保存完后开始接收数据{ debug_print("esp_ota_write\r\n");memcpy(buf, packet_data + PACKET_HEADER, packet_length);err = esp_ota_write( update_handle, (const void *)buf, packet_length);if (err != ESP_OK) {/* End session */Send_2Byte(CA,CA); esp_ota_abort(update_handle);task_fatal_error();}// else// {// Send_Byte(ACK);// }// binary_file_length += packet_length;// ESP_LOGI("UART_UPDATE", "Written image length %d", binary_file_length); }packets_received ++; }}break;}case -1: //数据头错误Send_2Byte(CA,CA);debug_print("case -1:\n");return -1;case -2://检验、补码错误{ errors ++; if (errors > MAX_ERRORS){Send_2Byte(CA,CA);return -2;}debug_print("------%d\n",errors);Send_Byte(NAK); //发送非应答,让发送端重新发送此包数据 break;} case -3: //中断数据Send_2Byte(CA,CA);debug_print("case -3:\n");return -3;case 4: //完成ss_delay_ms(25);Send_2Byte(ACK,CRC16);ss_delay_ms(60);Send_Byte(ACK);// ss_delay_ms(50);// Send_Byte(ACK);err = esp_ota_end(update_handle);if (err != ESP_OK) {if (err == ESP_ERR_OTA_VALIDATE_FAILED) {ESP_LOGE("UART_UPDATE", "Image validation failed, image is corrupted");}ESP_LOGE("UART_UPDATE", "esp_ota_end failed (%s)!", esp_err_to_name(err));task_fatal_error();}err = esp_ota_set_boot_partition(update_partition);if (err != ESP_OK) {ESP_LOGE("UART_UPDATE", "esp_ota_set_boot_partition failed (%s)!", esp_err_to_name(err));task_fatal_error();}ESP_LOGI("UART_UPDATE", "Prepare to restart system!");esp_restart(); return size;case 255: //不进行升级return 255;}if (file_done != 0){break;}}if (session_done != 0) //文件发送完成{break;}}return (int)size;
}
四、在main函数调用
void app_main(void)
{while (1){ uart_init_115200();//初始化RS485 115200nvs_data_init();Ymodem_Receive();//串口升级ss_delay_ms(10000);}
}
五、使用hypertrm.exe 进行串口下载
配置页面
发送即可
ESP32 串口升级相关推荐
- ESP32串口转WiFi双天线ESP32-S模组
▌ESP32小模块 1.外置管脚功能图 安信可原厂直销 WiFi+蓝牙模块ESP32串口转WiFi双天线ESP32-S模组 2.自动下载电路 参考下载电路分析 ESP8266专题-ESP8266自动下 ...
- 瑞萨单片机iap串口升级boot程序与app程序合并的工程构建-学习记录
MCU型号: R7F0C004 编辑软件:CS+ for CC boot区程序地址分配:0x0000-0x1ffff app区程序地址分配: 0x2000-0xfffff 复制一份常规的app程序,然 ...
- ESP32串口通信 双机串口通信
ESP32串口 ESP32双机串口通信 环顾论坛居然无使用uart进行双击通信的案例或者讲解!!! 还是自己摸索吧!环境依旧是arduino(熟练使用ESP32后必得用vs code,这个IDE居然有 ...
- STM32F103代码远程升级(三)基于YModem协议串口升级程序的实现
文章目录 一.YModem协议简介 二.YModem的数据格式 1.起始帧的数据格式 2.数据帧的数据格式 3.结束帧的数据格式 4.文件传输过程 三.基于Ymodem协议串口升级程序的实现过程 1. ...
- 串口升级华为S2300系列交换机
串口升级华为S2300系列交换机升级固件 华为2300系列交换机,固件版本较旧,没有web支持. 下载好固件.bootrom.web文件,用串口线连接交换机. 1升级bootrom,交换机上电启 ...
- 杰理之测试盒对耳串口升级功能【篇】
使用条件1.测试盒硬件版本为v2.2以上,且固件版本为v2.1.7以上,可以支持对耳测试接口.2.对耳LDO引脚支持串口通讯(与串口IO合绑)且LDOIN电容需小于103的样机,可以通过测试盒串口转接 ...
- ESP32 OTA升级之HTTP OTA
ESP32 OTA升级之 HTTP OTA 文章目录 ESP32 OTA升级之 HTTP OTA 1. 前言 2. 搭建http本地服务器 2. HTTP OTA 3. 补充学习 1. 前言 在所有电 ...
- ESP32 OTA升级框架
ESP32 SPI Flash 内有与升级相关的(至少)四个分区:OTA data.Factory App.OTA_0.OTA_1.其中 FactoryApp 内存有出厂时的默认固件. 首次进行 OT ...
- ESP32串口API
翻自:https://docs.espressif.com/projects/esp-idf/zh_CN/latest/api-reference/peripherals/uart.html UART ...
最新文章
- excel单元格斜线_如何用EXCEL做一套田字格模板?在家给小孩练习写字
- 【Android RTMP】RTMP 直播推流服务器搭建 ( Ubuntu 18.04.4 虚拟机 )
- Dispatch Queue 之 Invoke 当前队列
- Android性能优化-App后台优化
- 委托、Lambda表达式、事件系列06,使用Action实现观察者模式,体验委托和事件的区别...
- 明天开始放假了[2.5-2.13],春节期间计划
- 【算法学习】将MSRCR中的模糊处理由FFT修改为时域纯高斯模糊
- 区块链应用如何实现资金盘分红
- Python中计算二重积分
- 计算机组装实验硬盘分区方法,硬盘怎么分区和格式化 史上最详细的硬盘分区方法大全 (全文)...
- win10用户账户控制怎么设置白名单
- Java基本控制流程的理解之输出三角形和乘法表
- 一个复数可以用实部和虚部两部分组成,a1 = 1.2 + 3.4i,其中1.2是实部,3.4是虚部。定义一个结构体ComplexNumber,包含imaginary和real两个成员变量,能够表示
- Mysql—索引③:优化篇(不仅仅是索引)
- Linux基础入门 -用户与文件操作
- 【ubuntu常规使用】修改分辨率
- HTML基本知识点——图片标记
- IFE_js_task02
- 局域网计算机配置扫描系统,fly42局域网计算机配置检测系统
- CentOS7安装可移植Prometheus+grafana--pushgateway及自定义监控