找回密码
 立即注册
查看: 50|回复: 5

【已解决】求助,SPI1复用串口1dma总是不成功

[复制链接]
  • 打卡等级:偶尔看看III
  • 打卡总天数:30
  • 最近打卡:2025-10-25 19:36:02
已绑定手机

6

主题

10

回帖

202

积分

中级会员

积分
202
发表于 6 天前 | 显示全部楼层 |阅读模式
有没有大佬帮看看代码

  1. #define MAIN_Fosc     40000000UL  // 明确40MHz系统时钟(与硬件一致)
  2. #include "rgb.h"
  3. // 颜色定义(保持原逻辑,确保GRB顺序匹配WS2812)
  4. uint8 xdata rgb_red[3] = {0xff,0x00,0x00};    // 红色(RGB)→ GRB后为0x00,0xff,0x00
  5. uint8 xdata rgb_green[3] = {0x00,0xff,0x00};  // 绿色 → GRB后为0xff,0x00,0x00
  6. uint8 xdata rgb_blue[3] = {0x00,0x00,0xff};   // 蓝色 → GRB后为0x00,0x00,0xff
  7. uint8 xdata rgb_off[3] = {0x00,0x00,0x00};    // 熄灭
  8. uint8 xdata rgb_yellow[3] = {0xff,0xff,0x00}; // 黄色
  9. uint8 xdata rgb_purple[3] = {0xff,0x00,0xff}; // 紫色
  10. uint8 xdata rgb_cyan[3] = {0x00,0xff,0xff};   // 青色
  11. uint8 xdata rgb_white[3] = {0xff,0xff,0xff};  // 白色
  12. uint8 xdata rgb_black[3] = {0x00,0x00,0x00};  // 黑色
  13. // 呼吸灯颜色变量
  14. uint8 xdata breath_rgb_color[3];
  15. // WS2812编码缓冲区(3色×4SPI字节=12字节,单个灯,文档推荐缓冲区大小)
  16. uint8 xdata ws2812_buffer[12];
  17. bit B_SPI_DMA_busy = 0;  // SPI-DMA忙标志(文档DMA状态管理规范)
  18. // 状态变量(保持原定义)
  19. static uint8 xdata current_rgb_color[3];
  20. static RGB_STATE_enum current_state = RGB_STATE_OFF;
  21. static RGB_COLOR_enum current_color = RGB_COLOR_BLACK;
  22. static RGB_STATE_enum target_state = RGB_STATE_OFF;
  23. static RGB_COLOR_enum target_color = RGB_COLOR_BLACK;
  24. static uint32 last_toggle_time = 0;
  25. static uint8 blink_state = 0;
  26. static uint32 last_breath_time = 0;
  27. static uint8 breath_direction = 1;
  28. static uint8 breath_brightness = 10;
  29. // 引脚定义(USART1 SPI通道3:P4.0-SS, P4.1-MOSI, P4.2-MISO, P4.3-SCK,文档2.1.3节管脚图)
  30. #define SPI_MOSI_PIN 0x02  // P4.1 (bit1)
  31. #define SPI_SCK_PIN  0x08  // P4.3 (bit3)
  32. #define SPI_MISO_PIN 0x04  // P4.2 (bit2)
  33. #define Pin2 0x04          // P4.2 (bit2)
  34. /**
  35. * @brief RGB模块初始化(适配40MHz时钟,遵循文档SPI/DMA配置规范)
  36. * @note 参考文档:第24章SPI()、第25章HSSPI()、第34章DMA()
  37. */
  38. void rgb_init(void)
  39. {
  40.     // 1. SPI引脚配置(文档13章I/O口配置)
  41.     // MOSI(P4.1)、SCK(P4.3):推挽输出+高速模式(40MHz下需高速驱动)
  42.     P4M0 |= (SPI_MOSI_PIN | SPI_SCK_PIN);
  43.     P4M1 &= ~(SPI_MOSI_PIN | SPI_SCK_PIN);
  44.     // 使用标准寄存器操作替代SlewRateHigh函数
  45.     P4SR &= ~(SPI_MOSI_PIN | SPI_SCK_PIN);  // 高速输出,确保TH/TL时序
  46.     // MISO(P4.2):高阻输入+下拉(文档24.4节SPI从机配置)
  47.     // 使用标准寄存器操作替代函数调用
  48.     P4M1 |= SPI_MISO_PIN;  // 高阻输入模式
  49.     P4M0 &= ~SPI_MISO_PIN; // 输入模式
  50.     P4PU &= ~SPI_MISO_PIN; // 禁止上拉
  51.     P4PD |= SPI_MISO_PIN;  // 允许下拉,确保空闲低电平
  52.     // 2. USART1-SPI模式配置(文档18章同步串口)
  53.     USARTCR1 = 0x00;                // 复位USART1
  54.     USARTCR1 |= 0x08;               // 使能SPI功能(bit3=1)
  55.     USARTCR1 &= ~0x10;              // 0=SPI模式(bit4=0)
  56.     USARTCR1 |= 0x40;               // 主机模式(bit6=1)
  57.     USARTCR1 |= 0x80;               // 使能USART1(bit7=1)
  58.     USARTCR2 = 0x00;                // CPOL=0, CPHA=0(WS2812要求)
  59.     USARTCR3 = 0x00;                // 8位数据长度(bit0-1=00)
  60.     // 3. SPI波特率计算(40MHz→3.2MHz:Fosc/(2*(USARTBR+1))=3.2MHz → USARTBR=5,文档18.3.5节公式)
  61.     USARTBRH = 0x00;
  62.     USARTBRL = 0x05;
  63.     // 4. 高速SPI配置(40MHz下时序补偿,文档25.1节)
  64.     HSCLKDIV = 1;   // 主时钟分频(高频稳定)
  65.     SPI_CLKDIV = 1;  // SPI时钟分频
  66.     HSSPI_CFG = (2 << 4) | 3;       // SS_HOLD=2, SS_SETUP=3(40MHz下补偿传输延迟)
  67.     HSSPI_CFG2 = (1<<5) | (1<<4) | 3;// HSSPIEN=1(使能高速), FIFOEN=1(减少13个时钟延迟), SS_DACT=3
  68.     // 5. DMA配置(USART1发送DMA,文档34.7节)
  69.     DMA_UR1T_CR = 0x00;             // 停止当前DMA
  70.     DMA_UR1T_STA = 0x00;            // 清除状态标志
  71.     // DMA配置:中断使能+发送使能+优先级1+普通优先级
  72.     DMA_UR1T_CFG = (1<<7) | (1<<6) | (1<<2) | 0;
  73.     interrupt_set_priority(DMA_UR1T_VECTOR, 1);  // 中断优先级1(文档14章中断)
  74.     // 6. 初始化熄灭灯(确保初始状态可控)
  75.     while(B_SPI_DMA_busy);
  76.     send_color_data(rgb_off);
  77. }
  78. // 根据颜色枚举获取指针(保持原实现)
  79. uint8* get_color_ptr(RGB_COLOR_enum color)
  80. {
  81.     switch(color)
  82.     {
  83.         case RGB_COLOR_RED:    return rgb_red;
  84.         case RGB_COLOR_GREEN:  return rgb_green;
  85.         case RGB_COLOR_BLUE:   return rgb_blue;
  86.         case RGB_COLOR_YELLOW: return rgb_yellow;
  87.         case RGB_COLOR_PURPLE: return rgb_purple;
  88.         case RGB_COLOR_CYAN:   return rgb_cyan;
  89.         case RGB_COLOR_WHITE:  return rgb_white;
  90.         case RGB_COLOR_BLACK:  return rgb_black;
  91.         default:               return rgb_black;
  92.     }
  93. }
  94. // 前向声明(保持原定义)
  95. void RGB_SPI_DMA_TxTRIG(uint8 xdata *TxBuf, uint16 num);
  96. // 发送颜色数据(保持原逻辑,增加SPI空闲检查)
  97. void send_color_data(uint8 *color)
  98. {
  99.     while(B_SPI_DMA_busy);                  // 等待DMA空闲
  100.     while(USARTCR1 & 0x20);                 // 等待SPI总线空闲(避免冲突)
  101.    
  102.     // 更新当前颜色
  103.     current_rgb_color[0] = color[0];
  104.     current_rgb_color[1] = color[1];
  105.     current_rgb_color[2] = color[2];
  106.    
  107.     encode_ws2812_data(current_rgb_color);  // 编码
  108.     RGB_SPI_DMA_TxTRIG(ws2812_buffer, 12);  // DMA发送
  109.     while(B_SPI_DMA_busy);                  // 等待发送完成
  110.    
  111.     system_delay_us(50);  // WS2812复位时间(≥50us)
  112. }
  113. /**
  114. * @brief 启动SPI DMA传输(遵循文档34.4.4节配置)
  115. * @param TxBuf:发送缓冲区地址
  116. * @param num:传输字节数(实际长度,内部转换为num-1)
  117. */
  118. void RGB_SPI_DMA_TxTRIG(uint8 xdata *TxBuf, uint16 num)
  119. {
  120.     uint16 j = (uint16)TxBuf;
  121.     // 扩展寄存器已在主函数使能,无需重复调用EAXSFR
  122.     // 停止当前DMA并等待SPI空闲(文档34.4.4节)
  123.     DMA_UR1T_CR = 0x00;
  124.     while(USARTCR1 & 0x20);  // 等待SPI_BUSY标志清除(bit5=0)
  125.     // 配置DMA地址和长度(文档34.7.2节)
  126.     DMA_UR1T_TXAH = (uint8)(j >> 8);         // 发送地址高字节
  127.     DMA_UR1T_TXAL = (uint8)j;                 // 发送地址低字节
  128.     DMA_UR1T_AMTH = (uint8)((num-1)/256);     // 总字节数=num-1(文档要求)
  129.     DMA_UR1T_AMT  = (uint8)((num-1)%256);
  130.     // 移除不存在的寄存器
  131.     // DMA_UR1T_ITVH = 0;                      // 无间隔时间
  132.     // DMA_UR1T_ITVL = 0;
  133.     DMA_UR1T_STA  = 0x00;                   // 清除状态
  134.     // 启动DMA传输(文档34.7.2节)
  135.     DMA_UR1T_CR = (1<<7) | (1<<6) | 1;       // DMA_EN=1, TRIG_M=1, CLRFIFO=1
  136.     B_SPI_DMA_busy = 1;
  137. }
  138. // SPI DMA中断服务函数(保持原逻辑)
  139. void SPI_DMA_ISR (void) interrupt DMA_UR1T_VECTOR
  140. {
  141.     EAXSFR();  // 使用宏替代直接寄存器访问
  142.     DMA_UR1T_STA = 0;    // 清除中断标志
  143.     DMA_UR1T_CR = 0x00;  // 停止DMA
  144.     B_SPI_DMA_busy = 0;  // 释放忙标志
  145. }
  146. // WS2812编码(保持原逻辑,确认GRB顺序)
  147. void encode_ws2812_data(uint8 *rgb_color)
  148. {
  149.     static const uint8 code_table[4] = {0x88, 0x8E, 0xE8, 0xEE};  // 00=0x88, 01=0x8E, 10=0xE8, 11=0xEE
  150.     uint8 dat;
  151.     uint8 color_order[3];
  152.     // 清空缓冲区(32位操作优化)
  153.     *((uint32*)&ws2812_buffer[0]) = 0;
  154.     *((uint32*)&ws2812_buffer[4]) = 0;
  155.     *((uint32*)&ws2812_buffer[8]) = 0;
  156.     // WS2812要求GRB顺序(原代码正确)
  157.     color_order[0] = rgb_color[1];  // Green
  158.     color_order[1] = rgb_color[0];  // Red
  159.     color_order[2] = rgb_color[2];  // Blue
  160.     // 编码Green通道
  161.     dat = color_order[0];
  162.     ws2812_buffer[0] = code_table[(dat >> 6) & 0x03];
  163.     ws2812_buffer[1] = code_table[(dat >> 4) & 0x03];
  164.     ws2812_buffer[2] = code_table[(dat >> 2) & 0x03];
  165.     ws2812_buffer[3] = code_table[dat & 0x03];
  166.     // 编码Red通道
  167.     dat = color_order[1];
  168.     ws2812_buffer[4] = code_table[(dat >> 6) & 0x03];
  169.     ws2812_buffer[5] = code_table[(dat >> 4) & 0x03];
  170.     ws2812_buffer[6] = code_table[(dat >> 2) & 0x03];
  171.     ws2812_buffer[7] = code_table[dat & 0x03];
  172.     // 编码Blue通道
  173.     dat = color_order[2];
  174.     ws2812_buffer[8] = code_table[(dat >> 6) & 0x03];
  175.     ws2812_buffer[9] = code_table[(dat >> 4) & 0x03];
  176.     ws2812_buffer[10] = code_table[(dat >> 2) & 0x03];
  177.     ws2812_buffer[11] = code_table[dat & 0x03];
  178. }
  179. // RGB任务处理(保持原逻辑)
  180. void rgb_task(void)
  181. {
  182.     uint8 *color_ptr;
  183.     uint32 current_time = sys_tick_get();
  184.     if (target_state != current_state || target_color != current_color) {
  185.         current_state = target_state;
  186.         current_color = target_color;
  187.         color_ptr = get_color_ptr(current_color);
  188.         blink_state = 0;
  189.         breath_brightness = 10;
  190.         breath_direction = 1;
  191.         last_toggle_time = current_time;
  192.         last_breath_time = current_time;
  193.     }
  194.     switch(current_state)
  195.     {
  196.         case RGB_STATE_OFF:
  197.             send_color_data(rgb_off);
  198.             break;
  199.         case RGB_STATE_ON:
  200.             color_ptr = get_color_ptr(current_color);
  201.             send_color_data(color_ptr);
  202.             break;
  203.         case RGB_STATE_BLINK:
  204.             if(current_time - last_toggle_time >= 500)
  205.             {
  206.                 last_toggle_time = current_time;
  207.                 blink_state = !blink_state;
  208.                 send_color_data(blink_state ? get_color_ptr(current_color) : rgb_off);
  209.             }
  210.             break;
  211.         case RGB_STATE_FAST_BLINK:
  212.             if(current_time - last_toggle_time >= 200)
  213.             {
  214.                 last_toggle_time = current_time;
  215.                 blink_state = !blink_state;
  216.                 send_color_data(blink_state ? get_color_ptr(current_color) : rgb_off);
  217.             }
  218.             break;
  219.         case RGB_STATE_BREATH:
  220.             if(current_time - last_breath_time >= 30)
  221.             {
  222.                 last_breath_time = current_time;
  223.                 // 更新亮度
  224.                 if(breath_direction)
  225.                     breath_brightness = (breath_brightness >= 245) ? 255 : (breath_brightness + 10);
  226.                 else
  227.                     breath_brightness = (breath_brightness <= 10) ? 10 : (breath_brightness - 10);
  228.                 if(breath_brightness >= 255 || breath_brightness <= 10)
  229.                     breath_direction = !breath_direction;
  230.                 // 计算呼吸颜色
  231.                 color_ptr = get_color_ptr(current_color);
  232.                 breath_rgb_color[0] = (uint16)color_ptr[0] * breath_brightness / 255;
  233.                 breath_rgb_color[1] = (uint16)color_ptr[1] * breath_brightness / 255;
  234.                 breath_rgb_color[2] = (uint16)color_ptr[2] * breath_brightness / 255;
  235.                 // 避免完全熄灭
  236.                 if(breath_rgb_color[0] == 0 && breath_rgb_color[1] == 0 && breath_rgb_color[2] == 0)
  237.                 {
  238.                     if(color_ptr[0] > 0) breath_rgb_color[0] = 1;
  239.                     else if(color_ptr[1] > 0) breath_rgb_color[1] = 1;
  240.                     else if(color_ptr[2] > 0) breath_rgb_color[2] = 1;
  241.                 }
  242.                 send_color_data(breath_rgb_color);
  243.             }
  244.             break;
  245.         default:
  246.             send_color_data(rgb_off);
  247.             break;
  248.     }
  249. }
  250. // 设置RGB颜色和状态(保持原逻辑)
  251. void rgb_set_color_state(RGB_COLOR_enum color, RGB_STATE_enum state)
  252. {
  253.     target_color = color;
  254.     target_state = state;
  255. }
