一.软件模拟IIC

1.IIC延时函数

#define DELAY_TIME 5
void IIC_Delay(unsigned char i)
{
    do{_nop_();}
    while(i--);        
}

2.数据发送的条件

每个时钟脉冲传输一位数据。

在这里插入图片描述

3.开始和停止

在这里插入图片描述

//总线引脚定义
sbit SDA = P2^1;  /* 数据线 */
sbit SCL = P2^0;  /* 时钟线 */

//总线启动条件
//当SCLK时钟信号一直处于高电平状态时,
//SDA线由高电平跳变到低电平这个动作,表示起始信号
void IIC_Start(void)
{
    SDA = 1;
    SCL = 1;
    IIC_Delay(DELAY_TIME);
    SDA = 0;
    IIC_Delay(DELAY_TIME);
    SCL = 0;	
}

//总线停止条件
//当SCLK时钟信号一直处于高电平状态时,
//SDA线由低电平跳变到高电平这个动作,表示结束信号
void IIC_Stop(void)
{
    SDA = 0;
    SCL = 1;
    IIC_Delay(DELAY_TIME);
    SDA = 1;
    IIC_Delay(DELAY_TIME);
}

4.等待从机(外设)应答/主机发送应答

在这里插入图片描述

协议规定,当SDA和SCLK同时为高时,表示空闲状态。

应答:数据接收方发送信号(低电平),表示成功接收到数据,且可以继续接收。
非应答:数据接收方发送信号(高电平),表示由于某些原因不再接收数据。

//等待从机是否应答,0-应答,1-非应答
//主机每传完一个字节的数据即外设每收到一个字节的数据,
//从机就要在第9个时钟脉冲到来的时候,将SDA数据线拉低进行应答(ACK),
//且必须是稳定的低电平,表示已经收到了一个字节的数据,高电平表示不进行应答
bit IIC_WaitAck(void)
{
    bit ackbit;
	
    SCL  = 1;
    IIC_Delay(DELAY_TIME);
    ackbit = SDA;	//在时钟脉冲到来后,读取从机是否应答(此时已稳定)
    SCL = 0;
    IIC_Delay(DELAY_TIME);
    return ackbit;
}

//主机发送是否应答
void IIC_SendAck(bit ackbit)
{
    SCL = 0;
    SDA = ackbit;  	// 0:应答,1:非应答,在时钟脉冲到来前给出是否应答信号
    IIC_Delay(DELAY_TIME);
    SCL = 1;
    IIC_Delay(DELAY_TIME);
    SCL = 0; 
    SDA = 1;
    IIC_Delay(DELAY_TIME);
}

5.发送/接收一个字节

//通过I2C总线发送数据
void IIC_SendByte(unsigned char byt)
{
    unsigned char i;

    for(i=0; i<8; i++)
    {
        SCL  = 0;
        IIC_Delay(DELAY_TIME);
        
        if(byt & 0x80) SDA  = 1;	//按位发送一个字节数据,先发高位
        else SDA  = 0;
        IIC_Delay(DELAY_TIME);
        
        SCL = 1;	//产生时钟脉冲
        byt <<= 1;	//准备发送下一位
        IIC_Delay(DELAY_TIME);
    }
    SCL  = 0;  
}

//从I2C总线上接收数据
unsigned char IIC_RecByte(void)
{
    unsigned char i, da;
    for(i=0; i<8; i++)
    {   
    	SCL = 1;	//产生时钟脉冲
		IIC_Delay(DELAY_TIME);
		
		da <<= 1;		//准备接收下一位
		if(SDA) da |= 1;	//按位接收一个字节数据,先接收高位
		
		SCL = 0;
		IIC_Delay(DELAY_TIME);
    }
    return da;    
}

二.原理图

在这里插入图片描述

三.PCF8591(A/D转换芯片)

PCF8591器件地址:

高四位固定,为1001。A2、A1、A0由芯片对应引脚确定,此开发板全部接低电平,即全为0。最低位表示读/写,1—读,0—写。所以读芯片数据时,器件地址为0x91,写数据时,器件地址为0x90。

