更新CUSTOM SPI测试代码
authorpenglei <1624952103@qq.com>
Fri, 5 Dec 2025 06:49:31 +0000 (14:49 +0800)
committerpenglei <1624952103@qq.com>
Fri, 5 Dec 2025 06:49:31 +0000 (14:49 +0800)
cat1modem/src/LeiNiao/spi_write_test.py
code/sdk/zhihui/utils/custom_spi.c

index 76c528a..9afa115 100644 (file)
 #!/usr/bin/env python3
 # -*- coding: utf-8 -*-
 """
-SPI 写操作测试模块
-连续写2MB递增数据,仅当收到外部中断时发送数据块,用于测试SPI传输完整性和数据丢失检测
+SPI 写操作测试模块(保留原始时序版)
+核心:严格保留原有延时/时序 + 适配datasize参数 + 解决内存/赋值报错
 """
 
 import log
-
-# 设置日志输出级别
-log.basicConfig(level=log.INFO)
-clog = log.getLogger("spiwritetest")
-
 import utime
 from machine import SPI
 from machine import Pin
 from machine import ExtInt
 
-# SPI配置说明:
-# spi - 通道选择[0,1],int类型
-# mode - SPI 的工作模式,int类型:
-#   0: CPOL=0, CPHA=0
-#   1: CPOL=0, CPHA=1
-#   2: CPOL=1, CPHA=0
-#   3: CPOL=1, CPHA=1
-# clk - 时钟频率:
-#   0: 100kHz
-#   1: 200kHz
-#   8: 1.625MHz
-#   9: 3.25MHz
-#   12: 26MHz
+# 设置日志输出级别
+log.basicConfig(level=log.INFO)
+clog = log.getLogger("spiwritetest")
 
-# 测试参数
-TEST_DATA_SIZE = 2 * 1024 * 1024  # 2MB
-CHUNK_SIZE = 1024  # 每次发送1KB
-SPI_CHANNEL = 2  # SPI通道2(对应MCU端SPI1)
-SPI_MODE = 0  # Mode 0
-SPI_CLK = 8  # 1.625MHz(可根据需要调整:8=1.625MHz, 9=3.25MHz, 12=26MHz)
+# 测试参数(完全保留原始值)
+TEST_DATA_SIZE = 2 * 1024 * 1024  # 2MB总数据量
+PACKET_TOTAL_SIZE = 1024          # 每包总长度严格1024字节
+HEADER_SIZE = 1                   # 1字节序号头
+DATA_BODY_SIZE = PACKET_TOTAL_SIZE - HEADER_SIZE  # 1023字节数据体
+SPI_CHANNEL = 2                   # SPI通道2
+SPI_MODE = 0                      # Mode 0
+SPI_CLK = 8                       # 1.625MHz
 
-# å\85¨å±\80中æ\96­æ \87å¿\97ä½\8dï¼\9aæ \87è®°æ\98¯å\90¦æ\94¶å\88°ä¸­æ\96­ï¼\88ä»\85中æ\96­å\9b\9eè°\83å\8f¯ç½®ä½\8dï¼\8c主é\80»è¾\91å¤\8dä½\8d)
+# å\85¨å±\80中æ\96­æ \87å¿\97ä½\8dï¼\88ä¿\9dç\95\99å\8e\9få§\8b)
 interrupt_triggered = False
-# 中断等待超时时间(单位:秒),避免无限等待
-INTERRUPT_TIMEOUT = 60  
+INTERRUPT_TIMEOUT = 60            # 中断等待超时时间(秒)
 
+# SPI接收缓存(保留原始)
+recv_buf = bytearray(1)           # 通用接收缓存
+slave_feedback_buf = bytearray(1) # 从机反馈缓存(仅1字节:需要重发的序号)
 
-# ä¸­æ\96­å\9b\9eè°\83å\87½æ\95°ï¼\9aæ\94¶å\88°ä¸­æ\96­æ\97¶ç½®ä½\8dæ \87å¿\97ä½\8d
+# ä¸­æ\96­å\9b\9eè°\83å\87½æ\95°ï¼\88å®\8cå\85¨ä¿\9dç\95\99å\8e\9få§\8bé\80»è¾\91ï¼\89
 def ExtIntfun(args):
     global interrupt_triggered
-    # clog.info('### interrupt  {} ###'.format(args)) # args[0]:gpio号 args[1]:上升沿或下降沿
     if args[1] == 0:
         interrupt_triggered = True
-        clog.info("interrupt_triggered = True")
-            # pm.autosleep(0)
-            # pm.wakelock_lock(lpm_fd)
-        # elif args[1] == 1:
-            # clog.info("enable autosleep")
-            # pm.wakelock_unlock(lpm_fd)
-            # pm.autosleep(1)
-        # clog.info("recvDataPin={}".format(recvDataPin.read()))
+        clog.info("interrupt_triggered = True (need read slave data)")
 
-# 初始化外部中断(GPIO3,上升/下降沿触发,无上下拉)
+# 初始化外部中断(保留原始时序/配置)
 extint = ExtInt(ExtInt.GPIO3, ExtInt.IRQ_RISING_FALLING, ExtInt.PULL_DISABLE, ExtIntfun)
 extint.enable()
 clog.info("External interrupt initialized: GPIO3, RISING_FALLING edge")
 
-# å\88\9b建gpio对象ï¼\88å\94¤é\86\92MCUç\9a\84å¼\95è\84\9aï¼\8cGPIO4ï¼\8cè¾\93å\87ºæ¨¡å¼\8fï¼\8cå\88\9då§\8bé«\98ç\94µå¹³)
+# å\94¤é\86\92MCUå¼\95è\84\9aï¼\88å®\8cå\85¨ä¿\9dç\95\99å\8e\9få§\8bé\85\8dç½®/æ\97¶åº\8f)
 gpioWakeBT = Pin(Pin.GPIO4, Pin.OUT, Pin.PULL_DISABLE, 1)
