定点运算和定点数制转换 ;范例1 LSDAA: ADC R16,R16 ;十进制数(在R16中)左移调整子程序 ADDAA: IN R6,SREG ;bcd码相加调整子程序,先保存相加后的 LDI R17,$66 ;状态the old status ADD R16,R17 ;再将和预加立即数$66 IN R17,SREG ;输入相加后新状态(the new status) OR R6,R17 ;新旧状态相或 SBRS R6,0 ;相或后进位置位则跳行 SUBI R16,$60 ;否则减去$60(十位bcd不满足调整条件) SBRS R6,5 ;半进位置位则跳行 SUBI R16,6 ;否则减去$06(个位bcd不满足调整条件) ROR R6 ;向高位字节BCD返还进位位! RET ;范例2 SUDAA: BRCC SBD1 ;bcd码减法调整子程序,差在R16中 BRHC SBD3 SUBI R16,$66 ;进位半进位都置位,将差减去立即数$66 SEC ;并恢复借位C RET ;ret. with seC SBD1: BRHC SBD2 ;进位半进位都清位,返回 SUBI R16,6 ;进位清除而半进位置位,将差减去6 SBD2: RET ;ret. with clC SBD3: SUBI R16,$60 ;进位置位而半进位清除,将差减去$60 SEC ;并恢复借位C RET ;ret. with seC ;范例3 RSDAA: SBRC R16,7 ;bcd码(在R16中)右移调整子程序 SUBI R16,$30 ;十位BCD最高位为1(代表8),将其变为5(否则跳行) SBRC R16,3 SUBI R16,3 ;个位BCD最高位为1(代表8),将其变为5(否则跳行) RET ;范例4 ADBCD4: MOV R16,R15 ;4字节压缩bcd码相加子程序 ADD R16,R11 ;R12,R13,R14,R15内为被加数,R8,R9,R10,R11内为加数 RCALL ADDAA ;相加后调整 MOV R15,R16 ;并返还调整后结果 MOV R16,R14 ADC R16,R10 RCALL ADDAA MOV R14,R16 MOV R16,R13 ADC R16,R9 RCALL ADDAA MOV R13,R16 MOV R16,R12 ADC R16,R8 RCALL ADDAA MOV R12,R16 RET ;范例5 ADBCD: LDI R16,4 ;多字节压缩bcd码相加子程序 MOV R7,R16 ;(r7):字节数 CLC ADLOP: LD R16,-X ;X-1指向被加数; LD R6,-Y ;Y-1指向加数 ADC R16,R6 RCALL ADDAA ;相加后调整 ST X,R16 ;返还调整后结果 DEC R7 BRNE ADLOP RET ;范例6 SUBCD4: MOV R16,R15 ;4字节压缩bcd码减法子程序 SUB R16,R11 ;R12,R13,R14,R15内为被减数,R8,R9,R10,R11内为减数 RCALL SUDAA ;相减后调整 MOV R15,R16 ;并返还调整后结果 MOV R16,R14 SBC R16,R10 RCALL SUDAA MOV R14,r16 MOV R16,R13 SBC R16,R9 RCALL SUDAA MOV R13,R16 MOV R16,R12 SBC R16,R8 RCALL SUDAA MOV R12,R16 RET ;范例7 SUBCD: LDI R16,4 ;多字节压缩bcd码相减子程序 MOV R7,R16 ;(r7):压缩bcd码字节数 CLC SUBLP: LD R16,-X ;X-1指向被减数 LD R6,-Y ;Y-1指向减数 SBC R16,R6 RCALL SUDAA ;相减后调整 ST X,R16 ;返还调整后结果 DEC R7 BRNE SUBLP RET ;范例8 ;16位被乘数*16位乘数-->32位积 MUL16: LDI R16,17 ;(r10r11)*(r14r15)-->r12r13r14r15 ClR R12 ClR R13 ;积的高位字预清除 CLC ;第1次只右移,不相加 MLOOP: BRCC MUL1 ; ADD R13,R11 ;乘数右移移出位为1,将被乘数加入部分积 ADC R12,R10 MUL1: ROR R12 ROR R13 ROR R14 ROR R15 ;部分积连同乘数整体右移1位 DEC R16 BRNE MLOOP ;17次右移后结束 RET ;范例9 ;16位整数被乘数*16位小数乘数-->16位整数积,精确到0.5 MUL165: RCALL MUL16 ;先得到32位积 SBRS R14,7 ;积小数部分最高位为1,将整数部分加1 RET ;否则返回 LDI R17,255 SUB R13,R17 SBC R12,R17 ;以减去-1($FFFF)替代加1 RET ;范例10 ;32位被除数/16位除数-->16位商,精确到1 DIV16: LDI R16,16 ;(r12r13r14r15)/(r10r11)-->r14r15 DLOOP: LSL R15 ROL R14 ROL R13 ROL R12 ;被除数左移1位 BRCS DI1 SUB R13,R11 SBC R12,R10 ;移出位为0,被除数高位字减去除数试商 BRCC DI2 ;够减,本位商为1 ADD R13,R11 ADC R12,R10 ;否则恢复被除数 RJMP DI3 ;本位商0 DI1: SUB R13,R11 SBC R12,R10 ;移出位为1,被除数高位字减去除数 DI2: INC R15 ;本位商1 DI3: DEC R16 BRNE DLOOP RET ;范例11 ;32位被除数/16位除数-->16位商,精确到0.5 ;可能产生溢出!例$7FFFC000/$8000=$FFFF.8->$10000! DIV165: RCALL DIV16 ;(r12r13r14r15)/(r10r11)-->r14r15 LSL R13 ROL R12 ;余数乘2 BRCS D165 ;有进位,转5入 SUB R13,R11 SBC R12,R10 ;否则,余数乘2减去除数 BRCS D164 ;不够减,转4舍 D165: CLR R13 ;否则将商增1 SEC ADC R15,R13 ADC R14,R13 ADC R13,R13 ;若有溢出,溢出位在R13中 RET D164: CLR R13 RET ;范例12 ;32位整数/16位整数->16整数+16位小数->4字节浮点数 ;(r12r13r14r15)/(r10r11)-->r12r13r14r15 DIV16F: RCALL DIV16 ;先做整数除法 MOV R9,r15 MOV R8,r14 ;保存整数部分 CLR R15 CLR R14 RCALL DIV16 ;除得小数部分 MOV R11,R15 MOV R15,R14 MOV R13,R8 MOV R14,R9 ;整数部分在r13r14,小数部分在r15r11 LDI R17,$90 ;预设阶码$90(整数为16位) MOV R12,R17 LDI R17,32 ;设32次右移 DIV16L: SBRC R13,7 RJMP NMLDN ;最高位为1,已完成规格化 LSL R11 ;否则继续右移R13,R14,R15,R11 ROL R15 ROL R14 ROL R13 DEC R12 ;阶码减1 DEC R17 BRNE DIV16L CLR R12 ;右移达32次,浮点数为零,置零阶 RET NMLDN: SBRS R11,7 RJMP DIVRT ;欲舍去部分(R11)最高位为0,转4舍 RCALL INC3 ;否则尾数部分增1 BRNE DIVRT INC R12 ;尾数增1后变为0,改为0.5,并将阶码增1 DIVRT: LDI R17,$7F ;将尾数最高位清除,表示正数(负数不要清除) AND R13,R17 ;规格化浮点数在R12(阶码)R13R14R15(尾数)中 RET ;范例13 ;(R16,R12,R13,R14,R15)/(R10,R11)-->R13,R14,R15 DIV24: CLR R16 ;32位整数/16位整数->24位整数,要求(R10)不为0;否则 ;要求(R12)<(R11) DIV40: LDI 17,24 ;40位整数/16位整数->24位整数 要求(R16,R12) LXP: LSL R15 ; <(R10,R11) ROL R14 ROL R13 ROL R12 ROL R16 BRCC LXP1 SUB R12,R11 ;右移后C=1 够减 SBC R16,R10 ;被除数减去除数 RJMP DIV0 ;本位商为1 LXP1: SUB R12,R11 ;C=0 SBC R16,R10 ;被除数减去除数试商 BRCC DIV0 ;C=0 够减,本位商1 ADD R12,R11 ADC R16,R10 ;否则恢复被除数,本位商0 RJMP DIV1 DIV0: INC R15 ;记本位商1 DIV1: DEC R17 BRNE LXP LSL R12 ROL R16 BRCS GINC ;C=1,5入 SUB R12,R11 SBC R16,R10 BRCS RET3 ;不够减,舍掉 GINC: RCALL INC3 ;将商增1 RET3: RET ;范例14 ;定点整数(最大$FFFFFFFF)开平方子程序 INTSQR: LDI R16,17 ;SQR(R12,R13,R14,R15)-->(r15r8r9) CLR R8 ;R8,R9存储平方根 CLR R9 ;r10,r11,r12,r13,r14,r15 CLR R10 ; r8, r9(根) r16 (counter) CLR R11 ;r10,r11:被开平方数扩展字节 LDI R17,$40 SQR0: SUB R12,R17 SBC R11,R9 SBC R10,R8 BRCS SQR1 SEC ;试根够减,本位根1 RJMP SQR2 SQR1: ADD R12,R17 ADC R11,R9 ADC R10,R8 CLC ;否则恢复被开平方数,本位根0 SQR2: DEC R16 BRNE SQR3 ;when the No.17bit of root be getting SQR20: ADC R9,R15 ;R15 HAVE BEEN CLEARED! ADC R8,R15 ADC R15,R15 ;将开出之根4舍5入,使根最大可达65536(=$10000)! RET ;for example:sqr.($ffff0001)≈$10000 SQR3: ROL R9 ROL R8 ;记本位根 LSL R15 ROL R14 ROL R13 ROL R12 ROL R11 ROL R10 ;被开平方数连同其扩展字节左移一位 LSL R15 ROL R14 ROL R13 ROL R12 ROL R11 ROL R10 ;被开平方数连同其扩展字节再次左移一位/左移2位开出1位根 BRCS SQR20 ;被开平方数左移2位后,若进位置位,则仅表明第17位根 ;已被提前开出且该位根=1,将平方根增1,开平方结束。 RJMP SQR0 ;否则转试下一位根 ;范例15 ;定点整数二翻十 CONV1: LDI R17,24 ;r12r13r14r15<--(r9r10r11)左移24次 MOV R7,R17 ;例:16777215<--$FFFFFF CLR R12 CLR R13 ;68719476735<--$FFFFFFFFF CLR R14 ;1099511627775<--$FFFFFFFFFF CLR R15 ;十进制数存储区予清除 CV1: LSL R11 ROL R10 ROL R9 ;二进制数整体左移一位 MOV R16,R15 RCALL LSDAA MOV R15,R16 MOV R16,R14 RCALL LSDAA MOV R14,R16 MOV R16,R13 RCALL LSDAA MOV R13,R16 MOV R16,R12 RCALL LSDAA ;十进制数左移并调整 MOV R12,R16 DEC R7 BRNE CV1 RET ;范例16 ;定点整数十翻二 CONV2: LDI R17,24 ;(r9r10r11)-->r13r14r15,右移24次 CLR R31 ;例:999999-->$0F423F MOV R7,R17 ; 99999999-->$05F5E0FF CV2: LSR R9 ROR R10 ROR R11 ROR R13 ROR R14 ROR R15 ;十进制数连同二进制数右移一位 LDI R30,12 ;数据指针 CV2L: LD R16,-Z RCALL RSDAA ;十进制数右移调整 ST Z,R16 CPI R30,9 ;十进制数各字节调整完毕? BRNE CV2L DEC R7 ;右移次数(24次)完成? BRNE CV2 RET ;范例17 ;定点小数二翻十 CONV3: LDI R17,24 ;(r13r14r15)--->r9r10r11r12右移24次 CONV31: MOV R7,R17 CLR R9 CLR R10 ;例:$0.FFFFFF-->0.99999994 CLR R11 ;$0.FFFFFFFF-->0.999999999767 CLR R12 ;$0.FFFFFFFFF->0.999999999985448 CLR R31 CV3: LSR R13 ROR R14 ROR R15 ROR R9 ROR R10 ROR R11 ROR R12 ;二进制数连同十进制数右移一位 LDI R30,9 CV3L: LD R16,Z RCALL RSDAA ;十进制数右移调整 ST Z+,r16 CPI R30,13 BRNE CV3L ;十进制数各字节调整完毕? DEC R7 BRNE CV3 ;右移次数(24次)完成? RET ;范例18 ;定点小数十翻二 CONV4: LDI R17,32 ;r12r13r14r15<--r8r9r10r11<--(r12r13r14r15) MOV R7,R17 ;左移32次 CV4: CLC ;例:$0.FFFFFFD5<--0.99999999 MOV R16,R15 ;$0.FFFFFFFF92<--0.9999999999 RCALL LSDAA MOV R15,R16 MOV R16,R14 RCALL LSDAA MOV R14,R16 MOV R16,R13 RCALL LSDAA MOV R13,R16 MOV R16,R12 RCALL LSDAA MOV R12,R16 ;定点十进制小数左移并调整 ROL R11 ROL R10 ROL R9 ROL R8 ;定点二进制小数带进位位左移一位 DEC R7 BRNE CV4 MOV R12,R8 ;最终结果转入R12--R15 MOV R13,R9 MOV R14,R10 MOV R15,R11 RET AVR实用程序 ;范例19 ;等步距线性内插计算子程序 .EQU TBLGTH=10 CHETA: LDI R16,TBLGTH-1 ;r16<--表长(即字数)-1 LDI R31,HIGH(chtbl*2);y0(函数初值)在r14r15,STEP(步长)在r10r11,自变量X在r12r13 LDI R30,LOW(chtbl*2+1);查表指针,首指数据表第1字之高位字节! RCALL CPMR1 ;X与表中第一个字型数据(X0)比较 BRCC CHRET ;Xr16r17 MOV R15,R17 MOV R14,R16 ;转入r14r15 RCALL MUL16 ;(X-Xi)*STEP-->r12r13r14r15 MOV R10,R12 MOV R11,R13 ;保存乘积高位字 LPM ;X(i+1)低位字节 MOV R13,R0 ADIW R30,1 LPM ;X(i+1)高位字节 MOV R12,R0 SBIW R30,3 ;指针指向Xi RCALL SUBS ;X(i+1)-Xi-->r16r17 MOV R12,R10 MOV R13,R11 ;取回乘积高位字 MOV R10,R16 MOV R11,R17 ;X(i+1)-Xi-->r10r11 RCALL DIV165 ;(X-Xi)*STEP/[X(i+1)-Xi]-->r14r15 ADD R15,R9 ADC R14,R8 ;Y0+i*STEP+(X-Xi)*STEP/[X(i+1)-Xi]-->r14r15 RET ;若STEP为负值则改为计算(r8r9)减去(r14r15)之值 CMPR1: LPM ;取数据高位字节 ADIW R30,2 ;指向下一数据的高位字节 CP R0,R12 ;与X高位字节相比较 BRNE CPRT1 ;不相等即转出 SBIW R30,3 ;否则调整指针 LPM ;取数据低位字节 ADIW R30,3 ;指向下一数据的高位字节 CP R0,R13 ;与X低位字节相比较 CPRT1: RET ;以进位C带回比较结果 SUBS: LPM ;计算(X-Xi)或[X(i+1)-Xi]并送入r16r17 MOV R5,R0 ;取Xi低位字节 ADIW R30,1 LPM ;取Xi高位字节 SBIW R30,1 ;仍指向Xi低位字节 SUB R13,R5 MOV R17,R13 SBC R12,R0 MOV R16,R12 ;计算差并将其转入R16R17 RET ;自变量x表长为12字 CHTBL:DW 19214,23404,27600,32799,37009,40211,45414,48618,51821,55029,57787,60070 ;步距表长为11字 STEPT: DW 356,366,379,395,415,440,471,509,555,603,657 ;不等步距线性内插计算子程序,步距表首址在R6R7中 ;自变量X在R12R13之中, 函数初值Y0在R14R15中 ;范例20 ;表长(字个数)-1在R16中 CHTSTP: LDI R31,HIGH(chtbl*2) LDI R30,LOW(chtbl*2+1);查表指针 LDI R16,LOW(stept*2) MOV R7,R16 LDI R16,HIGH(stept*2) MOV R6,R16 ;步距表指针 LDI R16,TBLGTH-1 ;r16<--表长(字个数)-1 RCALL CMPR1 ;X与表首数据比较 BRCC CHSTPT ;Xr16r17 MOV R15,R17 MOV R14,R16 ;(X-Xi)转入R14R15 RCALL GTSTP ;查表取STEPi-->R10R11 RCALL MUL16 ;(X-Xi)*STEPi-->R12R13R14R15 MOV R10,R12 MOV R11,R13 ;保存积高位字 LPM MOV R13,R0 ADIW R30,1 LPM MOV R12,R0 SBIW R30,3 RCALL SUBS ;(X(i+1)-Xi)-->r16 r17 MOV R12,R10 MOV R13,R11 MOV R10,R16 MOV R11,R17 ;取回积高位字 &(X(i+1)-Xi)-->r10r11 RCALL DIV165 ;(X-Xi)*STEPi/[X(i+1)-Xi]-->r14r15 ADD R15,R9 ; ADC R14,R8 ;Y0+∑STEPk+(X-Xi)*STEPi/[X(i+1)-Xi]-->r14r15 RET GTSTP: MOV R5,R6 ;查取STEP字型变量/POINTER in r6r7! MOV R6,R30 MOV R30,R5 MOV R5,R7 MOV R7,R31 MOV R31,R5 ;(r6r7)<-->Z LPM MOV R11,R0 ADIW R30,1 LPM MOV R10,R0 ;STEPk取到r10r11 ADIW R30,1 MOV R5,R6 MOV R6,R30 MOV R30,R5 MOV R5,R7 MOV R7,R31 MOV R31,R5 ;指针增2后送回r6r7 RET ;范例21 ;功能表程序 FUNC2: LDS R16,$A3 ;use r0,r8,r9,r10,r11,r16&r17/& subprogram dspa SBR R16,$80 ;功能表程序标志 STS $A3,R16 LDI YH,2 LDI YL,0 ;功能内容表SRAM地址 RCALL FLFUNC ;CLR r27! LDI R16,2 ST X,R16 ;显示'FUNC.2' RCALL DL2S CLR R9 ;功能内容寻址偏移量R9! CLR R8 ;功能名称寻址偏移量(R8)=(r9)*3 FFUNC0: RCALL DSF_ ;显示'F- ' FF0: RCALL DSPA ;in subprogram dspy clr. r27! CPI R16,11 ;回车键按下? BRNE FF2P FF0C: RCALL COMBNO ;合成功能名称送入r16 CPI R16,20 ;是最后一个功能名称? BRNE FF1 CLR R9 ;是,两偏移量初始化! CLR R8 FF1: LDI ZH,HIGH(FTABL*2) LDI ZL,LOW(FTABL*2);功能名称表指针 ADD ZL,R8 ADC ZH,R27 ;(r27)=0 ALWAYS LPM MOV R16,R0 RCALL BRA3A ;分解新功能名称到$6E/$6F FF0G: LDI R28,0 ADD R28,R9 ;功能内容指针加偏移量 LD R16,Y LDI R26,$72 RCALL BRAX ;将新功能内容分解到$72/$73 FF0A: RCALL DSPA ;显示新功能名称/内容 CPI R16,11 BRNE FF0B ;回车键按下? INC R8 INC R8 INC R8 ;是,功能名称寻址偏移量加3 INC R9 ;功能内容寻址偏移量加1 RJMP FF0C ;转回 FF2P: RJMP FF2 FF0B: CPI R16,10 BRNE FF0D RCALL DSF_ ;清除键按下,清除显示区后,显示‘F-’ FF1B: RCALL DSPA CPI R16,11 BREQ FF1 ;转恢复当前显示 CPI R16,10 BRCC FF1B RJMP FF2D ;只有数字键按下才转出去处理 FF0D: CPI R16,10 BRCC FF0A FF1D: LDI R17,$24 ; STS $73,R17 ;数字键处理,先在缓存区内放一空白 FF0E: LDS R17,$73 STS $72,R17 ;键入数字左移 STS $73,R16 ;存入新数字 FF0F: RCALL DSPA CPI R16,10 BREQ FF0G ;清除键按下,恢复显示旧功能内容 BRCS FF0E ;键入数字左移更新 CPI R16,11 BRNE FF0F LDS R26,$72 ;回车键按下 RCALL COMBA ;合成新功能内容(combin $72&$73 into binary(r16)) MOV R17,R8 INC R17 LDI ZH,HIGH(FTABL*2) LDI ZL,LOW(FTABL*2) ADD ZL,R17 ;取当前功能内容下限 ADC ZH,R27 FF1F: LPM CP R16,R0 BRCS DSER2 ;新功能内容小于下限,错误 INC R17 LDI ZH,HIGH(FTABL*2) LDI ZL,LOW(FTABL*2) ADD ZL,R17 ;取当前功能内容上限 ADC ZH,R27 LPM CP R0,R16 BRCS DSER3 ;新功能内容大于上限,错误 FF7: LDI R28,0 ADD R28,R9 ;功能内容表首地址为$200! ST Y,R16 ;合法的新功能内容进入功能内容表 INC R9 INC R8 INC R8 INC R8 ;调整偏移量,进入下一个功能显示 RJMP FF0C FF1P: RJMP FF1 DSER2: RCALL FERR2 ;显示'F Err.2'2秒 RCALL EXCH0 RJMP FF0G ;恢复原数据显示 DSER3: RCALL FERR3 ;显示'F Err.3'2秒 RCALL EXCH0 RJMP FF0G ;恢复原数据显示 FF2: CPI R16,10 BRCS FF2D ;功能键按下,转初始 RJMP FF0 FF2D: LDI R17,$24 ;数字键按下,在显示缓存区内左移 STS $6F,R17 ; FF3: LDS R17,$6F STS $6E,R17 STS $6F,R16 FF4: RCALL DSPA CPI R16,10 BRNE FF41 RCALL DSF_ ;清除数字,显示‘F-’ FF40: RCALL DSPA CPI R16,11 BREQ FF1P ;转回显示当前功能名称及内容 CPI R16,10 BRCC FF40 ;无效键按下,转回 RJMP FF2D ;否则转数字处理 FF41: BRCS FF3 CPI R16,11 BRNE FF4 RCALL COMBNO ;合成新功能名称 CLR R10 ;功能名称偏移量计数器清除 CLR R11 ;功能内容偏移量计数器清除 SFFLP: LDI ZH,HIGH(FTABL*2) LDI ZL,LOW(FTABL*2) ADD ZL,R10 ADC ZH,R27 LPM CP R0,R16 ; BREQ SFFND ;在功能名称表中找到新名称 INC R11 ; INC R10 INC R10 INC R10 ;调整偏移量 LDI R17,60 CP R10,R17 ;功能名称指针偏移量超过59? BRCS SFFLP ;否,继续查功能名称表 RCALL FERR1 ;查完功能名称表未查到键入功能名称! RJMP FFUNC0 ;转回恢复原显示 SFFND: MOV R9,R11 ;得到功能内容指针偏移量 MOV R8,R10 ;得到功能名称指针偏移量 RJMP FF0G ;转显示新功能名称及内容 FTABL: .DB 1,0,1,2,1,8,3,0,2,4,0,1 5,1,2,6,0,4,7,1,4,8,1,2,9,2,7,10,1,5,11,1 .DB 5,12,0,5,13,1,2,14,1,7,15,1,10,16,1,4,17,2,4,18,2,5,19,1,2,20,1,3 COMBNO: LDI XL,$6E ;取$6E$6F中的BCD码,合成新功能名称子程序 COMBA: LD R16,X+ CPI R16,$24 BRNE CMBA CLR R16 CMBA: MOV R0,R16 LSL R16 LSL R16 ADD R16,R0 LSL R16 ;高位BCD乘10 LD R0,X ADD R16,R0 ;加低位BCD RET DSF_: RCALL FIL8 ;准备显示'F- ' LDI R16,$0F STS $6C,R16 LDI R16,$14 STS $6D,R16 RET BRA3A: LDI XL,$6E ;二进制数转换为两位BCD码并显示 BRAX: LDI R17,$24 ;十位为0时显示空白 ST X,R17 BRHOUR: CLR R0 ; BRX0: SUBI R16,10 ;减10 BRCS BRX2 INC R0 RJMP BRX0 BRX2: SUBI R16,-10 ;不够减恢复出十位BCD TST R0 BREQ BRX1 ST X,R0 ;放入显示区 BRX1: INC R26 ST X,R16 BRART: RET FERR1: LDI XL,$71 ;显示'F Err.1' LDI R16,1 ST X,R16 RJMP FER123 FERR2: RCALL MOVE1 ;显示'F Err.2' LDI R16,2 STS $71,R16 RJMP FER123 FERR3: RCALL MOVE1 ;显示'F Err.3' LDI R16,3 STS $71,R16 FER123: LDI XL,$6C LDI R16,$0F ST X+,R16 LDI R16,$24 ST X+,R16 LDI R16,$0E ST X+,R16 LDI R16,$1B ST X+,R16 LDI R16,$3B ST X+,R16 ;显示'F Err.1/2/3' LDI R16,$24 ;2秒 STS $72,R16 STS $73,R16 RCALL DL2S RET FIL8: LDI R26,8 ;将显示缓存区充空白 MOV R10,R26 LDI R26,$6C CLR R27 LDI R16,$24 FILP: ST X+,R16 DEC R10 BRNE FILP RET FLFUNC: RCALL FIL8 ;准备显示'Func.' LDS R26,$6C LDI R16,$0F ;'F' ST X+,R16 LDI R16,$1E ;'u' ST X+,R16 LDI R16,$17 ;'n' ST X+,R16 LDI R16,$40 ;'c.' ST X+,R16 RET EXCH0: LDI ZL,$14 ;将显示缓存区内容转移$6C-$73<-->$214-$21B LDI ZH,2 LDI XL,$6C EXL: LD R16,X LD R17,Z ST X+,R17 ST Z+,R16 CPI R26,$74 BRNE EXL RET MOVE1: LDI ZL,$14 ;将显示缓存区内容传送到$214-$21B LDI ZH,2 LDI XL,$6C MV1: LD R16,X+ ST Z+,R16 CPI R26,$74 BRNE MV1 RET ;EEPROM 读写程序 ;范例22 ;读出EEPROM子程序 REEP: LDI YH,1 LDI YL 0 ;EEPROM 读出首地址:$100 LDI XL,$60 ;读出数据存放首地址:$60 CLR XH REEP1: SBIC $1C,1 ;查EEWE位,EEWE=1为当前尚有写入操作未结束 RJMP REEP1 ;等待EEWE=0 OUT $1F,YH OUT $1E,YL ;读出地址写入EEPRO地址寄存器 SBI $1C,0 ;设置读出使能位(EERE) IN R16,$1D ;从EEPROM数据寄存器中读出数据 ST X+R16 ;存入缓存区 INC YL BRNE REEP1 ; INC YH CPI YH,2 ;EEPROM最末数据(地址为$1FF)读完? BRNE REEP1 RET ;范例23 ;写入EEPROM子程序 WEEP: LDI YH,1 LDI YL 0 ;EEPROM 写入之首地址:$100 LDI XL,$60 ;写入数据存储区首地址:$60 CLR XH WEEP1: SBIC $1C,1 ;查EEWE位,EEWE=1为当前尚有写入操作未结束 RJMP WEEP1 ;等待EEWE=0 OUT $1F,YH OUT $1E,YL ;送写入地址到EEPRO地址寄存器 LD R16,X+ ;取写入数据并调整数据指针 OUT $1D,R16 ;送到EEPROM数据寄存器 SBI $1C,2 ;设置EEPROM写入总使能位EEMWE SBI $1C,1 ;设置EEPROM写入使能位EEWE INC YL BRNE WEEP1 INC YH CPI YH,2 ;EEPROM最末写入单元地址为$1FF BRNE WEEP1 RET ;时钟日历芯片62×42×读写程序,时钟日历数据读入到显示缓存区$6C--$73 ;范例24 ;USE 8515!使用DSPA子程序 .EQU RTCH=$40 ;rtc地址高八位 RDATE: RCALL BSYT ;初始化,兼冻结RTC LDI XL,$6D ;数据缓存区首地址 LDI YL,$06 ;首指日单元 RDLP: LD R16,Y+ ;$6b 6c 6d 6e 6f 70 71 72 73 ANDI R16,15 ; 2 9(D) - 1 0(M) - 0 2(Y) CPI R16,10 BRCS RDL1 ANDI R16,$7F ;容错处理 RDL1: ST X,R16$ DEC R26 CPI R26,$6B BRNE RDLP1 LDI XL,$70 RDLP1: CPI R26,$6E BRNE RDLP2 LDI R16,$14 ;送‘-’到$6E单元 ST X,R16 LDI XL,$73 RDLP2: CPI R26,$71 BRNE RDLP LDI R16,$14 ST X,R16 ;送‘-’到$71单元并结束子程序 RDINVL: RJMP WCRT RTIME: RCALL FIL2 ;请除缓存区 RCALL BSYT LDI XL,$73 LDI YL,$02 ;指向分单元(只读时分) RCL: LD R16,Y+ ANDI R16,15 CPI R16,10 BRCS RCL0 ANDI R16,$7F ;容错处理 RCL0: ST X,R16 DEC R26 CPI R26,$71 BRNE RCL1 LDI R16,$14 ;写入‘-’ ST X,R16 DEC R26 RCL1: CPI R26,$6E ;$6c 6d 6e 6f 70 71 72 73 BRNE RCL ; 1 6 - 3 5 CLR R16 ST Y,R16 LDS R17,$9FFB ;时制存储单元 LDS R16,$6f SWAP R16 LDS R15,$70 ADD R16,R15 ;合成小时 SUBI R16,$24 ;模24 RCALL SUDAA ;BCD码减法调整 BRCC RCL2 ;够减,转 SUBI R16,-36 ;否则恢复被减数 RCL2: CPI R17,2 BRNE PRTD1 ;24小时制,转 SUBI R16,$12 RCALL SUDAA BRCC PRTD1 ;12小时制处理 SUBI R16,-18 PRTD1: MOV R17,R16 SWAP R16 ANDI R16,$0F ANDI R17,$0F STS $6F,R16 STS $70,R17 ;小时数据送入显示区 RJMP WCRT WDATE: RCALL WRTC ;将显示缓存区中日期数据写入RTC LDI XL,$6F LD R16,X CPI R16,10 BRCC WDRT ;非法数据,退出 LDI YL,6 WDLP: LD R16,X DEC R26 CPI R16,$24 ;SPC? BRNE WD0 CLR R16 ;变为0 WD0: ST Y+,R16 CPI R26,$6D BRNE WD1 ;$6d 6e 6f 70 71 72 73 LDI XL,$71 ; 2 9(日) 1 1(月) 0 2 (年) RJMP WDLP WD1: CPI R26,$6f BRNE WD2 LDI R26,$73 WD2: CPI R26,$71 BRNE WDLP LWDRT: RJMP WCRT WTIME: RCALL WRTC ;将显示缓存区中时间数据写入RTC LDI R26,$73 LD R16,X CPI R16,10 BRCC WCRT ;非法数据,退出 LDI YL,2 WLOP: LD R16,X CPI R16,$24 BRNE WT1 CLR R16 ;容错处理 WT1: ST Y+,R16 DEC R26 WLP: CPI R26,$6F BRNE WLOP ;$6E 6f 70 71 72 73 WCRT: CLR R16 ; 1 5 3 8 LDI YL,$0D ST Y,R16 ;解除对RTC之冻结 IN R16,MCUCR CBR R16,$C0 OUT MCUCR,R16 ;禁止读写外部RAM RET ;对rtc初始化/冻结时钟 BSYT: LDI YH,RTCH ;rtc地址高八位 LDI YL,$0D ;指向D寄存器 IN R16,MCUCR SBR r16,$C0 ;允许读写外部RAM并选一个时钟周期等待时间 OUT MCUCR,R16 LDI R16,5 ;设置冻结位和中断申请位 ST Y,R16 CLR XH BSRT: RET ;写RTC初始化子程序 WRTC: RCALL BSYT LDI YL,$0E ;指向寄存器E LDI R16,6 ST Y+,R16 ;指向寄存器F LDI R16,1 ;设置时制位 ST Y,R16 LDI R16,4 ;选24小时制 ST Y,R16 CLR R16 ;请除时制位 ST Y,R16 RJMP BSYT ;范例25 ;显示保护子程序/晶振4MHZ DSPRV: LDI R16,HIGH(ramend) OUT SPH,R16 LDI R16,LOW(ramend) OUT SPL,R16 CLR R2 ;调DSPY次数寄存器清除 WDR LDI R16,$0D ;启动看门狗,溢出时间为0.49s OUT WDTCR,R16 ;写入看门狗控制寄存器 CLR XH LDI XL,$6C DSPVL: ST X+,XH ;清显示缓存区($6c-$73) CPI XL,$74 BRNE DSPVL DSPV0: LDI R16,$66 MOV R9,R16 LDI R16,$82 ;$6582=25986,高位字节增1为$66 MOV R10,R16 ;调25986次DSPA耗时120s DSNEX: LDI XL,$74 ;将显示区十进制数据增1以演示数据变化 DSLOP: LD R16,-X ;实用时可以采样数据更新显示(参考范例96) INC R16 ST X,R16 CPI R16,$0A BRNE DSPRV1 CLR R16 ST X,R16 CPI R26,$6C BRNE DSLOP ;增1后如有进位则调整 DSPRV1: DEC R10 BRNE DSPGN DEC R9 BRNE DSPGN ;2分钟定时到? DSCLOS: RCALL FIL2 ;将显示缓存区充入空白($24) RCALL DSPA ;其效果相当于关显 SBRC R16,7 RJMP DSCLOS RJMP DLFUNC ;有键按下,转出;否则继续关显 DSPGN: RCALL DSPA ;未到,显示数据 SBRC R16,7 RJMP DSNEX ;无键按下,继续显示 DLFUNC: CPI R16,12 ;关显键键值为12 BEEQ DSCLOS ;关显键按下,转关闭显示 ;. ;. ;. ;. ;(其他键值处理,参考范例26 DEALKY程序) RJMP DSPV0 ;执行功能后转入二分钟定时 ;范例26 ;键值处理程序 DEALKY: LDI R16,HIGH(ramend) OUT SPH,R16 LDI R16,LOW(ramend) OUT SPL,R16 CLR R2 ;调DSPY次数寄存器清除 WDR LDI R16,$0D ;启动看门狗,溢出时间为0.49” OUT WDTCR,R16 ;写入看门狗控制寄存器 DEALK0: RCALL DSPA SBRC R16,7 RJMP DEALK0 ;无键按下,反复查询 CPI R16,10 BRCC FNCKY ;功能键按下,跳转 RCALL FIL2 ;键值<10为数字键 ,先清除显示缓存区 NUMKY: RCALL LSDD8 ;8位数字左移,新键值加入序列尾 DSLP: RCALL DSPA SBRC R16,7 RJMP DSLP ;无键按下,继续显示 CPI R16,11 BRCS NUMKY ;键入数字形成左移序列/按清除键则清除所有键入数据 BRNE DSLP ;键值大于11无效 ;11为回车键,对键入数字进行处理(如将其两两合并为BCD ;码,再转为二进制数等) RJMP DEALK0 ;转回 FNCKY: SUBI R16,10 ;功能键散转处理,先计算键值偏移量 LDI R31,HIGH(FKYTB) LDI R30,LOW(FKYTB);散转表表首 ADD R30,R16 CLR R16 ADC R31,R16 ;偏移量加入指针 IJMP ;散转 FKYTB: RJMP CLTTL ;10:清除累加和 RJMP DSTTL ;11:显示累加和 RJMP DSCLS ;12:关显示 RJMP SLFTS ;13:自检 RJMP FDPAP ;14:打印机走纸 RJMP PRSMP ;15:打印采样 RJMP PRTTL ;16:打印累加和 RJMP DSCLK ;17:显示系统时钟 ;............. ;......... ;............. ;......... CLTTL: ;............. ;程序内容略 ;............. RJMP DEALK0 ;程序执行完毕,转回 DSTTL: RCALL BRTTL ;分解累加和送显示缓存区 RCALL DSPA ;显示累加和 SBRC R16,7 RJMP DSTTL ;任一键按下,结束显示累加和 RJMP DEALK0 ;程序执行完毕,转回 DSCLS: RJMP DSCLOS ;转去关显示 SLFTS: ;............. ;............. RJMP DEALK0 ; 自检程序执行完毕,转回 FDPAP: ;............. ;............. RJMP DEALK0 ; 走纸程序执行完毕,转回 PRSMP: ;............. ;............. RJMP DEALK0 ; 打印采样程序执行完毕,转回 PRTTL: ;............. ;............. RJMP DEALK0 ;打印累加和程序执行完毕,转回 DSCLK: RCALL BRCLK ;分解系统时钟送入显示缓存区 RCALL DL1S ;延时1秒 RCALL DSPA ;显示时钟 SBRC R16,7 ;任一键按下,结束显示时钟 RJMP DSCLK RJMP DEALK0 ;程序执行完毕,转回 ;............. ;............. ;其他功能键处理略 ;............. ;范例27 ;主显子程序 DSPA: SBRC R16,7 ;USE R0,R2,R11,R12,r13,r14,r15,r16,r17&Z,X POINTERS RJMP DSA2 ;无键按下,跳转 DSA0: CLR R12 INC R12 ;有键按下,将计数器置1 DSA1: RCALL DSPY DEC R12 BRNE DSA1 ;等待键释放 DSA2: RCALL DSPY LDS R16,$A3 SBRS R16,7 ;有进入功能表程序标志? RET ;没有返回 SBI PORTA,0 ; SBIS PINA,0 ;退出功能表程序吗? RET CBR R16,$80 ;是,清除进入功能表程序标志($A3,7) STS $A3,R16 RCALL FIL2 LDI R16,$0F ;'F' STS $6C,R16 LDI R16,$0E ;'E' STS $6E,R16 LDI R16,$17 ;'n' STS $6F,R16 LDI R16,$0D ;'d' STS $70,R16 ;显示‘F End' RCALL DL2S ;2秒后 RJMP DIPA1 ;转到主程序(包括对堆栈)初始化 DL2S: RCALL DL1S ;延时2秒子程序 DL1S: LDI R16,217 ;延时1秒子程序/4MHz clk MOV R11,R16 ;4.618×217=1000ms DLCOM: RCALL DSPA DEC R11 BRNE DLCOM RET ;范例28 ;基显子程序,显示缓存区:$6C--$73,执行时间4.618ms/晶振4MHZ ;主程序应对看门狗初始化,设置溢出时间为0.49秒! DSPY: LDI R17,$0F ;使用R0,R2,R12,R13,R14,R15,R16&R17/z&x pointer! OUT DDRA,R15 ;PA7--PA4为键列值输入 CLR R15 COM R15 OUT DDRB,R15 OUT DDRC,R15 ;口B:段选输出,口C:位选输出 OUT PORTC,R15 ;关显 DPY1: LDI R26,$6C ;指向显示缓存区首址:$6C CLR R27 LDI R17,$7F MOV R13,R17 ;位选初始化(首显最高位) L0D: LD R17,X+ LDI R31,HIGH(table*2) LDI R30,LOW(table*2) ADD R30,R17 ADC R31,R27 L0C: LPM ;取段选码 OUT PORTB,R0 ;送段选口 OUT PORTC,R13 ;位选口 SEC ; ROR R13 ;指下一位位选 LDI R17,3 ;4MHz(6 if 8MHz) CLR R14 DLOP: DEC R14 BRNE DLOP DEC R17 BRNE DLOP ;延时0.5762毫秒 IN R16,PORTA ORI R16,$F0 ;保护PA3--PA0输出 OUT PORTA,R16 ;提拉PA7-PA4 IN R14,PINA ;读入列值 NEX: ROL R14 ;use high 4bits! BRCC L1 ;有键按下,跳转 NEX1: INC R17 ;指向下一列 CPI R17,4 BRNE NEX ;各列都查完? NEX2: SER R17 OUT PORTC,R17 ;将$FF写入位选口(关显) CPI R26,$74 BRNE L0D ;每位LED都显示一遍?? MOV R16,R15 ;YES INC R2 ;增一调DSPY次数寄存器 MOV R17,R2 CPI R17,100 ;到100次? BRNE NEX3 CLR R2 ;清除看门狗定时器时间到计数器/4.618ms×100=0.462s(<0.49s) WDR ;看门狗定时器复位 NEX3: RET L1: LDS R16,$73 ;计算键值代码/查键值 SUB R16,R26 ;$73-(r26)-->r16 LSL R16 LSL R16 ;行值*4 ADD R16,R17 ;键值代码=行值*4+列值 LDI R30,LOW(TABL0*2) ADD R30,R16 LDI R31,HIGH(TABL0*2) ADC R31,R27 LA00: LPM ;查出键值 MOV R15,R0 ;放在R15 LA10: INC R12 ;计数器增1以备判断键释放 RJMP NEX1 ;转回查下一列 TABL0: .DB 10,0,11,20,1,2,3,16,4,5,6,22,7,8,9,18,12,15,19,23,14,17,21,13 TABLE: .DB $3F,$06,$5B,$4F,$66,$6D,$7D,$07,$7F,$67,$77,$7C,$39 ;0--C .DB $5E,$79,$71,$6F,$74,$04,$1F,$40,$38,$37,$54,$5C ;'d'---'o' .DB $73,$67,$50,$6D,$78,$1C,$3E,$7E,$F8,$6E,$49,$00 .DB $48,$52,$D3,$76 ;$25(=),$26(/)$27(?) END AT $28(H) .DB $BF,$86,$DB,$CF,$E6,$ED,$FD,$87,$FF,$E7;THE 0.($29)--9.($32) .DB $D7,$C9,$80 ;THE 'X.' 'Z.' &'.'($33--$35) .DB $DE,$EF,$B8,$F3,$E7,$D0,$DC,$ED,$86,$F9,$B9H,$F7,$F1,$B7,$D4 ;the d.,g.,L.,p.,q.,r.,o.,s.,l.,E.,C.,A.,F.,M.,n.(36--44h) ;范例29 ;键入数字序列左移处理子程序 LSDD8: LDI R26,$6C ;8bcd码($6C--$73H) LDS R27,$A3 CBR R27,8 ;清$A3,3 STS $A3,R27 CLR R27 CPI R16,10 ;10为清除键 BRNE DDL RCALL FIL2 ;清除显示缓存区($6c-$73)! LDS R16,$A3 SBR R16,8 STS $A3,R16 ;建清除显示缓存区标志$A3,3=1 RET DDL: INC R26 ;数字键按下,序列左移 LD R16,X ; SUBI R16,$29 ;数字带小数点? BRCC DD4 ;若带则将其复原(参考DSPY子程序段码表) SUBI R16,$D7 ;恢复 DD4: ST -X,R16 ;移入左邻单元 DD5: INC R26 CPI R26,$73 BRNE DDL ;各数字都左移了一位? ST X,R15 ;新键入数字进入数字序列末位 LDI R26,$6C DEL: LD R16,X CPI R16,10 ;是BCD码? BRCS DEL2 CPI R16,$29 BRCC DELRT ;大于$29为错误! DELA: INC R26 ;0--9/$24/$14为有效! CPI R26,$73 BRNE DEL ;缓存区检查完毕? RJMP DELRT DEL2: CPI R16,0 BRNE DELRT LDI R16,$24 ;0改为空白 ST X,R16 RJMP DELA ; DELRT: LDS R16,$A0 ;小数点位置单元 TST R16 BREQ DDRET ;($a0)=0,无小数点 NEG R16 ADD R16,$73 MOV R26,R16 ;找到缓存区内带小数点的数据位 LD R16,X SUBI R16,$D7 ;加上小数点 ST X,R16 CPI R16,$4D ;在空白码加了小数点($24(空白)+$29=$4d)? BRNE STLR1 LDI R16,$29 ST X,R16 ;是,将其改为'0.' STLR1: CPI R26,$73 BREQ DDRET ;并将其后所有空白都改为0 INC R26 LD R16,X CPI R16,$24 BRNE DDRET CLR R16 ST X,R16 RJMP STLR1 DDRET: RET FIL2: LDI R26,8 ;在显示缓存区内填充空白 MOV R14,R26 FIL2A: LDI R26,$6C FIL: CLR R27 LDI R16,$24 FILP: ST X+,R16 DEC R14 BRNE FILP RET ;范例30 ;双键输入检查数据子程序,Ky1数据键/Ky2回车键 KYIN2: LDI R26,$60 ;寄存器地址:portb:$18/ddrb:$17/pinb:$16 CLR R27 ;指向数据区首地址 CBI DDRB,7 CBI DDRB,6 ;pb7和pb6皆为输入口 SER R17 OUT DDRC,R17 ;c口为数据显示口 LA0: LD R17,X ;取数据 CPI R17,$0A BRCS LA1 CLR R17 LA1: LDI R31,HIGH(table*2) LDI R30,LOW(table*2);DSPY段选码表 ADD R30,R17 ADC R31,R27 LPM COM R0 ;段选码取出并取反 OUT PORTC,R0 ;送C口 SBI PORTB,7 SBIC $16,7 RJMP NXA1 ;数字键未按下,转 RCALL DL50 ;否则延时 XA2: SBI PORTB,6 SBIC $16,6 RJMP XA0 ;只有数字键按下,转 XA20: RCALL DL50 ;两键都按下,先延时50mS SBI PORTB,6 SBIS $16,6 RJMP XA20 SBI PORTB,7 SBIS $16,7 RJMP XA20 ;等两键都释放 RCALL DL50 XA21: SBI PORTB,6 SBIS $16,6 RJMP XA21 ;等待释放 SBI PORTB,7 SBIS $16,7 RJMP XA21 ;再次等待释放 RJMP NXA6 ;先按数字键,再按回车键,待2都键释放后退出子程序 XA0: SBI PORTB,7 SBIS $16,7 RJMP XA2 ;等待数字键释放 XA1: RCALL DL50 ;延时 SBI PORTB,7 SBIS $16,7 RJMP XA1 ;再次等待释放 INC R17 ;数字增1 CPI R17,10 BRCS NXA1 CLR R17 ;超过10,将键值归为0 NXA1: SBI PORTB,6 SBIC $16,6 RJMP LA1 ;回车键也未按下,重新查键 RCALL DL50 ;延时 NXA3: SBI PORTB,6 SBIS $16,6 RJMP NXA3 ;再次等待回车键释放 RCALL DL50 SBI PORTB,6 SBIS $16,6 RJMP NXA3 ST X+,R17 ;数字转入缓存区 SER R17 OUT PORTB,R17 ;关显 RCALL DL50 ; CPI R26,$70 ;到规定数字个数? BRNE LA0 ; LDI R17,$86 ;显示'E'nd OUT PORTC,R17 ; NXA4: SBI PORTB,6 SBIS $16,6 RJMP NXA5 ;回车键按下,转 SBI PORTB,7 SBIC $16,7 ;数字键按下,转 RJMP NXA4 ;否则反复查键 NXA40: RCALL DL50 SBI PORTB,7 SBIS $16,7 RJMP NXA40 SBI PORTB,7 SBIS $16,7 RJMP NXA40 ;等待键释放 RJMP KYIN2 ;转检查键入数据 NXA5: RCALL DL50 SBI PORTB,6 SBIS $16,6 RJMP NXA5 SBI PORTB,6 SBIS $16,6 RJMP NXA5 ;等回车键释放 NXA6: SER R17 OUT PORTB,R17 ;关显,结束子程序 RET DL50: ;RCALL DL25 ;延时50毫秒子程序/8Mhz(去掉指令前“;”号) DL25: CLR R14 ;延时50毫秒子程序/4Mhz CLR R15 DL50L: DEC R15 NOP BRNE DL50L DEC R14 BRNE DL50L RET ;范例31 LPRNT: SER R17 ;宽行打印机检测及控打程序 OUT DDRC,R17 ;C口为打印机输出口! SBI DDRD,7 CBI DDRD,3 ;pd7为选通输出口,pd3(INT1)查忙输入口 SBI PORTD,3 SBIC PIND,3 ;查打印机忙信号 RJMP ERR5 ;打印机尚未工作忙信号即已为高,打印机不能打印 LDI R17,$0D ;写回车命令给打印机 OUT PORTC,R17 CBI PORTD,7 ;发出选通信号 NOP NOP NOP SBI PORTD,7 ;strobe LDI R16,50 TSPRT: SBI PORTD,3 SBIc PIND,3 RJMP LPRT2 ;50次内忙信号高起来为正常 DEC R16 ;否则为非正常状态 BRNE TSPRT ERR5: LDI R16,5 RCALL ERRX ;显示5号错误 RJMP DIPA1 ;转主程序初始化 LPRT2: LDI R25,1 CLR R24 ;point to $100 LDI R17,$80 OUT GIMSK,R17 ;允许int1中断 LDI R17,$0A OUT MCUCR,R17 ;INT1下降沿中断 SEI ;general interrupt enable RET EX_INT1:PUSH R26 PUSH R27 IN R27,SREG PUSH R27 PUSH R17 ;保护现场 MOV R27,R25 ;取数据指针 MOV R26,R24 LD R17,X+ ; MOV R25,R27 MOV R24,R26 ;增1后将指针送回 CPI R17,3 ;是停止符? BRNE INT1SD CLR R17 OUT GIMSK,R17 ;禁止INT1中断 RJMP INT1ED INT1SD: OUT PORTC,R17 ;打印数据输出到打印口 CBI PORTD,7 ;clr ($12,7) NOP NOP NOP SBI PORTD,7 ;向打印机发出选通 INT1ED: POP R17 POP R27 OUT SREG,R27 POP R27 POP R26 ;恢复现场 RETI ;范例32 ;步进电机控制程序 .ORG 0 STRT10: RJMP RST10 ;8535/8515/晶振4MHZ .ORG $011 RST10: LDI R16,HIGH(ramend) OUT SPH,R16 LDI R16,LOW(ramend) OUT SPL,R16 SER R16 OUT DDRB,R16 ;B口为输出 LDI R17,8 OUT PORTB,R16 ;接通总开关 LDI R16,50 ;50次基本运作 RCALL DELAY5 ;延时5毫秒 LOOPX: LDI R17,$68 ;step1时序脉冲控制 OUT PORTB,R17 RCALL DELAY2 ;延时2毫秒 LDI R17,$38 ;step2时序脉冲控制 OUT PORTB,R17 RCALL DELAY2 ;延时2毫秒 LDI R17,$98 ;step3时序脉冲控制 OUT PORTB,R17 RCALL DELAY2 ;延时2毫秒 LDI R17,$C8 ;step4时序脉冲控制 OUT PORTB,R17 RCALL DELAY2 ;延时2毫秒 DEC R16 BRNE LOOPX ;到50次? LDI R17,8 OUT PORTB,r17 ;关闭各相位开关 RCALL DELAY5 RCALL DELAY5 ;延时10毫秒 CLR R17 OUT PORTB,R17 ;关闭所有相位开关和总开关 HH0: RJMP HH0 ;踏步 DELAY1: LDI R17,$06 ;延时1毫秒 MOV R15,R17 ;1000/0.75=1333=$535,外層计数器装入$06 LDI R17,$35 ;DEC+BRNE=0.75微秒 RJMP DLCOM DELAY2: LDI R17,$0B ;延时2毫秒 MOV R15,R17 ;2000/0.75=2666=$0A6A,外層计数器装入$0B LDI R17,$6A DLCOM: DEC R17 BRNE DLCOM DEC R15 BRNE DLCOM RET DELAY5: LDI R17,$1B ;延时5毫秒 MOV R15,R17 ;5000/0.75=6666=$1A0A,外層计数器装入$1B LDI R17,$0A RJMP DLCOM ;范例33 .ORG 0 ;8515采用定时器中断输出时序脉冲方式控制电机转动 STRT11: RJMP RST11 ;晶振4MHZ .ORG $007 RJMP T0_OVF ;中断服务程序与STRT12共用 .ORG $00D RST11: LDI R17,HIGH(ramend) OUT SPH,R17 LDI R17,LOW(ramend) OUT SPL,R17 LDI R17,$68 MOV R7,R17 ;初始脉冲为0B01101000 SER R17 OUT DDRB R17 ;B口为输出 LDI R17,N ;运作次数N(N>0) RCALL STPDRV ;初始化子程序 HH20: RJMP HH20 ;实用时改为具体的背景程序! STPDRV: TST R17 BRNE STPDR1 INC R17 ;N=0时,将其改为1 STPDR1: MOV R6,R17 INC R6 ;N+1-->r6(max.is 256;“植树问题”,N必需增1! LDI R17,$A4 CBR R17,$20 STS $A4,R17 ;清除连续转动电机标志 LDI R17,$08 OUT PORTB,R17 ;接通总开关 LDI R17,4 ;0B00000100/ 256分频(4兆/256=1兆/64) OUT TCCR0,R17 LDI R17,178 ;78*64=4.992ms OUT TCNT0,R17 ;时间常数,首定时为5毫秒 LDI R17,$02 OUT TIMSK,R17 ;允许T/C0溢出中断 SEI HH21: SJMP HH21 ;范例34 .ORG $000 ;步进电机手动控制程序(8515)晶振4MHZ STRT12: RJMP RST12 .ORG $007 RJMP T0SEV .ORG $00D RST12: LDI R17,HIGH(ramend) OUT SPH,R17 LDI R17,LOW(ramend) OUT SPL,r17 LDI R17,$68 MOV R7,R17 ;第一个时序脉冲 LDI R17,$F8 OUT DDRB,R17 ;PB7-PB3输出,PB2-PB0输入 CLR R17 OUT PORTB,R17 ;输出为低电平 LDS R17,$A4 SBR R17,$20 ;设置连续转动标志 CBR R17,$40 ;设置电机正转标志 TSTLP1: SBI PORTB,1 ;PB1接地,正转 SBIS PINB,1 RJMP TSTL11 ; TSTL10: SBI PORTB,2 SBIC PINB,2 ;PB2接地,反转 RJMP TSTLP1 ;PB1,PB2都未接地,反复查询 SBR R17,$40 ;设置电机反转 TSTL11: STS $A4,R17 ;保存标志 CLR R6 INC R6 ;R6中装入1,减一次即为0! LDI R17,$08 OUT PORTB,R17 ;接通总开关 LDI R17,4 ;0B00000100/256分频(256/4=64微秒)! OUT TCCR0,R17 LDI R17,178 ;178之补为78,78*64=4.992ms OUT TCNT0,R17 ; LDI R17,$02 OUT TIMSK,R17 ;允许T/C0中断(toie1=$39,7 toie0=$39,1) ;8535,toie1:$39,2 toie0:$39,0 SEI TSTLP2: SBI PORTB,1 SBI PORTB,2 IN R17,PINB ANDI R17,6 CPI R17,6 BRNE TSTLP2 ;两开关未全部打开,查询等待 LDS R17,$A4 CBR R17,$20 ;清除连续转动标志 STS $A4,R17 ; TSTLP3: IN R17,TIMSK SBRC R17,1 ;已禁止8515中断?(8535:timsk,0) RJMP TSTLP3 ;未,查询等待 RJMP RST12 T0_OVF: PUSH R17 ;电机控制中断服务子程序 IN R17,SREG PUSH R17 LDS R17,$A4 SBRC R17,7 RJMP T0SV2 ;$A4,7:关电机前10毫秒延时标志 MOV R17,R7 CPI R17,$68 BRNE T0SV0 LDS R17,$A4 SBRC R17,5 RJMP T0SV0 ;电机连续转动,不减R6 DEC R6 ;R6减为0,将停止电机 BREQ T0SV1 ; T0SV0: LDI R17,225 ;每步进延时(256-225)*64=1.984MS err.<0.8% OUT TCNT0,R17 ; OUT PORTB,R7 ;步进控制脉冲输出 LDS R17,$A4 SBRC R17,6 RJMP T0SVA ;$A4,6=1 为连续反转 CLC SBRC R7,4 ;组织下一步控制脉冲 SEC ROR R7 LDI R17,$08 ;正转 OR R7,R17 ;01101***->00111***->10011***->11001***->01101***....... RJMP T0RET T0SVA: MOV R17,R7 ; SBR R17,$04 ROL R17 ;组织下一步控制脉冲(反转) BRCS T0SVB CBR R17,$10 ;01101***->11001***->10011***->00111***->01101***.......! T0SVB: MOV R7,R17 RJMP T0RET T0SV1: LDS R17,$A4 SBR R17,$80 STS $A4,R17 ;总开关关断前10毫秒延时标志 LDI R17,$08 OUT PORTB,R17 ;关断4个相位开关 LDI R17,100 ;156(256-100)*64=9.984ms OUT TCNT0,R17 ; RJMP T0RET T0SV2: LDI R17,$07 OUT PORTB,R17 ;关闭所有开关 CLR R17 OUT TCCR0,R17 ;关T/C0中断 OUT TIMSK,R17 LDS R17,$A4 CBR R17,$C0 STS $A4,R17 ;清除10毫秒延时和反向转动标志 T0RET: POP R17 OUT SREG,R17 POP R17 RETI ;精确定时及时钟日历走时子程序 ;范例35 .EQU DTPNT=$75 ;年年月日时分秒(from $7B to $75) .ORG $000 STRT20: RJMP RST20 ;晶体实测频率4.000119MHZ .ORG $006 ;8515 t1 overflow INT.vector RJMP T1_OVF .ORG $00D RST20: LDI R16,HIGH(ramend) OUT SPH,R16 LDI R16,LOW(ramend) OUT SPL,R16 LDI R16,2 ;8分频,4000119/8=500015 OUT TCCR1B,R16 LDI R16,$5E ;500015=65536*8-24273=8*$10000-$5ed1/TCC=$5Ed1 OUT TCNT1H,R16 ; LDI R16,$D1 ; OUT TCNT1L,R16 ;将TCC写入TCNT1 LDI R16,$80 OUT TIMSK,R16 ;允许T/C1溢出中断 LDI R16,8 ;8次中断出秒号 MOV R6,R16 SEI HH10: RJMP HH10 ;可改为具体的实用程序 T1_OVF: PUSH R16 PUSH R17 IN R7,SREG DEC R6 ;到8次中断? BRNE GOON1 IN R17,TCNT1L ;* IN R16,TCNT1H ;*读回TCNT1自然计数值 SUBI R17,$2F ;*$5ED1之补为$A12F,以减法替代加法修正TCC SBCI R16,$A1 ;*减去$A12E可不做下面的加1修正 SUBI R17,$FF ;*8条修正指令占用一个计数单位时间 SBCI R16,$FF ;*修正后TCC=$5ED1+(TCNT1)+1 OUT TCNT1H,R16 ;* OUT TCNT1L,R17 ;*将修整后TCC写入TCNT1 LDI R16,8 MOV R6,R16 ;重装中断次数8 ;. ;. RCALL ACLK ;时钟走时 GOON1: POP R17 POP R16 OUT SREG,R7 RETI ;范例36 .EQU DTPNT=$75 ;yyyy mm dd hh mm ss(from $7B--$75) .ORG $000 ;晶体实测频率8.000267MHZ,8分频 ;INT(8000267/8)=1000033 STRT21: RJMP STRT21 .ORG $006 ;8515 t1 overflow INT. vector RJMP T1_OVF .ORG $00D STRT21: LDI R16,HIGH(ramend) OUT SPH,R16 LDI R16,LOW(ramend) OUT SPL,R16 LDI R16,2 OUT TCCR1B,R16 ;8分频 LDI R16,1 ;1000033=62332*15+65053 ;=($10000-$0C84)*15-$10000-$1E3 OUT TCNT1H,R16 ;主常数62332(补码为$0C84) 补尝常数TCC=$01E3 LDI R16,$E3 ;$FE1D=65053\65053+62332*15=1000033 OUT TCNT1L,R16 CLR R16 OUT TCCR1A,R16 ;DISABLE CMPA/CMPB/PWM! LDI R16,$80 ;8515 OUT TIMSK,R16 ;允许T/C1溢出中断 LDI R16,16 ;16次中断 MOV R6,R16 SEI HH11: RJMP HH11 ; T1_OVF: PUSH R17 PUSH R16 IN R7,SREG DEC R6 ;中断次数到?未到转装入主常数 BRNE COMP ;否则重装入TCC IN R17,TCNT1L ;* IN R16,TCNT1H ;*读回自然计数值 SUBI R17,$1D ;* SBCI R16,$FE ;*减去TCC之补码 SUBI R17,255 ;*再加1 SBCI R16,255 ;*修正后TCC=$01E3+(TCNT1)+1 OUT TCNT1H,R16 ;* OUT TCNT1L,R17 ;* LDI R16,16 MOV R6,R16 ;重写中断次数 ;. ;. RCALL ACLK ;时钟走时 RJMP GOON2 COMP: IN R17,TCNT1L ;* IN R16,TCNTIH ;*读回TCNT1自然计数值 SUBI R17,$7C ;*先减去$0C84$'补码$F37C SBCI R16,$F3 ;*再作加1补偿 SUBI R17,$FF ;* SBCI R16,$FF ;*修整后重装值=[$0C84+(TCNT1)+1] OUT TCNT1H,R16 ;* OUT TCNT1L,R17 ;* GOON2: POP R16 POP R17 OUT SREG,R7 RETI ;范例37 ;8515使用T/C0定时,64分频,晶振频率4000131HZ .ORG $000 .EQU DTPNT=$75 STRT22: RJMP RST22 .ORG $007 RJMP T0_OVF ;INT(4000131/64)=62502=245*256-218 .ORG $00D RST22: LDI R16,245 ;245次中断 MOV R6,R16 LDI R16,3 OUT TCCR0,R16 ;主频FCK(4000131HZ)64分频 LDI R16,$02 OUT TIMSK,R16 ;允许T/C0溢出中断 LDI R16,218 OUT TCNT0,R16 ;TCC=218 SEI HH12: RJMP HH12 ; T0_OVF: IN R7,SREG DEC R6 BRNE DECL1 ; IN R16,TCNT0 ;1秒时间到! SUBI R16,38 ;218之补 OUT TCNT0,R16 ; LDI R16,245 MOV R6,R16 ;重装中断次数 RCALL ACLK ;时钟走时 DECL1; OUT SREG,R7 RETI ;范例38 ;8535异步时钟定时程序 .ORG $000 ;时钟频率32768HZ .EQU DTPNT=$75 STRT23: RJMP RST23 .ORG $004 RJMP T2_OVF .ORG $011 RST23: LDI R16,8 OUT ASSR,R16 ;选异步时钟 LDI R16,5 OUT TCCR2,R16 ;128分频 CLR R16 OUT TCNT2,R16 ;时间常数256($00) LDI R16,$40 OUT TIMSK,R16 ;允许T/C2溢出中断 ;............ SEI HH13: LDI R16,$70 ;掉电休眠模式 OUT MCUCR,R16 SLEEP ;进入休眠 RJMP HH13 ; T2_OVF IN R7,SREG ; RCALL ACLK ;时钟走时 ;........... OUT SREG,R7 RETI ;范例39 ACLK: PUSH R16 PUSH R27 PUSH R26 PUSH R7 LDI R26,LOW(dtpnt); LDI R27,HIGH(dtpnt);时钟日历单元指针 RCALL DHM3 ;秒单元加1调整 CPI R16,$60 ; BRNE COM0 ;未到60秒返回 RCALL DHM ;分单元加1调整 CPI R16,$60 BRNE COM0 ;未到60分返回 RCALL DHM ;时单元加1调整 CPI R16,$24 BRNE COM0 ;未到24时返回 RCALL DHM ;日单元加1调整 SUBI R16,$29 BRCS COM0 ;小于29返回 BRNE T30 ;转继续测试30/31/32日 ADIW R26,1 ;29,指向月 LD R16,X CPI R16,2 BRNE COM0 ;非二月返回 ADIW R26,1 ;指向年 LD R16,X ;取年十个位 TST R16 BRNE TYLB ADIW R26,1 LD R16,X ;年十个位为0,取年千百位 TYLB: SWAP R16 ANDI R16,15 MOV R7,R16 LSL R7 LSL R7 ;高位BCD乘4 ADD R16,R7 ;乘5 LSL R16 ;乘10 LD R7,X ;加个位BCD ADD R16,R7 ;年十个位(千百位)转成二进制数 ANDI R16,3 ;该二进制数末两位皆为0,为闰年 BREQ COM0 ;返回(二月有29日) RJMP DAY1 ;否则为3月1日 T30: SUBI R16,7 ;减7调整 BRNE T31 ;$30-$29-7=0 ADIW R26,1 ;指向月 LD R16,X CPI R16,2 BRNE COM0 ;非2月返回 RJMP DAY1 ;闰年的2月30日为3月1日 T31: DEC R16 ;$31-$29-7=1&$32-$29-7=2 BRNE DAY1 ;日为32 ,为下月1日 ADIW R26,1 ;日为31 ,指向月 LD R16,X SUBI R16,8 ;月份减去8 BRCC SCHY INC R16 ;月份小于8,差增1,奇数变偶数 SCHY: SBRS R16,0 RJMP COM0 ;1-7月奇数月为大月/8-12月偶数月为大月;有31日,返回 DAY1: LDI R26,LOW(dtpnt+3) LDI R27,HIGH(dtpnt+3);指向日 LDI R16,1 ; RCALL DHM1 ;日置为1,月加1 CPI R16,$13 BRNE COM0 LDI R16,1 ;月变为13,改为1 RCALL DHM1 ;年十个位加1调整,可能有$99+1=$A0 CPI R16,$A0 BRNE COM0 ; RCALL DHM ;年千百位加1调整 COM0: POP R7 POP R26 POP R27 POP R16 RET DHM: CLR R16 ;秒,分,时单元清除,高位加1 DHM1: ST X+,R16 DHM3: LD R16,X INC R16 ; CPI R16,$0A ;若个位BCD码未变成$0A BRHS DHM2 ;例如$58+1=$59,不须调整; SUBI R16,$FA ;否则做减$FA调整:例如$49+1-$FA=$50 DHM2: ST X,R16 ;并将调整结果送回 RET ;范例40 .ORG 0 ;8535UART串行通讯程序,晶振4MHZ .EQU DTPINT=$180 ;UBRR=12 波特率19200(REL.ERR.=0.16%) .EQU DRPINT=$1D0 STRT30: RJMP RST30 .ORG $00B RJMP U_RXC ;UART接收完成中断 .ORG $00C RJMP U_TXC ;UART发送寄存器空中断 .ORG $011 RST30: LDS R16,$A3 ; CBR R16,3 STS $A3,R16 ;清完整ASCII数据块接收到标志($A3,1),错误标志(FE/OR)($A3,0) LDI R16,12 OUT UBRR,R16 ;BAUD RATE=FCP/16(UBRR+1)=19200 LDI R27,HIGH(DIPINT) MOV R6,R27 LDI R26,LOW(DTPINT) MOV R7,R26 ;发送数据指针在r6r7(dtpint) CLR R11 INC R11 LDI R16,$30 ;发送数据块长度为$30 MOV R12,R16 RCALL CRC0 ;得到CRC检测之余式(冲掉$0D&$0A) INC XL INC XL LDI R16,$0D ST X+,R16 LDI R16,$0A ST X,R16 ;在数据块末尾加$0D&$0A,实际发送数据块长度为$32 LDI R16,$B8 ;允许UART发送和接收,接收中断,发送寄存器空中断,8位数据 OUT UCR,R16 LDI R16,HIGH(DRPINT) MOV R8,R16 LDI R16,LOW(DRPINT) MOV R9,R16 ;r8,r9:接收缓存区指针(FIRST POINT TO $1D0) CLR R10 ;接收数据块长预先清除 SEI ; HH30: LDS R16,$A3 SBRC R16,0 ;错误接收? RJMP RCVER ;错误处理 SBRS R16,1 ;接收数据完成? RJMP HH30 ;否,转再查询 RCVEF: CLR R11 ;块长予处理 INC R11 DEC R10 DEC R10 ;$0D&$0A不算块长度之内(故将块长减2) MOV R12,R10 ;(R11,R12):块长 LDI XH,HIGH(DRPINT) MOV R8,XH LDI XL,LOW(DRPINT) MOV R9,XL RCALL CRC0 ;恢复出CRC余式 LDI R16,$0D CP R16,R14 BRNE CRCER LDI R16,$0A CP R16,R15 ;恢复出$0D$0A为正确接收 BREQ HH30 CRCER: ;. ;循环冗余检测错误处理 ;. ;. RJMP STRT30 RCVER: CBI UCR,RXCIE ;. ;接收错误(FE/OR)处理 ;. ;(过程略) ;. RJMP STRT30 :UART接收数据块程序 U_RXC: PUSH R16 IN R16,SREG PUSH R16 PUSH R26 PUSH R27 RSC1: IN R16,USR ;UART状态寄存器 ANDI R16,$18 ;FE/OR ERROR? BRNE RVERR ;错误转 INC R10 ;块长加1 MOV XH,R8 MOV XL,R9 ;r8r9:接收数据指针,首指$1D0 IN R16,UDR ST X+,r16 ; MOV R8,XH MOV R9,XL CPI R16,$0A ;收到最末字符(回车命令LF)? BRNE RSCOM LDS R16,$A3 SBR R16,2 ;建立数据块接收完毕标志 STS $A3,R16 CBI UCR,RXCIE ;禁止接收中断 RJMP RSCOM RVERR: LDS R16,$A3 SBR R16,1 STS $A3,R16 ;$A3,0:FE/OR错误接收标志 RSCOM: POP R27 POP R26 POP R16 OUT SREG,R16 POP R16 RETI ; UART发送数据块程序 U_TXC: PUSH R16 IN R16,SREG PUSH R16 PUSH R26 PUSH R27 SPSV1: MOV XH,R6 MOV XL,R7 ;发送数据指针,首指$180 LD R16,X+ ;取发送数据,调指针 MOV R6,XH MOV R7,XL SPS11: OUT UDR,R16 ;送入数据寄存器,移入发送移位寄存器后即引起数据寄存器空中断 CPI R16,$0A BRNE SPCOM CBI UCR,UDRIE ;发送最末字符后禁止发送寄存器空中断 LDI R16,HIGH(DRPINT) MOV R8,R16 LDI R16,LOW(DRPINT) MOV R9,R16 ;接收数据指针初始化,指向$1D0 ;CBI USR,6 ; SPCOM: POP R27 POP R26 POP R16 OUT SREG,R16 POP R16 RETI .DSEG .ORG $180 DTPINT:.BYTE $32 ;$41,$45,$65,$73,$46,$42,$40,$6F,$33,$44,$66,$8C,$4D,$4B,$2F,$67 ;$42,$4F,$66,$78,$47,$45,$44,$63,$32,$48,$60,$7C,$6D,$45,$2A,$63 ;$43,$56,$55,$53,$4D,$4F,$40,$2E,$31,$42,$67,$4C,$47,$4A,$38,$39 ;$0D,$0A .EQU DRPINT=$1D0 .ORG $1D0 DRPINT: .BYTE $34 ;(内容略) ;范例41 ;外部中断int0接收ASCII码数据块 .ORG 0 ;8515/8535/晶振4MHZ STRT31: RJMP RST31 RJMP EX_INT0 .ORG $00D ;8535外部中断0 RST31: LDI R17,HIGH(ramend) OUT SPH,R17 LDI R17,LOW(ramend) OUT SPL R17 LDI R17,2 OUT TCCR1B,R17 ;4mhz/8分频,计数单位为2微秒,TCCR1B:$2e LDI R17,$40 OUT GIMSK,R17 ;gimsk,6(允许int0中断) LDI R17,2 OUT MCUCR,R17 ;设INT0为下降沿中断(mcucr'b1&b0=10) CBI DDRD,2 ;int0 为输入 ;. ;. ;其他初始化略 SEI ; CLRBUF: LDI R27,1 CLR R26 ;接收数据缓存区首址$100 LDI R17,$40 OUT GIMSK,R17 ;gimsk,6 CLR R17 CLRLOP: ST X+,R17 CPI R26,$48 BRNE CLRLOP ;清接收缓存区($100--$147) LDS R16,$A3 CBR R16,$60 STS $A3,R16 ;接收错误($A3,6)和接收完成($A3,5)标志清除 CLR5: ;. ;. ;背景程序略 RJMP CLR5 ; RCVST: CBI DDRD,2 ;int0 为输入 SER R16 ;接收开始 OUT PORTC,R16 ;关显 LDI R27,1 CLR R26 ;接收数据指针,首指$100 LDI R17,18 ;接收18个字符,其末尾为$0D$0A MOV R14,R17 RCALL RVBYT1 ;接收第一个字符 RJMP RVBYT RVBLOP: RCALL RVBYT2 ;接收第二个字符及其后字符 RVBYT: LDS R17,$A3 SBRC R17,6 RJMP CLRBUF ;接收出错,转去清除$100--$14F SBRC R17,5 RJMP DTCOM ;接收完整数据块,转去处理 ST X+,R16 DEC R14 BRNE RVBLOP ;未收完18个字符,继续 CPI R26,$42 ;指针达到$142? BREQ DTCOM ;接收完整数据块,转去处理 DEC R26 DEC R26 ;$0D$0A(CR&LF)丢掉 RCVLP: LDI R17,18 MOV R14,R17 RJMP RVBLOP DTCOM: LDI R27,1 CLR R26 ;接收数据首地址:$100 DLLOP: CLR R29 LDI R28,$90 ;处理ASCII码程序acum要求将数据放在$90--$9f LD R16,X CPI R16,$50 ;第一个字符约定为‘P’才有效 BRNE RVCOM1 ;也是判断处理结束符 DLLOP1: LD R16,X+ ST Y+,R16 CPI R28,$A0 BRNE DLLOP1 ;传16个字符 PUSH R26 PUSH R27 RCALL ACUM ;ASCII变BCD再变为二进制数,累加 POP R27 POP R26 BRTS RVCOM1 ;ASCII码无效,转出! RJMP DLLOP RVCOM1: CLT RJMP CLRBUF ;转去清缓存区,重新接收 ;晶振采用4MHZ,指令(DEC+BRNE)耗时0.75微秒)! EX_INT0:POP R16 ;int0中断服务子程序 POP R16 ;废弃返回地址 LDI R16,HIGH(RCVST) PUSH R16 LDI R16,LOW(RCVST) PUSH R16 ;设置返回地址 IN R16,GIMSK ;禁止int0中断 CBR R16,$40 OUT GIMSK,R16 RETI RVBYT1: LDI R17,2 ;查到0接收时,再做一次接收 MOV R15,R17 LDI R17,50 ;第一个起始位半位延时(50*0.75=38微秒) MOV R12,R17 RJMP RVBCM RVBYT2: LDI R17,2 MOV R15,R17 RVBY2: LDI R17,147 ;110微秒>1位宽/9600baud,110/0.75=147 MOV R12,R17 TEST3: SBI PORTD,2 SBIS PIND,2 ;停止位超宽测试 RJMP RVST DEC R12 BRNE TEST3 LDS R16,$A3 ;110微秒内查到低电平为起始位 ORI R16,$20 STS $A3,R16 ;否则为接收结束,令$A3,5=1 RET RVST: LDI R17,60 ;60*0.75=45微秒(半位延时) MOV R12,R17 RVBCM: DEC R12 BRNE RVBCM LDI R17,9 ;1位起始+8位数据 MOV R13,R17 SBI PORTD,2 SBIC PIND,2 RJMP RVER1 ;无效起始位(半位测试) RVLOP: LDI R17,130 ;may be 128-132/位延时常数 MOV R12,R17 RVLP1: DEC R12 ;0.25微秒 BRNE RVLP1 ;0.5微秒/if condition is true SEC SBI PORTD,2 SBIS PIND,2 CLC DEC R13 BRNE OVRRC ;不是停止位,转数据位接收 BRCC RVER1 ;无效停止位,出错 TST R16 ; BRNE RBYRT ;不为0,收到一个有效字符 DEC R15 BRNE RVBY2 ;2次接收到$00,出错 RVER1: LDS R16,$A3 ORI R16,$40 ;接收出错标志 CBR R16,$20 STS $A3,R16 RBYRT: RET OVRRC: ROR R16 ;组织数据 RJMP RVLOP ;100.7微秒/程序实设位宽 ;范例42 ;8535'T0中断发送ASCII码程序,晶振4MHZ .EQU DATA2=$150 .ORG $000 STRT32: RJMP RST32 .ORG 009 RJMP T0_OVF .ORG $011 RST32: SER R17 OUT DDRB,R17 ;B口为输出 OUT PORTB,R17 ;输出高电平 LDI R16,2 ;0B00000010/8 DIVIDED(4fc/8:2微秒) OUT $33,R17 ;写入tccr0 LDI R16,204 ;(256-204)*2=104微秒/9600baud 104微秒/位! OUT TCNT0,R17 ; LDI R17,HIGH(ramend) OUT SPH,R17 LDI R17,LOW(ramend) OUT SPL,R17 LDI R25,HIGH(DATA2) LDI R24,LOW(DATA2);发送数据指针 LDS R17,$A3 CBR R17,$14 ;发送出错标志($A3,4)/发送完毕标志位($A3,2)清除! STS $A3,R17 SEI LDI R17,1 OUT TIMSK,R17 ;允许T/C0溢出中断 CLR R17 ;位计数器请除 HH32: LDS R16,$A3 SBRC R16,4 RJMP HHER32 ;出错 SBRS R16,2 ; RJMP HH32 ;查询等待数据块发送完成 ;. ;其他程序略 ;. ;可安排接收对方发来数据程序,见STRT33 RJMP RST32 HHER32:;. ;. ;错误处理略 RJMP RST32 T0_OVF: PUSH R16 IN R16,SREG PUSH R16 PUSH R26 PUSH R27 IN R16,TCNT0 INC R16 SUBI R16,52 ;重写入一位定时常数(带修正) OUT TCNT0,R16 MOV R26,R24 ;数据指针 MOV R27,R25 CPI R17,10 BREQ SND10 TST R17 BRNE SND9 SND0: CBI PORTB,0 ;发起始位(0) RJMP SVCOM SND9: CPI R17,9 BRNE SND18 ;1-8为数据位 SBI PORTB,0 ;9为停止位(1) CLR R17 ;停止位发完后,位计数器清除 ADIW R24,1 ;指针增1,指下一位数据 LD R16,X CPI R16,$0A ;本次发送的是$0A? BRNE SVCOM1 LDI R17,10 ;停止位标志 RJMP SVCOM1 SND10: LDS R16,$A3 SBR R16,4 ;发送完成标志 STS $A3,R16 ; SND11: CLR R16 OUT TCCR0,R16 ;关闭T/C0 CLR R17 ;清位计数器 LDI R24,LOW(DATA2);发送指针初始化 LDI R25,HIGH(DATA2) RJMP SVCOM1 SENDER: LDS R16,$A3 SBR R16,$10 STS $A3,R16 ;建出错标志 RJMP SND11 SND18: BRCC SENDER ;大于10为错误 LD R16,X ROR R16 ;发送位传到进位C BRCC S182 SBI PORTB,0 ;C(=1)-->PB0($18,0) BRCS S183 S182: CBI PORTB,0 ;C(=0)-->PB0($18,0) S183: LD R16,X ROR R16 ; ST X,R16 ;保存剩余位 MOV R24,R26 ;存数据指针 MOV R25,R27 SVCOM: INC R17 ;位计数器增1 SVCOM1: POP R27 POP R26 POP R16 OUT SREG,R16 POP R16 ;恢复现场 RETI ;范例43 ;8515/8535/晶振4MHZ RECEIVING ASCII CHAR. BY TCNT0&PB0 .EQU DATA3=$100 ;UES R11 SAVE SREG, R12 R13:数据指针DATA3 ;R14: 块长(BLOCK LENGTH) ,R15:接收字符暂存寄存器 .ORG 0 ;R16:(THE BIT SEQUENCE COUNTER)位序列计数器 ;R17:WORKING REG.R18:FLAG UNIT, BAUD RATE:9600 STRT33: RJMP RST33 ;X&Y:POINTER/接收数据缓存区首地址:$100 .ORG $009 ;$007(8515) RJMP T0_OVF1 .ORG $011 ;$00D(8515) RST33: LDI R17,HIGH(ramend) OUT SPH,R17 LDI R17,LOW(ramend) OUT SPL,R17 LDI R17,HIGH(DATA3) MOV R12,R17 LDI R17,LOW(DATA3) MOV R13,R17 ;R12R13:接收数据指针 CLRBF1: CLR R16 CLRLP: ST X+,R16 CPI R26,$48 BRNE CLRLP ;接收数据缓存区请除 CLR R18 ;标志寄存器请除/R18,2:完整数据块收到,R18,1 ;第一字符(块长)收到:R18,0:出错 LDI R17,$02 ;8535:$01 OUT TIMSK,R17 ;允许T/C0溢出中断 LDI R17,6 ;外部脉冲下降沿计数 OUT TCCR0,R17 CBI DDRB,0 ;PB0为输入 LDI R17,$FF OUT TCNT0,R17 ;计一个数即中断 ; SEI ; TEST1: RCALL DSPLY3 ;调串行移位显示子程序 SBRC R18,0 ; RJMP DLERR ;出错,转错误处理 SBRS R18,2 ;数据块接收完成? RJMP TEST1 LDI R16,128 DECLP: DEC R16 BRNE DECLP RJMP DTCOM0 ;先延时,再转处理数据块 DLERR: ;. ;出错处理 ;. ;. RCALL DL50 ;延时50毫秒后 RJMP RST33 ;重新接收 DTCOM0: LDI R27,1 CLR R26 ;数据存储区首地址$100 DLLOP0: CLR R29 LDI R28,$90 ;ASCII码处理区为$90--$9f LD R16,X CPI R16,$50 ;字母P打头才有效 BRNE RVCOM0 ;否则为无效字串或ASCII码处理结束 DLLO1: LD R16,X+ ST Y+,R16 CPI R28,$A0 BRNE DLLO1 ;传送16个字符 PUSH R26 PUSH R27 RCALL ACUM ;处理一组ASCII码数据 POP R27 POP R26 BRTC DLLOP0 ;T=1,ASCII码数据无效 CLT ;. ;错误处理 RJMP STRT33 RVCOM0:;. ;错误处理 ;. RJMP STRT33 T0_OVF1:IN R11,SREG ;T/C0中断服务 PUSH R17 CPI R16,0 ;起始位下降沿中断? BRNE T0SV10 LDI R17,2 ;YES OUT TCCR0,R17 ;改为内定时(4MHZ/8分频) LDI R17,232 ;半位时间常数24 定48微秒(<52) OUT TCNT0,R17 RJMP T0SV6 T0SV10: CPI R16,1 ;1,半位定时到 BRNE T0SV2 SBI PORTB,0 SBIC PINB,0 RJMP T0ERR ;高电平,错误 RJMP T0SV60 ;低电平,有效起始位 T0SV2: CPI R16,10 ; BRNE T0SV3 SBI PORTB0 ;10,接收停止位 SBIS PINB,0 RJMP T0ERR ;低电平,错误 LDI R17,6 OUT TCCR0,R17 ;改为外部脉冲下降沿计数,为接受下一位字符准备 LDI R17,$FF ;计一个数即中断 OUT TCNT0,R17 CLR R16 ;位计数器请除 SBRC R18,1 ;是第一个字符(r18,1=0)? RJMP T0SV21 ;否,为块内数据 MOV R14,R15 ;块长转入r14 SBR R18,2 ;块长已收到 RJMP T0SV61 T0SV21: PUSH XL PUSH XH MOV XH,R12 MOV XL,R13 ;取缓存区指针 ST X+,R15 ;字符送入缓存区 MOV R12,XH MOV R13,XL POP XH POP XL DEC R14 BRNE T0SV61 SBR R18,4 ;块长减为0,完整数据块收到 CLR R16 OUT TCCR0,R16 ;停止TCNT0 RJMP T0SV61 T0SV3: BRCC T0ERR ;出错(大于10) CLC ;2--9:数据位 SBI PORTB,0 ;接收一位数据 SBIC PINB,0 SEC ROR R15 ;数据组织到R15 T0SV60: IN R17,TCNT0 ;读TCNT0计数值 INC R17 ; SUBI R17,52 OUT TCNT0,R17 ;写入补偿后的时间常数 T0SV6: INC R16 ;位序列计数器增1 T0SV61: POP R17 OUT SREG,R11 RETI T0ERR: CLR R16 T0ERL: SBR R18,1 ;错误接收标志 OUT TCCR0,R16 ;停止TCNT0 RJMP T0SV61 ;范例44 .ORG 0 ;8535多机通讯主机程序/振4MHZ .EQU DTPINT=$180 ;UBRR=12,波特率19200(REL.ERR.=0.16%) .EQU DRPINT=$1C0 ;主机发往#1,#2,#3,#分机数据在 ;$180-18F,$190-19F,$1A0-1AF和$1B0-1BF STRT34: RJMP RST34 ;主机接收#1,#2,#3,#4分机之数据块分别在 ;$1C0-1CF,$1D0-1DF,$1E0-1EF和$1F0-1FF .ORG $00B RJMP U_RXC ;UART接收完成中断 .ORG $00C RJMP U_TXC ;UART 发送完成中断 .ORG $011 RST34: LDI R16,12 OUT UBRR,R16 ;BAUD RATE=FCP/16(UBRR+1)=4000000/(16*13)=19200 CLR R15 ;分机号初始化 LDI R27,HIGH(DTPINT) LDI R26,LOW(DTPINT);发送数据指针,首指$180 LDI R29,HIGH(DRPINT) LDI R28,LOW(DRPINT);接收数据指针(POINT TO $1C0) NEXTNO: LDI R16,$18 OUT UCR,R16 ;允许UART接收和发送,8位数据模式 INC R15 ;指向分机 OUTLP: OUT UDR,R15 ;呼分机号 TSLOP: IN R16,USR SBRS R16,7 RJMP TSLOP ;分机返回机号? IN R16,UDR CP R16,R15 ;与发送分机号符合? BRNE OUTLP ;不符再发 TXLOP: LD R16,X+ OUT UDR,R16 ;向分机发送数据块 TESTL: IN R17,USR SBRS R17,5 ;发送寄存器空? RJMP TESTL CPI R16,$0A BRNE TXLOP ;发完整个数据块? RXTST: IN R17,USR SBRS R17,7 ;RXC=1 分机发来数据 RJMP RXTST IN R16,UDR ST Y+,R16 ;接收数据转入内存 CPI R16,$0A BRNE RXTST ;接收完整数据块后 MOV R16,R15 CPI R16,4 ;转与下一分机通讯(只有4台分机) BRNE NEXTNO ;直到轮询完毕 HH34: RJMP HH34 ;可改为处理分机发来数据,再转入下一周轮询 .DSEG .ORG $180 DTPINT:.BYTE $40 $41 $45 $65 $73 $46 $42 $40 $6F $33 $44 $66 $5C $4D $4B $0D $0A $42 $4F $66 $78 $47 $45 $44 $63 $32 $48 $60 $7C $6D $45 $0D $0A $43 $56 $55 $53 $4D $4F $40 $2E $31 $42 $67 $4C $47 $4A $0D $0A $45 $54 $59 $63 $3D $4B $48 $2F $35 $48 $69 $3C $77 $43 $0D $0A .ORG $1C0 DRPINT: .BYTE $40 ;范例45 .ORG 0 ;8535多机通讯1#分机程序,晶振4MHZ .EQU DTPIT1=$180 ;UBRR=12 波特率19200(REL.ERR.=0.16%) .EQU DRPNT1=$1C0 STRT35: RJMP RST35 .ORG $00B RJMP UARXC ;UART接收完成中断 .ORG $00C RJMP UATXC ;UART发送寄存器空中断 .ORG $011 RST35: CLR R18 ;请除主机发来完整数据块标志(R18,7)/主机呼号选中分机 ;标志(R18,6) LDI R16,12 OUT UBRR,R16 ;[BAUD RATE=FCP/16(UBRR+1)] LDI R16,HIGH(DRPNT1) MOV R8,R16 LDI R16,LOW(DRPNT1) mov R9,R16 ;r8,r9:接收数据指针(FIRST POINT TO $1C0) LDI R16,$98 ;允许UART发送,接收,接收完成中断 OUT UCR,R16 SEI CLR R15 INC R15 ;1#分机设为1/2#分机设为2/3#分机设为3/4#分机设为4 RXDTS: SBRS R18,6 ;收到主机发来呼号? RJMP RXDTS OUT UDR,R15 ;将分机号反还主机 TXDON: IN R16,USR SBRS R16,5 RJMP TXDON ;发送寄存器空? RCVBLK: SBRS R18,7 RJMP RCVBLK ;等待接收主机发来数据块 LDI R16,HIGH(DTPIT1) MOV R6,R16 LDI R16,LOW(DTPIT1) MOV R7,R16 ;设置发送数据指针r6r7,首指$180 SBI UCR,5 ;UDRIE=1 ,引起发送寄存器空中断 TXDN: SBIC UCR,5 RJMP TXDN RJMP RST35 ;等待UDRIE=0 ,向主机发送数据块完毕后,转下一轮通讯 :UART中断接收程序 UARXC: IN R14,SREG TST R18 BREQ NUMB ;无主机呼号选中标志,查机号 PUSH R26 PUSH R27 IN R17,UDR ;读入接收数据 MOV XH,R8 MOV XL,R9 ;r8r9:接收数据缓存区指针,首指$1C0 ST X+,R17 MOV R8,XH MOV R9,XL CPI R17,$0A ;收到换行符? BRNE RSCOM1 SBR R18,$80 ;建立数据块接收完毕标志 RSCOM1: POP R27 POP R26 DRETI: OUT SREG,R14 RETI NUMB: IN R17,UDR CP R17,R15 ;主机呼号与本分机号符合? BRNE DRETI ;不符,转 SBR R18,$40 ;建选中标志 RJMP DRETI ; UART中断发送数据程序 UATXC: IN R16,SREG ;r6 r7:the sendDATA pointer(FIRST POINT TO $180) PUSH R16 PUSH R26 PUSH R27 MOV XH,R6 MOV XL,R7 ;发送数据指针 LD R16,X+ MOV R6,XH MOV R7,XL OUT UDR,R16 ;发送数据写入数据寄存器 CPI R16,$0A ;发送LF? BRNE SDCOM CBI UCR,5 ;禁止数据寄存器空中断(清UDRIE) LDI R16,HIGH(DRPINT) MOV R8,R16 LDI R16,LOW(DRPINT) MOV R9,R16 ;为接收作准备(FIRST POINT TO $1C0) SDCOM: POP R27 POP R26 POP R16 OUT SREG,R16 POP R16 RETI .DSEG .ORG $180 DTPIT1: .BYTE $10 ;$41 $45 $65 $73 $46 $42 $40 $6F $33 $44 $66 $5C $4D $4B $0D $0A .ORG $1C0 DRPNT1: .BYTE $10 ;$41 $45 $65 $73 $46 $42 $40 $6F $33 $44 $66 $5C $4D $4B $0D $0A ;范例46 .ORG $000 ;RS232<->RS485通讯标准转换/晶振4MHZ STRT36: RJMP RST485 ;使用8515! .ORG $009 RJMP U_RXC ;UART 接收中断 .ORG $00D RST485: LDI R16,2 OUT SPH,R16 LDI R16,$5f ; OUT SPL,R16 LDI R16,$98 ;允许UART接收和发送,允许接收中断 OUT UCR,R16 LDI R16,12 OUT UBRR,R16 ;波特率19200 SBI DDRB,7 SBI DDRB,6 ;PB7,PB6为输出 CBI PORTB,7 CBI PORTB,6 ;PB7控制485发送(高有效)PB6控制485接收(低有效) SEI HERE0: CPI R16,3 ;收到停止符? BRNE HERE0 ;未收到循环等待 HERE1: SBIS USR,6 ; RJMP HERE1 ;等待停止符发送完毕 CBI PORTB,7 ;禁止485发送 CBI PORTB,6 ;允许485接收 SBI USR,6 ;写‘1’清除发送完成标志! CLR R16 RJMP HERE0 ;转等待下一轮中转 U_RXC: SBI PORTB,7 ;允许485发送 SBI PORTB,6 ;禁止485接收, IN R16,UDR ;读出接收数据,同时清除接收中断标志 TSAGN: SBIS USR,6 ;上一数据发送完毕? RJMP TSAGN SBI USR,6 ;清除发送完成标志 OUT UDR,R16 ;转发本次接收数据 RETI ;范例47 .EQU DATA4=$220 .ORG $000 ;同步串口通讯主机程序,晶振4MHZ STRT37: RJMP RST37 .ORG $00A ;8535 SPI中断矢量(8515为$008) RJMP SPINT .ORG $011 ;$00D(8515) RST37: LDI R16,2 OUT SPH,R16 LDI R16,$5f OUT SPL,R16 ;堆栈指针初始化 LDI R16,$A0 OUT DDRB,R16 ;SCK,MOSI为输出 LDI R16,$DC OUT SPCR,R16 ;允许SPI中断,先发送高位,主控方式,时钟为主频4分 ;频,后沿有效 LDI XH,HIGH(DATA4) LDI XL,LOW(DATA4);数据指针 LDI R16,$30 MOV R15,R16 ;数据块长 LDI R16,12 ;0.25微秒 SPI0: DEC R16 ;0.25微秒 BRNE SPI0 ;0.5微秒 总延时9微秒 LD R16,X OUT SPDR,R16 ;写发送数据寄存器,启动发送 SEI HH37: RJMP HH37 ;背景程序略 SPINT: IN R14,SREG IN R16,SPDR ;读出接收数据 ST X+,R16 DEC R15 BRNE SPI1 ;数据收发完毕? OUT SPCR,R15 ;是,停止收发 OUT SREG,R14 RETI SPI1: LDI R16,6 ;0.25微秒 SPI1A: DEC R16 ;0.25微秒 BRNE SPI1A ;0.5微秒 总共4.5微秒 LD R16,X OUT SPDR,R16 ;发下一个数据 OUT SREG,R14 RETI ;范例48 .ORG $000 STRT37S:RJMP RST37S ;同步串口通讯从机程序(8515) 晶振4MHZ .ORG $008 ;$00A(8535) RJMP SPINTS ;同步串口中断矢量 .ORG $00D ;$011(8535) RST37S: LDI R16,2 OUT SPH,R16 LDI R16,$5f OUT SPL,R16 LDI R16,$40 OUT DDRB,R16 ;MISO为输出 LDI R16,$CC OUT SPCR,R16 ;允许SPI中断,先发送高位,从控方式,时钟为主频4分频,后沿有效 LDI YH,HIGH(DATA4) LDI YL,LOW(DATA4);数据指针 LDI R16,$30 ;数据长度 LD R15,Y OUT SPDR,R15 ;写入数据寄存器 SEI HH37S: RJMP HH37S ;背景程序从略 SPINTS: IN R14,SREG IN R15,SPDR ;读接收数据 ST Y+,R15 DEC R16 BRNE SPI2 ;数据块收发完毕 OUT SPCR,R16 ;停止中断收发 RJMP SPI3 SPI2: LD R15,Y OUT SPDR,R15 ;发下一数据 SPI3: OUT SREG,R14 RETI ;范例49 以模拟串口与串行移位寄存器74165通讯,以74165驱动LED显示子程序 DSPLY3: SBI DDRC,1 ;PC1,串行数据输出 SBI DDRC,0 ;PC0,移位时钟 CBI PORTC,0 ; LDI R17,8 ;8字节显示缓存区$60(高)--$67(低)) MOV R8,R17 CLR XH LDI XL,$60 ;指针,首指最高位($60) SRDLOP: LDI R17,8 ;8位/字节 MOV R9,R17 LD R10,X+ LDI ZH,HIGH(TABLE*2) LDI ZL,LOW(TABLE*2);使用DSPY子程序段选表 ADD ZL,R10 ;加代码寻址 BRCC DSPL1 INC ZH DSPL1: LPM ;取段选码 COM R0 ;取为反码 SENDLP: ROR R0 ;段选码右移一位 C<--R0最低位 CBI PORTC,1 BRCC SNDL1 ;进位C传给PC1 SBI PORTC,1 SNDL1: SBI PORTC,0 ;移位时钟,上升沿有效 CBI PORTC,0 ;移位时钟变低 DEC R9 BRNE SENDLP ;8位段选码循环右移 DEC R8 BRNE SRDLOP ;8位LED显示数据都更新一遍? RET ;是,结束 ;脉宽调制(PWM)输出程序 ;范例50 ;以定时器定时产生精确半秒信号,以PD5输出精确秒号 .ORG $000 ;晶体实测频率为8000367HZ STRT40: RJMP RST40 ;USE 8535 .ORG $008 ;t/C1 overflow vector RJMP T1_OVF .ORG $011 RST40: LDI R16,HIGH(ramend) OUT SPH,R16 LDI R16,LOW(ramend) OUT SPL,R16 SBI DDRD,5 ;PD5(OC1A)为输出 CBI PORTD,5 ;初始输出为低 LDI R16,2 ;8分频 INT(8000367/8)=1000046) 折半500023定半秒 OUT TCCR1B,R16 ;T/C1控制寄存器(I/O ADDR:$2E) LDI R16,$5E ;500023=65536*8-24265=$10000*8-$5EC9 OUT TCNT1H,R16 ;TCC=$5EC9 先写高位字节 LDI R16,$C9 OUT TCNTIL,R16 LDI R16,$04 OUT TIMSK,R16 ;允许T/C1溢出中断/8535C/t1:timsk,2&t0:timsk,0 LDI R17,8 ;8次中断定半秒 CLR R16 OUT TIFR,R16 ;清除定时/计数器中断标志 SEI ; HH40: RJMP HH40 ;背景程序略 T1_OVF: IN R5,SREG ;保存状态寄存器 DEC R17 BRNE COMP1 ;定时时间到? IN R16,PORTD ;读入PD5当前状态 LDI R17,$20 EOR R16,R17 ;求反PD5(OC1A)输出 OUT PORTD,R16 IN R17,TCNT1L ;* IN R16,TCNT1H ;*读回TCNT1计数值 SUBI R17,$37 ;* SBCI R16,$A1 ;*减去$5EC9之补码$A137 SUBI R17,$FF ;*补偿指令8条占一个计数单位 SBCI R16,$FF ;*补偿后TCC=$5EC9+(TCNT1)+1 OUT TCNT1H,R16 ;* OUT TCNT1L,R17 ;*写入TCNT1 LDI R17,8 ;重新写入中断次数 COMP1: OUT SREG,R5 RETI ;范例51 ;以比较匹配A达到时交替输出高低电平及写入其维持 ;时间常数之方法实现脉宽调制输出 .ORG $000 STRT41: RJMP RST41 ;5.008MS(高):10.000MS(低) 晶振4MHZ .ORG $006 RJMP T1_CMPA ;USE 8535 .ORG $011 RST41: LDI R16,HIGH(RAMEND) OUT SPH,R16 LDI R16,LOW(RAMEND) OUT SPL,R16 LDI R16,$80 ;T/C1比较匹配A达到时,清除输出脚oc1a OUT TCCR1A,R16 LDI R16,$0B ;64分频 ctc1=1 比较匹配达到清tcnt1 OUT TCCR1B,R16 SBI DDRD,5 SBI PORTD,5 ;pd5(oc1a)初始化输出为高 CLR R16 OUT TCNT1H,R16 ;予清除tcnt1 OUT TCNT1L,R16 LDI R16,1 OUT OCR1AH,R16 LDI R16,$39 ;写比较匹配寄存器(313*0.25*64=5.008MS) OUT OCR1AL,R16 LDI R16,$10 OUT TIMSK,R16 ;允许比较匹配A中断 SEI HH41: RJMP HH41 ;背景程序略 T1_CMPA:IN R5,SREG IN R16,TCCR1A SBRS R16,6 RJMP OUTLOW ;当前输出低电平,转 LDI R16,1 OUT OCR1AH,R16 LDI R16,$39 ;写入高电平维持时间313 OUT OCR1AL,R16 LDI R16,$80 ;比较匹配A达到时,OC1A输出为低 OUT TCCR1A,R16 OUT SREG,R5 RETI OUTLOW: LDI R16,2 OUT OCR1AH,R16 LDI R16,$71 ;写入低电平维持时间625(=$271) (625*0.25*64=10.000MS) OUT OCR1AL,R16 LDI R16,$C0 ;比较匹配A达到时,OC1A输出为高 OUT TCCR1A,R16 OUT SREG,R5 RETI ;范例52 ;以比较匹配达到时求反输出并按高低电平写入 .ORG $000 ;维持时间之方法实现脉宽调制输出 STRT42: RJMP RST42 ;5.008MS(高):10.000MS(低) 晶振4MHZ .ORG $006 RJMP T1_CMPA .ORG $011 RST42: LDI R16,HIGH(ramend) OUT SPH,R16 LDI R16,LOW(RAMEND) OUT SPL,R16 LDI R16,$40 ;比较匹配A达到时,对OC1A输出求反 OUT TCCR1A,R16 LDI R16,$0C ;256分频 ctc1=1 比较匹配达到时 清除cnt1 OUT TCCR1B,R16 SBI DDRD,5 ;PD5(oc1a)为输出 SBI PORTD,5 ;初始输出为高 CLR R16 OUT TCNT1H,R16 ;清除tcnt1 OUT TCNT1L,R16 OUT OCR1AH,R16 LDI R16,78 ;高电平时间常数78 OUT OCR1AL,R16 LDI R16,$10 OUT TIMSK,R16 ;允许比较匹配A中断 SEI HH42: RJMP HH42 ;背景程序略 T1_CMPA:IN R5,SREG ; IN R16,PORTD SBRC R16,5 RJMP T1CM1 ;当前oc1a为高,转 LDI R16,0 OUT OCR1AH,R16 LDI R16,156 ;低电平时间常数156 OUT OCR1AL,R16 OUT SREG,R5 RETI T1CM1: LDI R16,0 OUT OCR1AH,R16 LDI R16,78 ;高电平时间常数78 OUT OCR1AL,R16 OUT SREG,R5 RETI ;模/数转换和数/模转换及脉宽调制输出应用 ;范例53 ;模拟量采集和3路脉宽调制输出(OCR1A/OCR1B&OCR2)综合程 ;序/晶振4MHZ .ORG $000 STRT50: RJMP RST50 ;avr is AT90S8535 .ORG $00E RJMP ADCOM ;模数转换完成中断 .ORG $011 RST50: LDI R16,HIGH(ramend) OUT SPH,R16 LDI R16,LOW(ramend) OUT SPL,R16 ;堆栈指针初始化 CLR R11 ;通道号初始化 CLR R12 CLR R13 ;累加和予清除 OUT $07,R11 ;ADC通道初始化,指向0#通道 LDI R16,$6C ;T/C2为自运行pwm输出,加法计数匹配清除OC2,减法计 ;数匹配置位OC2(正向PWM);对晶振64分频 OUT TCCR2,R16 ;tccr2' ADDR.:$25 LDI R16,$ED ;使能,启动ADC/自由运行/转换完成中断/对晶振32分频 OUT ADCSR,R16 ;ADDR:$06 adc控制状态寄存器 IN R16,ASSR CBR R16,8 OUT ASSR,R16 ;TCNT2 用主时钟! INC R11 OUT $07,R11 ;予切换到1号ADC通道 SBI DDRD,4 SBI PORTB,4 ;pd4:oc1b SBI DDRD,5 ;pd5:oc1a pd4,pd5 皆为输出 oc1b初始输出为高 SBI DDRD,7 ;oc2 输出 LDI R16,$E3 ;0B11100011,自运行PWM,COM1A1/0=11,COM1B1/0=10 OUT TCCR1A,R16 ;减法计数匹配清除OC1A,加法计数匹配置位OC1A(反向PWM);加法计 ;数匹配清除OC1B,减法计数匹配置位OC1B(正向PWM) LDI R16,2 OUT TCCR1B,R16 ;tcnt1 8分频 LDI R16,0 OUT TCNT1H,R16 ;wr.high B at first OUT TCNT1L,R16 ;清除TCNT1 OUT TCNT2,R16 ;清除TCNT2 OUT DDRA,R16 ;A口输入 OUT PORTA,R16 ;输入为高阻态 SEI COMLP: CPI R11,1 BREQ COMLP ;通道号初始为1,等待切换过去 COML0: CPI R11,1 BRNE COML0 ;通道号再次为1时,0#通道正在转换,7#通道已转换完毕, ;已得到8个A/D采样累加和 ASR R12 ROR R13 ASR R12 ROR R13 ASR R12 ROR R13 ;累加和除以8 BRCC COML1 CLR R16 ADC R13,R16 ADC R12,R16 ;四舍五入 COML1: OUT OCR1AH,R12 OUT OCR1AL,R13 OUT OCR1BH,R12 OUT OCR1BL,R13 ;10位数据写入比较匹配寄存器 ASR R12 ROR R13 ASR R12 ROR R13 BRCC COML2 INC R13 BRNE COML2 DEC R13 COML2: OUT OCR2,R13 ;8位数据写入比较匹配寄存器 CLR R12 CLR R13 ;累加和清除 RJMP COMLP ADCOM: IN R15,ADCL ;ADC完成中断 IN R14,ADCH ADD R13,R15 ;模拟数值加入累加和 ADC R12,R14 INC R11 SBRC R11,3 CLR R11 ;total 8 chanales!&8 CHANGED TO 0 OUT $07,R11 ;$07:admux'address REGISTER RETI ;范例54 ;以R-2R电阻网络和C口配合组成DAC与输入模拟量比较实现模数转换 .ORG $000 ;电阻网络DAC最大输出(AIN0)只能达到3.32V(PCi输出只能达到5V) STRT51: RJMP RST51 ;输入模拟量最大为4.98V,故应将DAC输出放大1.5倍再与前者比较 .ORG $011 ;也可将输入模拟量衰减为2/3再与DAC输出比较 RST51: LDI R16,2 ;但应将转换结果乘以1.5以使其复原,程序取后者 OUT SPH,R16 ;堆栈指针初始化 LDI R16,$5F OUT SPL,R16 SER R16 OUT DDRC,R16 ;C口全部为输出,DAC输出为AIN0输入 CLR R16 OUT DDRB,R16 ;B口为输入 LDI R16,$F3 OUT PORTB,R16 ;PB2(AIN0),PB3(AIN1)输入为高阻状态 CLR R15 ;模数转换结果予清除 LDI R16,$80 ;逼近增量初始值 CMPLP: ADD R15,R16 ;模数转换阶段值加逼近增量 OUT PORTC,R15 ;转成模拟量 NOP NOP NOP ;4MHZ/等待1微秒 SBIC ACSR,ACO ;输入模拟量大于DAC模拟量,清除ACO SUB R15,R16 ;否则去掉逼近增量 LSR R16 ;逼近增量折半 BRNE CMPLP ;逼近增量变为0? MOV R16,R15 ;*是,转换结束 LSR R15 ;* ADC R15,R16 ;*将转换结果乘以1.5 HH50: RJMP HH50 ;背景程序略 ;范例55 ;40点平均在r18r19,累加和在r5r6r7;20点平均在R14R15,累加和在R1R3R4 SLPAV: PUSH R26 ;采样在R8R9,采样数据存储区$150--$19F/工作寄存器r1--r19&r26 r27 PUSH R27 LDI R27,1 LDS R26,$14F ;数据存储区首地址$14F ADD R7,R9 ADC R6,R8 ;采样加入40点平均累加和 BRCC SLP1 INC R5 ;有进位,高位字节增1 SLP1: ADD R4,R9 ADC R3,R8 ;采样加入20点平均累加和 BRCC SLP2 INC R1 ;有进位,高位字节增1 SLP2: LD R16,X ST X+,R9 MOV R9,R16 ;置换出最旧采样低位字节 LD R16,X ST X+,R8 MOV R8,R16 ;置换出最旧采样高位字节 CPI R26,$A0 BRNE SLPA1 LDI R26,$50 ;采样放满存储区后,指针初始化($1A0=$150) STS $14F,R26 LDS R16,$A4 SBRC R16,4 RJMP SLPA2 ;40点平均时间达到,转 SBR R16,$10 ;设置40点平均时间达到标志 STS $A4,R16 RJMP SLDIV ;转去计算40点平均 SLPA1: STS $14F,R26 ;暂存指针 LDS R16,$A4 SBRS R16,4 RJMP SLPB0 ;还未到40点平均,转 SLPA2: SUB R7,R9 SBC R6,R8 ;到40点平均后除加上新采样外,还要减去最旧采样 BRCC SLDIV DEC R5 ;不够减,高位字节减1 SLDIV: CLR R12 LDI R16,40 MOV R11,R16 CLR R10 MOV R13,R5 MOV R14,R6 MOV R15,R7 RCALL DIV165 ;计算40点平均 MOV R18,R14 MOV R19,R15 ;存入r18r19 SLPB0: CPI R26,$78 BRNE SLPB1 LDS R16,$A4 SBRC R16,3 RJMP SLPB2 SBR R16,8 ;建20点平均时间到标志 STS $A4,R16 RJMP SLPDV ; SLPB1: LDS R16,$A4 SBRS R16,3 RJMP SLRET ;20点平均时间未到 SLPB2: SUBI R26,42 ;指针退回42字节,指向20点平均最旧数据 CPI R26,$50 ;不小于80,未超出采样数据存储区 BRCC SLPB20 SUBI R26,-80 ;否则加80调整回$150-$19F SLPB20: LD R11,X+ ; LD R10,X SUB R4,R11 SBC R3,R10 ;找到20点平均最旧采样,并将其从累加和中减去! BRCC SLPDV DEC R1 SLPDV: LDI R16,20 MOV R11,R16 CLR R10 CLR R12 MOV R13,R1 MOV R14,R3 MOV R15,R4 RCALL DIV165 ;20点平均在r14r15中 SLRET: POP R27 POP R26 RET ;范例56 ;断电保护芯片MAX704,/RESET脚接8515同名脚 ;/PFO接INT0,由VOUT脚给UT6264(或UT62256)/62x42x供电, ;本程序不涉及休眠! .ORG $000 ;AT90S8515/时钟4MHZ STRT60: RJMP RST60 RJMP EX_INT0 ;外部中断0 RJMP EX_INT1 :外部中断1 .ORG $009 ;uart_rxc interrupt RJMP RCVSV .ORG $010 RST60: LDI R16,2 OUT SPH,R16 LDI R16,$5f OUT SPL,R16 ;堆栈指针初始化,指向$25f CLR XH LDI XL,$60 CLR R16 CLRX: ST X+,R16 CPI XL,$5E BRNE CLRX CPI XH,2 BRNE CLRX ;清除$60--$25d LDI R16,$F0 OUT DDRB,R16 ;PB3-PB0输入 PB7-PB4输出 OUT PORTB,R16 ;上拉PB7-PB4 SBI PORTB,0 SBIS PINB,0 RJMP BG1A ;若将PB0接地,不做断电启动 LDI R16,70 CLR R12 CLR R11 DLOPX: DEC R11 BRNE DLOPX DEC R12 BRNE DLOPX DEC R16 BRNE DLOPX ;延时3.4秒(clk 4mhz) CLR R27 LDI R26,$60 CLR R16 LOPX1: ST X+,R16 CPI R26,$5E BRNE LOPX1 CPI R27,2 ;清除$60--$25d BRNE LOPX1 CLI LDI R16,$80 OUT GIMSK,R16 ;int1中断使能 LDI R16,$DA ;激活外部RAM,加1等待周期,不休眠,int0/int1下降沿有效 OUT MCUCR,R16 ;. ;. ;. ; LDS R16,$9FFE ;片外sram $8000-$9fff) CPI R16,$55 BRNE BG1A ;查断电标志 LDS R16,$9FFF CPI R16,$AA BREQ BG2B ;查到 BG1A: LDI R27,$80 LDI R26,0 CLR R16 CLOPX: ST X+,R16 CPI R27,$A0 BRNE CLOPX ;清除$8000--$9FFF LDI R16,$AA ST -X,R16 ;$AA-->($9fff) COM R16 ST -X,R16 ;$55-->($9ffe) BG1A0: IN R16,GIMSK SBR R16,$40 OUT GIMSK,R16 ;允许int0中断 RJMP NRMST BG2B: LDS R16,$9FFD ;$9FFD:最高位为生产标志 SBRS R16,7 RJMP NRMST ;无生产标志转平常启动 BG5C: CBI PORTB,7 ;指示断电启动 LDI R27,$80 LDI R26,2 ;SRAM 8002-825F传回片内 LDI R29,0 LDI R28,2 CLR R0 ;检查和清除 APX0: LD R1,X+ ST Y+,R1 ;传送数据块 ADD R0,R1 CPI R28,26 ;指向r26? BRNE APX0 LD R1,X+ ;取r26 ADD R0,R1 LD R1,X+ ;取r27 ADD R0,R1 LD R1,X+ ;取r28 ADD R0,R1 LD R1,X+ ;取r29/ r26--r29为数据指针,不能当作数据传送 ADD R0,R1 LDI R28,30 APX2: LD R1,X+ ADD R0,R1 ST Y+,R1 CPI R28,$5F BRNE APX2 INC XL INC YL ;SREG不断变化,不能加入累加和! APX3: LD R1,X+ ADD R0,R1 ST Y+,R1 CPI R28,$60 BRNE APX3 CPI R29,2 BRNE APX3 ;到$25f? LDS R1,$9FFC ;取检查和 ADD R0,R1 ;检查和(CHECKSUM)正确? BREQ BG5D RJMP BG1A ;错,转总清 BG5D: WDR LDI R16,$0D ;看门狗初始化,溢出时间0.49" OUT WDTCR,R16 CLR R2 ;调DSPA次数计数器清除 IN R16,GIMSK SBR R16,$40 OUT GIMSK,R16 ;允许int0中断 LDS R26,$235 OUT SPH,R26 LDS R26,$234 OUT SPL,R26 POP R26 POP R27 POP R28 POP R29 ;数据指针出栈 POP R1 OUT SREG,R1 ; POP R1 POP R0 RETI ;弹出断点,开放中断 NRMST: WDR LDI R16,$0D ;看门狗初始化,溢出时间0.49" OUT WDTCR,R16 CLR R2 ;....... SEI ;(略) RCVSV: ;. ;. ;. EX_INT0:PUSH R0 ;断电中断服务 I BE CLEARED! PUSH R1 IN R1,SREG PUSH R1 PUSH R29 PUSH R28 PUSH R27 PUSH R26 ;保护X,Y指针 LDI R26,$1D OUT WDTCR,R26 LDI R26,$15 OUT WDTCR,R26 ;禁止看门狗 IN R26,SPL STS $234,R26 IN R26,SPH STS $235,R26 ;保护堆栈指针 LDI R27,0 LDI R26,2 LDI R29,$80 LDI R28,2 ;SRAM $002-25F 转片外$8002-$825f CLR R0 ;检查和予清除 ALPX1: LD R1,X+ ST Y+,R1 ADD R0,R1 ;加入累加和 CPI R26,26 ; BRNE ALPX1 ; POP R1 ;R26~R29从堆栈中取! ADD R0,R1 ST Y+,R1 POP R1 ;取R27 ADD R0,R1 ST Y+,R1 POP R1 ;取R28 ADD R0,R1 ST Y+,R1 POP R1 ;取R29 ADD R0,R1 ST Y+,R1 IN R26,SPL SUBI R26,4 ;恢复堆栈指针,抵消4个POP OUT SPL,R26 LDI R26,30 ;越过R26-R29,指向R30 APX10: LD R1,X+ ST Y+,R1 ADD R0,R1 CPI R26,$5F BRNE APX10 INC XL ;SREG 越过! INC YL APX20: LD R1,X+ ST Y+,R1 ADD R0,R1 CPI R26,$60 BRNE APX20 CPI R27,2 BRNE APX20 ;完成到$8002-825F之转移 NEG R0 STS $9FFC,R0 ;SAVE THE CHECKSUM TO $9FFC LDI R26,62 CLR R27 CLR R28 DLPX5: DEC R28 BRNE DLPX5 DEC R27 BRNE DLPX5 DEC R26 BRNE DLPX5 ;延时3秒(49.16ms*62=3") LDI R27,$80 LDI R26,2 ;$8002-$825F LDI R29,0 LDI R28,2 ;$002-25F CLR R0 APX1A: LD R1,X+ ST Y+,R1 ;将片外SRAM数据传回片内 ADD R0,R1 CPI R28,26 BRNE APX1A LD R1,X+ ;R26 ADD R0,R1 LD R1,X+ ;R27 ADD R0,R1 LD R1,X+ ;R28 ADD R0,R1 LD R1,X+ ;R29 ADD R0,R1 LDI R28,30 APX1B: LD R1,X+ ST Y+,R1 ADD R0,R1 CPI R26,$5F BRNE APX1B INC XL ;越过SREG! INC YL APX2A: LD R1,X+ ADD R0,R1 ST Y+,R1 CPI R28,$60 BRNE APX2A CPI R29,2 BRNE APX2A ;到$25f? LDS R1,$9FFC ADD R0,R1 BRNE ERRDL RJMP BG5D ;检查和正确 ERRDL: (略) ;错误处理 ;范例57 ;使用干电池便携系统断电保护程序 MAX704 RESET引脚接8535同名脚 ;/PFO接8535INT0 断电时由电池给AT90LS8535供电 ;晶振4MHZ .ORG $000 ;AT90LS8535只使用片内sram;在片内RAM中保护数据 STRT61: RJMP RST61 RJMP EX_INT0 RJMP EX_INT1 .ORG $00B RJMP RVCMPLT ;串行数据接收完成 .ORG $011 RST61: LDI R16,$00 OUT DDRA,R16 ;PA7-PA0为输入 LDI R16,21 CLR R12 CLR R13 DLPX: DEC R13 BRNE DLOPX DEC R12 BRNE DLPX DEC R16 BRNE DLOPX ;延时1秒(clk 4mhz) LDI R16,2 OUT SPH,R16 LDI R16,$5f OUT SPL,R16 ;堆栈指针$25f CLR R2 ;调DSPB次数预清除 WDR LDI R16,$0D ;设置看门狗溢出时间0.49" OUT WDTCR,R16 LDI R16,$0F OUT PORTA,R16 IN R16,PINA CBR R16,$F0 ;清除无用的高4位 CPI R16,15 BRNE BG3A ;K0-K3有键按下,转 LDS R16,$23E ; CPI R16,$55 BRNE BG3A LDS R16,$23F CPI R16,$AA BRNE BG3A ;无断电标志,转 CLR R16 STS $23E,R16 ;清除断电标志 CLI LDI R16,$C0 OUT GIMSK,R16 ; LDI R16,$60 ;掉电休眠, OUT MCUCR,R16 ;int0 INT0 电平中断 ;. ;. ;其他初始化程序略 ;. ; RJMP REST2 ;转断电启动! RVCMPLT:;(MISSING) BG3A: CLR R27 LDI R26,$60 CLR R16 LOPX1: ST X+,R16 CPI R26,$60 BRNE LOPX1 CPI R27,2 BRNE LOPX1 ;清除$60--$25f LDI R16,2 OUT SPH,R16 LDI R16,$5FH OUT SPL,R16 CLI LDI R16,$C0 OUT GIMSK,R16 ;允许int0/int1中断 LDI R16,$60 ;掉电休眠 OUT MCUCR,R16 ;INT0/INT1 电平中断 ;. ;. ;其他初始化程序略 ;. SEI HH61: RCALL DSPB ;液晶显示子程序略 LDI R16,$0F ;激活上拉电阻 OUT PORTA,R16 ; IN R16,PINA ;读入键状态 CBR R16,$F0 CPI R16,$0F ;有键按下? BRNE HH61 ;等待释放 IN R16,TIMSK SBR R16,$C0 OUT TIMSK,R16 ;重新允许INT1中断 SLEEP ;进入掉电休眠 RJMP HH61 ;唤醒后显示新采集的数据 EX_INT1:SEI ;允许INT0中断 PUSH R16 ;K0/K1/K2/K3有按下者,产生电平中断唤醒MCU,采集数据 IN R16,SREG PUSH R16 SBI PORTA,3 SBIS PINA,3 RJMP DLK63 ;K3按下采集数据 SBI PORTA,2 SBIS PINA,2 RJMP DLK62 ;K2按下采集数据 SBI PORTA,1 SBIS PINA,1 RJMP DLK61 ;K1按下采集数据 RJMP DLK60 ;K0按下采集数据 DLKRT: IN R16,TIMSK CBR R16,$80 OUT TIMSK,R16 ;禁止INT1中断(键未释放或抖动时不引起中断) POP R16 OUT SREG,R16 POP R16 RETI DLK60: ;. ;采集、处理数据,数据处理后送入显示缓存区 ;. ;. RJMP DLKRT DLK61: ;. ;采集、处理数据,数据处理后送入显示缓存区, ;. ;. RJMP DLKRT DLK62: ;. ;采集、处理数据,数据处理后送入显示缓存区 ;. ;. RJMP DLKRT DLK63: ;. ;采集、处理数据,数据处理后送入显示缓存区 ;. ;. RJMP DLKRT EX_INT0:PUSH R0 ;掉电中断服务子程序 PUSH R2 PUSH R12 PUSH R13 ;CLI ALREADY! PUSH R14 PUSH R15 PUSH R16 PUSH R17 PUSH R26 PUSH R27 PUSH R30 PUSH R31 IN R16,SREG PUSH R16 ;保护状态寄存器 LDI R16,$1D OUT WDTCR,R16 LDI R16,$15 OUT WDTCR,R16 ;停止看门狗 IN R16,SPL STS $23C,R16 IN R16,SPH STS $23D,R16 ;保护堆栈指针 LDI R16,$55 STS $23E,R16 COM R16 STS $23F,R16 ;写断电标志 SER R16 OUT PORTC,R16 ;关显示 CLR R16 OUT GIMSK,R16 ;禁止外部中断(INT0&INT1) SLEEP ;进入掉电休眠 REST2: LDS R16,$23D OUT SPH,R16 LDS R16,$23C OUT SPL,R16 ;取出堆栈指针 POP R16 OUT SREG,R16 ;恢复状态寄存器 POP R31 POP R30 POP R27 POP R26 POP R17 POP R16 POP R15 POP R14 POP R13 POP R12 POP R2 POP R0 ;恢复工作寄存器,主程序初始化时只能使用这些寄存器! RETI ;弹出断点,开放中断 ;范例58 .EQU DPOINT=$100 ;DATA BLOCK from $100 to $22b CRCST: LDI R16,2 ;最末2字节在发送方已清为零(或仍为$0D$0A) MOV R11,R16 ;在接收方则为对方计算出的CRC校验码(余式) LDI R16,$2C MOV R12,R16 ;(r11r12)内装入$22C,块长为$12C CRCST1: LDI R26,HIGH(DPOINT) LDI R27,LOW(DPOINT);数据指针 CRC0: CLR R14 CLR R15 LDI R17,$80 ; 16 15 2 LDI R18,$05 ;P(X)=X +X +X +1=$18005 CRC1: LDI R16,8 MOV R13,R16 ;8位/字节 LD R16,X+ CRC2: LSL R16 ROL R15 ROL R14 BRCC CRC3 EOR R14,R17 EOR R15,R18 ;移出位为1时,将寄存器r14r15内容异或立即数$8005 CRC3: DEC R13 ;位数减1 BRNE CRC2 DEC R12 ;字节数减1 BRNE CRC1 DEC R11 BRNE CRC1 ST -X,R15 ST -X,R14 ;除得余数放在数据块尾部(或将原始数据恢复)! RET ;范例58A ;DS18B20读出温度数据CRC检测子程序,生成多项式为P(X)=X8+X4+X3+1 CRCSTA:LDI XL,$70 ;温度数据指针 CLR XH LDI R16,9 ;温度数据,上、下限......CRC校验码等共9字节 CLR R15 ;异或除法工作单元 LDI R18,$8C CRC1A: LD R14,X+ LDI R17,8 CRC2A: LSR R14 ROR R15 ;位序列右移 BRCC CRC3A EOR R15,R18 ;移出位为1时,位序列异或立即数$8C CRC3A: DEC R17 BRNE CRC2A ;右移次数减1 DEC R16 BRNE CRC1A ;块长减1 RET ;(R15)=0 接收正确! ;范例59 DEMCRC: LDI R27,1 ;CRC演示程序(校验码16位) CLR R26 ;数据块首地址为$100 DEMLP: ST X+,R26 ; CPI R26,$2A ;在$100-$229中充入数据 BRNE DEMLP CPI R27,2 BRNE DEMLP ;$100--$229中充入$00--$FF和$00-$29 CLR R16 ST X+,R16 ST X,R16 ;$22A,$22B两单元请除,将计算出余式(即CRC校验码)放在其中 RCALL CRCST ;在发送方计算出CRC校验码 RETEST: RCALL CRCST ;在接收方做CRC检测(余式在r14r15) OR R15,R14 ;r14r15恢复为$0000(或恢复出原数据为正确接收) BRNE ERCRC HCRC: RJMP HCRC ERCRC: ;. ;出错处理,要求对方重发 ;. RJMP RETEST ;重新CRC检测 .DSEG .ORG $100 DPOINT: .BYTE $12C ; $00 $01 $02 $03 $04 $05 $06 $07 $08 $09 $0a $0b $0c $0d $0e $0f ; $10 $11 $12 $13 $14 $15 $16 $17 $18 $19 $1a $1b $1c $1d $1e $1f ; $20 $21 $22 $23 $24 $25 $26 $27 $28 $29 $2a $2b $2c $2d $2e $2f ; $30 $31 $32 $33 $34 $35 $36 $37 $38 $39 $3a $3b $3c $3d $3e $3f ; $40 $41 $42 $43 $44 $45 $46 $47 $48 $49 $4a $4b $4c $4d $4e $4f ; $50 $51 $52 $53 $54 $55 $56 $57 $58 $59 $5a $5b $5c $5d $5e $5f ; $60 $61 $62 $63 $64 $65 $66 $67 $68 $69 $6a $6b $6c $6d $6e $6f ; $70 $71 $72 $73 $74 $75 $76 $77 $78 $79 $7a $7b $7c $7d $7e $7f ; $80 $81 $82 $83 $84 $85 $86 $87 $88 $89 $8a $8b $8c $8d $8e $8f ; $90 $91 $92 $93 $94 $95 $96 $97 $98 $99 $9a $9b $9c $9d $9e $9f ; $a0 $a1 $a2 $a3 $a4 $a5 $a6 $a7 $a8 $a9 $aa $ab $ac $ad $ae $af ; $b0 $b1 $b2 $b3 $b4 $b5 $b6 $b7 $b8 $b9 $ba $bb $bc $bd $be $bf ; $c0 $c1 $c2 $c3 $c4 $c5 $c6 $c7 $c8 $c9 $ca $cb $cc $cd $ce $cf ; $d0 $d1 $d2 $d3 $d4 $d5 $d6 $d7 $d8 $d9 $da $db $dc $dd $de $df ; $e0 $e1 $e2 $e3 $e4 $e5 $e6 $e7 $e8 $e9 $ea $eb $ec $ed $ee $ef ; $f0 $f1 $f2 $f3 $f4 $f5 $f6 $f7 $f8 $f9 $fa $fb $fc $fd $fe $ff ; $00 $01 $02 $03 $04 $05 $06 $07 $08 $09 $0a $0b $0c $0d $0e $0f ; $10 $11 $12 $13 $14 $15 $16 $17 $18 $19 $1a $1b $1c $1d $1e $1f ; $20 $21 $22 $23 $24 $25 $26 $27 $28 $29 $00 $00 ;码制转换 ;范例60 ;地址 $90 1 2 3 4 5 6 7 8 9 a b c d e f $a0 ;ascii码数据 P i , A S , +/- x x x x . x x k g .EQU DPNT=$90 ;the first ascii character addr. ;DataPoint(.'sit):$a0/weighing unit:$a1,$a2 ;P1-P4'no.:$a6$a7--$ac$ad/max.no.:$ae$af .EQU TPTR=$b0 ;the first total(total1($b0-$b3)) addr. .EQU CPTR=$C0 ;print char. buffer addr. .EQU SREG=$3F .EQU SPH=$3E .EQU SPL=$3D ACUM: CLR R29 CLR R27 ;ASCII码存放区为$90-9F LDI R26,$90 LD R16,X CPI R16,$50 ;P打头方为有效 BRNE ACRT ADIW R26,2 ;指向$92 LD R16,X CPI R16,$2C ;是','? BREQ DOP0 ACRT: SET ;非法数据! RET DOP0: LDI R17,4 LDI R26,$99 ;设指针,寻找小数点 DOP1: LD R16,X+ CPI R16,$2E BREQ DOP3 ;找到'.' DOP2: DEC R17 BRNE DOP1 DOP3: LDI R28,$A0 ;小数点放入$A0,1,2,3,4表示小数点后有1,2,3,4位数据 ST Y+,r17 ;0表示无小数点 LDI R26,$9E ;指向质量单位 LD R17,X+ ST Y+,R17 LD R17,X+ ST Y,R17 ;质量单位(kg/ t)-->$a1,$a2 LDI R28,11 CLR R9 CLR R10 CLR R11 ;予请除,存放BCD码 LDI R26,$9E F1: LD R16,-X ;减1后指向$9d! CPI R16,$2E ;从低位到高位顺序将ASCII转为BCD,两两合成1字节压缩BCD码 BREQ F1 ;遇到小数点跳过 F2: BRCS FEND ;遇空格/+/-等结束 SUBI R16,$30 ;十进制数ascii变bcd MOV R12,R16 ST Y,R16 F3: LD R16,-X CPI R16,$2E BREQ F3 F4: BRCS FEND ;小于$2E转换结束 SUBI R16,$30 SWAP R16 ADD R16,R12 ST Y,R16 DEC R28 CPI R28,8 BRNE F1 FEND: MOV R17,R9 OR R17,R10 OR R17,R11 BREQ ACRT ;0数据转出 RCALL CONV2 ;整数二翻十(r9r10r11-->r13r14r15) MOV R5,R13 MOV R6,R14 MOV R7,R15 CLR R12 LDS R16,$96 ;取数据符号 CPI R16,$2d ;'-'ASCII码 BRNE F09 LDI R26,16 RCALL NEG4 ;负数取补 F09: LDI R26,$91 ;指向数据序号ASCII码 LD R16,X+ SUBI R16,$31 ;将ASCII码序号$31--$34变为0--3, CPI R16,4 BRCC FRET ;大于3为无效 MOV R9,R16 ;暂存 LSL R16 LSL R16 ;乘4 LDI R26,$B0 ;$B0为第一个累加和首地址(TPTR) ADD R26,R16 ;得到实际首地址 LDI R16,4 LDI R28,16 ;数据指针 CLC LACM: LD R17,X ;取累加和一字节数据 LD R10,-Y ADC R17,R10 ST X+,R17 DEC R16 BRNE LACM ;r12,13,14,15 加入累加和 LSL R9 ;序号乘2 LDI R26,$AE ;指向最大累加次数 LDI R28,$A6 ;指向第一个累加次数 ADD R28,R9 ;指向实际累加次数 LD R11,X+ LD R10,X ;取最大累加次数(2字节) LD R13,Y INC R13 ;实际累加次数增1 ST Y+,R13 ;低位字节送回 LD R12,Y TST R13 BRNE F10 INC R12 ;低位字节增1后为0,高位字节增1 ST Y,R12 F10: SUB R11,R13 SBC R10,R12 ;与最大累加次数相比较 BRCC F12 ST X,R12 ST -X,R13 ;存最大累加次数 F12: MOV r15,r7 MOV r14,r6 MOV r13,r5 LDI R17,$98 ;予设阶码(假定为24位整数) MOV R12,R17 F120: SBRC R13,7 RJMP F13 LSL R15 ROL R14 ROL R13 DEC R12 RJMP F120 F13: LDS R0,$A0 ;取小数点位数(0,1,2,3,4) TST R0 BREQ F14 ;整数转 F130: RCALL G01 ;取浮点数0.1(范例70) RCALL FPMU ;(范例65) DEC R0 BRNE F130 ;小数点位置决定乘几个0.1 F14: LDS R16,$96 CPI R16,$2B ;负数? BRNE F9 LDI R16,$7F AND R13,R16 ;正数清除数符位 F9: CLT ;合法数据出口(T=0) RET FRET: SET ;非法数据出口(T=1) RET NEG4: LDI R16,4 ;4字节二进制数据求补 CLC NG4L: CLR R17 LD R11,-X ;X-1指向最低位字节 SBC R17,R11 ST X,R11 DEC R16 BRNE NG4L RET FLSPC: LDI R26,$C0 ;准备一行空格字符,为打印一行空格做准备 CLR R31 LDI R16,$20 ;SPC FSLOP: ST X+,R16 CPI r30,$d0 BRNE FSLOP LDI R16,$0D ;$0D-->($D0) ST X+,R16 LDI R16,$0A ;$0A-->($D1) ST X+,R16 RET BRDT: RCALL CONV1A ;二翻十并将压缩BCD码转换为ASCII码 RCALL FLSPC LDI R28,$A3 ;$A1,$A2 is the weighing unit CLR R29 ;取质量单位到打印数据存储区 LDI R26,$D0 LD R16,-Y ST -X,R16 ;'g'-->($D0-1) LD R16,-Y ST -X,R16 ;取质量单位:($A1)-->$ce &($A2)-->$CF LD R10,-Y ;取小数点位置:($A0)->R10 LP59: LDI R28,15 LP60: LD R16,Y RCALL BTOA ;低位BCD变为ASCII码 LD R16,Y RCALL BTOA0 ;高位BCD变为ASCII码 DEC R28 CPI R28,10 ;r11,12,13,14,15 都分解完毕? BRNE LP60 DL30H: LDI R26,$C5 BRTC DL300 ;数据为负? LDI R16,$2D ST X,R16 ;负数加'-',送入$C5 CLT ;并清除负数标志 DL300: INC R26 LD R16,X CPI R16,$30 BRCS DL300 ;去掉数据头无效的零ASCII码$30 DL301: CPI R16,$30 BRNE DLRT ;非零结束 INC R26 LD R16,X CPI R16,$30 BRCS DLRT ;小于$30结束(质量单位 t) CPI R16,$3A BRCS DL302 ;大于$3A结束(质量单位kg) DLRT: RET DL302: DEC R26 LDI R16,$20 ;无效零充以空格 ST X+,R16 LD R16,X RJMP DL301 BTOA0: SWAP R16 BTOA: ANDI R16,15 SUBI R16,$D0 ;加$30变为ASCII码 ST X,R16 DEC R26 DEC R10 BRNE BART LDI R16,$2E ;加入小数点ASCII码 ST X,R16 DEC R26 BART: RET PRAV: LDI R17,4 ;打印4组平均数据 MOV R0,R17 DEC R0 PRV: MOV R17,R0 LSL R17 LSL R17 ;组别序号之偏移量 LDI R26,$B0 ;TOTAL1首趾 ADD R26,R17 ;得到实际组别之首地址 LDI R28,16 ;将TOTAL取入 R12R13R14&R15 CLR R10 BRV: LD R16,X+ ST -Y,R16 OR R10,R16 NOC: CPI R28,12 BRNE BRV TST R10 BREQ NBR ;TOTAL为零不打印! BRV1: BST R12,7 ;数符位送入T BRTC BRV2 LDI R26,16 RCALL NEG4 ;负数求补 BRV2: MOV R26,R0 LSL R26 SUBI R26,-$A6 ;取本组累加和之累加次数(第一组从$A6开始) LD R11,X+ LD R10,X+ RCALL DIV24 ;计算平均值(在r13r14r15中) CLR R7 MOV R8,R13 MOV R9,R14 MOV R10,R15 ;r11r12r13r14r15<--r7r8r9r10 RCALL BRDT ;二翻十,再将BCD码转为ASCII码,如为负数将'-'装入$c5! LDI R26,$C5 LDI R16,$56 ;'V' ST -X,R16 ;start from $c4! LDI R16,$41 ;'A' ST -X,R16 LDI R16,$2C ;',' ST -X,R16 MOV R16,R0 ;i(=1/2/3/4) SUBI R16,$CF ;加上$31 ST -X,R16 LDI R16,$50 ST -X,R16 ;'P' RCALL PR1 ;打印一行 NBR: DEC R0 BRNE PRV RET PRNO: RCALL FLSPC ;打印最大累加次数(整数),不加小数点 LDI R26,$CD ;存放ASCII码指针 LDI R28,$AE ;指向最大累加次数 LD R10,Y+ LD R9,Y ;最大累加次数取到R9R10 CLR R8 CLR R7 RCALL CONV1A ;二进制数变为BCD码 BRN: CLR R10 ;不加 '.' RCALL LP59 ;转为ASCII码 LDI R26,$C4 LDI R16,$2E ;'.' ST -X,R16 LDI R16,$4F ;'O' ST -X,R16 LDI R16,$4E ST X,R16 ;'N',加上‘NO.’后 RJMP PR1 ;打印 PRTL: LDI R17,4 MOV R0,r17 ;取序号之偏移量 DEC R0 PRL: MOV R17,R0 LSL R17 LSL R17 ;乘4:累加和为4字节 CLR R27 LDI R26,$B0 ;第一个累加和TOTAL1之首地址 ADD R26,R17 ;累加和之实际地址 CLR R15 LDI R28,11 BRL: LD R16,X+ ST -Y,R16 OR R15,R16 CPI R28,7 BRNE BRL ;累加和取到r7r8r9r10 NINC: TST R15 BREQ NBL ;累加和为零,不打印 BST R7,7 BRTC BRTL1 LDI R26,11 RCALL NEG4 ;累加和为负数,取补 BRTL1: RCALL BRDT ;BCD转为ASCII LDI R26,$C5 LDI R16,$4C ;加'L' ST -X,R16 LDI R16,$54 ;加'T' ST -X,R16 LDI R16,$2C ST -X,R16 ;加',' MOV R16,R0 SUBI R16,$CF ;i(=1/2/3/4)/加上$30变为ASCII码 ST -X,R16 LDI R16,$50 ST -X,R16 ;加'P' RCALL PR1 ;打印一行累加和数据(一个TOTAL) NBL: DEC R0 BRNE PRL ;共打印4行 RET PR1: LDI R26,$C0 ;打印区首地址 CLR R27 LDI R16,$28 ;允许UART发送,允许发送寄存器空中断,8位数据 OUT UCR,R16 SEI ;使能总中断 RET CONV1A: LDI R17,32 ;整数二翻十(最大$FFFFFFFF=4294967295) MOV R0,R17 CLR R11 CLR R12 ;r11r12r13r14r15(BCD)<--(r7r8r9r10二进制) CLR R13 CLR R14 CLR R15 ;十进制数存储区清除 CV1A: LSL R10 ROL R9 ROL R8 ROL R7 ;二进制数左移一位 MOV R16,R15 RCALL LSDAA MOV R15,R16 MOV R16,R14 RCALL LSDAA MOV R14,R16 MOV R16,R13 RCALL LSDAA MOV R13,R16 ;十进制数带进位左移并调整 MOV R16,R12 RCALL LSDAA MOV R12,R16 MOV R16,R11 RCALL LSDAA MOV R11,R16 DEC R0 BRNE CV1A RET ;范例61 ;格雷码与二进制数相互转换 GTB8: LDI R16,7 ;8格雷码(在R17中)翻为二进制数 CLR R15 LSL R17 ADC R17,R15 ;将左移移出位加到末位上 GB1: SBRC R17,0 SUBI R17,$80 ;Bi⊕G(i+1)-->B(i+1) GB2: LSL R17 ADC R17,R15 ;将左移移出位加到末位上 DEC R16 BRNE BG1 ;循环7次,结束 RET ;二进制数在r17中 GTB9: LDI R16,8 ;9位格雷码(最高位在进位C,低8位在R17中)翻为二进制数 CLR R15 INC R15 ;1-->r15 ROL R17 ;9位格雷码带进位循环左移一位 GB90: ROL R17 ;the ORIGINAL highest bit->r17,1 AT THE FIRST! SBRC R17,1 EOR R17,R15 ;Bi⊕G(i+1)-->B(i+1) DEC R16 BRNE GB90 RET ;结果仍在进位C和r17中 BTG8: LDI R16,7 ;8位二进制数翻为格雷码 CLR R15 INC R15 ;1-->r15 CLC ROL R17 ;0-->r17,0&B1-->C BRCC BGLOP INC R17 ;B1-->r17.0 BGLOP: ROL R17 BRCC BG4 EOR R17,R15 ;Bi⊕B(i+1)-->G(i+1)/FOR EXAMPLE:(1110-->1001) BG4: DEC R16 BRNE BGLOP RET ;结果在R17 AVR浮点程序库 ;范例62 .ORG $A00 EXCH: MOV R5,R8 ;两浮点数交换子程序 MOV R8,R12 MOV R12,R5 EXCH1: MOV R5,R9 ;尾数交换 MOV R9,R13 MOV R13,R5 MOV R5,R10 ;双字节交换 MOV R10,R14 MOV R14,R5 MOV R5,R11 MOV R11,R15 MOV R15,R5 RET DP: ANDI R16,$7F ;处理积/商数符,计算积/商阶码子程序 SBRC R9,7 SUBI R16,$80 SBRC R13,7 SUBI R16,$80 ;积/商符号放在r16,7 ADD R12,R8 ;移码相加(除数阶码已求补) LDI R17,$80 BRCC DP1 ADD R12,R17 ;移码求和有进位,将和再加上$80,再有进位为溢出 RET DP1: SUB R12,R17 ;移码求和无进位,将和减去$80,有借位 RET ;或差为0也为溢出 NEG3: COM R15 ;3字节数据求补 COM R14 ;先求反后加1 COM R13 INC3: LDI R17,255 SUB R15,R17 ;以减去-1代替加1 SBC R14,R17 SBC R13,R17 RET ;范例63 ;浮点数比较大小子程序 X1为被减数 X2为减数 FPCP: SBRC R9,7 ;X1为正,跳行 RJMP CP1 SBRC R13,7 ;X2为正,跳行 RJMP CP2 ;X1,X2异号 FPCP1: CP R11,R15 ;X1,X2皆为正,以尾数低位字节,中位字节,高位字节和 CPC R10,R14 ;阶码的顺序(按无符号数)进行比较 CPC R9,R13 ;不等,阶码大者浮点数值也大;只有阶码和尾数对应相等, CPC R8,R12 ;两浮点数才相等 RET ;比较结果:Z=1时X1=X2,否则C=0时X1>X2,C=1时X1X2,C=1时X124,取被加数为和 NX2A: LSR R13 ROR R14 ROR R15 DEC R17 BRNE NX2A ;加数阶小,右移加数对阶 MOV R12,R8 ;取被加数阶为和之阶 BRCC GOON RCALL INC3 ;舍入移出位 RJMP GOON NX3: CPI R17,24 BRCC COM1 ;阶差>24,取加数为和 LOOP: LSR R9 ROR R10 ROR R11 DEC R17 BRNE LOOP ;加数阶大,右移被加数对阶 BRCC GOON RCALL INC3A ;舍入移出位 GOON: SBRC R16,6 SUBI R16,$80 SBRS R16,7 ;判别两数是否同号 RJMP SAMS ;同号转 SUB R15,R11 ;异号,执行减法,加数为被减数 SBC R14,R10 SBC R13,R9 BRCC NOM ;够减转 SUBI R16,$40 ;否则被减数数符求反为和之数符 RCALL NEG3 ;并将差求补 NOM: MOV R17,R13 OR R17,R14 OR R17,R15 BREQ DON0 ;差为0转 NMLOP: SBRC R13,7 RJMP COM1 LSL R15 ROL R14 ROL R13 DEC R12 BRNE NMLOP ;规格化 OV1: SEV ;阶码变为0,下溢(可取为0,不算溢出) RET SAMS: ADD R15,R11 ADC R14,R10 ADC R13,R9 ;两数同号,执行加法 BRCC COM1 ROR R13 ROR R14 ROR R15 INC R12 ;有进位时右规1次($7F+1=$80溢出) BREQ OV1 ;阶码增1后变为0为上溢 BRNC COM1 RCALL INC3 COM1: CLV SBRC R16,6 RET COMA: LDI R17,$7F AND R13,R17 ;正数数符为0 DON: RET EXAD: RCALL SAV0 ;取被加数为和 SBRS R16,7 RJMP COMA ;配置数符 RET DON0: CLR R12 ;浮点数为0 RET ;范例65 ;浮点乘法子程序 FPMU: TST R8 BREQ M0 ;被乘数为0,积为0 TST R12 BRNE M1 ;乘数为0,积也为0 M0: RJMP G0 M1: RCALL DP ;处理积符号,计算积之阶码 BRCS OV2 BREQ OV2 ;判断溢出 LDI R17,$80 OR R9,R17 OR R13,R17 ;恢复尾数最高位 MOV R5,R13 MOV R6,R14 MOV R7,R15 ;乘数转入R5,R6,R7 LDI R17,25 ;设右移部分积次数 CLR R13 CLR R14 CLR R15 ;r13r14r15清除,存放积 CLC LOOP1: BRCC M2 ADD R15,R11 ADC R14,R10 ADC R13,R9 ;乘数右移移出位为1,被乘数加入部分积1次 M2: ROR R13 ROR R14 ROR R15 ROR R5 ROR R6 ROR R7 ;部分积连同乘数右移1位 DEC R17 BRNE LOOP1 ;尾数相乘计算完成? SBRC R13,7 RJMP M3 ;乘积最高位为1 转 ROL R5 ROL R15 ROL R14 ROL R13 ;乘积最高位为0,高4位字节左移1位 SBRS R5,7 RJMP M5 RCALL INC3 ;末位字节舍入 BRNE M5 SEC ;舍入后R13变为0 ROR R13 ;将其改为$80(即0.5) RJMP COM2 M5: DEC R12 ;舍入后R13不为0 BRNE COM2 ;阶码减1 OV2: SEV ;变为0为溢出 RET M3: SBRC R5,7 RCALL INC3 ;乘积低3位字节舍入 COM2: LDI R17,$7F SBRS R16,7 AND R13,R17 ;正数将符号位请除 DON2: CLV RET ;范例66 FPDI: TST R12 ;浮点除法子程序 BREQ OV3 ;除数为0,溢出 TST R8 BRNE D1 RJMP G0 ;被除数为0,商为0 D1: NEG R12 ;除数阶码求补,以加补码代替减原码 RCALL DP ;处理商符号,计算商之阶码 BRCS OV3 BREQ OV3 ;判断溢出 LDI R17,$80 OR R9,R17 OR R13,R17 ;恢复尾数最高位 FPD3: LDI R17,25 ;左移相减试商25次,最后1次舍入 SUB R11,R15 SBC R10,R14 SBC R9,R13 BRCS D2 ;第一次尾数相减试商 INC R12 ;够减,商阶增1 SEC BRNE D3 ;商阶增1后不为0,转计商;否则为溢出 OV3: SEV RET D2: ADD R11,R15 ADC R10,R14 ADC R9,R13 ;不够减则恢复被除数 LOOP2: LSL R11 ROL R10 ROL R9 ;被除数算术左移 BRCS D4 ;进位位为1,够减,本位商1 SUB R11,R15 SBC R10,R14 SBC R9,R13 ;否则相减试商 BRCS D2A SEC RJMP D3 ;够减,本位商1 D2A: ADD R11,R15 ;不够减,恢复被除数 ADC R10,R14 ADC R9,R13 CLC ;本位商0 RJMP D3 D4: SUB R11,R15 SBC R10,R14 SBC R9,R13 ;被除数减去除数 D3: DEC R17 BRNE D5 ;除法未完成,循环(1-1=0,不溢出) MOV R13,R5 MOV R14,R6 MOV R15,R7 ;取回商 BRCC COM3 RCALL INC3 ;第25位商舍入($800000-$FFFFFF不溢出,故INC3不会溢出!) COM3: LDI R17,$7F SBRS R16,7 AND R13,R17 ;配置商数符 DON3: RET D5: ROL R7 ;在R5,R6,R7中记商(不必预先清除) ROL R6 ROL R5 ;商数左移1位并记商 RJMP LOOP2 ;范例67 FPSQ: ANDI R16,$7F ;模拟手算开平方子程序 SBRC R13,7 ORI R16,$80 ;负数 建虚根标志 FPS0: TST R12 BREQ DON4 ;0的平方根为0 LDI R17,$80 OR R13,R17 ;恢复尾数最高位 LSR R12 ;阶码算术右移1位 BRCC FSQ2 INC R12 ;移出位舍入 RCALL INC3 ;先将位数增1(提前舍入) BRCS FSQ1 ;C=1,不够减 SEC ROR R13 ;若尾数变为0将其改为0.5($80-->r13) RJMP FSQ2 FSQ1: LSR R13 ROR R14 ROR R15 ;否则将为数算术右移 FSQ2: LDI R17,25 ;开出25位根,末位舍入 MOV R8,R17 LDI R17,$40 ADD R12,R17 ;根恢复为移码 CLR R5 CLR R6 CLR R7 ;根扩展区清除 CLR R9 CLR R10 CLR R11 ;根存储区清除 FSQ3: SUB R13,R17 SBC R7,R11 SBC R6,R10 SBC R5,R9 ;试根 BRCS FSQ3A SEC RJMP FSQ4 ;够减,本位根1 FSQ3A: ADD R13,R17 ADC R7,R11 ADC R6,R10 ADC R5,R9 ;否则恢复开平方数之尾数 CLC ;本位商0 FSQ4: DEC R8 BRNE FSQ5 ;开出第25位根? FQDON: MOV R13,R9 MOV R14,R10 MOV R15,R11 ;回送根尾数 BRCC COM4 RCALL INC3 ;第25位根舍入 COM4: LDI R17,$7F AND R13,R17 ;根尾数为正数 DON4: RET FSQ5: ROL R11 ROL R10 ROL R9 ;根尾数带进位左移,记根 LSL R15 ROL R14 ROL R13 ROL R7 ROL R6 ROL R5 LSL R15 ROL R14 ROL R13 ROL R7 ROL R6 ROL R5 ;开平方数之尾数连同扩展区左移2位 BRCC FSQ3 ;未产生进位,循环 RJMP FQDON ;否则进位为第25位根(不须试,并结束子程序)! ;范例68 ;牛顿迭代开平方子程序 FSQR: TST R12 BREQ SQRT ;0的平方根为0 ANDI R16,$7E SBRC R13,7 ORI R16,$80 ;虚根标志 SBRC R12,0 INC R16 ;阶码为奇数 LDI R17,$7F AND R13,R17 ;尾数变为正数 LSR R12 LDI R17,$40 ADC R12,R17 ;得到根之移码 PUSH R12 ;暂存 LDI R17,$80 MOV R12,R17 SBRC R16,0 INC R12 ;得到X1的阶码(0.5≤X1<2) RCALL LD1 ;存 X1 LSR R13 ROR R14 ROR R15 LDI R17,$40 SBRS R16,0 ;阶码为奇数时算术右移尾数即得到X1之尾数;否则将其最 ;高位字节加上$40 OR R13,R17 ;得到首次根r0=(1+x1)/2 LDI R17,3 MOV R0,R17 ;迭代3次 FSQLP: RCALL LD2 RCALL GET1 RCALL FPDI RCALL GET2 RCALL FPAD DEC R12 ;计算r(i+1)=(x1/ri+ri)/2 DEC R0 BRNE FSQLP ;r3的尾数为根之尾数 POP R12 ;取回根之阶码 SQRT: RET ;r16,7=1 为虚数根 ;范例69 ;基本运算程序的演示程序 DMST1: .EQU SPL=$3D .EQU SPH=$3E LDI R16,2 ;high(ramend) OUT SPH,R16 LDI R16,$5F ;low(ramend) OUT SPL,R16 LDS R11,$60 ;r11,7:数符 r11,6 :阶符 r11,5--0:阶(最大为38) LDS R12,$61 ;r12-r15:尾数 LDS R13,$62 LDS R14,$63 LDS R15,$64 ;尾数共8位BCD码 RCALL DTOB ;转为二进制浮点数 RCALL LD2 ;暂存 LDS R11,$65 ;r11,7:数符 r11,阶符 r11,5--0:阶(最大为38) LDS R12,$66 ;r12-r15:尾数 LDS R13,$67 LDS R14,$68 LDS R15,$69 RCALL DTOB ;转为二进制浮点数 RCALL GET2 ;取第一操作数 RCALL FPAD ;调基本运算子程序之一(FPSU/FPMU/FPDI) RCALL BTOD ;转回十进制浮点数 DMRET: RJMP DMRET ;范例70 ;辅助子程序 KP2: MOV R8,R12 ;复制第二操作数 MOV R9,R13 MOV R10,R14 MOV R11,R15 RET LD1: STS $70,R12 ;存浮点数 STS $71,R13 STS $72,R14 SYS $73,R15 RET LD2: STS $74,R12 ;存浮点数 STS $75,R13 STS $76,R14 STS $77,R15 RET LD3: STS $78,R12 ;存浮点数 STS $79,R13 STS $7A,R14 STS $7B,R15 RET GET1: LDS R8,$70 ;取浮点数 LDS R9,$71 LDS R10,$72 LDS R11,$73 RET GET2: LDS R8,$74 ;取浮点数 LDS R9,$75 LDS R10,$76 LDS R11,$77 RET GET3: LDS R8,$78 ;取浮点数 LDS R9,$79 LDS R10,$7A LDS R11,$7B RET INVPI: LDI R17,$86 ;取浮点数180/л MOV R8,R17 LDI R17,$65 MOV R9,R17 LDI R17,$2E MOV R10,R17 LDI R17,$E1 MOV R11,R17 RET G90: LDI R17,$87 ;取浮点数90 MOV R8,R17 LDI R17,$34 MOV R9,R17 CLR R10 CLR R11 RET DTOR: RCALL PI18 ;角度化为弧度 RJMP FPMU RTOD: RCALL INVPI ;弧度化为角度 RJMP FPMU GHPI: LDI R17,$81 ;取浮点数л/2 MOV R8,R17 LDI R17,$49 MOV R9,R17 LDI R17,$0f MOV R10,R17 LDI R17,$DB MOV R11,R17 RET G01: LDI R17,$7D ;取浮点数0.1 MOV R8,R17 LDI R17,$4C MOV R9,R17 LDI R17,$CC MOV R10,R17 LDI R17,$CD MOV R11,R17 RET G1: LDI R17,$81 ;取浮点数1 MOV R8,R17 CLR R9 CLR R10 CLR R11 RET PI18: LDI R17,$7B ;取浮点数л/180 MOV R8,R17 LDI R17,$0E MOV R9,R17 LDI R17,$FA MOV R10,R17 LDI R17,$35 MOV R11,R17 RET GINT: LDI R17,R12 ;浮点数取整 CPI R17,$81 BRCC GINT1 RCALL G0 ;阶码<$81,结果为0 RJMP KP2 GINT1: ANDI R16,$DD SBRC R13,7 ORI R16,2 ;记数符 CPI R17,$98 BRCC GOVER ;阶码>$97,溢出 RCALL BRK ;分解出整数部分(在R9 R10 R11) SBRS R16,1 RET ;正数返回 NEG3A: COM R11 ;负数求(r9 r10 r11)之补 COM R10 COM R9 INC3A: LDI R17,255 SUBI R11,R17 SBCI R10,R17 SBCI R9,R17 ;求反后加1 RET GOVER: ORI R16,$20 ;设整数部分超过23位标志 RET BRK: ANDI R16,$DF ;将正浮点数分解为整数/小数两部分 LDI R17,$80 OR R13,R17 ;恢复尾数最高位 CLR R9 CLR R10 CLR R11 MOV R17,R12 SUBI R17,$80 BREQ BRKRT BRCS LOOPT CPI R17,$19 ;整数部分超过24位 BRCC GOVER ;为溢出 LOOP4: LSL R15 ROL R14 ROL R13 ROL R11 ROL R10 ROL R9 DEC R17 BRNE LOOPT ;左移位数为阶码-$80,整数部分进入r9-r11中 BRKRT: RET LOOPT: LSR R13 ;只有小数部分右移尾数($80-阶码)位 ROR R14 ROR R15 INC R17 BRNE LOOPT RET NRML: ANDI R16,$BF ;1字节正整数(在R13中)规格化为浮点数 CLR R14 CLR R15 LDI R12,$88 ;设阶码 RJMP NMLOP G10: LDI R17,$84 ;取浮点数10 MOV R8,R17 LDI R17,$20 MOV R9,R17 CLR R10 CLR R11 RET GLN2: LDI R17,$80 ;取浮点数ln2(=0.6931471806) MOV R8,R17 LDI R17,$31 MOV R9,R17 LDI R17,$72 MOV R10,R17 LDI R17,$18 MOV R11,R17 RET GLN10: LDI R17,$82 ;取浮点数ln10(=2.302585093) MOV R8,R17 LDI R17,$13 MOV R9,R17 LDI R17,$5D MOV R10,R17 LDI R17,$8E MOV R11,R17 RET INVX: TST R12 ;计算1/X, X=0时溢出 BRNE INV OV4: SEV RET INV: RCALL G1 ;取1 RJMP FPDI ;范例71 ;用荷纳法计算多项式值子程序 FPLN1: ORI R16,$10 ;设计算奇函数(lnx,sinx,arcsinx 等)标志 RCALL LD3 ;存X RCALL KP2 RCALL FPMU ;计算X2 RJMP FLN0 ; FPLN2: ANDI R16,$EF ;设计算偶函数(EXP,COSX等)标志 FLN0: RCALL LD1 ;存T,T=X 或T=X2 POP R30 POP R31 ;系数表数据地址进入Z LSL R30 ROL R31 ;由按字取数变为按字节取数 LPM ;r0<--(z)取阶码 MOV R8,R0 ADIW R30,1 ;指针增1 LPM ;取尾数高位字节 MOV R9,R0 ADIW R30,1 ;z+1 LPM ;取尾数中位字节 MOV R10,R0 ADIW R30,1 ;z+1 LPM ;取尾数低位字节 MOV R11,R0 ;取浮点数到r8 r9 r10&r11 ADIW R30,1 ;z+1 PLN: RCALL M1 ;计算(....((An*T+A(n-1))*T+A(n-2))*T+....+Ai)*T LPM MOV R8,R0 ADIW R30,1 LPM MOV R9,R0 ADIW R30,1 LPM MOV R10,R0 ADIW R30,1 LPM MOV R11,R0 ;取A(i-1) ADIW R30,1 RCALL FPLAD ;计算(....((An*T+A(n-1))*T+A(n-2))*T+....+Ai)*T+A(i-1) LPM RCALL GET1 DEC R0 BRNE PLN ;1为停止符号;否则继续计算 PEND: SBRS R16,4 RJMP REND RCALL GET3 ;奇函数 取出自变量 RCALL M1 ;自变量乘以计算结果才是函数值 REND: LSR R31 ROR R30 ;Z指针折半后 ADIW R30,1 ;增1为后继指令地址 IJMP ;转到该地址去执行 ;范例72 LNX: TST R12 ;对数函数子程序 BREQ OV5 SBRS R13,7 RJMP LN1 OV5: SEV ;求负数或0的对数为错误 RET LN1: ANDI R16,$7E ;R16,7:(T-1)/(T+1)或(2T-1)/(2T+1)之符号 R16,0:p之符号 ; m MOV R0,R12 ;设X=2 *T, 则LnX=m*Ln2+LnT,存入p=m LDI R17,$F3 CP R15,R17 LDI R17,$04 CPC R14,R17 LDI R17,$35 CPC R13,R17 ; _ BRCC LN5 ;T>√2/2时跳转 DEC R0 ;取p=m-1 LnX=(m-1)*Ln2+LN(2T) MOV R17,R15 OR R17,R14 OR R17,R13 MOV R12,R17 BREQ LN5A ;2T-1=0 只须计算(m-1)Ln2 RCALL KP2 ;R12 NOUSED! LSL R9 ROL R10 ROL R11 ;(2T-1) LSR R13 ROR R14 ROR R15 LDI R17,$80 OR R13,R17 ;2T+1 LDI R17,$7E MOV R12,R17 ;取1/(2T+1)的阶码 RJMP LNTLP LN5: ORI R16,$80 ;(T-1)为负,数符位改为1 RCALL KP2 RCALL NEG3A LDI R17,$80 ADD R9,R17 ;计算(T-1) LSR R13 ROR R14 ROR R15 LDI R17,$C0 OR R13,R17 LDI R17,$7F MOV R12,R17 ;取1/(T+1)的阶码 LNTLP: LSL R11 ROL R10 ROL R9 ;(2T-1)或(T-1)规格化 DEC R12 ;调整(2T-1)/(2T+1))或(T-1)/(T+1)的阶码 SBRS R9,7 RJMP LNTLP RCALL FPD3 ;计算(2T-1)/(2T+1)或(T-1)/(T+1) 位r16,7为商之数符 PUSH R0 RCALL FPLN1 ;计算LnT或Ln(2T) .DB $7E,$12,$49,$25 ;0.14285714 ;er.total<0.000000029! .DB $7E,$4C,$CC,$CD ;0.2 .DB $7F,$2A,$AA,$AB ;0.33333333 .DB $81,$00,$00,$00 ;1 .DB $01,$00 ;结束符 INC R12 POP R0 LN5A: LDI R17,$80 ADD R0,R17 BREQ LN53 ;p=$80结束 BRCS LN51 NEG R0 INC R16 ;p为负数 LN51: RCALL LD1 ;存LnT或Ln(2T) MOV R13,R0 RCALL NRML ;|P|规格化 RCALL GLN2 ;取ln2 RCALL FPMU ;计算|p|*ln2 RCALL GET1 ;取LnT或Ln(2T) SBRS R16,0 RJMP LN52 RCALL FPSU ;p<0 计算lnT-|p|*ln2或Ln(2T)-|p|*ln2 RET LN52: RCALL FPAD ;p>0 计算lnT+|p|*ln2或Ln(2T)+|p|*ln2 LN53: RET ;范例73 ;对数衍生函数子程序 LGX: RCALL LNX ;计算lnx RCALL GLN10 ;取ln10 RCALL EXCH RJMP FPDI ;转计算lgx=lnx/ln10 LGAX: RCALL LD2 ;存a RCALL EXCH RCALL LNX ;计算lnx RCALL GET2 ;取a RCALL LD2 ;存lnx RCALL EXCH RCALL LNX ;计算lna RCALL GET2 ;转计算logax=lnx/lna RJMP FPDI ;范例74 EXP: MOV R17,R12 ;指数函数子程序 CPI R17,$68 ;X之阶<$68 E1: BRCC E2 RCALL G0 ROR R12 ;(R12)=$80 INC R12 ;取exp=1 RET E2: ANDI R16,$3F ;r16,6:数符 SBRC R13,7 ORI R16,$40 ;负数 LDI R17,$7F AND R13,R17 ;取正(取|X|) LDI R17,$33 CP R15,R17 LDI R17,$0F CPC R14,R17 LDI R17,$30 CPC R13,R17 LDI R17,$87 CPC R12,R17 ;|X|与88.02969 比较 BRCS E3 ;|X|<88.02969 转 SBRS R16,6 RJMP OV6 G0: CLR R12 ;若x<-88.02969 CLR R13 CLR R14 ;Exp=0 CLR R15 RET OV6: SEV ;x>88.02969,Exp溢出 RET E3: CLR R0 ;X整数部分予清除 LDI R17,$81 MOV R8,R17 LDI R17,$38 MOV R9,R17 LDI R17,$AA MOV R10,R17 LDI R17,$3B MOV R11,R17 ;取log2e(=1/ln2) RCALL FPMU ;计算X/ln2 LDI R17,$80 SBRC R16,6 OR R13,R17 MOV R17,R12 CPI R17,$81 BRCS E6 ;X/ln2整数部分为0 转 RCALL BRK ;否则分解该数为整数I(在R11),小数F两部分 LDI R17,$80 MOV R12,R17 RCALL NOM ;小数部分规格化为浮点数 SBRC R16,6 NEG R11 ;整数部分求补 MOV R0,R11 ; E6: PUSH R0 RCALL FPLN2 ;计算EXP(F*ln2) .DB $69,$5A,$92,$9F ;0.10178086 E-6 ;er.total<0.000000024 .DB $6D,$31,$60,$11 ;0.13215487 E-5 .DB $70,$7F,$E5,$FE ;0.15252734 E-4 .DB $74,$21,$84,$89 ;0.15403530 E-3 .DB $77,$2E,$C3,$FF ;0.13333558 E-2 .DB $7A,$1D,$95,$5B ;0.96181291 E-2 .DB $7C,$63,$58,$47 ;0.55504109 E-1 .DB $7E,$75,$FD,$F0 ;0.24022651 .DB $80,$31,$72,$18 ;0.69314718 .DB $81,$00,$00,$00 ;1 .DB $01,$00 ;结束符 POP R0 ADD R12,R0 ;整数部分I 加入阶码中 RET ;范例75 ;指数衍生函数子程序 DXP: RCALL GLN10 ;取ln10 RJMP EXP0 ;转计算EXP(X*ln10) AXP: RCALL LD2 ;存X RCALL EXCH RCALL LNX ;计算lna RCALL GET2 ;取出x EXP0: RCALL FPMU RJMP EXP ;转计算EXP(X*lna) ;范例76 ;双曲函数和反双曲函数子程序 SHX: RCALL SUB11 ;计算双曲正弦 RCALL FPSU BRNE NX48 RET CHX: RCALL SUB11 ;计算双曲余弦 RCALL FPAD NX48: DEC R12 RET SUB11: RCALL EXP RCALL LD2 RCALL INVX RJMP GET2 ASHX: RCALL SUB2 ;计算反双曲正弦 RCALL FPAD ASH: RCALL FPSQ RCALL GET2 RCALL FPAD RJMP LNX ACHX: RCALL SUB2 ;计算反双曲余弦 RCALL EXCH RCALL FPSU RJMP ASH SUB2: RCALL LD2 ;存X RCALL KP2 RCALL FPMU ;得到X2 RJMP G1 ;取浮点数1 ; 范例77 ;正弦函数子程序 SINX: RCALL RTOD ;弧度化为角度 SINX1: CLR R16 ;X1为角度 SBRC R13,7 INC R16 ;存数符 LDI R17,$7F ;X1-->|X1| AND R13,R17 NX30: RCALL G90 INC R8 INC R8 ;取360° RCALL FPCP1 ;|X1|与360°比较 BREQ GE0 ;相等,转出 BRCC NX31 ;|X1|<360° 转出 RCALL EXCH RCALL FPSU ;否则|X1|-360°-->|X1| RJMP NX30 ;循环 NX31: DEC R8 RCALL FPCP1 ;|X1|与180°比较 BREQ GE0 ;相等,转出 BRCC NX32 ;|X1|<180°,转 RCALL EXCH RCALL FPSU ;否则|X1|-180°-->|X1| INC R16 ;将数符求反 NX32: RCALL G90 RCALL FPCP1 ;|X1|与90°比较 BRCC NX36 INC R8 RCALL FPSU ;|X1|>90°,取180°-|x1|-->|x1| RJMP NX36 GE0: RJMP G0 ;|X1|=0 则sinX=0 NX36: RCALL DTOR ;变回弧度X MOV R17,R12 CPI R17,$79 ;阶码<$79,sinX=X BRCS PP2 RCALL FPLN1 ;计算sin|X| .DB $60,$30,$92,$32 ; 0.16059044 E-9 er.total<0.0000000071 .DB $67,$D7,$32,$2A ;-0.25052108 E-7 .DB $6E,$38,$EF,$1C ; 0.27557319 E-5 .DB $74,$D0,$0D,$01 ;-0.19841270 E-3 .DB $7A,$08,$88,$88 ; 0.83333333 E-2 .DB $7E,$AA,$AA,$AA ;-0.16666667 .DB $81,$00,$00,$00 ;1 .DB $01,$00 ;结束符 PP2: LDI R17,$80 SBRC R16,0 PP3: OR R13,R17 ;配置数符 DON6: RET ;范例78 ;衍生三角函数子程序 CTNX: RCALL RTOD ;弧度化为角度 CTNX1: RCALL TANX1 ;计算tgX RJMP INVX ;取倒数为ctgX TANX: RCALL RTOD ;弧度化为角度 TANX1: RCALL LD2 ;存X RCALL SINX1 ;计算sinX RCALL GET2 ;取X RCALL LD2 ;存sinX RCALL EXCH RCALL COSX1 ;计算cosX BRNE NX39 OV7: SEV RET ;cosX=0,溢出 NX39: RCALL GET2 ;取sinX RJMP FPDI ;tgX=sinX/cosX COSX: RCALL RTOD ;弧度化为角度 COSX1: RCALL G90 ;取浮点数90° RCALL FPSU RJMP SINX1 ;cosX=sin(90-X) ;范例79 ;反正弦函数子程序 ASINX: MOV R17,R12 CPI R17,$78 BRCS DON6 ;X阶码<$78,acrsinX=X ANDI R16,8 ;清除数符和|X|>0.5标志,保留计算acosx标志(R16,3) SBRC R13,7 INC R16 ;记数符 LDI R17,$7F AND R13,R17 ;取绝对值 X-->|X| RCALL G1 RCALL FPCP1 BREQ AA BRCC AA1 OV8: SEV ;|X>1,溢出 RET AA: RCALL GHPI RCALL EXCH RJMP PP2 ;|X|=1,arcsinX=±л/2 AA1: MOV R17,R12 CPI R17,$80 BRNE AS1 ;|X|<0.5,y=|x| MOV R17,R13 OR R17,R14 OR R17,R15 BREQ AS1 ;X=0.5,y=|x| ORI R16,$20 ;X>0.5,建标 RCALL NEG3 LDI R17,$80 ADD R13,R17 LDI R17,$7F MOV R12,R17 ;((1-|x|)/2)方根之阶最大为$7F NRMLP: LSL R15 ROL R14 ROL R13 DEC R12 SBRS R13,7 RJMP NRMLP ; __________ RCALL FPS0 ;√(1-|X|)/2-->y AS1: RCALL FPLN1 ;计算arcsiny .DB $7A,$3D,$43,$C4 ;0.11551801 E-1 er. total<0.0000000245 .DB $7A,$64,$CC,$CD ;0.13964844 E-1 .DB $7B,$0E,$27,$62 ;0.17352764 E-1 .DB $7B,$37,$45,$D1 ;0.22372159 E-1 .DB $7B,$78,$E3,$8E ;0.30381944 E-1 .DB $7C,$36,$DB,$6E ;0.44642857 E-1 .DB $7D,$19,$99,$9A ;0.075 .DB $7E,$2A,$AA,$AA ;0.16666667 .DB $81,$00,$00,$00 ;1 .DB $01,$00 ;结束符 SBRS R16,5 RJMP PP2 ;|x|≤0.5 转配置数符,有acsin|x|=acsiny INC R12 ;否则取2arcsiny(=arccosx) SBRC R16,3 ;测试计算ARCCOSX的标志 RJMP ACSRT ;有计算ARCCOSX标志,转清除该标志(其余计算在ACOSX子程序中完成) RCALL GHPI ;否则取л/2 AS2: RCALL FPSU ;|X|>0.5时,arcsin|X|=л/2 -2arcsiny PP20: RJMP PP2 ;转去配置数符 ;范例80 ;函数值为弧度的反三角函数子程序 ACOSX: ORI R16,8 ;设计算arccosx标志 RCALL ASINX ;调反正弦函数子程序 RCALL GHPI ;取л/2 SBRC R16,3 ;计算ARCCOS|X|标志未被清除? RJMP AS3 ;是,转计算arccosx=л/2-arcsinx SBRS R16,0 ;x>0且x>0.5 RJMP ACSRT ;有arccosx=2arcsiny! INC R8 ;否则取л;即当x<0且|X|>0.5时,有arccosX=л-2arcsiny AS3: RCALL FPSU ACSRT: ANDI R16,$F7 ;清除计算arccosx标志 RET ATANX: MOV R17,R12 ;反正切函数子程序 CPI R17,$98 BRCS AT1 RCALL GHPI ;X阶码大于$98,取л/2 RCALL EXCH ROL R9 BRCC AT2 LDI R17,$80 OR R13,R17 ;arctgx=л/2 AT2: RET AT1: MOV R17,R12 CPI R17,$74 ;X阶码小于$74,arctgX=X BRCS AT2 RCALL KP2 RCALL LD1 ;存X RCALL FPMU RCALL G1 RCALL FPAD ; _______ RCALL FPSQ ;计算 √(1+X2) RCALL GET1 RCALL FPDI RJMP ASINX ;转计算arctgx=arcsin(X/√<(1+X2) ACTNX: RCALL ATANX ;反余切函数子程序 RCALL GHPI RJMP FPSU ;arcctgX=л/2-arctgx ;范例81 ;函数值为角度的反三角函数子程序 ASNX: RCALL ASINX ;反正弦函数子程序,结果以角度表示 RJMP RTOD ACSX: RCALL ACOSX ;反余弦函数子程序,结果以角度表示 RJMP RTOD ATNX: RCALL ATANX ;反正切函数子程序,结果以角度表示 RJMP RTOD ACNX: RCALL ACTNX ;反余切函数子程序,结果以角度表示 RJMP RTOD ;范例82 ;函数计算子程序演示程序 DMST2: LDI R16,2 OUT SPH,R16 LDI R16,$5F ;堆栈指针初始化 OUT SPL,R16 LDS R11,$65 ;取操作数(自变量X) LDS R12,$66 ;r11,7:数符 r11,6:阶符 LDS R13,$67 ;r11,5--0:阶(最大为38) LDS R14,$68 LDS R15,$69 ;r12-r15:十进制尾数(8位BCD码) RCALL DTOB ;翻为二进制浮点数 RCALL LNX ;调函数子程序之一 RCALL BTOD ;将函数值转为十进制浮点数 DMHER: RJMP DMHER ;范例83 ;阶乘子程序 NP: RCALL G1 ;取浮点数1 MOV R17,R12 ;二进制整数N在R12中 CPI R17,2 ;N<2,N!=1 BRCS GG CPI R17,34 BRCS NX59 OV9: SEV ;N>33,溢出 RET GG: RJMP SAV0 ;取N!=1 NX59: MOV R0,R12 ;存N DEC R0 ;N-1 LDI R17,1 PUSH R17 ;取T=1 并存入 LDI R17,$81 STS $70,R17 CLR R17 STS $71,R17 STS $72,R17 STS $73,R17 ;存储浮点数1 L43: POP R13 ;取T INC R13 ;T+1-->T PUSH R13 ;存T RCALL NRML ;T规格化 RCALL GET1 ;取阶段阶乘结果 RCALL FPMU ;得到当前T! RCALL LD1 DEC R0 BRNE L43 ;T=N时得到N! POP R0 RET ;范例84 ;长整数(r9,r10,r11,r12)规格化为浮点数 LINOM: BST R9,7 ;数符存于T BRTC LI10 CLR R16 ;负数求补 SUB R16,R12 MOV R12,R16 CLR R16 SBC R16,R11 MOV R11,R16 CLR R16 SBC R16,R10 MOV R10,R16 CLR R16 SUB R16,R9 MOV R9,R16 LI10: LDI R16,$A0 ;取阶32(长整数共32位) LP10: SBRC R9,7 RJMP NX63 ;最高位为1,已规格化 LSL R12 ROL R11 ROL R10 ROL R9 ;否则左规1位 DEC R16 ;阶码减1 CPI R16,$80 BRNE LP10 RJMP G0 ;左规达32次,浮点数为0 ;范例85 ;定点十进制数翻为二进制浮点数 DTOB1: RCALL LD1 ;存入十进制小数 RCALL CONV2 ;定点整数十翻二 RCALL GET1 ;取出十进制小数 RCALL LD1 RCALL SAV0 RCALL CONV4 ;定点小数十翻二 RCALL GET1 ;取出二进制定点整数 LDI R16,$98 ;予设阶码 LP11: SBRC R9,7 RJMP NX63 ;最高位为1,已规格化 LSL R15 ROL R14 ROL R13 ROL R12 ROL R11 ROL R10 ROL R9 ;整数和小数部分左移一位 DEC R16 ;阶码减1 CPI R16,$60 BRNE LP11 RET ;得到浮点数0 NX63: MOV R13,R9 MOV R14,R10 MOV R15,R11 ;尾数取到r13-r15 SBRS R12,7 RJMP PP6 RCALL INC3 ;尾数截去部分舍入 BRNE PP6 INC R16 ;尾数变为0将阶码增1 SEC ROR R13 ;$80-->r13,即将尾数变为0.5 PP6: MOV R12,R16 ;取回阶码 BLD R13,7 ;装入数符(T-->R13,7) RET ;范例86 ;浮点数十翻二 DTOB: ANDI R16,$FC ;r11,7:数符 r11,6:阶符 r11,5--0:阶(最大为38) SBRC R11,6 ;R12---R15;8BCD码尾数 INC R16 ;阶符存于R16,0 SBRC R11,7 ORI R16,2 ;数符存于R16,1 MOV R17,R11 ANDI R17,$3F ;取阶 MOV R0,R17 ;存于R0 MOV R8,R12 OR R8,R13 OR R8,R14 OR R8,R15 BREQ PP8 ;十进制浮点数尾数为0,取二进制浮点数0 PUSH R16 RCALL CONV4 ;十进制浮点数尾数翻为二进制定点小数 MOV R16,R15 MOV R15,R14 MOV R14,R13 MOV R13,R12 ;二进制定点小数转入r13r14r15r16 LDI R17,$80 ;予设阶码 MOV R12,R17 LP14: SBRC R13,7 RJMP NX67 LSL R16 ROL R15 ROL R14 ROL R13 DEC R12 RJMP LP14 ;二进制定点小数规格化为浮点数 NX67: SBRS R16,7 RJMP NX66 RCALL INC3 ;调整 BRNE NX66 INC R12 SEC ;调整后结果为0将其改为0.5 ROR R13 ;即$80-->r13 NX66: LDI R17,$7F POP R16 SBRS R16,1 AND R13,R17 ;配置数符 SBRS R16,0 RJMP DBL4 ;正阶转 DBL1: LDI R17,$10 SUB R0,R17 BRCS DBL2 RCALL INVDP ; RCALL FPMU ;阶码减10, X*10ˉ1o -->X RJMP DBL1 DBL2: ADD R0,R17 ;不够减则恢复阶 BREQ PP8 DBL3: RCALL G01 ;取0.1 RCALL FPMU DEC R0 ;X*0.1-->X,阶减1 BRNE DBl3 RET DBL4: LDI R17,$10 SUB R0,R17 ;阶减10 BRCS DBL5 RCALL DDP ;X*101o -->X RCALL FPMU RJMP DBL4 DBL5: ADD R0,R17 ;不够减则恢复阶 BREQ PP8 DBL6: RCALL G10 RCALL FPMU DEC R0 ;X*10-->X,阶减1 BRNE BDL6 PP8: RET INVDP: LDI R17,$5F ;取浮点数10ˉ1o MOV R8,R17 LDI R17,$5B MOV R9,R17 LDI R17,$E6 MOV R10,R17 LDI R17,$FF MOV R11,R17 RET ; DDP: LDI R17,$A2 ;取浮点数101o MOV R8,R17 LDI R17,$15 MOV R9,R17 LDI R17,$02 MOV R10,R17 LDI R17,$F9 MOV R11,R17 RET ;范例87 ;浮点数二翻十 BTOD: TST R12 BREQ PP4 ;转取十进制浮点数0 ANDI R16,$FC ;予清十进制浮点数数符及阶符(r16,1&0) CLR R0 ;予清十进制浮点数之阶 SBRC R13,7 ORI R16,2 ;取数符 LDI R17,$7F AND R13,R17 ;取绝对值 BTA: RCALL DDP RCALL FPCP1 ;|X|与101o 比较 BREQ BTB BRCC BTC ;|X|<101o 转 BTB: RCALL INVDP RCALL FPMU ;|X|*10ˉ1o-->|X| LDI R17,$10 ADD R0,R17 ;十进制浮点数阶加10 RJMP BTA BTC: RCALL INVDP ; RCALL FPCP1 ;|X|与10ˉ1o 比较 BREQ BTC1 ; BRCS BT0 ;|X|>10ˉ1o 转 BTE: RCALL DDP ; RCALL FPMU ;|X|*101o -->|X| LDI R17,$10 ADD R0,R17 ;十进制浮点数阶加10 ORI R16,1 ;置负阶 RJMP BTC BTC1: LDI R17,9 ;|X|=10ˉ1o 特别处理 ADD R0,R17 ; -9 ORI R16,1 ;取0.1*10 SJMP BT4 BT0: RCALL G1 RCALL FPCP1 ;|X|与1比较 BREQ BT1 BRCC BT2 ;|X|<1转 BT1: RCALL G01 RCALL FPMU ;|X|*0.1-->|X| INC R0 ;十进制浮点数阶加1 RJMP BT0 BT2: RCALL G01 RCALL FPCP1 ;|X|与0.1比较 BREQ BT4 BRCS BDS ;|X|≤0.1转出 BT3: RCALL G10 RCALL FPMU ;|X|*10--->|X| INC R0 ;十进制浮点数阶加1 ORI R16,1 ;置负阶 RJMP BT2 PP4: RJMP KP2 ;十进制浮点数取为0 BT4: LDI R17,$10 MOV R9,R17 CLR R10 CLR R11 CLR R12 ;十进制浮点数尾数取为0.10000000 BT6: MOV R8,R0 ;取十进制浮点数阶 SBRS R8,3 RJMP BT7 SBRC R8,1 SUBI R8,$FA ;对产生非法BCD调整(加6) BT7: LDI R17,$40 SBRC R16,0 OR R8,R17 ;配置阶符(r8,6) LSL R17 SBRC R16,1 OR R8,R17 ;配置阶浮(r8,7) RET BDS: RCALL BT6 ;BT6将十进制浮点数阶,阶符和数符配置到R8 LDI R17,$80 OR R13,R17 ;恢复尾数最高位 LDI R17,$98 SUB R17,R12 ;右移次数为($98-阶码) RJMP CONV31 ;调CONV31子程序完成尾数二翻十,结果在(r9r10r11r12) ;范例88 ;二进制浮点数快速翻为定点十进制数,整数在r9,r10,r11中,小数在r13,r14,r15中 FBTOD: RCALL BRK ;二进制浮点数分解为整数和小数两部分 SBRC R16,5 RET ;整数部分多于24位,溢出 MOV R0,R13 MOV R5,R14 MOV R8,R15 ;小数部分转入R0R5R8 RCALL CONV1 ;定点整数二翻十,结果在R12,R13,R14,R15 RCALL LD1 ;十进制整数-->RAM MOV R15,R8 MOV R14,R5 MOV R13,R0 ;取回二进制小数 RCALL CONV3 ;定点小数二翻十,结果在r9,r10,r11,r12 RCALL EXCH1 ;十进制定点小数转入r13,r14,r15,r12 RCALL GET1 ;取出十进制定点整数r8,r9,r10,r11)/小数在r13,r14,r15,r12 CLR R16 ;清除无用的标志! RET ;范例89 .ORG $E80 ;最小二乘法拟和直线子程序 .EQU NUMB=10 ;取10点,即十对浮点数,按增地址存放Y1,X1,Y2,X2,..Yn,Xn .EQU TABLA=$9000 ;数据表,第一个浮点数为Y1 STRT: LDI R28,$70 CLR R29 ;POINT TO $0070 LP51: ST Y+,R29 ;累加和或暂存区清除(LD1,LD2,LD3,LD4和LD5子程序工作区) CPI R28,$84 BRNE LP51 LDI R16,NUMB ;取拟合点数 MOV R0,R16 LDI R29,$90 CLR R28 ;参加拟合数据首地址$9000 IN R16,MCUCR,7 SBR R16,$C0 ;片外RAM,选一个读写等待周期 OUT MCUCR,R16 LOOP3: RCALL GETA ;取浮点数Yi 占4字节 即Yi0,Yi1,Yi2,Yi3 RCALL INVX ;计算1/Yi RCALL LD6 ;暂存 RCALL GET1 ;取累加和 n RCALL FPAD ;1/Yi加入累加和(∑1/Yi是 ∑1/Yi 简写形式,下同) RCALL LD1 ; i=1 RCALL GET6 ;取1/Yi PUSH R28 PUSH R29 ;保护堆栈指针 RCALL GETA ;取浮点数Xi(Xi0,Xi1,Xi2,Xi3)占4字节 POP R29 POP R28 ;恢复堆栈指针,仍指向Xi RCALL FPMU ;计算Xi/Yi RCALL LD7 ;暂存 RCALL GET2 RCALL FPAD ;Xi/Yi加入累加和∑(Xi/Yi) RCALL LD2 RCALL GET7 ;取出Xi/Yi RCALL SAV0 ; RCALL FPMU ;计算(Xi/Yi)2 RCALL GET3 RCALL FPAD ;(Xi/Yi)2加入累加和∑(Xi/Yi)2 RCALL LD3 RCALL GET6 ;取1/Yi RCALL SAV0 ; RCALL FPMU ;计算1/Yi2 RCALL LD6 ;暂存 RCALL GET4 RCALL FPAD ;1/Yi2 加入累加和∑1/Yi2 RCALL LD4 RCALL GET6 ;取出1/Yi2 RCALL GETA ;再取Xi RCALL FPMU ;计算Xi/Yi2 RCALL GET5 RCALL FPAD ;Xi/Yi2加入累加和∑Xi/Yi2 RCALL LD5 DEC R0 ;点数减1 BRNE LOOP3 ;未到总点数n,循环 RCALL GET4 RCALL SAV0 RCALL GET3 RCALL FPMU ;计算(∑1/Yi2)*(∑(Xi/Yi)2) 并存入 RCALL LD6 RCALL GET5 ;取出∑Xi/Yi2 RCALL SAV0 RCALL FPMU ;计算(∑Xi/Yi2)2 RCALL GET6 RCALL FPSU ;计算c=(∑1/Yi2)*(∑(Xi/Yi)2-(∑Xi/Yi2)2 RCALL LD6 ;存入 RCALL GET2 RCALL SAV0 RCALL GET4 RCALL FPMU ;计算(∑(Xi/Yi)*(∑1/Yi2)并存入 RCALL LD7 RCALL GET1 RCALL SAV0 RCALL GET5 RCALL FPMU ;计算(∑1/Yi)*(∑(Xi/Yi2) 并存入 RCALL GET7 RCALL FPSU ;计算d=(∑Xi/Yi)*(∑1/Yi2)-(∑1/Yi)*(∑Xi/Yi2)) RCALL GET6 ;取c RCALL EXCH RCALL FPDI ;计算b=d/c并存入 RCALL LD7 RCALL GET5 ;取 ∑Xi/Yi2 RCALL FPMU ;计算(∑Xi/Yi2)*b RCALL GET1 RCALL FPSU ;计算(∑1/Yi)-(∑Xi/Yi2)*b RCALL GET4 ;取 ∑1/Yi2 RCALL EXCH RCALL FPDI ;计算a=(∑1/Yi-(∑Xi/Yi2)*b)/∑1/Yi2 RCALL LD6 ;结果a在$84-$87中,b在$88-$8b中 RER GETA: LD R12,Y+ LD R13,Y+ LD R14,Y+ LD R15,Y+ ;从外部SRAM中取浮点数到R12-R15 RET LD4: STS $7C,R12 ;存浮点数 STS $7D,R13 STS $7E,R14 STS $7F,R15 RET LD5: STS $80,R12 ;计算∑Xi/Yi2的存储单元 STS $81,R13 STS $82,R14 STS $83,R15 RET LD6: STS $84,R12 ;暂存1/Yi,1/Yi2等浮点数 STS $85,R13 STS $86,R14 STS $87,R15 RET LD7: STS $88,R12 ;暂存Xi/Yi等浮点数 STS $89,R13 STS $8A,R14 STS $8B,R15 RET GET4: LDS R8,$7C ;取浮点数 LDS R9,$7D LDS R10,$7E LDS R11,$7F RET ; GET5: LDS R8,$80 ;取∑Xi/Yi2或中间结果 LDS R9,$81 LDS R10,$82 LDS R11,$83 RET GET6: LDS R8,$84 ;取浮点数1/Yi,1/Yi2等 LDS R9,$85 LDS R10,$86 LDS R11,$87 RET GET7: LDS R8,$88 ;取浮点数Xi/Yi等 LDS R9,$89 LDS R10,$8A LDS R11,$8B RET ;范例90 GETAD: LDI R17,0Bxxx01110;PC0&PC4输入/PC1-PC3输出&PC3(CAL) OUT DDRC,R17 ; CBI PORTC,1 GAD1: SBI PORTC,4 SBIB PINC,4 ;查DRDY RJMP GAD1 ;低为数据准备好 GAD2: SBI PORTC,4 SBIC PINC,4 ;PINC:$13/PORTB:$15 RJMP GAD2 ;DRDY低有效 CBI PORTC,2 ;置片选有效 LDI R16,16 ;16位数据 GETL0: CLC ;予清除C SBI PORTC,0 SBIC PINC,0 ;接收一位数据 SEC ROL R14 ;数据高位在前 ROL R13 ;在R13R14里带进位左移 SBI PORTC,1 CBI PORTC,1 ;发出时钟,下降沿读出数据 DEC R16 BRNE GETL0 SBI PORTC,2 ;置片选无效 MOV R4,R14 ; MOV R3,R13 ;保存 GADCOM: CLR R15 ;3字节小数r13r14r15(0)规格化为浮点数 LDI R16,$80 MOV R12,R16 ;阶码为$80 GAD: SBRC R13,7 RJMP GETL2 LSL R14 ROL R13 ;尾数左移,阶码递减 DEC R12 BRNE GAD RET ;如果(r12)=0 得到0浮点数 GETL2: LDI R16,$7F AND R13,R16 ;正数 LDI R16,$82 ;取浮点数2.5(基准源为2.5v) MOV R8,R16 LDI R16,$20 MOV R9,R16 CLR R10 CLR R11 RCALL FPMU ;相乘 RET ;(r12)不为0 ; 以下提供几个补充参考程序,都带有详细说明和指令注释.它们是主从多机通讯程序,采 ;用中断方式写入EEPROM,直接对晶振分频产生0.1秒和秒号的精确定时程序,以及RS-232/ ;RS-485标准转换程序,AVR频率计程序,串行时锺日历芯片DS1302读写,共享时基的PWM输出、 ;输入捕获测周期程序和定时信号获取,以及DS18B20测温等程序.多机通讯主要用8和9位数 ;据模式区分被选分机(9位)和其它分机(8位),达到主机只与被选分机交换数据之目的.以中 ;断方式写EEPROM的优点是可与系统运行同时进行(即在线写入),占用很少机时. ;精确定时用定时/计数器1(或0)直接对MCU主频(不设分频)设定时间常数,分频精度可达到 ;1HZ.RS-232/RS-485标准转换程序中AVR不作中转,使两种器件相关脚位直接连接.以TCNT0 ;定时,以T0引脚接收RS-232数据.以收到RS-232字符起始位下跳沿或结束符($03)为依据, ;控制切换RS-485的收发使能.(系统中的主AVR可兼做对通信标准之监控转换,即只是在完成 ;主要工作任务的同时'附带'进行).具体过程不再细述.串行时锺日历芯片DS1302具体积小, ;可靠性高,与单片机连接方便等优点. ; 以下程序请参看有关章节或程序中的注释。 ;范例91 ;多机通讯主机程序/晶振4MHZ .ORG 0 ;以8/9位数据模式区分被选/未被选分机通讯 .EQU DTPINT=$180 ;UBRR=12 波特率19200(REL.ERR.=0.16%) .EQU DRPINT=$1C0 ;主机对1#,2#,3#,4#分机发送数据块在$180-18F,$190-19F,$1A0-1AF)和$1B0-1BF STRT38: RJMP RST38 ;主机从1#,2#,3#,4#分机接收数据块在$1C0-1CF,$1D0-1DF,$1E0-1EF)和$1F0-1FF .ORG $00B ; RJMP STRT38 .ORG $00C RJMP STRT38 ;主机不设串口中断,只以查询接收 .ORG $011 RST38: LDI R16,12 OUT UBRR,R16 ;设波特率:[BAUD RATE=FCP/16(UBRR+1)] CLR R15 ;初始化分机号 LDI R27,HIGH(DTPINT) LDI R26,LOW(DTPINT);发送数据指针(首指$180) LDI R29,HIGH(DRPINT) LDI R28,LOW(DRPINT);接收数据指针(首指$1C0) NEXTNO: LDI R16,$18 OUT UCR,R16 ;允许UART接收和发送,8位数据模式 INC R15 ;指向1#分机 OUTLP: OUT UDR,R15 ;呼分机号,1:1#/2:2#/03:3#/04:4#... TSLOP: IN R16,USR SBRS R16,7 RJMP TSLOP ;分机返回机号? IN R16,UDR CP R16,R15 ;分机号正确返回? BRNE OUTLP LDI R16,$1C ;改为9位数据模式 TXB8=0 OUT UCR,R16 ; TXLOP: LD R16,X+ OUT UDR,R16 ;向分机发送数据块 TESTL: IN R17,USR SBRS R17,5 RJMP TESTL ;等待发送完成 CPI R16,$0A BRNE TXLOP ; RXTST: IN R17,USR SBRS R17,7 ;RXC=1 收到数据 RJMP RXTST ;等待接收分机返回数据块 IN R16,UDR ST Y+,R16 ;存储接收数据 CPI R16,$0A ;分机数据块发完? BRNE RXTST MOV R16,R15 CPI R16,4 ;与分机轮询通讯完毕? BRNE NEXTNO ;未完转对下一分机通信 HH38: RJMP HH38 ;否则踏步(可改为处理分机返回的数据,之后再进行下一个轮询) .DSEG .ORG $180 DTPINT:.BYTE $40 ;$41 $45 $65 $73 $46 $42 $40 $6F $33 $44 $66 $5C $4D $4B $0D $0A ;$42 $4F $66 $78 $47 $45 $44 $63 $32 $48 $60 $7C $6D $45 $0D $0A ;$43 $56 $55 $53 $4D $4F $40 $2E $31 $42 $67 $4C $47 $4A $0D $0A ;$45 $54 $59 $63 $3D $4B $48 $2F $35 $48 $69 $3C $77 $43 $0D $0A .ORG $1C0 DRPINT:.BYTE $40 ;范例92 .ORG 0 ;多机通讯1#分机程序/晶振4MHZ .EQU DTPIT1=$180 ;(UBRR)=12 波特率为19200(REL.ERR.=0.16%) .EQU DRPNT1=$1C0 STRT39: RJMP RST39 .ORG $00B RJMP UARXC ;8535UART接收完成中断 .ORG $00C RJMP UATXC ;UART发送完成中断 .ORG $011 RST39: CLR R18 ;清除分机被选中(R18,6)和主机数据块接收完毕标志(R18,7) LDI R16,12 OUT UBRR,R16 ;设波特率[BAUD RATE=4000000/16*(12+1)=19200] LDI R16,HIGH(DRPNT1) MOV R8,R16 LDI R16,LOW(DRPNT1) MOV R9,R16 ;r8,r9:接收数据指针(FIRST POINT TO $1C0) LDI R16,$98 ;允许UART中断接收,8位数据模式 OUT UCR,R16 SEI RXDTS: SBRS R18,6 ;主机呼号已收到(若收到,在R17中)? RJMP RXDTS OUT UDR,R17 ;返还该机号 TXDON: IN R16,USR SBRS R16,5 RJMP TXDON ;该机号发送完成? LDI R16,$9C ; 允许UART中断接收,9位数据模式,TXB8=0 OUT UCR,R16 RCVBLK: SBRS R18,7 RJMP RCVBLK ;主机发来数据块已接收完毕? LDI R16,HIGH(DTPIT1) MOV R6,R16 LDI R16,LOW(DTPIT1) MOV R7,R16 ;设发送数据指针r6r7,首指$180 LDI R16,$3C ;允许UART中断发送,9位数据模式,TXB8=0 OUT UCR,R16 TXDN: SBIC UCR,5 RJMP TXDN ;发送完毕? RJMP RST39 ; :UART中断接收程序 UARXC: SBIC USR,4 RETI ;祯错误(主机正与其它分机进行9位数据模式通信),不予接收 IN R14,SREG ;保存当前状态 TST R18 BREQ NUMB ;(R18)=0时收到数据,只可能是机号,转去核实 PUSH R16 ;否则为主机向本分机发来数据块(9位模式,机号已符合) PUSH R26 PUSH R27 IN R17,UDR ;接收数据 MOV XH,R8 MOV XL,R9 ;取接收数据指针 ST X+,R17 ;转入RAM MOV R8,XH MOV R9,XL ;存数据指针 CPI R17,$0A ;是数据块结束符LF? BRNE RSCOM1 SBR R18,$80 ;收到完整数据块标志 RSCOM1: POP R27 POP R26 POP R16 DRETI: OUT SREG,R14 RETI NUMB: IN R17,UDR ;取出数据 CPI R17,1 ;是1#分机?2#分机与$02比较/3#分机与$03比较... BRNE DRETI ;机号不符合,转! SBR R18,$40 ;建机号符合标志 RJMP DRETI ; UART中断发送程序 UATXC: PUSH R16 ;r6 r7:发送数据指针,首指$180 IN R16,SREG PUSH R16 PUSH R26 PUSH R27 MOV XH,R6 MOV XL,R7 ;取出发送指针 LD R16,X+ ;取数据,调指针 MOV R6,XH MOV R7,XL OUT UDR,R16 ;送入发送寄存器 CPI R16,$0A BRNE SDCOM CBI UCR,5 ;发送最后1个字符后,禁止发送寄存器空中断(CLR UDRIE) LDI R16,HIGH(DRPINT) MOV R8,R16 LDI R16,LOW(DRPINT) MOV R9,R16 ;接收数据指针初始化(POINT TO $1C0) SDCOM: POP R27 POP R26 POP R16 OUT SREG,R16 POP R16 RETI .DSEG .ORG $180 DTPIT1:.BYTE $40 .ORG $1C0 DRPNT1:.BYTE $10 ;$41 $45 $65 $73 $46 $42 $40 $6F $33 $44 $66 $5C $4D $4B $0D $0A ;范例93 ;以中断方式写入EEPROM(仅对8535,8515无此功能),克服查询方式占用过多机时的缺点, ;并可在线写入 ;运作过程特点如下: ;(1)主程序初始化时设置EEPROM就绪(ready)中断使能位和中断总使能位 ;(2)在主程序中写入第一个字节,写入完成后引起就绪中断,其他写入在中断服务中完成 ;(3)本程序为一写入特例,写入地址为$100--$1FF,可作适当修改(如设块长计数器等) ;(4)为防止高优先级中断破坏写入过程,中断服务中不允许中断嵌套 ;(5)本例为简化程序只以查询写入地址循环作为背景程序,实用时可改为具体的背景序 ;(6)如能确信当前系统没有EEPROM正在写入,可删除对其进行查询部分. STWEEP: LDI R16,HIGH(ramend) OUT SPH,R16 LDI R16,LOW(ramend) OUT SPL,R16 SBI EECR,3 ;设置EEPROM就绪(ready)中断使能位 SEI ;中断总使能 RJMP SRTW .ORG $00F RJMP EEPRDY ;8535 EEPROM就绪(ready)中断向量 SRTW: LDI YH,1 LDI YL 0 ;EEPROM 写入首地址:$100 LDI XL,$60 ;欲写入数据块首地址:$60 CLR XH WEEP0: SBIC EECR,1 ;当前有EEPROM写入操作,有则等待写入完成 RJMP WEEP0 RCALL WREEP ;写入第一个字节,($60)->$100,写入完成后,EEWE=0时引发EEPROM就绪中断 INC YL ;调整写入地址指针 HHWEEP: TST YL BRNE HHWEEP CPI YH,2 ;写入地址达到$200后,写入完成 BRNE HHWEEP CBI EECR,3 ;禁止EEPROM就绪(ready)中断 WDON: RJMP WDON ;踏步 EEPRDY: IN R6,SREG PUSH R16 RCALL WREEP ;写入一个字节 INC YL BRNE WRETI INC YH ;EEPROM末地址为$1FF WRETI: POP R16 OUT SREG,R6 RETI WREEP: OUT EEARH,YH ; OUT EEARL,YL ;写入地址送入EEAR LD R16,X+ ;取数据,调指针 OUT EEDR,R16 ;数据写入EEPROM数据寄存器 SBI EECR,2 ;设置EEPROM写入总使能位EEMWE SBI EECR,1 ;设置EEPROM写入使能位EEWE RET ;范例94 ;精确定时产生0.1秒信号 ;用定时/计数器1定时,不分頻定出0.1秒信号,由PC5脚输出正脉冲。 ;晶体4.000119MHZ,计400012个数定出0.1秒信号 ;对定时/计数器1重装常数进行加法补偿(扣除自然计数和补偿占用时间). ;加法补偿若产生进位,将中断次数减1 .ORG $000 ;精确定时产生0.1秒信号 STRT24: RJMP RST24 .ORG $006 ;8515 t1 overflow vector RJMP T1_OVFL ;400012=65536*7-58740=7*$10000-$E574/故TCC=$E574 .ORG $00D RST24: LDI R16,HIGH(ramend) OUT SPH,R16 LDI R16,LOW(ramend) OUT SPL,R16 SBI DDRC,5 ;PC5,0.1秒号输出(高有效) CBI PORTC,5 LDI R16,1 ;不分頻 OUT TCCR1B,R16 LDI R16,$E5 OUT TCNT1H,R16 LDI R16,$74 OUT TCNT1L,R16 ;写入时间常数TCC LDI R16,$80 OUT TIMSK,R16 ;允许定时/计数器1溢出中断 LDI R16,7 ;7次中断输出0.1秒号 MOV R6,R16 SEI ;中断总使能 HH1A: RJMP HH1A ; T1_OVFL:PUSH R16 PUSH R17 IN R7,SREG DEC R6 ;中断次数减一 BRNE GOON10 ;0.1秒时间到? LDI R16,7 MOV R6,R16 ;重新装入中断次数 SBI PORTC,5 ;0.1秒号输出前沿 IN R17,TCNT1L ;* IN R16,TCNT1H ;*读入TCNT1自然计数值 LDI R18,$7C ;*TCC=$E574 ADD R17,R18 ;*TCC+8=$E57C LDI R18,$E5 ;*8条单周期补偿指令占用8个时钟周期 ADC R16,R18 ;*修正后TCC=$E574+(TCNT1)+8 OUT TCNT1H,R16 ;* OUT TCNT1L,R17 ;*重新装入补偿修正后的TCC BRCC GOON09 DEC R6 ;加法补偿若产生进位,将中断次数减1 GOON09: ;. ;数据处理略 ;. ;. ;. ;. RCALL ACLK1 ;0.1秒走时软时钟 RJMP GOON11 GOON10: CBI PORTC,5 ;输出信号后沿 GOON11: POP R17 POP R16 OUT SREG,R7 RETI ;范例95 ;用定时/计数器1定时,不分頻定出1秒信号,由PC5脚输出正脉冲 ;晶体4.000133MHZ,计4000133个数定出1秒信号 ;对定时/计数器1重装常数进行加法补偿(扣除自然计数和补偿占用时间). ;加法补偿若产生进位,将中断次数减1 .ORG $000 ;精确定时产生秒号 STRT25: RJMP RST25 .ORG $006 RJMP T1_OVFB ;4000133=62*65536-63099=62*$10000-$F67B/故TCC=$F67B .ORG $00D RST25: LDI R16,HIGH(ramend) OUT SPH,R16 LDI R16,LOW(ramend) OUT SPL,R16 SBI DDRC,5 ;PC5输出秒信号(正脉冲) CBI PORTC,5 LDI R16,1 ;不分頻 OUT TCCR1B,R16 LDI R16,$F6 ; OUT TCNT1H,R16 ;写入TCC高8位 LDI R16,$7B ; OUT TCNT1L,R16 ;写入TCC低8位 LDI R16,$80 ; OUT TIMSK,R16 ;允许T/C1溢出中断 LDI R16,62 ;62次中断定出秒号 MOV R6,R16 SEI ; HH1B: RJMP HH1B ;等待中断 T1_OVFB:PUSH R16 PUSH R17 IN R7,SREG DEC R6 ;到62次中断? BRNE GOON12 LDI R17,62 MOV R6,R17 ;重装中断次数 SBI PORTC,5 ;输出秒信号 IN R17,TCNT1L ;* IN R16,TCNT1H ;*读入T/C1自然计数值 LDI R18,$83 ;*TCC=$F67B ADD R17,R18 ;*TCC+8=$F683 LDI R18,$F6 ;*8条单周期补偿指令占用8个时钟周期 ADC R16,R18 ;* OUT TCNT1H,R16 ;* OUT TCNT1L,R17 ;*重新装入补偿修正后的TCC BRCC GOON19 DEC R6 ;加法补偿若产生进位,将中断次数减1 GOON19: ;. ;数据处理略 ;. ;. ;. ;. RJMP GOON13 GOON12: CBI PORTC,5 ;秒号后沿 GOON13: POP R17 POP R16 OUT SREG,R7 RETI ;范例96 ;AVR频率计程序 ;运作特点如下: ;此程序为一完整频率测量显示程序,所测频率较高(2MHZ),使用4兆晶振 ;程序兼有启动看门狗及对其管理功能 ;以TCNT0精确定时输出秒号作为捕获信号,用TCNT1对被测信号频率计数 ;用TCNT0直接对(8515)4兆晶振计数产生秒号,定时精度达1Hz 主常数选为256(即0) ;由PA0输出精确定时产生的秒信号(与ICP脚相连)捕获TCNT1计数值,相减计算频率 ;将频率转换为十进制数,装入显示缓存区,调DSPA子程序显示之(参考范例27和图4-5) ;重装TCC时对TCC进行修正,若修正(减法)计算不产生借位,将中断次数n减1 ;被测频率可近2兆,故须设1字节扩展计数器,以tcnt1溢出中断对其计数(共3字节计数器) ;在TCNT1捕获中断服务中,以3字节减法计算频率,并置位T标志;若TCNT1溢出标志置位 ;必须提前增1扩展计数器,并将TCNT1溢出标志清除(不再增1扩展计数器),再计算频率. ;TCNT1溢出中断优先级高于TCNT0,故TCNT1中断服务可能影响秒号精度,导致测量误差 ;可以排队法剔除坏值,即将几个连续采样按大小顺序排队,‘掐头去尾'只留中间再作平均. ;也可以监视LED显示,连续3秒稳定显示(高频测量允许有2Hz误差)即为所测频率正确值. ;若晶振采用12兆,被测信号频率(暂空比1:1或接近1:1)可接近6兆. .ORG $000 STRT26: RJMP RST26 ;实测8515晶振频率4.000167MHZ 计4000167个数为1秒 .ORG $003 RJMP T1_CAPT ;T/C1捕获中断 .ORG $006 RJMP T1_OVRF ;T/C1溢出中断 .ORG $007 RJMP T0_OVFB ;T/C0溢出中断 .ORG $00D ;4000167=256*15626-89=256*$3D0A-89/故TCC=89 n=15626 RST26: LDI R16,HIGH(ramend) OUT SPH,R16 LDI R16,LOW(ramend) OUT SPL,R16 SBI DDRA,0 ;PA0输出秒定时信号,捕获频率计数值 CBI PORTA,0 ;初始为低 CLR R22 CLR R21 CLR R20 ;R20,R21,R22为频率量瞬时计数采样 CLR R2 WDR LDI R16,$0D ;启动看门狗,溢出时间为0.49" OUT WDTCR,R16 ;写入看门狗控制寄存器 CLR XH LDI XL,$6C ;set the display buffer pointer T26LP: ST X+,R2 CPI R26,$74 BRNE T26LP ;清除$6C--$73 LDI R16,$01 ;T/C0为定时器,不分频 OUT TCCR0,R16 LDI R16,89 ; OUT TCNT0,R16 ;写TCC到TCNT0 LDI R16,$C6 ;上升沿捕获,允许噪音滤除,外部脉冲计数 OUT TCCR1B,R16 LDI R16,$8A ;允许T/C1捕获,溢出以及T/C0溢出中断 OUT TIMSK,R16 ; LDI R16,$3E ;设15626(=$3D0A)次中断(高位字节已增1) MOV R1,R16 ; MOV R19,$0A ; SEI ; HH1C: BRTS HH2C ;已采集到频率? RCALL DSPA ;仍显示原数据 RJMP HH1C HH2C: CLT ;频率量已在R3,R4,R5 MOV R9,R3 MOV R10,R4 MOV R11,R5 RCALL CONV1 ;翻为十进制数(R12R13R14R15<--R9R10R11) LDI XL,$74 CLR XH LDI YL,15 CLR YH HHLOP: LD R16,Y ;分解十进制数,送入LED显示区($6C--$73) ADNI R16,$0F ST -X,R16 LD R16,Y SWAP R16 ANDI R16,$0F ST -X,R16 DEC YL CPI R26,$6C ;分解完毕? BRNE HHLOP RJMP HH1C ;显示新数据 T0_OVFB:SEI ;TCNT0溢出,允许中断嵌套 PUSH R16 IN R8,SREG DEC R19 BRNE GOON13 CBI PORTA,0 ;秒信号后沿 DEC R1 ;到15626次中断? BRNE GOON13 SBI PORTA,0 ;秒定时捕获信号前沿 IN R16,TCNT0 ;*读TCNT0自然计数值 SUBI R16,164 ;*89之补为167,考虑补偿操作本身耗时,减去164 OUT TCNT0,R16 ;*第15626次中断后,重新装入TCC=89+(TCNT0)+3到TCNT0 LDI R16,$3E MOV R1,R16 ;重新装入中断次数 LDI R19,$0A BRCC GOON13 ;补偿操作如有借位,将中断次数减1 DEC R19 ;->252 253 254 255 | 0 1 2 3 4 5...加法计数方向--> GOON13: POP R16 ; | | | | | | | | | | | OUT SREG,R8 ;<--15626次范围-->|<-15625次范围(补偿后进(借)位 ;C=l)-> RETI T1_OVRF:IN R18,SREG ;TCNT1溢出中断服务 INC R3 ;R3为TCNT1扩展字节 OUT SREG,R18 RETI T1_CAPT:IN R6,SREG ;T/C1捕获中断 PUSH R16 IN R5,ICR1L IN R4,ICR1H MOV R16,R22 MOV R22,R5 SUB R5,R16 MOV R16,R21 MOV R21,R4 ;与上一次采集的频率量相减,得到频率值 SBC R4,R16 IN R16,TIFR SBRS R16,7 RJMP T1CP1 INC R3 ;8515TCNT1溢出中断,预先对扩展字节计数 LDI R16,$80 ;并将溢出标志清除,(中断返回后不再计数) OUT TIFR,R16 ;清除TIFR,7 T1CP1: MOV R16,R20 MOV R20,R3 SBC R3,R16 ;采集频率量在R3,R4,R5 SET ;建采集频率量标志 POP R16 OUT SREG,R6 RETI ;范例97 时基资源共享式综合测量系统 ; 本时基资源共享式综合测量系统,具有精确定时PWM输出、输入捕获测外部信号 ;周期、获取TCNT1溢出中断信号等多种功能。特点是TCNT1启动之后即不停运行。 ; 时基资源共享式PWM的特点在于装入比较匹配寄存器之数据方式,它不是在比较 ;匹配达到时清除定时/计数器,再装入高(或低)电平时间常数:而是当比较匹配 ;达到时以定时/计数器当前值加上时间常数后将和装入比较匹配寄存器,二者效果 ;是相同的。可称前者为静态设置,后者为动态设置。后者因不停运行定时/计数器 ;,其资源可同时用于输出比较匹配A及B、输入捕获、定时信号输出等等。 ; 本程序使用晶体标称值4MHZ实测为4,000,236HZ。使用定时/计数器1直接 ;对主频精确定时设定PWM高低电平的维持时间。以ICP脚输入被测周期脉冲信号。 ; 本程序PWM之暂空比与范例51相同,为5毫秒(高):10毫秒。故维持 ;高电平的时间常数为4,000,236÷200=20,001,维持低电平的时间常数为 ;4,000,236÷100=40,002。此即输出比较匹配A达到时交替写入比较匹配寄 ;存器OCR1A之对TCNT1当前内容的超前值。 ; 因以TCNT1直接对主频计数,频率高周期短,输入捕获的外部信号周期不能 ;大于65536÷4,000,236=0.01638(秒)即16.38毫秒(但也不能太小,对频率 ;较高的脉冲信号应改为测频率)。以相邻两次捕获值相减之差除以主频得到被测信 ;号之周期(单位为秒)。 ; 为避免小数除法运算,可将相邻两次捕获值相减之差先乘以1,000,再将乘积 ;除以主频,将得到以毫秒为单位的周期值;考虑到除法子程序DIV16只实现整数 ;除法,且除数不能大于65535,可将主频缩小100倍,即以40,002作除数,故 ;除得之商扩大了100倍。这样将整数商二翻十后,其末两位皆为小数。本程序采用 ;这种计算方法。并在主循环程序中调DSPA子程序显示所测周期值。。 ; 若将以上算法中乘以1,000改为乘以10,000,并增加对商的万位转换, ;其余保留不变,则所得商数末3位皆为小数位。本算法精度高于上一种方法,如有 ;提高测量精度之必要,应采用后种算法。 ; 若扩大测量信号周期,应对TCNT1溢出信号计数,做3字节减法(见范例96) ;后再计算被测信号周期(除以4,000,236)。所测信号周期可达4.194秒。 ; 本示例定时精度可与范例51做如下比较:本例中高低电平分别对主频计数 ;20,001个和40,002个。范例51中高低电平分别对主频计数19,968个和 ;40,000个。本示例定时精度明显高于范例51。 ; 本示例TCNT1产生溢出中断之周期为16.38毫秒,其频率约为61HZ。在TCNT1 ;溢出中断服务子程序中由PA3以正脉冲形式输出该信号。 .ORG $000 ;USE 8535 STRT43: RJMP RST43 ;5.0000MS(高):9.9999MS(低) 晶振4,OOO,236HZ .ORG $005 RJMP T1_CP43 ;T/C1输入捕获中断 .ORG $006 RJMP T1_CA43 ;T/C1输出比较匹配A中断 .ORG $008 RJMP T1_OV43 ;TCNT1溢出中断 .ORG $011 RST43: LDI R16,HIGH(RAMEND) OUT SPH,R16 LDI R16,LOW(RAMEND) OUT SPL,R16 LDI R16,$80 ;T/C1比较匹配A达到时,清除输出脚OC1A OUT TCCR1A,R16 LDI R16,$41 ;不分频,比较匹配达到不清TCNT1;上升沿捕获/禁止噪音滤除 OUT TCCR1B,R16 SBI DDRD,5 SBI PORTD,5 ;PD5(OC1A)初始化输出为高 SBI DDRA,3 ;PA3为TCNT1溢出中断信号输出 CBI PORTA,3 ;PA3输出为低 LDI R16,$4E OUT OCR1AH,R16 LDI R16,$21 ;写比较匹配寄存器($4E21=20001脉宽5毫秒) OUT OCR1AL,R16 LDI R16,$34 ;允许输入捕获/输出比较匹配A/TCNT1溢出中断 OUT TIMSK,R16 CLR R21 CLR R20 ;捕获值暂存单元 CLR XH LDI XL,$6C CLR43: ST X+,R20 CPI XL,$74 BRNE CLR43 ;清除显示区$6C--$73 SEI HH43: RCALL DSPA ;背景程序:显示捕获频率信号之周期,单位:毫秒 BRTC HH43 RCALL FIL2 ;T=1,已捕获到数据在R4,R5/先关显示 CLT MOV R14,R4 MOV R15,R5 LDI R16,3 MOV R12,R16 LDI R16,$E8 ;取立即数1000(=$3E8) MOV R13,R16 RCALL MUL16 ;乘以1000 LDI R16,$9C ;使周期单位为毫秒 MOV R10,R16 LDI R16,$42 ;$9C42=40002 MOV R11,R16 RCALL DIV16 ;除以立即数40002,得到被测脉冲周期之单位为毫秒,且含因子100 MOV R16,R14 MOV R17,R15 LDI R18,3 LDI R19,$E8 RCALL CONVT ;二翻十,得千位 STS $70,R11 ;送入显示区 CLR R18 LDI R19,$64 RCALL CONVT ;二翻十,得百位 LDI R19,-$29 ;在百位处加小数点(百位实为个位) SUB R11,R19 STS $71,R11 ;送入显示区 LDI R19,10 RCALL CONVT ;二翻十,得十位 STS $72,R11 STS $73,R17 ;小数送入显示区 RJMP HH43 ;转去显示新采样数据 CONVT: CLR R11 COVLOP:SUB R17,R19 SBC R16,R18 ;减去十进制数某位之权 BRCS CONVCM INC R11 ;够减,增权 RJMP COVLOP CONVCM:ADD R17,R19 ;否则恢复余数 ADC R16,R18 RET T1_CA43:SEI IN R1,SREG IN R24,TCCR1A SBRS R24,6 RJMP OUTLW ;当前输出低电平,转 IN R24,OCR1AL IN R25,OCR1AH SUBI R24,$DF ;LOW(-20001) SBCI R25,$B1 ;HIGH(-20001)/$B1DF为20,001之补码 OUT OCR1AH,R25 OUT OCR1AL,R24 ;写入高电平维持时间超前值 LDI R24,$80 ;比较匹配A达到时,OC1A输出为低 OUT TCCR1A,R24 OUT SREG,R1 RETI OUTLW: IN R24,OCR1AL IN R25,OCR1AH SUBI R24,$BE ;LOW(-40002) SBCI R25,$63 ;HIGH(-40002)/$63BE为40,002之补码 OUT OCR1AH,R25 ; OUT OCR1AL,R24 ;写入低电平维持时间超前值 LDI R24,$C0 ;比较匹配A达到时,OC1A输出为高 OUT TCCR1A,R24 OUT SREG,R1 RETI T1_CP43:IN R3,SREG ;T/C1捕获中断 IN R5,ICR1L IN R4,ICR1H MOV R17,R21 MOV R21,R5 SUB R5,R17 MOV R17,R20 MOV R20,R4 ;与上一次采集的频率量相减,得到频率值 SBC R4,R17 ;在R4,R5中 SET ;建采集频率量标 OUT SREG,R3 RETI T1_OV43:SEI SBI PORTA,3 ;OUTPUT THE 61HZ PULS SBI PORTA,3 SBI PORTA,3 SBI PORTA,3 CBI PORTA,3 ;脉冲宽度2微秒 RETI ;范例98 ;智能型RS-232与RS-485标准转换程序 ;MAX232'R1OUT接MAX483'DI/MAX483'RO接MAX232'T1IN ;由TCNT0配合PB0以软件接收RS-232数据 对485进行监控:PB1接DE和/RE ;AVR对485发来数据不接收,该数据经MAX483'RO->MAX232'T1IN-->RS-232远端 ;数据起始位下降沿引起中断接收,中断服务一开始,将对RS-485的控制改为允发禁收使RS- ;232发来数据直接通过RS-485向远端发送 ;当收到RS-232数据结束符$03后,经半位延时,对RS-485的控制改为允收禁发 使能接收RS- ;485远端发来数据(故要求经RS-232发来数据要以$03为结束符,对来自RS-485数据无此要求) ;可采用avr专门管理两种标准转换方案(可采用少脚ATtiny系列),也可采取主avr兼管方案. ;主avr兼管时,它既接收处理完整串行数据块(及执行其它程序),又控制通讯标准转换. .EQU DATA4=$100 .ORG 0 ;R16:THE BIT SEQUENCE COUNTER R17:WORKING ;REG.R18:FLAG UNIT STRT3S: RJMP RST3S ;BAUD RATE:9600 USE 8515/may REPLACE BY ATtiny serials .ORG $007 ;$007(8515) RJMP T0_OF .ORG $00D RST3S: LDI R17,HIGH(ramend) OUT SPH,R17 LDI R17,LOW(ramend) OUT SPL,R17 LDI R17,$02 ;8535:$01 OUT TIMSK,R17 ;timsk,1(允许tcnt0中断) LDI R17,6 ;设外部脉冲计数 OUT TCCR0,R17 CBI DDRB,0 ;T0 为输入 LDI R17,$FF OUT TCNT0,R17 ;计1个数即中断 SBI DDRB,1 ;PB1输出,控制DE和/RE CBI PORTB,1 ;禁止485发送 SEI CLR R18 CLR R16 HERE0: SBRC R18,0 BRNE RST3S ;无错误标志循环 SBRS R18,1 BRNE HERE0 ;未收到数据块结束符($03)循环 LDI R16,64 HERE1: DEC R16 BRNE HERE1 ;延时(48+3.5=)52微秒(超过半位,以等待半个停止位发过去) RJMP RST3S ;以使远端485正确收到停止位 T0_OF: SBI PORTB,1 ;允许485发送 IN R11,SREG PUSH R17 CPI R16,0 ;接收起始位? BRNE T0SV11 LDI R17,2 ;YES OUT TCCR0,R17 ;改为内定时,8分频(4MHZ/8) LDI R17,232 ;半位定时常数24,定出48微秒(<52微秒) OUT TCNT0,R17 RJMP T0SV7 T0SV11: CPI R16,1 ;半位定时后,查起始位有效性 BRNE T0SV12 SBI PORTB,0 SBIC PINB,0 ;低电平为有效 RJMP T0ER ;否则转错误处理 RJMP T0SV62 ; T0SV12: CPI R16,10 ;停止位? BRNE T0SV3S CLR R16 ;是 SBI PORTB,0 ;停止位为l? SBIS PINB,0 RJMP T0ER ;否,转错误处理 MOV R17,R15 CPI R17,3 ;收到结束符$03? BRNE T0SV13 ORI R18,2 ;结束符收到 OUT TCCR0,R16 ;停止TCNT0 RJMP T0SV63 T0SV13: LDI R17,6 OUT TCCR0,R17 ;改为外定时 LDI R17,$FF ;停止位下降沿即中断 OUT TCNT0,R17 RJMP T0SV63 T0SV3S: BRCC T0ER ;出错(位计数器超过10) CLC ;2--9:接收一位数据 SBI PORTB,0 ;本位为1? SBIC PINB,0 SEC ROR R15 ;接收数据组织到R15 T0SV62: IN R17,TCNT0 ;读入TCNT0自然计数值 INC R17 SUBI R17,52 ;1位时间常数为52 OUT TCNT0,R17 ;补偿后回送定时常数 T0SV7: INC R16 ;位计数器增1 T0SV63: POP R17 OUT SREG,R11 RETI T0ER: SBR R18,1 ;出错标志 ERR. FLAG CLR R16 OUT TCCR0,R16 ;停止TCNT0 RJMP T0SV63 ;范例99 ; 串行日历/时钟芯片DS1302的应用子程序。AVR与DS1302接口为:PC0--SCLK,PC1--DATA,PC2--/RST。请参看本范例之附图。 ; 结构与运作特点如下: ;(1)采用标准频率晶体(32768HZ),便于调整(可加电容补偿),可对PC0/PC1/PC2加提拉电阻。 ;(2)DS1302只有8只脚,小巧精悍,耗电省,抗干扰.便于与单片机接口,以串行方式按位读写数据. ;(3)以备用电池供电保存数据,断电后自动执行写保护,故可靠性高。上电后须用指令解除写保护。 ;(4)片内除8个时钟日历单元外还有31个RAM单元,可作为系统断电保护数据存储单元 ;(5)可以并发(BURST,即连续)方式读写8个时钟日历单元(秒/分/时/日/月/周/年/年)或读写31个RAM单元, ; 命令如下: ; $BE为以并发方式写8个时钟日历单元,$BF为以并发方式读8个时钟日历单元。 ; $FE为以并发方式写31个RAM单元,$FF为以并发方式读31个RAM单元。 ;(6)除以并发方式读写外,还可按字节读写,但读写前须先写入命令。 ; 读写命令格式为:1 Y A4 A3 A2 A1 A0 X,最高位为1表示命令有效,Y=0,选择读写时间/日期 ; Y=1,选择读写片内RAM,A4-A0,片内RAM/时钟单元地址,X=0,选择写操作,X=1,选择读 ;操作. ;(7)串行时钟上升沿写入一位数据,下降沿读出一位数据;且读写只有在/RST信号为高时才有效。 ; 故要求/RST信号有效前时钟信号应已就绪。 ;(8)本程序AVR时钟为4MHZ,若使用其他时钟,重新调整读写延时时间(程序中NOP之个数) ;(9)对RAM并发读写方法可参考并发读写时钟日历子程序进行. ;1)并发(BURST)方式写时钟日历单元(时钟日历数据 秒,分,时,日,月,周,年,年分别在R8--R15,) WBURST: CLR YH ; LDI YL,8 ;数据指针,首指秒单元R8 RCALL DEPRV ;解除写保护(写入$8E00) CBI PORTC,0 ;为上升沿写作准备(SCLK升高) NOP NOP NOP SBI PORTC,2 ;复位信号变高(SETB RST) NOP NOP NOP NOP NOP NOP LDI R18,$BE ;BURST(wr.) ADDR.&INSTRUC.(命令$BE) RCALL WBYTE ; WLOP: LD R18,Y+ RCALL WBYTE ;写入1字节数据 CPI YL,16 BRNE WLOP ;数据都写完? CBI PORTC,2 ;禁止读写 NOP NOP NOP NOP CBI PORTC,0 RET ;2)并发(BURST)方式读时钟日历单元(时钟日历数据 秒,分,时,日,月,周,年, ;分别读到R8--R14中) RBURST: CLR YH ;首指R8 LDI YL,8 SBI DDRC,0 ;SCLK 输出 SBI DDRC,2 ;WR/RD ENABLE 输出 SBI PORTC,0 ;时钟SCLK初始输出为低 NOP NOP NOP SBI PORTC,2 ;复位有效,允许时钟相关沿有效 NOP NOP NOP NOP NOP NOP LDI R18,$BF ;BURST(rd.) ADDR.&INSTRUC. RCALL WBYTE ;写入并发读命令 RLOP: RCALL RBYTE ;读出一字节时钟/日历数据 ST Y+,R18 ;存储 CPI R28,15 BRNE RLOP ;数据都读完? CBI PORTC,2 ;禁止读写 NOP NOP NOP NOP CBI PORTC,0 ;使SCLK变低 RET ;3)解除写保护子程序(对DS1302写入$8E,$00) DEPRV: SBI DDRC,0 ;SCLK 输出 SBI DDRC,2 ;WR/RD ENABLE 输出 CBI PORTC,0 ;时钟SCLK初始输出为低 NOP NOP NOP SBI PORTC,2 ;复位有效,允许时钟相关沿有效 NOP NOP NOP NOP NOP NOP LDI R18,$8E RCALL WBYTE NOP NOP CLR R18 RCALL WBYTE ;写入$8E和$00 CBI PORTC,2 ;禁止读写 NOP NOP NOP NOP CBI PORTC,0 RET ;4)对DS1302秒,分,时单元写入3字节数据 WTIME: CLR YH ; LDI R17,$80 ;写秒单元命令 LDI YL,8 ;R8(秒)R9(分)R10(时) LDI R19,3 WCOM: RCALL DEPRV ;解除写保护 WLOP1: CBI PORTC,0 ;时钟SCLK初始输出为低 NOP NOP NOP SBI PORTC,2 ;复位有效,允许时钟相关沿有效 NOP NOP NOP NOP NOP NOP MOV R18,R17 RCALL WBYTE ;写入一字节命令 SUBI R17,$FE ;指向时间下一单元 LD R18,Y+ RCALL WBYTE ;写入时间单元1字节 CBI PORTC,2 ;禁止读写 NOP NOP NOP NOP CBI PORTC,0 DEC R19 BRNE WLOP1 ;写完规定字节? RET ;5)写入日期子程序 WDATE: CLR YH ; LDI YL,11 ;R11(日)R12(月)R13(周)R14(年) LDI R17,$86 ;写日单元命令 LDI R19,4 RJMP WCOM ;6)读出时间子程序 RTIME: CLR YH ; LDI R17,$81 ;读秒单元命令 LDI YL,8 ;读出数据送到R8(秒)R9(分)R10(时) LDI R19,3 RCOM: SBI DDRC,0 ;SCLK 输出 SBI DDRC,2 ;WR/RD ENABLE 输出 RLOP1: CBI PORTC,0 ;时钟SCLK初始输出为低 NOP NOP NOP SBI PORTC,2 ;允许读写 NOP NOP NOP NOP NOP NOP MOV R18,R17 RCALL WBYTE ;写入读命令 SUBI R17,$FE ;指向下一单元地址 RCALL RBYTE ;读出一字节数据 ST Y+,R18 CBI PORTC,2 ;禁止读写 NOP NOP NOP NOP CBI PORTC,0 ;时钟变低 DEC R19 BRNE RLOP1 ;已读出规定字节? RET ;7)读出日期子程序 RDATE: CLR YH LDI YL,11 ;读出数据放入R11(日)R12(月)R13(周)R14(年) LDI R17,$87 ;读出日单元命令 LDI R19,4 RJMP RCOM ;8)将R18中数据写入DS1302 WBYTE: LDI R16,8 ;8位/字节 SBI DDRC,1 ;PC1为输出 WB1: CBI PORTC,0 ;时钟SCLK初始输出为低 ROR R18 ;一位数据传到进位C BRCC WB10 SBI PORTC,1 RJMP WB2 WB10: CBI PORTC,1 ;1位数据输出到数据线(DS1302'DATA BUS) WB2: NOP NOP NOP NOP SBI PORTC,0 ;上升沿写入一位 DEC R16 BRNE WB1 ;8位数据都写完? RET ;9)读出DS1302一字节数据在r18中 RBYTE: LDI R16,8 ;8位/字节 CBI DDRC,1 ;PC1输入 RD1: CBI PORTC,0 ;下降沿读出一位数据 NOP NOP NOP NOP SBI PORTC,1 ;上拉电阻激活 CLC SBIC PINC,1 SEC ;读出一位数据并-->C ROR R18 ;组织数据 SBI PORTC,0 ;SCLK升高,为下位读准备 DEC R16 BRNE RD1 ;8位数据都写完? RET ;范例100 ;DALLAS 18B20测温程序 ; DS18B20为美国DALLAS公司(已被MAXIM公司并购)生产的单线数字温度传感器, ;可将温度信号直接转换成数字信号供单片机处理,所测温度范围-55°C~125°C,精度 ;达0.5°C,转换时间为750毫秒。该器件出厂时带有固化的8字节‘身份’编号,最低 ;位字节为家族号码$28,接下来6字节为器件流水线编号,最高位字节为CRC校验码。 ;有读ROM,匹配ROM,启动温度变换,读RAM数据等十余种命令对18B20操作。使用 ;18B20之前要用读ROM命令读出其身份编号并记录。一条单总线上可挂接任意多个 ;18B20,单片机通过单总线发出启动转换命令之后,所有18B20同时进行温度转换。 ;经等待延时后,单片机通过发出匹配ROM命令,18B20编号,读RAM数据等命令等, ;读取各18B20温度数据组(每组数据共9字节)。程序中对每组数据都进行CRC校验。 ;温度数据占2字节,为补码形式。最高位为符号位,0为正1为负。高位字节和低位 ;字节的高4位为温度整数部分,最低4位为温度小数部分。程序中对温度数据进行取 ;补、左移将整数部和小数部分分离,再将它们分别转成十进制数。整数二翻十用减 ;十进一法,小数二翻十采用按权累加法(并以减负替代加正),再将它们冠以数符 ;并加小数点送入DSPA子程序的显示缓存区,调该子程序进行显示。 ; 由于DSPA子程序中含0.462秒定时复位看门狗指令,故以调用DSPA为主循环程序 ;不必考虑对看门狗管理问题(初始化设置看门狗溢出周期为0.49秒)。 ; DS18B20不象一般串行器件既有数据线又有时钟线,它只有一条数据线,故它 ;只能靠较严格的时序脉冲信号进行读写,程序中多种延时环节就是为调整时序所 ;设。本程序AVR使用4MHZ时钟,如改变时钟,应按定时时间重新确定延时常数。 ; 18B20的使用可采用窃电方式,此种方式要将18B20电源端接地。在线缆长测点 ;的应用场合,可控制MOS管取得数据线的强上拉,以提高总线驱动能力。 ;对18B20的ROM操作命令如下: ; 命令 代码 ; 读ROM $33 ; 匹配ROM $55 ; 跳过ROM $CC ; 搜索ROM $F0 ; 告警搜索 $EC ; 对18B20的存储器操作命令如下: ; 命令 代码 ; 写暂时存储器 $4E ; 读暂时存储器 $BE ; 复制暂时存储器 $48 ; 启动温度变换 $44 ; EEPROM内容调出 $B8 ; 读电源 $B4 ; 有关18B20初始化,读写命令,读写时序等请参看参考文献9和10,18B20与AVR接口 ;见程序附图,CRC检测请参看4.8.4小节。 START2:LDI R16,2 OUT SPH,R16 LDI R16,$5FH OUT SPL,R16 ;堆栈指针初始化 SBI DDRA,2 CBI PORTA,2 ;MOS管不上拉 RCALL RESET ;复位18B20 WDR LDI R16,$0D OUT WDTCR,R16 ;启动看门狗,溢出时间为0.49秒 CLR R2 ;执行请除看门狗指令WDR的定时器初始化请除 LDI R16,$CC ;跳越ROM(SKIP ROM) RCALL WB LDI R16,$44 ;START DS18B20 TEMPORATURE CONVERTING RCALL WB CLR XH CLR YH ;指针高位字节清除 LDI YL,$6C CLR44:ST Y+,YH CPI YL,$74 BRNE CLR44 ;清除显示缓存区($6C~$74) LDI R17,163 ;4.618×163=753(ms) STR0: RCALL DSPA DEC R17 BRNE STR0 ;总共延时753ms,等待转换完成 RCALL RESET ;再次复位DS18B20 LDI XL,$60 ;温度数据指针 LDI R17,4 ;总共4只DS18B20 LDI ZH,HIGH(DATA*2) LDI ZL,LOW(DATA*2);18B20身份数据指针 LOOP0:LDI R16,$55 ;匹配ROM命令 (match rom) RCALL WB ;写入18B20 LDI R18,8 ;18B20身份数据共8个字节固化在FLASH中 LOOP4:LPM ;取数据 MOV R16,R0 ;转入R16 RCALL WB ;写入18B20 1字节 ADIW ZL,1 ;指向下一字节 DEC R18 BRNE LOOP4 ;共写入8个字节 LDI R16,$BE ;读18B20数据存储器命令 RCALL WB ;写入该命令 LDI YL,$74 LOP40:RCALL RB ;读出18B20数据共9个字节 ST Y+,R16 ;存入$74-$7C CPI YL,$7D BRNE LOP40 RCALL RESET ;再次复位18B20 LDI YL,$74 RCALL CRC9 ;对读得数据进行CRC校验 TST R15 BREQ LLL ERROR:LDI R16,15 ;CRC余式不为零,温度数据错误 LDI YL,$6C EER1: ST Y+,R16 CPI YL,$74 BRNE EER1 ERR1: RCALL DSPA ;显示$FFFFFFFF,等待按键 SBRC R16,7 RJMP ERR1 RJMP START2 ;有键按下,重新启动 LLL: LDS R16,$74 LDS R15,$75 ;取温度数据 R15为HIGH BYTE ST X+,R16 ST X+,R15 ;温度转入SRAM BST R15,7 ;数符存于T BRTC PLUS ;正数转 COM R16 COM R15 LDI R18,255 SUBI R16,R18 SBCI R15,R18 ;负数求补 PLUS: LDI R18,4 LOOP5:ADD R16,R16 ADC R15,R15 DEC R18 BRNE LOOP5 ;整数部分在R15 MOV R19,R16 ;小数部分转入R19 CLR R9 ;百位BCD予清 LDI R16,100 CP R15,R16 BRCS LOP51 INC R9 ;百位BCD存在(EXISTED)! SUB R15,R16 ;减去100 LOP51:LDI R16,10 CLR R10 LOP52:SUB R15,R16 BRCS LOP53 INC R10 RJMP LOP52 ;十位及个位BCD转换 LOP53:ADD R15,R16 ;十位在R10,个位在R15 CLR R16 SBRC R19,7 SUBI R16,-$50 ;小数最高位值为0.5 SBRC R19,6 ;其余折半递减 SUBI R16,-$25 ;0.25 SBRC R19,5 SUBI R16,-$13 ;0.125 SBRC R19,4 SUBI R16,-6 ;0.0625 CPI R16,$0A ;产生非法BCD?(只可能在低位产生) BRHC LOP54 SUBI R16,$FA ;小数部分二翻十/调整(在R16中) LOP54:MOV R11,R16 LDI YL,$6C LDI R16,$24 ST Y+,R16 ST Y+,R16 ST Y,R16 RTC LOP55 LDI R16,$14 ;负温度,加负号! ST Y,R16 LOP55:INC YL ST Y+,R9 ;百位BCD装入 $6F 单元 ST Y+,R10 ;十位BCD直接装入$70单元 MOV R16,R15 SUBI R16,-$29 ;个位BCD加小数点后 ST Y+,R16 ;装入$71单元 MOV R16,R11 SWAP R16 ANDI R16,$0F ST Y+,R16 MOV R16,R11 ANDI R16,$0F ST Y+,R16 ;分解小数BCD/并装入$72及$73单元 CLR R8 NORML:RCALL DSPA ;显示温度数据2.4秒 RCALL DSPA ;4.62ms×2×256=2.4s DEC R8 BRNE NORML DEC R17 BREQ HALT ;采集完4点温度? RJMP LOOP0 ;未完循环 HALT: LDI R16,$1D OUT WDTCR,R16 LDI R16,$15 OUT WDTCR,R16 ;已采集到4点温度,关看门狗 RDSPA:RCALL DSPA SBRC R16,7 RJMP RDSPA ;无键按下,显示最后采集的温度 RJMP START2 ;否则再次启动 DATA: .DB $28,$3A,$13,$08,$00,$00,$00,$E5 ;18B20身份数据 .DB $28,$4A,$4D,$08,$00,$00,$00,$14 .DB $28,$3A,$19,$08,$00,$00,$00,$5E .DB $28,$32,$33,$08,$00,$00,$00,$66 RESET: SBI DDRA,3 ;PA3为输出 CBI PORTA,3 ;负脉冲前沿 LDI R19,4 RES1: RCALL DL170 DEC R19 BRNE RES1 ;延时682.75微秒 (最短可555微秒) SBI PORTA,3 ;负脉冲结束 LDI R18,146 RES2: DEC R18 BRNE RES2 ;延时110微秒 RCALL DL170 ;总共280微秒 CBI DDRA3 ;转为输入(CHANGE TO INPUT) SBI PORTA,3 ;上拉电阻激活(PULL UP MOS ACTIVED) CLC SBIC PINA,3 ;18B20存在标志存于 C SEC RCALL DL170 ;再次延时 RET DL170: LDI R18 ,224 ;延时170.75微秒(含RCALL和RET时间) LP170: DEC R18 BRNE LP170 RET WB: LDI R19,8 ;写入1字节数据 MOV R15,R19 SBI DDRA,3 LOOP2: CBI PORTA,3 ;数据线输出为低 LDI R19,23 LOP21: DEC R19 BRNE LOP21 ;延时17微秒 ROR R16 BRCC LOP22 ;1位数据由进位C转入PC3 SBI PORTA,3 RJNP LOP23 LOP22: CBI PORTA,3 LOP23: LDI R19,88 LOP24: DEC R19 BRNE LOP24 ;延时66微秒 SBI PORTA,3 NOP NOP NOP NOP NOP NOP NOP NOP DEC R15 BRNE LOOP2 RET RB: LDI R19,8 ;读出1字节数据 MOV R15,R19 LOOP3: SBI DDRA,3 SBI PORTA,3 ;数据线输出为高 LDI R19,5 LOP31: DEC R19 BRNE LOP31 ;延时3.5微秒 CBI PORTA,3 ;数据线输出为低 LDI R19,6 LOP3A:DEC R19 BRNE LOP3A ;延时4.5微秒 SBI PORTA,3 ;数据线输出为高 LDI R19,26 LOP32: DEC R19 BRNE LOP32 ;延时19微秒 CBI DDRA,3 ;转为输入 SBI PORTA,3 ;上拉MOS管激活 CLC SBIC PINA,3 SEC ;读出1位数据到C LDI R19,88 LOP33:DEC R19 BRNE LOP33 ;延时66微秒 ROR R16 DEC R15 BRNE LOOP3 RET CRC9: LDI R18,9 ;9字节数据CRC检测程序 CRC90: CLR R15 ;异或工作单元 LDI R19,$8C MOV R14,R19 LP6: LDI R19,8 LD R16,Y+ LP7: LSR R16 ROR R15 BRCC NXRL EOR R15,R14 NXRL: DEC R19 BRNE LP7 DEC R18 BRNE LP6 RET ;范例101 产生循环冗余检测(CRC)校验码表格子程序 ;本子程序为生成$00--$FF共256个数据之双字节CRC校验码表子程序,生成多项式为 ;P(X)=X16+X15+X2+1=$18005。因每一字节都生成两字节的CRC校验码,故CRC校 ;验码表格长度为512字节。程序中规定将其放在片内SRAM$100--$2FF之中。也可将该 ;表存放地址作为子程序的入口条件,在主程序中规定存放地址。使用的单片机为MEGA8 ;/16/128;若使用8515单片机,须使用外部扩展SRAM;本子程序产生的CRC校验码表, ;可直接烧录到FLASH,或另行作为文件保存。 ;若采用4字节的CRC校验码,表格长度达1024字节,则必须使用MEGA103/128等高档AVR ;单片机,或外扩SRAM的8515;故若处理的位序列信息不是很长,或对CRC检测的实时性 ;要求不是很强,不必采用查表处理方式。 CRCTABL:LDI XH,$01 ;CRC-CODE-TABLE -GENERATING SUBPROGRAM CLR XL ;CRCTABLE FROM $100 TO $2FF CLR R16 ;USE MEGA8/16/128 LDI R17,$05 LDI R18,$80 ;P(X)=$18005 CRCT0: LDI R19,8 CLR R14 CLR R15 ;add 2bytes $00 behind a Bi CRCT1: LSL R14 ROL R15 ROL R16 BRCC CRCT2 EOR R14,R17 EOR R15,R18 CRCT2: DEC R19 BRNE CRCT1 ST X+,R14 ST X+,R15 INC R16 BRNE CRCT0 RET ;范例102 快速生成位序列校验码/或对接收位序列进行循环冗余检测子程序 ;100字节位序列m0,m1,m2,m3,m4,...m98,m99在发送方以递推方式生成CRC校验码子程序 ;或在接收方对该序列进行CRC检测之子程序 ;在发送方,本程序为CRC校验码生成子程序。将此位序列除以生成多项式P(X) ;=X16+X15+X2+1,将生成的CRC校验码(即余式)装入位序列的最低两位字节 ;(冲掉m0,m1),将最终处理的位序列发送出去。 ;在接收方,本程序为CRC检测子程序。将接收到的位序列除以生成多项式P(X) ;=X16+X15+X2+1,若将原位序列最低两位字节m0,m1恢复(即除得的余式R15R14 ;与原始位序列最高两位字节相等),则为正确接收。 ;本程序中循环次数为98,比位序列字节数少2。因为位序列最低两个字节m0,m1直 ;接作为(第一个)余式,不对它们查取CRC校验码。 ;X为按字节寻址位序列指针 ;寻址CRC校验码表格先按字计算地址指针,将其增倍后变为按字节寻址。 CRCOUT:LDI XH,$1 ;THE BIT SEQUENCE IS IN $100---$163 CLR XL ;TOTAL 100 BYTES LDI R16,$62 ;THE DATA BLOCK LENGTH IS 98(=100-2) LD R14,X+ ;m0 LD R15,X+ ;m1 CRCO1:LD R13,X+ ;fetch m2 at the first! LDI ZH,HIGH(DATA5) LDI ZL,LOW(DATA5) ADD R30,R13 CLR R13 ADC R31,R13 LSL R30 ROL R31 ;point to the CRC CODE! LPM EOR R14,R0 ADIW R30,1 LPM EOR R15,R0 ;CRC CODE IN R14&R15(HIGH)! DEC R16 BRNE CRCO1 STS $101,R15 STS $100,R14 ;将生成的CRC校验码放在位序列的最低两位字节中 RET ;或将原始位序列的最低两位字节恢复 DATA5:.DB $00,$00,$80,$05 ;THE CRC CODE TABLE .DB $80,$0F,$00,$0A ;与范例101中SRAM$100--$2FF单元内容完全相同! .DB $80,$1B,$00,$1E ;.................... ;其余略 end