2023年《单片机的C语言程序设计与运用第2版》期末复习题及超详细解析超详细解析超详细解析答案2.pdf
第四章 中断类 例 4-1 P104 假设外部中断 0 和外部中断 1 均为下降沿触发,当外部中断 0 发生时,P0 端口的电平反向,当外部中断 1 发生时,P1 端口的电平反向。#include void IS0(void)interrupt 0 P0=P0;/P0 端口反向 void IS1(void)interrupt 2 P1=P1;/P1 端口反向 void main()P0=0 x00;P1=0 xFF;IT0=1;IT1=1;EX0=1;EX1=1;EA=1;while(1);【例 4-9】外部中断示例 2 在本实例中,首先通过 P1.7 口点亮发光二极管 D1,然后外部输入一脉冲串,则发光二极管 D1 亮、暗交替。#include sbit P1_7=P17;void interrupt0()interrupt 0 using 2 /外部中断 0 P1_7=!P1_7;void main()EA=1;/开中断 IT0=1;/外部中断 0 脉冲触发 EX0=1;/外部中断 0 P1_7=0;do while(1);如果有 3 个脉冲,则灯亮、暗交替一次,可如下面编程:#include Sbit P17=P17;unsigned char i=3;void main()EA=1;IT0=1;EX0=1;P17=0;3 do while(1);void interrupt0()interrupt 0 i=i-1;if(i=0)P17=!P17;i=3;【例 4-10】如图 4-18 所示,8 只 LED 阴极接至单片机 P0 口,两开关 S0、S1分别接至单片机引脚 P3.2()和 P3.3()。编写程序控制 LED 状态。按下 S0后,点亮 8 只 LED;按下 S1 后,变为闪烁状态。#include sbit P32=P32;void delay(unsigned int d)/定义延时子函数 while(-d0);void main()P0=0 xFF;/熄灭 LED IT0=1;IT1=1;/外中断 0、1 脉冲触发方式 4 EA=1;EX0=1;EX1=1;/开中断 for(;)/延时等待中断发生 ;void INT0_ISR()interrupt 0/外中断 0 中断服务函数 P0=0 x00;void INT1_ISR()interrupt 2/外中断 1 中断服务函数 while(P32!=0)/如果有外部中断 0,则退出 delay(5000);P0=0 x00;delay(5000);P0=0 xFF;定时类【例 4-16】设单片机的 fosc=12MHz,要求在 P1.0 上产生周期为 2ms 的方波。要在P1.0 上产生周期为2ms 的方波,定时器应产生1ms 的周期性定时,定时到对 P1.0 取反。要产生 1ms 的定时,应选择方式 1,定时器方式。TMOD 的确定:选择定时器/计数器 T0,定时器方式。方式 1,GATE 不起作用,高 4 位为 0000,TMOD=01H。TH、TL 的确定:单片机的 fosc=12MHz,则单片机的机器周期为 1ms,1ms=1000ms,计数器的计数初值为 65 536-1000,TH0=(65 536-1000)/256,TL0=(65 536-1000)%256。采用查询方式 程序如下:#include sbit P1_0=P10;void main(void)TMOD=0 x01;TR0=1;for(;)5 TH0=(65536-1000)/256;TL0=(65536-1000)%256;do while(!TF0);P1_0=!P1_0;TF0=0;采用中断方式 程序如下:#include sbit P1_0=P10;void timer0(void)interrupt 1 using 1 P1_0=!P1_0;TH0=(65536-1000)/256;TL0=(65536-1000)%256;void main(void)TMOD=0 x01;P1_0=0;TH0=(65536-1000)/256;TL0=(65536-1000)%256;EA=1;ET0=1;TR0=1;do while(1);6 【例 4-17】设系统时钟频率为 12MHz,编程实现从 P1.1 输出周期为 1s 的方波。要输出周期为 1s 的方波,应产生 500ms的周期性定时,定时到则对 P1.1取反即可实现。由于定时时间较长,一个定时器/计数器不能直接实现,一个定时器/计数器最长定时时间为 65ms 多一点,可以用以下两种方法实现。(1)方法一 用定时/计数器 T0 产生周期性为 10ms 的定时,然后用一个变量对 10ms 计数 50 次。系统时钟为 12MHz,定时/计数器 T0 定时 10ms,计数值 N 为10000,选方式 1,方式控制字为 00000001B(01H),则初值 X 为 X=65 536-10 000。7 R1470RR2470RD0D1XTAL218XTAL119ALE30EA31PSEN29RST9P0.0/AD039P0.1/AD138P0.2/AD237P0.3/AD336P0.4/AD435P0.5/AD534P0.6/AD633P0.7/AD732P1.01P1.12P1.23P1.34P1.45P1.56P1.67P1.78P3.0/RXD10P3.1/TXD11P3.2/INT012P3.3/INT113P3.4/T014P3.7/RD17P3.6/WR16P3.5/T115P2.7/A1528P2.0/A821P2.1/A922P2.2/A1023P2.3/A1124P2.4/A1225P2.5/A1326P2.6/A1427U1AT89C51VCCAB#include sbit P1_1=P11;unsigned char i;/定义计数变量 void main()i=0;/初始化 TMOD=0 x01;TH0=(65536-10000)/256;TL0=(65536-10000)%256;EA=1;ET0=1;TR0=1;while(1);void time0_int(void)interrupt 1 /中断服务程序 TH0=(65536-10000)/256;/重载初始值 TL0=(65536-10000)%256;/重载初始值 i+;/每发生一次中断,计数变量加 1 if(i=50)/发生 50 次中断,定时 0.5ms P1_1=!P1_1;i=0;/计数变量清零 8 (2)方法二 用定时/计数器 T1 计数实现,对 10ms 计数 50 次。定时/计数器 T1 工作于计数方式时,计数脉冲通过 T1(P3.5)输入。设定时/计数器 T0 定时时间到对P1.0 取反一次,则T1(P3.5)每10ms 产生一个计数脉冲,那么定时500ms只需计数 25 次,设定时/计数器 T1 工作于方式 2,初值 X=256-25=231,TH1=TL1=231。因为定时/计数器 T0 工作于方式 1,定时方式,则这时方式控制字为 01100001B(61H)。定时/计数器 T0 和 T1 都采用中断方式工作。R2470RD1XTAL218XTAL119ALE30EA31PSEN29RST9P0.0/AD039P0.1/AD138P0.2/AD237P0.3/AD336P0.4/AD435P0.5/AD534P0.6/AD633P0.7/AD732P1.01P1.12P1.23P1.34P1.45P1.56P1.67P1.78P3.0/RXD10P3.1/TXD11P3.2/INT012P3.3/INT113P3.4/T014P3.7/RD17P3.6/WR16P3.5/T115P2.7/A1528P2.0/A821P2.1/A922P2.2/A1023P2.3/A1124P2.4/A1225P2.5/A1326P2.6/A1427U1AT89C51VCCAB#include sbit P1_1=P11;sbit P1_0=P10;void main()TMOD=0 x61;/初始化 TH0=(65536-10000)/256;TL0=(65536-10000)%256;TH1=231;TL1=231;EA=1;ET0=1;ET1=1;TR0=1;TR1=1;while(1);9 void time0_int(void)interrupt 1 /T0 中断服务程序 TH0=(65536-10000)/256;/重载初始值 TL0=(65536-10000)%256;/重载初始值 P1_0=!P1_0;void time1_int(void)interrupt 3 /T1 中断服务程序 P1_1=!P1_1;【例 4-18】设系统时钟频率为 12MHz,编程实现:P1.1 引脚上输出周期为 1s,占空比为 20%的脉冲信号 根据输出要求,脉冲信号在一个周期内高电平占 0.2s,低电平占 0.8s,超出了定时器的最大定时间隔,因此利用定时器 0 产生一个基准定时配合软件计数来实现。取 50ms 作为基准定时,采用工作方式 1,这样这个周期需要 20 个基准定时,其中高电平占 4 个基准定时。#include sbit P1_1=P11;unsigned char i;/定义计数变量 void main()i=0;/初始化 TMOD=0 x01;TH0=(65536-50000)/256;TL0=(65536-50000)%256;EA=1;ET0=1;TR0=1;while(1);void time0_int(void)interrupt 1 /中断服务程序 TH0=(65536-50000)/256;/重载初始值 TL0=(65536-50000)%256;i=i+1;if(i=4)P1_1=0;/高电平时间到变低 else if(i=20)/周期时间到变高 P1_1=1;10 i=0;/计数变量清零 第五章【例 5-9】串行口自发自收#include#define uchar unsigned char#define uint unsigned int void main()uchar i=0 x55;11 uint j=0;TMOD=0X20;/设定定时器 1 模式 2 TL1=TH1=0XF4;PCON=0X00;SCON=0X50;TR1=1;while(1)SBUF=i;/发送数据 do()while(!RI)RI=0;TI=0;i=SBUF;/读取接收数据 P1=i;i=i;/将发送数据取反 for(j=0;j12500;j+);【例 5-10】两个单片机串行通信 1 单片机 1 的 C51 源程序代码:#include#define uint unsigned int 12#define uchar unsigned char void main()uchar i;TMOD=0 x20;TH1=TL1=0 xff;SCON=0 x50;PCON=0 x80;TR1=1;P1=0 xff;while(1)P1=0 xff;i=P1;SBUF=i;while(TI=0);TI=0;单片机 2 的 C51 源程序:#include#define uint unsigned int#define uchar unsigned char void main()uchar i=0;TMOD=0 x20;TH1=TL1=0 xff;SCON=0 x50;PCON=0 x80;TR1=1;while(1)while(RI=0);RI=0;i=SBUF;P1=i;两个单片机串行通信 2 C51 源程序代码如下:13#include#define uchar unsigned char#define TR 1 /TR=1,发送 uchar idata buf10;uchar pf;void main()int();/串行口初始化子函数 if(TR=0)send(buf);/发送 else receive(buf);/接收 /*串口初始化子函数*/void init(void)TMOD=0 x20;/T1 工作于方式 2 TH0=0 xE8;TL0=0 xE8;TR1=1;SCON=0X50;/串行口工作于方式 1,REN=1 /*发送子函数*/void send(uchar idata*d)uchar i;do SBUF=0 xAA;/发送联络信号 while(TI=0);/等待一帧发送完毕 TI=0;/发送完毕,标志位清 0 while(RI=0);/等待乙机应答信号 RI=0;while(SBUF0 xBB!=0);/乙机未准备好,继续联络 do pf=0;/校验和变量清 0 for(i=0;i10;i+)14 SBUF=di;/发送一个数据 pf+=di;/计算校验和 while(TI=0);TI=0;SBUF=pf;/发送校验和 while(TI=0);TI=0;while(RI=0);RI=0;/等待乙机应答 while(SBUF!=0);/回答出错,则重新发送 /*接收函数*/void receive(uchar idata*d)uchar i;do while(RI=0);RI=0;while(SBUF0 xAA)!=0);/判断甲机是否请求 SBUF=0 xBB;/发应答信号 while(TI=0);TI=0;while(1)pf=0;/清校验和 for(i=0;i10;i+)di=SBUF;/接收数据 pf+=di;/计算校验和 while(RI=0);RI=0;/接收甲校验和 If(SBUFpf)=0)/比较校验和 SBUF=0 x00;break;/校验和相等,发 0 x00 else SBUF=0 xFF;/校验和不相等,发 0Xff while(TI=0);TI=0;第六章 静态:【例 6-1】利用单片机的并行口作为静态显示的输出口的示例 15 静态轮流显示“12”、“-”和“AB”的 C51 源程序如下:#include#define uchar unsigned char uchar data dis_buf2;/显示缓冲区 uchar code able18=0 x3f,0 x06,0 x5b,0 x4f,0 x66,0 x6d,0 x7d,0 x07,0 x7f,0 x6f,0 x77,0 x7c,0 x39,0 x5e,0 x79,0 x71,0 x40,0 x00;/显示的代码表 void dl()unsigned int i;for(i=0;i40000;i+);void display(void)/显示函数 uchar segcode;segcode=dis_buf0;/P0 口显示 segcode=tablesegcode;P0=segcode;segcode=dis_buf1;/P3 口显示 segcode=tablesegcode;P3=segcode;void main(void)/主函数 while(1)dis_buf0=1;dis_buf1=2;/显示 12 display();dl();dis_buf0=16;dis_buf1=16;/显示-display();dl();dis_buf0=10;dis_buf1=11;/显示 AB display();dl();16 示例中的显示函数 display()可以再简单一些,如下面程序段:void display(void)P0=tabledis_buf0;/P0 口显示 P3=tabledis_buf1;/P3 口显示 动态:【例 6-3】利用 MCS-51 单片机的并行口作为动态显示的段口与位口的示例 6 位数码管动态显示“123456”的 C51 源程序如下 1)随机调用#include#define uchar unsigned char uchar data dis_buf6;/显示缓冲区 uchar code table18=0 x3f,0 x06,0 x5b,0 x4f,0 x66,0 x6d,0 x7d,0 x07,0 x7f,0 x6f,0 x77,0 x7c,0 x39,0 x5e,0 x79,0 x71,0 x40,0 x00;/代码表 void dl_ms()/延时 1ms 函数 unsigned int j;for(j=0;j200;j+);void display(void)/显示函数 uchar segcode,bitcode,i;bitcode=0 xfe;/位码赋初值 for(i=0;i6;i+)segcode=dis_bufi;/显示缓冲器内容查表 P0=tablesegcode;P3=bitcode;dl_ms();P3=0 xff;/关闭显示 17 bitcode=bitcode1;/调整位码 bitcode=bitcode|0 x01;void main(void)dis_buf0=1;dis_buf1=2;dis_buf2=3;dis_buf3=4;dis_buf4=5;dis_buf5=6;while(1)display();(2)定时调用 定时调用是通过定时器/计数器的定时功能来定时一定的时间(如 20ms),定时时间到来调用显示函数。void main(void)/定时调用 TMOD=0 x01;TH0=-20000/256;TL0=-2000%256;EA=1;ET0=1;TR0=1;dis_buf0=1;dis_buf1=2;dis_buf2=3;void time0_int()interrupt 1 TH0=-20000/256;TL0=-2000%256;display();dis_buf3=4;dis_buf4=5;dis_buf5=6;while(1);【例 6-6】独立式按键示例 C51 源程序如下:18#include#define uchar unsigned char#define uint unsigned int uchar data key2;code uchar dirtab=0 xc0,0 xf9,0 xa4,0 xb0,0 x99,0 x92,0 x82,0 xf8,0 x80;void dl_6()uint i;for(i=0;i600;i+);void key()uchar key1;P1=0 xff;key1=P1;if(key1!=0 xff)dl_6();P1=0 xff;key1=P1;if(key1!=0 xff)key1=P1;switch(key1)case 0 xff:key2=8;break;case 0 xfe:key2=0;break;case 0 xfd:key2=1;break;case 0 xfb:key2=2;break;case 0 xf7:key2=3;break;case 0 xef:key2=4;break;case 0 xdf:key2=5;break;case 0 xbf:key2=6;break;case 0 x7f:key2=7;break;default:break;void main()19 key2=8;while(1)key();P3=dirtabkey2;【例 6-7】4 4 矩阵键盘示例#include#define uchar unsigned char#define uint unsigned int uchar data dir_buf;code uchar dirtab=0 x3f,0 x06,0 x5b,0 x4f,0 x66,0 x6d,0 x7d,0 x07,0 x7f,0 x6f,0 x77,0 x7c,0 x39,0 x5e,0 x79,0 x71,0 x40;code uchar keytab=0 xee,0 xed,0 xeb,0 xe7,0 xde,0 xdd,0 xdb,0 xd7,0 xbe,0 xbd,0 xbb,0 xb7,0 x7e,0 x7d,0 x7b,0 x77;void delay(void);void keyscan();void dir();uchar key;void main(void)dir_buf=16;20 while(1)keyscan();dir();void dir()P2=dirtabdir_buf;void delay(void)uchar i;for(i=0;i200;i+);void keyscan()uchar code_h,code_l,i1,i2;P1=0 xf0;/所有的行输出 0 code_l=P1;/读列值 code_l=code_l&0 xf0;/屏蔽掉高 4 位 if(code_l!=0 xf0)delay();/P1=0 xf0;code_l=P1;code_l=code_l&0 xf0;if(code_l!=0 xf0)code_h=0 xfe;for(i1=0;i14;i1+)kk:P1=code_h;code_l=P1;code_l=code_l&0 xf0;if(code_l=0 xf0)code_h=(code_h1)|0 x01;goto kk;goto ll;/for 21 ll:code_h=code_h&0 x0f;key=code_h+code_l;for(i2=0;i216;i2+)if(key=keytabi2)dir_buf=i2;*【例 6-10】一个 8 8 共阴极 LED 点阵显示“ILOVEYOU”(不用记)一个 8 8 在某一个时刻只能显示一个字符,要想显示字符串,必须在显示完一个字符后接着显示下一个字符,因此需要建立一个字符串库。由于每个字符有 8个段码值,该字符串有 8 个字符,所以该字符串库中有 8 8 共 64 个段码值。字符串 ILOVEYOU 的字模段码值 I 00H,00H,42H,7EH,42H,42H,00H,00H L 00H,42H,7EH,42H,02H,02H,02H,00H O 00H,3CH,22H,42H,42H,22H,3CH,00H V 00H,40H,70H,0CH,0CH,70H,40H,00H E 00H,42H,7EH,4AH,4AH,5AH,22H,00H Y 00H,40H,62H,1EH,12H,60H,40H,00H O 00H,3CH,22H,42H,42H,22H,3CH,00H U 00H,7CH,46H,02H,02H,7CH,40H,00H 22#include#define uint unsigned int#define uchar unsigned char code uchar tab1=0 x00,0 x00,0 x42,0 x7e,0 x42,0 x42,0 x00,0 x00,/I 0 x00,0 x42,0 x7e,0 x42,0 x02,0 x02,0 x02,0 x00,/L 0 x00,0 x3c,0 x22,0 x42,0 x42,0 x22,0 x3c,0 x00,0 x00,0 x40,0 x70,0 x0c,0 x0c,0 x70,0 x40,0 x00,x00,0 x42,0 x7e,0 x4a,0 x4a,0 x5a,0 x22,0 x00,0 x00,0 x40,0 x62,0 x1e,0 x12,0 x60,0 x40,0 x00,0 x00,0 x3c,0 x22,0 x42,0 x42,0 x22,0 x3c,0 x00,0 x00,0 x7c,0 x46,0 x02,0 x02,0 x7c,0 x40,0 x00 ;/U code uchar tab2=0 x00,0 x01,0 x02,0 x03,0 x04,0 x05,0 x06,0 x07;data uchar j;void delay(uint n)data uint i;for(i=0;in;i+);void dir()data uchar h,v=0;for(h=j;hj+8;h+)P0=tab1h;P3=tab2v;23 delay(555);P3=0 xff;v+;if(v=8)v=0;void dl()uint r;for(r=0;r40;r+)dir();void main(void)while(1)j=0;dir();dl();j=8;dir();dl();j=16;dir();dl();j=24;dir();dl();j=32;dir();dl();j=40;dir();dl();j=48;24 dir();j=56;dir();dl();*例 6-12 编程在 LCD1602 上显示由键盘输入的字符或数字。(不用记)/S51+1602,晶振为 12M#includereg51.h#includeintrins.h#includeabsacc.h sbit RS=P35;sbit RW=P36;sbit E=P37;#define busy 0 x80#define uchar unsigned char#define uint unsigned int uchar a=0,1,2,3,4,5,6,7,8,9,a,b,c,d,e,f,;void delay_LCM(uchar k)/延时函数 uint i,j;for(i=0;ik;i+)25 for(j=0;j60;j+);void test_1602busy()/测忙函数 P1=0 xff;E=1;RS=0;RW=1;_nop_();_nop_();while(P1&busy)/检测 LCD DB7 是否为 1 E=0;_nop_();E=1;_nop_();E=0;void write_1602Command(uchar co)/写命令函数 test_1602busy();/检测 LCD 是否忙 RS=0;RW=0;E=0;_nop_();P1=co;_nop_();E=1;/LCD 的使能端 高电平有效 _nop_();E=0;26 void write_1602Data(uchar Data)/写数据函数 test_1602busy();P1=Data;RS=1;RW=0;E=1;_nop_();E=0;void init_1602(void)/初始化函数 write_1602Command(0 x38);/LCD 功能设定,DL=1(8 位),N=1(2行显示)delay_LCM(5);write_1602Command(0 x01);/清除 LCD 的屏幕 delay_LCM(5);write_1602Command(0 x06);/LCD 模式设定,I/D=1(计数地址加1)delay_LCM(5);write_1602Command(0 x0F);/显示屏幕 delay_LCM(5);void DisplayOneChar(uchar X,uchar Y,uchar DData)Y&=1;X&=15;if(Y)X|=0 x40;/若 y 为 1(显示第二行),地址码+0X40 X|=0 x80;/指令码为地址码+0X80 write_1602Command(X);write_1602Data(DData);void display_1602(uchar*DData,X,Y)/显示函数 27 uchar ListLength=0;Y&=0 x01;X&=0 x0f;while(X16)DisplayOneChar(X,Y,DDataListLength);ListLength+;X+;void delay(uint i)/延时程序 uint j;for(j=0;ji;j+);uchar checkkey()/检测有没有键按下 uchar i;uchar j;j=0 x0f;P2=j;i=P2;i=i&0 x0f;if (i=0 x0f)return(0);else return(0 xff);uchar keyscan()/键盘扫描程序 uchar scancode;uchar codevalue;uchar a;uchar m=0;uchar k;uchar i,j;28 if(checkkey()=0)return(0 xff);else delay(100);if(checkkey()=0)return(0 xff);else scancode=0 xf7;m=0 x00;/键盘行扫描初值,M 为列数 for(i=1;i=4;i+)k=0 x10;P2=scancode;a=P2;for(j=0;j4;j+)/J 为行数 if(a&k)=0)codevalue=m+j;while(checkkey()!=0);return(codevalue);else k=k1;scancode=scancode;void main()/主函数 29 uchar*s;uchar z;uchar i=0,j=0;/i 为 LCD 的行,j 为 LCD 的列 delay_LCM(15);init_1602();/1602初始化 s=WELCOME TO HERE!;display_1602(s,0,0);/第一行显示WELCOM TO HERE!delay_LCM(200);delay_LCM(200);delay_LCM(200);while(1)if(checkkey()=0 x00)continue;else i=1;/LCD 在第二行显示 z=keyscan();if(j=16)j=0;i=1;break;else DisplayOneChar(j,i,az);j+;delay(100);第七章 【例 7-4】采用 DAC0832实现的波形发生器 30#include#include#define uchar unsigned char#define DAC0832 XBYTE0 x7fff code uchar sintab=0 x7f,0 x89,0 x94,0 x9f,0 xaa,0 xb4,0 xbe,0 xc8,0 xd1,0 xd9,0 xe0,0 xe7,0 xed,0 xf2,0 xf7,0 xfa,0 xfc,0 xfe,0 xff;sbit P10=P10;sbit P11=P11;sbit P12=P12;sbit P13=P13;uchar m=0;void delay()uchar j;for(j=0;j250;j+);void int0()interrupt 0 P1=0 xff;if(P10=0)m=1;if(P11=0)m=2;31 if(P12=0)m=3;if(P13=0)m=4;void main()uchar data i;EA=1;EX0=1;IT0=1;while(1)kkk:switch(m)case 0:goto kk;break;case 1:goto k0;break;case 2:goto k1;break;case 3:goto k2;break;case 4:goto k3;break;default:break;k0:while(1)for(i=0;i0 xff;i+)DAC0832=i;goto kkk;k1:while(1)for(i=0;i0;i-)DAC0832=i;goto kkk;32 k2:while(1)DAC0832=0 xff;delay();DAC0832=0;delay();delay();goto kkk;k3:while(1)for(i=0;i0;i-)DAC0832=sintabi;for(i=0;i0;i-)DAC0832=sintabi;goto kkk;kk:DAC0832=0;【例 7-6】1 路模拟输入 A/D 转换示例 33 (1)无条件方式:#include#include#define uchar unsigned char#define ADC08090 XBYTE0 x78ff void delay()uchar j;for(j=0;j250;j+);void main()while(1)ADC08090=0;/启动 A/D delay();P1=ADC08090;/读取数据 (2)查询方式#include#include#define uchar unsigned char#define ADC08090 XBYTE0 x78ff sbit P33=P33;Void main()34 while(1)ADC08090=0;/启动 A/D l1:P33=1;if(P33=0)P1=ADC08090;/读取数据 else goto l1;(3)中断方式#include#include#define uchar unsigned char#define ADC08090 XBYTE0 x78ff sbit P33=P33;void main()EA=1;EX1=1;IT1=1;ADC08090=0;/启动 A/D while(1);void int0()interrupt 2 P1=ADC08090;/读取数据 ADC08090=0;/启动 A/D