最近在使用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; // 波特率为115200
dcbSerialParams.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());
}
else
serial->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());
}
else
serial->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);
}
重新编译程序后运行,发现可以正常发送文件了!抓取数据也有了结束符,用户程序也成功升级!问题解决了,后续会继续完善下此程序,欢迎大家一起讨论。