复制代码


回复

使用道具 举报 送花

3

主题

1849

回帖

546

积分

等待验证会员

积分
546
发表于 6 天前 | 显示全部楼层
您好,关于您提到的“SPI1复用串口1 DMA总是不成功”的问题,我将从以下几个方面进行分析与建议,帮助您排查问题根源:

一、问题定位与理解

您提到使用的是 SPI1,但尝试将其复用为 串口1(UART1)DMA 功能。这在某些MCU(如STM32系列)中是可能的,因为DMA通道通常是多路复用的,但需要注意以下几点:
DMA通道与外设映射关系:不同外设(SPI、UART等)使用的DMA通道和请求源不同,必须正确配置DMA请求映射。
引脚复用配置:若SPI1与UART1共享部分引脚或DMA通道,需确认GPIO配置是否冲突。
DMA优先级与中断冲突:多个DMA请求同时触发可能导致优先级冲突或未正确处理中断。

二、常见问题排查方向

1. 外设时钟是否已正确使能

确保以下外设时钟均已开启:
  1. c
  2. RCCAPB2PeriphClockCmd(RCCAPB2PeriphSPI1, ENABLE);
  3. RCCAPB2PeriphClockCmd(RCCAPB2PeriphUSART1, ENABLE);
  4. RCCAHBPeriphClockCmd(RCCAHBPeriphDMA1, ENABLE); // 或DMA2,视具体型号而定
