فهرست منبع

修复ymodem发送和接收最后一包不是完整的128或1024字节时的问题

niujiuru 2 روز پیش
والد
کامیت
3012ae07c4
4فایلهای تغییر یافته به همراه39 افزوده شده و 27 حذف شده
  1. 3 3
      ymodem/readme.txt
  2. 30 21
      ymodem/ymodem.c
  3. 4 1
      ymodem/ymodem.h
  4. 2 2
      ymodem/ymodem_test.c

+ 3 - 3
ymodem/readme.txt

@@ -2,15 +2,15 @@
 
 该模块提供了完整的发送和接收功能,适用于嵌入式系统固件更新、设备配置传输等场景。模块设计注重可移植性和易用性,允许用户通过实现简单的接口来适配不同的底层串口通信驱动。
 
-协议简介Ymodem 是一种基于串行通信的发送并等待协议,其核心特征包括:
+协议简介Ymodem是一种基于串行通信的发送并等待协议,其核心特征包括:
 
-数据块尺寸灵活:支持 128 字节(SOH)和 1024 字节(STX)两种数据包格式,优先使用1K大包以提高传输效率。
+数据块尺寸灵活:支持 128 字节(SOH)和 1024 字节(STX)两种数据包格式,优先使用1K大包以提高传输效率(发送目前只支持1K大包)
 
 可靠的错误校验:强制使用 CRC-16 校验算法,确保数据完整性。
 
 文件内容的传输:在传输文件内容前,通过起始帧传递文件名、文件大小等元数据。
 
-多文件批量接收:支持批量连续接收多个文件,发送目前支持单一文件。
+多文件批量接收:支持批量连续接收多个文件,接收开启后,会阻塞等待发送端发送,直到会话结束或发生错误;发送目前支持批量发送多文件,只支持发送单一文件(够用)
 
 基本的通信流程: 由接收方发起,接收方持续发送字符'C'启动 CRC 模式的传输;-> 发送方收到后,首先发送一个包含文件名和大小的文件头包(起始帧);
                -> 接收方确认后,发送方开始发送文件数据包;-> 文件传输结束后,发送方发送 EOT,并最终以一个空文件头包结束整个会话。

+ 30 - 21
ymodem/ymodem.c

