STC8A8K64单片机关于AT24C04基本读写操作(包含硬件I2C与软件模拟I2C)

  •           实验:AT24C04基本读写操作
    

    步骤及现象:在下载程序前,选择stc-isp的IRC频率:12MHz。
    程序下载完成后,在串口助手界面,HEX模式下,选择波
    特率9600,然后点击“打开串口”按钮。这时按独立按键1
    在接收缓冲区打印出刚写入的16个数据。

*数据帧格式
I2C总线上传送的数据信号是广义的,既包括地址信号,又包括真正的数据信号。
在起始信号后必须传送一个从机的地址(7位),第8位是数据的传送方向位(R/T),用“0”表示主机发送数据(T),“1”表示主机接收数据(R)。每次数据传送总是由主机产生的终止信号结束。但是,若主机希望继续占用总线进行新的数据传送,则可以不产生终止信号,马上再次发出起始信号对另一从机进行寻址。
在总线的一次数据传送过程中,可以有以下几种组合方式:
a、主机向从机发送数据,数据传送方向在整个传送过程中不变:
a
注:有阴影部分表示数据由主机向从机传送,无阴影部分则表示数据由从机向主机传送。
A表示应答,A非表示非应答(高电平)。S表示起始信号,P表示终止信号。
b、主机在第一个字节后,立即从从机读数据:
b
c、在传送过程中,当需要改变传送方向时,起始信号和从机地址都被重复产生一次,但两次读/写方向位正好反相。
c
总线的寻址
I2C总线协议有明确的规定:采用7位的寻址字节(寻址字节是起始信号后的第一个字节)。
(1)寻址字节的位定义
寻址方式
D7~D1位组成从机的地址。D0位是数据传送方向位,为“0”时表示主机向从机写数据,为“1”时表示主机由从机读数据。
主机发送地址时,总线上的每个从机都将这7位地址码与自己的地址进行比较,如果相同,则认为自己正被主机寻址,根据R/T位将自己确定为发送器或接收器。
从机的地址由固定部分和可编程部分组成。在一个系统中可能希望接入多个相同的从机,从机地址中可编程部分决定了可接入总线该类器件的最大数目。如一个从机的7位寻址位有4位是固定位,3位是可编程位,这时仅能寻址8个同样的器件,即可以有8个同样的器件接入到该I2C总线系统中。
(2)写入过程
AT24C系列E2PROM芯片地址的固定部分为1010,A2、A1、A0引脚接高、低电平后得到确定的3位编码。形成的7位编码即为该器件的地址码。
单片机进行写操作时,首先发送该器件的7位地址码和写方向位“0”(共8位,即一个字节),发送完后释放SDA线并在SCL线上产生第9个时钟信号。被选中的存储器器件在确认是自己的地址后,在SDA线上产生一个应答信号作为相应,单片机收到应答后就可以传送数据了。
传送数据时,单片机首先发送一个字节的被写入器件的存储区的首地址,收到存储器器件的应答后,单片机就逐个发送各数据字节,但每发送一个字节后都要等待应答。
AT24C系列器件片内地址在接收到每一个数据字节地址后自动加1,在芯片的“一次装载字节数”(不同芯片字节数不同)限度内,只需输入首地址。装载字节数超过芯片的“一次装载字节数”时,数据地址将“上卷”,前面的数据将被覆盖。
当要写入的数据传送完后,单片机应发出终止信号以结束写入操作。写入n个字节的数据格式 :
写格式
(3)读出过程
单片机先发送该器件的7位地址码和写方向位“0”(“伪写”),发送完后释放SDA线并在SCL线上产生第9个时钟信号。被选中的存储器器件在确认是自己的地址后,在SDA线上产生一个应答信号作为回应。
然后,再发一个字节的要读出器件的存储区的首地址,收到应答后,单片机要重复一次起始信号并发出器件地址和读方向位(“1”),收到器件应答后就可以读出数据字节,每读出一个字节,单片机都要回复应答信号。当最后一个字节数据读完后,单片机应返回以“非应答”(高电平),并发出终止信号以结束读出操作。
读过程

