Small RTOS 下SJA1000驱动程序的编写
1 概述
本文主要介绍了在实时操作系统Small RTOS下,编写独立CAN控制器SJA1000驱动程序的方法。并介绍了SJA1000 PeliCan模式下接口函数库的使用方法,以及简要的介绍了对Small RTOS的配置方法。
1.1 CAN控制器SJA1000
关于独立的CAN控制器SJA1000的详细介绍,请参阅参考文献1。
1.2 实时微内核Small RTOS
关于实时操作系统Small RTOS的详细介绍,请参阅参考文献2。
2 SJA1000 PeliCan模式下接口函数库说明
本库文件为独立的CAN控制器SJA1000 PeliCAN模式下,基于51系列微处理器的应用函数库。应用本库文件可以轻松实现对SJA1000的操作。以下是库函数的详细说明。
2.1 测试SJA1000与微处理器接口函数
/*
********************************************************************************************
**函数原型: char SJATestinterface(unsigned char testvalue)
**参数说明: testvalue 为用户输入的测试值
**返回值: SJA_OK ; 表示SJA1000接口正常
** SJA_INTERFACE_ERR ; 表示SJA1000与处理器接口出错
**应用子函数:无
**说明: 该函数用于检测CAN控制器与微处理器的接口是否正常,用户输入testvalue,若函数返回
** SJA_OK则表示接口正常,否则接口错误,应检查SJA1000与微处理器的物理连接
********************************************************************************************
*/
/*======================调用示例=================================================*/
#define TEST_VALUE 0xAA
char STATUS;
STATUS = SJATestInterface(TEST_VALUE);
If(STATUS == SJA_OK){
//接口正常用户处理代码
}
else if(STATUS == SJA_INTERFACE_ERR){
//接口错误用户处理代码
}
/*=============================================================================*/
2.2 置位SJA1000中某寄存器的某位
/*
********************************************************************************************
**函数原型: char SetBitMask(unsigned char RegAdr,unsigned char BitValue)
**参数说明: RegAdr 要设置位的寄存器地址
** BitValue 要设置的值;
**返回值: SJA_OK ; 表示设置值 成功
** SJA_INIT_ERR ; 表示设置值 失败
**应用子函数: ReadRawIO ;在sjapeli.h中定义的宏,用来读取sja1000中寄存器的内容
** WriteRawIO ;在sjapeli.h中定义的宏,用来写入sja1000中寄存器的内容
**说明: 该函数用于置位sja1000中某寄存器的某几位
********************************************************************************************
*/
/*======================调用示例=================================================*/
//设置模式寄存器(MOD)的第2位(MOD.2)
#define REG_MOD 0x00
# define MOD_BIT2 0x04
char STATUS;
STATUS = SetBitMask(REG_MOD, MOD_BIT2);
If(STATUS == SJA_OK){
//接口正常用户处理代码
}
else if(STATUS == SJA_INIT_ERR ){
//接口错误用户处理代码
}
/*=============================================================================*/
2.3 清零SJA1000中某寄存器的某位
/*
********************************************************************************************
**函数原型: char ClearBitMask(unsigned char RegAdr, unsigned char BitValue)
**参数说明: RegAdr 要清除位的寄存器地址
** BitValue 要清除的值
**返回值: SJA_OK ; 表示位清除 成功
** SJA_INIT_ERR ; 表示位清除 失败
**应用子函数: ReadRawIO ;在sjapeli.h中定义的宏,用来读取sja1000中寄存器的内容
** WriteRawIO ;在sjapeli.h中定义的宏,用来写入sja1000中寄存器的内容
**说明: 该函数用于清除sja1000某寄存器的某位
********************************************************************************************
*/
/*======================调用示例=================================================*/
//清除模式寄存器(MOD)的第2位(MOD.2)
#define REG_MOD 0x00
# define MOD_BIT2 0x04
char STATUS;
STATUS = ClearBitMask (REG_MOD, MOD_BIT2);
If(STATUS == SJA_OK){
//接口正常用户处理代码
}
else if(STATUS == SJA_INIT_ERR ){
//接口错误用户处理代码
}
/*=============================================================================*/
2.4 SJA1000进入PeliCAN模式
/*
********************************************************************************************
**函数原型: char SJAEntryPeliCANMode(void)
**参数说明: 无
**返回值: SJA_OK ; 表示SJA1000进入PeliCAN模式 成功
** SJA_INIT_ERR ; 表示SJA1000进入PeliCAN模式 失败
**说明: 该函数用于使sja1000进入PeliCAN模式
********************************************************************************************
*/
*/
/*======================调用示例=================================================*/
char STATUS;
STATUS = SJAEntryPeliCANMode();
If(STATUS == SJA_OK){
//接口正常用户处理代码
}
else if(STATUS == SJA_INIT_ERR ){
//接口错误用户处理代码
}
/*=============================================================================*/
2.5 SJA1000进入复位模式函数
/*
********************************************************************************************
**函数原型: char SJAEntryResetMode(void)
**参数说明: 无
**返回值: SJA_OK ; 表示SJA1000进入复位模式成功
** SJA_ENTRYRESET_ERR ; 表示SJA1000进入复位模式失败
**
**说明: 该函数用于使sja1000进入复位模式。调用该函数后SJA1000的一些状态将有可能改变,
** 用户应在退出复位模式之前重新初始化SJA1000。
********************************************************************************************
*/
/*======================调用示例=================================================*/
char STATUS;
STATUS = SJAEntryResetMode();
If(STATUS == SJA_OK){
//进入复位模式成功用户处理代码
}
else if(STATUS == SJA_ENTRYRESET_ERR){
//进入复位模式失败用户处理代码
}
/*=============================================================================*/
2.6 SJA1000退出复位模式函数
/*
********************************************************************************************
**函数原型: char SJAQuitResetMode(void)
**参数说明: 无
**返回值: SJA_OK ; 表示SJA1000退出复位模式成功
** SJA_QUITRESET_ERR ; 表示SJA1000退出复位模式失败
**
**说明: 该函数用于使sja1000退出复位模式,进入工作模式。
********************************************************************************************
*/
/*======================调用示例=================================================*/
char STATUS;
STATUS = SJAQuitResetMode ();
If(STATUS == SJA_OK){
//退出复位模式成功用户处理代码
}
else if(STATUS == SJA_QUITRESET_ERR){
//退出复位模式失败用户处理代码
}
/*=============================================================================*/
2.7 测试SJA1000当前工作模式函数
/*
********************************************************************************************
**函数原型: bit SJATestRstMode(void)
**参数说明: 无
**返回值: 0 ; 表示SJA1000处于复位模式
** 1 ; 表示SJA1000处于工作模式
**说明: 该函数用于测试SJA1000 的工作模式
********************************************************************************************
*/
/*======================调用示例=================================================*/
bit StatusFlag;
StatusFlag = SJATestRstMode ();
If(!StatusFlag){
//表示SJA1000处于复位模式
}
else {
//表示SJA1000处于工作模式
}
/*=============================================================================*/
2.8 设置SJA1000输出控制
/*
********************************************************************************************
**函数原型: char SJASetOutControl(unsigned char OutCtrl)
**参数说明: OutCtrl 输出控制寄存器的设定值,用以控制输出管脚的方式
**返回值:
** SJA_OK ; 设置输出控制寄存器成功
** SJA_SETOCR_ERR ; 设置输出控制寄存器错
** SJA_NOTRSTMODE ; sja1000不在复位模式
**说明: 该函数用于设定sja1000的输出控制。本函数只能用于复位模式
********************************************************************************************
*/
/*======================调用示例=================================================*/
char STATUS;
STATUS = SJASetOutControl (0xAA); //设置输出为正常模式
switch(STATUS ){
case SJA_OK: //设置输出控制寄存器成功 处理
break;
case SJA_SETOCR_ERR: //设置输出控制寄存器错
break;
case SJA_NOTRSTMODE :// sja1000不在复位模式处理
break;
default: break;
}
/*=============================================================================*/
2.9 设置时钟分频寄存器
/*
********************************************************************************************
**函数原型: char SJASetClockDivision(unsigned char clockdiv)
**参数说明: clockdiv 时钟分频寄存器的设定值
**返回值:
** SJA_OK ; 设置时钟分频寄存器成功
** SJA_SETCDR_ERR ; 设置时钟分频寄存器错
** SJA_NOTRSTMODE ; sja1000不在复位模式
**说明: 该函数用于设定sja1000的时钟分频寄存器。本函数只能用于复位模式
********************************************************************************************
*/
/*======================调用示例=================================================*/
char STATUS;
STATUS = SJASetClockDivision (0x48);
switch(STATUS ){
case SJA_OK: //设置时钟分频寄存器成功
break;
case SJA_SETCDR_ERR: //设置时钟分频寄存器错
break;
case SJA_NOTRSTMODE :// sja1000不在复位模式处理
break;
default: break;
}
/*=============================================================================*/
2.10 设置验收代码滤波器
/*
**********************************************************************************************函数原型: char SJASetAccCode(unsigned char acr0,unsigned char acr1,
** unsigned char acr2,unsigned char acr3
** )
**参数说明: acr0 代码验收寄存器0的设定值
** acr1 代码验收寄存器1的设定值
** acr2 代码验收寄存器2的设定值
** acr3 代码验收寄存器3的设定值
**返回值:
** SJA_OK ; 设置代码验收寄存器成功
** SJA_SETACR_ERR ; 设置代码验收寄存器错
** SJA_NOTRSTMODE ; sja1000不在复位模式
**说明: 该函数用于设定报文滤波的代码验收滤波器。本函数只能用于复位模式
********************************************************************************************
*/
/*======================调用示例=================================================*/
char STATUS;
STATUS = SJASetAccCode (0xAA,0xBB,0xCC,0xDD);
switch(STATUS ){
case SJA_OK: //设置代码验收寄存器成功
break;
case SJA_SETACR_ERR: //设置代码验收寄存器错
break;
case SJA_NOTRSTMODE :// sja1000不在复位模式处理
break;
default: break;
}
/*=============================================================================*/
2.11 设置验收屏蔽寄存器
/*
********************************************************************************************
**函数原型: char SJASetAccMask(unsigned char amr0,unsigned char amr1,
** unsigned char amr2,unsigned char amr3
** )
**参数说明: amr0 验收屏蔽寄存器0的设定值
** amr1 验收屏蔽寄存器1的设定值
** amr2 验收屏蔽寄存器2的设定值
** amr3 验收屏蔽寄存器3的设定值
**返回值:
** SJA_OK ; 设置验收屏蔽寄存器成功
** SJA_SETAMR_ERR ; 设置验收屏蔽寄存器错
** SJA_NOTRSTMODE ; sja1000不在复位模式
**说明: 该函数用于设定报文滤波的验收屏蔽寄存器。本函数只能用于复位模式
********************************************************************************************
*/
/*======================调用示例=================================================*/
char STATUS;
STATUS = SJASetAccMask (0x00,0x00,0x00,0x00);
switch(STATUS ){
case SJA_OK: //设置验收屏蔽寄存器成功
break;
case SJA_SETAMR_ERR: //设置验收屏蔽寄存器错
break;
case SJA_NOTRSTMODE :// sja1000不在复位模式处理
break;
default: break;
}
/*=============================================================================*/
2.12 设定波特率
/*
********************************************************************************************
**函数原型: char SJASetBandRateStandard(unsigned char BandRateSize)
**参数说明: BandRateSize 标准常用波特率(Kbps)
** 0 20
** 1 40
** 2 50
** 3 80
** 4 100
** 5 125
** 6 200
** 7 250
** 8 400
** 9 500
** 10 666
** 11 800
** 12 1000
**返回值:
** SJA_OK ; 设置总线定时器成功
** SJA_SETBTR_ERR ; 设置总线定时器错
** SJA_NOBTRSIZE ;波特率不能设为此值
**
**说明: 该函数用于设定在系统晶体为16MHZ时,常用的标准波特率的值。参数BandRateSize只能
**为0~12,其它的值会返回SJA_NOBTRSIZE错误。本函数只能用于复位模式
********************************************************************************************
*/
/*======================调用示例=================================================*/
char STATUS;
STATUS = SJASetBandRateStandard (0x00); //设置波特率为20kbps
switch(STATUS ){
case SJA_OK: //设置总线定时器成功
break;
case SJA_SETBTR_ERR: //设置总线定时器错
break;
case SJA_NOTRSTMODE :// sja1000不在复位模式处理
break;
default: break;
}
/*=============================================================================*/
2.13 将要发送到总线的数据写入SJA1000的发送缓冲区
/*
********************************************************************************************
**函数原型: char SJAWriteDataToTxBuf(unsigned char *databuf,
** volatile SJAFrameStruct *info
** )
**参数说明: databuf 存放要发送到总线的特定帧格式的数据的缓冲区首地址
** info sj1000帧格式类型的结构变量
**返回值:
** SJA_OK ; 表示将数据成功的写到到sja1000的发送缓冲区
** SJA_TXBUFLOCK ; sja1000发送缓冲区锁定
** SJA_WTXBUF_ERR ; 表示写数据失败
**说明: 该函数用于将要发送到总线的特定帧格式的数据写入sja1000的发送缓冲区
********************************************************************************************
*/
/*======================调用示例=================================================*/
char STATUS;
unsigned char buffer[8];
volatile SJAFrameStruct FrameInfo;
STATUS = SJAWriteDataToTxBuf (buffer,& FrameInfo);
switch(STATUS ){
case SJA_OK: //表示将数据成功的写到到sja1000的发送缓冲区处理
break;
case SJA_TXBUFLOCK: // sja1000发送缓冲区锁定处理
break;
case SJA_WTXBUF_ERR :// 写数据失败处理
break;
default: break;
}
/*=============================================================================*/
2.14 从SJA1000接收缓冲区读取数据
/*
********************************************************************************************
**函数原型: char SJAReadDataFromRxBuf(unsigned char *databuf,
** volatile SJAFrameStruct *info
** )
**参数说明: databuf 存放从sja1000读取数据的缓冲区首地址
** info sj1000帧格式类型的结构变量
**返回值:
** SJA_OK ; 表示将读取数据成功
** SJA_RXBUFEMPTY ; sja1000接收缓冲区为空
** SJA_RRXVUF_ERR ; sja1000接收数据错
**说明: 该函数用于从sja1000读取数据
********************************************************************************************
*/
/*======================调用示例=================================================*/
char STATUS;
unsigned char buffer[8];
volatile SJAFrameStruct FrameInfo;
STATUS = SJAReadDataFromRxBuf (buffer,& FrameInfo);
switch(STATUS ){
case SJA_OK: //读取数据成功处理
break;
case SJA_RXBUFEMPTY: // sja1000接收缓冲区为空
break;
case SJA_RRXVUF_ERR :// sja1000接收数据错
break;
default: break;
}
/*=============================================================================*/
2.15 执行SJA1000的命令
/*
********************************************************************************************
**函数原型: char SJASystemPrgCMD(unsigned char cmd)
**参数说明: cmd SJA1000 的PeliCAN 工作方式命令字
**返回值:
** SJA_OK ; 表示命令执行成功
** SJA_IMPCMD_ERR ; 表示命令执行出错
** SJA_NOTHISCMD ; 没有此命令
**
**说明: 该函数用于执行sja1000的命令
********************************************************************************************
*/
/*======================调用示例=================================================*/
char STATUS;
STATUS = SJASystemPrgCMD (0x10); //自接收请求命令
switch(STATUS ){
case SJA_OK: //命令执行成功处理
break;
case SJA_IMPCMD_ERR: //命令执行出错处理
break;
case SJA_NOTHISCMD :// 没有此命令
break;
default: break;
}
/*=============================================================================*/
2.16 向CAN总线发送数据
/*
********************************************************************************************
**函数原型: char SJASendData(unsigned char *databuf,
** unsigned char cmd
** )
**参数说明: databuf 存放要发送到总线的特定帧格式的数据的缓冲区首地址
** cmd 发送命令字
**返回值: 0 ; 表示将数据成功的发送到can总线
** SJA_TXBUFLOCK ; sja1000发送缓冲区锁定
** SJA_WTXBUF_ERR ; 表示写发送缓冲区失败
** SJA_IMPCMD_ERR ; 表示命令执行出错
**说明: 本函数用于向can总线发送数据
********************************************************************************************
*/
char SJASendData(unsigned char *databuf,
unsigned char cmd
);
/*======================调用示例=================================================*/
//将数据缓冲区SendBuf内的数据发送到CAN总线
//其中帧信息结构为SJAFrameInfo
//只发送1次命令
#define CMD_SINGLE_SEND 0x03
unsigned char SendBuf[8];
SJAFrameStruct SJAFrameInfo;
char STATUS;
STATUS = SJASendData(SendBuf, CMD_SINGLE_SEND);
switch(STATUS ){
case SJA_OK: //发送成功
break;
case SJA_IMPCMD_ERR: //命令执行出错处理
break;
case SJA_TXBUFLOCK :// 发送缓冲区锁定
break;
case SJA_WTXBUF_ERR :// 表示写发送缓冲区失败
break;
default: break;
}
/*=============================================================================*/
2.17 接收CAN总线数据
/*
********************************************************************************************
**函数原型: char SJARcvData(unsigned char *databuf,
** )
**参数说明: databuf 存放要接收数据缓冲区首地址
**返回值: 0 ; 表示将读取数据成功
** SJA_RXBUFEMPTY ; sja1000接收缓冲区为空
** SJA_RRXVUF_ERR ; sja1000接收数据错
**说明: 本函数用于接收can总线数据
********************************************************************************************
*/
/*======================调用示例=================================================*/
//将接收到的CAN总线数据放入数据缓冲区RcvBuf内
//其中接收到的帧信息放入帧信息结构变量SJAFrameInfo内
unsigned char RcvBuf[8];
SJAFrameStruct SJAFrameInfo;
char STATUS;
STATUS = SJARcvData(RcvBuf,& SJAFrameInfo);
switch(STATUS ){
case SJA_OK: //接收成功
break;
case SJA_RXBUFEMPTY : //接收缓冲区为空
break;
case SJA_RRXVUF_ERR :// 表示接收数据失败
break;
default: break;
}
/*=============================================================================*/
2.18 将指定的数值写入SJA1000的指定的寄存器
/*
********************************************************************************************
**函数原型: char WriteSJAReg(unsigned char *RegAdr,unsigned char Value)
**参数说明: Value 写入寄存器的值
** RegAdr 要写入sja1000的内部寄存器地址
**返回值:
** SJA_OK
**说明: 该函数用于将指定的数值写入sja1000的指定的寄存器
********************************************************************************************
*/
char WriteSJAReg(unsigned char RegAdr, unsigned char Value);
2.19 读出SJA1000指定寄存器的值
/*
********************************************************************************************
**函数原型: char ReadSJAReg(unsigned char *RegAdr)
**参数说明: RegAdr 要写入sja1000的内部寄存器地址
**返回值: 该寄存器的值
**说明: 该函数用于读出sja1000的指定的寄存器
********************************************************************************************
*/
extern unsigned char ReadSJAReg(unsigned char RegAdr);
3 库函数使用说明
在应用本库文件时,将SJA1000_PELI_APP.h头文件引入自己的应用程序,然后可以调用上述的基本
函数。在编译时将SJAPELI.lib同用户文件一起添加到同一个工程中,编译即可。
4 SJA1000编程流程
微处理器可以利用中断或查询的方式来访问SJA1000,为此用户可根据自己的实际需要来编写适当的主程序。由于CAN总线的传输速度比较快,建议应用中断方式。本文的示例也应用了中断方式。
下面就应用本库函数,编写SJA1000的应用程序作一说明:
首先对SJA1000进行操作前必须先对SJA1000的一些控制寄存器进行初始化。这些寄存器包括BasicCAN模式和PeliCAN模式选择所在的时钟分频寄存器,总线定时寄存器,以及输出控制寄存器等。
在SJA1000硬件复位上电后,应首先调用SJATestInterface()函数来测试微处理器与SJA1000是否连接正常。如果本函数不能通过测试,那么应检查物理连线,务必确信SJA1000与微处理器正确连接,才能进行后面的操作。
SJA1000上电或复位后即进入复位模式,这时便可对各控制寄存器进行操作。如要使用PeliCAN模式应调用SJAEntryPeliCANMode()函数,将SJA1000设置为PeliCAN模式,否则系统默认值为BasicCAN模式。进入PeliCAN后,系统可调用SJASetAccMask()函数来设置系统的波特率;调用SJASetOutControl()来设置输出控制;调用SJASetClockDivision()函数来设置时钟分频寄存器;调用SJASetAccMask()和SJASetAccCode()函数来设置报文滤波。可以调用SetBitMask()和ClearBitMask()函数来设置或清除SJA1000中某些特定寄存器的特定位的值。初始化完成后,请调用SJAQuitResetMode()函数退出复位模式,进入工作模式。
进入工作模式后,如应用中断方式来访问SJA1000请设置SJA1000的中断使能寄存器,来使能SJA1000可能需要的中断类型。
如发送数据可以调用SJASendData()函数,当产生接收中断后调用SJARcvData()函数接收数据。
注: 1、上文中有些函数为书写简单,只是写了它的函数名,并非函数原型,各函数原型请参考第2章的函数原型说明。
2、要正确的应用本库函数请务必确定SJA1000_PELI_APP.h文件中,SJA1000的访问基址的定义与自己硬件电路的实际相符,如不符请修改。同时请定义SJAFrameStruct 结构数据 和SJAFrameDataBufUnion联合数据结构。
5 SmallRtos下SJA1000应用示例
本应用示例显示了SJA1000在自我检测模式下,发送和接收数据的实际应用。本示例仅仅作为测试应
用,用户如有特殊的应用,请自己根据实际需要来构建自己的应用程序。
5.1 本实例中的任务
本实例中应用了3个任务,CAN发送任务(void send(void))、显示任务(void display(void))、错误处理任务(void error (void))。其中在任务send中每个2个时间周期发送1次,发送的数据为unsigned long 型的数据依次加1,接收应用中断来完成,将接收到数据的低两位字节发送到显示缓冲区。任务display为将系统显示缓冲区的内容送到LED上显示。任务error为处理系统的各种异常,将系统的异常代码送到显示缓冲区。系统与error任务之间通过信号量来通信。
同时系统的时钟节拍由自己来初始化,这样就用到在系统定时中断中调用用户函数。
这样系统中任务用到了时间溢出、用户中断、信号量等资源。
5.2 SmallRtos的配置
SmallRtos中所有的系统配置全部在os_cfg.h文件中,根据上面应用到的系统资源,os_cfg.h文件相关的配置如下:
#define OS_MAX_TASKS 3 /* 最大任务数1~16*/
#define EN_USER_TICK_TIMER 1 //禁止(0)或允许(1)系统定时中断调用用户
//函数UserTickTimer()
#define EN_OS_INT_ENTER 0 //禁止(0)或允许(1)中断嵌套管理
#define EN_TIMER_SHARING 1 // 禁止(0)或允许(1)定时器中断调用OSTimeTick()
#define TICK_TIMER_SHARING 1 // 定义进入多少次硬件中断为一次系统定时器软中断
#define EN_OS_SEM 1 // 禁止(0)或允许(1)信号量
#define EN_OS_SEM_CHK 0 //禁止(0)或允许(1)校验信号量索引
#define OS_SEM_MEM_SEL idata //信号量储空间选择,keil c51有用,必须为idata、xdata
// 不是keil c51时它必须为空格
#define OS_MAX_SEMS 2 // 最大信号量数目
#define EN_OS_SEM_PENT 1 //禁止(0)或允许(1)等待信号量
#define EN_OS_SEM_ACCEPT 0 //禁止(0)或允许(1)无等待请求信号量
#define EN_OS_SEM_INT_POST 0 //禁止(0)或允许(1)中断中发送信号量
#define EN_OS_SEM_POST 1 // 禁止(0)或允许(1)中发送信号量
#define EN_OS_SEM_QUERY 0 // 禁止(0)或允许(1)查询信号量
//#define data // 非keil c51时必须加止这一句
#ifdef IN_OS_CPU_C
#if EN_USER_TICK_TIMER > 0
extern void UserTickTimer(void); // 系统定时中断中调用的用户函数
#endif
同时在config.h文件中修改指针函数数组为:
void (* const TaskFuction[OS_MAX_TASKS])(void)={send,display,error};
5.3 示例程序编译、下载、运行
其操作参阅参考文献3
5.4 运行结果
在DP-51试验板上下载运行后,会看到LED不断显示变化的数据,显示数据为依次加1,到达0xFFFF后会从0开始,如此往复。
6 本示例运行环境
6.1 软件环境
编译器Keil C51V7.02
实时微内核SmallRTOSV1.12
6.2 硬件环境
广州致远电子有限公司生产的DP-51学习板
附A 源程序
见文件S_RTOS_TEST
附 参考文献
1 《独立的CAN控制器SJA1000 数据手册》
2 《实时微内核SmallRTOS》
3 《增强型80c51单片机应用速成与实践》
0