-# 设置输入输出模式
 gpioWakeBT.set_dir(Pin.OUT)
-
-# 设置引脚电平
 gpioWakeBT.write(1)
 
 class SpiWriteTest:
-    """
-    SPI写操作测试类
-    生成递增数据并通过SPI发送,用于测试数据完整性
-    """
+    """SPI写操作测试类(严格保留原始时序)"""
     
-    def __init__(self, data_size=TEST_DATA_SIZE, chunk_size=CHUNK_SIZE, 
+    def __init__(self, total_data_size=TEST_DATA_SIZE, packet_size=PACKET_TOTAL_SIZE,
                  spi_channel=SPI_CHANNEL, spi_mode=SPI_MODE, spi_clk=SPI_CLK):
-        """
-        初始化SPI写测试
-        
-        Args:
-            data_size: 要发送的数据总大小(字节),默认2MB
-            chunk_size: 每次发送的数据块大小(字节),默认1KB
-            spi_channel: SPI通道,默认2(对应MCU端SPI1)
-            spi_mode: SPI模式,默认0
-            spi_clk: SPI时钟频率,默认8(1.625MHz)
-        """
-        self.data_size = data_size
-        self.chunk_size = chunk_size
+        self.total_data_size = total_data_size
+        self.packet_size = packet_size
+        self.header_size = HEADER_SIZE
+        self.data_body_size = DATA_BODY_SIZE
         self.spi_channel = spi_channel
         self.spi_mode = spi_mode
         self.spi_clk = spi_clk
         self.spi_obj = None
-        self.current_value = 0  # 当前数据值(从0开始递增)
-    
+        
+        # 包缓存区:key=序号, value=完整包数据(1024字节)
+        self.packet_cache = {}
+        self.retrans_count = 0   # 总重传次数
+        
+        # 发送控制(保留原始)
+        self.current_seq = 0
+        self.current_data_start = 0 
+        self.packet_count = 0
+        self.total_sent_bytes = 0
+
     def __init_spi(self):
-        """初始化SPI对象"""
+        """初始化SPI对象(保留原始逻辑)"""
         try:
             self.spi_obj = SPI(self.spi_channel, self.spi_mode, self.spi_clk)
+            if hasattr(self.spi_obj, 'set_full_duplex'):
+                self.spi_obj.set_full_duplex(True)
             clog.info("SPI initialized: channel={}, mode={}, clk={}".format(
                 self.spi_channel, self.spi_mode, self.spi_clk))
             return True
@@ -108,327 +86,172 @@ class SpiWriteTest:
             clog.error("Failed to initialize SPI: {}".format(e))
             return False
     
-    def __generate_chunk(self, start_value, chunk_size):
-        """
-        生成递增数据块
-        
-        Args:
-            start_value: 起始值(字节值,0-255)
-            chunk_size: 数据块大小(字节)
-            
-        Returns:
-            bytearray: 递增数据块
-        """
-        chunk = bytearray(chunk_size)
+    def __generate_data_body(self, start_value):
+        """生成1023字节数据体(保留原始逻辑,解决赋值报错)"""
+        # 改为直接构建bytes,避免bytearray赋值
+        data_body_list = []
         value = start_value
-        for i in range(chunk_size):
-            chunk[i] = value & 0xFF  # 只取低8位
-            value = (value + 1) % 256  # 递增,超过255后回到0
-        return chunk
+        for i in range(self.data_body_size):
+            data_body_list.append(value & 0xFF)
+            value = (value + 1) % 256
+        return bytes(data_body_list)
     
-    def __send_chunk(self, chunk):
-        """
-        通过SPI发送数据块
-        
-        Args:
-            chunk: 要发送的数据块(bytearray或bytes)
-            
-        Returns:
-            bool: 发送是否成功
-        """
-        gpioWakeBT.write(0)
-        # clog.info("gpioWakeBT.write(0)")
-        utime.sleep_ms(2)
-        gpioWakeBT.write(1)
+    def __build_packet(self, seq, start_value):
+        """构建1024字节完整包并缓存(保留原始逻辑)"""
+        # 1字节序号头
+        header = bytes([seq & 0xFF])
+        # 1023字节数据体
+        data_body = self.__generate_data_body(start_value)
+        # 拼接成1024字节总包
+        packet = header + data_body
+        # 缓存包(保留原始缓存逻辑)
+        self.packet_cache[seq] = packet
+        return packet
+    
+    def __send_packet(self, packet, seq):
+        """发送指定包(仅修复datasize参数,严格保留原始时序)"""
         try:
+            # 严格保留原始时序:write(0)→2ms→write(1)→10ms
+            gpioWakeBT.write(0)
+            utime.sleep_ms(2)
+            gpioWakeBT.write(1)
+            utime.sleep_ms(10)
+            
             if self.spi_obj is None:
-                clog.error("SPI object is None, cannot send chunk")
-                return False
-                
-            chunk_len = len(chunk)
-            if hasattr(self.spi_obj, 'write'):
-
-                # write(data, datasize) - datasize 参数必需
-                clog.debug("Calling SPI.write() with {} bytes".format(chunk_len))
-                self.spi_obj.write(chunk, chunk_len)
-                clog.debug("SPI.write() completed successfully")
-            # 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
+            
+            # 核心修复:显式传入datasize参数,保留原始发送逻辑
+            self.spi_obj.write(packet, len(packet))
+            clog.info("reply  seq {} (1024 bytes total)".format(seq))
             return True
         except Exception as e:
-            clog.error("Error sending chunk via SPI: {}".format(e))
-            import sys
-            clog.error("Exception type: {}, details: {}".format(type(e).__name__, str(e)))
+            clog.error("send seq {} fail: {}".format(seq, e))
             return False
     
+    def __read_slave_feedback(self):
+        """中断触发时读取从机反馈(保留原始时序/逻辑)"""
+        global slave_feedback_buf, interrupt_triggered
+        try:
+            if self.spi_obj is None:
+                return
+            
+            # 保留原始读取逻辑,补充datasize参数
+            self.spi_obj.read(slave_feedback_buf, len(slave_feedback_buf))
+            retrans_seq = slave_feedback_buf[0]
+            
+            # 保留原始重发逻辑
+            if retrans_seq in self.packet_cache:
+                self.retrans_count += 1
+                clog.warning("Slave send NACK, need retransmit seq {}".format(retrans_seq))
+                # 严格保留原始重发时序/逻辑
+                self.__send_packet(self.packet_cache[retrans_seq], retrans_seq)
+            else:
+                clog.warning("seq {} not in cache, skip retrans".format(retrans_seq))
+                
+            interrupt_triggered = False
+        except Exception as e:
+            clog.error("Read slave feedback failed: {}".format(e))
+            interrupt_triggered = False
+    
     def close(self):
-        """关闭SPI对象,释放资源"""
+        """关闭SPI(保留原始逻辑)"""
         if self.spi_obj is not None:
             try:
                 if hasattr(self.spi_obj, 'close'):
                     self.spi_obj.close()
-                    clog.info("SPI object closed via close() method")
                 elif hasattr(self.spi_obj, 'deinit'):
                     self.spi_obj.deinit()
-                    clog.info("SPI object closed via deinit() method")
-                else:
-                    clog.warning("SPI object does not have close() or deinit() method")
             except Exception as e:
-                clog.error("Error closing SPI object: {}".format(e))
+                clog.error("Close SPI failed: {}".format(e))
             finally:
                 self.spi_obj = None
-    
-    def __wait_for_interrupt(self, timeout_seconds=INTERRUPT_TIMEOUT):
-        """
-        等待中断信号
-        
-        Args:
-            timeout_seconds: 超时时间(秒),默认使用全局 INTERRUPT_TIMEOUT
-            
-        Returns:
-            bool: 是否收到中断(True表示收到,False表示超时)
-        """
-        global interrupt_triggered
-        
-        # 先检查是否已经有中断信号(可能在调用此函数之前就已经触发了)
-        if interrupt_triggered:
-            clog.info("Interrupt signal already present, consuming it immediately")
-            interrupt_triggered = False  # 消费掉这个中断信号
-            return True
-        
-        # 重置中断标志,准备等待新的中断
-        interrupt_triggered = False
-        
-        # 等待中断,带超时机制
-        start_wait_time = utime.time()
-        check_count = 0
-        while not interrupt_triggered:
-            elapsed = utime.time() - start_wait_time
-            if elapsed >= timeout_seconds:
-                clog.warning("Interrupt wait timeout after {:.2f} seconds (checked {} times)".format(
-                    elapsed, check_count))
-                return False
-            try:
-                utime.sleep_ms(5)  # 每10ms检查一次,减少CPU占用
-            except Exception as e:
-                clog.error("Error in utime.sleep_ms: {}".format(e))
-                return False
-            check_count += 1
-            # 每1秒打印一次等待状态
-            if check_count % 100 == 0:
-                clog.info("Still waiting for interrupt... (elapsed: {:.2f}s)".format(elapsed))
-        
-        clog.info("Interrupt received after {:.2f} seconds (checked {} times)".format(
-            utime.time() - start_wait_time, check_count))
-        # 重置标志,为下次等待做准备
-        interrupt_triggered = False
-        return True
-    
+
     def run_test(self):
-        """
-        运行SPI写测试
-        首次发送不等待中断,后续每次发送前等待中断信号
-        
-        Returns:
-            tuple: (success: bool, send_time: float, total_bytes: int, data_pattern_info: dict)
-                success: 测试是否成功
-                send_time: 发送耗时(秒)
-                total_bytes: 发送的总字节数
-                data_pattern_info: 数据模式信息(用于MCU端验证)
-        """
+        """运行测试(严格保留原始时序/逻辑,仅修复报错)"""
         global interrupt_triggered
-        
         try:
-            # 初始化SPI
             if not self.__init_spi():
                 return False, 0.0, 0, {}
             
-            clog.info("Starting SPI write test")
-            clog.info("Test parameters:")
-            clog.info("  Total data size: {} bytes ({:.2f} MB)".format(
-                self.data_size, self.data_size / 1024.0 / 1024.0))
-            clog.info("  Chunk size: {} bytes".format(self.chunk_size))
-            clog.info("  SPI channel: {}, mode: {}, clk: {}".format(
-                self.spi_channel, self.spi_mode, self.spi_clk))
-            clog.info("  Delay between chunks: 5ms (to reduce SPI pressure)")
-            clog.info("  Data pattern: Incremental bytes (0, 1, 2, ..., 255, 0, 1, ...)")
+            # 保留原始日志
+            clog.info("=== SPI Write Test Start (1024B/packet: 1B seq + 1023B data) ===")
+            clog.info("Total data size: {} bytes ({:.2f} MB)".format(
+                self.total_data_size, self.total_data_size / 1024 / 1024))
+            clog.info("Packet size: {} bytes (1B seq header + 1023B data body)".format(self.packet_size))
             
-            # 记录开始时间
             start_time = utime.time()
             
-            # 发送数据
-            total_sent = 0
-            chunk_count = 0
-            current_byte_value = 0  # 当前字节值(0-255循环)
-            
-            # 计算需要发送的块数
-            total_chunks = (self.data_size + self.chunk_size - 1) // self.chunk_size
-            
-            clog.info("Starting to send {} chunks...".format(total_chunks))
-            
-            while total_sent < self.data_size:
-                # 计算剩余数据量
-                remaining_bytes = self.data_size - total_sent
-                remaining_chunks = (remaining_bytes + self.chunk_size - 1) // self.chunk_size
+            # 保留原始发送逻辑
+            while self.total_sent_bytes < self.total_data_size:
+                clog.info("Sending packet {} (seq {})".format(self.packet_count, self.current_seq))
                 
-                # 首次发送不等待中断,后续每次发送前等待中断
-                if chunk_count == 0:
-                    # 首次发送,不等待中断
-                    clog.info("Sending first chunk (chunk_count=0) without waiting for interrupt")
-                else:
-                    # 后续发送,等待中断信号
-                    clog.info("Waiting for interrupt before sending chunk {} (remaining: {} bytes, {} chunks)...".format(
-                        chunk_count, remaining_bytes, remaining_chunks))
-                    if not self.__wait_for_interrupt():
-                        clog.error("Interrupt wait timeout at chunk {} (sent: {} bytes, remaining: {} bytes)".format(
-                            chunk_count, total_sent, remaining_bytes))
-                        self.close()
-                        return False, 0.0, total_sent, {}
-                    clog.info("Interrupt received, sending chunk {}".format(chunk_count))
+                # 构建当前包(保留原始逻辑)
+                packet = self.__build_packet(self.current_seq, self.current_data_start)
+                # 缓存上一包(保留原始逻辑)
+                self.packet_cache[self.current_seq] = packet
                 
-                # 计算本次发送的块大小(最后一块可能小于chunk_size)
-                remaining = self.data_size - total_sent
-                current_chunk_size = min(self.chunk_size, remaining)
+                # 发送包(保留原始时序/逻辑)
+                send_ok = self.__send_packet(packet, self.current_seq)
                 
-                # 生成递增数据块
-                chunk = self.__generate_chunk(current_byte_value, current_chunk_size)
+                # 保留原始中断处理逻辑
+                if interrupt_triggered:
+                    clog.info("Interrupt triggered, read slave feedback...")
+                    self.__read_slave_feedback()  # 读取从机,为主机提供CLK
+                    interrupt_triggered = False  # 重置中断标志
                 
-                # 更新当前字节值(为下一块准备)
-                current_byte_value = (current_byte_value + current_chunk_size) % 256
-                
-                # 通过SPI发送数据块
-                if not self.__send_chunk(chunk):
-                    clog.error("Failed to send chunk {} at offset {}".format(
-                        chunk_count, total_sent))
+                if not send_ok:
+                    clog.error("Send packet {} (seq {}) failed".format(self.packet_count, self.current_seq))
                     self.close()
-                    return False, 0.0, total_sent, {}
+                    return False, 0.0, self.total_sent_bytes, {}
                 
-                total_sent += current_chunk_size
-                chunk_count += 1
+                # 保留原始统计更新逻辑
+                self.total_sent_bytes += self.packet_size  # 按1024字节/包统计
+                self.current_data_start = (self.current_data_start + self.data_body_size) % 256  # 数据体起始值递增
+                self.current_seq = (self.current_seq + 1) % 256  # 序号循环
+                self.packet_count += 1
                 
-                # 每次发送后延迟5ms,减少SPI压力,避免FIFO溢出
-                utime.sleep_ms(5)
+                # 保留原始进度打印逻辑
+                if self.packet_count % 64 == 0:
+                    progress = (self.total_sent_bytes / self.total_data_size) * 100
+                    clog.info("Progress: {:.2f}% | Packets sent: {} | Total bytes: {} | Retrans: {} times".format(
+                        progress, self.packet_count, self.total_sent_bytes, self.retrans_count))
                 
-                # 每发送64KB打印一次进度
-                if total_sent % (64 * 1024) < self.chunk_size:
-                    progress = (total_sent / self.data_size) * 100
-                    clog.info("SPI TX Progress: {:.2f}% ({}/{}) bytes, chunks: {}".format(
-                        progress, total_sent, self.data_size, chunk_count))
+                # 保留原始延时
+                utime.sleep_ms(5)
             
-            # 记录结束时间并计算耗时
+            # 保留原始测试完成逻辑
             end_time = utime.time()
             send_time = end_time - start_time
+            speed_kbps = (self.total_sent_bytes * 8) / send_time / 1024 if send_time > 0 else 0
             
-            # 计算传输速度
-            speed_kbps = (total_sent * 8.0) / send_time / 1024.0  # Kbps
-            speed_mbps = speed_kbps / 1024.0  # Mbps
-            
-            # 数据模式信息(用于MCU端验证)
-            data_pattern_info = {
-                'total_bytes': total_sent,
-                'chunk_size': self.chunk_size,
-                'pattern': 'incremental',  # 递增模式
-                'start_value': 0,  # 起始值为0
-                'cycle': 256,  # 每256字节循环一次(0-255)
-                'description': 'Data values increment from 0 to 255, then cycle back to 0'
-            }
+            clog.info("=== Test Completed ===")
+            clog.info("Total packets sent: {} ({} bytes total)".format(
+                self.packet_count, self.total_sent_bytes))
+            clog.info("Send time: {:.3f}s | Speed: {:.2f} Kbps".format(send_time, speed_kbps))
+            clog.info("Total retrans times: {}".format(self.retrans_count))
             
-            if speed_mbps >= 1.0:
-                clog.info("SPI write test completed: {} bytes in {:.3f}s, Speed: {:.2f} Mbps ({:.2f} Kbps)".format(
-                    total_sent, send_time, speed_mbps, speed_kbps))
-            else:
-                clog.info("SPI write test completed: {} bytes in {:.3f}s, Speed: {:.2f} Kbps".format(
-                    total_sent, send_time, speed_kbps))
-            
-            clog.info("Data pattern info:")
-            clog.info("  Pattern: Incremental bytes (0, 1, 2, ..., 255, 0, 1, ...)")
-            clog.info("  Start value: 0")
-            clog.info("  Cycle: Every 256 bytes")
-            clog.info("  Total chunks: {}".format(chunk_count))
-            clog.info("  Expected byte at offset N: (N % 256)")
-            
-            # 关闭SPI
             self.close()
-            
-            return True, send_time, total_sent, data_pattern_info
-            
+            return True, send_time, self.total_sent_bytes, {
+                "packet_size": self.packet_size,
+                "header_size": self.header_size,
+                "data_body_size": self.data_body_size,
+                "retrans_count": self.retrans_count
+            }
+        
         except Exception as e:
-            clog.error("Error in SPI write test: {}".format(e))
+            clog.error("Test failed: {}".format(e))
             self.close()
             return False, 0.0, 0, {}
-    
-    def get_expected_byte_at_offset(self, offset):
-        """
-        获取指定偏移位置的期望字节值(用于MCU端验证)
-        
-        Args:
-            offset: 字节偏移位置(0开始)
-            
-        Returns:
-            int: 期望的字节值(0-255)
-        """
-        return offset % 256
 
-
-def run_spi_write_test(data_size=TEST_DATA_SIZE, chunk_size=CHUNK_SIZE,
-                       spi_channel=SPI_CHANNEL, spi_mode=SPI_MODE, spi_clk=SPI_CLK):
-    """
-    运行SPI写测试的便捷函数
-    
-    Args:
-        data_size: 要发送的数据总大小(字节),默认2MB
-        chunk_size: 每次发送的数据块大小(字节),默认1KB
-        spi_channel: SPI通道,默认2(对应MCU端SPI1)
-        spi_mode: SPI模式,默认0
-        spi_clk: SPI时钟频率,默认8(1.625MHz)
-        
-    Returns:
-        tuple: (success: bool, send_time: float, total_bytes: int)
-    """
-    test = SpiWriteTest(data_size, chunk_size, spi_channel, spi_mode, spi_clk)
-    try:
-        success, send_time, total_bytes, pattern_info = test.run_test()
-        if success:
-            clog.info("SPI write test PASSED")
-        else:
-            clog.error("SPI write test FAILED")
-        return success, send_time, total_bytes
-    finally:
-        test.close()
-
-
-# 自动运行测试(如果直接执行此脚本)
-if __name__ == '__main__':
-    # 运行2MB递增数据测试
-    success, send_time, total_bytes = run_spi_write_test(
-        data_size=2 * 1024 * 1024,  # 2MB
-        chunk_size=1024,  # 1KB chunks
-        spi_channel=2,  # SPI通道2(对应MCU端SPI1)
-        spi_mode=0,  # Mode 0
-        spi_clk=8  # 1.625MHz(可根据需要调整)
-    )
-    
+# 便捷运行函数(保留原始)
+def run_spi_write_test():
+    test = SpiWriteTest()
+    success, send_time, total_bytes, info = test.run_test()
     if success:
-        clog.info("Test completed successfully!")
-        clog.info("Total bytes sent: {} ({:.2f} MB)".format(
-            total_bytes, total_bytes / 1024.0 / 1024.0))
-        clog.info("Send time: {:.3f} seconds".format(send_time))
+        clog.info("Test PASSED | Total bytes sent: {}".format(total_bytes))
     else:
-        clog.error("Test failed!")
-
-# 如果作为模块导入,不自动运行
-# 可以通过以下方式使用:
-# from spi_write_test import run_spi_write_test
-# success, send_time, total_bytes = run_spi_write_test()
+        clog.error("Test FAILED")
+    return success
 
+if __name__ == '__main__':
+    run_spi_write_test()
\ No newline at end of file
index 25aefb8..44da629 100644 (file)
 #include <stdint.h>
 #include "spi.h"
 #include "custom_spi.h"
+#include "custom_utils.h"
+
+osSemaphoreId_t g_semaphore_spi_id = NULL;
 
 static int total_cnt = 0;
 static uint8_t g_rx_buffer[1024] = {0};
+static uint8_t last_expect_seq = 1;  // 初始期望递增序号(根据主设备实际起始值调整)
+static const uint8_t retrans_cmd = 0x01;
+#define PACKET_HEADER_OFFSET 0
+#define PACKET_TOTAL_SIZE 1024
+static uint8_t first_flag = 0;
+static int discontinuous_cnt = 0;
+
+static uint32_t pkg_recv_cnt = 0;  // 新增:接收包计数
+static int current_seq = 0;
+static int expected_seq = 0;
+
 void custom_spi_read(void)
 {
     spi_xfer_data_t spi_data = {0};
+    errcode_t ret = ERRCODE_FAIL;
+    
+    memset(g_rx_buffer, 0, sizeof(g_rx_buffer));
     spi_data.rx_bytes = sizeof(g_rx_buffer);
     spi_data.rx_buff = g_rx_buffer;
-    errcode_t ret = uapi_spi_slave_read(SPI_BUS_1, &spi_data, 50);
 
-    // osal_irq_restore(irq_sts);
-    printf("uapi_spi_slave_read ret : %x \n",ret);
-    printf("spi_data.rx_bytes : %d\n",spi_data.rx_bytes);
-    if(ret == 0)
+    ret = uapi_spi_slave_read(SPI_BUS_1, &spi_data, 50);
+
+    if(ret == 0 && spi_data.rx_bytes == 1024)
+    {
+        uint8_t current_seq = spi_data.rx_buff[0];
+        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]);
+        
+        // 新增:包计数自增
+        pkg_recv_cnt++;
+        
+        // 核心修改:仅在接收包数超过10个后,才执行连续性判定
+        if (pkg_recv_cnt > 10) {
+            if (last_expect_seq != 0xFF) {  // 非首次接收才判定
+                uint8_t expected_seq = (last_expect_seq == 0x00) ? 0xFF : (last_expect_seq - 1);
+                if (current_seq != expected_seq) {
+                    discontinuous_cnt++;
+                    printf("不连续(跳变):当前0x%02X, 期望0x%02X | 累计不连续数:%d\n", 
+                           current_seq, expected_seq, discontinuous_cnt);
+                    uapi_gpio_set_dir(S_AGPIO_L13, GPIO_DIRECTION_OUTPUT);
+                    uapi_gpio_set_val(S_AGPIO_L13, GPIO_LEVEL_HIGH);
+                    uapi_tcxo_delay_ms(2);
+                    uapi_gpio_set_val(S_AGPIO_L13, GPIO_LEVEL_LOW);
+                    uapi_tcxo_delay_ms(5);
+                    uint8_t data = expected_seq;
+                    spi_data.tx_buff = &data;
+                    spi_data.tx_bytes = 1;
+                    ret = uapi_spi_slave_write(SPI_BUS_1, &spi_data, 50);
+                    printf("uapi_spi_slave_write ret : 0x%02X \n", ret);
+                }
+            }
+        }
+        
+        // 更新上一次接收的序号(用于下次判定)
+        last_expect_seq = current_seq;
+        
+        total_cnt += spi_data.rx_bytes;
+        printf("total_cnt : %d, 当前接收包数:%d\n", total_cnt, pkg_recv_cnt);
+    }
+    else
     {
-        printf("spi_data.rx_bytes : %d,spi_data.rx_data[0]:0x%x\n",spi_data.rx_bytes,spi_data.rx_buff[0]);
-        if(spi_data.rx_bytes == sizeof(g_rx_buffer))
-            total_cnt += spi_data.rx_bytes;
-        printf("total_cnt : %d\n",total_cnt);
+        // 新增:包计数自增(失败/长度不符的包也计数,若仅统计成功包可移到上面if内)
+        pkg_recv_cnt++;
+        
+        uapi_gpio_set_dir(S_AGPIO_L13, GPIO_DIRECTION_OUTPUT);
+        uapi_gpio_set_val(S_AGPIO_L13, GPIO_LEVEL_HIGH);
+        uapi_tcxo_delay_ms(2);
+        uapi_gpio_set_val(S_AGPIO_L13, GPIO_LEVEL_LOW);
+        uapi_tcxo_delay_ms(5);
+        uint8_t data = expected_seq;
+        spi_data.tx_buff = &data;
+        spi_data.tx_bytes = 1;
+        ret = uapi_spi_slave_write(SPI_BUS_1, &spi_data, 50);
+        printf("uapi_spi_slave_write ret : 0x%02X \n", ret);
+        // printf("接收失败/长度不符,当前接收包数:%d\n", pkg_recv_cnt);
+    }
+
+    // spi_xfer_data_t spi_data = {0};
+    // errcode_t ret = ERRCODE_FAIL;
+    
+    // memset(g_rx_buffer, 0, sizeof(g_rx_buffer));
+    // spi_data.rx_bytes = sizeof(g_rx_buffer);
+    // spi_data.rx_buff = g_rx_buffer;
+
+    // ret = uapi_spi_slave_read(SPI_BUS_1, &spi_data, 50);
+
+    // if(ret == 0 && spi_data.rx_bytes == 1024)
+    // {
+    //     printf("spi_data.rx_bytes : %d,spi_data.rx_data[0]:0x%x\n",spi_data.rx_bytes,spi_data.rx_buff[0]);
+    //     if(spi_data.rx_bytes == sizeof(g_rx_buffer))
+    //         total_cnt += spi_data.rx_bytes;
+    //     printf("total_cnt : %d\n",total_cnt);
+
+
+    //     // uapi_gpio_set_dir(S_AGPIO_L13, GPIO_DIRECTION_OUTPUT);
+
+    //     // uapi_gpio_set_val(S_AGPIO_L13, GPIO_LEVEL_HIGH);
+    //     // uapi_tcxo_delay_ms(10); /* delay 10 ms */
+    //     // uapi_gpio_set_val(S_AGPIO_L13, GPIO_LEVEL_LOW);
+    //     // if(spi_fp == NULL)
+    //     // {
+    //     //     spi_fp = fopen("/user/data/spi_text.txt","w+");
+    //     // }
+
+    //     // if(spi_fp)
+    //     // {
+    //     //     int ret = fwrite(spi_data.rx_buff,1,spi_data.rx_bytes,spi_fp);
+    //     //     printf("fwrite ret:%d\n",ret);
+    //     // }
+    // }
+    // else
+    // {
+    //     uapi_gpio_set_dir(S_AGPIO_L13, GPIO_DIRECTION_OUTPUT);
+
+    //     uapi_gpio_set_val(S_AGPIO_L13, GPIO_LEVEL_HIGH);
+    //     uapi_tcxo_delay_ms(2); /* delay 10 ms */
+    //     uapi_gpio_set_val(S_AGPIO_L13, GPIO_LEVEL_LOW);
+
+    //     uint8_t data = last_expect_seq;
+    //     last_expect_seq++;
+    //     spi_data.tx_buff = &data;
+    //     spi_data.tx_bytes = 1;
+    //     ret = uapi_spi_slave_write(SPI_BUS_1, &spi_data, 50);
+    //     printf("uapi_spi_slave_write ret : %x \n",ret);
+    // }
+
+
+    // if(ret == 0 && spi_data.rx_bytes == 1024)
+    // {
+    //     if(first_flag == 0)
+    //     {
+    //         first_flag = 1;
+    //         last_expect_seq = spi_data.rx_buff[0];
+    //     }
+    //     else if(last_expect_seq != spi_data.rx_buff[0] - 1)
+    //     {
+    //         printf("data error,last_expect_seq:0x%x,spi_data.rx_data[0]:0x%x\n",last_expect_seq,spi_data.rx_buff[0]);
+    //         uint8_t data = last_expect_seq;
+    //         last_expect_seq++;
+    //         spi_data.tx_buff = &data;
+    //         spi_data.tx_bytes = 1;
+    //         ret = uapi_spi_slave_write(SPI_BUS_1, &spi_data, 50);
+    //         printf("uapi_spi_slave_write ret : %x \n",ret);
+    //     }
+    //     else if(last_expect_seq == 0 && spi_data.rx_buff[0] == 0xff)
+    //     {
+    //         last_expect_seq = spi_data.rx_buff[0];
+    //         printf("spi_data.rx_bytes : %d,spi_data.rx_data[0]:0x%x\n",spi_data.rx_bytes,spi_data.rx_buff[0]);
+    //         if(spi_data.rx_bytes == sizeof(g_rx_buffer))
+    //             total_cnt += spi_data.rx_bytes;
+    //         printf("total_cnt : %d\n",total_cnt);
+    //     }
+    //     else
+    //     {
+    //         last_expect_seq = spi_data.rx_buff[0];
+    //         printf("spi_data.rx_bytes : %d,spi_data.rx_data[0]:0x%x\n",spi_data.rx_bytes,spi_data.rx_buff[0]);
+    //         if(spi_data.rx_bytes == sizeof(g_rx_buffer))
+    //             total_cnt += spi_data.rx_bytes;
+    //         printf("total_cnt : %d\n",total_cnt);
+    //     }
+
 
 
         // uapi_gpio_set_dir(S_AGPIO_L13, GPIO_DIRECTION_OUTPUT);