@@ -135,9 +135,9 @@ int ymodem_recv_files(const char *dir)
 {
   unsigned char packet_data[PACKET_1K_SIZE + PACKET_OVERHEAD];
   int packet_length, i, file_done, session_done, ret;
-  unsigned int packets_received, errors, first_try, files_num;
-  char file_name[FILE_NAME_LENGTH], file_size[FILE_SIZE_LENGTH], *file_ptr;
-  unsigned char block[PACKET_1K_SIZE]; unsigned int file_size_val = 0; char path[MAX_PATH_CHARS];
+  unsigned int packets_received, errors, waits, first_try, files_num;
+  char file_name[FILE_NAME_LENGTH], file_size[FILE_SIZE_LENGTH], *file_ptr, path[MAX_PATH_CHARS];
+  unsigned char file_data[PACKET_1K_SIZE]; unsigned int file_size1 = 0, file_size2 = 0;
 
   RingBuf_ctor(&s_myUart.rx_buf, s_myUart.rx_buf_storage, sizeof(s_myUart.rx_buf_storage)/sizeof(RingBufElement));
   s_myUart.h = serial_open(UART_DEVICE_NAME, UART_BAUD_RATE, UART_PARITY_CHECK, \
@@ -145,7 +145,7 @@ int ymodem_recv_files(const char *dir)
   if(!s_myUart.h) return -1;
 
   file_name[0] = '\0'; first_try = 1; ret = files_num = 0;
-  for(session_done = 0, errors = 0; serial_recvThrd_isAlive(s_myUart.h); )
+  for(session_done = 0, errors = 0, waits = 0; serial_recvThrd_isAlive(s_myUart.h); )
   { // receive files
     if(!first_try) uart_putchar(CRC);
     first_try = 0;
@@ -156,6 +156,7 @@ int ymodem_recv_files(const char *dir)
       {
       case  0: // receive success
         if(errors != 0) errors = 0;
+        if(waits  != 0) waits  = 0;
         switch(packet_length)
         {
         case -1: { uart_putchar(ACK); ret = -2; goto ret_p; } // abort
@@ -170,11 +171,11 @@ int ymodem_recv_files(const char *dir)
               file_name[i++] = '\0'; // 接收的文件名称
               for(++file_ptr, i = 0; *file_ptr != ' ' && i < FILE_SIZE_LENGTH; ) file_size[i++] = *file_ptr++;
               file_size[i++] = '\0'; // 接收的文件大小
-              file_size_val = str_to_u32(file_size);
+              file_size1 = str_to_u32(file_size); file_size2 = 0;
 
               uart_putchar(ACK); uart_putchar(CRC);
 
-              sw_log_debug("[%s] <文件: %s, 大小: %u字节> 开始接收...", UART_MODULE_NAME, file_name, file_size_val);
+              sw_log_debug("[%s] <文件: %s, 大小: %u字节> 开始接收...", UART_MODULE_NAME, file_name, file_size1);
               snprintf(path, sizeof(path), "%s/%s", dir, file_name);
               if(sw_file_exists(path)) sw_file_delete(path);
             }
@@ -185,24 +186,27 @@ int ymodem_recv_files(const char *dir)
           }
           else
           {
-            for(i = 0; i < packet_length; i++) { block[i] = packet_data[PACKET_HEADER+i]; }
+            for(i = 0; i < packet_length; i++) { file_data[i] = packet_data[PACKET_HEADER+i]; }
             uart_putchar(ACK);
             sw_log_debug("[%s] <文件: %s, 大小: %u字节> 收到第%u包数据, 数据包大小: %u字节", \
-              UART_MODULE_NAME, file_name, file_size_val, packets_received, packet_length);
+              UART_MODULE_NAME, file_name, file_size1, packets_received, packet_length);
 
             if(!sw_dir_exists(dir)) sw_dir_create(dir);
-            ret = sw_file_update(path, "ab", (char *)block, packet_length);
+            if((file_size1-file_size2) < packet_length) packet_length = (file_size1-file_size2); // 最后一包数据可能小于PACKET_1K_SIZE
+            ret = sw_file_update(path, "ab", (char *)file_data, packet_length);
             if(ret != packet_length) { uart_putchar(CAN); uart_putchar(CAN); ret = -3; goto ret_p; }
+            file_size2 += ret;
           }
           ++packets_received; // 累加已接收包数
         }
         break;
       default: // receive error
         if(packets_received != 0) { if(++errors >= MAX_ERRORS/* too many errors */) { uart_putchar(CAN); uart_putchar(CAN); ret = -4; goto ret_p; } }
+        else { if(++waits >= MAX_START_WAITS) { uart_putchar(CAN); uart_putchar(CAN); ret = -5; goto ret_p; } } // 等待超时, 发送端一直没有开始发送则退出
         uart_putchar(CRC);
       }
 
-      if(file_done)  { if(!session_done) sw_log_debug("[%s] 文件: %s 接收完成, 实际接收文件大小: %u字节", UART_MODULE_NAME, file_name, sw_file_getSize(path)); break; } // 文件接收完成
+      if(file_done)  { if(!session_done) sw_log_debug("[%s] 文件: %s 接收完成, 实际接收文件大小: %u字节", UART_MODULE_NAME, file_name, sw_file_getSize(path)); break; } // 文件接收完成
     }
 
     if(session_done) { ret = files_num; break; } // 传输会话结束
@@ -234,20 +238,20 @@ static void send_packet(unsigned char *data, int block_no)
 static char send_log_prefix[MAX_LINE_CHARS];
 static void send_packet0(char *filename, unsigned long size)
 {
-  unsigned long count = 0; unsigned char block[PACKET_SIZE];
+  unsigned long count = 0; unsigned char zero_block[PACKET_SIZE];
   const char *num; const char *str = (filename ? filename : "null");
 
   if(filename) {
-    while(*filename && (count < PACKET_SIZE-FILE_SIZE_LENGTH-2)) block[count++] = *filename++;
-    block[count++] = 0;
+    while(*filename && (count < PACKET_SIZE-FILE_SIZE_LENGTH-2)) zero_block[count++] = *filename++;
+    zero_block[count++] = 0;
 
     num = u32_to_str(size);
-    while(*num) block[count++] = *num++;
+    while(*num) zero_block[count++] = *num++;
   }
 
-  while(count < PACKET_SIZE) block[count++] = 0;
+  while(count < PACKET_SIZE) zero_block[count++] = 0;
 
-  send_packet(block, 0);
+  send_packet(zero_block, 0);
   snprintf(send_log_prefix, sizeof(send_log_prefix), "<文件: %s, 大小: %lu字节>", str, size);
   sw_log_debug("[%s] %s 开始发送...", UART_MODULE_NAME, send_log_prefix);
 }