at24c04_i2c.c文件:

#include "at24c0x_i2c.h"
#include <stc8.h>
#include <intrins.h>

#define AT24_Address  0xA0    // AT24C0X的设备地址

// i2c总线引脚定义
sbit SDA = P7^6;              // 数据线
sbit SCL = P7^7;              // 时钟线 

/********************************************************************
**********                硬件I2C操作部分              **************
*********************************************************************/

#ifdef HardI2C                // 硬件I2C操作部分

// 毫秒延时
void DelayI2C(unsigned int xms)
{
    unsigned int i, j;

	for(i=xms;i>0;i--)
	{
		for(j=921;j>0;j--);
	}
}

// 初始化硬件I2C
void InitI2C()
{
	// 访问逻辑地址位于XDATA区域的特殊功能寄存器前需要
	// 将 P_SW2(BAH)寄存器的最高位(EAXFR)置 1
	P_SW2 = 0x80; 
	
    I2CCFG = 0xe0;            // 使能I2C主机模式
    I2CMSST = 0x00;
	P_SW2 |= 0x20;
}

// 清中断
void Wait()     
{
    while (!(I2CMSST & 0x40));
    I2CMSST &= ~0x40;
}

// 起始信号
void Start()
{
    I2CMSCR = 0x01;                             //发送START命令
    Wait();
}

// 发送数据
void SendData(char dat)
{
    I2CTXD = dat;                               //写数据到数据缓冲区
    I2CMSCR = 0x02;                             //发送SEND命令
    Wait();
}

// 接收应答信号
void RecvACK()
{
    I2CMSCR = 0x03;                             //发送读ACK命令
    Wait();
}

// 接收数据
char RecvData()
{
    I2CMSCR = 0x04;                             //发送RECV命令
    Wait();
    return I2CRXD;
}

// 发送应答信号
void SendACK()
{
    I2CMSST = 0x00;                             //设置ACK信号
    I2CMSCR = 0x05;                             //发送ACK命令
    Wait();
}

// 发送非应答信号
void SendNAK()
{
    I2CMSST = 0x01;                             //设置NAK信号
    I2CMSCR = 0x05;                             //发送ACK命令
    Wait();
}

// 停止信号
void Stop()
{
    I2CMSCR = 0x06;                             //发送STOP命令
    Wait();
}

// 往地址add中,写入数据dat
//void write_24c04(unsigned char add, unsigned char dat)
//{
//	Start();                  // 发送起始命令
//    SendData(AT24_Address);   // 发送设备地址+写命令
//    RecvACK();
//    SendData(add);            // 发送存储地址
//    RecvACK();
//    SendData(dat);            // 写数据
//    RecvACK();
//    Stop();                   // 发送停止命令
//    DelayI2C(2000);           // 等待设备写数据,延时2s
//}

// 从地址add中,读数据
//unsigned char read_24c04(unsigned char add)
//{
//	unsigned char dat1;
//	Start();                  // 发送起始命令
//	SendData(AT24_Address);   // 发送设备地址+写命令
//	RecvACK();
//	SendData(add);            // 发送存储地址
//	RecvACK();
//	Start();                  // 发送起始命令
//	SendData(AT24_Address | 0x01);// 发送设备地址+读命令
//	RecvACK();
//	dat1 = RecvData();        // 读取数据
//	SendNAK();                
//	Stop();                   // 发送停止命令
//	return dat1;
//}

// 一次写count个数据(count < 17)   , 参数:首地址,数组指针,写入个数
void Mult_Write24c04(unsigned char add, unsigned char *s, unsigned char count)
{
	unsigned char i=0;
	Start();                  // 发送起始命令
    SendData(AT24_Address);   // 发送设备地址+写命令
    RecvACK();
    SendData(add);            // 发送存储地址
    RecvACK();
	for(i=0;i<count;i++)
	{
		SendData(*s);            // 写数据
		RecvACK();
		s++;
	}
    Stop();                   // 发送停止命令
    DelayI2C(2000);           // 等待设备写数据,延时2s
}