@@ -53,26 +200,80 @@ void custom_spi_read(void)
         //     int ret = fwrite(spi_data.rx_buff,1,spi_data.rx_bytes,spi_fp);
         //     printf("fwrite ret:%d\n",ret);
         // }
-    }
-    // else if(ret == ERRCODE_SPI_DMA_TRANSFER_ERROR)
+    // }
+    // else
     // {
-    //     uint8_t data = 2;
+    //     uint8_t data = last_expect_seq;
+    //     last_expect_seq++;
     //     spi_data.tx_buff = &data;
     //     spi_data.tx_bytes = 1;
     //     ret = uapi_spi_slave_write(SPI_BUS_1, &spi_data, 50);
-    //     data = 0;
-    //     spi_data.tx_buff = &data;
+    //     printf("uapi_spi_slave_write ret : %x \n",ret);
+    // }
+    // printf("\n=== SPI Read ===\n");
+    // printf("uapi_spi_slave_read ret : 0x%x, rx bytes: %d\n", ret, spi_data.rx_bytes);
+
+    // if (ret == 0 && spi_data.rx_bytes == PACKET_TOTAL_SIZE)
+    // {
+    //     uint8_t current_seq = g_rx_buffer[PACKET_HEADER_OFFSET];
+    //     printf("Header seq: 0x%02X, Expect seq: 0x%02X\n", current_seq, last_expect_seq);
+
+    //     // 核心:判定序号是否为“递增连续”(仅允许递增,禁止递减)
+    //     uint8_t is_continuous = 0;
+    //     // 情况1:正常递增(如 0x7→0x8、0xFF→0x0)
+    //     if (current_seq == last_expect_seq)
+    //     {
+    //         is_continuous = 1;
+    //     }
+    //     // 情况2:255→0 溢出递增(合法)
+    //     else if (last_expect_seq == 0xFF && current_seq == 0x00)
+    //     {
+    //         is_continuous = 1;
+    //         printf("Seq overflow (255→0) → continuous\n");
+    //     }
+
+    //     if (is_continuous)
+    //     {
+    //         // 序号合法:累加计数 + 更新下一个期望序号(递增)
+    //         total_cnt += spi_data.rx_bytes;
+    //         last_expect_seq++;  // uint8_t自动溢出(255→0)
+    //         printf("Seq OK! Total cnt: %d, Next expect: 0x%02X\n", total_cnt, last_expect_seq);
+    //     }
+    //     else
+    //     {
+    //         // 序号递减/跳变:触发重传,不更新计数
+    //         printf("Seq ERROR! Current:0x%02X, Expect:0x%02X → Send retrans cmd (0x01)\n", 
+    //                current_seq, last_expect_seq);
+            
+    //         spi_data.tx_buff = (uint8_t*)&retrans_cmd;
+    //         spi_data.tx_bytes = 1;
+    //         errcode_t write_ret = uapi_spi_slave_write(SPI_BUS_1, &spi_data, 50);
+    //         printf("Retrans cmd send ret: 0x%x\n", write_ret);
+    //         return;  // 不更新计数,等待主设备重发正确序号
+    //     }
+    // }
+    // else if (ret == 0)
+    // {
+    //     printf("Rx packet incomplete! Expect:1024, Actual:%d\n", spi_data.rx_bytes);
+    // }
+    // else
+    // {
+    //     // 读取失败:回传当前期望序号,便于主设备定位
+    //     printf("SPI Read FAIL (0x%x) → Write back expect seq:0x%02X\n", ret, last_expect_seq);
+    //     spi_data.tx_buff = &last_expect_seq;
     //     spi_data.tx_bytes = 1;
     //     ret = uapi_spi_slave_write(SPI_BUS_1, &spi_data, 50);
