From 734ed5ed6e38c18a76221eb8b9629d40741e9177 Mon Sep 17 00:00:00 2001 From: penglei <1624952103@qq.com> Date: Sat, 6 Dec 2025 19:57:34 +0800 Subject: [PATCH] =?utf8?q?=E6=9B=B4=E6=96=B0httpfiledownload.py,=E9=80=82?= =?utf8?q?=E9=85=8D=E7=8E=B0=E6=9C=89SPI=E6=97=B6=E5=BA=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit --- cat1modem/src/LeiNiao/httpfiledownload.py | 269 ++++++++++++++++------ code/sdk/zhihui/utils/custom_spi.c | 71 +++--- 2 files changed, 239 insertions(+), 101 deletions(-) diff --git a/cat1modem/src/LeiNiao/httpfiledownload.py b/cat1modem/src/LeiNiao/httpfiledownload.py index 3f461b5ad..fc3b006a2 100644 --- a/cat1modem/src/LeiNiao/httpfiledownload.py +++ b/cat1modem/src/LeiNiao/httpfiledownload.py @@ -25,6 +25,7 @@ from usr.EventMesh import eventMesh import usocket import _thread from machine import SPI +from machine import Pin # spi - 通道选择[0,1],int类型。 @@ -56,7 +57,13 @@ from machine import SPI REQUESTS_AVAILABLE = True +# 创建gpio对象(唤醒MCU的引脚,GPIO4,输出模式,初始高电平) +gpioWakeBT = Pin(Pin.GPIO4, Pin.OUT, Pin.PULL_DISABLE, 1) +# 设置输入输出模式 +gpioWakeBT.set_dir(Pin.OUT) +# 设置引脚电平 +gpioWakeBT.write(1) class HttpFileDownloader: """ @@ -276,8 +283,8 @@ class HttpFileDownloadTcpServer: # 初始化 SPI 对象 if spi_obj is None: # 默认创建 SPI(1, 0, 1): 通道1, 模式0, 时钟频率1(1.625MHz) - self.spi_obj = SPI(2, 0, 8) - clog.info("Created default SPI object: SPI(2, 0, 8)") + self.spi_obj = SPI(2, 0, 12) + clog.info("Created default SPI object: SPI(2, 0, 12)") else: self.spi_obj = spi_obj clog.info("Using provided SPI object") @@ -422,53 +429,113 @@ class HttpFileDownloadTcpServer: # 下载数据并通过 SPI 发送 downloaded_size = 0 + # 缓存未发送的剩余数据 + remaining_data = b'' + # 累计补0字节数 + total_padded_zeros = 0 # response.content 是 bytes 类型生成器 for chunk in response.content: if chunk: # 过滤掉空块 try: - # 通过 SPI 发送数据块 - # SPI API: write(data, datasize) 或 write_read(read_buf, write_data, datasize, timeout) - chunk_len = len(chunk) - if hasattr(self.spi_obj, 'write'): - # write(data, datasize) - datasize 参数必需 - self.spi_obj.write(chunk, chunk_len) - elif hasattr(self.spi_obj, 'write_read'): - # write_read(read_buf, write_data, datasize, timeout) - # 或者 write_read(read_buf, write_data, datasize) - read_buf = bytearray(chunk_len) - try: - # 尝试带timeout的调用 - self.spi_obj.write_read(read_buf, chunk, chunk_len, 5) - except TypeError: - # 尝试不带timeout的调用 - self.spi_obj.write_read(read_buf, chunk, chunk_len) - else: - clog.error("SPI object does not have write or write_read method") - return False, 0.0, -1 + # 合并剩余数据和当前chunk + combined_data = remaining_data + chunk + remaining_data = b'' - downloaded_size += len(chunk) + # 拆分数据为1024字节的块 + while len(combined_data) >= 1024: + # 取前1024字节 + send_chunk = combined_data[:1024] + # 剩余数据保留 + combined_data = combined_data[1024:] + + gpioWakeBT.write(0) + utime.sleep_ms(2) + gpioWakeBT.write(1) + utime.sleep_ms(2) + + # 通过 SPI 发送1024字节数据块 + chunk_len = len(send_chunk) # 固定为1024 + if hasattr(self.spi_obj, 'write'): + self.spi_obj.write(send_chunk, chunk_len) + utime.sleep_ms(5) + elif hasattr(self.spi_obj, 'write_read'): + read_buf = bytearray(chunk_len) + try: + self.spi_obj.write_read(read_buf, send_chunk, chunk_len, 5) + except TypeError: + self.spi_obj.write_read(read_buf, send_chunk, chunk_len) + else: + clog.error("SPI object does not have write or write_read method") + return False, 0.0, -1 + + downloaded_size += len(send_chunk) + + # 显示进度(每发送 64KB 打印一次进度) + if total_size > 0: + progress = (downloaded_size / total_size) * 100 + if downloaded_size % (64 * 1024) < 1024: + clog.info("SPI TX Progress: {:.2f}% ({}/{}) bytes (TX: 1024 bytes, total padded zeros: {})".format( + progress, downloaded_size, total_size, total_padded_zeros)) + + # 保存剩余不足1024字节的数据 + remaining_data = combined_data - # 显示进度(每发送一定数据量后打印一次) - if total_size > 0: - progress = (downloaded_size / total_size) * 100 - # 每发送 64KB 打印一次进度 - if downloaded_size % (64 * 1024) < self.sizeof: - clog.info("Progress: {:.2f}% ({}/{}) bytes".format( - progress, downloaded_size, total_size)) except Exception as e: clog.error("Error sending data via SPI: {}".format(e)) return False, 0.0, -1 + # 处理最后不足1024字节的剩余数据(拼接空字节,完全避开bytearray下标赋值) + if len(remaining_data) > 0: + try: + # 计算需要补的空字节数 + pad_len = 1024 - len(remaining_data) + total_padded_zeros += pad_len # 累计补0字节数 + # 拼接空字节(直接生成pad_len个0字节的bytes) + send_chunk = remaining_data + b'\x00' * pad_len + + gpioWakeBT.write(0) + utime.sleep_ms(2) + gpioWakeBT.write(1) + utime.sleep_ms(2) + + if hasattr(self.spi_obj, 'write'): + self.spi_obj.write(send_chunk, 1024) + utime.sleep_ms(5) + elif hasattr(self.spi_obj, 'write_read'): + read_buf = bytearray(1024) + try: + self.spi_obj.write_read(read_buf, send_chunk, 1024, 5) + except TypeError: + self.spi_obj.write_read(read_buf, send_chunk, 1024) + + downloaded_size += len(remaining_data) # 只统计实际数据长度 + clog.info("=== Final SPI Chunk Info ===") + clog.info("Actual data bytes: {}".format(len(remaining_data))) + clog.info("Padded zero bytes: {}".format(pad_len)) + clog.info("Total padded zeros in whole transmission: {}".format(total_padded_zeros)) + clog.info("============================") + + # 最后一块进度显示 + if total_size > 0: + progress = (downloaded_size / total_size) * 100 + clog.info("SPI TX Progress: {:.2f}% ({}/{}) bytes (TX: 1024 bytes, final chunk, total padded zeros: {})".format( + progress, downloaded_size, total_size, total_padded_zeros)) + except Exception as e: + clog.error("Error sending final SPI chunk: {}".format(e)) + return False, 0.0, -1 + # 记录结束时间并计算耗时 end_time = utime.time() download_time = end_time - start_time - clog.info("Download and send via SPI completed: {} bytes in {:.3f}s".format( - downloaded_size, download_time)) - - # 注意:TCP服务器模式下,SPI对象在服务器运行期间保持打开,不在这里关闭 - # 只有在服务器停止时才关闭SPI + # 打印整体传输统计 + clog.info("=== SPI Transmission Summary ===") + clog.info("Total actual data sent: {} bytes".format(downloaded_size)) + clog.info("Total padded zero bytes: {} bytes".format(total_padded_zeros)) + clog.info("Total SPI packets sent: {} (each 1024 bytes)".format((downloaded_size + total_padded_zeros) // 1024)) + clog.info("Transmission time: {:.3f} seconds".format(download_time)) + clog.info("================================") return True, download_time, downloaded_size @@ -563,8 +630,8 @@ class HttpFileDownloadSpiTest: # 默认创建SPI(2, 0, 1): 通道2, 模式0, 时钟频率1(26MHz) # 注意:Python端SPI2对应MCU端SPI1 # 频率参数1表示26MHz(根据实际SPI驱动实现) - self.spi_obj = SPI(2, 0, 8) - clog.info("Created default SPI object: SPI(2, 0, 8) for 26MHz") + self.spi_obj = SPI(2, 0, 12) + clog.info("Created default SPI object: SPI(2, 0, 12) for 26MHz") else: self.spi_obj = spi_obj clog.info("Using provided SPI object") @@ -628,46 +695,107 @@ class HttpFileDownloadSpiTest: # 下载数据并通过SPI发送 downloaded_size = 0 + # 缓存未发送的剩余数据 + remaining_data = b'' + # 累计补0字节数 + total_padded_zeros = 0 # response.content是bytes类型生成器 for chunk in response.content: if chunk: # 过滤掉空块 try: - # 通过SPI发送数据块 - # SPI API: write(data, datasize) 或 write_read(read_buf, write_data, datasize, timeout) - chunk_len = len(chunk) - if hasattr(self.spi_obj, 'write'): - # write(data, datasize) - datasize 参数必需 - self.spi_obj.write(chunk, chunk_len) - elif hasattr(self.spi_obj, 'write_read'): - # write_read(read_buf, write_data, datasize, timeout) - # 或者 write_read(read_buf, write_data, datasize) - read_buf = bytearray(chunk_len) - try: - # 尝试带timeout的调用 - self.spi_obj.write_read(read_buf, chunk, chunk_len, 5) - except TypeError: - # 尝试不带timeout的调用 - self.spi_obj.write_read(read_buf, chunk, chunk_len) - else: - clog.error("SPI object does not have write or write_read method") - self.close() # 关闭SPI - return False, 0.0, -1 + # 合并剩余数据和当前chunk + combined_data = remaining_data + chunk + remaining_data = b'' - downloaded_size += len(chunk) + # 拆分数据为1024字节的块 + while len(combined_data) >= 1024: + # 取前1024字节 + send_chunk = combined_data[:1024] + # 剩余数据保留 + combined_data = combined_data[1024:] + + gpioWakeBT.write(0) + utime.sleep_ms(2) + gpioWakeBT.write(1) + utime.sleep_ms(2) + + # 通过SPI发送1024字节数据块 + chunk_len = len(send_chunk) # 固定为1024 + if hasattr(self.spi_obj, 'write'): + self.spi_obj.write(send_chunk, chunk_len) + utime.sleep_ms(5) + elif hasattr(self.spi_obj, 'write_read'): + read_buf = bytearray(chunk_len) + try: + self.spi_obj.write_read(read_buf, send_chunk, chunk_len, 5) + except TypeError: + self.spi_obj.write_read(read_buf, send_chunk, chunk_len) + else: + clog.error("SPI object does not have write or write_read method") + self.close() # 关闭SPI + return False, 0.0, -1 + + downloaded_size += len(send_chunk) + + # 显示进度(每发送64KB打印一次,减少打印数量) + if total_size > 0: + progress = (downloaded_size / total_size) * 100 + # 每发送64KB打印一次进度(64*1024=65536) + if downloaded_size % (64 * 1024) < 1024: + clog.info("SPI TX Progress: {:.2f}% ({}/{}) bytes (TX: 1024 bytes, total padded zeros: {})".format( + progress, downloaded_size, total_size, total_padded_zeros)) + + # 保存剩余不足1024字节的数据 + remaining_data = combined_data - # 显示进度(每发送64KB打印一次,减少打印数量) - if total_size > 0: - progress = (downloaded_size / total_size) * 100 - # 每发送64KB打印一次进度 - if downloaded_size % (64 * 1024) < self.sizeof: - clog.info("SPI TX Progress: {:.2f}% ({}/{}) bytes".format( - progress, downloaded_size, total_size)) except Exception as e: clog.error("Error sending data via SPI: {}".format(e)) self.close() # 关闭SPI return False, 0.0, -1 + # 处理最后不足1024字节的剩余数据(拼接空字节,完全避开bytearray下标赋值) + if len(remaining_data) > 0: + try: + # 计算需要补的空字节数 + pad_len = 1024 - len(remaining_data) + total_padded_zeros += pad_len # 累计补0字节数 + # 直接拼接空字节(b'\x00' * pad_len),无需bytearray + send_chunk = remaining_data + (b'\x00' * pad_len) + + gpioWakeBT.write(0) + utime.sleep_ms(2) + gpioWakeBT.write(1) + utime.sleep_ms(2) + + if hasattr(self.spi_obj, 'write'): + self.spi_obj.write(send_chunk, 1024) + utime.sleep_ms(5) + elif hasattr(self.spi_obj, 'write_read'): + read_buf = bytearray(1024) + try: + self.spi_obj.write_read(read_buf, send_chunk, 1024, 5) + except TypeError: + self.spi_obj.write_read(read_buf, send_chunk, 1024) + + downloaded_size += len(remaining_data) # 只统计实际数据长度 + # 打印最后一块补0详情 + clog.info("=== Final SPI Chunk Detail ===") + clog.info("Last chunk actual data: {} bytes".format(len(remaining_data))) + clog.info("Last chunk padded zeros: {} bytes".format(pad_len)) + clog.info("Total padded zeros in transmission: {} bytes".format(total_padded_zeros)) + clog.info("==============================") + + # 最后一块进度显示 + if total_size > 0: + progress = (downloaded_size / total_size) * 100 + clog.info("SPI TX Progress: {:.2f}% ({}/{}) bytes (TX: 1024 bytes, final chunk, total padded zeros: {})".format( + progress, downloaded_size, total_size, total_padded_zeros)) + except Exception as e: + clog.error("Error sending final SPI chunk: {}".format(e)) + self.close() # 关闭SPI + return False, 0.0, -1 + # 记录结束时间并计算耗时 end_time = utime.time() download_time = end_time - start_time @@ -676,12 +804,17 @@ class HttpFileDownloadSpiTest: speed_kbps = (downloaded_size * 8.0) / download_time / 1024.0 # Kbps speed_mbps = speed_kbps / 1024.0 # Mbps + # 打印整体传输统计信息 + clog.info("=== SPI Transmission Complete Summary ===") + clog.info("Total actual data transmitted: {} bytes ({:.2f} MB)".format(downloaded_size, downloaded_size/1024/1024)) + clog.info("Total padded zero bytes: {} bytes ({:.2f} KB)".format(total_padded_zeros, total_padded_zeros/1024)) + clog.info("Total SPI packets sent: {} (each 1024 bytes)".format((downloaded_size + total_padded_zeros) // 1024)) + clog.info("Total transmission time: {:.3f} seconds".format(download_time)) if speed_mbps >= 1.0: - clog.info("SPI speed test completed: {} bytes in {:.3f}s, Speed: {:.2f} Mbps ({:.2f} Kbps)".format( - downloaded_size, download_time, speed_mbps, speed_kbps)) + clog.info("Effective transmission speed: {:.2f} Mbps ({:.2f} Kbps)".format(speed_mbps, speed_kbps)) else: - clog.info("SPI speed test completed: {} bytes in {:.3f}s, Speed: {:.2f} Kbps".format( - downloaded_size, download_time, speed_kbps)) + clog.info("Effective transmission speed: {:.2f} Kbps".format(speed_kbps)) + clog.info("==========================================") # 传输完成后关闭SPI self.close() diff --git a/code/sdk/zhihui/utils/custom_spi.c b/code/sdk/zhihui/utils/custom_spi.c index 4bc69a452..f8d64b180 100644 --- a/code/sdk/zhihui/utils/custom_spi.c +++ b/code/sdk/zhihui/utils/custom_spi.c @@ -44,10 +44,12 @@ void custom_spi_read(void) ret = uapi_spi_slave_read(SPI_BUS_1, &spi_data, 50); - if(ret == 0 && spi_data.rx_bytes == 1024) + printf("total_cnt:%d,spi_data.rx_bytes:%d\n",total_cnt,spi_data.rx_bytes); + + if(ret == 0) { total_cnt += spi_data.rx_bytes; - printf("total_cnt:%d\n",total_cnt); + printf("ret:%x,total_cnt:%d,spi_data.rx_bytes:%d\n",ret,total_cnt,spi_data.rx_bytes); // printf("spi_data.rx_buff[0]:0x%x,total_cnt:%d\n",spi_data.rx_buff[0],total_cnt); // printf("spi_data.rx_bytes : %d, spi_data.rx_data[0]:0x%02X,spi_data.rx_data[1023]:0x%02X\n", // spi_data.rx_bytes, current_seq, spi_data.rx_buff[1023]); @@ -56,41 +58,44 @@ void custom_spi_read(void) pkg_recv_cnt++; // --------------- 逐字节严格递增判断(无跳过,首个包就校验)--------------- - uint8_t byte_error_cnt = 0; // 单包内字节错误计数 - uint8_t prev_byte = spi_data.rx_buff[0]; // 初始化为第一个字节 - // printf("head\n"); - // 遍历1024字节(从第2个字节开始校验) - for (int i = 1; i < 1024; i++) - { - // if(i < 20) - // printf("(0x%02X)",spi_data.rx_buff[i]); - // if(i == 20) - // printf("\n"); - uint8_t curr_byte = spi_data.rx_buff[i]; - // 8位字节递增规则:0xFF后应为0x00,其余为前一字节+1 - uint8_t expected_byte = (prev_byte == 0xFF) ? 0x00 : (prev_byte + 1); + // uint8_t byte_error_cnt = 0; // 单包内字节错误计数 + // uint8_t prev_byte = spi_data.rx_buff[0]; // 初始化为第一个字节 + // // printf("head\n"); + // // 遍历1024字节(从第2个字节开始校验) + // for (int i = 1; i < 1024; i++) + // { + // // if(i < 20) + // // printf("(0x%02X)",spi_data.rx_buff[i]); + // // if(i == 20) + // // printf("\n"); + // uint8_t curr_byte = spi_data.rx_buff[i]; + // // 8位字节递增规则:0xFF后应为0x00,其余为前一字节+1 + // uint8_t expected_byte = (prev_byte == 0xFF) ? 0x00 : (prev_byte + 1); - // 校验当前字节是否等于期望的递增字节(无跳过) - if (curr_byte != expected_byte) - { - byte_error_cnt++; - // 打印错误详情(调试用,正式环境可注释) - printf("字节[%d]不连续:当前0x%02X, 期望0x%02X(前一字节0x%02X)\n", - i, curr_byte, expected_byte, prev_byte); - } + // // 校验当前字节是否等于期望的递增字节(无跳过) + // if (curr_byte != expected_byte) + // { + // byte_error_cnt++; + // // 打印错误详情(调试用,正式环境可注释) + // printf("字节[%d]不连续:当前0x%02X, 期望0x%02X(前一字节0x%02X)\n", + // i, curr_byte, expected_byte, prev_byte); + // } - // 更新前一字节,用于下一轮校验 - prev_byte = curr_byte; - } + // // 更新前一字节,用于下一轮校验 + // prev_byte = curr_byte; + // } // printf("该包无错误\n"); - // 单包字节校验结果汇总(首个包就统计) - if (byte_error_cnt > 0) - { - printf("【包%d】字节递增错误数:%d | 累计字节错误数:%d\n", - pkg_recv_cnt, byte_error_cnt, total_byte_error_cnt += byte_error_cnt); - } + // // 单包字节校验结果汇总(首个包就统计) + // if (byte_error_cnt > 0) + // { + // printf("【包%d】字节递增错误数:%d | 累计字节错误数:%d\n", + // pkg_recv_cnt, byte_error_cnt, total_byte_error_cnt += byte_error_cnt); + // } + } + else + { + printf("error ret:%x,spi_data.rx_bytes:%d\n",ret,spi_data.rx_bytes); } - // else // { // // 新增:包计数自增(失败/长度不符的包也计数,若仅统计成功包可移到上面if内) -- 2.17.1