我们从2011年坚守至今,只想做存粹的技术论坛。  由于网站在外面,点击附件后要很长世间才弹出下载,请耐心等待,勿重复点击不要用Edge和IE浏览器下载,否则提示不安全下载不了

 找回密码
 立即注册
搜索
查看: 726|回复: 4

风板程序(传感器用的陀螺仪,功能是让风板被吹到任意的

[复制链接]

该用户从未签到

10

主题

49

回帖

372

积分

二级逆天

积分
372

终身成就奖特殊贡献奖

QQ
发表于 2016-8-19 08:02:12 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区

您需要 登录 才可以下载或查看,没有账号?立即注册

×
#include "stc15.h"
#define CYCLE   0x1000L     //定义PWM周期(最大值为32767)
typedef unsigned char BYTE;
typedef unsigned int WORD;
//-----------------------------------------------
#define FOSC 11059200L
#define T1MS (65536-FOSC/1000)      //1T模式
//#define T1MS (65536-FOSC/12/1000) //12T模式
//sfr AUXR = 0x8e;                    //Auxiliary register
//sbit P10 = P1^0;
int i;
int j;
#include  <math.h>    //Keil library  
#include  <stdio.h>   //Keil library   
#include  <INTRINS.H>
unsigned int DUTY1 = 3000;         //定义占空比为50%
unsigned int DUTY2 = 3000;          //定义占空比为50%
#define PWMC        (*(unsigned int  volatile xdata *)0xfff0)
#define PWMCH       (*(unsigned char volatile xdata *)0xfff0)
#define PWMCL       (*(unsigned char volatile xdata *)0xfff1)
#define PWMCKS      (*(unsigned char volatile xdata *)0xfff2)
#define PWM2T1      (*(unsigned int  volatile xdata *)0xff00)
#define PWM2T1H     (*(unsigned char volatile xdata *)0xff00)
#define PWM2T1L     (*(unsigned char volatile xdata *)0xff01)
#define PWM2T2      (*(unsigned int  volatile xdata *)0xff02)
#define PWM2T2H     (*(unsigned char volatile xdata *)0xff02)
#define PWM2T2L     (*(unsigned char volatile xdata *)0xff03)
#define PWM2CR      (*(unsigned char volatile xdata *)0xff04)
#define PWM3T1      (*(unsigned int  volatile xdata *)0xff10)
#define PWM3T1H     (*(unsigned char volatile xdata *)0xff10)
#define PWM3T1L     (*(unsigned char volatile xdata *)0xff11)
#define PWM3T2      (*(unsigned int  volatile xdata *)0xff12)
#define PWM3T2H     (*(unsigned char volatile xdata *)0xff12)
#define PWM3T2L     (*(unsigned char volatile xdata *)0xff13)
#define PWM3CR      (*(unsigned char volatile xdata *)0xff14)
#define PWM4T1      (*(unsigned int  volatile xdata *)0xff20)
#define PWM4T1H     (*(unsigned char volatile xdata *)0xff20)
#define PWM4T1L     (*(unsigned char volatile xdata *)0xff21)
#define PWM4T2      (*(unsigned int  volatile xdata *)0xff22)
#define PWM4T2H     (*(unsigned char volatile xdata *)0xff22)
#define PWM4T2L     (*(unsigned char volatile xdata *)0xff23)
#define PWM4CR      (*(unsigned char volatile xdata *)0xff24)
#define PWM5T1      (*(unsigned int  volatile xdata *)0xff30)
#define PWM5T1H     (*(unsigned char volatile xdata *)0xff30)
#define PWM5T1L     (*(unsigned char volatile xdata *)0xff31)
#define PWM5T2      (*(unsigned int  volatile xdata *)0xff32)
#define PWM5T2H     (*(unsigned char volatile xdata *)0xff32)
#define PWM5T2L     (*(unsigned char volatile xdata *)0xff33)
#define PWM5CR      (*(unsigned char volatile xdata *)0xff34)
#define PWM6T1      (*(unsigned int  volatile xdata *)0xff40)
#define PWM6T1H     (*(unsigned char volatile xdata *)0xff40)
#define PWM6T1L     (*(unsigned char volatile xdata *)0xff41)
#define PWM6T2      (*(unsigned int  volatile xdata *)0xff42)
#define PWM6T2H     (*(unsigned char volatile xdata *)0xff42)
#define PWM6T2L     (*(unsigned char volatile xdata *)0xff43)
#define PWM6CR      (*(unsigned char volatile xdata *)0xff44)
#define PWM7T1      (*(unsigned int  volatile xdata *)0xff50)
#define PWM7T1H     (*(unsigned char volatile xdata *)0xff50)
#define PWM7T1L     (*(unsigned char volatile xdata *)0xff51)
#define PWM7T2      (*(unsigned int  volatile xdata *)0xff52)
#define PWM7T2H     (*(unsigned char volatile xdata *)0xff52)
#define PWM7T2L     (*(unsigned char volatile xdata *)0xff53)
#define PWM7CR      (*(unsigned char volatile xdata *)0xff54)
#define   uchar unsigned char
#define   uint unsigned int   
#define   DataPort P2    //LCD1602数据端口
sbit      SCL=P1^0;      //IIC时钟引脚定义
sbit       SDA=P1^1;      //IIC数据引脚定义
sbit      LCM_RS=P4^0;   //LCD1602命令端口        
sbit      LCM_RW=P4^1;   //LCD1602命令端口        
sbit      LCM_EN=P4^2;   //LCD1602命令端口
sbit      P1_1=P0^3;
sbit      P1_2=P0^4;
sbit      beep=P1^3;
sbit      light=P0^4;
sbit      vvv=P4^3;   //LCD1602命令端口
#define    SlaveAddress   0xA6      //定义器件在IIC总线中的从地址,根据ALT  ADDRESS地址引脚不同修改
//ALT  ADDRESS引脚接地时地址为0xA6,接电源时地址为0x3A
//typedef unsigned char  BYTE;
//typedef unsigned short WORD;
BYTE equal[6];
BYTE BUF[8];                         //接收数据缓存区         
uchar ge,shi,bai,qian,wan;           //显示变量
int  dis_data1;                       //变量
int  dis_data2;                       //变量
int  dis_data3;                       //变量
int count;
float Roll,Pitch,zz,Q,T,K;
int iin=0;
int iIncpid;
int SetPoint; //设定目标
typedef struct PID
{
int SetPoint; //设定目标
long SumError; //误差累计
double Proportion; //比例常数
double Integral; //积分常数
double Derivative; //微分常数
int LastError; //Error[-1]
int PrevError; //Error[-2]
} PID;
static PID sPID;
static PID *sptr = &sPID;
void delay(unsigned int k);
void InitLcd();                      //初始化lcd1602
void Init_ADXL345(void);             //初始化ADXL345
void WriteDataLCM(uchar dataW);
void WriteCommandLCM(uchar CMD,uchar Attribc);
void DisplayOneChar(uchar X,uchar Y,uchar DData);
void conversion(uint temp_data);
void  Single_Write_ADXL345(uchar REG_Address,uchar REG_data);   //单个写入数据
uchar Single_Read_ADXL345(uchar REG_Address);                   //单个读取内部寄存器数据
void  Multiple_Read_ADXL345();                                  //连续的读取内部寄存器数据
//------------------------------------
void Delay5us();
void Delay5ms();
void ADXL345_Start();
void ADXL345_Stop();
void ADXL345_SendACK(bit ack);
bit  ADXL345_RecvACK();
void ADXL345_SendByte(BYTE dat);
BYTE ADXL345_RecvByte();
void ADXL345_ReadPage();
void ADXL345_WritePage();
//-----------------------------------
//-----------------倾角显示开始---------------------//
void conversion(uint temp_data)  
{  
    wan=temp_data/10000+0x30 ;
    temp_data=temp_data%10000;   //取余运算
    qian=temp_data/1000+0x30 ;
    temp_data=temp_data%1000;    //取余运算
    bai=temp_data/100+0x30   ;
    temp_data=temp_data%100;     //取余运算
    shi=temp_data/10+0x30    ;
    temp_data=temp_data%10;      //取余运算
    ge=temp_data+0x30;     
}
/*******************************/
void delay(unsigned int k)   
{                        
unsigned int i,j;               
for(i=0;i<k;i++)
{            
for(j=0;j<121;j++)            
{;}}                        
}
/*******************************/
void WaitForEnable(void)   
{                    
DataPort=0xff;        
LCM_RS=0;LCM_RW=1 ;_nop_();
LCM_EN=1;_nop_();_nop_();
while(DataPort&0x80);   
LCM_EN=0;               
}                    
/*******************************/
void WriteCommandLCM(uchar CMD,uchar Attribc)
{                    
if(Attribc)WaitForEnable();   
LCM_RS=0;LCM_RW=0;_nop_();
DataPort=CMD;_nop_();   
LCM_EN=1;_nop_();_nop_();LCM_EN=0;
}                    
/*******************************/
void WriteDataLCM(uchar dataW)
{                    
WaitForEnable();        
LCM_RS=1;LCM_RW=0;_nop_();
DataPort=dataW;_nop_();   
LCM_EN=1;_nop_();_nop_();LCM_EN=0;
}        
/***********************************/
void InitLcd()               
{            
WriteCommandLCM(0x38,1);   
WriteCommandLCM(0x08,1);   
WriteCommandLCM(0x01,1);   
WriteCommandLCM(0x06,1);   
WriteCommandLCM(0x0c,1);
}            
/***********************************/
void DisplayOneChar(uchar X,uchar Y,uchar DData)
{                        
Y&=1;                        
X&=15;                        
if(Y)X|=0x40;                    
X|=0x80;            
WriteCommandLCM(X,1);        
WriteDataLCM(DData);        
}                        
/**************************************
延时5微秒(STC90C52RC@12M)
不同的工作环境,需要调整此函数,注意时钟过快时需要修改
当改用1T的MCU时,请调整此延时函数
**************************************/
void Delay5us()
{
    _nop_();_nop_();_nop_();_nop_();
    _nop_();_nop_();_nop_();_nop_();
    _nop_();_nop_();_nop_();_nop_();
}
/**************************************
延时5毫秒(STC90C52RC@12M)
不同的工作环境,需要调整此函数
当改用1T的MCU时,请调整此延时函数
**************************************/
void Delay5ms()
{
    WORD n = 560;

    while (n--);
}
/**************************************
起始信号
**************************************/
void ADXL345_Start()
{
    SDA = 1;                    //拉高数据线
    SCL = 1;                    //拉高时钟线
    Delay5us();                 //延时
    SDA = 0;                    //产生下降沿
    Delay5us();                 //延时
    SCL = 0;                    //拉低时钟线
}
/**************************************
停止信号
**************************************/
void ADXL345_Stop()
{
    SDA = 0;                    //拉低数据线
    SCL = 1;                    //拉高时钟线
    Delay5us();                 //延时
    SDA = 1;                    //产生上升沿
    Delay5us();                 //延时
}
/**************************************
发送应答信号
入口参数:ack (0:ACK 1:NAK)
**************************************/
void ADXL345_SendACK(bit ack)
{
    SDA = ack;                  //写应答信号
    SCL = 1;                    //拉高时钟线
    Delay5us();                 //延时
    SCL = 0;                    //拉低时钟线
    Delay5us();                 //延时
}
/**************************************
接收应答信号
**************************************/
bit ADXL345_RecvACK()
{
    SCL = 1;                    //拉高时钟线
    Delay5us();                 //延时
    CY = SDA;                   //读应答信号
    SCL = 0;                    //拉低时钟线
    Delay5us();                 //延时
    return CY;
}
/**************************************
向IIC总线发送一个字节数据
**************************************/
void ADXL345_SendByte(BYTE dat)
{
    BYTE i;
    for (i=0; i<8; i++)         //8位计数器
    {
        dat <<= 1;              //移出数据的最高位
        SDA = CY;               //送数据口
        SCL = 1;                //拉高时钟线
        Delay5us();             //延时
        SCL = 0;                //拉低时钟线
        Delay5us();             //延时
    }
    ADXL345_RecvACK();
}
/**************************************
从IIC总线接收一个字节数据
**************************************/
BYTE ADXL345_RecvByte()
{
    BYTE i;
    BYTE dat = 0;
    SDA = 1;                    //使能内部上拉,准备读取数据,
    for (i=0; i<8; i++)         //8位计数器
    {
        dat <<= 1;
        SCL = 1;                //拉高时钟线
        Delay5us();             //延时
        dat |= SDA;             //读数据               
        SCL = 0;                //拉低时钟线
        Delay5us();             //延时
    }
    return dat;
}
//******单字节写入*******************************************
void Single_Write_ADXL345(uchar REG_Address,uchar REG_data)
{
    ADXL345_Start();                  //起始信号
    ADXL345_SendByte(SlaveAddress);   //发送设备地址+写信号
    ADXL345_SendByte(REG_Address);    //内部寄存器地址,请参考中文pdf22页
    ADXL345_SendByte(REG_data);       //内部寄存器数据,请参考中文pdf22页
    ADXL345_Stop();                   //发送停止信号
}
//********单字节读取*****************************************
uchar Single_Read_ADXL345(uchar REG_Address)
{  uchar REG_data;
    ADXL345_Start();                          //起始信号
    ADXL345_SendByte(SlaveAddress);           //发送设备地址+写信号
    ADXL345_SendByte(REG_Address);                   //发送存储单元地址,从0开始   
    ADXL345_Start();                          //起始信号
    ADXL345_SendByte(SlaveAddress+1);         //发送设备地址+读信号
    REG_data=ADXL345_RecvByte();              //读出寄存器数据
    ADXL345_SendACK(1);   
    ADXL345_Stop();                           //停止信号
    return REG_data;
}
//*********************************************************
//
//连续读出ADXL345内部加速度数据,地址范围0x32~0x37
//
//*********************************************************
void Multiple_read_ADXL345(void)
{   uchar i;
    ADXL345_Start();                          //起始信号
    ADXL345_SendByte(SlaveAddress);           //发送设备地址+写信号
    ADXL345_SendByte(0x32);                   //发送存储单元地址,从0x32开始   
    ADXL345_Start();                          //起始信号
    ADXL345_SendByte(SlaveAddress+1);         //发送设备地址+读信号
     for (i=0; i<6; i++)                      //连续读取6个地址数据,存储中BUF
    {
        BUF = ADXL345_RecvByte();          //BUF[0]存储0x32地址中的数据
        if (i == 5)
        {
           ADXL345_SendACK(1);                //最后一个数据需要回NOACK
        }
        else
        {
          ADXL345_SendACK(0);                //回应ACK
       }  
    }
    ADXL345_Stop();                          //停止信号
    Delay5ms();
}
//*****************************************************************
//初始化ADXL345,根据需要请参考pdf进行修改************************
void Init_ADXL345()
{
   Single_Write_ADXL345(0x31,0x0B);   //测量范围,正负16g,13位模式
   Single_Write_ADXL345(0x2C,0x08);   //速率设定为12.5 参考pdf13页
   Single_Write_ADXL345(0x2D,0x08);   //选择电源模式   参考pdf24页
   Single_Write_ADXL345(0x2E,0x80);   //使能 DATA_READY 中断
   Single_Write_ADXL345(0x1E,0x00);   //X 偏移量 根据测试传感器的状态写入pdf29页
   Single_Write_ADXL345(0x1F,0x00);   //Y 偏移量 根据测试传感器的状态写入pdf29页
   Single_Write_ADXL345(0x20,0x05);   //Z 偏移量 根据测试传感器的状态写入pdf29页
}
//***********************************************************************
//显示x轴
void display_x()
{   
    int temp=(sptr->SetPoint - Pitch);
    if(temp<0)
    {
    temp=-temp;
    DisplayOneChar(2,0,'-');      //显示正负符号位
    }
    else DisplayOneChar(2,0,' '); //显示空格
    conversion(temp);          //转换出显示需要的数据
    DisplayOneChar(0,0,'X');   //第0行,第0列 显示X
    DisplayOneChar(1,0,':');
    DisplayOneChar(3,0,qian);
    DisplayOneChar(4,0,bai);
    DisplayOneChar(5,0,shi);
    DisplayOneChar(6,0,ge);
}
//***********************************************************************
//显示y轴
void display_y()
{   
    if(Pitch<0){
    Pitch=-Pitch;
    DisplayOneChar(2,1,'-');      //显示正负符号位
    }
    else DisplayOneChar(2,1,' '); //显示空格
    conversion(Pitch);          //转换出显示需要的数据
    DisplayOneChar(0,1,'Y');   //第1行,第0列 显示y
    DisplayOneChar(1,1,':');
    DisplayOneChar(3,1,qian);
    DisplayOneChar(4,1,bai);
    DisplayOneChar(5,1,shi);  
    DisplayOneChar(6,1,ge);  
}
//***********************************************************************
//显示z轴
void display_z()
{   
    if(iin<0){
    iin=-iin;
    DisplayOneChar(10,1,'-');       //显示负符号位
    }
    else DisplayOneChar(10,1,' ');  //显示空格
    conversion(iin);          //转换出显示需要的数据
    DisplayOneChar(9,1,'Z');  //第0行,第10列 显示Z
    DisplayOneChar(11,1,':');
    DisplayOneChar(12,1,qian);
    DisplayOneChar(13,1,bai);
    DisplayOneChar(14,1,shi);
    DisplayOneChar(15,1,ge);
}
//----------------倾角显示结束---------------------//
sfr PIN_SW2 =   0xba;
/*====== Initialize PID Structure PID参数初始化 =====*/
void IncPIDInit(void)
{
sptr->SumError = 0;
sptr->LastError = 0; //Error[-1]
sptr-&gtrevError = 0; //Error[-2]
sptr-&gtroportion = 15; //比例常数
sptr->Integral =0; //积分常数
sptr->Derivative = 0; //微分常数
//sptr->SetPoint = 90;
}
/*===== 增量式PID计算部分=========*/
int IncPIDCalc(int NextPoint)
{
register int iError, iIncpid; //当前误差
iError = sptr->SetPoint - NextPoint; //增量计算
iIncpid = sptr-&gtroportion * iError //E[k]项
- sptr->Integral * sptr->LastError //E[k-1]项
+ sptr->Derivative * sptr-&gtrevError; //E[k-2]项   //存储误差,用于下次计算
sptr-&gtrevError = sptr->LastError;
sptr->LastError = iError; //返回增量值
if(iIncpid >= 1090)iIncpid=1090;
if(iIncpid <= -1080)iIncpid=-1080;
return(iIncpid);
}
void cqq()
{
    Multiple_Read_ADXL345();       //连续读出数据,存储在BUF中
   
    dis_data1=(BUF[1]<<8)+BUF[0];  //合成数据
    dis_data2=(BUF[3]<<8)+BUF[2];  //合成数据
    dis_data3=(BUF[5]<<8)+BUF[4];  //合成数据
   
    Q=(float)dis_data1*3.9;
    T=(float)dis_data2*3.9;
    K=(float)dis_data3*3.9;
    Roll=(float)(((atan2(K,Q)*180)/3.1416)-90);    //X轴角度值
    Pitch=(float)(((atan2(K,T)*180)/3.1416));   //Y轴角度值
    zz=(float)((atan2(Q,K)*180)/3.1416);
}
//char filter()
//{
//    long sum=0;
//    for(count=0;count<10;count++)
//    {     cqq();
//        sum+=Pitch;
//    }
//    return(char)(sum/10);
//}
void K1CHECK()
{
   TR1=0;
   sptr->SetPoint =110;
}
void K2CHECK()
{  
   TR1=1;
}
void tm1_isr() interrupt 3 using 1
{
    i++;
    if(i==1000)
    sptr->SetPoint = 70;
    if(i==2000)
    sptr->SetPoint = 110;
    if(i==3000)
    {
    sptr->SetPoint = 70;
    i=0;    j++;   
     }   
}
void main()
{
    vvv=1;
    P0M0 = 0x00;
    P0M1 = 0x00;
    P1M0 = 0x00;
    P1M1 = 0x00;
    P2M0 = 0x00;
    P2M1 = 0x00;
    P3M0 = 0x00;
    P3M1 = 0x00;
    P4M0 = 0x00;
    P4M1 = 0x00;
    P5M0 = 0x00;
    P5M1 = 0x00;
    P6M0 = 0x00;
    P6M1 = 0x00;
    P7M0 = 0x00;
    P7M1 = 0x00;
    PIN_SW2 |= 0x80;                //使能访问XSFR
    PWMCFG = 0x00;                  //配置PWM的输出初始电平为低电平
    PWMCKS = 0x00;                  //选择PWM的时钟为Fosc/(0+1)
    PWMC = CYCLE;                   //设置PWM周期
    PWM2T1 = 0x0000;                //设置PWM2第1次反转的PWM计数
    PWM2T2 = 2048;//CYCLE * DUTY1 / 100;    //设置PWM2第2次反转的PWM计数
    PWM3T1 = 0x0000;                //设置PWM2第1次反转的PWM计数
    PWM3T2 = 2048;//CYCLE * DUTY2 / 100;    //设置PWM2第2次反转的PWM计数
    PWM3CR = 0x08;                  //选择PWM2输出到P4.5,不使能PWM2中断
    PWM2CR = 0x00;                  //选择PWM2输出到P3.7,不使能PWM2中断
    PWMCR = 0x03;                   //使能PWM信号输出
    PWMCR |= 0x40;                  //使能PWM归零中断
    PWMCR |= 0x80;                  //使能PWM模块
    PIN_SW2 &= ~0x80;
    EA = 1;
    delay(500);delay(500);delay(500);delay(500);delay(500);
    delay(500);delay(500);delay(500);delay(500);delay(500);
    delay(500);                       //上电延时        
    InitLcd();                      //液晶初始化ADXL345
    Init_ADXL345();                 //初始化ADXL345
    IncPIDInit();
   
    AUXR |= 0x40;        //定时器时钟1T模式
    TMOD &= 0x0F;        //设置定时器模式
    TL1 = 0x9A;        //设置定时初值
    TH1 = 0xA9;        //设置定时初值
    TF1 = 0;        //清除TF0标志
     ET1 = 1;                        //使能定时器0中断
   
    EA = 1;
    while (1)
    {
     if(P1_1==0)
       delay(500);
     if(P1_1==0)
        K1CHECK();
     if(P1_2==0)
        delay(500);
     if(P1_2==0)
        K2CHECK();
        cqq();
    // Pitch=filter();
     display_x();                   //---------显示X轴
     display_y();                   //---------显示Y轴
     display_z();                   //---------显示Z轴         

     if((sptr->SetPoint - Pitch)>3 ||(sptr->SetPoint - Pitch)<-3)
          iin=IncPIDCalc(Pitch);
     if(j==3)
    {
    sptr->SetPoint = 0;
    TR1=0;
    if(sptr->SetPoint -Pitch==0)
    {
         beep=0;
        light=0;
    }
    }
} }
void pwm_isr() interrupt 22 using 1
{
   
    if (PWMIF & 0x40)
    {
        PWMIF &= ~0x40;
        DUTY1=3000+iin;
        DUTY2=3000-iin;
        PIN_SW2 |= 0x80;
        PWM2T2 = DUTY1 ;    //设置PWM2第2次反转的PWM计数
        PWM3T2 = DUTY2 ;    //设置PWM2第2次反转的PWM计数
        PIN_SW2 &= ~0x80;
    }
}
回复

使用道具 举报

该用户从未签到

9

主题

639

回帖

2

积分

游客

积分
2

终身成就奖

QQ
发表于 2016-8-19 08:54:18 | 显示全部楼层
谢谢LZ的分享。这个程序应该是针对ADXL345它的哦!
回复

使用道具 举报

  • TA的每日心情
    慵懒
    5 小时前
  • 签到天数: 178 天

    [LV.7]常住居民III

    82

    主题

    1万

    回帖

    1万

    积分

    三级逆天

    积分
    10450

    社区居民社区劳模原创先锋奖终身成就奖特殊贡献奖优秀斑竹奖

    QQ
    发表于 2016-8-19 09:36:16 | 显示全部楼层
    回复

    使用道具 举报

    该用户从未签到

    10

    主题

    49

    回帖

    372

    积分

    二级逆天

    积分
    372

    终身成就奖特殊贡献奖

    QQ
     楼主| 发表于 2016-8-19 10:27:26 | 显示全部楼层
    回复

    使用道具 举报

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

    本版积分规则

    每日签到,有金币领取。


    Copyright ©2011-2024 NTpcb.com All Right Reserved.  Powered by Discuz! (NTpcb)

    本站信息均由会员发表,不代表NTpcb立场,如侵犯了您的权利请发帖投诉

    ( 闽ICP备2024076463号-1 ) 论坛技术支持QQ群171867948 ,论坛问题,充值问题请联系QQ1308068381

    平平安安
    TOP
    快速回复 返回顶部 返回列表