-    //     printf("uapi_spi_slave_write ret : %x \n",ret);
+    //     if (ret != 0) ret = uapi_spi_slave_write(SPI_BUS_1, &spi_data, 50);
+    //     printf("Write back ret:0x%x, data:0x%02X\n", ret, last_expect_seq);
+    //     last_expect_seq++;
     // }
 
 
-    uapi_gpio_set_dir(S_AGPIO_L13, GPIO_DIRECTION_OUTPUT);
+    // uapi_gpio_set_dir(S_AGPIO_L13, GPIO_DIRECTION_OUTPUT);
 
-    uapi_gpio_set_val(S_AGPIO_L13, GPIO_LEVEL_HIGH);
-    uapi_tcxo_delay_ms(2); /* delay 10 ms */
-    uapi_gpio_set_val(S_AGPIO_L13, GPIO_LEVEL_LOW);
+    // uapi_gpio_set_val(S_AGPIO_L13, GPIO_LEVEL_HIGH);
+    // uapi_tcxo_delay_ms(2); /* delay 10 ms */
+    // uapi_gpio_set_val(S_AGPIO_L13, GPIO_LEVEL_LOW);
 
     // if(ret == ERRCODE_SUCC)
     // {
@@ -85,6 +286,44 @@ void custom_spi_read(void)
     // }
 }
 