复制代码

2. DMA通道选择与映射是否正确

STM32中每个外设的DMA请求映射是固定的。例如:

| 外设       | DMA请求源 | DMA通道 | 方向(Mem/Periph) |
|------------|------------|----------|---------------------|
| SPI1 TX    | DMA1Channel3 | Channel3 | Mem → Periph |
| UART1 TX   | DMA1Channel4 | Channel4 | Mem → Periph |

请检查是否将SPI1的数据流错误地绑定到了UART1的DMA通道上,或者反之。

3. DMA配置参数是否正确

DMA配置包括:
数据源地址(Memory Address)
目标地址(Peripheral Address)
数据长度
数据宽度(字节/半字/字)
传输方向(Mem → Periph / Periph → Mem)
循环模式(Circular Mode)
中断使能(Transfer Complete / Error)

示例配置代码片段:
  1. c
  2. DMAInitTypeDef DMAInitStruct;
  3. DMAInitStruct.DMAPeripheralBaseAddr = (uint32t)&SPI1->DR; // 或USART1->DR
  4. DMAInitStruct.DMAMemoryBaseAddr = (uint32t)txBuffer;
  5. DMAInitStruct.DMADIR = DMADIRPeripheralDST;
  6. DMAInitStruct.DMABufferSize = bufferSize;
  7. DMAInitStruct.DMAPeripheralInc = DMAPeripheralIncDisable;
  8. DMAInitStruct.DMAMemoryInc = DMAMemoryIncEnable;
  9. DMAInitStruct.DMAPeripheralDataSize = DMAPeripheralDataSizeByte;
  10. DMAInitStruct.DMAMemoryDataSize = DMAMemoryDataSizeByte;
  11. DMAInitStruct.DMAMode = DMAModeNormal;
  12. DMAInitStruct.DMAPriority = DMAPriorityHigh;
  13. DMAInitStruct.DMAM2M = DMAM2MDisable;
  14. DMAInit(DMA1Channel3, &DMAInitStruct);
  15. DMACmd(DMA1Channel3, ENABLE);
