Y-MODEM协议定制上位机

news2024/7/4 0:03:45

最近在使用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);
}

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

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

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

相关文章

能量不够,AI帮助

问CHAT&#xff1a;有关工程管理的中文参考文献 CHAT回复&#xff1a; 1. 余庆麟. (2009). 工程项目管理[M]. 北京&#xff1a; 机械工业出版社. 2. 刘宁. (2013). 工程建设管理[M]. 北京&#xff1a;清华大学出版社. 3. 谈刚&#xff0c;陈嘉春&#xff0c;尚雅凡. (2014).…

最详细的软件测试面试题整理与分析

前言 时光荏苒&#xff0c;一转眼到了2023年末尾&#xff0c;2024年也快要来了&#xff0c;人员就业市场以往的寒冬也貌似有了转暖的迹象&#xff0c;身边大批的就业人员也开始了紧张的备战之中。 近几周也和多家合作公司的HR进行了沟通&#xff0c;发现虽然岗位就业情况较去年…

2021年06月 Scratch(三级)真题解析#中国电子学会#全国青少年软件编程等级考试

Scratch等级考试(1~4级)全部真题・点这里 一、单选题(共25题,每题2分,共50分) 第1题 下图中的程序执行一次之后,“我的变量”最终的值是? A:0或者1 B:true或者false C:包含或者不包含 D:成立或者不成立 答案:B 积木“()包含()”是一个条件判断语句,它判…

Unity RenderFeature架构分析

自定义RenderFeature接口流程 URP内部ScriptableRenderPass分析 public、protected属性 renderPassEvent &#xff1a;渲染事件发生的时刻colorAttachments &#xff1a;渲染的颜色纹理列表 m_ColorAttachmentscolorAttachment &#xff1a;m_ColorAttachments[0];depthAttac…

【转载】如何在Macbook上把Ubuntu安装到移动硬盘里

我的设备系统版本、遇到的问题和解决&#xff1a; Mac&#xff1a;macOS Ventura 13.3 Ubuntu&#xff1a;22.04.3 问题&#xff1a; 按照这个教程在【3.3.10】修改完启动项后&#xff0c;Mac系统无法启动&#xff0c;Ubuntu可以正常启动。 原因&#xff1a; Mac找不到启动引导…

自己实名绑定了几个微信号?赶紧来看看

我们都明白&#xff0c;微信的重要性无处不在&#xff0c;它与我们生活的方方面面紧密相连。如今&#xff0c;微信支付已成为我们日常生活中不可或缺的一部分。无论是购物、用餐&#xff0c;还是日常消费&#xff0c;微信支付都能轻松解决。如果你担心携带现金会有遗失的风险&a…

服务台需要跟踪的3个重要指标MTBF+MTTF+MTTR

衡量标准是IT服务管理的核心&#xff0c;可提供有关运营的见解&#xff0c;并帮助确定需要持续改进的领域。通常的服务台指标有助于展示内部运营效率。例如&#xff0c;衡量在规定时间内解决问题的工单数量的 SLA 是展示服务台效率的关键因素。另一方面&#xff0c;故障指标可帮…

那些被玩烂了的设计模式

单例模式 单例模式是指一个类在一个进程中只有一个实例对象&#xff08;但也不一定&#xff0c;比如Spring中的Bean的单例是指在一个容器中是单例的&#xff09; 单例模式创建分为饿汉式和懒汉式&#xff0c;总共大概有8种写法。但是在开源项目中使用最多的主要有两种写法&am…

SPSS信度分析

前言&#xff1a; 本专栏参考教材为《SPSS22.0从入门到精通》&#xff0c;由于软件版本原因&#xff0c;部分内容有所改变&#xff0c;为适应软件版本的变化&#xff0c;特此创作此专栏便于大家学习。本专栏使用软件为&#xff1a;SPSS25.0 本专栏所有的数据文件请点击此链接下…

HBase之Region Splitting

目录 Region Splitting步骤 Region状态过程 Region Splitting 步骤 RegionServer开始split region&#xff0c;SPLIT事务开启。RegionServer在表上获取共享读锁防止split过程中数据被修改。接着在zk中创建一个znode&#xff0c;标记为SPLITTING。Master 将会观察到该znode的创…

