求助,没学过南昌大学单片机实验 做个实验,求解答,在线

单片机论坛(MCU论坛)讨论 - 单片机论坛 -
中国电子顶级开发网论坛(EETOP) 最大最火的半导体、集成电路、IC设计、嵌入式设计、电子电路、电子工程师论坛
(为了活跃板块,本版块内回帖为10信元,高于其他板块,但请大家自觉不要恶意灌水)
单片机论坛(MCU论坛)讨论
版块主题&&&该商品已下柜,非常抱歉!
单片机原理学习指导与实践指导(普通高等教育十二五规划教材)
商品介绍加载中...
扫一扫,精彩好书免费看
服务承诺:
京东平台卖家销售并发货的商品,由平台卖家提供发票和相应的售后服务。请您放心购买!
注:因厂家会在没有任何提前通知的情况下更改产品包装、产地或者一些附件,本司不能确保客户收到的货物与商城图片、产地、附件说明完全一致。只能确保为原厂正货!并且保证与当时市场上同样主流新品一致。若本商城没有及时更新,请大家谅解!
权利声明:京东上的所有商品信息、客户评价、商品咨询、网友讨论等内容,是京东重要的经营资源,未经许可,禁止非法转载使用。
注:本站商品信息均来自于合作方,其真实性、准确性和合法性由信息拥有者(合作方)负责。本站不提供任何保证,并不承担任何法律责任。
印刷版次不同,印刷时间和版次以实物为准。
价格说明:
京东价:京东价为商品的销售价,是您最终决定是否购买商品的依据。
划线价:商品展示的划横线价格为参考价,该价格可能是品牌专柜标价、商品吊牌价或由品牌供应商提供的正品零售价(如厂商指导价、建议零售价等)或该商品在京东平台上曾经展示过的销售价;由于地区、时间的差异性和市场行情波动,品牌专柜标价、商品吊牌价等可能会与您购物时展示的不一致,该价格仅供您参考。
折扣:如无特殊说明,折扣指销售商在原价、或划线价(如品牌专柜标价、商品吊牌价、厂商指导价、厂商建议零售价)等某一价格基础上计算出的优惠比例或优惠金额;如有疑问,您可在购买前联系销售商进行咨询。
异常问题:商品促销信息以商品详情页“促销”栏中的信息为准;商品的具体售价以订单结算页价格为准;如您发现活动商品售价或促销信息有异常,建议购买前先联系销售商咨询。
加载中,请稍候...
加载中,请稍候...
加载中,请稍候...
加载中,请稍候...
加载中,请稍候...
加载中,请稍候...
加载中,请稍候...
浏览了该商品的用户还浏览了
加载中,请稍候...
价 格: 到
   
