没买板子之前,用protues画过电路图,实现了通过i2c总线向EEPROM中写入和读出数据。
今天,在自己买的板子上面写关于i2c总线的程序,有个地方忘了延时,调程序的时候很蛋疼。下面说说我对I2c总线的理解
i2c总线共有两根线,一根是时钟信号线,一根是数据线。这里我只实现了,指定EEPROM片内地址,并向其中写入一个字节。从EEPROM中指定地址读出一个字节。
写入过程
1.需要发一个启动信号
2.发送EEPROM设备地址
3.发送EEPROM片内地址
4.发送你要写入的数据
5.发送结束信号
读出过程:
1.发送启动信号
2.发送设备地址(注意这里是伪写 和写入过程前两个步骤一样 注意仔细看EEPROM文档)
3.发送EEPROM片内地址
4.发送启动信号(读数据需要启动两次)
5.发送设备地址(这里和读过程不同的是r/w位 前面是低电平 这里是高电平)
6.读取数据
7.发送结束信号
6.发送结束信号
当然单片机向总线写数据完成后,要注意检测应答信号,读取数据后,单片机应向设备发送,非应答信号或者叫应答非信号。
/*=================================================================================
功能:通过i2c总线向EEPROM中写入数据 并读回数据 验证I2C总线通信 实现记录开机次数
电路图:P2.0接SCL
P2.1接SDA
P1口接led发光二极管
P2.3接 数码管位选信号
P2.2接段选信号
P0口接数码管
注意理解设备发出的应答
和单片机发出的非应答或者叫应答非
两者使得通信完整 只不过方向不一样
====================================================================================*/
#include <reg51.h>
#include <intrins.h>
#define uchar unsigned char
#define uint unsigned int
#define TIME_SET 50000 /* 一次定时时间 */ sbit scl = P2^;
sbit sda = P2^; bit ack; uchar code table[] = {
0x3F,0x06,0x5b,0x4f,0x66,0x6d,
0x7d,0x07,0x7f,0x6f,0x77,0x7c,
0x39,0x5e,0x79,0x71,0x00
}; //led亮的数字 sbit wela = P2^;//位选信号
sbit dula = P2^;//段选
uchar times; void start(); /* 开始信号 */
void stop(); /* 结束信号 */
void write(uchar byte); /* 向EEPROM中写入一个字节 */
void delay(uchar time_delay); /* 延时函数 */
void write_address(uchar byte, uchar address); /* 根据地址向EEPROM中写入数据 */
uchar read_address(uchar address); /* 从指定地址读出数据 */
void no_ack(); /* 发送一个非应答信号 */
void display(uchar num_display); /* 显示开机次数 */
void init(); void main()
{
init();
times = read_address(0x50);
times += ;
write_address(times, 0x50);
display(times);
while();
}
/*==========================
功能:发送启动信号
输入:无
输出:启动信号 SDA负跳变
============================*/
void start()
{
sda = ;
// _nop_(); /* ? */
scl = ;
delay();
sda = ;
delay();
scl = ; /* scl为什么要拉低 */
delay(); /* ? */
}
/*==========================
功能:发送结束信号
输入:无
输出:启动信号 SDA正跳变
============================*/
void stop()
{
sda = ;
_nop_();
scl = ;
delay();
sda = ;
delay();
}
/*==============================================
功能:延时函数 time_delay = 10 延时大约0.5MS
输入:延时时间
输出:延时
================================================*/
void delay(uchar time_delay)
{
uchar i,j;
for(i = time_delay; i > ; i--)
for(j = time_delay; j > ; j --)
{}
}
/*=====================================
功能:向数据线上写一个字节 先写高位
输入:待写字节
输出:写到数据总线上
=======================================*/
void write(uchar byte)
{
uchar i = ; for(; i < ; i ++)
{
if((byte << i) & 0x80)
sda=;
else
sda = ;
delay(); /* 为啥要延时 */
scl = ;
delay();
scl = ;
}
delay(); sda = ;
delay();
scl = ;
delay();
if(sda == )
ack = ;
else
ack = ; scl = ;
} /*=========================================
功能:向指定地址写入一个字节数据
输入:address写入地址,byte写入字节数据
输出:无
===========================================*/
void write_address(uchar byte, uchar address)
{
start();
delay();
write(0xae); /* 写入设备地址 */
delay();
write(address);
delay();
write(byte); delay(); stop();
delay(); /* 延时很重要!!??? */
}
/*==========================
功能:从指定地址读出数据
输入:数据地址
输出:指定地址的数据
============================*/
uchar read_address(uchar address)
{
uchar i;
uchar result = ;
start(); write(0xae); /* 写入设备地址 */ write(address); start(); /* 第二次设置开始信号 */ write(0xaf); sda = ; for(i = ; i < ; i ++) /* 读出数据 */
{
scl = ;
delay();
scl = ;
delay();
result <<= ; /* result <<= 1? */
if(sda == )
result = result + ; delay();
}
scl = ; /* 为啥要拉低 */
delay(); no_ack(); /* 发送非应答信号 */ stop();
return result;
}
/*=======================
功能:初始化变量
输入:无
输出:初始化后的变量
========================*/
void init()
{
ack = ;
scl = ;
sda = ; times = ;
EA = ;
ET0 = ;
TH0 = ( - TIME_SET) / ;
TL0 = ( - TIME_SET) % ;
TR0 = ;
}
/*==========================
功能:发送一个非应答信号
输入:无
输出:非应答信号
============================*/
void no_ack()
{
sda = ;
delay();
scl = ;
delay();
scl= ;
delay();
}
/*======================
功能:定时器中断函数
输入:定时器0中断
输出:刷新数码管显示
========================*/
void timer0() interrupt
{
TH0 = ( - TIME_SET) / ;
TL0 = ( - TIME_SET) % ;
display(times);
}
/*==========================
功能:数码管显示开机次数
输入:开机次数
输出:数码管显示开机次数
============================*/
void display(uchar num_display)
{
uchar ge,shi;//个位和十位 ge = num_display % ;
shi = num_display / ;
//消影
P0 = 0xff;
wela = ;
wela = ;
P0 = ;
dula = ;
dula = ; //显示十位数字
P0 = 0xfe;
wela = ;
wela = ;
P0 = table[shi];
dula = ;
dula = ;
delay(); //消影
P0 = 0xff;
wela = ;
wela = ;
P0 = ;
dula = ;
dula = ; //显示个位数字
P0 = 0xfd;
wela = ;
wela = ;
P0 = table[ge];
dula = ;
dula = ;
delay();
}