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 
#include 

#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;i0;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

uart1.c文件:

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

bit busy;                   // 发送标志位 

/*************  串口1初始化配置   *********************/
void Uart1Init(void)		// [email protected]
{
	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                   // 如果要使用"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;
	}
}

你可能感兴趣的:(备忘录)