iframe(src='///ns.html?id=GTM-T947SH', height='0', width='0', style='display: visibility:')当前位置: >>
单片机实验学习总结
学习心得:1.劳劳逸(脑力劳动,体力劳动, 娱乐活动)2.发现问题,解决问题就是在你 的大脑中建立广泛的联系 3.条缕清晰的做事 风格会让你的大脑变得异常秩序,清晰.做 到了此三点你的大脑会一天天聪明,清楚, 灵活起来.反过来有秩序的大脑将会使你的 行为更加利索,清晰. 注意:我的人生需注意 1.不要口出妄语,只 要脚踏实地.2.不要轻易生气,不要轻易露 出邪恶的表情.3.不要做作要自然.4.不要 猥琐,慌张,要大方,放松,镇定,让自己 尽量多的处在禅定状态中2010 年 5 月 8 日 一.单片机学习中的问题总结:(天才可以被训练出来,任何人可以在任何领域取得惊人的 成就,只要你对你所从事的领域感兴趣,并能长久地保持专注,并具有吃苦耐劳的精神.你 的成功与否取决于你的思维方式(习惯)训练的过程是一个不断积累模式的过程,而解决问 题的过程最后就变成了对模式进行识别的过程. 训练到最后拿到一个问题你可以在 0.8 秒内 迅速再现头脑中类似的模式并凭借直觉给出解决问题的绝佳方式. 那样你就是一个天才 (后 天培养的人才) 说明:黑色的程序代表没有问题可以直接用,绿色代表存在缺陷需要改进,紫色代表自创的 程序精华!红色代表要交代的重点内容 4.22 1.%bx 代表什么数据类型呢? 2.串口调试时有时会发送和接收不一? 3.直接对 P1 口取反可以实现灭亮的时间对等,但是通过对 P1 口赋值则比较复杂! 4.做计数显示于 LED 上的实验的时候不太明白当按下键后 P3.4 被拉低但是当松开手的时候 是什么因素导致了此口被抬为高电平呢详见 D:\dpj\shiyanji\lesson1_4\计数器实验???? 5.在利用单片机进行音乐播放的时候需要注意一下几点: 1.对于音乐的编码是遵循音调常数, 节拍常数,音调常数,节拍常数,.................的顺序进行的数组组合.2.音乐的最小单元是音 符,每个音符又分为音高和音长,其中控制音高的称为音调常数(共 21 个) ,控制音长的叫-1- 节拍常数 (共 7 个分别为四分之一拍 0x10,半拍 0x20,3/4 拍 0x30,1 拍 0x40,1 又 1/2 拍 0x60,2 拍 0x80,4 拍 0x100),其中这两个常数变量又分别由控制节拍常数变量的定时器中断服务子 程序和控制频率常数变量的延时程序来控制. 6.驱动三极管一般只工作在饱和和截止两种状态下,只有当三极管用在放大电路中时才工作 在放大区. 7.注意 DOFLY 的板子采用的是:1.利用 573 锁存器来驱动段,利用三八译码器来驱动位选. 8.松手检测和松手处理不是同一个概念.松手去抖操作一般不进行.硬件按键去抖有:1.滤 波去抖和双稳态去抖!所谓的双稳态即为 RS 触发器,所谓的滤波即为在按键之后加入一个 滤波器和一个反向器以实现在键按下后输出正规的方波. 9.LCD 液晶显示器依据内容分为段式,点阵式,以及字符式其中字符式应用最为简单.进一 步可学习图案式 LCD.1602 内部的控制驱动器是 HD44780.DDRAM:用于存放 LCD 要显示的 数据,只要将标准的 ASCII 码送入 DDRAM 中内部的控制电路将自动将数据送到显示器上 进行显示. 10.总结:要学会使用别人编好的功能程序包!例如 LCD 显示驱动功能程序包!I2C 总线驱 动功能程序包. 二.经典程序收集 (1)利用定时计数器 0 实现计数功能 #include&reg52.h& void main() { TMOD=0X05; TH0=0; TL0=0; TF0=0; IE=0; TR0=1; //init(); while(1) { P1=TL0; /*if(p34==0) { p34=1; temp++; P1= }*/ } } 问题与思考:谁来讲 P3.4 口的点评拉高?按键抖动问题在此难道不需要予以考虑吗?为什 么没有加入去抖程序却没有抖动?此处的初值不用反复复是因为默认为 0. (2)定时计数器 1 的计数功能实验 1 #include&reg52.h& 2 #define uchar unsigned char 3 //-2- 4 //sbit p34=P3^4; 5 //void init(); 6 uchar i=0; 7 void main() 8 { TMOD=0X60; 9 1 TH1=0 10 1 TL1=0 11 1 TF1=0; 12 1 TR1=1; 13 1 //init(); 14 1 while(1) 15 1 16 1 { 17 2 while(!TF1); 18 2 TF1=0; 19 2 ++i; 20 2 P1=i; 21 2 /*if(p34==0) 22 2 { 23 2 p34=1; 24 2 temp++; 25 2 P1= 26 2 }*/ 27 2 } 28 1 } 问题与思考:同上此处的初始值不用反复复由于使用的工作方式 2. (3)串口通信实验:利用 P3.0,P3.1 实现字符串的发送. #include&reg52.h& #include&intrins.h& #define uchar unsigned char #define uint unsigned int uchar trdata[]={'8','0','c','5','1',0x0d,0x0a,0x00}; void main() { uint i,j; SCON=0X40; PCON=0; // REN=1;//有没有它也是不影响串口通信的因为他只在单片机接收的时候才要求必须置 一; TMOD=0X20; TH1=0XE6; TL1=0XE6; // TI=1;//说明:有没有这个都不影响串口通信且位为有硬件置一 TR1=1;-3- while(1) { i=0; while(trdata[i]!=0x00) { SBUF=trdata[i]; while(TI==0); TI=0; i++; } for(j=0;j&50000;j++); } } 说明:当 TI=1 且 SBUF='A'时便启动了串口的发送计算机将接受到单片机发过来的数据 A. REN=1;虽说对于发送而言可有可无但是最好还是加上吧! (4)数码管的动态十分交替显示 123456 和 050906 的规范化程序 #include&reg52.h& #define uchar unsigned char uchar code dula[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f}; uchar code wela[]={0x00,0x01,0x02,0x03,0x04,0x05}; uchar a,b,flag, sbit p37=P3^7; void delay(uchar); void display(uchar,uchar); void init(); void main() { while(1) { for(i=0;i&90;i++) { display(6,1); delay(2); display(5,2); delay(2); display(4,3); delay(2); display(3,4); delay(2); display(2,5); delay(2); display(1,6);-4- delay(2); } for(i=0;i&90;i++) { display(6,0); delay(2); display(5,5); delay(2); display(4,0); delay(2); display(3,9); delay(2); display(2,0); delay(2); display(1,6); delay(2); } } } void delay(uchar x) { for(;x&0;x--) for(j=0;j&125;j++); } void display(uchar a,uchar num) { switch(a) { case 6:p37=1,P0=dula[num],P2=wela[0],p37=0; case 5:p37=1,P0=dula[num],P2=wela[1],p37=0; case 4:p37=1,P0=dula[num],P2=wela[2],p37=0; case 3:p37=1,P0=dula[num],P2=wela[3],p37=0; case 2:p37=1,P0=dula[num],P2=wela[4],p37=0; case 1:p37=1,P0=dula[num],P2=wela[5],p37=0; default: } } 思考: 用此程序实现比起我原来写的那个程序要好不必写两个显示函数了减少了程序的篇幅 提高了效率, 而且使得整个程序看上去整齐划一非常之规范, 利用子函数实现特定的功能实 现模块化程序设计的思路必须得以贯彻,子程序的效率也要必须得以提高! 注意:此程序在延迟时间上并不准确的实现一秒显示,显示 123456 为 480ms 显示 050906 为 480ms 因此从一个 123456 到另一个持续时间为 960 毫秒小于一秒.-5- (5)自编的高效矩阵键盘检测程序 #include&reg52.h& #define uchar unsigned char uchar code table[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71,0 x76,0x38,0x3e,0x6e,0x00}; uchar temp=0; //sbit p37=P3^7; void delay(uchar); void display(); uchar keyscan(); void main() { P2=0x02; while(1) { display(); } } void delay(uchar x) { for(;x&0;x--) for(j=0;j&125;j++); } uchar keyscan() { P3=0x0f; temp=P3&0x0f; if(temp!=0x0f) { delay(10); temp=P3&0x0f; if(temp!=0x0f) { temp=P3&0x0f; P3=0xf0; P3=P3&0xf0; if(P3!=0xf0) { delay(10); P3=P3&0xf0; if(P3!=0xf0) temp=temp+P3;-6- } } } } void display() { temp=keyscan(); switch(temp) { case 0x7e:P0=table[1]; case 0x7d:P0=table[2]; case 0x7b:P0=table[3]; case 0x77:P0=table[4]; case 0xbe:P0=table[5]; case 0xbd:P0=table[6]; case 0xbb:P0=table[7]; case 0xb7:P0=table[8]; case 0xde:P0=table[9]; case 0xdd:P0=table[10]; case 0xdb:P0=table[11]; case 0xd7:P0=table[12]; case 0xee:P0=table[13]; case 0xed:P0=table[14]; case 0xeb:P0=table[15]; case 0xe7:P0=table[16]; } } 说明:其高效体现在:1.代码量少.注意:P3 可以作为如变量 temp 一样使用.另外键盘的 工作方式有三种 1.程序控制扫描 2.定时扫瞄 3.中断扫描.其中前两中方式或多或少对 CPU 造成一定的浪费.而方式 3 则最高效.(6)LCD 驱动程序包 #include&reg52.h& #include&intrins.h& #define uchar unsigned char #define uint unsigned int sbit rs=P2^4; sbit rw=P2^5; sbit e=P2^6; #define dataport P0 const uchar nodisp=0;//无显示;-7- const uchar nocur=1;//有显示但无光标; const uchar curNoFlash=2;//有光标但无闪烁; const uchar curflash=3;//有光标且有闪烁; void lcdpos(uchar,uchar);//确定光标的位置; void lcdwd(uchar);//写字符; void lcdwc(uchar);//送控制字且检测忙信号; void lcdwcn(uchar);//送控制字但不检测忙信号; void mdelay(uchar);//延迟子程序; void waitidle();//正常读/写操作之前的检测 LCD 控制状态; //延迟子程序; void mdelay(uchar j) { uint i=0; for(;j&0;j--) for(i=0;i&125;i++); } //实现写字符功能将某个字符显示与 ypos 行 xpos 列. void writechar(uchar c,uchar xpos,uchar ypos) { lcdpos(xpos,ypos);//调用光标设置子程序; lcdwd(c);//调用写字符子程序; } //功能在制定的位置显示字符串; void writestring(uchar *s,uchar xpos,uchar ypos) { if(*s=='\0') for(i=0;;i++) { if(*(s+i)=='\0') writechar(*(s+i),xpos,ypos); xpos++; if(xpos&=15) } } //设置光标的功能; void setcur(uchar para) { mdelay(2); switch(para)-8- { case 0:{lcdwc(0x08);} case 1:{lcdwc(0x0c);} case 2:{lcdwc(0x0e);} case 3:{lcdwc(0x0f);} default: } } //实现清屏功能的子函数 void clrlcd() { lcdwc(0x01);//调用送控制字子程序; } //正常读写之前的检测 lcd 控制状态; void waitidle() { rs=0; rw=1; e=1; _nop_(); for(;;) { tmp= tmp&=0x80; if(tmp==0) } e=0; } //实现写字符功能; void lcdwd(uchar c) { waitidle(); rs=1; rw=0; dataport=c; e=1; _nop_(); _nop_(); e=0; } //送控制字子程序(检测忙信号) ; void lcdwc(uchar c)-9- { waitidle(); lcdwcn(c);//调用送控制字子程序(不检测忙信号) ; } //送控制字子程序(不检测忙信号) ; void lcdwcn(uchar c) { rs=0; rw=0; dataport=c; e=1; _nop_(); e=0; } //设置第 xpos,ypos 光标所在的地址; void lcdpos(uchar xpos,uchar ypos) { xpos&=0x0f; ypos&=0x01; if(ypos==0) tmp= else tmp=xpos+0x40; tmp|=0x80; lcdwc(tmp); } //复位 LCD 控制器; void lcdreset() { mdelay(15); lcdwc(0x38); lcdwc(0x08); lcdwc(0x01); lcdwc(0x06); lcdwc(0x0c); } //--------------------------------以下是用 lcd 实现现实一个字符串的主函数----------------------------void main() { uchar xpos, uchar *s=&80c51!&; xpos=8;- 10 - ypos=0; lcdreset(); clrlcd(); setcur(curflash); writestring(s,xpos,ypos); for(;;) {;} } -------------------------------以下实现让字母 A 围绕着 LCD 屏转动--------------------------------------void main() { char xpos, while(1) { p37=0; ypos=0; for(xpos=0;xpos&=15;xpos++) { lcdreset(); clrlcd(); setcur(nocur); writechar('A',xpos,ypos); mdelay(3000); if(xpos==15) { lcdreset(); clrlcd(); setcur(nocur); ypos=1; //lcdwc(0x18); for(xpos=15;xpos&=0;xpos--) { lcdreset(); clrlcd(); setcur(nocur); writechar('A',xpos,ypos); mdelay(3000); if(xpos==-1) } ypos=0; }- 11 - } } } ------------------------实现上行动态显示娄浩很性感, 下行静态显示 80C51--------------------------添加以上的 LCD 显示驱动包..... .... void main() { uchar s[]=&louhao is sexy!&; char xpos, lcdreset(); clrlcd(); setcur(nocur); writestring(&80c51&,6,1); while(1) { p37=0; ypos=0; for(xpos=0;xpos&=15;xpos++) { writechar(s[xpos],xpos,ypos); mdelay(5000); if(xpos==15) { lcdreset(); clrlcd(); setcur(nocur); writestring(&80c51&,6,1); } } } } 说明:LCD 显示比起 9 段数码显示更加高级且由于有了现成的驱动程序包因而用户只需要 把握如何与驱动程序之间建立联系(接口)便可以了!在其他的工程项目应用场合要尽量使 用 lcd 来显示,当然是在成本允许的情况下! (用户只需关注以下几点:1.写字符函数 writechar(uchar s,uchar xpos,uchar ypos)2.写字符串函数 writestring(uchar*s,uchar xpos,uchar ypos)3.setcur(uchar set)在使用时只需对函数中的各个参数作出合理的设置便可其他的程序 则套用周坚老师的模式便可! ) (7)I2C 总线驱动程序包 /*-----------------------------------------------启动 IIC 总线 ------------------------------------------------*/- 12 - void start(void) { sda=1; _nop_();_nop_(); scl=1; _nop_();_nop_();_nop_();_nop_();_nop_(); sda=0; _nop_();_nop_();_nop_();_nop_();_nop_(); scl=0; }/*-----------------------------------------------停止 IIC 总线 ------------------------------------------------*/ void stop(void) { sda=0; _nop_(); scl=1; _nop_();_nop_();_nop_();_nop_();_nop_(); sda=1; _nop_();_nop_();_nop_();_nop_();_nop_(); scl=0; }/*-----------------------------------------------应答 IIC 总线 ------------------------------------------------*/ void ack(void) { sda=0; _nop_();_nop_();_nop_(); scl=1; _nop_();_nop_();_nop_();_nop_();_nop_(); scl=0; _nop_();_nop_(); }/*-----------------------------------------------非应答 IIC 总线 ------------------------------------------------*/- 13 - void noack(void) { sda=1; _nop_();_nop_();_nop_(); scl=1; _nop_();_nop_();_nop_();_nop_();_nop_(); scl=0; _nop_();_nop_(); }/*-----------------------------------------------发送一个字节 ------------------------------------------------*/ void send(unsigned char c) { unsigned char bitcounter=8; do { temp=c; scl=0; _nop_();_nop_();_nop_();_nop_();_nop_(); if((temp&0x80)==0x80) sda=1; else sda=0;scl=1; temp=c&&1; c= bitcounter--; } while(bitcounter); scl=0; }/*-----------------------------------------------读入一个字节并返回 ------------------------------------------------*/ unsigned char read(void)- 14 - { unsigned char temp=0; unsigned char temp1=0; unsigned char bitcounter=8; sda=1; do { scl=0; _nop_();_nop_();_nop_();_nop_();_nop_(); scl=1; _nop_();_nop_();_nop_();_nop_();_nop_(); if(sda) temp=temp|0x01; else temp=temp&0 if(bitcounter-1) { temp1=temp&&1; temp=temp1; } bitcounter--; } while(bitcounter); return(temp); } /*-----------------------------------------------写入数据 ------------------------------------------------*/ void wr_to_rom(unsigned char shuju[],unsigned char address,unsigned char num) { unsigned char * pshuju= for(i=0;i&i++) { start(); send(addwr); //写入芯片地址 ack(); send(address+i);//写入存储地址 ack(); send(*(pshuju+i));//写数据- 15 - ack(); stop(); mdelay(20); } } /*-----------------------------------------------读出数据 ------------------------------------------------*/ void rd_from_rom(unsigned char shuju[],unsigned char address,unsigned char num) { unsigned char * pshuju= for(i=0;i&i++) { start(); //写入芯片地址 send(addwr); ack(); send(address+i);//写入存储地址 ack(); start(); send(addrd); //读入地址 ack(); *(pshuju+i)=read();//读数据 scl=0; noack(); stop(); } } 说明:以上程序为 I2C 总线驱动程序(模拟)用时只需关注其中的两个函数便可: rd_from_rom(shuzu[],address,num)和 wr_to_rom(shuzu[],address,num).I2c 总线的应用还需要继 续进行练习我仅仅只是一知半解而已!而且还有周坚老师的一个实验没有完成以后继续 做!!!!! !!!!! ------------------------------------以下为利用 I2C 总线实现的对 EEPROM 的读和写------------------void main() { uchar num[]={0x7f,0x6f,0x77,0x06,0x5b}; wp=0;//关写保护如若开则写不进; wr_to_rom(num,1,4); for(i=0;i&5;i++) num[i]=0x3f; P2=0x03;- 16 - rd_from_rom(num,1,5); for(i=0;i&5;i++) { p37=1; P0=num[i]; mdelay(1000); p37=0; } } 注:参考浩豚公司的师访程序! (8)数码管 999 数字计数程序(带有消影功能)非定时器应用 #include&reg52.h& #define uint unsigned int #define uchar unsigned char void delay(uint); sbit p37=P3^7; uchar bai,shi, uint num=0,a; uchar wela[]={0x00,0x01,0x02}; uchar code shuma[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f}; void main() { while(1) { for(num=0;num&1000;num++) { bai=num/100; shi=num%100/10; ge=num%10; for(a=200;a&0;a--)//a 的值可以实现数字显示速度的快慢; { P2=wela[2]; p37=1; P0=shuma[ge]; p37=0; delay(100); P0=0// 消影 P2=wela[1]; p37=1; P0=shuma[shi]; p37=0; delay(100); P0=0// 消影- 17 - P2=wela[0]; p37=1; P0=shuma[bai]; p37=0; delay(100); P0=0// 消影 } } } } void delay(uint i)//用延时程序实现数码管的动态显示; { while(i--); } 注:消影功能即是让数码管的不该亮的部分熄灭; (9)按键计数程序(待改进不太稳定而且只能计数 255 次)// 有待改进的程序用绿色 #include&reg52.h& #include&intrins.h& #define uint unsigned int #define uchar unsigned char void delay(uint); void init(); sbit p37=P3^7; sbit p34=P3^4; uchar bai,shi, uint num, uchar wela[]={0x00,0x01,0x02}; uchar code shuma[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f}; void main() { init(); while(1) { temp+=TH0; temp=_crol_(temp,8); temp+=TL0; num= ge=num%10; shi=num%100/10; bai=num/100; P2=wela[0]; p37=1; P0=shuma[bai];- 18 - p37=0; P0=0 delay(250); P2=wela[1]; p37=1; P0=shuma[shi]; p37=0; P0=0 delay(250); P2=wela[2]; p37=1; P0=shuma[ge]; p37=0; P0=0 delay(250); } } void delay(uint i) { while(i--); } void init() { TMOD=0X05;//jishuqi0 工作方式 1; (16 位定时计数器) // EA=1; // ET0=1; TH0=255; TL0=255; TF0=0; TR0=1; num=0; } 说明:此程序利用到了定时器 T0 的计数功能,注意在计数功能中一定不能开定时器中断, 定时器中断仅适用于定时器.存在的问题:此处三位数码管只能实现 0---255 的按键记数, 而且计数值有时不太稳定,即有时下一次 P3.4 口的按键可能会出现两个甚至两个以上的数 字累加, 造成计数不准确, 原因是按键没有加入去抖程序和松手检测但是数码管的动态显示 就不能加入这两个因为加后必出现停顿而停顿即意味着有两个数码管不能被点亮. 下面的程 序是采用数码管的静态显示,同时使用 T0 的计数功能,此时可以在程序中加入松手检测和 去抖过程.具体实现如下: #include&reg52.h& #include&intrins.h& #define uint unsigned int #define uchar unsigned char void delay(uint);- 19 - void init(); sbit p37=P3^7; sbit p34=P3^4; //uchar bai,shi, uchar code shuma[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f}; void main() { init(); while(1) { if(p34==0) { if(TL0==10) TL0=0; num=TL0; delay(1000); if(p34==0) { while(!p34); num=TL0; p37=1; P0=shuma[num]; p37=0; } } } } void delay(uint i) { while(i--); } void init() { TMOD=0X05;//jishuqi0 工作方式 1; (16 位定时计数器) // EA=1; // ET0=1; TH0=255; TL0=255; TF0=0; TR0=1; num=0; P2=0x00;- 20 - } 说明:这个程序的效果还可以就是有一条计数值只能是从 0―9 因而限制了其应用! 当然了这个程序还可以利用外部中断来完成.每按一次发生一次中断则计数一次. 以下程序可以实现六位按键计数功能:利用定时器 0 工作于计数方式 1,利用定时器 1 工作 于定时方式 1 并能产生 1.2―1.3MS 的定时中断,以对六位选通的数码管进行轮翻扫描实现 动态显示.程序中的参数是经我一再调试最终产生的无闪烁参数:具体程序如下 #include&reg52.h& #include&intrins.h& #define uint unsigned int #define uchar unsigned char void delay(uint); void init(); sbit p37=P3^7; sbit p34=P3^4; uchar wei,du,bit_wei[6]; uchar wela[]={0x00,0x01,0x02,0x03,0x04,0x05}; uchar code shuma[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f}; void main() { init(); while(1) { num=TH0*256+TL0; bit_wei[5]=0; bit_wei[4]=num/10000; bit_wei[3]=num/1000%10; bit_wei[2]=num/100%10; bit_wei[1]=num/10%10; bit_wei[0]=num%10; p37=1; P2=wela[wei]; P0=shuma[bit_wei[du]]; p37=0; delay(5); if(wei==5) wei=0; if(du==0) du=5; } } void init() {- 21 - TMOD=0X15;//定时器 1 工作于定时方式 1,定时器 0 工作于记数方式 1; EA=1; ET1=1; TH0=0; TL0=0; TH1=()/256; TL1=()%256;//定时 3 毫秒; TF0=0; TR0=1; TR1=1; num=0; wei=0; du=5; } void timer1()interrupt 3 { TH1=()/256; TL1=()%256; wei++; du--; } void delay(uint i) { while(i--); } 说明:其中 NUM 的值通过按键次数来由定时器 0 获得. (10)普通时钟程序(存在问题:调节一个键的时候其他数码管跟着变化! ) #include&reg52.h& #define uchar unsigned char #define uint unsigned int sbit p37=P3^7; sbit p16=P1^6; sbit p32=P3^2; sbit p33=P3^3;//是否启动定时功能标识位;在中断服务程序中设置; uchar code huayang[]={0x7f,0xbf,0xdf,0xef,0xf7,0xfb,0xfd,0xfe,0xff,0xff,0x00,0,0x55,0x55,0xaa,0xaa}; uchar code shuma[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f}; uchar code wela[]={0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07}; uchar hour,min,//时,分,秒的计数器及用于设置定时时间的中间计数器//数码管位选标志//实现 1 秒定时功能控制寄存器;//花样流水灯控制;- 22 - void init() { TMOD=0X11;//定时器 1 工作方式 1 定时功能;定时器 0 工作方式 1 定时功能; TH0=()/256;//设置各个定时计数器的处置 TL0=()%256; TH1=()/256; TL1=()%256; //IT1=0;//电平触发 EA=1;//开总中断 ET0=1;//开定时计数器 0 中断 ET1=1;//开定时计数器 1 中断 TR0=1;//启动定时器 0 TR1=1;//启动定时器 1 hour=00; min=00; sec=00; flag=0; a=0; i=0; } void msdelay(uint i) { while(i--); } void display() { p37=1;P2=wela[0];P0=shuma[hour/10];p37=0; p37=1;P2=wela[1];P0=shuma[hour%10];p37=0; p37=1;P2=wela[2];P0=0x40;p37=0; p37=1;P2=wela[3];P0=shuma[min/10];p37=0; p37=1;P2=wela[4];P0=shuma[min%10];p37=0; p37=1;P2=wela[5];P0=0x40;p37=0; p37=1;P2=wela[6];P0=shuma[sec/10];p37=0; p37=1;P2=wela[7];P0=shuma[sec%10];p37=0; } uchar keyscan()//行列交叉扫描法检测键盘; { uchar temp, P3=0xf0; temp=P3; temp=temp&0xf0; if(temp!=0xf0) { msdelay(10000);- 23 - temp=P3; temp=temp&0xf0; if(temp!=0xf0) { temp=P3; temp=temp&0xf0; } } P3=0x0f; tmp=P3; tmp=tmp&0x0f; if(tmp!=0x0f) { msdelay(10000); tmp=P3; tmp=tmp&0x0f; if(tmp!=0x0f) { tmp=P3; tmp=tmp&0x0f; } } temp=temp+ } //定时计数器 1 中断服务子程序; void timer1()interrupt 3 { TH1=()/256; TL1=()%256; switch(flag)//实现对数码管的位选及显示; { case 0:p37=1;P2=wela[0];P0=shuma[hour/10];p37=0;msdelay(100); case 1:p37=1;P2=wela[1];P0=shuma[hour%10];p37=0;msdelay(100); case 2:p37=1;P2=wela[2];P0=0x40;p37=0;msdelay(100); case 3:p37=1;P2=wela[3];P0=shuma[min/10];p37=0;msdelay(100); case 4:p37=1;P2=wela[4];P0=shuma[min%10];p37=0;msdelay(100); case 5:p37=1;P2=wela[5];P0=0x40;p37=0;msdelay(100); case 6:p37=1;P2=wela[6];P0=shuma[sec/10];p37=0;msdelay(100); case 7:p37=1;P2=wela[7];P0=shuma[sec%10];p37=0;msdelay(100); default: } flag++; if(flag==8)- 24 - flag=0; } //定时器 0 中断服务子程序:包括了时间更新,花样流水灯显示,定时器开启控制,扬声器 开启控制; void timer0()interrupt 1 { TH0=()/256;//设置各个定时计数器的处置 TL0=()%256; a++; if(a==20) { a=0; sec++; if(sec==60) { sec=0; min++; if(min==60) { min=0; hour++; if(hour==24) hour=0; } } } i++; if(i==16) i=0; P1=huayang[i]; } void main() { init(); while(1) { num=keyscan(); switch(num)//键值确定及其对应键功能服务程序; { case 0x7e:hour++;if(hour==24)hour=0;display(); case 0x7d:{hour--;if(hour==255)hour=23;} case 0x7b:{min++;if(min==60)min=0;display();}- 25 - // // } }case 0x77:{min--;if(min==255)min=59;display();} case 0xbe:{sec++;if(sec==60)sec=0;display();} case 0xbd:{sec--;if(sec==255)sec=59;display();} case 0xbb:; case 0xb7:; case 0xde:; case 0xdd:; case 0xdb:p32=0; case 0xd7:p33=0; default:} 对以上程序的改进:实现的功能:1.时钟显示.2.定时动作.其中定时到可以无限地做定时 到得动作而不是原来的一分钟!而且定时动作的取消可以通过手动按键来实现! #include&reg52.h& #define uchar unsigned char #define uint unsigned int sbit p37=P3^7; sbit p16=P1^6; sbit p30=P3^0; sbit p31=P3^1; sbit p32=P3^2; sbit p33=P3^3; ////是否启动定时功能标识位;在中断服务程序中设置; uchar code huayang[]={0x7f,0xbf,0xdf,0xef,0xf7,0xfb,0xfd,0xfe,0xff,0xff,0x00,0,0x55,0x55,0xaa,0xaa,0xaa, 0xaa,0x55,0x55,0,0x00,0xff,0xff,0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f}; uchar code shuma[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f}; uchar code wela[]={0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07}; uchar hour,min,sec,sethour,//时,分,秒的计数器及用于设置定时时间的中间计数器//数码管位选标志 uchar a,b,c,d,e;//实现 1 秒定时功能控制寄存器;//花样流水灯控制; void init() { TMOD=0X11;//定时器 1 工作方式 1 定时功能;定时器 0 工作方式 1 定时功能; TH0=()/256;//设置各个定时计数器的处置 TL0=()%256; TH1=()/256; TL1=()%256; //IT1=0;//电平触发 EA=1;//开总中断 ET0=1;//开定时计数器 0 中断- 26 - ET1=1;//开定时计数器 1 中断 TR0=1;//启动定时器 0 TR1=1;//启动定时器 1 hour=00; min=00; sec=00; sethour=00; setmin=00; flag=0; a=0;b=0;c=0;d=0;e=0; i=0; } void msdelay(uint i) { while(i--); } void display(uchar flag) { switch(flag)//实现对数码管的位选及显示; { case 0:p37=1;P2=wela[0];P0=shuma[hour/10];p37=0; case 1:p37=1;P2=wela[1];P0=shuma[hour%10];p37=0; case 2:p37=1;P2=wela[2];P0=0x40;p37=0; case 3:p37=1;P2=wela[3];P0=shuma[min/10];p37=0; case 4:p37=1;P2=wela[4];P0=shuma[min%10];p37=0; case 5:p37=1;P2=wela[5];P0=0x40;p37=0; case 6:p37=1;P2=wela[6];P0=shuma[sec/10];p37=0; case 7:p37=1;P2=wela[7];P0=shuma[sec%10];p37=0; default: } msdelay(25); P0=0x00; msdelay(25); } /*uchar keyscan()//行列交叉扫描法检测键盘; { uchar temp, P3=0xf0; temp=P3; temp=temp&0xf0; if(temp!=0xf0) { msdelay(2000); temp=P3;- 27 - temp=temp&0xf0; if(temp!=0xf0) { temp=P3; temp=temp&0xf0; } } P3=0x0f; tmp=P3; tmp=tmp&0x0f; if(tmp!=0x0f) { msdelay(2000); tmp=P3; tmp=tmp&0x0f; if(tmp!=0x0f) { tmp=P3; tmp=tmp&0x0f; } } temp=temp+ }*/ //定时计数器 1 中断服务子程序; void timer1()interrupt 3 { TH1=()/256; TL1=()%256; display(flag); flag++; if(flag==8) flag=0; } //定时器 0 中断服务子程序:包括了时间更新,花样流水灯显示,定时器开启控制,扬声器 开启控制; void timer0()interrupt 1 { TH0=()/256;//设置各个定时计数器的处置 TL0=()%256; a++; b++; if(a==20) {- 28 - a=0; sec++; if(sec==60) { sec=0; min++; if(min==60) { min=0; hour++; if(hour==24) hour=0; } } } while(b==5) { b=0; if((d==sethour)&&(e==setmin)) { // sethour=0; // setmin=0; if(1) { i++; if(i==32) i=0; P1=huayang[i]; } } else { P1=~P1; } } } void main() { // init(); while(1) { /*num=keyscan();- 29 - switch(num)//键值确定及其对应键功能服务程序; { case 0xbe:liushui=0; case 0xbd:; case 0xbb:; case 0xb7:; case 0xde:; case 0xdd:; case 0xdb:p32=0; case 0xd7:p33=0; default: }*/ if(!p30)//gaiyikankan { msdelay(20000); if(!p30) { hour++; if(hour==24) hour=0; } } if(!p32) { msdelay(20000); c= while(!p32) { //liushui=~ // if(hour==255) // hour=24; hour= sethour++; msdelay(20000); } hour=c; if(sethour==hour) d= } if(!p31) { msdelay(10000); if(!p31) {- 30 - min++; if(min==60) min=0; } } if(!p33) { c= msdelay(10000); while(!p33) { // dingshi=! min= setmin++; msdelay(20000); if(setmin==60) setmin=0; } min=c; } if(hour==sethour)//使用 if 语句来实现满足条件则做某个动作否则什么都不做此举 可以实现 d= if(min==setmin)//在实际时间等于设定时间时及时地将实际时间保存在寄存器 d,e 之中,而当过了一分钟后设定时间与实际时间不相等时 d,e 中的值 e=//不会被改变这样就可延长当定时到后所做动作的时间(实际可为任意 长只要计算机允许)而不是仅仅表现为一分钟. } } 说明:1.此时钟我调了一天半,觉得技术含量比较高的地方就是主函数中最后的那两句:实 现了定时动作的任意常时间. 2.本程序对按键的扫描及功能的控制开始使用的是矩阵键盘!但不知怎地就是老闪而且按控 制时的键时分为和秒位对应的数码管也跟着不停的闪晃, 有待研究! 后来参考师先生的程序 才最后决定使用独立键盘,效果还行!关键是要对有些延时的参数设置好才可以!师先生的 程序实现的功能仅为显示时间调时调分. 我在此基础上又加入了定时模块以及定时到后动作 可以无限进行下去而不是当时间值过了设定值之后就不在做时间到得动作了! 除非人工通过 按键的方式强制叫停! (11)DS18B20 温度传感器应用程序(基于 min 开发板) #include&reg52.h& #define uchar unsigned char #define uint unsigned int #include&intrins.h& sbit DQ=P1^3; bit flag_- 31 - sfr dataled=0x80; uchar num,count, uchar code huayang[]={0x7f,0xbf,0xdf,0xef,0xf7,0xfb,0xfd,0xfe,0xff,0xff,0x00,0,0x55,0x55,0xaa,0xaa,0xaa, 0x55,0x55,0,0x00,0xff,0xff,0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f}; uchar tab[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f}; uchar str[6];//显示寄存器; void delay(uint); void DS18B20_init() { uchar x=0; DQ=1;//复位 DQ; delay(8);//稍作延时; DQ=0;//由单片机将 DQ 拉低; delay(80);//精确延时大于 480 微秒;DS18B20 的内部构造要求; DQ=1; delay(10); x=DQ;//稍作延时后如果 x=0 则表明初始化成功否则失败需重新初始化; delay(5); } void delay(uint i) { while(i--); } uchar read_one_char(void) { uchar i=0; uchar dat=0; for(i=8;i&0;i--) { DQ=0;//给脉冲信号; dat&&=1; DQ=1;//给脉冲信号; if(DQ) dat|=0x80; delay(5); } } void write_one_char(uchar dat) { uchar i=0; for(i=8;i&0;i--)- 32 - { DQ=0; DQ=dat&0x01; delay(5); DQ=1; dat&&=1; } delay(5); } uint read_temperature(void) { uchar a=0; uint b=0; uint t=0; DS18B20_init(); write_one_char(0xcc);//跳过读序列号的操作 write_one_char(0x44);//启动温度转换; delay(200); DS18B20_init(); write_one_char(0xcc);//跳过读序列号的操作; write_one_char(0xbe);//读温度寄存器等 9 个寄存器前两个就是温度 a=read_one_char();//依次读取地位和高位; b=read_one_char(); b&&=8; t=a+b; } void timer0()interrupt 1 { num++; TH0=0 TL0=0xf0; if(num==50) { num=0; flag_get=1;//标志位有效; second++; if(second==60) { second=0; } } count++; if(count==1)- 33 - {P2=0;dataled=str[0];}//数码管扫描; if(count==2) {P2=1;dataled=str[1];} if(count==3) {P2=2;dataled=str[2];}//数码管扫描; if(count==4) {P2=3;dataled=str[3];} if(count==5) {P2=4;dataled=str[4];}//数码管扫描; if(count==6) {P2=5;dataled=str[5];count=0;} } void main() { uchar temph,templ,i=0; TMOD|=0X01; TH0=0XEF; TL0=0XF0; IE=0X82; TR0=1; P2=0X00; count=0; while(1) { str[5]=0x39; str[1]=tab[temph/100]; str[2]=tab[temph%100/10]; str[3]=tab[temph%10]|0x80; str[4]=tab[templ]; if(flag_get==1) { temp=read_temperature(); if(temp&0x8000) { str[0]=0x40; temp=~ temp+=1; } else str[0]=0; temph=temp&&4; templ=temp&0x0f; templ=templ*6/10; flag_get=0;- 34 - } if(temph&30) { P1=0 delay(10000); P1=~P1; delay(10000); } else if(temph&30) { for(i=0;i&32;i++) { P1=huayang[i]; delay(5000); } } else {P1=0XFF;delay(10000);} } } 说明: 本程序实现的功能是利用温度传感器测量环境的温度, 并将测量的结果显示于数码管 之上,当温度小于 29 摄氏度时流水灯开始流动当温度大于 29 而小于 31 摄氏度时灯全灭, 当温度大于 31 摄氏度时八个等同时闪烁.存在问题:运行时现象并非如预料的那样好时下 虽已是 32 摄氏度但偶尔数码管上偶尔会显示负的温度很是诡异,程序在这个程序中有一些 程序可以被制成软件包供移植使用(DS18B20 专用程序包) .具体如下: (11.1)DS18B20 专用程序包: void delay(uint i) { while(i--); } void DS18B20_init() { uchar x=0; DQ=1;//复位 DQ; delay(8);//稍作延时; DQ=0;//由单片机将 DQ 拉低; delay(80);//精确延时大于 480 微秒;DS18B20 的内部构造要求; DQ=1; delay(10); x=DQ;//稍作延时后如果 x=0 则表明初始化成功否则失败需重新初始化; delay(5); } uchar read_one_char(void)- 35 - { uchar i=0; uchar dat=0; for(i=8;i&0;i--) { DQ=0;//给脉冲信号; dat&&=1; DQ=1;//给脉冲信号; if(DQ) dat|=0x80; delay(5); } } void write_one_char(uchar dat) { uchar i=0; for(i=8;i&0;i--) { DQ=0; DQ=dat&0x01; delay(5); DQ=1; dat&&=1; } delay(5); } uint read_temperature(void) { uchar a=0; uint b=0; uint t=0; DS18B20_init(); write_one_char(0xcc);//跳过读序列号的操作 write_one_char(0x44);//启动温度转换; delay(200); DS18B20_init(); write_one_char(0xcc);//跳过读序列号的操作; write_one_char(0xbe);//读温度寄存器等 9 个寄存器前两个就是温度 a=read_one_char();//依次读取地位和高位; b=read_one_char(); b&&=8; t=a+b;- 36 - } 说明: 用户在使用此软件包前 1.需要更具具体的情况定义好传感器单线的管脚连在单片机的 哪个管脚上此开发板在 P1.3 上.因此定义为 sbit DQ=P1^3;2.用户需要关注的最重要的一个 函数是最后一个 read_temperature()函数并要在主函数中定义一个寄存器用于存放此温度值 3.用户需要注意的地方就是传感器的温度是一双字节 16 位的形式传到主函数中的例如 temp 变量中, 其中又包含了整数部分和小数部分, 因此使用者还要在主函数中使用一些即成的代 码对寄存器 temp 中的值进行分离并分别将整数和小数部分存于 temph 和 templ 之中,下面 才是调用显示程序在显示器上对温度进行显示. 以下程序的红字部分既是主函数中实现温度 分离的即成代码(用户只需使用而不必问为什么?) //以下是一个用户接口的例子 if(flag_get==1) { temp=read_temperature(); if(temp&0x8000) { str[0]=0x40; temp=~ temp+=1;//若为负则在第一位添一个负号! } else str[0]=0; temph=temp&&4; templ=temp&0x0f; templ=templ*6/10;//分离温度值并分别保存! flag_get=0; } 红色部分是用户使用以上软件包必须重点关注的地方即 1.传感器的温度过来了存在哪里?2. 传感器传来的是一个 16 位双字节的二进制码(其中包含了整数部分和小数部分)怎样将二 者进行分离并化成十进制的数用于在数码管上进行显示. (12)温度传感器的使用实验(实现温度显示,当温度超过 30 摄氏度时发声音警报,当温 度低于 29 摄氏度时显示流水灯发出光警报,本实验可用于工业温控系统之中. ) #include&reg52.h& #define uchar unsigned char #define uint unsigned int #include&intrins.h& sbit DQ=P1^3; bit flag_ sfr dataled=0x80; static uchar temph=0,templ=0; uchar num,count,second,a=0; uchar code huayang[]= {0x3f,0x3f,0x9f,0xaf,0xb7,0xbb,0xbd,0xbe,0xbf,0xbf,0x00,0,0x15,0x15,0x2a,0x2a,0x2a,0x15,0x- 37 - 15,0,0x00 ,0xbf,0xbf,0xbe,0xbe,0xbd,0xbd,0xbb,0xbb,0xb7,0xaf,0x9f,0x3f,0x3f}; uchar code tab[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f}; sbit speaker=P1^6; //定义音乐输出端口,需要连接到对应的喇叭,请参考电路图 uint timer1,timer2; uchar str[6];//显示寄存器; void delay1(uint); void DS18B20_init() { uchar x=0; DQ=1;//复位 DQ; delay1(8);//稍作延时; DQ=0;//由单片机将 DQ 拉低; delay1(80);//精确延时大于 480 微秒;DS18B20 的内部构造要求; DQ=1; delay1(10); x=DQ;//稍作延时后如果 x=0 则表明初始化成功否则失败需重新初始化; delay1(5); } void delay1(uint i) { while(i--); } void timer1_inter() interrupt 3 { TR1=0; if((temph&=30)&&(temph&=34)) { speaker=! TH1=timer1; TL1=timer2; } TR1=1; } uchar read_one_char(void) { uchar i=0; uchar dat=0; for(i=8;i&0;i--) { DQ=0;//给脉冲信号; dat&&=1; DQ=1;//给脉冲信号;- 38 - if(DQ) dat|=0x80; delay1(5); } } void write_one_char(uchar dat) { uchar i=0; for(i=8;i&0;i--) { DQ=0; DQ=dat&0x01; delay1(5); DQ=1; dat&&=1; } delay1(5); } uint read_temperature(void) { uchar a=0; uint b=0; uint t=0; DS18B20_init(); write_one_char(0xcc);//跳过读序列号的操作 write_one_char(0x44);//启动温度转换; delay1(200); DS18B20_init(); write_one_char(0xcc);//跳过读序列号的操作; write_one_char(0xbe);//读温度寄存器等 9 个寄存器前两个就是温度 a=read_one_char();//依次读取地位和高位; b=read_one_char(); b&&=8; t=a+b; } void timer0()interrupt 1 { num++; a++; TH0=()/256; TL0=()%256; if(num==6)- 39 - { num=0; flag_get=1;//标志位有效; second++; if(second==40) { second=0; } } count++; if(count==1) {P2=0;dataled=str[0];}//数码管扫描; if(count==2) {P2=1;dataled=str[1];} if(count==3) {P2=2;dataled=str[2];}//数码管扫描; if(count==4) {P2=3;dataled=str[3];} if(count==5) {P2=4;dataled=str[4];}//数码管扫描; if(count==6) {P2=5;dataled=str[5];count=0;} if(a==4) { str[5]=0x39; str[1]=tab[temph/100]; str[2]=tab[temph%100/10]; str[3]=tab[temph%10]|0x80; str[4]=tab[templ]; if(flag_get==1) { temp=read_temperature(); if(temp&0x8000) { str[0]=0x40; temp=~ temp+=1; } else str[0]=0; temph=temp&&4; templ=temp&0x0f; templ=templ*6/10; flag_get=0;- 40 - } } } void main() { TMOD=0X11; TH0=0XEF; TL0=0XF0; IE=0X8a; TR0=1; TR1=1; P2=0X00; count=0; while(1) { if((temph&=30)&&(temph&34)) { P1=0 if((temph&=30)&&(temph&=34)) 完从头再来 { timer1=()/256; timer2=()%256; delay1(50000);delay1(50000);delay1(50000); timer1=()/256; timer2=()%256; delay1(14000); } } if((temph&=28)&&(temph&25)) { speaker=0; for(j=0;j&34;j++) { if((temph&=29)&&(temph&25)) { P1=huayang[j]; delay1(10000); }//音乐数组长度 ,唱- 41 - } } } } (12.1) 对以上实验作出的改进可以在液晶显示器上显示日期和字符但不足之处在于不能跟 随温度传感器进行温度实时动态显示(程序急需改进,等做完嵌入式实验以后在改之)而且 液晶显示部分的程序我没有理解,让我匪夷所思! #include&reg52.h& #define uchar unsigned char #define uint unsigned int #include&intrins.h& sbit DQ=P1^3; sbit k1=P3^0; bit flag_ sfr dataled=0x80; static uchar temph=0,templ=0; uchar num,count,second,a=0; uchar code huayang[]={0x3f,0x3f,0x9f,0xaf,0xb7,0xbb,0xbd,0xbe,0xbf,0xbf,0x00,0,0x15,0x15,0x2a,0x2a,0x 2a,0x15,0x15,0,0x00,0xbf,0xbf,0xbe,0xbe,0xbd,0xbd,0xbb,0xbb,0xb7,0xaf,0x9f,0x3f,0x3f}; uchar code tab[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f}; sbit speaker=P1^6; //定义音乐输出端口,需要连接到对应的喇叭,请参考电路图 uint timer1,timer2; uchar str[6];//显示寄存器; sbit rs=P2^4; sbit rw=P2^5; sbit e=P2^6; #define dataport P0 const uchar nodisp=0;//无显示; const uchar nocur=1;//有显示但无光标; const uchar curNoFlash=2;//有光标但无闪烁; const uchar curflash=3;//有光标且有闪烁; void lcdpos(uchar,uchar);//确定光标的位置; void lcdwd(uchar);//写字符; void lcdwc(uchar);//送控制字且检测忙信号; void lcdwcn(uchar);//送控制字但不检测忙信号; void mdelay(uchar);//延迟子程序; void waitidle();//正常读/写操作之前的检测 LCD 控制状态; //延迟子程序; void mdelay(uchar j) { uint i=0;- 42 - for(;j&0;j--) for(i=0;i&125;i++); } //实现写字符功能将某个字符显示与 ypos 行 xpos 列. void writechar(uchar c,uchar xpos,uchar ypos) { lcdpos(xpos,ypos);//调用光标设置子程序; lcdwd(c);//调用写字符子程序; } //功能在制定的位置显示字符串; void writestring(uchar *s,uchar xpos,uchar ypos) { if(*s=='\0') for(i=0;;i++) { if(*(s+i)=='\0') writechar(*(s+i),xpos,ypos); xpos++; if(xpos&=15) } } //设置光标的功能; void setcur(uchar para) { mdelay(2); switch(para) { case 0:{lcdwc(0x08);} case 1:{lcdwc(0x0c);} case 2:{lcdwc(0x0e);} case 3:{lcdwc(0x0f);} default: } } //实现清屏功能的子函数 void clrlcd() { lcdwc(0x01);//调用送控制字子程序; }- 43 - //正常读写之前的检测 lcd 控制状态; void waitidle() { rs=0; rw=1; e=1; _nop_(); for(;;) { tmp= tmp&=0x80; if(tmp==0) } e=0; } //实现写字符功能; void lcdwd(uchar c) { waitidle(); rs=1; rw=0; dataport=c; e=1; _nop_(); _nop_(); e=0; } //送控制字子程序(检测忙信号) ; void lcdwc(uchar c) { waitidle(); lcdwcn(c);//调用送控制字子程序(不检测忙信号) ; } //送控制字子程序(不检测忙信号) ; void lcdwcn(uchar c) { rs=0; rw=0; dataport=c; e=1; _nop_(); e=0;- 44 - } //设置第 xpos,ypos 光标所在的地址; void lcdpos(uchar xpos,uchar ypos) { xpos&=0x0f; ypos&=0x01; if(ypos==0) tmp= else tmp=xpos+0x40; tmp|=0x80; lcdwc(tmp); } //复位 LCD 控制器; void lcdreset() { mdelay(15); lcdwc(0x38); lcdwc(0x08); lcdwc(0x01); lcdwc(0x06); lcdwc(0x0c); }void delay1(uint); void DS18B20_init() { uchar x=0; DQ=1;//复位 DQ; delay1(8);//稍作延时; DQ=0;//由单片机将 DQ 拉低; delay1(80);//精确延时大于 480 微秒;DS18B20 的内部构造要求; DQ=1; delay1(10); x=DQ;//稍作延时后如果 x=0 则表明初始化成功否则失败需重新初始化; delay1(5); } void delay1(uint i) { while(i--); } void timer1_inter() interrupt 3- 45 - { TR1=0; if((temph&=30)&&(temph&=34)) { speaker=! TH1=timer1; TL1=timer2; } TR1=1; } uchar read_one_char(void) { uchar i=0; uchar dat=0; for(i=8;i&0;i--) { DQ=0;//给脉冲信号; dat&&=1; DQ=1;//给脉冲信号; if(DQ) dat|=0x80; delay1(5); } } void write_one_char(uchar dat) { uchar i=0; for(i=8;i&0;i--) { DQ=0; DQ=dat&0x01; delay1(5); DQ=1; dat&&=1; } delay1(5); } uint read_temperature(void) { uchar a=0; uint b=0; uint t=0; DS18B20_init();- 46 - write_one_char(0xcc);//跳过读序列号的操作 write_one_char(0x44);//启动温度转换; delay1(200); DS18B20_init(); write_one_char(0xcc);//跳过读序列号的操作; write_one_char(0xbe);//读温度寄存器等 9 个寄存器前两个就是温度 a=read_one_char();//依次读取地位和高位; b=read_one_char(); b&&=8; t=a+b; } void timer0()interrupt 1 { num++; a++; TH0=()/256; TL0=()%256; if(num==6) { num=0; flag_get=1;//标志位有效; second++; if(second==40) { second=0; } } count++; if(count==1) {P2=0;dataled=str[0];}//数码管扫描; if(count==2) {P2=1;dataled=str[1];} if(count==3) {P2=2;dataled=str[2];}//数码管扫描; if(count==4) {P2=3;dataled=str[3];} if(count==5) {P2=4;dataled=str[4];}//数码管扫描; if(count==6) {P2=5;dataled=str[5];count=0;} if(a==4) { str[5]=0x39;- 47 - str[1]=tab[temph/100]; str[2]=tab[temph%100/10]; str[3]=tab[temph%10]|0x80; str[4]=tab[templ]; if(flag_get==1) { temp=read_temperature(); if(temp&0x8000) { str[0]=0x40; temp=~ temp+=1; } else str[0]=0; temph=temp&&4; templ=temp&0x0f; templ=templ*6/10; flag_get=0; } } } void main() { uchar *s=&&; uchar xpos, uchar p[6]; lcdreset(); clrlcd(); setcur(nocur); TMOD=0X11; TH0=0XEF; TL0=0XF0; IE=0X8a; TR0=1; TR1=1; P2=0X00; count=0; while(1) { p[0]=' ';- 48 - p[1]=temph/100; p[2]=temph%100/10; p[3]=temph%10; p[4]='.'; p[5]= p[6]='c'; if((temph&=30)&&(temph&34)) { P1=0 if((temph&=30)&&(temph&=34)) 从头再来 { timer1=()/256; timer2=()%256; delay1(50000);delay1(50000);delay1(50000); timer1=()/256; timer2=()%256; delay1(14000); } } if((temph&=28)&&(temph&25)) { speaker=0; for(j=0;j&34;j++) { if((temph&=29)&&(temph&25)) { P1=huayang[j]; delay1(10000); } } } while(!k1) { xpos=4; ypos=0; writestring(s,xpos,ypos); xpos=4; ypos=1; // writestring(&T=&,xpos,ypos); writestring(&80C51&,6,1); // writestring(str,8,1); // while(!k1);//音乐数组长度 ,唱完- 49 - } // // // xpos=8; ypos=1; writestring(str,xpos,ypos);//匪夷所思怎么会显示呢?} } (13)串行通信程序设计(实现由单片机发送数据到 PC 而后再由主机 PC 将单片机发过去 的数据返回来在显示区显示) #include&reg52.h& #define uchar unsigned char #define uint
bit flag=0; void delay(uint i) { while(i--); } void init_searial_com() { SCON=0X50; TMOD|=0X20; PCON=0X00; TH1=0xe6; TL1=0xe6; TR1=1; IE|=0X90; TI=0; RI=0; } void send_char_searial(uchar ch) { SBUF= while(TI==0); TI=0; } void main() { init_searial_com(); while(1) { if(flag==1)- 50 - { flag=0; send_char_searial(a); delay(20000); } } } void searial_inter()interrupt 4 { if(RI) { RI=0; ch=SBUF; a= flag=1; } } (14) 串行通讯与温度传感器实验实验 (在数码管上显示温度同时与计算机通信将数码管上 的那个温度值发送到串行调试软件的接受区:完全的实时跟踪且以字符形式显示) (使用查 询的方式实现串行通讯) (光报警) #include&reg52.h& #define uchar unsigned char #define uint unsigned int #include&intrins.h& uchar dis[8]; sbit DQ=P1^3; sbit k1=P3^2; bit flag_ bit flag=0; sfr dataled=0x80; float f=0.00; static uchar temph=0,templ=0; uchar num,count,second,a=0,b=0; uchar change[]={0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39}; uchar code huayang[]={0x3f,0x3f,0x9f,0xaf,0xb7,0xbb,0xbd,0xbe,0xbf,0xbf,0x00,0,0x15,0x15,0x2a,0x2a,0x 2a,0x15,0x15,0,0x00,0xbf,0xbf,0xbe,0xbe,0xbd,0xbd,0xbb,0xbb,0xb7,0xaf,0x9f,0x3f,0x3f}; uchar code tab[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f}; uint timer1,timer2; uchar str[6];//显示寄存器; //单片机接收到一帧(一个字节)以后向主机发送一个字节函数!- 51 - void send_char_com(uchar ch) { SBUF= while(TI==0); TI=0; } void delay1(uint); void DS18B20_init() { uchar x=0; DQ=1;//复位 DQ; delay1(8);//稍作延时; DQ=0;//由单片机将 DQ 拉低; delay1(80);//精确延时大于 480 微秒;DS18B20 的内部构造要求; DQ=1; delay1(10); x=DQ;//稍作延时后如果 x=0 则表明初始化成功否则失败需重新初始化; delay1(5); } void delay1(uint i) { while(i--); } uchar read_one_char(void) { uchar i=0; uchar dat=0; for(i=8;i&0;i--) { DQ=0;//给脉冲信号; dat&&=1; DQ=1;//给脉冲信号; if(DQ) dat|=0x80; delay1(5); } } void write_one_char(uchar dat) { uchar i=0; for(i=8;i&0;i--) { DQ=0;- 52 - DQ=dat&0x01; delay1(5); DQ=1; dat&&=1; } delay1(5); } uint read_temperature(void) { uchar a=0; uint b=0; uint t=0; DS18B20_init(); write_one_char(0xcc);//跳过读序列号的操作 write_one_char(0x44);//启动温度转换; delay1(200); DS18B20_init(); write_one_char(0xcc);//跳过读序列号的操作; write_one_char(0xbe);//读温度寄存器等 9 个寄存器前两个就是温度 a=read_one_char();//依次读取地位和高位; b=read_one_char(); b&&=8; t=a+b; } void timer0()interrupt 1 { num++; a++; TH0=()/256; TL0=()%256; if(num==6) { num=0; flag_get=1;//标志位有效; second++; if(second==40) { second=0; } } count++; if(count==1) {P2=0;dataled=str[0];}//数码管扫描;- 53 - if(count==2) {P2=1;dataled=str[1];} if(count==3) {P2=2;dataled=str[2];}//数码管扫描; if(count==4) {P2=3;dataled=str[3];} if(count==5) {P2=4;dataled=str[4];}//数码管扫描; if(count==6) {P2=5;dataled=str[5];count=0;} if(a==4) { str[5]=0x39; str[1]=tab[temph/100]; str[2]=tab[temph%100/10]; str[3]=tab[temph%10]|0x80; str[4]=tab[templ]; if(flag_get==1) { temp=read_temperature(); if(temp&0x8000) { str[0]=0x40; temp=~ temp+=1; } else str[0]=0; temph=temp&&4; templ=temp&0x0f; templ=templ*6/10; flag_get=0; } } } void main() { TMOD=0X21; PCON=0X00;//SMOD=0; SCON=0X40;//串口工作方式 1 允许接受并关闭 RI,TI; TH0=0XEF;- 54 - //TL0=0XF0; TH1=0Xe6; TL1=0Xe6;//波特率取 1200b/s 对应晶振为 12MHZ; EA=1; ES=1; ET0=1; TR0=1; TR1=1;//启动定时器 0,1; P2=0X00; while(1) { if((temph&=30)&&(temph&34)) { P1=0 if((temph&=30)&&(temph&=34)) { delay1(10000); P1=0x00; delay1(10000); } } if((temph&=28)&&(temph&25)) { for(j=0;j&34;j++) { if((temph&=29)&&(temph&25)) { P1=huayang[j]; delay1(1000); } } } dis[0]=change[temph/10]; dis[1]=change[temph%10]; dis[2]=0x2e; dis[3]=change[templ]; dis[4]=0x64; dis[5]=0x75; dis[6]=0x0a; dis[7]=0x00; i=0; while(dis[i]!=0x00) {//音乐数组长- 55 - SBUF=dis[i]; while(TI==0); TI=0; i++; } for(j=0;j&10000;j++); } } 更改后的 #include&reg52.h& #define uchar unsigned char #define uint unsigned int #include&intrins.h& sbit DQ=P1^3; bit flag_ sfr dataled=0x80; static uchar temph=0,templ=0; uchar num,count,second,a=0,b=0; uchar change[]={0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39}; uchar code huayang[]={0x3f,0x3f,0x9f,0xaf,0xb7,0xbb,0xbd,0xbe,0xbf,0xbf,0x00,0,0x15,0x15,0x2a,0x2a,0x 2a,0x15,0x15,0,0x00,0xbf,0xbf,0xbe,0xbe,0xbd,0xbd,0xbb,0xbb,0xb7,0xaf,0x9f,0x3f,0x3f}; uchar code tab[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f}; uchar str[6];//显示寄存器; uchar dis[8]; void send_char_com(uchar ch) { SBUF= while(TI==0); TI=0; } void delay1(uint); void DS18B20_init() { uchar x=0; DQ=1;//复位 DQ; delay1(8);//稍作延时; DQ=0;//由单片机将 DQ 拉低; delay1(80);//精确延时大于 480 微秒;DS18B20 的内部构造要求; DQ=1; delay1(10); x=DQ;//稍作延时后如果 x=0 则表明初始化成功否则失败需重新初始化;- 56 - delay1(5); } void delay1(uint i) { while(i--); } uchar read_one_char(void) { uchar i=0; uchar dat=0; for(i=8;i&0;i--) { DQ=0;//给脉冲信号; dat&&=1; DQ=1;//给脉冲信号; if(DQ) dat|=0x80; delay1(5); } } void write_one_char(uchar dat) { uchar i=0; for(i=8;i&0;i--) { DQ=0; DQ=dat&0x01; delay1(5); DQ=1; dat&&=1; } delay1(5); } uint read_temperature(void) { uchar a=0; uint b=0; uint t=0; DS18B20_init(); write_one_char(0xcc);//跳过读序列号的操作 write_one_char(0x44);//启动温度转换; delay1(200); DS18B20_init();- 57 - write_one_char(0xcc);//跳过读序列号的操作; write_one_char(0xbe);//读温度寄存器等 9 个寄存器前两个就是温度 a=read_one_char();//依次读取地位和高位; b=read_one_char(); b&&=8; t=a+b; } void timer0()interrupt 1 { num++; a++; TH0=()/256; TL0=()%256; if(num==6) { num=0; flag_get=1;//标志位有效; second++; if(second==40) { second=0; } } count++; if(count==1) {P2=0;dataled=str[0];}//数码管扫描; if(count==2) {P2=1;dataled=str[1];} if(count==3) {P2=2;dataled=str[2];}//数码管扫描; if(count==4) {P2=3;dataled=str[3];} if(count==5) {P2=4;dataled=str[4];}//数码管扫描; if(count==6) {P2=5;dataled=str[5];count=0;} if(a==4) { str[5]=0x39; str[1]=tab[temph/100]; str[2]=tab[temph%100/10]; str[3]=tab[temph%10]|0x80; str[4]=tab[templ];- 58 - if(flag_get==1) { temp=read_temperature(); if(temp&0x8000) { str[0]=0x40; temp=~ temp+=1; } else str[0]=0; temph=temp&&4; templ=temp&0x0f; templ=templ*6/10; flag_get=0; } } } void main() { TMOD=0X21; PCON=0X00;//SMOD=0; SCON=0X40;//串口工作方式 1 允许接受并关闭 RI,TI; TH0=0XEF; TL0=0XF0; TH1=0Xe6; TL1=0Xe6;//波特率取 1200b/s 对应晶振为 12MHZ; EA=1; ET0=1; TR0=1; TR1=1;//启动定时器 0,1; P2=0X00; while(1) { if((temph&=30)&&(temph&34)) { P1=0 if((temph&=30)&&(temph&=34)) { delay1(10000); P1=0x00;//音乐数组长- 59 - delay1(10000); } } P1=0 if((temph&29)&&(temph&25)) { for(j=0;j&34;j++) { if((temph&=29)&&(temph&25)) { P1=huayang[j]; delay1(5000); } } } dis[0]=change[temph/10]; dis[1]=change[temph%10]; dis[2]=0x2e; dis[3]=change[templ]; dis[4]=0x64; dis[5]=0x75; dis[6]=0x0a; dis[7]=0x00; i=0; while(dis[i]!=0x00) { SBUF=dis[i]; while(TI==0); TI=0; i++; } for(j=0;j&10000;j++); } } 串行通信带声音警报的 #include&reg52.h& #define uchar unsigned char #define uint unsigned int #include&intrins.h& sbit DQ=P1^3; sbit k1=P3^0; bit flag=0; bit flag_- 60 - sfr dataled=0x80; uchar a1; static uchar temph=0,templ=0; uchar num,count,second,a=0; uchar code huayang[]= {0x3f,0x3f,0x9f,0xaf,0xb7,0xbb,0xbd,0xbe,0xbf,0xbf,0x00,0,0x15,0x15,0x2a,0x2a,0x2a,0x15,0x 15,0,0x00 ,0xbf,0xbf,0xbe,0xbe,0xbd,0xbd,0xbb,0xbb,0xb7,0xaf,0x9f,0x3f,0x3f}; uchar code tab[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f}; sbit speaker=P1^6; //定义音乐输出端口,需要连接到对应的喇叭,请参考电路图 uint timer1,timer2; uchar str[6];//显示寄存器; void delay1(uint); void DS18B20_init() { uchar x=0; DQ=1;//复位 DQ; delay1(8);//稍作延时; DQ=0;//由单片机将 DQ 拉低; delay1(80);//精确延时大于 480 微秒;DS18B20 的内部构造要求; DQ=1; delay1(10); x=DQ;//稍作延时后如果 x=0 则表明初始化成功否则失败需重新初始化; delay1(5); } void delay1(uint i) { while(i--); } void timer1_inter() interrupt 3 { TR1=0; if((temph&=30)&&(temph&=34)) { speaker=! TH1=timer1; TL1=timer2; } TR1=1; } uchar read_one_char(void)- 61 - { uchar i=0; uchar dat=0; for(i=8;i&0;i--) { DQ=0;//给脉冲信号; dat&&=1; DQ=1;//给脉冲信号; if(DQ) dat|=0x80; delay1(5); } } void write_one_char(uchar dat) { uchar i=0; for(i=8;i&0;i--) { DQ=0; DQ=dat&0x01; delay1(5); DQ=1; dat&&=1; } delay1(5); } uint read_temperature(void) { uchar a=0; uint b=0; uint t=0; DS18B20_init(); write_one_char(0xcc);//跳过读序列号的操作 write_one_char(0x44);//启动温度转换; delay1(200); DS18B20_init(); write_one_char(0xcc);//跳过读序列号的操作; write_one_char(0xbe);//读温度寄存器等 9 个寄存器前两个就是温度 a=read_one_char();//依次读取地位和高位; b=read_one_char(); b&&=8; t=a+b;- 62 - } void timer0()interrupt 1 { num++; a++; TH0=()/256; TL0=()%256; if(num==6) { num=0; flag_get=1;//标志位有效; second++; if(second==40) { second=0; } } count++; if(count==1) {P2=0;dataled=str[0];}//数码管扫描; if(count==2) {P2=1;dataled=str[1];} if(count==3) {P2=2;dataled=str[2];}//数码管扫描; if(count==4) {P2=3;dataled=str[3];} if(count==5) {P2=4;dataled=str[4];}//数码管扫描; if(count==6) {P2=5;dataled=str[5];count=0;} if(a==4) { str[5]=0x39; str[1]=tab[temph/100]; str[2]=tab[temph%100/10]; str[3]=tab[temph%10]|0x80; str[4]=tab[templ]; if(flag_get==1) { temp=read_temperature(); if(temp&0x8000) { str[0]=0x40; temp=~- 63 - temp+=1; } else str[0]=0; temph=temp&&4; templ=temp&0x0f; templ=templ*6/10; flag_get=0; } } } void init_searial_com() { SCON=0X40; PCON=0X00; } void send_char_searial(uchar ch) { SBUF= while(TI==0);//串口通信之发送数据(由单片机发) TI=0; } //以下为主函数 void main() { TMOD=0X11; TH0=0XEF; TL0=0XF0; IE=0X8a; TR0=1; TR1=1; P2=0X00; count=0; init_searial_com(); while(1) { if((temph&=30)&&(temph&34)) { P1=0 if((temph&=30)&&(temph&=34)) 从头再来//音乐数组长度 ,唱完- 64 - { timer1=()/256; timer2=()%256; delay1(50000);delay1(50000);delay1(50000); timer1=()/256; timer2=()%256; delay1(14000); } } if((temph&=28)&&(temph&25)) { speaker=0; for(j=0;j&34;j++) { if((temph&=29)&&(temph&25)) { P1=huayang[j]; delay1(10000); } } } if(RI) { RI=0; ch=SBUF; a1= flag=1; } if(flag==1) { flag=0; send_char_searial(a1); delay1(10000); } } } 2010 年 5 月 19 日星期三晴 利用仿真软件 proteus7.5 所做的实验 (1) 利用总线显示 0123 D:\仿真\数字电压表 test.DSN (2) 在使用 proteus7.5 软件时碰到的问题? 1.有时在元器件库中可以找到某种元器件但是当你添加时系统会问是否需要更新之类 的话?我选是, 添加元器件后运行系统报错: 找不到此元器件的对应模型! ADC0809 如- 65 - 但是当换做 ADC0808 时则可以运行通过! 项目 1.数字电压表设计 电路图(见 D:\仿真\数字电压表 test1-成功.DSN) 源程序 #include&reg52.h& #include&intrins.h& #define uchar unsigned char #define uint unsigned int #define ch0 0x02 #define ch1 0x03 sbit LE2=P2^6; sbit LE1=P2^7; sbit ad_cs=P3^4; sbit clk=P2^0; sbit di=P2^1; sbit ad_do=P2^1; sbit acc0=ACC^0; sbit acc1=ACC^1; uchar data dis[5];//注意此处的存储类型必须使用此种 data 型而不能使用 code 型为什么? uchar code tab[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0XFF}; void delay(uint x) { while(x--); } void ADC_start() { ad_cs=1; _nop_(); clk=0; _nop_(); ad_cs=0; _nop_(); // di=1; _nop_(); clk=1; _nop_(); // di=0; _nop_(); clk=0; _nop_(); } uchar ADC_read(uchar mode) {- 66 - ////// di=0; ADC_start(); ACC= di=acc1; clk=1;//di2ge _nop_(); di=acc1; di=0; clk=0; _nop_(); di=acc0; _nop_(); clk=1; _nop_(); //di=acc0; di=1;//此 di 奇怪之非常!有则显示无则不显示! clk=0; _nop_(); _nop_(); clk=1; _nop_(); ACC=0; for(i=8;i&0;i--) { clk=0; ACC=ACC&&1; acc0=ad_ _nop_(); clk=1; } ad_cs=1; return ACC;} void ad_convert(uchar ad_data) { dis[3]=0 dis[2]=ad_data/51; temp=ad_data%51; temp=temp*8+28; dis[1]=temp/51; temp=temp%51+24; temp=temp*8+40;//重点!- 67 - temp=temp/51; dis[0]= } void display(void) { LE2=1; P3=0X0E; LE2=0; LE1=1; P1=tab[dis[3]]; LE1=0; delay(10); P1=0XFF; LE2=1; P3=0X0D; LE2=0; LE1=1; P1=tab[dis[2]]|0x80; LE1=0; delay(10); P1=0XFF; LE2=1; P3=0X0B; LE2=0; LE1=1; P1=tab[dis[1]]; LE1=0; delay(10); P1=0XFF; LE2=1; P3=0X07; LE2=0; LE1=1; P1=tab[dis[0]]; LE1=0; delay(10); P1=0XFF; } void main() { uchar acq,i; while(1) { acq=ADC_read(ch0);- 68 - for(i=0;i&8;i++) { ad_convert(acq); display(); } } } 心得:这个小项目我足足做了五天时间,开始时总是让人失望,最初我使用的是 ADC0809 但是在仿真软件里不认,接着使用 ADC0808 虽认了但电路图练好了,程序也是按照别人写 好的程序写的可是就是出不来现象!我考虑是不是因为 0808 的 VCC 不在导致的呢?(尚 未验证)最后实在没办法了我决丁使用串行的 ADC0832,用的也是别人的程序.只不过我 做了少许的修改,这点修改很重要,因为在开始用别人的程序只能测出整数部分的电压,而 后面的两位小数则不翼而飞了. 我不断地修改其中的参数才使得小数部分显现但精度依然没 有达到要求 (理想状态下应为最大误差 0.02V 但是我的电压表却是 0.03---0.06 的误差, 不过 也不错了! )另外就是在调试阶段必须要抓住问题的核心,不要太注重细枝末节了对于时序 图大致能看懂就可以, 写出的程序大致符合此时序就行了! 事实上即使你完全按照时序来也 不一定能调出现象来! 就本实验而言: 在读 ad_do 口时就没有按照时序来. 下面我将 ADC0832 的驱动程序编程驱动程序包如下: #define ch0 0x02 #define ch1 0x03 sbit ad_cs=P3^4; sbit clk=P2^0; sbit di=P2^1; sbit ad_do=P2^1; sbit acc0=ACC^0; sbit acc1=ACC^1; uchar data dis[5];//注意此处的存储类型必须使用此种 data 型而不能使用 code 型为什么? uchar code tab[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0XFF}; void ADC_start() { ad_cs=1; _nop_(); clk=0; _nop_(); ad_cs=0; _nop_(); // di=1; _nop_(); clk=1; _nop_(); // di=0; _nop_(); clk=0;- 69 - _nop_(); } uchar ADC_read(uchar mode) { di=0; ADC_start(); ACC= di=acc1; clk=1;//di2ge _nop_(); di=acc1; di=0; clk=0; _nop_(); di=acc0; _nop_(); clk=1; _nop_(); //di=acc0; di=1;//此 di 奇怪之非常!有则显示无则不显示! clk=0; _nop_(); _nop_(); clk=1; _nop_(); ACC=0; for(i=8;i&0;i--) { clk=0; ACC=ACC&&1; acc0=ad_ _nop_(); clk=1; } ad_cs=1; return ACC;//////} void ad_convert(uchar ad_data) { dis[3]=0 dis[2]=ad_data/51; temp=ad_data%51;- 70 - temp=temp*8+28; dis[1]=temp/51; temp=temp%51+24; temp=temp*8+40; temp=temp/51; dis[0]= } 在使用此程序包时用户只要关注:接口 ACC 即可因为 ACC 中存放的是转化后的数字量, 最后的一个转换函数实现进制转换 16―10; 当然了 ADC0832 有四种数据采集方式这里使用 的是以 CH0 作为数据采集的正输入端以地作为负输入端!当然你也可以选择其他的方式, 只需通过设置其中的 mode 的值!0x00-----CH0+,CH1-的差分输入,0x01-----CH0-,CH1+的差 分输入方式,0x02-----CH0+,地---,0x03------CH1+地----. 17.利用仿真软件制作一个按键可调的时钟 问题:1.利用 NPN 型三极管无法控制数码管的位选,原因是无论集电极是否为高电平,只 要基极为高电平那么对应的数码管就能被点亮,因此三极管就失去了控制作用.它不同于 PNP 对于数码管位选的控制,只要基极为低电平,那么集电结就能够导通,那样集电极与 发射极之间就形成了通路,从而实现控制! 总结:可以采用屏蔽其他程序的方法来检验我们所关心的程序是否正确! 17.单片机做的时钟(未用时钟芯片) (1)硬件电路..\仿真\数字时钟.DSN (2)软件实现 #include&reg52.h& #define uchar unsigned char #define uint unsigned int sbit LE1=P2^6; sbit LE2=P2^7; sbit key0=P3^1; sbit key1=P3^4; sbit key2=P3^7;//用于记录 key0 的按下次数!//用于标识是否在黑暗状态下按下 key0 键! uchar count0,count1,count2, uchar dis[6]={0x00,0x00,0x00,0x00,0x00,0x00}; uchar wela[]={0xDF,0xEF,0xF7,0xFB,0xFD,0xFE,0xFF};//此处必须使用这种形式而不能是 0X1F 等! uchar tab[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x00}; void init(); void display(); void keyscan(); void delayms(uint); void main()//没问题! { init(); while(1)- 71 - { display(); keyscan(); } } void init(void)//没问题! { con=0;temp=0;count0=0;count1=0;flag=0;count2=0;//验证中间换成,的情景? LE1=0; LE2=0; TMOD=0X11; EA=1; TH1=()/256; TL1=()%256; TH0=()/256; TL0=()%256; ET1=0; TR1=0; ET0=1; TR0=1; } void display(void)//没问题! { LE2=1; P2=wela[0]; LE2=0; LE1=1; P1=tab[dis[0]]; LE1=0; delayms(5); P1=0x00; LE2=1; P2=wela[1]; LE2=0; LE1=1; P1=tab[dis[1]]; LE1=0; delayms(5); P1=0x00; LE2=1; P2=wela[2]; LE2=0; LE1=1; P1=tab[dis[2]]|0X80;- 72 - LE1=0; delayms(5); P1=0x00; LE2=1; P2=wela[3]; LE2=0; LE1=1; P1=tab[dis[3]]; LE1=0; delayms(5); P1=0x00; LE2=1; P2=wela[4]; LE2=0; LE1=1; P1=tab[dis[4]]|0X80; LE1=0; delayms(5); P1=0x00; LE2=1; P2=wela[5]; LE2=0; LE1=1; P1=tab[dis[5]]; LE1=0; delayms(5); P1=0x00; } void delayms(uint x)//没问题! { while(x--) for(i=0;i&125;i++); } void keyscan(void) { EA=0; if(key0==0) { delayms(5); if(key0==0) { while(key0==0);//松手检测! if(count2%2!=0)- 73 - { if(con==0) { temp=dis[0]; } if(con&5) { dis[con]=temp=dis[con+1]; } else if(con==5){dis[con]=temp=dis[0];} //if(con==0) {dis[6]=dis[7];}// } if(dis[con]==10) dis[con]=0; TR0=0;ET0=0;TR1=1;ET1=1; con++; if(con==6) {con=0;TR0=1;ET0=1;TR1=0;ET1=0;}//此处可以完成在黑暗时按 k0 键依 然可以! } } if(con!=0) { if(key1==0) { delayms(5); if(key1==0) { while(key1==0); // if(count2%2!=0) // dis[con]++; // else dis[con]++; if(con%2==0&&con!=4) { if(dis[con]==10)//就在此刻黑暗因子将以另外的一种形态出 现在公众的面前 0x00; dis[con]=0; } else if(con==4&&dis[5]==2) {if(dis[con]==4)dis[con]=0;} else if(con==4&&dis[5]&2) {if(dis[con]==10)dis[con]=0;}- 74 - else if(con%2!=0) { if(con!=5) {if(dis[con]==6) dis[con]=0;} else {if(dis[con]==3)dis[con]=0;} } } } } if(con!=0) { if(key2==0) { delayms(5); if(key2==0) { // while(key2==0); delayms(200); dis[con]--; if(con%2==0&&con!=4) { if(dis[con]==255) dis[con]=9; } else if(con%2!=0&&con!=5) {if(dis[con]==255)dis[con]=5;} else if(con==5) {if(dis[con]==255)dis[con]=2;} else if(con==4&&dis[5]==2) { if(dis[con]==255) dis[con]=3; } else if(dis[5]&2) if(dis[con]==255)dis[con]=9; } } } EA=1; } void timer0(void)interrupt 1//没问题! { // EA=0;- 75 - //TH0=()/256; TL0=()%256; EA=1; count0++; if(count0==20) { count0=0; dis[0]++; if(dis[0]==10) { dis[0]=0; dis[1]++; if(dis[1]==6) { dis[1]=0; dis[2]++; if(dis[2]==10) { dis[2]=0; dis[3]++; if(dis[3]==6) { dis[3]=0; dis[4]++; if(dis[5]&=2) { if(dis[4]==10&&dis[5]&2) { dis[4]=0; dis[5]++; } else if(dis[5]==2) { if(dis[4]==4) {dis[4]=0;dis[5]=0;} } } } } } } }} void timer1(void)interrupt 3//实现交替闪烁!- 76 - { EA=0;//很有必要; TH1=()/256; TL1=()%256; count1++; if(count1==12) { count1=0; count2++; if(count2%2!=0) { temp=dis[con];//问题就出在此处? dis[con]=10; } else { dis[con]= } } EA=1;//很有必要; } 说明:实验过程中遇到的问题及其解决? 1. 利用三极管无法驱动共阴极数码管, 到目前为止仅见过用 PNP 型数码管驱动的共阳极数 码管. 2. 显示数据出现错位,一开始认为是由于为数组 WELA[]的编码错误!后来发现是由于各 位 的 数 组 元 素 的 本 身 的 错 误 造 成 的 只 需 将 原 来 的 wela[]={0x1f,…….0x3f} 改 为 wela[]={0xdf,…..0xff}便可! 3. 利用 P0 口对数码管进行位选驱动不可得,而且各位的状态不定,最后只能换用 P2 口. 猜想 P0 口石否是类似于三态 OC 门的原因能(验证知:对)即 P0 口的使用必须外接上 拉电阻或下拉电阻呢? 4. 各位数码管交替闪烁尽管没有按下 key0 键解决将显示函数中的延时部分从 10ms―5 5. 当 key0 键按下时每按一次所有的数码管狂闪一次,由于当按下时只有最高位是亮的. 6. 关于 P0 口的问题!? P0 口是真正的三态 I/O 口,内部无上拉电阻,使用时必须外接上拉或下拉电阻!在试验 中我在 P0^1 口加一个电阻串一个 LED 灯当未接上拉电阻+VCC 时 LED 不亮且端口的电 平显示不定,当接上上拉电阻时只有当电阻值小于 105 欧时端口被拉高,否则不定于此 同时 LED 变亮.当在 P0 端口接电阻和地时则在任何情况下对应的端口均为低电平! 2010 年 5 月 28 日星期五 1. 一般单片机 P0 口接上拉电阻时为 4.7K 左右,而接下拉电阻接地时通常选择 100 欧 左右的,在点亮 LED 时使用的限流电阻一般选择 300-1000 欧左右. 2. 接在复位电路上的电容一般选择 10 微法的极性电解电容. 而接在晶振上的电容一般 选择普通的 10~30 皮法的瓷片电容. 3. 51 单片机的封装形式有三种:TQFP,PLCC(以上两种均为正方形的芯片且为贴片型- 77 - 的),DIP(我现在用的)(一般插在 40 脚的 DIP 插座上) 4. 单片机的 GND 引脚应当接+5 伏电源的负极. 5. 单片机的时钟(晶振)接口可提供同步时序,以便让单片机在统一的步调下工作. 此外在设计时晶振和电容应尽量的靠近芯片以便减少 PCB 板的分布电容保证晶振 工作的稳定性!而当采用外部震荡方式时应在 XTAL2 上接外部时钟信号,XTAL1 接地.(此种方式应用于多片单片机组成的系统中:为保证多片单片机的时钟的同步 就必须引入唯一的公用外部时钟脉冲作为各个单片机的震荡脉冲! 6. 复位引脚只要有两个周期的高电平那么就能实现对单片机的复位!) 7. 控制信号引脚 PSEN(29):片外 ROM 选通信号.当单片机要扩展存储器时该引脚 与外部存储器的片选引脚 OE 相连,当单片机要读取外部存储器的数据时该引脚输 出低电平信号!ALE/PROM(30)地址锁存信号输出端/EPROM 编程脉冲输入端. 当访问外部存储器时,用于将 P0 口的地址锁存在外部存储器中!EA/VPP(31) :内 /外部 ROM 选择端.当单片机内部的程序存储器不够用时,需要将该脚接地表示使 用外部存储器,当使用内部程序存储器时则将该脚接+5V 表示使用片内存储器! 8. 关于输入/出 P0―P3 口: (1)四者均可用作普通的 I/O 口,除了 P1 口以外其他口均 具有第二功能.对于 P0 口当要向外部存储器读写数据时,P0 口作为复用口,P0 口 和 P2 口配合完成低 8 位地址的传送后, 口再传送 8 位数据! 口之举有普通 I/O P0 P1 口功能.P2:当向外部存储器读写数据时 P2 口用于传送高 8 位地址!P3 口除了具有 普通 I/O 口工能外还具有和第二功能!P3.0RXD 穿行口输入 P3.1TXD 串行口输出 P3.6WR 外部 RAM 写入选通信号 P3.7 RD 外部 RAM 读出选通信号! 关于四个双向 I/O 口的总结:P1,P2,P3 口都能驱动 4 个 TTL 门,且不需要上拉电阻就能 驱动 MOS 电路.P0 口内部没有上拉电阻,驱动 TTL 电路时能带 8 个 TTL 门;但驱动 CMOS 电路时,作为地址/数据总线可以直接驱动,而作为 I/O 口时则须外接上拉电阻! 9.关于存储器 ROM,RAM:前者类似与计算机中的硬盘后者类似于内存.我们编译好的 程序可执行代码被烧录到了 ROM 中,AT89S51 片内 ROM 有 4K,52 有 8K,RAM 有 256 个字节对于 51 仅有低 128 个字节可以由用户支配.而 52 则可支配高 128 个字节. 对 51 而言低 128 个字节被分成了四部分:1.工作寄存器区含有 R0----R7 共四组每组 8 个寄存器地址为 00H---1FH,依 R0---R7,R0---R7,….顺序进行排放.相当于 32 个房间,当 有客人(数据)要来住房间时,你可以告诉他住 00H 或住 R0 房间等价的.具体的住 R0 的那个(共四个)则由 PSW 中的 RS0 和 RS1 这两位来进行区分.PSW 中的第三四位分 别为以上二者取 1 1 选择 R3... .. 2.位寻址区 20H----2FH 共 16 个字节定义为位寻址区每个字节包含 8 个位位寻址区包含 128 个位. 之所以称其为位寻址区是因为只在该区域即可以对特定的字节进行寻址又可以对各个 字节中的各个位进行寻址操作.其中各位被统一编址为 00H----7FH.(从低往上走)例如我 想把位寻止区的第九个位进行置一则可以执行如下的操作 STEB 08H 相当于把 21H 字节 的最低位置一.STEB 只能使用在位寻止区而 MOV 指令则可用在一切区域! 3.一般的工作区 内存中 30H---7FH 这 80 个字节成为一般工作区具体到每个单元的用途完全由用户来自 己定义.当然位寻止区和工作寄存器区也可以由用户定义每个寄存器的用途但一般不要 这个样子! 4.特殊功能寄存器区:80H----FFH 下面按照寄存器名地址复位初值可否位寻止及其功能 为顺序写!SFR B F0H 00H Y B 寄存器- 78 - ACC E0H 00H Y 累加器 PSW D0H 00H Y 程序状态字 IP B8H 00H Y 中断优先级控制寄存器 P3 B0H FFH Y P3 口锁存器 IE A8H 00H Y 中断允许控制寄存器 P2 A0H FFH Y P2 口锁存器 SBUF 99H 不定 N 穿行口锁存器 SCON 98H 00H Y 串行口控制寄存器 P1 90H FFH Y P1 口锁存器 TH1 8DH 00H N 定时/计数器 1 高 8 位 TH0 8CH 00H N 定时/计数器 0 高 8 位 TL1 8BH 00H N 定时/计数器 1 低 8 位 TL0 8AH 00H N 定时/计数器 0 低 8 位 TMOD 89H 00H N 定时计数器方式寄存器 TCON 88H 00H Y 定时计数器控制寄存器 DPH 83H 00H N 数据地址指针高 8 位 DPL 82H 00H N 数据地址指针低 8 位 SP 81H 07H N 堆栈指针 P0 80H FFH Y P0 口锁存器 PCON 87H 0XXX0000B N 电源控制寄存器 注意穿行数据的接受和发送必须通过单片机的 P3.0 和 P3.1 口完成! 注意所有的中断必须由硬件系统向 CPU 提出申请!且对于中断优先级可以设置两级中 断优先级通过对 IP 的设置可以改变各个终端原的中断优先级. 9. 关于 ROM 的分类 (1) ROM 只读内存:只能读取内部资料的内存其内容在写入后就不能再进行更改了所 以又称为光照式只读内存 (2) PROM 可编程只读内存内部有行列式的熔丝可以使用电流将其烧断但是只能烧写一 次.EPROM(可光擦除)可擦出可编程只读内存,可利用高电压将资料编程写入. 擦除时可以通过封装外预留的石英晶体窗口进行紫外线擦出.但是每次写入时间较 长(15 分钟左右) (3) EEPROM 是电子式可擦除可编程只读内存, 其功能类似于 EPROM 只是擦除的方式 是高电场擦除速度更快! (4) FLASH ROM 是一种快速存储式只读存储器简称闪存.这种存储器的特点是可以电 擦写而且掉电后程序不会丢失.可以反复少些 1000 次以上. 10. 问题:ROM 和 RAM 中都有数据吗?有什么不同,程序执行时是怎样使用 ROM 和 RAM 的?(相当之重要! ) 答:这个问题提的太棒了!可以通过一个故事来说明二者的区别!比如在一个旅馆中, 有老板,有服务员,有领班经理,当然也一定会有房间了.这个老板每天晚上都会把第 二天要员工做的事情一一记录在一张纸上:第一,把 201 房间的 8 张床上面的东西都搬 到 203 房间的 8 张床上去;第二,205 房间的客人走后要把房间收拾出来,把 207 房间 的六号床上的被罩扯下来; 第三, 去隔壁商店买 8 个不同的被罩给 209 房间换上;... ... 第二天,领班经理就拿着那张纸安排人干活了,完成一件就划掉一件.在这里,白纸就 是程序存储器,上面写的东西就相当于我们所写的程序,老板就是程序的设计者.领班 经理就是程序指针 PC,负责读出并安排程序上的每一件事情.而被操作的那些房间就- 79 - 相当于内存的 RAM,而房间里的每个床位就相当于内存中的每个字节中的每个位,到隔 壁商店去买 8 个不同的被罩给 209 房间的每个床位换上就相当于到片外的某个器件里去 读数据并将其复制到 209 单元中来. 现在再考虑 ROM 和 RAM 中的 0 和 1 的关系, ROM 中的这些 0 和 1 是由我们编写的经过编程软件的编译后生成的单片机所能够识别的代 码.单片机根据 ROM 中的这些数字就能够知道下一步该干什么!一旦将程序烧写进入 ROM 区就不能再更改了除非编程人员再重新进行程序的编写,编译,和烧写.而内存 RAM 就相当于那些个房间:有的高级房间上面不但有房间号还有名称如 R0 等,这些房 间不会分租给个人,只能整体包下来!而有些房间可以把单个床位提供给某个顾客,也 可以整体包给一个团.还有一些特殊的房间只能给指定的人住,因为他把这个房间永久 性的包了下来!这几类房间就相当于内存 RAM 中的工作寄存器区,位寻址区,一般区 和特殊功能寄存器区. 11. 问题: ROM 和 RAM 都可以可以存储数据当我们在片外分别扩展 64K 的存储空间时, 单片机到片外 101H 去取数据怎么区分是到 ROM 还是到 RAM 中去取数据呢? 答:其实不乱!单片机到片外取数据虽然都是派相同的人去的,即用 P0 和 P2 口去取, 但是引领 P0 和 P2 去的人却不是同一个, P0 和 P2 到单片机外部程序存储器去取程序 领 代码的是单片机的 29 脚 PSEN,当然了 30 脚和 31 脚也要配合;而领单片机到片外数据 存储器去取数据的是单片机的 P3.6(WR)和 P3.7(RD).因此虽然是一个地址 101 但是 由于由不同的人带路,最终到达的地方是不同的!就好比两个楼都有 101 房间,而负责 领路的两个人分别负责两个楼. 12. 问题:位寻止区有什么作用? 总の:通常位寻址区是用来做标志的! 范先生的锦囊妙计:学习单片机如同远征,能否取得最后的胜利,取决于诸多的因素: 你是否能坚持克服困难,你一定要手握单片机引脚分布图,内存分布图,指令表(对于 使用汇编的)特殊功能寄存器的功能表,以及常用

我要回帖

更多关于 南昌大学单片机实验 的文章

 

随机推荐