刚学单片机C语言,记录学习IIC协议用24C02连续保存读取数据成功:
1、根据24C02的数据手册先画流程图,如下:
2、在.H头文件中进行 GPIO脚位定义与初始化:
sbit IO_SDA =P1^0; //数据线
sbit IO_SCL =P1^1; //时钟线
#define IO_SDA_L IO_SDA=0; // 数据线SDA输出低
#define IO_SDA_H IO_SDA=1; // 数据线SDA输出高
#define IO_SCL_L IO_SCL=0; // 时钟线SCL输出低
#define IO_SCL_H IO_SCL=1; // 时钟线SCL输出高
#define IO_SDA_inport P1M0=0x68; //引脚输入模式
#define IO_SDA_outport P1M0=0xC0; //引脚输出模式
#define IIC_IO_INIT {P1M0=0xC0; P1M1=0xC0;IO_SCL_H; IO_SDA_H; } //IO口初始化
3、对着流程图和数据手册开始写代码,一步步的分解:
2.1、先来起始信号,IIC协议的起始信号定义 ::时钟线保持高电平期间 数据线电平从高到低的跳变作为 I 2 C 总线的起始信号。.C文件中的代码如下:
void IIC_start() //起始信号:SCK高电平时,SDA由高变低
{
IIC_IO_INIT; //io口初始化 SDA输出高 SCK输出高
delay5us();
IO_SDA_L;
delay5us();
}
2.2、延时子程序的代码如下,会多次调用,先写出来。
//========================================================================
// 描述: 延迟5微秒.
// 参数: none.
// 返回: none.
//========================================================================
void delay5us() //@16.000MHz
{
unsigned char i;
i = 26;
while (--i);
}
2.3、有了起始信号,就有停止信号,IIC协议的停止信号定义 :时钟线保持高电平期间 数据线电平从低到高的跳变作为 I 2 C 总线的停止信号。代码如下:
/************************************************************************
函 数:停止信号
返 回:
说 明:读写取完数据后主机发送停止信号
*************************************************************************/
void IIC_stop() //停止信号:SCK高电平时,SDA由低变高
{
IO_SDA_outport;
IO_SDA_L;
delay5us();
IO_SCL_H;
delay5us();
IO_SDA_H;
delay5us();
}
2.4、再来应答信号,根据IIC协议的应答信号定义:I 2 C 总线数据传送时 每成功地传送一个字节数据后 接收器都必须产生一个应答信号 应答的器件在第 9 个时钟周期时将 SDA 线拉低 表示其已收到一个 8 位数据。 代码如下:
/************************************************************************
函 数:接收方发送应答信号
返 回:
说 明:连续读数据时,接收方要发送应答信号,从机才发送下一个数据
*************************************************************************/
void IIC_ACK() // 发送应答信号
{
IO_SCL_L;
delay5us();
IO_SDA_outport;
IO_SDA_L;
IO_SCL_H;
delay5us();
IO_SCL_L;
IO_SDA_inport;
}
2.5、有了应答信号, 就会有等待应答信号(也就是接收应答信号),有返回值的子函数,用于判断24C02是否在空闲状态,代码如下:
/************************************************************************
函 数:unsigned char Waite_ASK() 等待IIC的应答信号
返 回:0 无应答,1 有应答
说 明:等待从机的应答信号
*************************************************************************/
unsigned char Waite_ASK() // 等待IIC的应答信号
{
IO_SCL_L;
IO_SDA_inport; //SDA脚输入模式
delay5us();
IO_SCL_H;
delay5us();
if (IO_SDA)
{
IO_SCL_L;
IO_SDA_outport;
IO_SDA_L;
IO_SCL_L;
return 0; //无应答,返回0
}
else
{
IO_SCL_L;
IO_SDA_outport;
IO_SDA_L;
IO_SCL_L;
return 1; //被拉低,有应答 返回1
}
}
3、 接下来就是读写数据了。
3.1、先写发送一个字节的子函数,注意,是高位先发,代码如下:
/************************************************************************
函 数:主机MCU发送一个字节
输 入:r_data 要发送的数据
说 明:高位先发
1、发送完后要等待从机的应答信号
*************************************************************************/
void IIC_SendByte(unsigned char r_data)
{
unsigned char r_cnt=0;
IO_SDA_outport; // SDA脚输出模式
for (r_cnt=0; r_cnt<8; r_cnt++ )
{
IO_SCL_L;
delay5us();
if (r_data & 0x80)
{
IO_SDA_H; //发送1
}
else
{
IO_SDA_L; // 发送0
}
delay5us();
IO_SCL_H;
delay5us();
r_data<<=1;
}
IO_SCL_L;
}
3.2、有发送,也有接收数据,注意,是高位先收,再来一个接收数据的代码如下:
/************************************************************************
函 数:主机MCU接收一个字节
返 回:r_data 接收到的数据
说 明:高位先收
1、
*************************************************************************/
unsigned char IIC_ReceiveByte()
{
unsigned char r_cnt=0;
unsigned char r_data=0;
IO_SDA_inport;
for (r_cnt=0; r_cnt<8; r_cnt++)
{
IO_SCL_L;
delay5us();
IO_SCL_H;
r_data<<=1; //左移1位
if (IO_SDA) // 高为1(逻辑或置1),低为0(移位就是0)
{
r_data |= 0x01; //最低位置1
}
}
IO_SCL_L;
return r_data; //返回数据
}
4、到这里子函数都写完了,下一步就是按照上图的流程进行组合,完成一个完整的数据读取和保存。
4.1、先来个写数据保存的程序,有保存才能读取到数据, 先定义全局的两个数组,下面是连续保存4个字节的数据,保存的起始地址是0x10 , 用指针很好玩哦,多多使用指针,让逻辑结构更加清晰,第1次使用goto语句(goto语句有使用限制,最好使用在函数内哦)代码如下:
/***************** 函数的变量与位定义 ***********************************/
unsigned char data arr_IIC_RecevieData[4]={0}; //接收数组
unsigned char data arr_IIC_SendData[4]={0}; //发送数组
/************************************************************************
函 数:IIC 写操作
输 入:*Pw -->要保存的数据, r_Sendcnt---> 保存数据的次数
返 回:无
说 明:
1、发送起始信号和从器件地址 addr =0xA0
2、发送要写入数据的地址单元 write_addr 从0x00 开始到0xFF
3、发送要写的数据 r_data
4、查询应答信号,有继续发送,无就退出循环
4、如继续写入数据,继续发送数据,否则就发送停止信号
*************************************************************************/
void EEPROM_WriteByte(unsigned char data *Pw, unsigned char r_Sendcnt)
{ //定义指向data区的无符号字符型指针变量
unsigned char cnt;
IIC_start(); //起始信号
IIC_SendByte(0xA0); //发送从器件地址
if (!(Waite_ASK())) //查询应答信号
{
P06=0; //测试指示灯
goto IIC_Write_Quit; // 无应答退出
}
IIC_SendByte(0x10); //发送写保存数据的地址
Waite_ASK(); //发送完一个字节要等待应答信号
for(cnt=0; cnt<r_Sendcnt;cnt++) //连续发送
{
IIC_SendByte(*Pw); //发送要保存的数据
Waite_ASK(); //发送完一个字节要等待应答信号
Pw++; //坑点-->连续存储的数据个数(r_Sendcnt)必须是2的N次方。
} //24C02 连续个数不能超16个
IIC_Write_Quit:
IIC_stop(); //停止信号
}
4.2、存储完数据后,再进行读取,注意,保存数据后,最少要过5MS后才能读取哦,这里也使用了指针和goto语句。读取代码如下:
/************************************************************************
函 数:IIC 读操作
输 入:r_Readcnt--> 读取数据的次数
返 回:unsigned char
说 明:
1、发送起始信号和从器件伪写操作地址 24C02 addr =0xA0
2、发送要读取数据的地址 write_addr 从0x00 开始到0xFF
3、再次发送起始信号,发送从器件读操作 24C02 addr =0xA1
4、查询从器件应答信号
5、如继续读取数据,应答。
6、不继续读取数据,不应答, 再发送停止信号。
7、保存数据
*************************************************************************/
void EEPROM_ReadByte(unsigned char r_Readcnt)
{
unsigned char cnt;
unsigned char data *Pr=arr_IIC_RecevieData; //读取数据保存的地址
IIC_start();
IIC_SendByte(0xA0); //发送从器件伪写操作地址
if (!(Waite_ASK())) //查询应答信号
{
goto IIC_Read_Quit; // 无应答退出
}
IIC_SendByte(0x10); //发送要读取数据的地址
Waite_ASK(); //发送完一个字节要等待应答信号
IIC_start();
IIC_SendByte(0xA1); //发送读操作地址
Waite_ASK(); //发送完一个字节要等待应答信号
for(cnt=0; cnt<r_Readcnt;cnt++) //连续接收
{
*Pr= IIC_ReceiveByte();
Pr++;
IIC_ACK(); // 连续接收需发送应答信号后才能接收下一个数据
}
IIC_Read_Quit:
IIC_stop(); //停止信号
}
5、 到这里就写完了, 下面到主循环里面去验证一下吧,要保存的数据初始化=0,每200ms 加1,并存入24C02, 然后每过230ms就去读取一次, 读取到的数据通过串口发送出去。
void main()
{
CLKSWR = 0x51; //选择内部高频RC为系统时钟,2分频,Fosc=16MHz
CLKDIV = 0x01; //1分频得到Fcpu=16MHz 指令周期Tcpu=1/16us = 0.0625us
WDTC = B00010011; //看门狗初始化,
WDTCCR = 0xFF;
timer0_int(); //定时器0初始化;
gpio_init(); //IO口初始化;
P06=1;
while(1)
{
WDTC |= 0x10; //看门狗清0 按位或
if (F_200ms)
{
P04=~P04; //状态指示灯
F_200ms=0; //200ms保存一次
arr_IIC_SendData[0]++; //4个数据自动加1,验证保存的有效性
arr_IIC_SendData[1]++;
arr_IIC_SendData[2]++;
arr_IIC_SendData[3]++;
EEPROM_WriteByte(&arr_IIC_SendData,4); //保存4个数据
}
if(F_230ms)
{
P05=~P05; //状态指示灯
F_230ms=0; //间隔230ms 读取和发送串口数据
EEPROM_ReadByte(4); //读出数据4个数据
cont_uart_TxD(&arr_IIC_RecevieData,4); //串口发送4个数据
}
}
}
6、 看看串口发送出来的数据, 完全是对的上的。哦耶。享受小小成功的喜悦。
加油,加油,越努力越,,,,,,