Y-MODEM协议定制上位机

最近在使用N32G031和STM32F10X系列单片机进行IAP,使用的是Ymodem协议。单片机上的软件已经完成了,一般是使用secureCRT这样的工具作为上位机来进行测试,后来想做一个定制化的简单的上位机。在网上找了下资料,以下这篇文章写的使用C++实现的方式思路非常清晰,值得我好好学习,我也是使用了他的代码进行修改:

C++win32上位机使用Ymodem协议通过串口给单片机在线更新程序 - 阿坦 - 博客园 (cnblogs.com)

为了运行这个C++程序我也是费了很大劲,直接在VS.NET 2010中运行时提示找不到<thread>这个头文件,提示在std::thread t1(receive_thread, &serial)这条语句中thread不是std的成员,后来参照网上的解决办法下载了MinGW-64,设置好了环境变量,将mingw.thread.h等相关头文件拷贝到MinGW-64的解压目录:C:\MinGW-64\lib\gcc\x86_64-w64-mingw32\8.1.0\include\c++,但还是不行,后来使用VSCode新建一个目录.vscode,并在其中添加如下几个json文件后解决了问题,文件中设置和MinGW-64相关目录及VSCode路径请根据自己的实际情况填写,文件配置各个字段的意义有空可以去了解下,但MinGW-64还是要安装

1、launch.json

{// 使用 IntelliSense 了解相关属性。 // 悬停以查看现有属性的描述。// 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387"version": "0.2.0","configurations": [{"name": "g++.exe build and debug active file","type": "cppdbg","request": "launch","program": "${fileDirname}\\${fileBasenameNoExtension}.exe","args": [],"stopAtEntry": false,"cwd": "${workspaceFolder}","environment": [],"externalConsole": true,"MIMode": "gdb","miDebuggerPath": "C:\\MinGW-64\\bin\\gdb.exe",//同理修改为自己的路径"setupCommands": [{"description": "为 gdb 启用整齐打印","text": "-enable-pretty-printing","ignoreFailures": true}],"preLaunchTask": "task g++"}]
}

2、tasks.json