@@ -255,12 +259,17 @@ static void send_packet0(char *filename, unsigned long size)
 static long send_data_packets(unsigned char *data, unsigned long size)
 {
   int blockno = 1; unsigned long send_size; int ch, retry = 0;
-  unsigned long total_sent = 0;
+  unsigned long total_sent = 0; unsigned char last_block[PACKET_1K_SIZE];
 
   while(size > 0)
   {
-    if(size > PACKET_1K_SIZE) send_size = PACKET_1K_SIZE;
-    else send_size = size;
+    if(size >= PACKET_1K_SIZE) send_size = PACKET_1K_SIZE;
+    else
+    { // 最后一包
+      memset(last_block, 0x1A, sizeof(last_block));
+      memcpy(last_block, data, size);
+      send_size = size; data = last_block;
+    }
 
     send_packet(data, blockno);
     sw_log_debug("[%s] %s 已发送第%u包数据, 数据包大小: %lu字节", UART_MODULE_NAME, send_log_prefix, blockno, send_size);
@@ -287,7 +296,7 @@ static long send_data_packets(unsigned char *data, unsigned long size)
 
   retry = 0; do {
     uart_putchar(EOT);
-    sw_log_debug("[%s] %s 文件发送完成, 实际发送字节数: %lu字节", UART_MODULE_NAME, send_log_prefix, total_sent);
+    sw_log_debug("[%s] %s 文件发送完成, 实际发送字节数: %lu字节", UART_MODULE_NAME, send_log_prefix, total_sent);
     ch = uart_getchar(PACKET_TIMEOUT);
   } while((ch != ACK) && (ch != -1) && (++retry < MAX_ERRORS));
 
@@ -329,7 +338,7 @@ int ymodem_send_file(const char *path)
                            comio_data_recv_proc, NULL, NULL);
   if(!s_myUart.h) { ret = -5; goto ret_p; }
 
-  retry = 0; do { ch = uart_getchar(PACKET_TIMEOUT); } while(ch != CRC && ++retry < 30); // 等待接收CRC, 而后才开始发送文件
+  retry = 0; do { ch = uart_getchar(PACKET_TIMEOUT); } while(ch != CRC && ++retry < MAX_START_WAITS); // 等待接收CRC, 而后才开始发送文件
   if(ch != CRC) { uart_putchar(CAN); uart_putchar(CAN); ret = -6; goto ret_p; }
 
   retry = 0; do

+ 4 - 1
ymodem/ymodem.h

@@ -27,7 +27,10 @@
 #define CRC (0x43)  /* use in place of first NAK for CRC mode */
 
 /* Number of consecutive receive errors before giving up: */
-#define MAX_ERRORS (5)
+#define MAX_ERRORS      (5)
+
+/* Number of startup wait attempts before giving up: */
+#define MAX_START_WAITS (30)
 
 #define UART_MODULE_NAME "YMODEM"         /* 串口模块名称 */
 #define UART_DEVICE_NAME  "/dev/ttymxc2"  /* 串口设备名称 */

+ 2 - 2
ymodem/ymodem_test.c

@@ -9,11 +9,11 @@ int main(int argc,char *argv[])
   int send_file_size = sw_file_getSize(send_file_name);
   if(send_file_size <= 0) return -1;
   ret = ymodem_send_file(send_file_name);
-  sw_log_info("[%s] 发送测试完成 -> File: %s, Size: %d字节, Sent: %d字节", UART_MODULE_NAME, send_file_name, send_file_size, ret);
+  sw_log_info("[%s] 发送测试结束 -> File: %s, Size: %d字节, Sent: %d字节", UART_MODULE_NAME, send_file_name, send_file_size, ret);
 
   // 接收测试
   ret = ymodem_recv_files("./received");
-  sw_log_info("[%s] 接收测试完成 <- Received files: %d个", UART_MODULE_NAME, ret);
+  sw_log_info("[%s] 接收测试结束 <- Received files: %d个", UART_MODULE_NAME, ret);
 
   return 0;
 }