十一.24CO8 的读写操作
[实验任务]
利用 24C08 断电以后存储的数据不消失的特点,可以做一个断电保护装置。
首先利用单片机做一个 0-99 秒的自动计时器。然后随机关断电源,在 通电以
后计时器接着断电前的状态继续计时。
[硬件电路图]
[实验原理]
首先简单的说明以下 (I方C) 总线,(I方C) 总线是一种串行数据总线,只有二根信号线,一根是双向的数据线 SDA,另一根是时钟线 SCL。在 (I方C) 总线上传送的一个数据字节由八位组成。总线对每次传送的字节数没有限制,但每个字节后必须跟一位应答位。数据传送首先传送最高位(MSB),数据传送按图1所示格式进行。首先由主机发出启动信号“S”(SDA在 SCL 高电平期间由高电平跳变为低电平),然后由主机发送一个字节的数据。启动信号后的第一个字节数据具有特殊含义:高七位是从机的地址,第八位是传送方向位,0表示主机发送数据(写),1表示主机接收数据(读)。被寻址到的从机设备按传送方向位设置为对应工作方式。标准 (I方C) 总线的设备都有一个七位地址,所有连接在 (I方C) 总线上的设备都接收启动信号后的第一个字节,并将接收到的地址与自己的地址进行比较,如果地址相符则为主机要寻访的从机,应在第九位答时钟脉冲时向 SDA 线送出低电平作为应答。除了第一字节是通用呼叫地址或十位从机地址之外第二字节开始即数据字节。数据传送完毕,由主机发出停止信号“P”(SDA在 SCL 高电平期间由低电平跳变为高电平)。AT24C 系列串行 E2PROM 具有 (I方C) 总线接口功能,功耗小,宽电源电压(根据不同型号 2.5V~6.0V),工作电流约为 3mA,静态电流随电源电压不同为30μA~110μA,AT24C 系列串行E2PROM 参数如下型 号 容 量 器件寻址字节(8 位) 一次装载字节数
AT24C01 128×8 1010A2A1A0 R/W 4
AT24C02 256×8 1010A2A1A0 R/W 8
AT24C04 512×8 1010A2A1P0 R/W 16
由于 (I方C) 总线可挂接多个串行接口器件,在 (I方C) 总线中每个器件应有唯一的器件地址,按 (I方C)总线规则,器件地址为7位数据(即一个(I方C)总线系统中理论上可挂接 128 个不同地址的器件),它和 1 位数据方向位构成一个器件寻址字节,最低位D0为方向位(读/写)。器件寻址字节中的最高4位(D7~D4)为器件型号地址,不同的(I方C)总线接口器件的型号地址是厂家给定的,如AT24C 系列E2PROM的型号地址皆为 1010,器件地址中的低 3 位为引脚地址 A2 A1 A0,对应器件寻址字节中的D3、D2、D1位,在硬件设计时由连接的引脚电平给定。对AT24C系列 E2PROM的读写操作完全遵守I2C总线的主收从发和主发从收的规则。
AT24C08 1024×8 1010A2P1P0 R/W 16
AT24C16 2048×8 1010P2P1P0 R/W 16
[C语言源程序]
#include <reg52.H>
#include <stdio.h>
#include <absacc.h>
unsigned char code table[]={0x3f,0x06,0x5b,0x4f,0x66,
0x6d,0x7d,0x07, 0x7f,0x6f,};
unsigned char sec; //定义计数值,每过1 秒,sec加1
unsigned int tcnt; //定时中断次数
bit write=0; //写24C08 的标志;
sbit gewei=P2^0; //个位选通定义
sbit shiwei=P2^1; //十位选通定义
/24C08 读写驱动程序
sbit scl=P3^4; // 24c08 SCL
sbit sda=P3^5; // 24c08 SDA
void delay1(unsigned char x)
{ unsigned int i;
for(i=0;i<x;i++);
;}
void flash()
{ ; ; }
void x24c08_init() //24c08 初始化子程序
{scl=1; flash(); sda=1; flash();}
void start() //启动(I方C)总线
{sda=1; flash(); scl=1; flash(); sda=0; flash(); scl=0; flash();}
void stop() //停止(I方C)总线
{sda=0; flash(); scl=1; flash(); sda=1; flash();}
void writex(unsigned char j) //写一个字节
{ unsigned char i,temp;
temp=j;
for (i=0;i<8;i++)
{temp=temp<<1; scl=0; flash(); sda=CY; flash(); scl=1; flash();}
scl=0; flash(); sda=1; flash();
}
unsigned char readx() //读一个字节
{
unsigned char i,j,k=0;
scl=0; flash(); sda=1;
for (i=0;i<8;i++)
{
flash(); scl=1; flash();
if (sda==1) j=1;
else j=0;
k=(k<<1)|j;
scl=0;}
flash(); return(k);
}
void clock() //(I方C)线时钟
{
unsigned char i=0;
scl=1; flash();
while ((sda==1)&&(i<255))i++;
scl=0; flash();
}
从24c02 的地址address 中读取一个字节数据/
unsigned char x24c08_read(unsigned char address)
{
unsigned char i;
start(); writex(0xa0);
clock(); writex(address);
clock(); start();
writex(0xa1); clock();
i=readx(); stop();
delay1(10);
return(i);
}
//向24c02 的address 地址中写入一字节数据info/
void x24c08_write(unsigned char address,unsigned char info)
{
EA=0;
start(); writex(0xa0);
clock(); writex(address);
clock(); writex(info);
clock(); stop();
EA=1;
delay1(50);
}
/24C08 读写驱动程序完/
void Delay(unsigned int tc) //延时程序
{
while( tc != 0 )
{unsigned int i;
for(i=0; i<100; i++);
tc--;}
}
void LED() //LED显示函数
{
shiwei=0; P0=table[sec/10]; Delay(8); shiwei=1;
gewei=0; P0=table[sec%10]; Delay(5); gewei=1;
}
void t0(void) interrupt 1 using 0 //定时中断服务函数
{
TH0=(65536-50000)/256; //对TH0 TL0 赋值
TL0=(65536-50000)%256; //重装计数初值
tcnt++; //每过250ust tcnt 加一
if(tcnt==20) //计满20次(1 秒)时
{
tcnt=0; //重新再计
sec++;
write=1; //1 秒写一次24C08
if(sec==100) //定时100 秒,在从零开始计时
{sec=0;}
}
}
void main(void)
{
TMOD=0x01; //定时器工作在方式1
ET0=1; EA=1;
x24c08_init(); //初始化24C08
sec=x24c08_read(2);//读出保存的数据赋于sec
TH0=(65536-50000)/256; //对TH0 TL0 赋值
TL0=(65536-50000)%256; //使定时器0.05 秒中断一次
TR0=1; //开始计时
while(1)
{
LED();
if(write==1) //判断计时器是否计时一秒
{
write=0; //清零
x24c08_write(2,sec); //在24c08 的地址2 中写入数据sec
}
}
}
--------------------------------------------------------------------------------------------------------------------------------
十二.PC 机与单片机通信(RS232 协议)
[实验任务]
单片机串口通信的应用,通过串口,我们的个人电脑和单片机系统进行通信。
个人电脑作为上位机,向下位机单片机系统发送十六进制或者ASCLL码,单片机
系统接收后,用LED显示接收到的数据和向上位机发回原样数据。
[硬件电路图]
[实验原理]
RS-232是美国电子工业协会正式公布的串行总线标准,也是目前最常用的串行接口标准,用来实现计算机与计算机之间、计算机与外设之间的数据通讯。RS-232串行接口总线适用于:设备之间的通讯距离不大于15m,传输速率最大为20kBps。RS-232协议以-5V-15V表示逻辑1;以+5V-15V 表示逻辑0。 我们是用MAX232芯片将RS232电平转换为TTL电平的。 一个完整的RS-232接口有22根线,采用标准的25芯插头座。我们在 这里使用的是简化的9芯插头座。注意我们在这里使用的晶振是11.0592M的,而不是12M。因为波特率的设置需要11.0592M的。
“串口调试助手V2.1.exe” 软件的使用很简单,只要将串口选择‘CMO1’波特率设置为‘9600’ 数据位为 8 位。打开串口(如果关闭)。然后在发送区里输入要发送的数据,单击手动发送就将数据发送出去了。注意,如果选中‘十六进制发送’那么发送的数据是十六进制的,必须输入两位数据。如果没有选中, 则发送的是ASCLL码,那么单片机控制的数码管将显示ASCLL码值。
数字 | 二进制 | Px0~Px7 | Abcdefg p | 十六进制 |
0 | 00111111 | 00000011 | 11111100 | 0xco |
1 | 00000110 | 10011111 | 01100000 | 0xf9 |
2 | 01011011 | 00100101 | 11011010 | 0xa4 |
3 | 01001111 | 00001101 | 11110010 | 0xb0 |
4 | 00110110 | 10010011 | 01101100 | 0x99 |
5 | 01101101 | 01001001 | 10110110 | 0x92 |
6 | 01111101 | 01000001 | 10111110 | 0x82 |
7 | 00000111 | 00011111 | 11100000 | 0xf8 |
8 | 01111111 | 00000001 | 11111110 | 0x80 |
9 | 01101111 | 00001001 | 11110110 | 0x90 |
A |
|
| 11101110 | 0x88 |
B |
|
|
| 0x83 |
C |
|
|
| 0xc6 |
D |
|
|
| 0xa1 |
E |
|
|
| 0x86 |
F |
|
|
| 0x8e |
[C语言源程序]
#include "reg52.h" //包函8051 内部资源的定义
unsigned char dat; //用于存储单片机接收发送缓冲寄存器SBUF里面的内容
sbit gewei=P2^6; //个位选通定义
sbit shiwei=P2^5; //十位选通定义
sbit baiwei=P2^4;
unsigned char code table[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0x88,0x83,0xc6,0xa1,0x86,0x8e,0x40,};
void Delay(unsigned int tc) //延时程序
{
while( tc != 0 )
{unsigned int i;
for(i=0; i<100; i++);
tc--;}
}
void LED() //LED显示接收到的数据(十进制)
{
shiwei=0; P0=table[dat/10]; Delay(10); shiwei=1;
gewei=0; P0=table[dat%10]; Delay(10); gewei=1;
baiwei=0; P0=table[dat/100]; Delay(10); baiwei=1;
}
///功能:串口初始化,波特率9600,方式1/
void Init_Com(void)
{
TMOD = 0x20;
PCON = 0x00;
SCON = 0x50;
TH1 = 0xFd;
TL1 = 0xFd;
TR1 = 1;
}
/主程序功能:实现接收数据并把接收到的数据原样发送回去///
void main()
{
Init_Com();//串口初始化
while(1)
{
if ( RI ) //扫描判断是否接收到数据,
{
dat = SBUF; //接收数据SBUF赋与dat
RI=0; //RI 清零。
SBUF = dat; //在原样把数据发送回去
}
LED(); //显示接收到的数据
}
}
///这一个例子是以扫描的方式编写的,还可以以中断的方式编写,请大家思考//
-------------------------------------------------------------------------------------------------------------------------------------------------------------
十三.DS18B20 测量温度系统
[实验任务]
用一片 DS18B20 构成测温系统,测量的温度精度达到 0.1 度,测量的温度的
范围在-20度到+50度之间,用4位数码管显示出来。
[硬件电路图]
[实验原理]
DS18B20 数字温度计是 DALLAS 公司生产的 1-Wire,即单总线器件,具有线
路简单,体积小的特点。因此用它来组成一个测温系统,具有线路简单,在一根
通信线,可以挂很多这样的数字温度计。DS18B20 产品的特点(1)、只要求一个
I/O口即可实现通信。(2)、在DS18B20中的每个器件上都有独一无二的序列号。
(3)、实际应用中不需要外部任何元器件即可实现测温。(4)、测量温度范围在
-55。C到+125。 C之间。(5)、数字温度计的分辨率用户可以从9位到12位选
择。(6)、内部有温度上、下限告警设置。
DS18B20详细引脚功能描述1 GND地信号;2 DQ数据输入/输出引脚。开漏单
总线接口引脚。当被用着在寄生电源下,也可以向器件提供电源;3 VDD可选择
的VDD引脚。当工作于寄生电源时,此引脚必须接地。
DS18B20的使用方法。由于DS18B20采用的是1-Wire总线协议方式,即在一根
数据线实现数据的双向传输,而对AT89S51单片机来说,我们必须采用软件的方
法来模拟单总线的协议时序来完成对DS18B20芯片的访问。由于DS18B20是在一
根I/O线上读写数据,因此,对读写的数据位有着严格的时序要求。DS18B20有
严格的通信协议来保证各位数据传输的正确性和完整性。该协议定义了几种信号
的时序:初始化时序、读时序、写时序。所有时序都是将主机作为主设备,单总
线器件作为从设备。而每一次命令和数据的传输都是从主机主动启动写时序开
始,如果要求单总线器件回送数据,在进行写命令后,主机需启动读时序完成数
据接收。数据和命令的传输都是低位在先。
[C语言源程序]
#include<reg52.h>
code unsigned char seg7code[11]={0x3f,0x06,0x5b,0x4f,0x66,
0x6d,0x7d,0x07,0x7f,0x6f,0x40}; //显示段码
void Delay(unsigned int tc) //显示延时程序
{while( tc != 0 )
{unsigned int i;
for(i=0; i<100; i++);
tc--;}
}
sbit TMDAT =P3^1; //DS18B20 的数据输入/输出脚DQ,根据情况设定
unsigned int sdata;//测量到的温度的整数部分
unsigned char xiaoshu1;//小数第一位
unsigned char xiaoshu2;//小数第二位
unsigned char xiaoshu;//两位小数
bit fg=1; //温度正负标志
void dmsec (unsigned int count) //延时部分
{
unsigned char i;
while(count--)
{for(i=0;i<115;i++);}
}
void tmreset (void) //发送复位
{
unsigned char i;
TMDAT=0; for(i=0;i<103;i++);
TMDAT = 1; for(i=0;i<4;i++);
}
bit tmrbit (void) //读一位//
{
unsigned int i;
bit dat;
TMDAT = 0;
i++;
TMDAT = 1;
i++; i++; //微量延时 //
dat = TMDAT;
for(i=0;i<8;i++);
return (dat);
}
unsigned char tmrbyte (void) //读一个字节
{
unsigned char i,j,dat;
dat = 0;
for (i=1;i<=8;i++)
{ j = tmrbit(); dat = (j << 7) | (dat >> 1); }
return (dat);
}
void tmwbyte (unsigned char dat) //写一个字节
{
unsigned char j,i;
bit testb;
for (j=1;j<=8;j++)
{ testb = dat & 0x01;
dat = dat >> 1;
if (testb)
{ TMDAT = 0; //写0
i++; i++;
TMDAT = 1;
for(i=0;i<8;i++); }
else
{ TMDAT = 0; //写0
for(i=0;i<8;i++);
TMDAT = 1;
i++; i++;}
}
}
void tmstart (void) //发送ds1820 开始转换
{ tmreset(); //复位
dmsec(1); //延时
tmwbyte(0xcc); //跳过序列号命令
tmwbyte(0x44); //发转换命令 44H,
}
void tmrtemp (void) //读取温度
{
unsigned char a,b;
tmreset (); //复位
dmsec (1); //延时
tmwbyte (0xcc); //跳过序列号命令
tmwbyte (0xbe); //发送读取命令
a = tmrbyte (); //读取低位温度
b = tmrbyte (); //读取高位温度
if(b>0x7f) //最高位为1 时温度是负
{a=~a; b=~b+1; //补码转换,取反加一
fg=0; //读取温度为负时fg=0
}
sdata = a/16+b*16; //整数部分
xiaoshu1 = (a&0x0f)*10/16; //小数第一位
xiaoshu2 = (a&0x0f)*100/16%10;//小数
xiaoshu=xiaoshu1*10+xiaoshu2; //小数
}
void DS18B20PRO(void)
{ tmstart();
//dmsec(5); //如果是不断地读取的话可以不延
tmrtemp(); //读取温度,执行完毕温度将存于
}
void Led()
{
if(fg==1) //温度为正时显示的数据
{ P2=P2&0xef;
P0=seg7code[sdata/10]; //输
Delay(8); P2=P2|0xf0; P2=P2&0xdf;
P0=seg7code[sdata%10]|0x80; //输出个
Delay(8); P2=P2|0xf0; P2=P2&0xbf;
P0=seg7code[xiaoshu1]; //输出小数点
Delay(8); P2=P2|0xf0; P2=P2&0x7f;
P0=seg7code[xiaoshu2]; //输出小
Delay(4); P2=P2|0xf0;
}
if(fg==0) //温度为负时显示的数据
{ P2=P2&0xef;
P0=seg7code[11]; //负号
Delay(8); P2=P2|0xf0; P2=P2&0xdf;
P0=seg7code[sdata/10]|0x80; //输出十位
Delay(8); P2=P2|0xf0; P2=P2&0xbf;
P0=seg7code[sdata%10]; //输出个位
Delay(8); P2=P2|0xf0; P2=P2&0x7f;
P0=seg7code[xiaoshu1]; //输出小
Delay(4); P2=P2|0xf0;
}
}
main()
{fg=1;
while(1)
{
DS18B20PRO();
Led();
}
}