linux如何查看文件的hash数值

在Linux系统中&#xff0c;你可以使用各种工具来查看文件的哈希值。下面是一些常见的方法&#xff1a; md5sum命令&#xff1a; md5sum 文件名例如&#xff1a; md5sum example.txtsha1sum命令&#xff1a; sha1sum 文件名例如&#xff1a; sha1sum example.txtsha256sum命令&a…

Android 打包aar包含第三方aar 解决方案

Android 打包aar包含第三方aar 因项目需要&#xff0c;打包aar包含第三方aar&#xff0c;如果直接对module进行打包会产生一些问题。 * What went wrong: Direct local .aar file dependencies are not supported when building an AAR. The resulting AAR would be broken be…

加速你的自动化测试:3种等待方式!

在自动化测试中&#xff0c;等待是一个重要的技术&#xff0c;用于处理页面加载、元素定位、元素状态改变等延迟问题。 等待能够确保在条件满足后再进行后续操作&#xff0c;提高自动化测试的稳定性以及可靠性。 等待方式&#xff1a;显示等待、隐式等待、线程睡眠 1. 显式等…

Python丨让简历脱颖而出的关键,居然是“它”!

进入疫情后时代&#xff0c;各行各业都在力争新的发展&#xff01;财会行业亦是如此&#xff0c;浏览各大招聘网站&#xff0c;不难发现财会相关岗位的招聘要求越来越“卷”&#xff0c;那求职者如何才能让自己获得面试邀请呢&#xff1f; 答案就是&#xff1a;一份亮眼且具有…

设计模式—开闭原则

1.背景 伯特兰迈耶一般被认为是最早提出开闭原则这一术语的人&#xff0c;在他1988年发行的《面向对象软件构造》中给出。这一想法认为一旦完成&#xff0c;一个类的实现只应该因错误而修改&#xff0c;新的或者改变的特性应该通过新建不同的类实现。新建的类可以通过继承的方…

Duplicate 模型中的 ROLLUP(十六)

因为 Duplicate 模型没有聚合的语意。所以该模型中的 ROLLUP&#xff0c;已经失去了“上卷”这一层含义。而仅仅是作为调整列顺序&#xff0c;以命中前缀索引的作用。下面详细介绍前缀索引&#xff0c;以及如何使用 ROLLUP 改变前缀索引&#xff0c;以获得更好的查询效率。 前…

【开源】基于Vue.js的城市桥梁道路管理系统的设计和实现

项目编号&#xff1a; S 025 &#xff0c;文末获取源码。 \color{red}{项目编号&#xff1a;S025&#xff0c;文末获取源码。} 项目编号&#xff1a;S025&#xff0c;文末获取源码。 目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块三、系统展示四、核心代码4.1 查询城市桥…

选择TikTok直播网络方案前的必读指南

真实、稳定、可靠的TikTok直播网络方案挑选需要考虑这三大要素&#xff1a; 原生IP、独享带宽、线路 之前小编也分享了不少关于TikTok直播网络搭建方法和注意事项的文章及用户案例&#xff0c;但还是不少TikTok直播卖家不知道各个方法有何区别&#xff0c;毕竟市面上说可以解决…

Centos 7、Debian、Ubuntu中tree指令的检查与下载

目录 前言 Centos 7中检查tree指令是否安装的两种办法 which指令检查 查看当前版本指令 不同版本下安装tree指令 Centos 7的发行版本 重点 Debian的发行版本 重点 Ubuntu的发行版本 重点 前言 在大多数Linux发行版中&#xff0c;tree命令通常不是默认安装的指令。…

动物病理学3d虚拟仿真场景还原系统提高学员的判断准确性

动物疾病检查培训存在着标本难找、操作复杂及费用高等难点&#xff0c;将VR虚拟现实技术应用到动物疾病检查培训中&#xff0c;突破了诸多传统限制&#xff0c;为学生提供更为真实、直观的学习体验&#xff0c;通过实践操作&#xff0c;提高培训效果和质量。 每种动物的系统模块…