单片机学习之二十:E2PROM芯片24C02的读写程序
一、实验目的:
给24C02的内部RAM写入一组数据,数据从24C02内部RAM的01h开始存放。然后再把这组数据读出来,检验写入和读出是否正确。
在这里我们给24C02中写入0、1、2的段码,然后把它读出来,送到数码管显示。
二、理论知识准备:
上面两个实验主要学习的是利用单片机的串口进行通讯,本实验要介绍的是基于I2C总线的串行通讯方法,下面我们先介绍一下I2C总线的相关理论知识。
(一)、I2C总线概念
I
(二)、I2C总线结构
I
一个典型的I2C总线应用系统的组成结构如下图所示(假设图中的微控制器、LCD驱动、E2PROM、ADC各器件都是具有I2C总线接口的器件):
我们知道单片机串行通讯的发送和接收一般都各用一条线TXD和RXD,而I2C总线的数据线既可以发送也可以接受,工作方式可以通过软件设置。所以,I2C总线结构的硬件结构非常简洁。
当某器件向总线上发送信息时,它就是发送器,而当其从总线上接收信息时,又成为接收器。
(三)、I
下面我们看看I2C总线是如何进行数据传送的。我们知道,在一根数据线上传送数据时必须一位一位的进行,所以我们首先研究位传送。
1、位传输
I
那么是不是所有I2C总线中的信号都必须符合上述的有效性呢?只有两个例外,就是开始和停止信号。
开始信号:当SCL为高电平时,SDA发生从高到低的跳变,就定义为开始信号。
停止信号:当SCL为高电平时,SDA发生从低到高的跳变,就定义为结束信号。
开始和结束信号的时序图如下图所示:
2、数据传输的字节格式
SDA传送数据是以字节为单位进行的。每个字节必须是8位,但是传输的字节数量不受限制,首先传送的是数据的最高位。每次传送一个字节完毕,必须接收到从机发出的一个应答位,才能开始下一个字节的传输。如果没有接受到应答位,主机则产生一个停止条件结束本次的传送。那么从机应该发出什么信号算是产生了应答呢?这个过程是这样的。当主器件传送一个字节后,在第9个SCL时钟内置高SDA线,而从器件的响应信号将SDA拉低,从而给出一个应答位。
好啦,了解了I2C传输数据的格式,现在来研究双方传送的协议问题。
3、 I
I
(1)、主器件发出开始信号
(2)、主器件发出第一个字节,用来选通相应的从器件。其中前7位为地址码,第8位为方向位(R/W)。方向位为“0”表示发送,方向位为“1”表示接受。
(3)、从机产生应答信号,进入下一个传送周期,如果从器件没有给出应答信号,此时主器件产生一个结束信号使得传送结束,传送数据无效。
(4)、接下来主、从器件正式进行数据的传送,这时在I2C总线上每次传送的数据字节数不限,但每一个字节必须为8位(传送的时候先送高位,再送低位)。当一个字节传送完毕时,再发送一个应答位(第9位),如上一条所述,这样每次传送一个字节都需要9个时钟脉冲。数据的传送过程如下图所示:
(四)、
AT
我们对引脚的功能作一个简单的解释:
VCC,GND:电源、地引脚
A
SCLK、SDA:通信引脚
WP:写保护引脚
从上面的电路连接知:A2A1A0=000,可见如果要对24C02进行写操作,寻址字节是1010 000 0;如果对24C02进行读操作,寻址字节是1010 000 1。用单片机的P1.6脚作为串行时钟线,用P1.7脚作串行数据线。
(五)、程序分析
写过程:
(1)、主机首先发出开始信号
(2)、发出写24C02的寻址字节1010 000 0,即0A0H
(3)、发数据写入24C02的地址,本例中为01H
(4)、往24C02中写入数据,这里是3个字节,分别为48h,0ebh,52h。
(5)、写完毕发出停止信号
读过程:
(1)、主机发出start信号
(2)、发写24C02的寻址字节1010 000 0
(大家可能要问:我们是读数据,为什么要发写信号呢?这是因为你首先要送出一个信号,说明从24C02中的哪个地址读取数据。)
(3)、发要读取的数据在24C02中的地址,即01h
(4)、主机发start信号
(5)、发读24C02的寻址字节1010 000 1
(5)、从24 C02中读取数据
(6)、读取完毕发出停止信号
在这个程序中,我们把开始信号,结束信号、写一个字节数据、读一个字节数据都编制成为通用的子程序,便于在程序中随时调用。发送和接受应答位的过程放到子程序中,这样可以使得程序结构简化。具体的程序如下所示,希望大家认真理解。
三、实验程序
Org 0000h
I2cdata equ 30h ;发送数据缓冲区的首址
2402data equ 01h ;接受缓冲区首址
numdata equ 03h ;传送的字节数,传送3个字节
Sda bit p1.7
Scl bit p1.6
Ajmp main
Main: Lcall init ;初始化给30h,31h,32h中存入0,1,2的段码
Mainwr: Lcall start ;启动
Mov r7,#
Lcall send ;发送写
Mov r7,#2402data
Lcall send ;发送数据存入
Mov r5,#Numdata ;欲发送的字节数
Mov r0,#i2cdata ;发送缓冲区的首址
wrloop: Mov a,@r0
Mov r7,a
Inc r0
Lcall send
Djnz r5, wrloop ;把3个字节的数据发送出去
lcall stop ;停止
lcall d1s
mov r5,#Numdata ; 要读取的字节数重新赋值
Mainre: lcall start ;启动
Mov r7,#
Lcall send ;发送写
Mov r7,#2402data
Lcall send ;发接受缓冲区首址
Lcall start ; 再次启动
Mov r7,#
Lcall send ;发送读
Reloop: Lcall read ;调用读取一个字节数据的子程序
mov p0,r7 ;把读进来的数送到p0口显示
lcall d1s
lcall d1s
Djnz r5,reloop
Lcall stop ;3字节读取完毕发出停止信号
Ajmp $
init: mov p2,#0ffh ;初始化,30h、31h、32h中存入0、1、2的段码
mov 30h,#48h
mov 31h,#0ebh
mov 32h,#52h
ret
start: setb sda ;启动信号子程序,大家可以参考开始信号的时序图
setb scl
lcall d5u
clr sda
lcall d5u
clr scl
ret
stop: clr sda ;停止信号子程序
setb scl
lcall d5u
setb sda
lcall d5u
clr sda
clr scl
ret
;send是发送一个字节子程序
send: mov r6,#08h
mov a,r7 ;要发送的数在r7中
sendlop1 : rlc a ;左环移,把A的最高位移入cy
mov sda,c ;把cy的值通过sda发送出去
setb scl ;在scl上产生一个时钟
lcall d5u
clr scl
djnz r6, sendlop1 ;重复8次,发送一个字节
;cack是检查应答信号的子程序
cack: setb sda ;主机首先拉高sda
setb scl ;发出一个时钟
lcall d5u
sendlop2:mov c,sda ;读入sda的状态,如果是0表示接受到了应答
jc sendlop2
clr scl ;接受到应答位,结束时钟
ret
read: mov r6,#08h ;读取一个字节子程序
readlop1: setb sda ;置sda为输入方式
setb scl ;发出一个时钟
lcall d5u
mov c,sda ;读入sda状态
rlc a ;把该位的状态移入A中
clr scl ;结束时钟
djnz r6,readlop1 ;重复8次,读入一个字节
mov r7,a ;读进来的数放在r7中
;sack是发送应答位子程序
sack: clr sda ;拉低sda线
setb scl ;发出时钟信号
lcall d5u
clr scl
setb sda
ret
d5u: nop ;延时5us子程序
nop
nop
nop
nop
ret
d1s: mov r1,#100 ;延时1s子程序
del1: mov r4,#20
del2: mov r3,#0ffh
del3: djnz r3,del3
djnz r4,del2
djnz r1,del1
ret
end
大家把这个程序下载到测试板上面,发现数码管依次显示数字0、1、2
评论