+void custom_spi_service_task(void *arg)
+{
+    printf("SPI处理任务启动,开始等待中断...\r\n");
+    
+    // 任务循环:永久运行,处理每次中断
+    while (1) {
+        // 3. 等待信号量(阻塞,直到中断释放/超时)
+        errcode_t ret = osSemaphoreAcquire(g_semaphore_spi_id, 60 * 1000);
+        
+        if (ret == 0) {
+            // 4. 信号量获取成功:处理SPI数据
+            // printf("获取信号量成功,开始读取SPI数据...\r\n");
+            
+            custom_spi_read();
+            
+            // 5. 数据处理完成后,信号量自动回到「锁住」状态(无需手动操作)
+            //    下一次循环会再次调用osSemaphoreAcquire,等待下一次中断释放
+            // printf("SPI数据处理完成,重新等待中断...\r\n");
+            
+        } else if (ret == osErrorTimeout) {
+            // 超时处理:打印日志,继续循环等待
+            printf("SPI中断等待超时(%d秒),继续等待...\r\n", 60);
+        } else {
+            // 信号量错误:退出循环,任务结束(可选重试逻辑)
+            printf("信号量获取失败(状态:%d),任务退出\r\n", ret);
+            // break;
+        }
+    }
+    
+    // 任务退出前清理资源
+    if (g_semaphore_spi_id != NULL) {
+        osSemaphoreDelete(g_semaphore_spi_id);
+        g_semaphore_spi_id = NULL;
+    }
+    printf("SPI处理任务退出\r\n");
+    osThreadExit();
+}
+
 void custom_spi_init(void)
 {
     spi_bus_t bus = SPI_BUS_1;
@@ -104,7 +343,7 @@ void custom_spi_init(void)
     attr.frame_format = SPI_CFG_FRAME_FORMAT_MOTOROLA_SPI;
     attr.spi_frame_format = (uint32_t)HAL_SPI_FRAME_FORMAT_STANDARD;  // 标准SPI格式(非QUAD)
     attr.frame_size = (uint32_t)HAL_SPI_FRAME_SIZE_8;  // 8位数据(与Python端匹配)
-    attr.tmod = HAL_SPI_TRANS_MODE_RX;      // 只接收模式
+    attr.tmod = HAL_SPI_TRANS_MODE_TXRX;      // 只接收模式
     attr.sste = SPI_CFG_SSTE_DISABLE;
     attr.ndf = 1;
     
@@ -135,10 +374,53 @@ void custom_spi_init(void)
         printf("uapi_gpio_register_isr_func failed: 0x%x\n", ret);
         return;
     }
