最近在使用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);
}
重新編譯程序后運行,發現可以正常發送文件了!抓取數據也有了結束符,用戶程序也成功升級!問題解決了,后續會繼續完善下此程序,歡迎大家一起討論。