复制代码

4. 外设DMA请求是否使能

对于SPI或UART,需单独开启其DMA请求:
  1. c
  2. SPII2SDMACmd(SPI1, SPII2SDMAReqTx, ENABLE); // SPI1 DMA使能
  3. USARTDMACmd(USART1, USARTDMAReqTx, ENABLE);   // UART1 DMA使能
复制代码

5. 是否存在DMA通道冲突

若同时使用多个DMA通道(如SPI1与UART1),请检查它们是否属于同一DMA控制器(DMA1/DMA2),并确保优先级设置合理,避免冲突。

三、调试建议

1. 使用调试器查看DMA状态寄存器

通过调试器查看DMAISR寄存器的状态,判断是否出现以下情况:
传输完成标志(TCIFx)
传输错误标志(TEIFx)
缓存溢出(DMEIFx)

2. 添加DMA中断处理函数

启用DMA中断并实现中断处理函数,可以更精确地定位失败原因:
  1. c
  2. void DMA1Channel3IRQHandler(void) {
  3.     if (DMAGetITStatus(DMA1ITTC3)) {
  4.         // 传输完成处理
  5.         DMAClearITPendingBit(DMA1ITTC3);
  6.     }
  7.     if (DMAGetITStatus(DMA1ITTE3)) {
  8.         // 传输错误处理
  9.         DMAClearITPendingBit(DMA1ITTE3);
  10.     }
  11. }
