#define uchar unsigned char #define uint unsigned int
sbit K1=P3^7;
uchari,Second_Counts,Key_Flag_Idx; bit Key_State;
ucharDSY_CODE[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f}; //延时
voidDelayMS(uintms) { uchart;
while(ms--) for(t=0;t<120;t++); }
//处理按键事件
voidKey_Event_Handle() {
if(Key_State==0) {
Key_Flag_Idx=(Key_Flag_Idx+1)%3; switch(Key_Flag_Idx)
{ case 1: EA=1;ET0=1;TR0=1;break; case 2: EA=0;ET0=0;TR0=0;break;
case 0: P0=0x3f;P2=0x3f;i=0;Second_Counts=0; } } }
//主程序 voidmain() {
P0=0x3f; //显示00 P2=0x3f; i=0;
Second_Counts=0;
Key_Flag_Idx=0; //按键次数(取值0,1,2,3) Key_State=1; //按键状态
TMOD=0x01; //定时器0方式1 TH0=(65536-50000)/256; //定时器0:50ms TL0=(65536-50000)%256; while(1) { if(Key_State!=K1) { DelayMS(10); Key_State=K1; Key_Event_Handle(); } } }
//T0中断函数
voidDSY_Refresh() interrupt 1 {
TH0=(65536-50000)/256; //恢复定时器0初值 TL0=(65536-50000)%256;
if(++i==2) //50ms*2=0.1s转换状态 { i=0;
Second_Counts++;
P0=DSY_CODE[Second_Counts/10]; P2=DSY_CODE[Second_Counts%10];
if(Second_Counts==100) Second_Counts=0; //满100(10s)后显示00 } }
四、Protel原理图及其仿真
第一次按键:启动计数
第二次按键:暂停
第三次按键:清零
五、PCB原理图及版图
原理图:
敷铜前
敷铜后:
六、实验总结
1、秒表的实现与单片机的中断系统息息相关,掌握单片机的硬件结构及其各种中断机制是我们编程的第一步
2、编程需要分模块,即各种子程序如延时子程序,对应的中断路口程序等等,并做好相应的标记,以防调用错误
3、编程思路按照模板并在所需功能加上相应的逻辑,可以更高效。
4、本实验采用两个数码管接到两个I/O口,占用单片机资源大,因此可以采用动态扫描的方式,只需一个I/O口以及相应的段选和位选口即可。
5、在设计控制开关时,注意2个中断的打开和关闭的先后顺序,否则就会出错。 6、本次实验的按键控制功能用一个简单的逻辑就能实现消抖。
7、PCB器件的排放需要按照一定的摆放规则,使制版更容易,更可观。
实验二 AD转换显示
一、实验内容
用单片机实现四个数码管显示TLC549所采样到的电压值。
二、实验原理
TLC549是一种高性能的8位A/D转换器,它以8位开关电容逐次逼近的方法实现A/D转换,采用三线串行接口方式与单片机连接,端口SCLK、SD0、CS与单片机的I/O口连接来控制A/D转换。本实验通过用该芯片采集电压模拟量,然后将采集到的模拟量转换为数字量后送至数码管显示大小。
三、C程序代码
#include #include#define uchar unsigned char #define uint unsigned int sbit dian=P0^7;//小数点位 sbit led_e=P2^7;//液晶使能端口 uchartemp,ad;
uchar code table1[12]=
{0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0xff,0xbf}; //数码管段选 sbit CS=P3^3; //片选 sbit CLK=P2^6; //时钟 sbit DAT=P2^7; //数据 uchar bdata adctemp; uintadcdata;
voidDelay_MS(uintms) {
uintx,y;
for(x=ms;x>0;x--) for(y=110;y>0;y--); }
uintTLC549ADC(void) {
uchari; CS=1; CLK=0; DAT=1; CS=0;
for(i=0;i<8;i++) { CLK=1;
_nop_(); _nop_();_nop_(); _nop_();
adctemp=DAT;
adcdata=adcdata|adctemp; adcdata<<=1; //AD转换 CLK=0;
_nop_(); _nop_();_nop_(); }
return ((adcdata&511));//保留9位 }
voidmain() {
uinti,AD_DATA; //定义A/D转换数据变量 while(1) {
// TLC549ADC(); //启动一次A/D转换 // for(i=0xff;i>0;i--) //延时 // {_nop_();}
AD_DATA=TLC549ADC(); //读取当前电压值A/D转换数据 led_e=0; //防止液晶影响 P1=0xFD; //电压个位 P0=table1[AD_DATA/100]; dian=0;
for(i=0xff;i>0;i--) //延时 {_nop_();}
for(i=0xff;i>0;i--) //延时 {_nop_();}
// P0=0XFF; //关闭数码,消除余辉
P1=0xFB; //小数点第一位 P0=table1[(AD_DATA%100)/10];
for(i=0xff;i>0;i--) //延时
{_nop_();} for(i=0xff;i>0;i--) //延时 {_nop_();} // P0=0XFF;
P1=0xF7; //小数点第二位 P0=table1[AD_DATA%10]; for(i=0xff;i>0;i--) //延时 {_nop_();}
for(i=0xff;i>0;i--) //延时 {_nop_();} // P0=0XFF; } }
四、Protel原理图及其仿真
调节滑动变阻器,示数从0-512发生变化
五、PCB原理图及版图
七、实验总结
1、本次采用TLC549的AD转换芯片,实验前必需对其工作原理进行分析和理解,掌握其工作时序。TLC549在每一个时钟下降沿时开始将数据从高位到低位一位一位移出,因此在AD转换的子程序里我们定义两个16位的数据变量,其中一个用来存放从TLC549移出的数据,另外一个变量与第一个相或,并且每次向左移一位,得到数据再保留9位,最后通过return函数返回到主函数。
2、该实验采用软件延时的方式对四个数码管进行动态扫描,使用三极管的导通进行位选。也可采用定时器中断的方式进行扫描显示,可以使显示效果更稳定。
实验三单片机双向通信
一、实验内容
实现单片机与单片机之间相互通信:甲机向乙机发送控制两个LED灯亮灭的命令字符,甲机同时接收乙机发送的0~9的数字,并显示在数码管上。
二、实验原理
两个单片机都使用串口方式1进行通信,晶振频率为11.0592MHz,选用定时器T1作为波特率发生器,T1工作于方式2,通信的波特率为9600,并且必须保证两单片机通信波特率完全一致,否则接受不到正确的数。选用11.0592MHZ晶振的目的就是为了使计算得到的初值为整数,选用定时器T1工作于方式2作为波特率发生器,只需要在初始化编程的时候,将计算得到的初值写入TH1和TL1,当T1溢出时会自动重新装入初值,从而产生精确的波特率。在发送数据时,向SBUF中写入一个数据后,使用“while(TI==0);”等待是否发送完毕,因为当发送完毕后,TI被硬件置1,然后才退出“while(TI==0);”接下来在将TI手动清零,同理,在接受数据时,在中断服务程序中也需要将接受中断标志位RI置零。
三、C程序代码
单片机1
#include#define uchar unsigned char #define uint unsigned int sbit LED1=P1^0; sbit LED2=P1^3;
sbit K1=P1^7;
uchar Operation_No=0; //数码管代码
uchar code DSY_CODE[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f}; //延时
void DelayMS(uint ms) { }
//向串口发送字符
void Putc_to_SerialPort(uchar c) { } //主程序
void main() {
LED1=LED2=1; P0=0x00;
SCON=0x50; 串口模式1,允许接收 TMOD=0x20; T1工作模式2 PCON=0x00; 波特率不倍增 TH1=0xfd; TL1=0xfd; 设定波特率为4.8kbps TI=RI=0; 发送与接收标志位置零 TR1=1; 启动定时器 IE=0x90; {
switch(Operation_No) {
case 0: Putc_to_SerialPort('X');
LED1=LED2=1; break;
//根据操作代码发送A/B/C或停止发送
DelayMS(100); if(K1==0) {
while(K1==0);
Operation_No=(Operation_No+1)%4;
//按下K1时选择操作代码0,1,2,3 允许串口中断
SBUF=c; 把C变量数据付植给专用寄存器SBUF while(TI==0); 等待发送完毕 TI=0; TI标志位置零 uchar i;
while(ms--) for(i=0;i<120;i++);
//操作代码
while(1)
}
}
}
case 1: Putc_to_SerialPort('A');
LED1=~LED1;LED2=1; break;
LED2=~LED2;LED1=1; break;
LED1=~LED1;LED2=LED1; break;
case 2: Putc_to_SerialPort('B');
case 3: Putc_to_SerialPort('C');
}
//甲机串口接收中断函数
void Serial_INT() interrupt 4 {
if(RI) 如果RI标志位为1,则开始接受数据 {
RI=0; RI标志位置零
if(SBUF>=0&&SBUF<=9) P0=DSY_CODE[SBUF];
把SBUF寄存器的接受值付给一维数组元素值,一维数组元素值付给P0口编码 }
}
else P0=0x00; 否则P0口置零
单片机2
#include#define uchar unsigned char #define uint unsigned int sbit LED1=P1^0; sbit LED2=P1^3; sbit K2=P1^7; uchar NumX=-1; //延时
void DelayMS(uint ms) { }
void main() {
LED1=LED2=1; SCON=0x50;
uchar i;
while(ms--) for(i=0;i<120;i++);
}
TMOD=0x20; TH1=0xfd;
TL1=0xfd; PCON=0x00; RI=TI=0; TR1=1;
IE=0x90; while(1) { }
DelayMS(100); if(K2==0) { }
while(K2==0); NumX=++NumX%11; SBUF=NumX; while(TI==0); TI=0;
void Serial_INT() interrupt 4 { }
if(RI) //如收到则LED则动作 { }
RI=0;
switch(SBUF) { }
case 'X': case 'A': case 'B': case 'C':
LED1=LED2=1;break; LED1=0;LED2=1;break; LED2=0;LED1=1;break; LED1=LED2=0;
//全灭 //LED1亮 //LED2亮 //全亮
//根据所收到的不同命令字符完成不同动作
四、Protel原理图及其仿真
甲机按键按下时,控制双方LED的亮灭
乙机按键次数显示在甲机的数码管:
五、PCB原理图及版图
敷铜前
敷铜后
八、实验总结
1、首先应先理解单片机串行通信的原理。单片机串行接口有两个控制寄存器:SCON和PCON。串行口工作在方式0时,可通过外接移位寄存器实现串并行转换。在这种方式下,数据为8位,只能从RXD端输入输出,TXD端用于输出移位同步时钟信号,其波特率固定为振荡频率的1/12。由软件置位串行控制寄存器(SCON)的REN位后才能启动,串行接收,在CPU将数据写入SBUF寄存器后,立即启动发送。待8位数据输完后,硬件将SCON寄存器的T1位置1,必须由软件清零。这就是主程序工作的过程。
2、响应中断后,没有退出中断前,如果继续有数据传送过来,那么后传送过来的数据将丢失。
3、PCB器件排布应将排针和数码管等大器件放置外围,而晶振各种小芯片应放到单片机芯片旁。