// 一次读count个数据(count < 17)  , 参数:首地址,接收数组,读取个数
void Mult_Read24c04(unsigned char add, unsigned char *str ,unsigned char count)
{
	unsigned char i=0;
	Start();                       // 发送起始命令
	SendData(AT24_Address);        // 发送设备地址+写命令
	RecvACK();
	SendData(add);                 // 发送存储地址高字节
	RecvACK();
	Start();                       // 发送起始命令
	SendData(AT24_Address | 0x01); // 发送设备地址+读命令
	RecvACK();
	for(i=0;i<count;i++)
	{
		if(i < count-1)
		{
			str[i] = RecvData();   // 读取数据
			SendACK();
		}
		else 
		{    
			str[i] = RecvData();   // 读取数据			
			SendNAK();
		}
	}		
	Stop();                        // 发送停止命令
}

/********************************************************************
**********             软件模拟I2C操作部分             **************
*********************************************************************/

#else                   

// 5微秒
void Delay5us()		          //@12.000MHz
{
	unsigned char i;

	_nop_();
	_nop_();
	i = 12;
	while (--i);
}

/***** 延时函数,xms为多少就延时多少毫秒 *****/    
// 在at24c04写单个字节时,用这个延时确保数据成功写入
//void AtDelay_ms(unsigned int xms)    // 晶振:12MHz
//{
//	unsigned int i, j;
//	for(i=xms;i>0;i--)
//		for(j=921;j>0;j--);
//}
/********** i2c启动信号 *************/
/*起始信号:在时钟线处于高电平时,数据线产生下降沿*/
void I2C_Start(void)
{
	SDA = 1;                   // 数据线拉高
	Delay5us();                // 延时5微秒
	SCL = 1;                   // 时钟线拉高
	Delay5us();                // 延时5微秒
	SDA = 0;                   // 数据线拉低
	Delay5us();                // 延时5微秒
}
/********** i2c停止信号 *************/
/*停止信号:在时钟线处于高电平时,数据线产生上升沿*/
void I2C_Stop(void)                    
{
	SDA = 0;                   // 数据线拉低
	Delay5us();                // 延时5微秒
	SCL = 1;                   // 时钟线拉高
	Delay5us();                // 延时5微秒
	SDA = 1;                   // 数据线拉低
	Delay5us();                // 延时5微秒
}
/********** 发送应答信号 ************/     
//void I2C_SendAck(bit ack)
//{
//	SDA = ack;
//	SCL = 1;
//	Delay5us();                // 延时5微秒
//	SCL = 0;
//	Delay5us();                // 延时5微秒
//}
/********** i2c检测应答 *************/
bit I2C_CheckAck()
{
	SCL = 1;
	Delay5us();                // 延时5微秒
	CY = SDA;
	SCL = 0;
	Delay5us();                // 延时5微秒
	return CY;
}
/********** i2c写一字节 *************/
void I2C_WriteByte(unsigned char byt)
{
    unsigned char i;
    for(i=0; i<8; i++)        // 发送八位数据
    {
        SCL = 0;              // 置低时钟线
        Delay5us();           // 延时5微秒
        if(byt & 0x80) 
			SDA  = 1;         // 发送1
        else 
			SDA  = 0;         // 发送0
        Delay5us();           // 延时5微秒
        SCL = 1;              // 置高时钟线
		Delay5us();           // 延时5微秒
        byt <<= 1;            // 数据左移一位       
    }
    SCL = 0;                  // 置低时钟线
	Delay5us();               // 延时5微秒
	SDA = 1;
	Delay5us();               // 延时5微秒
}
/********** i2c读一字节 *************/
unsigned char I2C_ReadByte(void)
{
    unsigned char i, da;
    for(i=0; i<8; i++)        // 八位 循环八次
    {   
    	SCL = 1;              // 置高时钟线
		Delay5us();           // 延时5微秒
		da <<= 1;             // 数据左移一位
		if(SDA) 
			da |= 1;          // 接收1
		SCL = 0;              // 置低时钟线
		Delay5us();           // 延时5微秒
    }
    return da;                // 返回接收到的数据
}