{"version": "2.0.0","tasks": [{"type": "shell",//这里是shell要注意"label": "task g++","command": "C:\\MinGW-64\\bin\\g++.exe",//自路径"args": ["-g","${file}","-o","${fileDirname}\\${fileBasenameNoExtension}.exe","-I","D:\\Program Files\\Microsoft VS Code",//自路径"-std=c++17"],"options": {"cwd": "C:\\MinGW-64\\bin"//自路径},"problemMatcher":["$gcc"],"group": "build"}]
}

3、c_cpp_properties

{"configurations": [{"name": "Win32","includePath": ["${workspaceFolder}/**"],"defines": ["_DEBUG", "UNICODE", "_UNICODE"],"windowsSdkVersion": "10.0.17763.0","compilerPath": "C:\\MinGW-64\\bin\\g++.exe","cStandard": "c11","cppStandard": "c++17","intelliSenseMode": "${default}"}],"version": 4
}

再将主程序添加到VSCode中后就可以正常运行了,main.cpp文件内容如下:

#define _CRT_SECURE_NO_WARNINGS#include <iostream>
#include <windows.h>
#include <string>
#include <thread>//typedef unsigned char uint8_t;
//typedef unsigned int uint16_t;
//typedef unsigned long uint32_t;bool IsStopPrintfReceive = false;class SerialPort {
public:HANDLE hSerial;//构造函数,打开串口并设置参数SerialPort(const char* portName) {std::string fullPortName = "\\\\.\\" + std::string(portName);hSerial = CreateFileA(fullPortName.c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL,OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);if (hSerial == INVALID_HANDLE_VALUE) {std::cerr << "Error opening serial port\n";exit(1);}else{std::cerr << "Opening serial port succeeded!\n";}// 初始化串口参数DCB dcbSerialParams = { 0 };COMMTIMEOUTS timeouts = { 0 };dcbSerialParams.DCBlength = sizeof(dcbSerialParams);if (!GetCommState(hSerial, &dcbSerialParams)) {std::cerr << "Error getting serial port state\n";CloseHandle(hSerial);exit(1);}else{std::cerr << "Getting serial port state succeeded!\n";}// 设置串口参数dcbSerialParams.BaudRate = CBR_115200; // 波特率为115200dcbSerialParams.ByteSize = 8; // 数据位为8位dcbSerialParams.StopBits = ONESTOPBIT; // 停止位为1位dcbSerialParams.Parity = NOPARITY; // 无校验位if (!SetCommState(hSerial, &dcbSerialParams)) {std::cerr << "Error setting serial port state\n";CloseHandle(hSerial);exit(1);}else{std::cerr << "Setting serial port state succeeded!\n";}// 设置超时时间timeouts.ReadIntervalTimeout = 50; // 读取数据之间的间隔时间timeouts.ReadTotalTimeoutConstant = 50; // 读取数据的固定超时时间timeouts.ReadTotalTimeoutMultiplier = 10; // 读取数据的超时时间倍数timeouts.WriteTotalTimeoutConstant = 50; // 写入数据的固定超时时间timeouts.WriteTotalTimeoutMultiplier = 10; // 写入数据的超时时间倍数if (!SetCommTimeouts(hSerial, &timeouts)) {std::cerr << "Error setting serial port timeouts\n";CloseHandle(hSerial);exit(1);}else{std::cerr << "Setting serial port timeout succeeded!\n";}}//析构函数,关闭串口~SerialPort() {CloseHandle(hSerial);}/*** @brief 重置串口超时时间** @param timeout 读写超时时间* @return true 重置成功* @return false 重置失败*/bool resetTimeout(DWORD timeout) {COMMTIMEOUTS timeouts = { 0 };timeouts.ReadIntervalTimeout = 50; // 读取数据之间的间隔时间timeouts.ReadTotalTimeoutConstant = timeout; // 读取数据的固定超时时间timeouts.ReadTotalTimeoutMultiplier = 10; // 读取数据的超时时间倍数timeouts.WriteTotalTimeoutConstant = timeout; // 写入数据的固定超时时间timeouts.WriteTotalTimeoutMultiplier = 10; // 写入数据的超时时间倍数if (!SetCommTimeouts(hSerial, &timeouts)) {std::cerr << "Error setting serial port timeouts\n";CloseHandle(hSerial);exit(1);}return true;}//向串口写入数据bool write(const char* data) {DWORD bytes_written;if (!WriteFile(hSerial, data, strlen(data), &bytes_written, NULL)) {std::cerr << "Error writing to serial port\n";return false;}return true;}bool write(const uint8_t data) {DWORD bytes_written;if (!WriteFile(hSerial, &data, 1, &bytes_written, NULL)) {std::cerr << "Error writing to serial port\n";return false;}return true;}/*** @brief 向串口写入数据** @param data 要写入的数据* @param start_index 数据的起始位置* @param length 数据的长度* @param timeout 写入数据的超时时间* @return true 写入成功* @return false 写入失败*/bool write(const uint8_t* data, uint32_t start_index, uint32_t length, DWORD timeout) {DWORD bytes_written;COMMTIMEOUTS timeouts = { 0 };timeouts.WriteTotalTimeoutConstant = timeout; // 写入数据的固定超时时间timeouts.WriteTotalTimeoutMultiplier = 10; // 写入数据的超时时间倍数if (!SetCommTimeouts(hSerial, &timeouts)) {std::cerr << "Error setting serial port timeouts\n";CloseHandle(hSerial);exit(1);}if (!WriteFile(hSerial, data + start_index, length, &bytes_written, NULL)) {std::cerr << "Error writing to serial port\n";return false;}return true;}//从串口读取数据bool read(char* buffer, DWORD buffer_size, DWORD& bytes_read) {if (!ReadFile(hSerial, buffer, buffer_size, &bytes_read, NULL)) {std::cerr << "Error reading from serial port\n";return false;}return true;}//从串口读取数据bool read(uint8_t* buffer, uint32_t length, DWORD timeout, DWORD& bytes_read) {COMMTIMEOUTS timeouts = { 0 };timeouts.ReadIntervalTimeout = MAXDWORD;timeouts.ReadTotalTimeoutConstant = timeout;timeouts.ReadTotalTimeoutMultiplier = 10;if (!SetCommTimeouts(hSerial, &timeouts)) {std::cerr << "Error setting serial port timeouts\n";CloseHandle(hSerial);exit(1);}if (!ReadFile(hSerial, buffer, length, &bytes_read, NULL)) {std::cerr << "Error reading from serial port\n";return false;}return true;}};/*** @brief  计算10的幂次* @param  x: The integer to be converted* @retval None*/
int mi(int x)	//
{int i=0,ans=1;for(i;i<x;i++){ans=ans*10;}return ans;
}/*** @brief  Convert an Integer to a string  将整数转换为字符串* @param  p_str: The string output pointer   字符串输出指针* @param  intnum: The integer to be converted   要转换的整数* @retval None*/
/*
void Int2Str(uint8_t* p_str, uint32_t intnum) {uint32_t i, divider = 1000000000, pos = 0, status = 0;for (i = 0; i < 10; i++) {p_str[pos++] = (intnum / divider) + 48;intnum = intnum % divider;divider /= 10;if ((p_str[pos - 1] == '0') & (status == 0)) {pos = 0;}else {status++;}}
}*//*** @brief  Convert an Integer to a string  将整数转换为字符串* @param  p_str: The string output pointer   字符串输出指针* @param  intnum: The integer to be converted   要转换的整数* @retval None*/
void Int2Str(uint8_t* p_str, uint32_t intnum) {int n=intnum,count=0;while(intnum!=0)		//求出a的位数count{intnum=intnum/10;count++;}int i=0,j=count;//char b[count];for(i;i<j;i++)		//这里我是正序添加字符的{//b[i]=n/mi(count-1)+'1'-1;p_str[i]=n/mi(count-1)+48;//也就是加上字符'0'的ASCII码值n=n%mi(count-1);count--;} p_str[i]=0;//printf("%s",b);
}/*	另外一种将整型转换为字符串的实现方法
*
*	基本思路是:先不计算整数长度,直接利用整除求余,倒序取出数字,即倒序存入字符数组,最后再将它们逆序
*/
/*** @brief  Convert an Integer to a string  将整数转换为字符串* @param  a: The integer to be converted   要转换的整数* @retval None*/
char* int_to_char(int a)
{char count=0,b[100];while(a!=0)		//逆序存入{b[count]=a%10+'0';//'0'=0x30,也就是0的ASCCI值,如果a/10 = 6,则b[count]当前 = 6 + '0',实际保存的就是6的ASCII码值a=a/10;count++;}char c[100],i,j;i=count-1;j=0;for(j;j<count;j++)	//倒序{c[j]=b[i];i--;}c[j]='\0';  //字符串结束return c;	//指针函数不可以返回局部变量,可以把变量改成静态的或常量,也可以返回堆上的地址(malloc)
}/*** @brief  Update CRC16 for input byte* @param  crc_in input value* @param  input byte* @retval None*/
uint16_t UpdateCRC16(uint16_t crc_in, uint8_t byte) {uint32_t crc = crc_in;uint32_t 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 None*/
uint16_t Cal_CRC16(const uint8_t* p_data, uint32_t size) {uint32_t crc = 0;const uint8_t* dataEnd = p_data + size;while (p_data < dataEnd)crc = UpdateCRC16(crc, *p_data++);crc = UpdateCRC16(crc, 0);crc = UpdateCRC16(crc, 0);return crc & 0xffffu;
}/*** @brief  Comm status structures definition*/
typedef enum {COM_OK = 0x00,COM_ERROR = 0x01,COM_ABORT = 0x02,COM_TIMEOUT = 0x03,COM_DATA = 0x04,COM_LIMIT = 0x05
} COM_StatusTypeDef;/* Packet structure defines */
#define PACKET_HEADER_SIZE      ((uint32_t)3)
#define PACKET_DATA_INDEX       ((uint32_t)4)
#define PACKET_START_INDEX      ((uint32_t)1)
#define PACKET_NUMBER_INDEX     ((uint32_t)2)
#define PACKET_CNUMBER_INDEX    ((uint32_t)3)
#define PACKET_TRAILER_SIZE     ((uint32_t)2)
#define PACKET_OVERHEAD_SIZE    (PACKET_HEADER_SIZE + PACKET_TRAILER_SIZE - 1)
#define PACKET_SIZE             ((uint32_t)128)
#define PACKET_1K_SIZE          ((uint32_t)1024)/* /-------- Packet in IAP memory ------------------------------------------\* | 0      |  1    |  2     |  3   |  4      | ... | n+4     | n+5  | n+6  |* |------------------------------------------------------------------------|* | unused | start | number | !num | data[0] | ... | data[n] | crc0 | crc1 |* \------------------------------------------------------------------------/* the first byte is left unused for memory alignment reasons                 */#define FILE_NAME_LENGTH        ((uint32_t)64)
#define FILE_SIZE_LENGTH        ((uint32_t)16)#define SOH                     ((uint8_t)0x01)  /* start of 128-byte data packet */
#define STX                     ((uint8_t)0x02)  /* start of 1024-byte data packet */
#define EOT                     ((uint8_t)0x04)  /* end of transmission */
#define ACK                     ((uint8_t)0x06)  /* acknowledge */
#define NAK                     ((uint8_t)0x15)  /* negative acknowledge */
#define CA                      ((uint32_t)0x18) /* two of these in succession aborts transfer */
#define CRC16                   ((uint8_t)0x43)  /* 'C' == 0x43, request 16-bit CRC */
#define NEGATIVE_BYTE           ((uint8_t)0xFF)#define ABORT1                  ((uint8_t)0x41)  /* 'A' == 0x41, abort by user */
#define ABORT2                  ((uint8_t)0x61)  /* 'a' == 0x61, abort by user */#define NAK_TIMEOUT             ((uint32_t)0x100000)
#define DOWNLOAD_TIMEOUT        ((uint32_t)1000) /* One second retry delay */
#define MAX_ERRORS              ((uint32_t)5)#define USER_FLASH_SIZE               ((uint32_t)0x00010000) /* Small default template application *//*** @brief  Prepare the first block* @param  p_data:  output buffer* @param  p_file_name: name of the file to be sent* @param  length: length of the file to be sent in bytes* @retval None*/
static void PrepareIntialPacket(uint8_t* p_data, const uint8_t* p_file_name, uint32_t length) {uint32_t i, j = 0;uint8_t astring[10];/* first 3 bytes are constant  */p_data[PACKET_START_INDEX] = SOH;p_data[PACKET_NUMBER_INDEX] = 0x00;p_data[PACKET_CNUMBER_INDEX] = 0xff;/* Filename written  */for (i = 0; (p_file_name[i] != '\0') && (i < FILE_NAME_LENGTH); i++) {p_data[i + PACKET_DATA_INDEX] = p_file_name[i];}p_data[i + PACKET_DATA_INDEX] = 0x00;/* file size written */Int2Str(astring, length);i = i + PACKET_DATA_INDEX + 1;while (astring[j] != '\0') {p_data[i++] = astring[j++];}/* padding with zeros */for (j = i; j < PACKET_SIZE + PACKET_DATA_INDEX; j++) {p_data[j] = 0;}
}/*** @brief  Prepare the data packet* @param  p_source: pointer to the data to be sent* @param  p_packet: pointer to the output buffer* @param  pkt_nr: number of the packet* @param  size_blk: length of the block to be sent in bytes* @retval None*/
static void PreparePacket(uint8_t* p_source, uint8_t* p_packet, uint8_t pkt_nr, uint32_t size_blk) {uint8_t* p_record;uint32_t i, size, packet_size;/* Make first three packet */packet_size = size_blk >= PACKET_1K_SIZE ? PACKET_1K_SIZE : PACKET_SIZE;size = size_blk < packet_size ? size_blk : packet_size;if (packet_size == PACKET_1K_SIZE) {p_packet[PACKET_START_INDEX] = STX;}else {p_packet[PACKET_START_INDEX] = SOH;}p_packet[PACKET_NUMBER_INDEX] = pkt_nr;p_packet[PACKET_CNUMBER_INDEX] = (~pkt_nr);p_record = p_source;/* Filename packet has valid data */for (i = PACKET_DATA_INDEX; i < size + PACKET_DATA_INDEX; i++) {p_packet[i] = *p_record++;}if (size <= packet_size) {for (i = size + PACKET_DATA_INDEX; i < packet_size + PACKET_DATA_INDEX; i++) {p_packet[i] = 0x1A; /* EOF (0x1A) or 0x00 */}}
}/* @note ATTENTION - please keep this variable 32bit alligned 请保持此变量32位对齐*/
uint8_t aPacketData[PACKET_1K_SIZE + PACKET_DATA_INDEX + PACKET_TRAILER_SIZE];void send_file(SerialPort* serial, const char* file_path) {uint32_t errors = 0, ack_recpt = 0, size = 0, pkt_size;uint8_t* p_buf_int;COM_StatusTypeDef result = COM_OK;uint32_t blk_number = 1;uint8_t a_rx_ctrl[2];uint8_t i;uint32_t temp_crc;uint8_t* p_file_name;uint32_t file_size;DWORD bytes_read;FILE* file = fopen(file_path, "rb");if (!file) {std::cerr << "Error opening file\n";return;}//提取file_path路径里的文件名并读取文件的大小std::string path(file_path);std::string filename = path.substr(path.find_last_of("\\/") + 1);uint8_t files[128];strcpy((char*)files, filename.c_str());p_file_name = files;fseek(file, 0, SEEK_END);file_size = ftell(file);fseek(file, 0, SEEK_SET);std::cout << "Sending file: " << filename << ", size: " << file_size << " bytes\n";//uint8_t data[32000];//fread(data, 1, file_size, file);p_buf_int = (uint8_t*)malloc(file_size * sizeof(byte));fread(p_buf_int, 1, file_size, file);fclose(file);/* Prepare first block - header */PrepareIntialPacket(aPacketData, p_file_name, file_size);while ((!ack_recpt) && (result == COM_OK)) {/* Send Packet */serial->write(aPacketData, PACKET_START_INDEX, PACKET_SIZE + PACKET_HEADER_SIZE, NAK_TIMEOUT);/* Send CRC or Check Sum based on CRC16_F */temp_crc = Cal_CRC16(&aPacketData[PACKET_DATA_INDEX], PACKET_SIZE);serial->write((uint8_t)(temp_crc >> 8));serial->write((uint8_t)(temp_crc & 0xFF));/* Wait for Ack and 'C' */if (serial->read(&a_rx_ctrl[0], 1, NAK_TIMEOUT, bytes_read) == true) {if (a_rx_ctrl[0] == ACK) {ack_recpt = 1;}else if (a_rx_ctrl[0] == CA) {if ((serial->read(&a_rx_ctrl[0], 1, NAK_TIMEOUT, bytes_read) == true) && (a_rx_ctrl[0] == CA)) {Sleep(2);PurgeComm(serial->hSerial, PURGE_RXCLEAR);result = COM_ABORT;}}}else {errors++;}if (errors >= MAX_ERRORS) {result = COM_ERROR;}}//p_buf_int = data;size = file_size;/* Here 1024 bytes length is used to send the packets */while ((size) && (result == COM_OK)) {/* Prepare next packet */PreparePacket(p_buf_int, aPacketData, blk_number, size);ack_recpt = 0;a_rx_ctrl[0] = 0;errors = 0;/* Resend packet if NAK for few times else end of communication */while ((!ack_recpt) && (result == COM_OK)) {/* Send next packet */if (size >= PACKET_1K_SIZE) {pkt_size = PACKET_1K_SIZE;}else {pkt_size = PACKET_SIZE;}/* Send CRC or Check Sum based on CRC16_F */temp_crc = Cal_CRC16(&aPacketData[PACKET_DATA_INDEX], pkt_size);aPacketData[pkt_size + PACKET_HEADER_SIZE + PACKET_START_INDEX + 0] = (uint8_t)(temp_crc >> 8);aPacketData[pkt_size + PACKET_HEADER_SIZE + PACKET_START_INDEX + 1] = (uint8_t)(temp_crc & 0xFF);serial->write(aPacketData, PACKET_START_INDEX, pkt_size + PACKET_HEADER_SIZE + 2, NAK_TIMEOUT);PurgeComm(serial->hSerial, PURGE_RXCLEAR);uint8_t progress = (uint8_t)((float)(file_size - size) / file_size * 100);printf("current progress:%d%%\n", progress);/* Wait for Ack */if ((serial->read(&a_rx_ctrl[0], 1, NAK_TIMEOUT, bytes_read) == true)) {if (a_rx_ctrl[0] == ACK) {ack_recpt = 1;if (size > pkt_size) {p_buf_int += pkt_size;size -= pkt_size;if (blk_number == (USER_FLASH_SIZE / PACKET_1K_SIZE)) {result = COM_LIMIT; /* boundary error */}else {blk_number++;}}else {p_buf_int += pkt_size;size = 0;}}}else {errors++;}/* Resend packet if NAK  for a count of 10 else end of communication */if (errors >= MAX_ERRORS) {result = COM_ERROR;}}}/* Sending End Of Transmission char */ack_recpt = 0;a_rx_ctrl[0] = 0x00;errors = 0;while ((!ack_recpt) && (result == COM_OK)) {serial->write(EOT);/* Wait for Ack */if (serial->read(&a_rx_ctrl[0], 1, NAK_TIMEOUT, bytes_read) == true) {if (a_rx_ctrl[0] == ACK) {ack_recpt = 1;}else if (a_rx_ctrl[0] == CA) {if ((serial->read(&a_rx_ctrl[0], 1, NAK_TIMEOUT, bytes_read) == true) && (a_rx_ctrl[0] == CA)) {Sleep(2);PurgeComm(serial->hSerial, PURGE_RXCLEAR);result = COM_ABORT;}}}else {errors++;}if (errors >= MAX_ERRORS) {result = COM_ERROR;}}/* Empty packet sent - some terminal emulators need this to close session */if (result == COM_OK) {/* Preparing an empty packet */aPacketData[PACKET_START_INDEX] = SOH;aPacketData[PACKET_NUMBER_INDEX] = 0;aPacketData[PACKET_CNUMBER_INDEX] = 0xFF;for (i = PACKET_DATA_INDEX; i < (PACKET_SIZE + PACKET_DATA_INDEX); i++) {aPacketData[i] = 0x00;}/* Send Packet */serial->write(aPacketData, PACKET_START_INDEX, PACKET_SIZE + PACKET_HEADER_SIZE, NAK_TIMEOUT);/* Send CRC or Check Sum based on CRC16_F */temp_crc = Cal_CRC16(&aPacketData[PACKET_DATA_INDEX], PACKET_SIZE);serial->write((uint8_t)(temp_crc >> 8));serial->write((uint8_t)(temp_crc & 0xFF));/* Wait for Ack and 'C' */if (serial->read(&a_rx_ctrl[0], 1, NAK_TIMEOUT, bytes_read) == true) {if (a_rx_ctrl[0] == CA) {Sleep(2);PurgeComm(serial->hSerial, PURGE_RXCLEAR);result = COM_ABORT;}}}printf("current progress:100%%\n");serial->resetTimeout(50);IsStopPrintfReceive = false;
}void receive_thread(SerialPort* serial) {char buffer[32];DWORD bytes_read;while (true) {if (IsStopPrintfReceive == false) {if (serial->read(buffer, sizeof(buffer), bytes_read)) {if (bytes_read > 0) {std::cout.write(buffer, bytes_read);// Print received data}}}else {Sleep(1);}}
}void send_thread(SerialPort* serial) {char input[32];while (true) {std::cin.getline(input, sizeof(input));if (input[0] == '6') {IsStopPrintfReceive = true;std::string file_path = "I2C_SLAVE.bin";send_file(serial, file_path.c_str());}elseserial->write(input);}
}/*
void receive_thread(SerialPort* serial) {char buffer[32];DWORD bytes_read;if (IsStopPrintfReceive == false) {if (serial->read(buffer, sizeof(buffer), bytes_read)) {if (bytes_read > 0) {std::cout.write(buffer, bytes_read);// Print received data}}}else {Sleep(1);}
}void send_thread(SerialPort* serial) {char input[32];    std::cin.getline(input, sizeof(input));if (input[0] == '6') {IsStopPrintfReceive = true;std::string file_path = "../Debug/F303APP.bin";send_file(serial, file_path.c_str());}elseserial->write(input);}
*/int main() {//int a = 563298;//char *p=int_to_char(a);//printf("%s",p);SerialPort serial("COM3");std::thread t1(receive_thread, &serial);std::thread t2(send_thread, &serial);t1.join();t2.join();return 0;//while(true)//{//receive_thread(&serial);//send_thread(&serial);//}	
}

编译程序没有错误提示,试着测试下升级功能,上位机这边文件可以正常发送并显示进度,但最后单片机那边返回了Failed to receive the file! 证明单片机接收文件有问题,我试着运行用户程序(也就是IAP升级后跳转到的程序),果然没有成功。看样子升级是没有成功,开始调试找问题。

既然使用同样的升级文件secureCRT能正确执行,但这个C++程序不行,那证明程序有问题。刚开始也不知道从哪里下手比较好,刚好我手边有个逻辑分析仪,之前我就是用它抓取了Microchip官方程序UnifiedHost-1.19.0使用Ymodem协议和RS485发送给PIC18F45K80单片机的升级程序内容才成功制作了自定义的升级文件。具体方法可以参考我另一篇博文:PIC18F45K80系列MCU固件升级方案-CSDN博客。于是我抓取了secureCRT和本程序发送的数据,经过对比发现是第一帧数据不同,就是文件名称和长度这个数据包。两个文件的第一帧数据包 截图如下:

1、secureCRT发送的第一帧内容

2、本程序发送的第一帧内容

经过观察后发现使用C++编写的程序文件长度(12496)这里没有字符串结束标志0,很可能就是这里的原因,于是找到源程序中的Int2Str函数对它进行了改写,原来的程序和改写后的程序如下:

原来的代码

/*** @brief  Convert an Integer to a string  将整数转换为字符串* @param  p_str: The string output pointer   字符串输出指针* @param  intnum: The integer to be converted   要转换的整数* @retval None*/
void Int2Str(uint8_t* p_str, uint32_t intnum) {uint32_t i, divider = 1000000000, pos = 0, status = 0;for (i = 0; i < 10; i++) {p_str[pos++] = (intnum / divider) + 48;intnum = intnum % divider;divider /= 10;if ((p_str[pos - 1] == '0') & (status == 0)) {pos = 0;}else {status++;}}
}

修改后的代码(增加了一个函数)

/*** @brief  计算10的幂次* @param  x: The integer to be converted* @retval None*/
int mi(int x)	//
{int i=0,ans=1;for(i;i<x;i++){ans=ans*10;}return ans;
}/*** @brief  Convert an Integer to a string  将整数转换为字符串* @param  p_str: The string output pointer   字符串输出指针* @param  intnum: The integer to be converted   要转换的整数* @retval None*/
void Int2Str(uint8_t* p_str, uint32_t intnum) {int n=intnum,count=0;while(intnum!=0)		//求出intnum的位数count{intnum=intnum/10;count++;}int i=0,j=count;//char b[count];for(i;i<j;i++)		//这里我是正序添加字符的{//b[i]=n/mi(count-1)+'1'-1;p_str[i]=n/mi(count-1)+48;//也就是加上字符'0'的ASCII码值n=n%mi(count-1);count--;} p_str[i]=0;//printf("%s",b);
}

重新编译程序后运行,发现可以正常发送文件了!抓取数据也有了结束符,用户程序也成功升级!问题解决了,后续会继续完善下此程序,欢迎大家一起讨论。

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

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

相关文章

浅谈安科瑞无线测温设备在挪威某项目的应用

摘要&#xff1a;安科瑞无线温度设备装置通过无线温度收发器和各无线温度传感器直接进行温度值的传输&#xff0c;并采用液晶显示各无线温度传感器所测温度。 Absrtact:Acre wireless temperature device directly transmits the temperature value through the wireless temp…

基于51单片机倾角MPU6050老人跌倒远程GSM短信报警器+源程序

一、系统方案 1、本设计采用这51单片机作为主控器。 2、MPU6050角度值送到液晶1602显示。 3、红外传感器检测心率。 4、跌倒远程GSM报警。 二、硬件设计 原理图如下&#xff1a; 三、单片机软件设计 1、首先是系统初始化 void LCD_Init() //初始化液晶时间显示 { write_com…

yarn:无法加载文件 C:\Users\***\AppData\Roaming\npm\yarn.ps1,因为在此系统上禁止运行脚本

原因&#xff1a;PowerShell 脚本的执行有着严格的安全策略限制&#xff01; 解决方案&#xff1a;管理员身份启动Windows PowerShell 在命令行中输入set-ExecutionPolicy RemoteSigned 再使用yarn就可以了

SQL常见函数整理 _ LAG() 向上偏移

1. 用法 窗口函数&#xff0c;用于访问窗口中当前行之前的行的数据。该函数可以根据需要计算当前行之前的值&#xff0c;使我们能够轻松地比较不同行之间的差异和变化。 2. 基本语法 LAG(column, offset, default_value) OVER (ORDER BY column)column&#xff1a;代表在返回…

【UE5】资源(Asset)

了解UE游戏的基本构成 资源&#xff08;Asset&#xff09;: 在UE中&#xff0c;资源&#xff08;Asset&#xff09;是指游戏中使用到的各种素材&#xff0c;例如模型、纹理、材质、声音、动画、蓝图、数据表格、关卡等&#xff08;通常以uasset结尾&#xff09;&#xff0c;他…

土壤教学经典用图30张

一、土壤分布 二、土壤形成与气候 三、土壤形成与地形 四、土壤形成与成土母质 五、成土过程示意图 六、土壤剖面实景图 七、土壤剖面示意图 八、土壤质地 以上图片多来源于 人教、湘教、鲁教、中图、沪教 五套新教材及地图册

忘记7-zip密码,如何解压文件?

7z压缩包设置了密码&#xff0c;解压的时候就需要输入正确对密码才能顺利解压出文件&#xff0c;正常当我们解压文件或者删除密码的时候&#xff0c;虽然方法多&#xff0c;但是都需要输入正确的密码才能完成。忘记密码就无法进行操作。 那么&#xff0c;忘记了7z压缩包的密码…

YOLOv3老矣尚能战否?基于YOLOv3开发构建建钢铁产业产品智能自动化检测识别系统,我们来与YOLOv5进行全方位对比评测

钢铁产业产品智能自动化检测识别相关的项目在我们前面的博文中已经有了相应的实践了&#xff0c;感兴趣的话可以自行移步阅读即可&#xff1a;《python基于DETR(DEtection TRansformer)开发构建钢铁产业产品智能自动化检测识别系统》 《AI助力钢铁产业数字化&#xff0c;pytho…

OPPO VOOC快充原理

1 USB 3.0标准A插头 USB 3.0连接器是基于USB 2.0改进而来的&#xff0c;这个设计给USB 3.0连接器带来了一些潜在风险&#xff0c;如果USB 3.0设备插入主机的速度太慢&#xff0c;3.0的针脚还没来得及被识别到&#xff0c;就会被主机判定成USB 2.0的设备。 Figure 1-1 USB 3.0标…

centos系统下,docker安装sqlserver并用本地Navicat连接

文章目录 一&#xff0c;centos下安装docker二&#xff0c;docker安装sqlserver20192.1 安装遇到的问题2.1.1 修改用户名进不去数据库2.1.2 安装2022版的sqlserver发现启动失败 三&#xff0c;Navicat连接centos下的sqlserver3.1 下载ODBC Driver 参考微软网址&#xff1a; 使…

代码随想录算法训练营第四十五天【动态规划part07】 | 70. 爬楼梯 (进阶)、322. 零钱兑换、279.完全平方数

70. 爬楼梯 &#xff08;进阶&#xff09; 题目链接&#xff1a; 题目页面 求解思路&#xff1a; 动规五部曲 确定dp数组及其下标含义&#xff1a;爬到有i阶楼梯的楼顶&#xff0c;有dp[i]种方法递推公式&#xff1a;dp[i] dp[i-j];dp数组的初始化&#xff1a;dp[0] 1;确…

数据治理技术之数据清洗

数据清洗背景 数据质量一般由准确性、完整性、一致性、时效性、可信性以及可解释性等特征来描述&#xff0c;根据 Rahm 等人在 2000 年对数据质量基于单数据源还是多数据源以及问题出在模式层还是实例层的标准进行分类&#xff0c;将数据质量问题分为单数据源模式层问题、单数…

虚幻学习笔记—给UI添加动画

一、前言 本文所使用的虚幻版本为5.3.2&#xff0c;之前工作都是用unity&#xff0c;做这类效果用的最多的是一个DoTween的插件&#xff0c;在虚幻中都内置集成了这这种效果制作。 图1.1 UI动画 二、过程 1、首先&#xff0c;在诸如按钮、图像等可交互控件中选中&#xff0c;如…

计算机网络之运输层

一、概述 物理层、数据链路层以及网络层它们共同解决了将主机通过异构网络互联起来所面临的的问题&#xff0c;实现了主机到主机的通信 但实际上在计算机网络中进行通信的真正实体是位于通信两端主机中的进程 如何为运行在不同主机上的应用进程提供直接的通信服务时运输层的任务…

从设计上理解JDK动态代理

作者简介&#xff1a;大家好&#xff0c;我是smart哥&#xff0c;前中兴通讯、美团架构师&#xff0c;现某互联网公司CTO 联系qq&#xff1a;184480602&#xff0c;加我进群&#xff0c;大家一起学习&#xff0c;一起进步&#xff0c;一起对抗互联网寒冬 照理说&#xff0c;动态…

上门预约小程序开发优势

想要放松身心&#xff0c;享受按摩的舒适感&#xff1f;那就需要一个专业的按摩师来上门服务。我们开发的预约按摩小程序app系统&#xff0c;汇聚各类上门按摩服务&#xff0c;包括推拿SPA、小儿推拿、中医等&#xff0c;为您提供高价值、高标准的养生健康体验。24小时随时提供…

【前端】让列表像Excel单元格一样编辑

前言 领导说了一堆的话,最后总结一句就是客户很懒,客户的员工更加懒。 本着让别人节省时间的原则,提倡出了让列表和Excal的单元格一样,不仅看数据还可以随时更改数据。 查资料 根据 Jeecg-Vue3 源码介绍,从而知道是基于 Vben Admin 开源项目进行改造的。 因此在 Vben…

沉头孔和埋头孔的区别

埋头空和沉头孔的区别在于螺栓孔上部扩孔&#xff1a;沉头孔是直筒结构&#xff1b;埋头孔是四十五度结构&#xff0c;比沉头孔较为平顺。 螺栓孔上部扩孔能容纳螺栓头部&#xff0c;使螺头部不高于周围表面。埋头空和沉头孔只是两种不同的叫法。 沉头孔是 PCB 上的圆柱形凹槽…

将form表单中的省市区的3个el-select下拉框的样式调成统一的间隔距离和长度,vue3项目iot->供应商管理

省市区是用3个el-select组成的 在表单中用el-col&#xff0c;会导致3个下拉的距离不统一&#xff0c;市和区的前面也是不需要文字label的 如何解决:用vue3的:deep()进行样式穿透&#xff0c;由于el-form-item标签都是一样的&#xff0c;为了能准确的找到市的el-form-item&…

解决:前端js下载文件流出现“未知文件格式”错误

第一中情况&#xff1a; 出现的问题&#xff0c;前端已经设置了responseType: blob,下载下来还是格式不对。 最后经过排查&#xff0c;后端缺少charsetutf-8&#xff0c;所以前端可以设置编码&#xff1a; 第二中情况&#xff1a; 后端已经设置了charsetutf-8&#xff0c;前…