谁能帮我在这个单片机多功能调试助手的p16口写adc程序吗?怎么写?

单片机源程序_甜梦文库
单片机源程序
第5章【例】从片外数据存储器 1000H、1001H 地址单元中的数据作“与”运算,并把结 果放到片内 30H 单元中。 程序如下: ORG LJMP ORG MAIN: MOV MOV INC ANL MOV SJMP END 【例】 比较两个无符号数大小, 设两个无符号数分别存放在内部 RAM 的 30H 和 31H 单元中,比较大小后把大数存到 32H 中。 程序如下: ORG ORG MAIN: CLR SUBB 比较 JC DATA2 ;条件判断 MOV 32H,30H ;30H 单元中的数大 WAIT: SJMP $ DATA2: MOV 32H,31H ;31H 单元中的数大 SJMP END WAIT H C A,31H ;保证前面的操作不会对后面结果有影响 ;取 30H 单元中的数据到累加器中 ;30H 单元中的数减去 31H 单元中的数,两数大小 LJMP MAIN 0000H MAIN 0050H DPTR,#1000H ;设置指针初值 ;取 1000H 单元中的数据 ;保存到 A 中 ;指针加 1 ;取 1001H 单元中的数据 ;两个数据相与 ;把结果存到 30H 单元中 R0,A DPTR A,R0 30H,A $MOVX A,@DPTRMOVX A,@DPTRMOV A,30H第 1 页 共 87 页 【例】 利用散转指令编写程序,根据 30H 单元中变量 X 的内容转入相应的分 支。X=0 时执行 Y=0+0;X=1 时执行 Y=1+1;X=2 时执行 Y=2+2。并将结果存 入 31H 单元中。 程序如下: ORG LJMP ORG 0000H MAIN 1000H ; 散转程序入口地址 ; 每个入口地址均为 2 个字节MAIN: MOV A,30H MOV DPTR,#TAB RL A JMP @A+DPTR WAIT: TAB: SJMP $ AJMP X0 AJMP X1 AJMP X2 X0: MOV A,#0 RL A MOV 31H,A LJMP WAIT X1: MOV A,#1 RL A MOV 31H,A LJMP WAIT X2: MOV A,#2 RL A MOV 31H,A LJMP WAIT END 【例】有一数据块从片内 RAM 的 30H 单元开始存入,设数据块长度为 10 个 单元。根据下式:?x ? 2 x ? 0 ? y ? ? 100 x ? 0 ? x x?0 ?求出 y 值,并将 y 值放回原处。第 2 页 共 87 页 参考程序如下: ORG 0000HMOV R0,#10 MOV R1,#30H START:MOV A,@R1 JB ACC.7,NEG JZ ZER0 ;取数 ;若为负数,转 NEG;若为零,转 ZER0 ;若为正数,求 X+2ADD A,#02HAJMP SAVE ;转到 SAVE,保存数据 ZER0: MOV A,# 64H ;数据为零,Y=100AJMP SAVE ;转到 SAVE,保存数据 NEG: DEC A CPL SAVE: A @R1,A ;保存数据 ;求OXO(减 1 后取反)MOVINC R1 DJNZ SJMP 程序如下: ORG LJMP ORG 0000H MAIN 1000H;地址指针指向下一个地址 R0,START $ ;暂停 ;数据未处理完,继续处理【例】编写程序,实现内部 RAM 30H 单元开始的 20 个单元全部清 0。MAIN: MOV R1,#30H;地址指针赋初值MOV R3,#20 ;循环次数 LOOP: MOV @R1,#0 INC R1 DJNZ R3,LOOP SJMP $ END ;相应地址单元清 0 ;地址指针加 1 ;判断循环是否结束第 3 页 共 87 页 【例】编写程序,利用软件实现 100ms 的延时。假设使用 12MHz 晶振,一个机器 周期为 1us , 利用 DJNZ 指令构成循环, 执行一条 DJNZ 指令需要两个机器周期, 即 2us,适当设置循环次数,便可实现所需延时。 程序如下: ORG LJMP DELAY: 0000H DELAY ; 外循环次数 ; 内循环次数 ; 内循环时间 200 * 2=400 usORG 1000H MOV R7,#250 DELAY1: MOV R6,#200 DELAY2: DJNZ R6,DELAY2 SJMP $ END 【例】 ORG LJMP ORG MAIN: MOV MOV MOV MOV ADD MOV SJMP 子程序: SQR: MOV MOVC RET TAB: DB END 0,1,4,9,16,25,36,49,64,81 DPTR,#TAB ;查表 A,@A+DPTR 编写程序,实现 c=a2+b2 。设 a、b 均小于 10,a、b、c 分别存于 0000H MAIN 0100H SP,#3FH A,30H R1,A A,31H A,R1 32H,A $ ;设置堆栈指针 ;取 a 值 ;调用子函数 ;a2 值暂存 R1 中 ;取 b 值 ;调用子函数 ;a2+b2 ;结果存到 32H 内部 RAM 的 30H,31H,32H 三个单元中。主程序如下:DJNZ R7,DELAY1 ; 外循环时间 0.4 ms * 250=100 msACALL SQRACALL SQR第 4 页 共 87 页 【例】 将 R0 和 R1 指向的内部 RAM 中两个 3 字节无符号整数相加,结果送 到由 R0 指向的内部 RAM 中。入口时,R0 和 R1 分别指向加数和被加数的低位 字节;出口时,R0 指向结果的高位字节。低字节在高地址,高字节在低地址。 实现程序: NADD: MOV CLR NADD1:MOV ADDC MOV DEC DEC DJNZ INC RET 【例】 把内部 RAM 中 20H 单元中的 1 个字节十六进制数转换为 2 位 ASCII 码,存放在 31H 和 32H 两个单元中。 分析: 十六进制数 0~9 的 ASCII 码为 30H~39H, 即十六进制数 (0~9) =ASCII 码-30H;十六进制数 A~F 的 ASCII 码为 41H~46H,即十六进制数(A~F) =ASCII-37H。根据此对应关系,编写如下程序: ;主程序: ORG LJMP ORG MAIN: MOV PUSH POP MOV SWAP PUSH POP SJMP ;子程序: SBU: DEC SP 0000H MAIN 0090H SP,#7FH 20H ;调用子函数 31H A,20H A ACC 32H $ ;设置堆栈指针 C A,@R0 A,@R1 @R0,A R0 R1 R7,NADD1 R0 R7,#3 ;三字节加法 ; ;取加数低字节 ;被加数低字节加 ALCALL SBULCALL SBU第 5 页 共 87 页 DEC POP ANL MOV MOVC PUSH INC INC RET TAB: DB DB ENDSP ACC A,#0FH DPTR,#TAB A,@A+DPTR ACC SP SP 030H,031H,032H,033H,034H,035H,036H,037H 038H,039H,041H,042H,043H,044H,045H,046H【例】 多字节无符号数的加法。 设两个 L 字节的无符号数分别存放在内部 RAM 中以 30H 和 40H 开始的单 元中。相加后的结果要求存放在 40H 数据区。 ;3 字节无符号数相加 ORG 0000H LJMP MAIN ORG 1000H MAIN: MOV MOV MOV CLR LOOP: MOV MOV INC INC SJMP $ END 【例】 多字节无符号数的减法。 设两个 N 字节的无符号数分别存放在内部 RAM 中以 DATA1 和 DATA2 开 始的单元中。相减后的结果要求存放在 DATA2 数据区。 R0,#30H R1,#40H R7,#3 C A,@R0 ;求和 ;保存结果 ;修改指针 @R1,A R0 R1 ;相加的字节数ADDC A,@R1DJNZ R7, LOOP第 6 页 共 87 页 LOOP:MOVMOV R0,#DATA1 MOV R1,#DATA2 MOV R7,#N CLR C A,@R0 ; SUBB A,@R1 MOV @R1,A INC R0 INC R1 DJNZ R7, LOOP; ; ;置字节数 ; ;求差 ;存结果 ;修改指针 ; ;【例】 将 1 位十六进制数转换成相应的 ASCII 码。 设十六进制数存放在 R0 中,转换后的 ASCII 码存放于 R2 中。实现程序 如下: HASC:MOV A,R0 ANL CLR SUBB 间 POP JC ACC LOOP ;弹出原 4 位二进制数 ;借位位为 1,跳转至 LOOP ;借位位为 0,该数在 A~F 之间,加 37H ;该数在 0~9 之间,加 30H ;ASCII 码存于 R2 A,#0FH C A,#0AH PUSH ACC ;取 4 位二进制数 ;屏蔽掉高 4 位 ;4 位二进制数入栈 ;清进(借)位位 ;用借位位的状态判断该数在 0~9 还是 A~F 之ADD A,#07H LOOP:ADD A,#30H MOV R2,A RET【例】 将多位十六进制数转换成 ASCII 码。 设地址指针 R0 指向十六进制数低位,R2 中存放字节数,转换后地址指针 R0 指向十六进制数的高位。R1 指向要存放的 ASCII 码的高位地址。实现程序如 下: HTASC: MOV A,@R0 ANL ADD MOVC MOV INC MOV SWAP第 7 页 共 87 页;取低 4 位二进制数 ; ;偏移量修正 ;查表 ;存 ASCII 码 ; ;取十六进制高 4 位A,#0FH A,#15 A,@A+PC @R1,A R1 A ,@R0 A ANL ADD MOVC MOV INC INC DJNZ RET ASCTAB:DB DBA,#0FH A,#06H A,@A+PC @R1,A R0 R1 R2,HTASC; ;偏移值修正 ; ;指向下一单元 ; ;字节数存于 R230H,31H,32H,33H,34H,35H,36H,37H 38H,39H,41H,42H,43H,44H,45H,46H【例】 双字节二进制数转换成 BCD 码。 设(R2R3)为双字节二进制数,(R4R5R6)为转换完的压缩型 BCD 码。 十进制数 B 与一个 8 位的二进制数的关系可以表示为: 只要依十进制运算法则,将 bi(i=7,6,… …,1,0)按权相加,就可以 得到对应的十进制数 B。(逐次得到:b7× 20;b7× 21+b6× 20;b7× 22+b6× 21+ b5× 20 ;…)。 DCDTH: CLR A ; ;R4 清 0 ;R5 清 0 ;R6 清 0 ;计数初值 ; ; ; ;R3 左移一位并送回 ; ; ;R2 左移一位并送回 ; ; ; ;(R6)乘 2 并调整后送回 ; ; MOV R4,A MOV R5,A MOV R6,A MOV R7,#16 LOOP: CLR MOV RLC MOV MOV RLC MOV MOV DA MOV MOV C A,R3 A R3,A A,R2 A R2,A A,R6 A R6,A A,R5ADDC A,R6ADDC A,R5第 8 页 共 87 页 DA MOV MOV DA MOVA R5,A A,R4 A R4,A; ;(R5)乘 2 并调整后送回 ; ; ; ;(R4)乘 2 并调整后送回ADDC A,R4DJNZ R7,LOOP ; 例:编制一个循环闪烁灯程序.设 51 单片机的 P1 口作为输出口,经驱动电路 74LS240(8 反相三态缓冲驱动器)接 8 只发光二极管。当输出位为“1”时,发光 二极管点亮,“0”时为暗。编程实现:每个灯闪烁点亮 5 次,再转移到下一个灯 闲烁点亮 5 次,一直循环下去。(DELAYY1S 为延时 1 秒子程序) ORG 0000H LJMP MAIN ORG 0100H MAIN: CLR MOV C A,#01H ;为 LED 灯赋值,低位先亮 ;闪烁次数 ;LED 亮 ;延时 1LED 灭 ;延时 1闪烁 5 次 ;左移一位 FLASH ;不断循环FLASH:MOV R2,#5 LOOP: MOV P1,A LCALL DELAYY1S MOV P1,#00H LCALL DELAYY1S DJNZ R2,LOOP RLC SJMP END A【例】 数据端与 P0 口正序连接。 编写程序, 分别实现功能: 上电后数码管显示“P”, 按下任何键后,显示从“0”开始每隔 1 秒加 1,加至“F”后,数码管显示“P”,进入 等待按键状态。第 9 页 共 87 页 K0 K1 K2 K3 K4 K5 K6 K7P3.0 P3.1 P3.2 P3.3 P3.4 P3.5 P3.6 P3.7P0.0 P0.1 P0.2 P0.3 P0.4 P0.5 P0.6 P0.7a b c d e f gdpcom100Ω+5V89S51共阳极图 5.5.1 数码管数据端正序连接图 实现程序如下: TEMP EQU ORG JMP ORG START:MOV MOV MOV CPL JZ MOV MOV MOV MOV LOOP:MOV MOV MOVC MOV INC NOKEY:MOV A NOKEY ;无键按下 TEMP,P3 ;有键按下 A,P3 R7,#16 R2,#0 A,R2 DPTR,#CODE_P0 A,@A+DPTR P0,A R2 30H 0000H START 0100H SP,#5FH P0,#8CH ;显示&P& P3,#0FFH A,P3CALL D10ms CJNE A,TEMP,NOKEY;去抖SETB RS0 ;切换组 CALL D_1S CLR JMP RS0 START DJNZ R7,LOOP第 10 页 共 87 页 D_1S:(子程序) D10ms:(子程序) CODE_P0:DB 0C0H,0F9H,0A4H,0B0H,99H,92H,82H,0F8H DB 80H,90H,88H,83H,0C6H,0A1H,86H,8EH 有时为方便走线而采用逆序连接,如图 5.5.2 所示,显示段码要进行调整: CODE_P2:DB 03H,9FH,25H,0DH,99H,49H,40H,1FH DB 01H,09H,11H,0C1H,63H,85H,61H,71HK0 K1 K2 K3 K4 K5 K6 K7 P3.0 P3.1 P3.2 P3.3 P3.4 P3.5 P3.6 P3.7 P2.7 P2.6 P2.5 P2.4 P2.3 P2.2 P2.1 P2.0a b c d e f gdpcom100Ω+5V89S51共阳极图 5.5.2 数码管数据端逆序连接图第 11 页 共 87 页 第6章例 6.2 设单片机的晶振频率为 12MHz,现要求从 P1.0 输出周期为 500us(频 率为 2kHz),占空比 50%的方波。 方法一:使用定时/计数器 0,方式 0,查询方式 程序代码如下: ORG 0000H AJMP MAIN_START ORG 0100H MAIN_START: MOV TMOD, #00H ;设置计数器工作模式 MOV TH0, #0F8H ;设置初始值 MOV TL0, #06H ; CLR P1.0 ;清 P1.0 为零 SETB TR0 ;启动定时/计数器 0 WAIT: JNB TF0, $ ;查询,等待计数溢出 CLR TF0 ;软件清除溢出标志 MOV TH0, #0F8H ;重新赋初值 MOV TL0, #06H ; CPL P1.0 ;P1.0 取反 AJMP WAIT ;循环 END 方法二:使用定时/计数器 1,方式 1,中断方式 程序代码如下: ORG 0000H AJMP MAIN_START ORG 001BH ;定时/计数器 1 中断服务程序入口地址 AJMP INT_T1_HANDLE ORG 0030H INT_T1_HANDLE: MOV TH1, #0FFH ;重设初始值 MOV TL1, #06H ; CPL P1.0 ;P1.0 翻转 RETI ORG 0100H MAIN_START: MOV TMOD, #10H ;设置计数器工作模式为 1 SETB ET1 ;开定时/计数器 1 中断 MOV TH1, #0FFH ;设置初始值 MOV TL1, #06H ; CLR P1.0 ;清 P1.0 为零 SETB EA ;开全局中断 SETB TR1 ;启动定时/计数器 1第 12 页 共 87 页 WAIT: AJMP $ ;循环等待 END 方法三:使用定时/计数器 1,方式 2,中断方式 程序代码如下: ORG 0000H AJMP MAIN_START ORG 001BH ;定时/计数器 1 中断服务程序入口地址 AJMP INT_T1_HANDLE ORG 0030H INT_T1_HANDLE: CPL P1.0 RETI;P1.0 翻转ORG 0100H MAIN_START: MOV TMOD, #20H ;设置计数器工作模式为 2 SETB ET1 ;开定时/计数器 1 中断 MOV TH1, #06H ;设置初始值 MOV TL1, #06H ; CLR P1.0 ;清 P1.0 为零 SETB EA ;开全局中断 SETB TR1 ;启动定时/计数器 1 WAIT: AJMP $ ;等待中断发生 END 例 6.3 在某个会场上,专门设置了人员入口和出口,为了安全需要,会场 内只允许 1000 人同时在场。当人数到达 1000 人时,入口亮红灯,门卫阻止人员 进入。 现使用 80C51 进行设计,统计人数。为了简化程序,暂时不考虑中途有人 退场的情况。系统使用定时/计数器 0 工作,每次进入一人人员统计的传感器就 向 T0(P3.4)脚输出一个负脉冲。门口的红灯由 P1.0 引脚控制,高电平为灯亮。 本系统中,使用定时/计数器 0 统计外部事件用作计数器,需要选择外部时 钟,不需要使用中断;计数值最大为 1000,使用方式 1 和方式 0 都可以,在此 选用方式 1。把人数统计的高 8 位放在 30H,低 8 位放在 31H。 TMOD=H,计数初值为 0。 程序代码如下: ORG 0000H AJMP MAIN_START ORG 0100H MAIN_START: MOV TMOD, #05H ;设置定时/计数器 0 为方式,计数器模式 MOV TH0, #0 ;计数值清零 MOV TL0, #0 ; CLR P1.0 ;关闭红灯第 13 页 共 87 页 SETB TR0 ;启动计数器 COUNT: MOV 30H, TH0 ;读高 8 位计数结果 MOV 31H, TL0 ;读低 8 位计数结果 MOV A, TH0 ;第二次读高 8 位计数结果 CJNE A, 30H, COUNT ;比较两次读取 TH0 是否一致, 不一 致则重读 CHECK: CLR C ;清零进位标志,为了后面减法正确 SUBB A, #03H ;做减法比较是否小于 768(300H) JC COUNT ;小于则继续计数 MOV A, 31H ;等于 300H,再判断低 8 位是否小于 232。 (232+768=1000) SUBB A, #0E8H ; JC COUNT ;小于继续计数 SETB P1.0 ;等于,则表示达到 1000 人,亮红灯 STOP: AJMP STOP ; END第 14 页 共 87 页 第7章例 1 应用串行口控制的键盘和显示的硬件电路图如图 7.6 所示,试编写程 序。图 7.6 应用串行口控制的键盘和显示的硬件电路图 解:串行口控制的键盘和显示的程序框图如图 7.7 所示。第 15 页 共 87 页 开始初始化显示键盘初始化扫描有键按下吗?延时10ms是否抖动?键盘扫描,判断是那个键按下, 相应寄存器赋偏址差值显示子程序图 7.7 串行口控制的键盘和显示的程序框图 其程序清单如下: (1)初始化显示程序 ORG 0000H SJMP MAIN ORG 0030H MAIN: NOP CSHX: SETB P3.3 ;开放显示 MOV R7,#06H ;显示位数 MOV R0,#00H ; R0 初始化 MOV DPTR,#CSTAB ; 字形代码表首地址 DL0: MOV A,R0 ; 加上偏移量 MOVC A,@A+DPTR ; 取出字形表代码 MOV SBUF,A ; 送出显示 DL1: JNB TI,DL1 ; 输出完成否? CLR TI ; 清中断标志 INC R0 ; 偏移量加 1 DJNZ R7,DL0 ; 显示输出是否结束? CLR P3.3 ; 关显示 SJMP KEY ; CSTAB: DB 7DH,3FH,07H,40H,76H,7CH ;字形表 (2)键盘和显示程序第 16 页 共 87 页 KEY: KL0: KL1: PK1:PK2:MOV MOV JNB CLR JNB JB ACALL JNB JB MOV MOV MOV MOV MOV JNB CLR JNB JB MOV AJMP MOV MOV MOV JNB CLR JNB JNB MOV ADD MOV RL MOV INC DJNZ ACALL AJMPA,#00H SBUF,A TI,KL0 TI P3.4,PK1 P3.5,KL1 D10MS P3.4,PK2 P3.5,KL1 R7,#08H R6,#0FEH R3,#00H A,R6 SBUF,A TI,KL2 TI P3.4,PK0 P3.5,NEXT R4,#08H PK3 R4,#00H A,#00H SBUF,A TI,KL3 TI P3.4,KL4 P3.5,KL4 A,R4 A,R3 A,R6 A R6,A R3 R7,KL5 DISPLAY KEY P3.3 R7,#08H R0,#58H A,@R0;对键盘进行扫描 ; ; 发送结束否? ; 清中断标志 ; 第一排有键闭合吗? ; 第二排有键闭合吗? ; 延时 ; 是否抖动? ; 不是抖动 ; 这一排哪一个键被按 下? ; ; ; 对本排逐行扫描 ; 等待 ; 清中断 ; 是第一排某个键? ; 是第二排某个键? ; 是第二排按下 ; ; 是第一排按下 ;等待释放 ; ; ; ; ; ; 按键释放,取得键值 ; ; ; 判断下一行是否有键按 下 ; ; ; ; ; 调显示子程序 ; ; 显示子程序 ; ; 58H 为显示缓冲区首址 ; 取显示数据KL5: KL2:PK0: PK3: KL3: KL4:NEXT:DISPLAY:DS0:第 17 页 共 87 页SETB MOV MOV MOV DS1:D10MS: DTAB:MOV MOVC MOV JNB CLR INC DJNZ CLR RET Nop RET DBDPTR,#DTAB A,@A+DPTR SBUF,A TI,DS1 TI R0 R7,DS0 P3.3; ; ; ; ; ; ; ;字形表首址 取字形数据 送显示关显示; 延时 10ms 子程序(略)2.双机通信 单片机之间的通信, 除了采用相同的波特率, 通信双方还必须遵循同一协议, 其中简单的通信协议可以自己设计,并按设计的协议编写通信程序。 例 2 由 MCS-51 构成的双机通信系统如图 7.8 所示。1 号机将内部 RAM 的 40H~4FH 中的 16 个无符号随机数通过串行口发送到 2 号机,2 号机将接收到的 1 号机发送过来的数据存放在 RAM 的 40H~4FH 单元, 要求采用累加校验。 设单 片机的晶振频率为 11.0592MHz,波特率为 2400bps,采用串口方式 1,试编写程 序。 解:现分析通信双方约定如下: 设 1 号机是发送方,2 号机是接收方,当 1 号机发送时,先发送一个“E1”联 络信号,2 号机收到后回答一个“E2”应答信号,表示同意接收。当 1 号机收到应 答信号“E2”后,开始发送数据,每发送一个字节数据都要计算“校验和”,假定数 据块长度为 16 个字节,起始地址为 40H,一个数据块发送完毕后立即发送“校验 和”。2 号机接收数据并转存到数据缓冲区,起始地址为 40H,每接收到一个字 节数据都要计算一次“校验和”,当收到一个数据块后再接收 1 号机发来的“校验 和”,并将它与 2 号机求出的校验和进行比较。若两“校验和”相等,说图 7.8 MCS-51 构成的双机通信系统 明接收正确, 2 号机回答 00H; 若两者不相等, 说明接收不正确, 2 号机回答 0FFH, 请求重发。1 号机接收到 00H 后结束发送,若收到 0FFH,则重新发送数据一次。 双方约定采用串行口方式 1 进行通信,一帧信息由 1 个起始位、8 个数据位、1 个停止位共 10 位组成,波特率为 2400bps,T1 工作在定时器方式 2,单片机系 统晶振频率选用 11.0592MHz,查表 7.3 可得,TH1=TL1=0F4H,PCON 寄存器 的 SMOD 位为 0。程序流程图如图 7.9 示。 程序如下: (1)发送程序清单第 18 页 共 87 页 ORG 0100H ASTART: CLR EA MOV TMOD,#20H MOV TH1,#0F4H MOV TL1,#0F4H MOV PCON,#00H SETB TR1 MOV SCON,#50H ALOOP1: MOV SBUF,#0E1H JNB TI,$ CLR TI JNB RI,$ CLR RI MOV A,SBUF XRL A,#0E2H JNZ ALOOP1 ALOOP2: MOV R0,#40H 值 MOV R7,#10H MOV R6,#00H ALOOP3: MOV SBUF,@R0 MOV A,R6 ADD A,@R0 MOV R6,A INC R0 JNB TI,$ CLR TI DJNZ R7,ALOOP3 MOV SBUF,R6 JNB TI,$ CLR TI JNB RI,$ CLR RI MOV A,SBUF JNZ ALOOP2 RET END (2)接收程序清单 ORG 0200H BSTART: CLR EA MOV TMOD ,#20H MOV TH1,#0F4H MOV TL1,#0F4H MOV PCON,#00H第 19 页 共 87 页;定时器 1 工作于方式 2 ;装载定时器初值,波特率为 2400bps ;波特率不加倍 ;启动定时器 T1 ;串行口工作于方式 1,允许接收 ;发送联络信号 ;等待一帧发送完毕 ;允许再发送 ;等待 2 号机的应答信号 ;允许再接收 ;2 号机应答后,读至 A ;判断 2 号机是否准备完毕 ;2 号机未准备好,继续联络 ;2 号机准备好,设定数据块地址指针初 ;设定数据块长度初值 ;清校验和单元 ;发送一个数据字节 ;检验字送累加器 A ;求校验和 ;保存校验和 ;地址单元加 1 ;等待发送完 ;清发送标志位 ;整个数据块是否发送完毕 ;发送校验和 ;等待发送完 ;清发送标志 ;等待 2 号机的应答信号 ;清接受标志 ;2 号机应答,读至 A ;2 号机应答“错误”,转重新发送 ;2 号机应答“正确”,返回;设置定时器 1 工作方式 2 ;设置 T1 初值 ;波特率不加倍 BLOOP1:SETB TR1 MOV SCON,#50H JNB RI ,$ CLR RI MOV A,SBUF XRL A,#0E1H JNZ BLOOP1 MOV SBUF,#0E2H JNZ TI,$ CLR TI MOV R0,#40H MOV R7,#10H MOV R6,#00H开始设置波特率 启动定时器T1;启动 T1 ;设定串口工作方式 1,且准备接收 ;等待 1 号机的联络信号 ;清接收标志位 ;收到 1 号机的信号 ;判断是否为 1 号机联络信号 ;不是 1 号机联络信号,再等待 ;是 1 号机联络信号,发应答信号 ;等待发送完毕 ;清发送标志 ;设定数据块地址指针初值 ;设定数据块长度初值 ;清校验和单元开始设置波特率 启动定时器T1设置串行口工作方式设置串行口工作方式发送“EI”联系信号等待1号机联系信号2号机允许发送?1号机请求发送?指针初始化 校验和清零 发送1个数据字节 求校验和发送应答信号指针初始化 校验和清零 接收1个数据字节 求校验和数据块发送完毕?发送校验和数据块发送完毕?2号机接收正确?发送校验和发送出错标志返回2号机接收正确?返回图 7.9由 MCS-51 构成的双机通信程序流程图 (b) 发送程序流程图 ;等待接收信息 ;清接收标志位 ;读取接收缓冲区内容 ;接收数据转储 ;存储单元加 1 ;求校验和 ;校验和存入 R6 ;判断数据块是否接收完毕(a) 接收程序流程图 BLOOP2: JNZ RI,$ CLR RI MOX A,SBUF MOX @R0,A INC R0 ADD A,R6 MOV R6,A DJNZ R7,BLOOP2第 20 页 共 87 页 END1:JNB CLR MOV XRL JZ MOV JNB CLR MOV RET ENDRI,$ RI A,SBUF A,R6 EDN1 SBUF,#0FFH TI,$ TI SBUF,#00H;完毕,接收 1 号机发来的校验和 ;清接收标志位 ;发送 ;比较校验和 ;校验和相等,跳至发正确标志 ;校验和不相等,发错误标志 ;转重新接收 ;清发送标志位 ;缓冲区清零 ;子程序返回MCS-51 单片机的 I2C 总线接口模拟 I2C 协议规定,数据传送的基本步骤是:主机发出起始信号后,先发出从机 的 8 位地址信息,该信息前 7 位是从机芯片的内部地址,第 8 位是读写信息 ( R / W ),“1”为读,“0”为写;然后进行和从机之间的读写数据传送;最后由主 机发出停止信号,结束数据传送。 图 7.18 为 MCS-51 单片机与 I2C 器件接口。电路中单片机的 P1.0 引脚作为 串行时钟线 SCL,P1.1 引脚作为串行数据线 SDA,通过程序模拟 I2C 串行总线 通信方式。I2C 总线适用于通信速度要求不高而体积 要求较高的应用系统。图 7.18 MCS-51 单片机与 I2C 器件接口 I2C 总线接口模拟子程序 设主机采用 MCS51 单片机,晶体频率 6MHz,则几个典型信号的模拟子程序如 下: 1.开始信号 START: SETB SDA SETB SCL JNB SDA, S0 ; 错误跳转 JNB SCL, S0 ; 错误跳转 NOP ; 延时 CLR SDA NOP ; 延时 NOP ; NOP ;第 21 页 共 87 页 NOP NOP CLR CLR JMP S0: SETB S1: RET 2.发送结束信号 STOP: CLR NOP NOP SETB NOP NOP NOP NOP NOP SETB RET 3.发送应答信号“0” ACK: CLR NOP NOP SETB NOP NOP NOP NOP CLR RET 4.发送非应答信号“1” NAK: SETB NOP NOP SETB NOP NOP NOP NOP CLR第 22 页 共 87 页; ; SCL C S1 C ; 清错误标志 ; 设置错误标志SDA ; SCL ; ; ; ; ; SDASDA ; ; SCL ; ; ; ; SCL;;;SDA ; SCL ; ; ; ; SCL; ; ;; RET I2C 总线的典型应用 I2C 总线器件的优点是体积小,功耗低、占用的 IO 口线少,性能价格比高。 典型的产品如 Atmel 公司的 AT24C02,它和单片机的连接如下图所示。图 7.19 单片机与 I2C 总线器件的接口连线与读写流程 AT24C02是一个2K位串行CMOS E2PROM 内部含有256 个8 位字节,有一 个16 字节页写缓冲器,该器件通过I2C 总线接口进行操作,有一个专门的写保 护功能。 1.管脚描述 SCL 串行时钟:AT24C02串行时钟输入管脚,用于产生器件所有数据发送 或接收的时钟; SDA 串行数据/地址: AT24C02双向串行数据/地址管脚用于器件所有数据的 发送或接收,SDA 是一个开漏输出管脚可与其它开漏输出或集电极开路输出进 行线或。 A0 A1 A2 器件地址输入端:这些输入脚用于多个器件级联时设置器件地址。 WP 写保护:如果WP 管脚连接到Vcc 所有的内容都被写保护,只能读当 WP 管脚连接到Vss 或悬空允许器件进行正常的读/写操作 2.写操作 对于存储在器件中的数据,有不同的写模式。 (1)字节写 在字节写模式下主器件发送起始命令和从器件地址信息给从器件, 在从器件 产生应答信号后, 主器件发送字节地址,主器件在收到从器件的另一个应答信号 后,再发送数据到被寻址的存储单元, 从器件再次应答并在主器件产生停止信 号后开始内部数据的擦写, 在内部擦写过程中不再应答主器件的任何请求。字节 写的时序及程序如下:第 23 页 共 87 页 图7.20字节写时序图WRITE_BYTE: CALL START JC X49 RL A ORL A, #FADDR CLR ACC.0 CALL SHOUT ; 发送器件地址 JC X48 MOV A, ADDR_HI ;发送地址高位 JC X48 MOV A, ADDR_LO ;发送地址地位 CALL SHOUT ; JC X48 MOV A, ZDATA CALL SHOUT ; 发送数据 JC X48 CLR C ; 清错误标志 X48: CALL STOP X49: RET (2)页写 用页写AT24C02可以一次写入16 个字节的数据,页写操作的启动和字节写 一样, 不同在于传送了一字节数据后并不产生停止信号, 主器件被允许发送P=15 个额外的字节, 每发送一个字节数据后,AT24C02产生一个应答位并将字节地址 低位加1,高位保持不变。 如果在发送停止信号之前主器件发送超过P+1个字节地址计数器将自动翻 转,先前写入的数据被覆盖。 接收到P+1字节数据和主器件发送的停止信号后,AT24C02启动内部写周期 将数据写到数据区,所有接收的数据在一个写周期内写入AT24C02。 页写的时序及程序如下:第 24 页 共 87 页 图7.21页写时序图WRITE_BLOCK: CALL START JC X38 RL A ORL A, #FADDR CLR ACC.0 CALL SHOUT ; 发送器件地址 JC X37 MOV A, ADDR_HI CALL SHOUT ; JC X37 MOV A, ADDR_LO CALL SHOUT JC X37 MOV INDEX, #BUFFER ; 获取数据指针 X36: MOV A, @INDEX ; 取数据 CALL SHOUT ; 发送数据 JC X37 INC INDEX ; 调节数据指针 DJNZ KOUNT, X36 CLR C X37: CALL STOP X38: RET 3. 读操作 对AT24C02读操作的初始化方式和写操作时一样,仅把R/W 位置为1。 有 三种不同的读操作方式:立即地址读、选择读和连续读。 (1)立即地址读 AT24C02的地址计数器内容为最后操作字节的地址加1,也就是说如果上次 读/写的操作地址为N, 则立即读的地址从地址N+1 开始,如果N为器件的最后一 个地址,对24C02,N=255 ,则计数器将翻转到0,且继续输出数据。AT24C02 接收到从器件地址信号后, 它首先发送一个应答信号,然后发送一个8 位字节数 据。主器件不需发送一个应答信号,但要产生一个停止信号。 立即地址读的时序及程序如下:第 25 页 共 87 页 图7.22 立即地址读时序图 READ_CURRENT: CALL START JC X45 RL A ORL A, #FADDR SETB ACC.0 CALL SHOUT JC X44 CALL SHIN ; 接收数据 CALL NAK CLR C X44:CALL STOP X45:RET (2)选择性读 选择性读操作允许主器件对寄存器的任意字节进行读操作。 主器件首先通过 发送起始信号从器件地址和它想读取的字节数据的地址,执行一个伪写操作在 AT24C02应答之后,主器件重新发送起始信号和从器件地址。此时R/W 位置1, AT24C02 响应并发送应答信号,然后输出所要求的一个8 位字节数据,主器件 不发送应答信号但产生一个停止信号。选择性读时序图如下图所示。图7.23 选择性读时序图 相应的读程序如下: READ_RANDOM: PUSH B MOV B, A ; 保存地址 ; 发送一个伪写操作 CALL START JC X47 RL A第 26 页 共 87 页 ORL A, #FADDR CLR ACC.0 CALL SHOUT JC X46 MOV A, ADDR_HI CALL SHOUT JC X46 MOV A, ADDR_LO CALL SHOUT JC X46 MOV A, B CALL READ_CURRENT JMP X47 X46:CALL STOP X47:POP B RET (3)连续读 连续读操作可通过立即读或选择性读操作启动。在 AT24C02 发送完一个 8 位字节数据后,主器件产生一个应答信号来响应,告知 AT24C02 主器件要求更 多的数据,对应每个主机产生的应答信号 AT24C02 将发送一个 8 位数据字节。 当主器件不发送应答信号而发送停止位时结束此操作。 从 AT24C02 输出的数据按顺序由 N 到 N+1 输出。读操作时地址计数器在 AT24C02 整个地址内增加,这样整个寄存器区域在可在一个读操作内全部读出。 当读取的字节超过 E=255 计数器将翻转到零并继续输出数据字节。连续写的时 序图如图 7.24 所示。图7.24 连续读时序图 READ_BLOCK: CALL START JC X35 RL A ORL A, #FADDR MOV INDEX, A CLR ACC.0 CALL SHOUT JC X34 MOV A, ADDR_HI CALL SHOUT ;第 27 页 共 87 页 JC X34 MOV A, ADDR_LO CALL SHOUT ; JC X34 ; 发送读命令并接收数据. CALL START JC X34 MOV A, INDEX SETB ACC.0 CALL SHOUT JC X34 MOV INDEX, #BUFFER X31:CALL SHIN MOV @INDEX, A CJNE KOUNT, #1, X32 CALL NAK JMP X33 X32:CALL ACK INC INDEX DJNZ KOUNT, X31 X33: CLR C X34: CALL STOP X35: RET 例题: 将 AT24C02 的所有存储单元写入相同的数值。 解: ORG 0000H JMP ON_RESET ORG 0080H USING 0 ON_RESET: MOV SP, #(STACK-1) SETB SDA ; HIGH SETB SCL ; HIGH CALL BYTE_FILL JC FAULT FAULT: JMP $ BYTE_FILL: MOV ZDATA, #FILL ; 设置需要写入的数据 MOV DPTR, #0 ; 设置地址初值 X51:MOV ADDR_LO, DPL MOV ADDR_HI, DPH ;第 28 页 共 87 页 MOV X52:MOV CALL JNC DJNZ SETB JMP X53:INC MOV CJNE MOV CJNE CLR X54: ENDB, #120 A, #PADDR WRITE_BYTE X53 B, X52 N C G X54 DPTR A, DPL A, #(LOW SIZE), X51 A, DPH A, #(HIGH SIZE), X51 C RET第 29 页 共 87 页 SPI 总线应用 AT93C46 是 ATMEL 公司 SPI 总线方式的同步串行 EEPROM 芯片,采用 CMOS 技术,具有 1K 位的存储容量,即 128 字节,能够重复写 100 万次,字节 写入时间最大 10ms。和并行的 EEPROM 芯片相比,串行 EEPROM 芯片的数据 传送速度较低,但其引脚少、体积小、抗干扰能力强,所以特别适用于存放非挥 发数据、占用资源少、面积小、传送速度要求不高的单片机系统中。AT93C46 的引脚排列与内部结构如图 7.27 所示。图 7.27 AT93C46 的引脚排列与内部结构图 1.AT93C46 的引脚功能: CS ―― 片选,高电平有效 SK ―― 同步时钟,上升沿触发 DI ―― 数据输入 DO―― 数据输出 ORG―― 接地输出 8 位数据,接高电平或悬空输出 16 位数据 DC ―― 空脚, VCC ―― +5V, GND―― 0V 2.AT93C46 的控制指令 AT93C46 的 7 条控制指令见表 7.8。 表 7.8 AT93C46 控制指令集 起始 字节地 字地 字节数 字数 指令 操作码 内容 位 址 址 据 据 10XXX 10XX 所有存储单 ERAL 1 00 空 XX XX 元擦除 ERAS 指定单元擦 1 11 A6-A0 A5-A0 空 E 除 00XXX 00XX EWDS 1 00 空 写禁止 XX XX EWE 11XXX 11XX 1 00 空 写允许 N X X READ 1 10 A6-A0 A5-A0 空 指定单元读 WRA 01XXX 01XX D15- 所有存储单 1 00 D7-D0 L XX XX D0 元写入 WRIT D151 01 A6-A0 A5-A0 D7-D0 指定单元写 E D0第 30 页 共 87 页 指令格式排列分三部分:(1)起始位和操作码。SB 是指令起始位,该位 为逻辑“1”,起始位之后跟操作码; (2)操作地址部分。操作码后面接操作地址, 若操作的对象是字节,则地址位 A6~A0,共 7 位;若操作的对象是字,则地址位 A5~A0,共 6 位;(3)操作数据部分。 7 条指令的含义是: (1)写允许指令(EWEN)。允许对芯片进行擦/写操作。芯片上电后自动 进入禁止擦写状态,故在进行擦/写操作之前,须先执行该指令。 (2) 指定单元擦指令(ERASE)。对指定地址进行擦除操作,即使指定单 元内容为“FFH”。 擦除期间,DO 引脚电平为“0”,擦除结束,DO 引脚电平为“1”。 (3) 指定单元写(WRITE)指令。 对指定地址进行写操作,即将数据 通过 DI 写入使指定单元,先写最高位,最后写最低位。写字节最大用时 10ms。 (4) 指定读(READ)指令。对指定地址进行读操作,即将指定单元数据 通过 DO 读出,先读出最高位,最后读出最低位。 (5) 所有存储单元擦除(ERAL)指令。 将芯片整体擦除。即使全体单元 内容为“FFH”。 (6)所有存储单元写(WRAL)指令。 对芯片整体进行写操作。 (7)写禁止(EWDS)指令。 该指令可对以写入的数据进行写保护。 AT93C46 和 MCS-51 的连接有两种方式, 一种是四线制, 将 SPI 芯片的 CS、 SK、DI 和 DO 引脚分别连接到 MCS-51 的 4 个 I/O 端口位;另一种是三线制,是将 SPI 芯片的 DI 和 DO 并联接后作为一条线连接到 MCS-51 的一个端口位。AT93C46 和 MCS-51 的四线制 连接如图 7.28。图 7.28 AT93C46 和 MCS-51 的连接 将 MCS-51 RAM 区首地址 40H 开始的 8 个字节数据写入到 AT93C46 中首 地址 40H 开始的存储区中。汇编程序如下。 ;-------------------------赋值定义----------------------------------DATA EQU 60H ;操作数据存 放单元 DATA ADDR EQU 62H ;操作 EEPROM 地 址存放单元 ADDR CS BIT P1.0 ;SPI 片选 SK BIT P1.1 ;移位时钟线 DI BIT P1.2 ;SPI 输入线 DO BIT P1.3 ;SPI 输出线 ;--------------------------AT93C46 初始化---------------------------第 31 页 共 87 页 CLR CLR SET SET LCALL 序 LCALL MOV 40H MOV MOV MOV MOV LCALL INC INC 加CS SK DI DO WRNUM EWEN ADDR,#40H B,#08H R0,#40H A,@R0 DATA,A WRITE R0 ADDR ;调用“写数据设置”子程 ;调用“写允许”子程序 ; EEPROM 数据首地址, ;待写数据个数 ;MCS-51 数据首地址,40H ;取出待写数据 ;待写数据送入 DATA 寄存 ;调用字节写子程序 ;待写数据地址加 ;EEPROM 被写数据地址LOOP: 器DJNZ B,LOOP ;写循环 SJMP $ ;-----------------写数据设置子程序----------------------------------;此处添入要写到 AT93C46 的数据 WRNUM: MOV 40H,#00H MOV 41H,#01H MOV 42H,#02H MOV 43H,#03H MOV 44H,#04H MOV 45H,#05H MOV 46H,#06H MOV 47H,#07H RET ;-----------------------写允许子程序---------------------------------EWEN: SETB CS MOV A,#100 B ;写允许指令(高字节位) MOV B,#03 ;写允许指令高字节 3 位 LCALL OUTDATA ;调用数据输出子程序 MOV A,#B ;写允许指令(低 字节位) MOV B,#07 ;写允许指令低字节 7 位 ;写允许指令格式 #100 1100000B LCALL OUTDATA ;调用数据输出子程序 CLR CS ;芯片等待 RET第 32 页 共 87 页 ;----------------------数据输出子程序--------------------------------;MCS-51 输出字节数据到 AT93C46,输出数据在 A,输出位数在 B OUTDATA: PUSU B ;保护 B EE3: RR DJNZ EE4: A B,EE3;待输出数据最高位到 ACC.7LCALL DOOUT ;调用字节输出执行子程序 SETB DO ;释放 DO 线 POP B ;恢复 B RET ;----------------------位输出子程序----------------------------------;长度由 B 内容决定 DOOUT: CLR SK RLC A ;待输出字节最高位到 C MOV DI,C ;DI 总线输出数据 NOP ;延时 SETB SK DJNZ B,DOOUT RET ;------------------------ 字节写子程序-----------------------------; EEPROM 被写地址在 ADDR,待写数据在 DATA,字节长度 由 B 的内容决定。 WRITE: PUSH B ;保存 B SET CS ;片选 MOV A,#101B ;写指令高 3 位 MOV B,#3 ;“写指令” 3 位码 LCALL OUTDATA ;调用“数据输出子程序” MOV A,ADDR ;地址 MOV B,#7 ;7 位地址 LCALL OUTDATA ;调用“数据输出子程 序”, MOV A,DATA ;待写数据送 DPL MOV B,#8 ;8 位数据 LCALL OUTDATA ;调用“数据输出子程序” CLR CS ;EEPROM 芯片内 部写,等待 LCALL DELAY10ms ; 调用延时子程序 10ms, 略 POP B RET END CAN 总线 MODE EQU 0100H第 33 页 共 87 页 BTR0 EQU MODE+6 BTR1 EQU MODE+7 ACR0 EQU MODE+16 AMR0 EQU MODE+20 OCR EQU MODE+8 RBSA EQU MODE+30 CDR EQU MODE+31 MOV A,#01H MOV DPTR,#MODE MOVX @DPTR,A MOV A,#0C0H MOV DPTR,# CDR MOVX @DPTR,A MOV A,#1AH MOV DPTR,# OCR MOVX @DPTR,A MOV A,#85H MOV DPTR,# BTR0 MOVX @DPTR,A MOV A,#0B4H MOV DPTR,# BTR1 MOVX @DPTR,A MOV A,#00H MOV DPTR,# RBSA MOVX @DPTR,A MOV A,#0FFH MOV DPTR,# AMR0 MOV R0,#04H BEGIN10:MOVX @DPTR,A INC DPTR DJNZ R0,BEGIN10 MOV A,#00H MOV DPTR,#MODE MOVX @DPTR,A第 34 页 共 87 页 第8章例8.9已知显示数字十六进制的形式存储在片内RAM 50H(高位)、51H中, 试将其转换为5位共阴字段码,将字段码存在以30H(高位)为首址的片内RAM 中。 要完成这个功能,可以连续调用下列二个子程序即可。 ⑴ 分离显示数各位数字子程序 TT1: MOV MOV MOV MOV MOV R0,#30H A,50H B,51H R6,#27H R5,#10H ;置万位BCD码间址 ;置被除数 ; ;置除数10000 = 2710H ; ;除以10000,万位商存30H,余数存A、B ;置除数1000 = 03E8H ; ;指向千位商间址(31H) ;除以1000,千位商存31H,余数存A、B ;置除数100 ; ;指向百位商间址(32H) ;除以100,百位商存32H,余数存A(B=0) ;置除数10 ;除以10 ;指向十位商间址(33H) ;十位商存33H ;读个位数 ;指向个位间址(34H) ;个位存34H ;LCALL SUM MOV MOV INC R6,#03H R5,#0E8H R0LCALL SUM MOV MOV INC R6,#0 R5,#100 R0LCALL SUM MOV DIV INC MOV XCH INC MOV RET B,#10 AB R0 @R0,A A,B R0 @R0,A第 35 页 共 87 页 说明:SUM是16位除以16位子程序:(A、B)÷ (R6、R5)=商(@R0),余数(A、 B)。 ⑵ 转换显示字段码子程序 TT2: MOV MOV CGLP: MOV MOVC MOV INC CJNE RET TAB: DPTR,#TAB R0,#30H A,@R0 A,@A+DPTR @R0,A R0 R0,#35H,CGLP ;置共阴字段码表首址 ;置显示数据区首址 ;取显示数字 ;读相应显示字段码 ;存显示字段码 ;指向下一显示数字 ;判5个显示数字转换完否?未完继续 ;转换完毕,结束DB 3FH,06H,5BH,4FH,66H ;共阴字段码表 DB 6DH,7DH,07H,7FH,6FH ;并行扩展的LED静态显示电路如图8.18所示。图中有三个共阳极的静态显示 数码管,每个数码管都由一个八位的输出口控制,采用的三片74377来扩展的 并行I/O口线,同时也起到对数码管的驱动作用,因为是静态显示,因此较小 的驱动电流就可以得到较高的显示亮度。 每一片74377的地址由P2口的三根线 P2.5、P2.6、P2.7来决定,根据连线可以得到个十百位的地址分别为7FFFH、 BFFFH和DFFFH。单片机的写信号 WR 控制对每片74377的数据的写入。每片 74377的输入均与80C51的P0口相连接即可。第 36 页 共 87 页 +5VR COM COM COM80C51Dpg? ?baDpg? ?baDpg? ?baP0.0~P0.7 /WR P2.5 P2.6 P2.78Q7 Q6 Q1 Q0 74377 (百位) CLK /CE D0~D7Q7 Q6 Q1 Q0 74377 (十位) D0~D7 CLK /CEQ7 Q6 Q1 Q0 74377 (个位) D0~D7 CLK /CE图8.18 LED静态显示电路 例8.9按图8.18编制显示子程序,显示数(≤255)存在片内RAM 30H单元中。 解: DIR1: MOV MOV DIV MOV MOVC MOV MOVX MOV MOV DIV MOV MOVC MOV MOVX MOV MOV MOVC第 37 页 共 87 页A,30H B,#100 AB DPTR,#TAB A,@A+DPTR DPTR,#0DFFFH @DPTR,A A,B B,#10 AB DPTR,#TAB A,@A+DPTR DPTR,#0BFFFH @DPTR,A A,B DPTR,#TAB A,@A+DPTR;读显示数 ;置除数 ;产生百位显示数字 ;置共阳字段码表首址 ;读百位显示符 ;置74377(百位)地址 ;输出百位显示符 ;读余数 ;置除数 ;产生十位显示数字 ;置共阳字段码表首址 ;读十位显示符 ;置74377(十位)地址 ;输出十位显示符 ;读个位显示数字 ;置共阳字段码表首址 ;读个位显示符 MOV MOVX RET TAB:DPTR,#7FFFH @DPTR,A ;;置74377(个位)地址 ;输出个位显示符DB 0C0H,0F9H,0A4H,0B0H,99H;共阳字段码表 DB 92H,82H,0F8H,80H,90H;我们用74LS164扩展静态显示数码管的连接电路电路见图8.20。 74164的输入 口A和B并接在单片机的串口RXD端,RXD端作为移位数据输出端。CLK信号由 单片机的TXD端和P1.0相与的结果来提供,TXD端提供移位时钟信号,P1.0则作 为数码管允许控制信号线。+5V R COM 80C51 COM COMA b Q0 Q1 RXDA B? ?g DpA b Q0 Q1A B? ?g DpA b Q0 Q1A B? ?g DpQ6 Q7 74164 (百位) CLK CLRQ6 Q7 74164 (十位) CLK CLRQ6 Q7 74164 (个位) CLK CLR +5VTXD P1.0&图8.20 串行口静态显示电路 例8.9按图8.20编制显示子程序,显示字段码高位到底为依次存在片内 RAM32H~30H中。 DIR2: MOV CLR SETB MOV JNB CLR MOV JNB CLR第 38 页 共 87 页SCON,#00H ES P1.0 SBUF,30H TI,$ TI SBUF,31H TI,$ TI;置串口方式0 ;串口禁中断 ;“与”门开,允许TXD发移位脉冲 ;串行输出个位显示字段码 ;等待串行发送完毕 ;清串行中断标志 ;串行输出十位显示字段码 ;等待串行发送完毕 ;清串行中断标志 MOV JNB CLR CLR RETSBUF,32H TI,$ TI P1.0;串行输出百位显示字段码 ;等待串行发送完毕 ;清串行中断标志 ;“与”门关,禁止TXD发移位脉冲+5VG1 /G2A /G2B P1.0 P1.1 P1.2 A B C/Y0 /Y1 /Y2 /Y3 /Y4 /Y5 /Y6 /Y7P2.7 /WR P0 /E74377CLK Q0~Q7 D0~D7Dpgfedcba88图8.23 共阴极型8位动态显示电路 例8.11 按图8.23,试编制动态显示子程序,已知显示字段码存在以30H(低 位)为首地址片内RAM中。 编程如下: DIR4:MOV MOV DLP1:ANL MOV DLP2:MOV MOVX R7,#n DPTR,#7FFFH ;置循环扫描次数n( n ? 255 ) ;置74377口地址P1,#B ;第0位先显示 R0,#30H A,@R0 @DPTR,A ;置显示字段码首址 ;读显示字段码 ;输出显示字段码 ;调用2ms延时子程序 ;指向下一位字段码 ;选通下一位显示 ;判8位扫描显示完否?未完继续 ;8位扫描显示完毕,判n次循环完否? ;n次循环完毕,显示暗LCALL DALAY2ms INC INC CJNE DJNZ CLR第 39 页 共 87 页R0 P1 R0,#38H,DLP2 R7,DLP1 A MOVX RET@DPTR,A; ;子程序返回例8.12根据图8.24电路,试编制动态扫描显示程序(循环n次),已知显示字段码 存在以30H(低位)为首地址的片内RAM中。 编程如下: DIR5: MOV MOV DIR50: SETB MOV MOVX CLR DPTR,#0BFFFH R7,#n P1.2 A,30H @DPTR,A P1.0 ;置74377地址 ;置循环显示次数n( n & 255 ) ;百位停显示 ;取个位字段码 ;输出个位字段码 ;个位显示 ;调用延时2ms子程序 ;个位停显示 ;取十位字段码 ;输出十位字段码 ;十位显示 ;延时2ms ;十位停显示 ;取百位字段码 ;输出百位字段码 ;百位显示 ;延时2ms ;判循环显示结束否?未完继续LCALL DY2ms DIR51: SETB MOV MOVX CLR P1.0 A,31H @DPTR,A P1.1LCALL DY2ms DIR52: SETB MOV MOVX CLR P1.1 A,32H @DPTR,A P1.2LCALL DY2ms DJNZ ORL RET 单片机与液晶显示模块接口电路 R7,DIR50P1,#B ;3位灭显示 ;液晶显示模块与MCS-51单片机有二种接口方式:直接访问方式和间接控制 方式。所谓直接访问就是将液晶显示模块的接口作为存储器或者I/O口设备直接 挂在单片机总线上,单片机以访问存储器或I/O口设备的方式对液晶显示模块进第 40 页 共 87 页 行操作。 间接控制方式是通过单片机自身或者系统的并行接口与液晶显示模块相 连接,如80C51的P1口、P3口,或接口芯片81C55,单片机通过对这些接口芯片 的操作, 达到对液晶显示模块的控制。下面分别给出直接访问方式和间接控制方 式的连接图如下。图8.26为直接访问方式连接电路图,8.26为间接控制方式连接 电路。 直接访问方式的编程简单但是由于P0口本身带负载的能力有限, 通常在实 际应用中需要加入缓冲电路。 间接控制方式连接电路简单,简化了系统的设计工 作,但是程序的编写要复杂一点。P0.0-P0.7 P2.0 P2.1 80C51 P2.2 P2.3 RD WR 74LS00 DB0-DB7 R/W E MGLS-12864 CSA CSB D/I图8.26液晶显示模块直接访问方式连接电路P00-P07 P30 P31 P32 P33 P34 80C51 P35 P36 P37 PSEN ALE/P EA/VP 10 11 12 13 14 15 16 17 29 30 31 VCC VCC GND GND VV08DB0-DB7 D/I R/W E CSB CSA MGLS-12864图 8.27 液晶显示模块间接控制方式连接电路 由于直接访问方式的程序编写和访问片外存储器和I/O口的方式相同,在此 不再介绍。 我们以间接控制的方式为例来介绍程序的编写。通常情况下液晶模块 生产厂家会提供相应的驱动程序, 这里引用了清华蓬远科贸公司液晶驱动器的驱第 41 页 共 87 页 动程序。MGLS12864液晶显示模块的列驱动有左右两块芯片电路,分别对应显 示器的左右两部分区域, 因此编写显示驱动程序时也分左右两部分。我们以左驱 动芯片的程序为例来进行说明。 左边区域驱动程序包含写指令、写数据、读数据三个子程序。 1)左边区域写指令子程序 COM DAT1 WLCOM: CLR SETB CLR SETB WLCOM1: MOV EQU EQU P3.4 P3.3 P3.0 P3.1 P0,#0FFH P3.2 A, P0 P3.2 ACC.7,WLCOM1 50H 51H ;指令码寄存器 ;数据寄存器 ; CSA =0, ; CSB =1,选中左芯片 ;D/I =0,进行指令操作 ;R/W=1,进行读操作 ;P0口置“1” ;E=1,芯片使能有效 ;读状态字 ;E=0,芯片使能无效 ;该位为“1 ”表示“忙”,SETB MOV CLR JB 再重读 CLR MOV SETB CLR RET 2) 左边区域写数据子程序 WLDAT: CLR SETB CLR SETB WLDAT1:P3.1 P0,COM P3.2 P3.2;R/W=0,芯片写选通 ;写指令代码 ;E=1 ;E=0P3.4 P3.3 P3.0 P3.1; CSA =0, ; CSB =1,选中左芯片 ;D/I =0,进行指令操作 ;R/W=1,进行读操作 ;P0口置“1” ;E=1,芯片使能有效MOV P0,#0FFH P3.2SETB第 42 页 共 87 页 MOV CLR JB 重读 SETB CLR MOV SETB CLR RET 3)读左区数据子程序 RLDAT: CLR SETB CLR SETB RLDAT1:MOV SETB MOV CLR JB 重读 SETB MOV SETB MOV CLR RETA, P0 P3.2 ACC.7,WLDAT1;读状态字 ;E=0,芯片使能无效 ;该位为“1 ”表示“忙”,再P3.0 P3.1 P0,DAT1 P3.2 P3.2; D/I =1,进行数据操作 ;R/W=0,芯片写选通 ;写数据 ;E=1,芯片使能有效 ;E=0,芯片使能无效P3.4 P3.3 P3.0 P3.1 P0,#0FFH P3.2 A, P0 P3.2 ACC.7,RLDAT1; CSA =0, ; CSB =1,选中左芯片 ;D/I =0,进行指令操作 ;R/W=1,进行读操作 ;P0口置“1” ;E=1,芯片使能有效 ;读状态字 ;E=0,芯片使能无效 ;该位为“1 ”表示“忙”,再P3.0 P0,#0FFH P3.2 DAT1,P0 P3.2; D/I =1,进行数据操作 ;P0口置“1” ;E=1,芯片使能有效 ;读数据 ;E=0,芯片使能无效右边区域驱动程序也一样包括写指令、写数据、读数据三个子程序。我们可 以仿照左边区域驱动程序来编写。在此不再重复。第 43 页 共 87 页 4)初始化子程序INT 初始化子程序完成显示起始行的设置并打开显示器开始工作。 INT: 行 LCALL 程序 LCALL 子程序 MOV LCALL LCALL RET 5)清显示数据存储区(清屏)子程序 该程序用于将显示模块数据存储区全部清0。屏幕左、右区存储区均分成8 个页面,每页有64个字节。 CLEAR: MOV CLEAR1: MOV ORL 形成0页地址 MOV COM,A ;页面地址设置 ;写入左区 ;写入右区 ;初始列地址设置为“0” ;写入左区 ;写入右区 ;一页清64个字节 ;显示数据为“0” ;左区清0 ;右区清0 R7,#00H A,R7 A,#0B8H ;“或”页面地址设置代码, ;页面地址暂存器设置 COM,#3FH WLCOM WRCOM ;开启显示 ;左区开显示 ;右区开显示 WRCOM ;调用右边区域写指令 WLCOM ; 调用左边区域写指令子 MOV COM,#0C0H ;设置显示起始行为第一LCALL WLCOM LCALL WRCOM MOV COM,#40HLCALL WLCOM LCALL WRCOM MOV CLEAR2: MOV R3,#40H DAT1,#00HLCALL WLDAT LCALL WRDAT第 44 页 共 87 页 DJNZ INCR3,CLEAR2 R7;页内64个字节没清完继续 ;页地址暂存器加1,清下一页CJNE R7,#08H,CLEAR1 ;8页RAM区清完退出,否 则继续 RET 例8.13 根据图8.27的连接电路图编写程序, 要求分两行显示“少壮不努力 老大徒 伤悲” 参考源程序如下: ORG 0000H EQU EQU EQU 50H 51H 30H ;指令码寄存器 ;数据寄存器 ;列地址(左、右区各64列,COM DAT1 XADR 地址为0~127) YADR CODEEQU EQU31H 32H 33H;行地址 D2,D1,D0: ;字符代码寄存器 ;计数器COUNT1 EQU AJMP MAINORG 0030H MAIN:MOV SP,#60H ;设栈指针MOV P3,#0C3H LCALL INT 示 LCALL CLEAR LJMP AQ:SJMP DISCHA:MOV MOV MOV LCALL DISCHA AQ YADR,#01H XADR,#15H CODE,#00H XSCHR ;第1页 ;第21列 ;汉字代码 ;清屏 ;调显示汉字子程序 ;初始化,设起始行,开显第 45 页 共 87 页 MOV MOV MOV LCALL MOV MOV MOV LCALL MOV MOV MOV LCALL MOV MOV MOV LCALL MOV MOV MOV LCALL MOV MOV MOV LCALL MOV MOV MOV LCALL MOVYADR,#01H XADR,#29H CODE,#01H XSCHR YADR,#01H XADR,#3DH CODE,#02H XSCHR YADR,#01H XADR,#52H CODE,#03H XSCHR YADR,#01H XADR,#66H CODE,#04H XSCHR YADR,#04H XADR,#15H CODE,#05H XSCHR YADR,#04H XADR,#29H CODE,#06H XSCHR YADR,#04H XADR,#3DH CODE,#07H XSCHR YADR,#04H;第1页 ;第41列 ;汉字代码;第1页 ;第61列 ;汉字代码;第1页 ;第82列 ;汉字代码; 第1页 ;第102列 ; 汉字代码;第4页 ;第21列;第4页 ;第41列 ;汉字代码;第4页 ;第61列 ;汉字代码;第4页第 46 页 共 87 页 MOV MOV LCALL MOV MOV MOV LCALL RETXADR,#52H CODE,#08H XSCHR YADR,#04H XADR,#66H CODE,#09H XSCHR;第82列 ;汉字代码;第4页 ;第102列 ;汉字代码下面是16*16点阵中文写入子程序XSCHR XSCHR: 地址 MOV MOV MUL ADD MOV 码x 32 MOV ADDC MOV PUSH PUSH MOV 寄存器 XSC1: MOV MOV ANL ORL MOV COUNT1,#10H A,YADR A,#07H A,#0B8H COM,A ;计数器设置为16 ;读页地址寄存器 ;取有效页地址 ;“或”页地址设置代码 ;写页地址设置指令 A,B A,DPH DPH,A XADR XADR CODE,#00H ;列地址入栈 ;列地址入栈 ;代码寄存器借用为间址 A,CODE B,#20H AB A,DPL DPL,A ;取代码 ;字模块宽度为32个字节 ;代码x32 ;字符字模块首地址 ;= 字模库首地址 + 代 MOV DPTR,#CCTAB ;确定字符字模块首第 47 页 共 87 页 LCALL LCALL POP MOV CLR SUBB JC MOV MOV SETB MOV XSC2: MOV ORL 位 MOV 置哪个控制器 ANL XSC31: CJNE LCALL LJMP XSC32: XSC4: LCALL MOV MOVC MOV MOV ANL XSC41: CJNE LCALL LJMPWLCOM WRCOM XADR A,XADR C A,#40H XSC2 XADR,A A,YADR ACC.5 YADR,A COM,XADR COM,#40H;写左区页命令 ;写右区页命令 ;取列地址值 ;读列地址寄存器;列地址-64 ;& 0为左屏显示区域 ;≥ 0为右屏显示区域;设置区域标志位. ;“00”为左,“10”为右 ;设置列地址值 ;“或”列地址指令标志A,YADR;判区域标志以确定设A,#30H A,#20H,XSC32 WRCOM XSC4 WLCOM A,CODE A,@A+DPTR DAT1,A A,YADR A,#30H A,#20H,XSC42 WRDAT XSC5 ;“10”为右区 ;写右区数据 ;“00”为左区 ; 取间址寄存器值 ; 取汉字字模数据 ; 写数据 ; 判区域标志 ; “10”为右区 ;写右区列命令第 48 页 共 87 页 XSC42: XSC5:LCALL INC INC MOVWLDAT CODE XADR A,XADR;“00”为左区 ;间址寄存器加1 ;列地址寄存器加1 ;判列地址是否超出区域范围 CJNE XSC6: JC MOV MOV JB XSC61: SETB MOV MOV LCALL XSC7: XSC8: DJNZ MOV JB 则完成退出 INC SETB ANL MOV MOV LJMP XSC9: RET DELAY:MOV MOV DELAY1: NOP DJNZ R5,DELAY1 R6,#00H R5,#00H ; 延时子程序 A ACC.7 A,#0CFH YADR,A CODE,#10H XSC1 ;间址寄存器设置为16 ;大循环 ;否则页地址加1 ;置完成位为&1& ;清区域标志 A,#40H,XSC6 XSC7 XADR,#00H A,YADR ACC.5,XSC8 ACC.5 YADR,A COM, #40H WRCOM COUNT1,XSC4 A,YADR ACC.7,XSC9 ;当页循环 ;读页地址寄存器 ;判完成标志D7位,“1” ; 设置右区列地址为“0” ;超出则判在何区域 ;在右区域则退出 ;在左区则转右区 ;未超出则继续第 49 页 共 87 页 DJNZ RETR6,DELAY1CCTAB:DB 000H,000H,080H,060H,018H,000H,000H,0FFH DB 000H,000H,008H,090H,020H,060H,000H,000H DB 000H,081H,080H,080H,040H,040H,020H,017H DB 008H,004H,002H,001H,000H,000H,000H,000H DB 008H,030H,000H,0FFH,040H,040H,040H,040H DB 040H,0FFH,040H,040H,040H,060H,040H,000H DB 004H,006H,001H,0FFH,000H,020H,020H,020H DB 020H,03FH,020H,020H,030H,020H,000H,000H …… 大徒伤 DB 082H,092H,092H,092H,092H,0FFH,000H,000H DB 000H,0FFH,092H,092H,09AH,0D3H,082H,000H DB 020H,018H,000H,03CH,040H,041H,044H,048H DB 058H,041H,040H,070H,000H,008H,030H,000H END; 少; 壮;不努力,老; 悲例8.14 根据图8.30(a)、(b)所连接的电路图,编写按键扫描子程序。 解:我们以图中(a)图为例来进行程序的编程: KEYA:ORL MOV CPL ANL JZ P1,#0FH A,P1 A ;置P1.0~P1.3为输入态 ;读键值,键闭合相应位为0 ;取反,键闭合相应位为1A,#B;屏蔽高4位,保留有键值信息的低4位 GRET ;全0,无键闭合,返回 ;非全0,有键闭合,延时10ms,软件去抖动 ;重读键值,键闭合相应位为0 ;取反,键闭合相应位为1LCALL DY10ms MOV CPL ANL JZ A,P1 AA,#B;屏蔽高4位,保留有键值信息的低4位 GRET ;全0,无键闭合,返回;非全0,确认有键闭合第 50 页 共 87 页 JB JB JB JB GRET:RETAcc.0,K0 Acc.1,K1 Acc.2,K2 Acc.3,K3;转0#键功能程序 ;转1#键功能程序 ;转2#键功能程序 ;转2#键功能程序K0:LCALL key0 RET K1:LCALL key1 RET K2:LCALL key2 RET K1:LCALL key3 RET;执行0#键功能子程序;执行1#键功能子程序;执行2#键功能子程序;执行3#键功能子程序例8.14 按图8.32所示的矩阵式键盘结构图,试编制该键盘程序控制扫描程 序。 程序编写如下: KEY: KEY0: MOV MOV CPL ANL MOV JZ KEY1: MOV MOV CPL ANL MOV JZ JBC A A,#0F0H R1,A GRET P1,#0FH A, P1 A A, #0FH R2,A GRET F0,WAIT P1,#0FH A,P1 ;行线置低电平,列线置输入状态 ;读列线数据 ;数据取反,“1”有效 ;屏蔽行线,保留列线数据 ;存列线数据(R1高4位) ;全0,无键按下,返回 ;行线置输入态,列线置低电平 ;读行线数据 ;数据取反,“1”有效 ;屏蔽列线,保留行线数据 ;存行线数据(R2低4位) ;全0,无键按下,返回 ;已有消抖标志,转至等待按键释放第 51 页 共 87 页 SETBF0;无消抖标志,置消抖标志 ;调用10ms延时子程序,消抖 ;重读行线列线数据 ;LCALL DY10ms SJMP GRET: RET WAIT: MOV CPL ANL JNZ KEY2: MOV MOV MOV CLR KEY3: RLC JC DEC DJNZ 按下的列线 KEY4: MOV MOV MOV CLR KEY5: RRC JC INC DJNZ 的行线 KEY6: MOV CLR RLC C A A,R2 A,R2 R2,#00H R3,#03H C A KEY6 R2 R3,KEY5 ; A A,#0FH WAIT A,R1 R1,#03H R3,#03H C A KEY4 R1 R3,KEY3 ; A,P1 ; KEY0;等待按键释放; ;按键未释放,继续等待 ;取列线数据(高4位) ;取列线编号初值 ;置循环数;依次左移入C中 ;C=1,该列有键按下,(列线编号存R1) ;C=0,无键按下,修正列编号 ;判断循环是否结束?未结束继续寻找有键;取行线数据(低4位) ;置行线编号初值 ;置循环数;依次右移入C中 ;C=1,该行有键按下,(行线编号存R2) ;C=0,无键按下,修正行线编号 ;判循环结束否?未结束继续寻找有键按下;取行线编号 ; ;行编号× 2第 52 页 共 87 页 RLC ADD KEY7: CLR RLC RLC MOV JMP TABJ:A A,R1 C A A DPTR,#TABJ @A+DPTR;行编号× 4 ;行编号× 4+列编号=按键编号 ; ;按键编号× 2 ;按键编号× 4(LCALL+ RET共4字节) ; ;散转,执行相应键功能子程序 ;调用执行0#键功能子程序 ; ;调用执行1#键功能子程序 ;LCALL WORK0 RET LCALL WORK1 RET … …LCALL WORK15 RET ;;调用执行15#键功能子程序例8.15 按图8.33所示的连接图, 试编制中断方式键盘扫描程序,将键盘中的按 键码存入片内RAM30H单元中。/INT0 0 P1.0 4 P1.1 8 P1.2 80C51 12 P1.3 P1.4 P1.5 P1.6 P1.7 13 14 15 9 10 11 5 6 7 1 2 3 &+5V图8.33 ORG 0000H LJMP工作于中断方式的矩阵式键盘接口电路 ;复位地址 ;转初始化STAT第 53 页 共 87 页 ORG LJMP ORG STAT: MOV SETB MOV MOV 输出0 SETB SETB LJMP0003H PINT0 0100H SP,#60H IT0;中断入口地址 ;转中断服务程序 ;初始化程序首地址 ;置堆栈指针 ;置为边沿触发方式IP,#B ;置为高优先级中断 P1,#B ;置P1.0~P1.3置为输入态,置P1.4~P1.7EA EX0 $;CPU开中 ;开中 ;等待有键按下时中断 ;中断服务程序首地址 ;保护现场 ; ;读行线(P1.0~P1.3)数据 ;数据取反,“1”有效 ;屏蔽列线,保留行线数据 ;存行线(P1.0~P1.3)数据(R2低4位) ;行线置低电平,列线置输入态 ;读列线(P1.4~P1.7)数据 ;数据取反,“1”有效 ;屏蔽行线,保留列线数据(A中高4位) ;取列线编号初值 ;置循环数 ; ;依次左移入C中 ;C=1,该列有键按下,(列线编号存R1) ;C=0,无键按下,修正列编号OGR 2000H PINT0: PUSH PUSH MOV CPL ANL MOV MOV MOV CPL ANL MOV MOV CLR PINT01:RLC JC DEC DJNZ Acc PSW A,P1 A A, #0FH R2,A P1,#0F0H A,P1 A A,#0F0H R1,#03H R3,#03H C A PINT02 R1R3,PINT01 ;判循环结束否?未结束继续寻找有键按下列线第 54 页 共 87 页 PINT02:MOV MOV MOV PINT03:RRC JC INC DJNZ PINT04:MOV CLR RLC RLC ADD MOV POP POP RETI 8279程序:A,R2 R2,#00H R3,#03H A PINT04 R2;取行线数据(低4位) ;置行线编号初值 ;置循环数 ;依次右移入C中 ;C=1,该行有键按下,(行线编号存R2) ;C=0,无键按下,修正行线编号R3,PINT03 ;判循环结束否?未结束继续寻找有键按下行线 A,R2 C A A A,R1 30H,A PSW Acc ;取行线编号 ; ;行编号× 2 ;行编号× 4 ;行编号× 4+列编号=按键编号 ;存按键编号 ; ; ;START: MOV DPTR, #0100H MOV A, #12H 依次读出 MOVX @DPTR, A MOV A, #34H MOVX @DPTR, A MOV A, #D3H MOVX @DPTR, A MOV A, #90H 00H MOVX @DPTR, A MOV第 55 页 共 87 页;右边进入,8位显示,编码扫描键盘,N键;设时钟分频系数20;清除显示器RAM,FIFO RAM为0;写显示RAM命令,自动递增,起始地址为IE, #81H;开中断 JMP$中断服务程序: ZDXS: PHSH ACC PUSH PSWMOV DPTR, #0100H MOV A, #40H 00H MOVX @DPTR, A MOV DPTR, #0000H MOVX A, @DPTR ANL A, #0FH ;读FIFO RAM键码 ;取低4位 ;暂存键码 ;读取FIFO RAM,自动递增,起始地址为MOV A, 30H CLR CSUBB A, #0AH JNC EXT1 MOV A, 30H SWAP A;识别数字键与功能键 ;若为功能键则转移 ;取出键码 ;高低4位交换,利用OUTA输出MOV @DPTR, A SJMP EXT: RETURNMOV DPTR, #0100H MOV A, #D3H ;显示0MOVX @DPTR, A RETURN:POP POP RETI PSW ACC第 56 页 共 87 页 第9章DAC0832 与 80C51 单片机接口应用举例 80C51 单片机没有与 DAC0832 的专门接口, 但是可以把 DAC0832 当做 80C51 的外部数据存储空间进行访问。在两次更新 DAC0832 的输出模拟值之间的时间 间隔需要大于 DAC0832 的转换时间 1?s,对于 12M 时钟的单片机系统,其一个 机器周期的时间为 1?s,所以在输出数据到 DAC0832 后,插入一条单机器周期 的空操作指令 NOP 即可。下面举例说明各种应用情况。 单缓冲方式的应用举例 例 9.1 在 5V 电压的工作系统中,通过 80C51 单片机控制 DAC0832 输出峰 峰值为 4V,频率为 1kHz 的三角波。硬件连接如图 9.5 所示,VCC 工作为 5V, 单片机工作时钟为 12MHz,机器周期为 1?s;DAC0832 的 CS 连接 80C51 的 P2.7,所以其地址为最高位为 0,其它位任意。为了能够尽量避免与其他地址冲 突,可以人为设置其地址为 7FFFH。此外,图中使用了两个运放,前级用来转 换电流成电压,后级用来把负电压变成正电压输出。 若使用以下代码控制 DAC0832 输出模拟电压: MOV DPTR, #7FFFH ; 2 个机器周期,执行时间为 2?s MOV A, #DATA ; 1 个机器周期,执行时间为 1?s MOVX @DPTR, A ; 2 个机器周期,执行时间为 1?s CJNE Rn, #DATA, … ; 2 个机器周期,执行时间为 2?sVCC P0 DI0 DI7 VCC ILE Vref80C51DAC0832P2.7CS WR1 XFER WR2Rfb Iout1 Iout2 GND + R +RWR VSSVout图 9.5 例 9.1 硬件连接原理图则以上程序的执行时间一共为 7 个机器周期, 即 7?s, 所以需要每次更新 DAC 的最短时间为 7?s。本例中,需要三角波的周期为 1kHz,则其周期为 1ms,上升 时间为 500?s。峰峰值为 4V,则 DAC0832 的最大值为 4V,最小值为 0V,参考 电压为 5V 时,DAC0832 的最小输出电压变化为 5V/256=0.0195V。4V 对应的数 字量是 4×256/5≈205。如果设置 DAC0832 每次更新输出的间隔为 10?s,从零开 始,则每次增加 4,则增加 50 次后,数字量为 200,对应输出为 3.9V,然后在 递减到 0,如此重复即可近似符合要求。 具体代码如下: ORG 0000H AJMP MAIN_START ORG 0100H第 57 页 共 87 页 MAIN_START: MOV DPTR, #7FFFH ;地址指针指向 DAC0832 MOV A, #0 ; MOV R1, #0 ; ;-------------------------------------;开始输出三角波的上升沿 UP: MOVX @DPTR, A ;输出数据到 DAC0832,2 个机器周期 ADD A, #4 ;输出电压增加约 0.078V,1 个机器周期 INC R1 ;记录输出次数,1 个机器周期 NOP ;插入空操作,1 个机器周期 NOP ;插入空操作,1 个机器周期 NOP ;插入空操作,1 个机器周期 NOP ;插入空操作,1 个机器周期 CJNE R1, #51, UP;判断是否到达最大值,2 个机器周期 ;-------------------------------------;开始输出三角波的下降沿 DOWN: CLR C ;1 个机器周期 SUBB A, #4 ;每次减少 0.078V,1 个机器周期 MOVX @DPTR, A ;从最大值 3.9V 开始输出,2 个机器周期 INC R1 ;记录输出次数,1 个机器周期 NOP ;插入空操作,1 个机器周期 NOP ;插入空操作,1 个机器周期 NOP ;插入空操作,1 个机器周期 CJNE R1, #102, DOWN;判断是否到达最大值,2 个机器周期 MOV R1, #0 SJMP UP ;2 个机器周期 END 以上代码与前面的分析略有差异,这是可以理解的。在分析部分,我们把 DPTR 赋值的内容也计算了循环时间,但是实际只需要一次赋值即可,而分析部 分没有考虑累加器 ACC 的内容修改部分。这些都是需要在后面程序调试中加入 的。 以上代码实现三角波峰峰值为 3.9V,频率也只是近似的 1kHz,如需要精确 定时,则需要使用定时器来设计。 双缓冲方式的应用 当系统中,需要使用多个 D/A 转换器,且要求同时更新模拟量时,则可以设 置系统中的所有 DAC0832 工作在双缓冲方式,每个 DAC0832 的输入数据寄存 器使用不同的地址,但是所有的 DAC0832 转换寄存器使用同一个地址。在更新 数据时,首先把数据输入到所有的 DAC 输入数据寄存器,由于指令执行时间的 先后,这个动作不可能同时进行;然后,通过执行一个写 D/A 转换寄存器的指 令,实现同时启动系统中的所有 DAC0832,达到同时更新模拟量输出的目的。 例 9.2 有一种绘图仪,输入两个模拟量 x,y 则可以在仪器上根据输入模拟 量的变化绘制出 x, y 的关系曲线图。 现通过一个 80C51 单片机和两片 DAC0832第 58 页 共 87 页 与此绘图仪连接,如果 x,y 值同时更新则可以绘制出平滑的曲线图,否则就会 出现阶梯波形图,因此需要使 DAC0832 工作在双缓冲方式。硬件连接如图 9.6 所示。 U2 的 CS 连接 P2.5, 其输入寄存器地址为 0DFFFH, U3 的 CS 连接 P2.6, 其输入寄存器地址为 0BFFFH,两个 DAC0832 的 XFER 连接在一起接到 P2.7, 其地址为 7FFFH。所有的 WR1 , WR2 都接到 80C51 的 WR 引脚。U2 用 来输出 X 值,U3 用来输入 Y 值。 有 20 组 x,y 值分别存在地址 30H,50H 开始的数据存储器中,编程绘制此 组数据的关系曲线图。U1 P0 U2 DI0 DI7 VCC ILE Vref VCC80C51P2.5 P2.6 P2.7 WR VSSDAC0832CS XFER WR1 WR2Rfb Iout1 Iout2 GND + R +RVxX VCCU3 DI0 DI7VCC ILE Vref绘图仪 YDAC0832CS XFER WR1 WR2Rfb Iout1 Iout2 GND + R +RVy图 9.6 例 9.2 硬件连接图参考代码如下: ORG 0000H AJMP MAIN_START ORG 0100H MAIN_START: MOV R0, #30H ;设置 x 数据指针 MOV R1, #50H ;设置 y 数据指针 MOV R2, #0 ;清计数器为零 ;-------------------------------------------;输出 x 数据到 DAC0832 U2 的输入寄存器 GOON: MOV DPTR, #0DFFFH ;x 数据 DAC 地址为 DFFFH MOV A, @R0 ; MOVX @DPTR, A ;写数据到 DAC0832 U2 INC R0 ;x 数据指针指向下一个数据 ;-------------------------------------------;输出 y 数据到 DAC0832 U2 的输入寄存器 MOV DPTR, #0BFFFH ;y 数据 DAC 地址为 BFFFH MOV A, @R1 ;第 59 页 共 87 页 MOVX @DPTR, A ;写数据到 DAC0832 U3 INC R1 ;y 数据指针指向下一个数据 ;------------------------------------------;把所有 DAC0832 的输入数据寄存器的写入到 DAC 转换寄存器, ;1?s 后同时输出数据到绘图仪 MOV DPTR, #7FFFH ;DAC 转换寄存器地址为 7FFFH MOVX @DPTR, A ;使能 WR 和 XFER ,启动 D/A 转换 INC R2 ;统计输出数据个数 CJNE R2, #20, GOON ;输出 20 个数据后,绘图结束 SJMP $ ;停机 END 例 9.3 某光通信收发模块中,需要同时检测环境温度,发射光功率和接收 功光率。现选用 80C51 作为控制器,设计系统。 ORG 0000H AJMP MAIN_START ORG 0003H AJMP INT_EX0_HANDLE ORG 0100H ;ADC0809 中断处理子程序 INT_EX0_HANDLE: MOVX A, @DPTR;读取 ADC0809 的转换结果 MOV @R0, A ;保存数据 INC R0 ;数据指针指向下一个位置 INC DPTR ;指向下一个模拟量 DJNZ R1, READ ;判断转换次数 MOV DPTR, #8000H ;重现开始新一轮 A/D 转换 MOV R0, #30H ; MOV R1, #03H ; READ: MOVX @DPTR, A ;一次 A/D 结束,重新启动 ADC0809 RETI ORG 0200H MAIN_START: MOV R0, #30H ;设置数据保存指针 MOV R1, #03H ;设计数据个数 MOV DPTR, #8000H ;设置 ADC0809 的最低地址 SETB IT0 ;设置外部中断为下降沿触发 SETB EX0 ;开外部中断 0 SETB EA ;开全局中断 MOVX @DPTR, A ;启动 ADC0809 进行 A/D 转换 SJMP $ END 80C51 与 PCF8591 的接口设计第 60 页 共 87 页 80C51 单片机没有硬件 I2C 接口,因此不能直接与 PCF8591 直接通信。解决 的办法就是通过软件模拟出一个 I2C 接口。参考硬件设计原理图如图 9.22 所示。 PCF8591 的 SCL 连接 80C51 的 P1.0 口,SDA 接 P1.1 口。然后通过编写代码控 制 P1.0 和 P1.1 的电平变化,模拟 I2C 的各个状态,向 PCF8591 传送数据。U3 VC C 19 18 P0 . 0 /AD 0 P0 . 1 /AD 1 P0 . 2 /AD 2 P0 . 3 /AD 3 P0 . 4 /AD 4 P0 . 5 /AD 5 P0 . 6 /AD 6 P0 . 7 /AD 7 P1 . 0 /T2 P1 . 1 /T2 EX P1 . 2 P1 . 3 P1 . 4 P1 . 5 P1 . 6 P1 . 7 P2 . 0 /A8 P2 . 1 /A9 P2 . 2 /A1 0 P2 . 3 /A1 1 P2 . 4 /A1 2 P2 . 5 /A1 3 P2 . 6 /A1 4 P2 . 7 /A1 5 40 39 38 37 36 35 34 33 32 1 2 3 4 5 6 7 8 21 22 23 24 25 26 27 28 VC C VC C VR 1XTA L1 XTA L2RR 1 4.7K RR 2 4.7KU2 R1 1K 16 15 14 13 12 11 10 9 VD D AO UT VR EF AG ND EXT OSC SCL SDA PCF8 5 9 1 T LED1 AIN0 AIN1 AIN2 AIN3 A0 A1 A2 VSS9 31 29 30RST EA/VPP PSEN ALE10K 1 2 3 4 5 6 7 8J P210 11 12 13 14 15 16 17P3 . 0 /Rx D P3 . 1 /Tx D P3 . 2 /INT0 P3 . 3 /INT1 P3 . 4 /T0 P3 . 5 /T1 P3 . 6 /W R P3 . 7 /RDJ P420VSS STC9 0 C 5 1图 9.22 PCF8591 与 80C51 连接原理图从 PCF8591 读取一次 A/D 转换结果和启动 PC8591 进行一次 D/A 转换的 的参考程序如下:其中,I2C 的软件模拟程序参考第 7 章。 LCALL I2C_START ;启动 IIC 总线操作 MOV A, #B ;访问 PCF8591 的 A/D LCALL WRB LCALL RDB ;读上次采样数据,结果存放在 R6 中 LCALL I2C_STOP ;停止 IIC 总线操作 LCALL I2C_START ;启动 IIC 总线操作 MOV A, #B ;访问 PCF8591 的 D/A LCALL WRB MOV A, #H ;设置控制字 LCALL WRB MOV A, R6 ;从 D/A 输出采样值 LCALL WRB LCALL I2C_START ;停止 IIC 总线操作第 61 页 共 87 页 第 10 章[例 10-2] 设单片机的晶振频率为 12MHz,要求在 P1.0 引脚上输出周期为 2ms 的方波。 解 周期为 2ms 的方波要求定时的间隔 1ms,定时时间到则 P1.0 取反。定时 器计数频率=晶振频率/12=1MHz。 计数周期=1/计数频率=1us。 1ms=l000us, 故计算器要计数 1000 次。考虑到计数器工作时是向上计数,所以必须给定时器 赋初值为 。 (1)用定时器 0 的方式 1 编程,采用查询方式。 #include &reg51.h& sbit P1_0=P1^0; void main() { TMOD=0x01;/*设置定时器 0 的工作方式为 1*/ TR0=1; /*开启 T/C0*/ For(;;) { TH0=()/256); /*装载计数初值*/ TL0=()%256); do {} while (!TF0); /*查询等待 TF0 置位(计数溢出时 TF 置 1)*/ P1_0=!P1_0; /*时间到 P1.0 取反*/ TF0=0; /*软件清 TF0*/ } } (2)用定时器 0 的方式 1 编程,采用中断方式。 # include &reg51.h& sbit P1_0=P1^0; void time0(void) interrupt 1 using 1 /*定义 T/C 中断函数*/ { P1_0=!P1_0; /* P1.0 取反*/ TH0=(()/256); /*装载计数初值*/ TL0=(()%256); } void main(void) { TMOD=0x01; /*设置定时器 0 的工作方式为 1*/ TH0=()/256); /*装载计数初值*/ TL0=()%256); EA=1; /*开启总中断*/ ET0=1;/*开启 T/C0 中断*/第 62 页 共 87 页 TR0=1;/*启动 T/C0 定时*/ while (1); /*死循环等待中断*/ } [例 10-3]请利用单片机串行口发送/接收程序,每接收到字节即刻发送出去, 并利用串口调试助手将字符回显在屏幕上。 /****************************************************** * Title 串口接收发送程序(字母)* *******************************************************/ #include &reg51.h& #define uchar unsigned char void main() { TMOD=0x20; // 定时器 1 的工作方式为 1,即自动重设初值的 8 位定时/计 数器 TL1=0 // 采用 11.0592MHz 的晶振,波特率 9600bps TH1=0 SCON=0xd8; //串行口的工作方式 3 PCON=0x00; TR1=1; //启动定时器 T1 while(1) { while(RI==0); //串口接收 RI=0; a=SBUF; SBUF=a; while(TI==0); TI=0; } } LCD 接口编程 SMG12864A 可以看成是两个 64*64 的屏幕, 每个屏通过 CS1,CS2 两个片选 信号来控制(低电平有效),对于每一片(左边和右边),分成 8 行*64 列,每 一行的高度为 8 位,从上到下对应的是 DB 的低位到高位。每写一个数据,光标 自动向后移动,到行末的时候返回到下一行行首。具体信号如下: CS1,CS2:屏幕控制片选信号,低电平有效;第 63 页 共 87 页 RST:复位信号,低电平有效; DB0-DB7:双向并行指令/数据信号; E:使能信号,高电平有效; R/W:读写控制信号,低电平为写入,高电平为读出; RS:数据命令控制信号,低电平为指令,高电平为数据; [例 10-4]: 利用图 10.5 的接口电路, 要求在 LCD 上交替实现“测试开始”和“测 试完毕”字样,试编写程序。 +5V #include &reg51.h& #define Disp_On 0x3f//开显示指令码 #define Disp_Off 0x3e//关显示指令码 #define Col_Add 0x40//列指针指令码 #define Page_Add 0xb8//页指针指令 码 #define Start_Line 0xc0//初始行指令 码 #define Lcd_Bus P1 sbit Mcs=P3^1; sbit Scs=P3^2; sbit Enable=P3^4; sbit RS=P3^0; sbit RW=P3^3;P1.0. . .. . .RST DB0. . .P1.7 P3.4 P3.3 P3.2 P3.1 P3.0DB7 E R/W CS1 变量a CS2 变量b RS V0SMG12864A8031+5VVEE BLK BLA图10.5 单片机和液晶接口电路char code ce[]={ /*-- 文字: 测 --*/ /*-- 宋体 12; 此字体下对应的点阵为:宽 x 高=16x16 --*/ 0x08,0x31,0x86,0x60,0x00,0xFE,0x02,0xF2,0x02,0xFE,0x00,0xF8,0x00,0x00,0 xFF,0x00,0x04,0xFC,0x03,0x00,0x80,0x47,0x30,0x0F,0x10,0x67,0x00,0x07,0x40,0 x80,0x7F,0x00}; char code shi[]={ /*-- 文字: 试 --*/ 0x40,0x42,0xDC,0x08,0x00,0x90,0x90,0x90,0x90,0x90,0xFF,0x10,0x12,0x1C, 0x10,0x00,0x00,0x00,0x7F,0x20,0x10,0x20,0x20,0x1F,0x10,0x10,0x01,0x06,0x18,0 x20,0x78,0x00}; char code kai[]={ /*-- 文字: 开 --*/ 0x40,0x42,0x42,0x42,0x42,0xFE,0x42,0x42,0x42,0x42,0xFE,0x42,0x42,0x42,0 x42,0x00,0x00,0x40,0x20,0x10,0x0C,0x03,0x00,0x00,0x00,0x00,0x7F,0x00,0x00,0x 00,0x00,0x00}; char code shi3[]={ /*-- 文字: 始 --*/第 64 页 共 87 页 0x10,0x90,0x70,0x1F,0x12,0xF0,0x00,0x20,0x70,0x28,0x27,0x22,0x28,0x70,0 x20,0x00,0x40,0x21,0x12,0x0C,0x06,0x09,0x30,0x00,0x7F,0x21,0x21,0x21,0x21,0x 7F,0x00,0x00}; char code wan[]={ /*-- 文字: 完 --*/ 0x00,0x90,0x8C,0xA4,0xA4,0xA4,0xA5,0xA6,0xA4,0xA4,0xA4,0xA4,0x94,0x 8C,0x04,0x00,0x00,0x80,0x40,0x20,0x18,0x07,0x00,0x00,0x00,0x3F,0x40,0x40,0x4 0,0x70,0x00,0x00}; char code bi[]={ /*-- 文字: 毕 --*/ 0x00,0x00,0xFF,0x88,0x48,0x28,0x08,0x80,0x7F,0x88,0x88,0x88,0x84,0x84,0 xE0,0x00,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0xFF,0x04,0x04,0x04,0x04,0x04,0x 04,0x04,0x00}; /*------------------延时子程序-----------------------------*/ void delay(unsigned int t) {unsigned int i,j; for(i=0;i&t;i++); for(j=0;j&10;j++); } /*------------------写命令到 LCD------------------------------*/ void write_com(unsigned char cmdcode) {RS=0;RW=0; Lcd_Bus=delay(0); Enable=1;delay(0);Enable=0; } /*-------------------写数据到 LCD----------------------------*/ void write_data(unsigned char Dispdata) {RS=1;RW=0; Lcd_Bus=Ddelay(0); Enable=1;delay(0);Enable=0; } /*------------------清除内存---------------*/ void Clr_Scr() {unsigned char j,k; Mcs=1;Scs=1; write_com(Page_Add+0); write_com(Col_Add+0); for(k=0;k&8;k++) { write_com(Page_Add+k);第 65 页 共 87 页 for(j=0;j&64;j++)write_data(0x00); } } /*---------------指定位置显示汉字 16*16-----------------------*/ void hz_disp16(char pag, char col, char code *hzk) { unsigned char j=0,i=0; for(j=0;j&2;j++) { write_com(Page_Add+pag+j); write_com(Col_Add+col); for(i=0;i&16;i++) write_data(hzk[16*j+i]); } } /*------------------初始化 LCD 屏--------------------------*/ void init_lcd() {Mcs=1;Scs=1;delay(100); write_com(Disp_Off); write_com(Page_Add+0); write_com(Start_Line+0); write_com(Col_Add+0); write_com(Disp_On); } /*-----------------------------信息显示-----------------------*/ void Msg(int flg) {Clr_Scr(); if(flg==0) //显示&测试开始& {Mcs=0;Scs=1; hz_disp16(3,32,ce); hz_disp16(3,48,shi); Mcs=1;Scs=0; hz_disp16(3,0,kai); hz_disp16(3,16,shi3); delay(2000); } else if(flg==1) //显示&测试完毕& {Mcs=0;Scs=1; hz_disp16(3,32,ce);第 66 页 共 87 页 hz_disp16(3,48,shi); Mcs=1;Scs=0; hz_disp16(3,0,wan); hz_disp16(3,16,bi); } } /*--------------------主程序---------------------------------*/ main() { unsigned char i=0;init_lcd(); while(1) { Msg(0);delay(10000); Msg(1); delay(10000); } } [例 10-5]:利用图 10.6 的接口电路,要求运放输出端 OUT 得到一个锯齿波 电压信号,试编写程序。 # include&absacc.h& # include &reg51.h& #define DA0832 XBYT[0xffe] void main() { unsigned char i while(1) { for(i=0;i&=255;i++)/*循 环形成语齿波, 输出最大值为 255*/ { DA0832=i; } } } [例 10-6]:利用图 10.7 ADC0809 接口电路图,实现 8 路模拟信号轮流采集 一次数锯,并将采集结果放在数组 ad 中。请利用查询方式编写程序。ALE EA+5V +5VVcc VssWR P0.7 P0.6 P0.5 P0.4 P0.3 P0.2 P0.1 P0.0WR1 WR274LS 373 A0Vcc ILE VrefG EGNDCS XFEERRfb IO18051D7 D6 D5 D4 D3 D2 D1 D0IO2 AGND DGNDGNDOUT图10.6DAC0832接口电路第 67 页 共 87 页 #include<absacc.h> #include<reg51.h> #define IN0 XBYTE[0x7ff8] /*设置 AD0809 的通道地址*/ #define uchar unsigned char sbit ad_busy=P3^3; /*EOC 状态*/ void ad0809(uchar idata *x) /*采 样结果放在指针 x 中的采集函数*/ { uchar i; uchar xdata *ad_adr; ad_adr=&IN0; for(i=0;i<8,i++) { *ad_adr=0; while[ad_busy!=0) x[i]=*ad_adr; ad_adr++; } } void main(void) { static uchar idata ad[10]; ad0809(ad); } /*启动转换*/ /*查询等待转换结束*/ /*存转换结果*/ /*采集下一通道*/ALEG OEP0clk74L S373 Q2Q1 q0ADC 0809C B A D0-780C51EA WRP2.7 RD+ +START ALE OE EOCP3.3图10.7 ADC0809接口电路第 68 页 共 87 页 第 11 章以下是一个用定时/计数器 T0 作软件看门狗的完整程序: ORG 0000H AJMP MAIN ORG 000BH LJMP ERR MAIN: MOV SP, #60H MOV PSW, #00H MOV SCON, #00H MOV TMOD, #01H ;设置 T0 为 16 位定时器 SETB ET0 ;允许 T0 中断 SETB PT0 ;设置 T0 中断为高级中断 MOV TL0, #00H ;设定 T0 的定时初值,定时时间约为 16ms(6M 晶振) MOV TH0, #0B0H SETB EA ;开中断 SETB TR0 ;启动 T0 LOOP: ...... ;主程序开始 LCALL WATCH DOG ;调用喂狗子程序 ...... LJMP LOOP WATCH DOG: MOV TL0, #00H ;喂狗子程序 MOV TH0, #0B0H SETB TR0 RET ERR: POP ACC ;定时器中断 POP ACC ;看门狗软件复位程序 CLR A PUSH ACC PUSH ACC RETI 用 DS18B20 温度传感器进行温度测量 INIT_1820: SETB P3.3 NOP CLR P3.3 ;主机发出延时 540 微秒的复位低脉冲 MOV R0,#36 LCALL DELAY SETB P3.3 ;然后拉高数据线 NOP NOP MOV R0,#36 TSR2: JNB P3.3,TSR3 ;等待 DS18B20 回应第 69 页 共 87 页 DJNZ R0,TSR2 LJMP TSR4 ; 延时 TSR3: SETB FLAG1 ; 置标志位,表示 DS1820 存在 LJMP TSR5 TSR4: CLR FLAG1 ; 清标志位,表示 DS1820 不存在 LJMP TSR7 TSR5: MOV R0,#06BH TSR6: DJNZ R0,TSR6 ;复位成功!时序要求延时一段时间 TSR7: SETB P3.3 RET 读位子程序: GET_TEMPER: SETB P3.3 LCALL INIT_1820 ;先复位 DS18B20 JB FLAG1,TSS2 CLR P1.2 RET ; 判断 DS1820 是否存在?若 DS18B20 不存在则返回 TSS2: CLR P3.3;DS18B20 已经被检测到!!!!!!!!!!!!!!!!!! MOV A,#0CCH ; 跳过 ROM 匹配 LCALL WRITE_1820 MOV A,#44H ; 发出温度转换命令 LCALL WRITE_1820 ;这里通过调用显示子程序实现延时一段时间,等待 AD 转换结束,12 位的话 750 微秒 LCALL INIT_1820;准备读温度前先复位 MOV A,#0CCH ; 跳过 ROM 匹配 LCALL WRITE_1820 MOV A,#0BEH ; 发出读温度命令 LCALL WRITE_1820 LCALL READ_18200; 将读出的温度数据保存到 35H/36H CLR P3.3 RET WRITEDS18: MOV R2,#8 ; 写一个字节 CLR C W1: CLR P3.3 MOV R3, #5第 70 页 共 87 页 DJNZ R3, $ RRC A MOV P3.3, C MOV R3, #21 DJNZ R3, $ SETB P3.3 NOP DJNZ R2, W1 SETB P3.3 ; 每位数据中恢复高电平 RET FLASH 存储器扩展设计与制作: 以下程序实现 K9F4G08 的块擦除、读 ID、复位、页编程、页读 FLASH 的操作: //******************************************************************** 页编程写入 0x00~0x1D,页读出 0x00~0x1D.读出 ID 号为 EC、DC、10、9 5。 //******************************************************************** #include&reg51.h& #include&intrins.h& #define uchar unsigned char #define uint unsigned int #define IO P0 sbit nWP=P1^6; //写保护 ,低有效 接高电平sbit nWE=P1^5;//写,/WE 控制向 I/O 口写入。命令,地址和数据在/WE 的上 升沿锁存。 sbit ALE=P1^4; //地址锁存 ,高有效 sbit CLE=P1^3; //命令锁存 ,高有效 sbit nCE=P1^2; sbit nRE=P1^1; sbit RB=P1^0; //延时函数 void delay(int us){ while(us--); } //芯片选通 ,低有效 接低电平//读,数据在/RE 的下降沿过后 tREA 时间后有效 //读忙//串口初始化函数 void InitUART(void)第 71 页 共 87 页 { EA=0; //暂时关闭中断 // TMOD&=0x0F; TMOD|=0x21; SCON=0x50; //定时器 1 模式控制在高 4 位 //定时器 1 工作在模式 2,自动重装模式 //串口工作在模式 1TH1=0 //计算定时器重装值 TL1=0 TH0=()/256; TL0= ()%256; //ES=1; TR1=1; TR0=1; REN=1; ET0=1; EA=1; } //写命令函数 void write(unsigned char wdat) { nWE=0; IO= nWE=1; _nop_(); // IO=0 } unsigned char rdat() { IO=0 nRE=0;第 72 页 共 87 页//串行中断允许 //启动定时器 1//允许接收//允许中断 //nRE=1; rdat=IO; nRE=1; // nRE=0; // IO=0 } // K9F4G08 初始化 void initflash() { CLE=1; write(0xff); CLE=0; delay(1000); //while(!RB); } //读取芯片 ID 函数 void Read_ID() { unsigned char i,ID[4]; nCE=0; CLE=1; write(0x90); CLE=0; ALE=1; write(0x00); ALE=0; for(i=0;i&4;i++) { ID[i]=rdat();第 73 页 共 87 页//写命令//检查 RB 线上的电平//写命令//写地址 } nCE=1; /////////////////串口发送数据//////////////////// for(i=0;i&4;i++) { TI=0;SBUF=ID[i]; delay(1000);while(~TI);TI=0; } //////////////////////////////////////////////// } //块擦除函数 void { nCE=0; CLE=1; write(0x60); CLE=0; _nop_(); ALE=1; write(radd1); //写地址 write(radd2); //写地址 write(radd3); //写地址 ALE=0; CLE=1; write(0xD0); //写命令 CLE=0; delay(1000); nWE=1; // while(!RB); nCE=1;第 74 页 共 87 页Block_Erase(uchar radd1,uchar radd2,uchar radd3)//写命令//检查 RB 线上的电平 } //读页函数 void Page_Read(uchar cad1,uchar cad2,uchar rad1,uchar rad2,uchar rad3,uchar n um) //页读 { uchar i,r[30]; nRE=1; nCE=0; CLE=1; write(0x00); CLE=0; ALE=1; write(cad1); write(cad2); write(rad1); write(rad2); write(rad3); ALE=0; CLE=1; write(0x30); CLE=0; delay(1000); //while(!RB); nWE=1; for(i=0;i&i++) { //IO=0 r[i]=rdat(); } nCE=1;第 75 页 共 87 页//写命令//写地址 //写地址 //写地址 //写地址 //写地址//写命令//检查 RB 线上的电平 /////////////////串口发送//////////////////// for(i=0;i&i++) { TI=0;SBUF=r[i]; delay(2000); while(~TI);TI=0; } } //页编程 void Page_Program(uchar cad1,uchar cad2,uchar rad1,uchar rad2,uchar rad3,ucha r num) { Block_Erase(rad1,rad2,rad3);//为了保证写入的数据正确,必须先擦除单元内容 的值 nCE=0; CLE=1; write(0x80); CLE=0; _nop_(); ALE=1; write(cad1); write(cad2); write(rad1); write(rad2); write(rad3); ALE=0; // write(wdata); //写数据 //写地址 //写地址 //写地址 //写地址 //写地址 //写命令for(i=0;i&i++) {第 76 页 共 87 页 write(i); //写数据,从 0 到 0x1D } CLE=1; write(0x10); CLE=0; delay(1000); // while(!RB); nCE=1; } //主函数 void main() { nCE=0; nWP=1; CLE=0; ALE=0; InitUART(); //初始化串口 while(1); } void timer0() interrupt 1 { EA=0; a++; if(a==20) { a=0; TI=0;SBUF=1; //发个数据,测试串口是否能收到 while(~TI);TI=0; 9600 //检查 RB 线上的电平 //写命令,页写确认第 77 页 共 87

我要回帖

更多关于 80c51单片机引脚功能 的文章

 

随机推荐