/***** 往at24c04地址add中写入dat ****/        
//void write_24c04(unsigned char add, unsigned char dat)
//{
//	bit ack;
//    I2C_Start();                   // 起始信号
//    I2C_WriteByte(AT24_Address);   // 发送设备地址+写信号
//    ack = I2C_CheckAck(); 
//    I2C_WriteByte(add);            // 发送存储单元地址
//    I2C_CheckAck();
//    I2C_WriteByte(dat);
//    ack = I2C_CheckAck();	
//    I2C_Stop();
//	AtDelay_ms(2);
//}
/**** 从at24c04地址add中读出数据 ****/
unsigned char read_24c04(unsigned char add)
{
	unsigned char temp, ack;
    I2C_Start();                        // 起始信号
    I2C_WriteByte(AT24_Address);        // 发送设备地址+写信号
    ack = I2C_CheckAck();
    I2C_WriteByte(add);                 // 发送存储单元地址
    ack = I2C_CheckAck();
    I2C_Start();                        // 起始信号
    I2C_WriteByte(AT24_Address | 0x01); // 发送设备地址+读信号
    ack = I2C_CheckAck();
	temp = I2C_ReadByte();
	I2C_Stop();                         // 停止信号
	return temp;
}
// 一次写count个数据(count < 17)   , 参数:首地址,数组指针,写入个数
void Mult_Write24c04(unsigned char add, unsigned char *s, unsigned char count)
{
	unsigned char i;
	bit ack;
	I2C_Start();                   // 起始信号
    I2C_WriteByte(AT24_Address);   // 发送设备地址+写信号
    ack = I2C_CheckAck();
    I2C_WriteByte(add);            // 发送存储单元地址
    ack = I2C_CheckAck();
	for(i=0;i<count;i++)
	{
		I2C_WriteByte(*s);
		ack &= I2C_CheckAck();
		s++;
	}
	I2C_Stop();
}
// 一次读count个数据(count < 17)  , 参数:首地址,接收数组,读取个数
void Mult_Read24c04(unsigned char add, unsigned char *str ,unsigned char count)
{
	unsigned char i;
	for(i=0;i<count;i++)
	{
		str[i] = read_24c04(add);
		add = add + 0x01;
	}
}

#endif

uart1.c文件:

#include "stc8.h"
#include "uart1.h"

bit busy;                   // 发送标志位 

/*************  串口1初始化配置   *********************/
void Uart1Init(void)		// 9600bps@12.000MHz
{
	SCON = 0x50;		    // 8位数据,可变波特率
	AUXR |= 0x40;		    // 定时器1时钟为Fosc,即1T
	AUXR &= 0xFE;		    // 串口1选择定时器1为波特率发生器
	TMOD &= 0x0F;		    // 设定定时器1为16位自动重装方式
	TL1 = 0xC7;		        // 设定定时初值(FEC7)H=(65223)D
	TH1 = 0xFE;		        // 65536-12M/9600/4=65223(小数直接舍去)
	ET1 = 0;		        // 禁止定时器1中断
	TR1 = 1;		        // 启动定时器1
	ES = 1;                 // 使能串口1中断
	EA = 1;                 // 开总中断
}

/******************* 发送一个字节  ********************/
void Uart1_Byte(char dat)
{
    while (busy);                // 当busy=0时,跳出循环,表示可以发送数据了
    busy = 1;                    // 发送标志置1,为下次发送做准备
    SBUF = dat;                  // 开始发送字节
}

/******************   发送一个字符串   ***************/
//void Uart1_String(char *s)
//{
//	while(*s != '\0')
//	{
//		Uart1_Byte(*s++);
//	}
//}
/******************** 重写putchar函数 ***************/
char putchar(char c)
{
    Uart1_Byte(c);                // printf映射到串口1的发送函数
    return c;
}



main.c文件:

// 请先看ReadMe.txt文件
#include "stc8.h"
#include <stdio.h>                  // 如果要使用"printf"需要加上这个头文件,同时在串口中要重写putchar
#include "at24c0x_i2c.h"
#include "uart1.h"