在这里插入图片描述

控制字节:

在这里插入图片描述

第7位:固定值0;

第6位:0—A/D转换,1—D/A转换;

第5、4位:00—四路单端输入,01—三路差分输入,10—两路单端,一路差分,11—两路差分输入;

第3位:固定值0;

第2位:0—禁止自动增量,1—允许自动增量;

第1、0位:00—选择AIN0,01—选择AIN1,10—选择AIN2,11—选择AIN3.

1.A/D转换输出过程:

在这里插入图片描述

代码

unsigned char READ_IIC_PCF8591(unsigned char con)
{
	unsigned char temp;
	EA=0;					//关闭中断,防止干扰读取
	IIC_Start();
	IIC_SendByte(0x90);		//发送器件地址,写
	IIC_WaitAck();
	IIC_SendByte(con);		//发送控制字节
	IIC_WaitAck();
	
	IIC_Start();
	IIC_SendByte(0x91);		//发送器件地址,读
	IIC_WaitAck();
	temp=IIC_RecByte();		//读一个字节数据
	IIC_SendAck(1);			//主机发送非应答,不再接收数据
	IIC_Stop();
	EA=1;
	return temp;
}

void mian()
{
	unsigned char vol;
	while(1)
	{
		vol=READ_IIC_PCF8591(0x03);		
		//A/D转换模式,读取通道3的数据,即电位器电压数字量
		//若为0x01,则读取通道1的数据,即光敏电阻的数值,得到光强
		vol=vol/255.0*5;	//把数字量回归成模拟量,8位AD
	}
}

2.D/A转换输出过程:

在这里插入图片描述

代码

void IIC_PCF8591_DA(unsigned char dat)
{
	IIC_Start();
	IIC_SendByte(0x90);		//发送器件地址,写
	IIC_WaitAck();
	IIC_SendByte(0x40);		//发送控制字节,设置D/A转换模式
	IIC_WaitAck();
	
	IIC_SendByte(dat);		//发送数字量
	IIC_WaitAck();
	IIC_Stop();	
}

void mian()
{
	unsigned char vol;
	while(1)
	{
		vol=3.0/5*255;	//把3V电压转换成8位数字量
		IIC_PCF8591_DA(vol);	//输出3V电压
	}
}

四.AT24C02(EEPROM,256*8bit=2Kb容量,分成32页)

器件地址:

该系列芯片地址表,此芯片是2K容量,所以看第一个地址。与PCF8591类似。

在这里插入图片描述

1.向芯片写一个字节数据:

在这里插入图片描述

代码

void WRITE_AT24C02(unsigned char add,unsigned char dat)
{
	IIC_Start();
	IIC_SendByte(0xa0);		//发送器件地址,写
	IIC_WaitAck();	
	IIC_SendByte(add);		//发送数据存储地址(0x00~0xFF,共256个地址,每个地址一个字节)
	IIC_WaitAck();
	
	IIC_SendByte(dat);		//发送需存入的数据
	IIC_WaitAck();
	IIC_Stop();	
}

2.从芯片任意地址读一个字节数据

在这里插入图片描述

代码

unsigned char READ_AT24C02(unsigned char add)
{
	unsigned char temp;
	EA=0;
	IIC_Start();
	IIC_SendByte(0xa0);		//发送器件地址,写
	IIC_WaitAck();
	IIC_SendByte(add);		//发送需要读取的数据地址
	IIC_WaitAck();
	
	IIC_Start();
	IIC_SendByte(0xa1);		//发送器件地址,读
	IIC_WaitAck();
	temp=IIC_RecByte();		//读取一个字节数据
	IIC_SendAck(1);			//主机发送非应答,停止读取数据
	IIC_Stop();
	EA=1;
	return temp;
}
//每次复位或加电,自加1,并存入AT24C02
void main()
{
	unsigned char number;
	number=READ_AT24C02(0x00);	//读取0x00地址的数据
	WRITE_AT24C02(0x00,++number);	//自加1后重新存入
	while(1);
}