复制代码

3. 使用逻辑分析仪或示波器观察SPI/I2C波形

验证SPI是否发出数据,有助于判断是DMA未启动,还是外设本身未正确工作。

四、代码优化建议

从您提供的代码片段来看,似乎是在为WS2812灯带发送GRB数据。这类应用通常要求精确的时序控制,使用DMA可以有效减轻CPU负担。但需要注意以下几点:
DMA缓冲区应为全局或静态变量,避免局部变量被优化或释放。
SPI时钟频率应与WS2812要求匹配(通常为2.4MHz~3.2MHz)。
使用SPI的DMA发送模式,而非UART,因为UART波特率精度和DMA控制不如SPI灵活。

五、总结与建议

综上所述,您的问题可能源于以下几点之一:

1. DMA通道与外设映射错误
2. 外设DMA请求未正确使能
3. DMA配置参数不匹配
4. DMA中断未处理或冲突
5. SPI/UART引脚复用配置错误

建议您按照以下步骤逐一排查:

1. 检查DMA通道是否与外设匹配;
2. 检查外设DMA请求是否开启;
3. 配置DMA参数并启用中断;
4. 使用调试工具查看DMA状态;
5. 确保外设时钟和GPIO配置正确。

如能提供完整DMA初始化代码和外设配置代码,我可以进一步帮助您分析具体问题。

