1、ARM 应用系统设计启动代码佘黎煌东北大学信息科学与工程学院电子信息工程研究所0 x0000 00000 xFFFF FFFF用户程序中断服务程序异常向量表1.正在执行用户程序;2.外部中断0发生中断;3.AIC硬件将中断服务程序地址装入AIC_IPER寄存器;4.程序跳转至异常向量表中IRQ入口0 x0018处;5.执行指令跳转至AIC_IPER寄存器中的中断服务地址;6.中断服务程序执行完毕,返回被中断的用户程序继续执行被中断的代码。n图示IRQ中断的发生过程Vector.s(五种异常处理C函数)nAIC_IPEREQU0 xFFF8210C;Interrupt priority enco
2、ding register nAIC_EOSCREQU0 xFFF82130;End of service command register nIMPORTIMPORTReset_Handler;init.sReset_Handler;init.snIMPORT Undefined_HandlernIMPORTSWI_HandlernIMPORTPrefetch_Handler nIMPORTAbort_HandlerVector.s(中断处理C函数)n nIMPORTWDT_Handler;Watch Dog Timer InterruptnIMPORTnIRQ0_Handler;Exter
3、nal Interrupt 0nIMPORTnIRQ1_Handler;External Interrupt 1nIMPORTnIRQ2_Handler;External Interrupt 2nIMPORTnIRQ3_Handler;External Interrupt 3nIMPORTAC97_Handler;AC97 InterruptnIMPORTLCD_Handler;LCD Controller InterruptnIMPORTRTC_Handler;RTC Controller InterruptnIMPORTUART0_Handler;UART 0 InterruptnIMPO
4、RTUART1_Handler;UART 1 InterruptnIMPORTUART2_Handler;UART 2 InterruptnIMPORTUART3_Handler;UART 3 InterruptnIMPORTTIMER0_Handler;Timer Interrupt 0Vector.s(中断处理C函数)nIMPORTIMPORTTIMER1_HandlerTIMER1_Handler;Timer Interrupt 1;Timer Interrupt 1nIMPORTIMPORTUSBH0_HandlerUSBH0_Handler;USB Host Interrupt 0;
5、USB Host Interrupt 0nIMPORTIMPORTUSBH1_HandlerUSBH1_Handler;USB Host Interrupt 1;USB Host Interrupt 1nIMPORTIMPORTEMCTX_HandlerEMCTX_Handler;EMC TX Interrupt;EMC TX InterruptnIMPORTIMPORTEMCRX_HandlerEMCRX_Handler;EMC RX Interrupt;EMC RX InterruptnIMPORTIMPORTGDMA0_HandlerGDMA0_Handler;GDMA Channel
6、Interrupt 0;GDMA Channel Interrupt 0nIMPORTIMPORTGDMA1_HandlerGDMA1_Handler;GDMA Channel Interrupt 1;GDMA Channel Interrupt 1nIMPORTIMPORTSDIO_HandlerSDIO_Handler;SDIO Interrupt;SDIO InterruptnIMPORTIMPORTUSBD_HandlerUSBD_Handler;USB Device Interrupt;USB Device InterruptnIMPORTIMPORTSC0_HandlerSC0_H
7、andler;SmartCard Interrupt 0;SmartCard Interrupt 0nIMPORTIMPORTSC1_HandlerSC1_Handler;SmartCard Interrupt 1;SmartCard Interrupt 1nIMPORTIMPORTI2C0_HandlerI2C0_Handler;I2C Interrupt 0;I2C Interrupt 0nIMPORTIMPORTI2C1_HandlerI2C1_Handler;I2C Interrupt 1;I2C Interrupt 1nIMPORTIMPORTSSP_HandlerSSP_Handl
8、er;SPI Interrupt;SPI InterruptnIMPORTIMPORTPWM_HandlerPWM_Handler;PWM Interrupt;PWM InterruptnIMPORTIMPORTKPI_HandlerKPI_Handler;KPI Interrupt;KPI InterruptnIMPORTIMPORTPS2_HandlerPS2_Handler;PS2 Interrupt;PS2 InterruptnIMPORTIMPORTIRQ45_HandlerIRQ45_Handler;IRQ45 Interrupt;IRQ45 Interrupt中断向量表的定义(v
9、ector.s)nAREA Vect,CODE,READONLYn ENTRYnstart n BReset_Handlern BUndefined_Addr n BSWI_Addrn BPrefetch_Addrn BAbort_Addrn B.n BIRQ_Addrn BFIQ_Addr异常的处理(vector.s)nUndefined_Addr ;未定义异常n STMFDSP!,R0-R12,lrn BLUndefined_Handlern LDMFDSP!,R0-R12,LR n SUBS PC,LR,#0 nSWI_Addr ;软件中断(管理模式)n STMFDSP!,R0-R12,
10、lrn BLSWI_Handlern LDMFDSP!,R0-R12,LR n SUBS PC,LR,#0nPrefetch_Addr ;预取指令异常n STMFDSP!,R0-R12,lrn BLPrefetch_Handlern LDMFDSP!,R0-R12,LR n SUBS PC,LR,#4nAbort_Addr ;中止异常n STMFDSP!,R0-R12,lrn BLAbort_Handlern LDMFDSP!,R0-R12,LR n SUBS PC,LR,#8异常的处理(vector.s)nIRQ_Addr ;IRQ异常n STMFD SP!,R0-R12,lr n LDRR
11、0,=ISR_BASE n LDRR1,=AIC_IPERn LDRR2,R1n LDRPC,R2,R0 n nFIQ_Addr ;FIQ异常n STMFD SP!,R0-R12,lr n LDRR0,=ISR_BASE n LDRR1,=AIC_IPERn LDRR2,R1n LDRPC,R2,R0 中断向量表(vector.s)n ;interrupt service routine Entry_address definitionnISR_BASEnDCD_ISR_FAKE nDCD_ISR_INT1 nDCD_ISR_INT2 nDCD_ISR_INT3 nDCD_ISR_INT4 n
12、DCD_ISR_INT5 nDCD_ISR_INT6nDCD_ISR_INT7nDCD_ISR_INT8nDCD_ISR_INT9nDCD_ISR_INT10nDCD_ISR_INT11nDCD_ISR_INT12nDCD_ISR_INT13nDCD_ISR_INT14nDCD_ISR_INT15nDCD_ISR_INT16nDCD_ISR_INT17nDCD_ISR_INT18nDCD_ISR_INT19nDCD_ISR_INT20nDCD_ISR_INT21nDCD_ISR_INT22nDCD_ISR_INT23nDCD_ISR_INT24nDCD_ISR_INT25nDCD_ISR_IN
13、T26nDCD_ISR_INT27nDCD_ISR_INT28nDCD_ISR_INT29 nDCD_ISR_INT30nDCD_ISR_INT31 中断函数的设计(vector.s)n_ISR_FAKEn LDRR1,=AIC_EOSCRn STRR0,R1n LDMFDSP!,R0-R12,LRn SUBSPC,LR,#4n_ISR_INT1n BLWDT_Handler ;Watch Dog Timer Interruptn LDRR1,=AIC_EOSCRn STRR0,R1n LDMFDSP!,R0-R12,LR n SUBSPC,LR,#4 n_ISR_INT2n BLnIRQ0_
14、Handler ;External Interrupt 0n LDRR1,=AIC_EOSCRn STRR0,R1n LDMFDSP!,R0-R12,LR n SUBSPC,LR,#4 void nIRQ0_Handler(void)(init.s)启动代码(变量定义部分)nMode_USR EQU 0 x10nMode_FIQ EQU 0 x11nMode_IRQ EQU 0 x12nMode_SVC EQU 0 x13nMode_ABT EQU 0 x17nMode_UNDEF EQU 0 x1BnMode_SYS EQU 0 x1FnI_Bit EQU 0 x80 ;when I bit
15、 is set,IRQ is disablednF_Bit EQU 0 x40 ;when F bit is set,FIQ is disablednRAM_Limit EQU 0 xffe02000 ;For unexpanded W90P710 boardnUND_Stack EQU RAM_LimitnAbort_Stack EQU RAM_Limit-256nIRQ_Stack EQU RAM_Limit-512 ;followed by IRQ stacknFIQ_Stack EQU RAM_Limit-768 ;followed by IRQ stacknSVC_Stack EQU
16、 RAM_Limit-1024 ;SVC stack at top of memorynUSR_Stack EQU 0 xffe01000 ;followed by USR(SYS)stack(1)初始化流程(init.s)n(禁止所有中断)nAREA Init,CODE,READONLYn ENTRYn EXPORTReset_HandlernReset_HandlernMRSr0,CPSRnORR r0,r0,#I_Bit nORRr0,r0,#F_BitnMSRCPSR_c,r0初始化流程(init.s)n LDR r0,=AIC_MDCR ;关闭所有中断源n LDRr1,=0 x7FF
17、FEn STRr1,r0初始化流程(init.s)n LDR r0,=REG_WTCR;Disable watch dognMOV r1,#0nSTR r1,r0nnLDR r0,=REG_CAHCNF n;LDR r1,=0 x7 ;Enable write buffer and I/D_cachenMOV r1,#0 ;Disable write buffer and I/D_cache初始化流程(init.s)n IF RELEASEn;*n;Initalize the memoryn;FLASH4 M(base address 0 x00000000)n;SDRAM16M(base a
18、ddress 0 x01000000)n;*n LDR r0,=MemoryInitn LDMIA r0,r1-r6n LDR r0,=EBI_Ctrln STMIA r0,r1-r6初始化流程(init.s)n;*n;Self copy from FLASH to SDRAMn;*n LDRr0,=|Image$RO$Base|n LDRr1,=|Image$RO$Limit|n LDRr2,=|Image$RW$Base|n LDRr3,=|Image$RW$Limit|n SUB r1,r1,r0n SUBr3,r3,r2n ADDr1,r1,r3n LDRr2,=0 x1000000
19、;16MnSelfCopyn LDRr3,r0,#4n STRr3,r2,#4n SUBSr1,r1,#4n BNESelfCopyn ENDIF 初始化流程(init.s)n在一个简单的image里面:Image$RO$Base:是RO段的执行地址开始和装载地址开始,由-RO-BASE这个参数指定的Image$RO$Limit:是RO段的装载地址结束的后一个地址,也就是RW的装载地址的开始。n没运行:没运行:flashflash中的中的roro段是段是ro-basero-base指定的地址开指定的地址开始,等始,等roro段完了后面紧接着就是段完了后面紧接着就是rwrw段段运行时:flash
20、中的ro段地址没有动,还是Image$RO$BaseImage$RO$limit-1,原来flash中的rw段移到了sdram中,地址为Image$RW$BaseImage$ZI$Base初始化流程(init.s)n;*n;remap the memoryn;FLASH 4 M(base address 0 x7F000000)n;SDRAM 16M(base address 0 x00000000)n;*n LDR r0,=MemoryRemapn LDMIA r0,r1-r6n LDR r0,=EBI_Ctrln STMIA r0,r1-r6(2)初始化流程(初始化各模式堆栈)n MSR
21、CPSR_c,#Mode_UNDEF:OR:I_Bit:OR:F_Bitn LDR SP,=UND_Stackn MSR CPSR_c,#Mode_ABT:OR:I_Bit:OR:F_Bit n LDR SP,=Abort_Stackn MSR CPSR_c,#Mode_IRQ:OR:I_Bit:OR:F_Bitn LDR SP,=IRQ_Stackn MSR CPSR_c,#Mode_FIQ:OR:I_Bit:OR:F_Bitn LDR SP,=FIQ_Stackn MSR CPSR_c,#Mode_SVC:OR:I_Bit:OR:F_Bitn LDR SP,=SVC_Stack n MSR
22、 CPSR_c,#Mode_SYS:OR:I_Bit:OR:F_Bitn LDR SP,=USR_Stack(3)初始化流程(使能中断)nMSRCPSR_c,#Mode_USRnIMPORTMainnBMain nB.STMIA,LDMIA块拷贝 EXPORT MydataCopyMydataCopy MOVS R3,R2,LSL#3 BEQ copywords STMFD sp!,r4-r11Octcopy LDMIA R0!,R4-R11 STMIA R1!,R4-R11 SUBS R3,R3,#1 BNE Octcopy LDMFD sp!,r4-r11copywords ANDS R2
23、,R2,#7 BEQ stopwordcopy LDR R3,R0,#4 STR R3,R1,#4 SUBS R2,R2,#1 BNE wordcopystop mov pc,lr END Swi异常的处理nSwi_handlerMRS R0,SPSR ;读取SPSR_SVCD到R0TST R0,#0X20 ;判断标志T是否置位LDRNEH R0,LR,-2 ;读取LR-2地址的半字到R0BICNE R0,0XFF00 ;取THUMB指令的低8位为 ;软中断号LDREQ R0,LR,#-4 ;读取LR-4地址的字到R0BICEQ R0,0XFF000000;取ARM指令的低24位为 软中断号程
24、序状态寄存器27273131N Z C V N Z C V Q Q28286 67 7I F I F T T mode mode161623238 815155 54 40 02424f fs sx xc c U n d e f i n e dU n d e f i n e dJ Jn后缀后缀s sn数据处理指令可以选择s后缀,以影响状态标志。但是比较指令(cmp、cmn、tst和teq)不需要后缀s,它们总会直接影响cpsr中的状态标志。n在数据处理指令中,除了比较指令以外,其它的指令如果带有s后缀,同时又以pc为目标寄存器进行操作,则操作的同时从spsr恢复cpsr。比如:nmovs pc
25、,#0 xff/*cpsr=spsr;pc=0 xff*/nadds pc,r1,#0 xffffff00/*cpsr=spsr;pc=r1+0 xffffff00*/nands pc,r1,r2/*cpsr=spsr;pc=r1&r2;*/n如果在user或者system模式下使用带有s后缀的数据处理指令,同时以pc为目标寄存器,那么会产生不可预料的结果。因为user和system模式下没有spsr。27273131N Z C V N Z C V Q Q28286 67 7I F I F T T mode mode161623238 815155 54 40 02424f fs sx xc
26、c U n d e f i n e dU n d e f i n e dJ JTHANK YOUSUCCESS2024/5/7 周二26可编辑n注意:只有在特权模式下才能修改状态寄存器的控制域7:0,以实现处理器模式转换,或设置开/关异常中断。程序中不能通过MSR指令直接修改CPSR中的T控制位来实现ARM状态Thumb状态的切换,必须使用BX指令完成处理器状态的切换。用户模式下不能对CPSR23:0做修改。n例例nMRS R0MRS R0,CPSRCPSRnBIC R0,R0,#0 x1F ;R0后5位清0 nORR R0,R0,#0 x1F ;R0后5位赋值为0b11111nMSR CPS
27、R,R0 ;根据模式位4:0切换工作模式到系统模式nMOV R13,#1nMOV R14,#2nMRS R0MRS R0,CPSRCPSR nBIC R0,R0,#0 x1F ;R0后5位清0nORR R0,R0,#0 x11 ;R0后5位赋值为0b10001nMSR CPSR,R0 ;根据模式位4:0切换工作模式到FIQ模式nMOV R13,#33nMOV R14,#44nMRS R0MRS R0,CPSRCPSR nBIC R0,R0,#0 x1F ;R0后5位清0 nORR R0,R0,#0 x10 ;R0后5位赋值为0b10000nMSR CPSR,R0 ;根据模式位4:0切换工作模式
28、到用户模式n(四)一些基本的(四)一些基本的ARMARM指令功能段指令功能段n4.1 算数逻辑运算指令的应用n例1:实现乘法的指令段MOV R0,R0,LSL#n ;R0=R0b)a=a-b;else b=b-a;return a;v对应的ARM代码段。(代码执行前R0中存放a,R1中存放b;代码执行后R0中存放最大公约数。gcbCMP R0,R1 ;比较a和b的大小SUBGT R0,R0,R1;if(ab)a=a-bSUBLT R1,R1,R0;if(ba)b=b-aBNE gcb ;if(a!=b)跳转到gcb继续执行MOV PC,LR ;子程序结束,返回n例3:循环语句n下面代码段实现了
29、程序循环执行。MOV R0,#loopcount ;初始化循环次数 loop ;循环体 SUBS R0,R0,#1 ;循环计数器减1,设置条件标志 BNE loop ;循环计数器不为0,跳到loop继续执行 ;循环计数器为0,程序继续执行程序状态寄存器27273131N Z C V N Z C V Q Q28286 67 7I F I F T T mode mode161623238 815155 54 40 02424f fs sx xc c U n d e f i n e dU n d e f i n e dJ J程序状态寄存器NZCV IM0M1M2M3M4TF.31 30 29 28
30、27 26 8 7 6 5 4 3 2 1 0条件代码标志保留控制位溢出标志进位或借位扩展零负或小于IRQ禁止FIQ禁止状态位模式位NZCVITFCPSR寄存器的格式大多数大多数“数值处理指令数值处理指令”可以可以选择是否影响条件代码标志位(指选择是否影响条件代码标志位(指令带令带S S后缀);但有些指令执行总是后缀);但有些指令执行总是影响条件代码标志。影响条件代码标志。所有所有ARMARM指令都可按条件来指令都可按条件来执行,而执行,而ThumbThumb指令中只有分支指令指令中只有分支指令可按条件执行。可按条件执行。运运算算结结果果的的最最高高位位反反映映在在该该标标志志位位。对对于于有
31、有符符号号二二进进制制补补码码,结结果果为为负负数数时时N=1N=1,结结果果为为正正数数或或零时零时N=0N=0;指令结果为指令结果为0 0时时Z=1Z=1(表示比(表示比较结果较结果“相等相等”),否则),否则Z=0Z=0;当进行加法运算,并且最高位产生进位时C=1,否则C=0。当进行减法运算,并且最高位产生借位时C=0,否则C=1。对于移位操作指令,C为从最高位最后移出的值,其它指令C通常不变;当进行加法当进行加法/减法运算,减法运算,并且发生有符号溢出时并且发生有符号溢出时V=1V=1,否则,否则V=0V=0,其它指令,其它指令V V通常不变。通常不变。最低最低8 8位为控制位,当发生
32、异常时,这些位为控制位,当发生异常时,这些位被硬件改变。当处理器处于一个特权模式位被硬件改变。当处理器处于一个特权模式时,可用软件操作这些位。时,可用软件操作这些位。保留位被保留将来使用。为了提高程序的可保留位被保留将来使用。为了提高程序的可移植性,当改变移植性,当改变CPSRCPSR标志和控制位时,请不要标志和控制位时,请不要改变这些保留位。另外,请确保您程序的运行改变这些保留位。另外,请确保您程序的运行不受保留位的值影响,因为将来的处理器可能不受保留位的值影响,因为将来的处理器可能会将这些位设置为会将这些位设置为1 1或者或者0 0。操作码条件助记符标志含义0000EQZ=1相等0001N
33、EZ=0不相等0010CS/HSC=1无符号数大于或等于0011CC/LOC=0无符号数小于0100MIN=1负数0101PLN=0正数或零0110VSV=1溢出0111VCV=0没有溢出1000HIC=1,Z=0无符号数大于1001LSC=0,Z=1无符号数小于或等于1010GEN=V有符号数大于或等于 1011LTN!=V有符号数小于 1100GTZ=0,N=V有符号数大于 1101LEZ=1,N!=V有符号数小于或等于 1110AL任何无条件执行(指令默认条件)1111NV任何从不执行(不要使用)指令条件码表CPSR模式位设置表M4:0模式模式M4:0模式模式10000用户用户10111
34、中止中止10001快中断快中断11011未定义未定义10010中断中断11111系统系统10011管理管理注意:不是所有模式位的组合都定义了有效的处理器模式,如果使用了错误的设置,将引起一个无法恢复的错误。Memory(contd)nMemory unitnAddressnDatanControl signalsnReadnWriteMemory(contd)nRead cycle1.Place address on the address bus2.Assert memory read control signal3.Wait for the memory to retrieve the d
35、atanIntroduce wait stateswait states if using a slow memory4.Read the data from the data bus5.Drop the memory read signalnIn Pentium,a simple read takes three clocks cyclesnClock 1:steps 1 and 2nClock 2:step 3nClock 3:steps 4 and 5Memory(contd)nWrite cycle1.Place address on the address bus2.Place da
36、ta on the data bus3.Assert memory write signal4.Wait for the memory to retrieve the datanIntroduce wait stateswait states if necessary5.Drop the memory write signalnIn Pentium,a simple write also takes three clocksnClock 1:steps 1 and 3nClock 2:step 2nClock 3:steps 4 and 5ARM7TDMI指令集编码1.1.存储器从存储器从0
37、x4000000 x400000开始的开始的100100个单元中存放着个单元中存放着ASCIIASCII码,编写程序,将其所有的小写字母转换码,编写程序,将其所有的小写字母转换成大写字母,对其它的成大写字母,对其它的ASCIIASCII码不做变换。码不做变换。(0 x610 x7a(0 x610 x7a为小写为小写,0 x410 x5a,0 x410 x5a为相应的大写为相应的大写,转转换只需把处于换只需把处于0X610X710X610X71中的数减去中的数减去0 x20,0 x20,在写回在写回存储器即刻存储器即刻)2.2.编写程序,比较存储器中编写程序,比较存储器中0 x4000000 x
38、400000和和0 x4000040 x400004两两无符号字数据的大小,并且将比较结果存于无符号字数据的大小,并且将比较结果存于0 x4000080 x400008的字中,若两数相等其结果记为的字中,若两数相等其结果记为0 0,若,若前者大于后者其结果记为前者大于后者其结果记为1 1,若前者小于后者其,若前者小于后者其结果记为结果记为-1-1。编写一程序,存储器中从编写一程序,存储器中从0 x4002000 x400200开始有一个开始有一个6464位位数。(数。(1 1)将取反,再存回原处;()将取反,再存回原处;(2 2)求其补码,)求其补码,存放到存放到0 x4002080 x400
39、208处处 。n编写一简单编写一简单ARMARM汇编程序段,实现汇编程序段,实现1+2+1+2+100+100的的运算。运算。1 1 LDRLDR伪指令与伪指令与LDRLDR加载指令的功能和应用有何区别加载指令的功能和应用有何区别,举例说明?举例说明?2 2 编写一简单编写一简单ARMARM汇编程序段,实现汇编程序段,实现1+2+1+2+100+100的的运算。运算。3 3 编写程序,比较存储器中编写程序,比较存储器中0 x4000000 x400000和和0 x4000040 x400004两两无符号字数据的大小,并且将比较结果存于无符号字数据的大小,并且将比较结果存于0 x4000080
40、x400008的字中,若两数相等其结果记为的字中,若两数相等其结果记为0 0,若前,若前者大于后者其结果记为者大于后者其结果记为1 1,若前者小于后者其结果,若前者小于后者其结果记为记为-1-1。4 4 将存储器中将存储器中0 x4000000 x400000开始的开始的200200字节的数据,传字节的数据,传送到送到0 x4008000 x400800开始的区域。开始的区域。5 5 编写一程序,存储器中从编写一程序,存储器中从0 x4002000 x400200开始有一个开始有一个6464位数。(位数。(1 1)将取反,再存回原处;()将取反,再存回原处;(2 2)求其补)求其补码,存放到码
41、,存放到0 x4002080 x400208处处 。阶段测验阶段测验嵌入系统的构成D#include n#include n n#if CONFIG_MODVERSIONS=1n#define MODVERSIONSn#include n#endifn#include n#include n#include n#include n#include n#ifndef KERNEL_VERSIONSn#define KERNEL_VERSIONS(a,b,c)(a)*65536+(b)*256+(c)n#endifn n#if LINUX_VERSION_CODE KERNEL_VERSION(2
42、,2,0)n#include n#endifn D#define SUCCESS 0n#define DEVICE_NAME char_driver_testn#define BUF_LEN 80nstatic char MessageBUF_LEN;Dstatic int device_open(struct inode*inode,struct file*file)nnMOD_INC_USE_COUNT;nreturn SUCCESS;nnstatic int device_release(struct inode*inode,struct file*file)nnMOD_DEC_USE_
43、COUNT;nreturn 0;nnstatic ssize_t device_write(struct file*file,const char*buffer,size_t length,loff_t*offset)nncopy_from_user(Message,buffer,length);nprintk(device writenr);nreturn length;nnstatic ssize_t device_read(struct file*file,char*buffer,size_t length,loff_t*offset)nncopy_to_user(buffer,Mess
44、age,length);nreturn length;nDstatic int Major;nstruct file_operations Fops=nllseek:NULL,nread:device_read,nwrite:device_write,nreaddir:NULL,n/select:NULL,nioctl:NULL,nmmap:NULL,nopen:device_open,nrelease:device_releasen;Dint init_module()nn/int ret_val;nMajor=register_chrdev(0,DEVICE_NAME,&Fops);nreturn 0;nnvoid cleanup_module()nnint ret;nret=unregister_chrdev(Major,DEVICE_NAME);nTHANK YOUSUCCESS2024/5/7 周二51可编辑