+
+    g_semaphore_spi_id = osSemaphoreNew(1, 0, NULL);
+    if(g_semaphore_spi_id == NULL)
+    {
+        printf("osSemaphoreNew failed\n");
+        return;
+    }
+
+    app_task_definition_t taskcfg={0};
+    // taskcfg.create_queue_handle = create_ppg_queue;
+    taskcfg.attr.name="custom_spi_service";
+
+    taskcfg.attr.priority = osPriorityNormal4;
+
+    taskcfg.attr.stack_size = 0x2000;
+    taskcfg.func = custom_spi_service_task;
+    taskcfg.use_mem = USE_EXTEND_MEM;
+    
+    ret=uapi_create_osThread_custom(&taskcfg);
+    if(ret != ERRCODE_SUCC)
+    {
+        printf("create custom_spi_service_task failed %d!\r\n",ret);
+        return;
+    }
+
+    
+    // ret = osSemaphoreAcquire(g_semaphore_spi_id, 60 * 1000);
+    // if (ret != 0) 
+    // {
+    //     printf("SPI中断等待超时/失败(状态:%d)\r\n", ret);
+    //     // 超时处理:销毁任务+信号量
+    //     // uapi_delete_osThread_custom(taskcfg.attr.name); // 假设存在任务销毁接口
+    //     osSemaphoreDelete(g_semaphore_spi_id);
+    //     g_semaphore_spi_id = NULL;
+    //     return;
+    // }
+
+    // printf("SPI中断触发,初始化完成\r\n");   
+
 }
 
 void custom_spi_deinit(void)
 {
+    discontinuous_cnt = 0;
+    last_expect_seq = 1;
+    total_cnt = 0;
+    memset_s(g_rx_buffer,sizeof(g_rx_buffer),0,sizeof(g_rx_buffer));
     uapi_spi_deinit(SPI_BUS_1);
     uapi_pin_set_ie(S_MGPIO23,HAL_PIO_IE_DISABLE);
     errcode_t ret = uapi_gpio_unregister_isr_func(S_MGPIO23);
@@ -146,13 +428,33 @@ void custom_spi_deinit(void)
         printf("uapi_gpio_unregister_isr_func failed: 0x%x\n", ret);
         return;
     }