祝调试顺利!

如有其他问题,欢迎继续交流。

—— 国芯人工智能

[本文内容由国芯人工智能辅助生成,仅供参考]
回复

使用道具 举报 送花

  • 打卡等级:以坛为家I
  • 打卡总天数:342
  • 最近打卡:2025-10-29 08:51:51
已绑定手机

90

主题

3328

回帖

7459

积分

荣誉版主

无情的代码机器

积分
7459
发表于 6 天前 | 显示全部楼层
三天不学习,赶不上刘少奇~
回复

使用道具 举报 送花

  • 打卡等级:偶尔看看III
  • 打卡总天数:30
  • 最近打卡:2025-10-25 19:36:02
已绑定手机

6

主题

10

回帖

202

积分

中级会员

积分
202
发表于 6 天前 | 显示全部楼层
erci*** 发表于 2025-10-24 13:34
有例程可参考:
https://www.stcaimcu.com/data/download/DemoCode/STC32G-DEMO-CODE-V9.6.zip

对就是看的这个,
回复

使用道具 举报 送花

  • 打卡等级:偶尔看看III
  • 打卡总天数:30
  • 最近打卡:2025-10-25 19:36:02
已绑定手机

6

主题

10

回帖

202

积分

中级会员

积分
202
发表于 6 天前 | 显示全部楼层
erci*** 发表于 2025-10-24 13:34
有例程可参考:
https://www.stcaimcu.com/data/download/DemoCode/STC32G-DEMO-CODE-V9.6.zip

佬,多少频率要上高速spi,还有高速spi是不是只支持独立spi模块,usart复用的不支持

点评

spi二分频就开hsspi,usart和hsspi是否兼容得试下 [attachimg]118840[/attachimg]  详情 回复 发表于 6 天前
回复

使用道具 举报 送花

  • 打卡等级:以坛为家I
  • 打卡总天数:342
  • 最近打卡:2025-10-29 08:51:51
已绑定手机

90

主题

3328

回帖

7459

积分

荣誉版主

无情的代码机器

积分
7459
发表于 6 天前 | 显示全部楼层
cod*** 发表于 2025-10-24 16:22
佬,多少频率要上高速spi,还有高速spi是不是只支持独立spi模块,usart复用的不支持 ...


spi二分频就开hsspi,usart和hsspi是否兼容得试下
截图202510241626472310.jpg
三天不学习,赶不上刘少奇~
回复

使用道具 举报 送花

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|手机版|深圳国芯人工智能有限公司 ( 粤ICP备2022108929号-2 )

GMT+8, 2025-10-30 02:19 , Processed in 0.133655 second(s), 81 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

快速回复 返回顶部 返回列表