#define _CRT_SECURE_NO_WARNINGS #include <iostream> #include <windows.h> #include <thread> bool IsStopPrintfReceive = false; class SerialPort { public: HANDLE hSerial; //构造函数,打开串口并设置参数 SerialPort(const char* portName) { hSerial = CreateFileA( portName, 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); } // 初始化串口参数 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); } // 设置串口参数 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); } // 设置超时时间 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); } } //析构函数,关闭串口 ~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 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 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); 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); std::cout << "Sent Block: " << blk_number << std::endl; /* 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++; } std::cout << "Receive: " << std::hex << (int)a_rx_ctrl[0] << std::endl; /* 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; } } } 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 (serial->write(input)) { std::cout << "Sent: " << input << std::endl; if (input[0] == '6') { IsStopPrintfReceive = true; send_file(serial, "C:\\Users\\MingYi-LZQ\\Desktop\\F303APP.bin"); } } } } int main() { SerialPort serial("COM7"); std::thread t1(receive_thread, &serial); std::thread t2(send_thread, &serial); t1.join(); t2.join(); return 0; }