+
+    if (g_semaphore_spi_id != NULL) 
+    {
+        osSemaphoreDelete(g_semaphore_spi_id);
+        g_semaphore_spi_id = NULL;
+    }
 }
 
 
 void custom_spi_rx_callback(pin_t pin, uintptr_t param)
 {
-    // printf("custom_spi_rx_callback %d 0x%X\n",pin,param);
+    // printf("custom_spi_rx_callback \n");
+    // 1. 清除中断标志(避免重复触发)
     uapi_gpio_clear_interrupt(pin);
-
-    // do_custom_cmd_service_check_msg(CUSTOM_CMD_MSG_SPI_READ,NULL,0);
+    
+    // 2. 安全释放信号量(空指针检查)
+    if (g_semaphore_spi_id != NULL) {
+        osSemaphoreRelease(g_semaphore_spi_id);
+        // printf("SPI中断触发,释放信号量\r\n");
+    }
 }
+
+void custom_spi_start(void)
+{
+    uapi_gpio_set_dir(S_AGPIO_L13, GPIO_DIRECTION_OUTPUT);
+
+    uapi_gpio_set_val(S_AGPIO_L13, GPIO_LEVEL_HIGH);
+    uapi_tcxo_delay_ms(2); /* delay 10 ms */
+    uapi_gpio_set_val(S_AGPIO_L13, GPIO_LEVEL_LOW);
+}
\ No newline at end of file