unsigned char dat[16]={0xa1,0xb2,0xc3,0xd4,0xe5,0xf6,0x1a,0x2b,0x3c,0x4d,0x5e,0x6f,0xaa,0xbb,0xcc,0xdd};
unsigned char das[16]={0};
sbit keyCtrol = P0^7;                          // 独立按键总开关
sbit key = P0^0;                               // 独立按键1
extern bit busy;                               // 发送数据标志位,在uart1.c文件中定义   

/***** 延时函数,xms为多少就延时多少毫秒 *****/
void Delay_ms(unsigned int xms)    // 晶振:12MHz
{
	unsigned int i, j;
	for(i=xms;i>0;i--)
		for(j=921;j>0;j--);
}

/*********  主函数  ***********/
void main()
{
	unsigned char i, val;
	keyCtrol = 0;                              // 开独立按键总开关
	Uart1Init();                               // 串口1初始化

#ifdef HardI2C                                 // 使用硬件I2C时,初始化操作
	InitI2C();
#endif

	val = 0x00;                                // AT24C04连存16个数据,首地址0x00。(0x00~0x0f)
	Mult_Write24c04(val,dat,16);
	while(1)
	{
		if(key == 0)                           // 判断独立按键1按下
		{
			Delay_ms(10);                      // 延时消抖
			if(key == 0)                       // 再次判断独立按键按下
			{
				val = 0x00;                    // at24c04中连读16个数据,首地址0x00。(0x00~0x0f)
				Mult_Read24c04(val,das,16);
				for(i=0;i<16;i++)              // 在串口接收缓冲区打印那16个数据
					printf("%c",das[i]);
				Delay_ms(200);                 // 避免按一次独立按键1,打印多次16个数据
			}			
		}	
	}
}
void uart1_Isr() interrupt 4                   // 串口1中断函数
{
	if(TI)
	{
		TI = 0;
		busy = 0;
	}
}

首先简单的说明以下I2C总线,I2C总线是一种串行数据总线,只有二根信号线,一根是双向的数据线SDA,另一根是时钟线SCL。在 I2C总线上传送的一个数据字节由八位组成。总线对每次传送的字节数没有限制,但每个字节后必须跟一位应答位。数据传送首先传送最高位(MSB),数据传送按图1所示格式进行。首先由主机发出启动信号“S”(SDA在SCL高电平期间由高电平跳变为低电平),然后由主机发送一个字节的数据。启动信号后的第一个字节数据具有特殊含义:高七位是从机的地址,第八位是传送方向位,0表示主机发送数据(写),1表示主机接收数据(读)。被寻址到的从机设备按传送方向位设置为对应工作方式。标准I2C总线的设备都有一个七位地址,所有连接在I2C总线上的设备都接收启动信号后的第一个字节,并将接收到的地址与自己的地址进行比较,如果地址相符则为主机要寻访的从机,应在第九位答时钟脉冲时向SDA线送出低电平作为应答。除了第一字节是通用呼叫地址或十位从机地址之外第二字节开始即数据字节。数据传送完毕,由主机发出停止信号“P”(SDA在SCL高电平期间由低电平跳变为高电平)。   AT24C系列串行E2PROM具有I2C总线接口功能,功耗小,宽电源电压(根据不同型号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 AT24C08 1024×8 1010A2P1P0 R/W 16 AT24C16 2048×8 1010P2P1P0 R/W 16   由于I2C总线可挂接多个串行接口器件,在I2C总线中每个器件应有唯一的器件地址,按I2C总线规则,器件地址为7位数据(即一个I2C总线系统中理论上可挂接128个不同地址的器件),它和1位数据方向位构成一个器件寻址字节,最低位D0为方向位(读/写)。器件寻址字节中的最高4位(D7~D4)为器件型号地址,不同的I2C总线接口器件的型号地址是厂家给定的,如AT24C系列E2PROM的型号地址皆为1010,器件地址中的低3位为引脚地址A2 A1 A0,对应器件寻址字节中的D3、D2、D1位,在硬件设计时由连接的引脚电平给定。   对AT24C系列 E2PROM的读写操作完全遵守I2C总线的主收从发和主发从收的规则。
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值