IIC通讯学习笔记
什么是IIC总线 ?
IIC总线是有一条SDA数据线和一条SCL时钟线组成,在这两条线上可以挂接多个IIC设备。每个IIC设备都有自己的地址,这些地址由硬件的出厂固定地址位和可编程地址位组成,只有在两条线上传输的值是IIC设备的地址时,这个IIC设备才会作出响应。
/*******************************************************************************
-
函 数 名 : IIC_Init
-
函数功能 : IIC初始化
-
输 入 : 无
-
输 出 : 无
*******************************************************************************/
void IIC_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(IIC_SCL_PORT_RCC|IIC_SDA_PORT_RCC,ENABLE);GPIO_InitStructure.GPIO_Pin=IIC_SCL_PIN;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;
GPIO_Init(IIC_SCL_PORT,&GPIO_InitStructure);GPIO_InitStructure.GPIO_Pin=IIC_SDA_PIN;
GPIO_Init(IIC_SDA_PORT,&GPIO_InitStructure);IIC_SCL=1;
IIC_SDA=1;
}
/*******************************************************************************
-
函 数 名 : SDA_OUT
-
函数功能 : SDA输出配置
-
输 入 : 无
-
输 出 : 无
*******************************************************************************/
void SDA_OUT(void)
{
GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Pin=IIC_SDA_PIN;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;
GPIO_Init(IIC_SDA_PORT,&GPIO_InitStructure);
}
/*******************************************************************************
-
函 数 名 : SDA_IN
-
函数功能 : SDA输入配置
-
输 入 : 无
-
输 出 : 无
*******************************************************************************/
void SDA_IN(void)
{
GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Pin=IIC_SDA_PIN;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;
GPIO_Init(IIC_SDA_PORT,&GPIO_InitStructure);
}
/*******************************************************************************
- 函 数 名 : IIC_Start
- 函数功能 : 产生IIC起始信号
- 输 入 : 无
- 输 出 : 无
*******************************************************************************/
void IIC_Start(void)
{
SDA_OUT(); //sda设置为输出模式
IIC_SDA=1;
IIC_SCL=1;
delay_us(5);
IIC_SDA=0;//SDA由高跳到低
delay_us(6);
IIC_SCL=0;//钳住I2C总线,准备发送或接收数据
}
/*******************************************************************************
- 函 数 名 : IIC_Stop
- 函数功能 : 产生IIC停止信号
- 输 入 : 无
- 输 出 : 无
*******************************************************************************/
void IIC_Stop(void)
{
SDA_OUT();//sda线输出
IIC_SCL=0;
IIC_SDA=0;//STOP:when CLK is high DATA change form low to high
IIC_SCL=1;
delay_us(6);
IIC_SDA=1;//发送I2C总线结束信号
delay_us(6);
}
/*******************************************************************************
-
函 数 名 : IIC_Wait_Ack
-
函数功能 : 等待应答信号到来
-
输 入 : 无
-
输 出 : 1,接收应答失败
0,接收应答成功
*******************************************************************************/
u8 IIC_Wait_Ack(void)
{
u8 tempTime=0;IIC_SDA=1;
delay_us(1);
SDA_IN(); //SDA设置为输入
IIC_SCL=1;
delay_us(1);
while(READ_SDA)
{
tempTime++;
if(tempTime>250)//等待一段时间还没应答则发送停止信号
{
IIC_Stop();
return 1;
}
}
IIC_SCL=0;//时钟输出0
return 0;
}
/*******************************************************************************
- 函 数 名 : IIC_Ack
- 函数功能 : 产生ACK应答
- 输 入 : 无
- 输 出 : 无
*******************************************************************************/
void IIC_Ack(void)
{
IIC_SCL=0;
SDA_OUT();
IIC_SDA=0;
delay_us(2);
IIC_SCL=1;
delay_us(5);
IIC_SCL=0;
}
/*******************************************************************************
- 函 数 名 : IIC_NAck
- 函数功能 : 产生NACK非应答
- 输 入 : 无
- 输 出 : 无
*******************************************************************************/
void IIC_NAck(void)
{
IIC_SCL=0;
SDA_OUT();
IIC_SDA=1;
delay_us(2);
IIC_SCL=1;
delay_us(5);
IIC_SCL=0;
}
/*******************************************************************************
- 函 数 名 : IIC_Send_Byte
- 函数功能 : IIC发送一个字节
- 输 入 : txd:发送一个字节
- 输 出 : 无
/
void IIC_Send_Byte(u8 txd)
{
u8 t;
SDA_OUT();
IIC_SCL=0;//拉低时钟开始数据传输
for(t=0;t<8;t++)
{
if((txd&0x80)>0) //因为先发的是最高位
IIC_SDA=1;//如果刚刚的位是1则证明SDA为高,所以置1
else
IIC_SDA=0;
txd<<=1; //为读下一位作准备
delay_us(2); //对TEA5767这三个延时都是必须的
IIC_SCL=1;//时钟线高的时候,数据保持稳定,保证正确读取
delay_us(2);
IIC_SCL=0; //时钟线拉低,允许读下一位
delay_us(2);
}
}
为什么要先把SCL设置为低电平?其实上面已经有提示,因为在SCL为高电平的时候,SDA的变化代表着起始或者终止信号,所以在SCL为低的时候,才允许SDA变化(这些变化代表数据)
/ - 函 数 名 : IIC_Read_Byte
- 函数功能 : IIC读一个字节
- 输 入 : ack=1时,发送ACK,ack=0,发送nACK
- 输 出 : 应答或非应答
*******************************************************************************/
u8 IIC_Read_Byte(u8 ack)
{
u8 i,receive=0;
SDA_IN();//SDA设置为输入
for(i=0;i<8;i++ )//一个字节一个字节读出来,所以要循环8次
{
IIC_SCL=0;
delay_us(2);
IIC_SCL=1;
receive<<=1;//左移一位
if(READ_SDA)//当SDA为1的时候读入1,否则直接补零
receive|=0x01;
delay_us(1);
}
if (!ack)
IIC_NAck();//发送nACK
else
IIC_Ack(); //发送ACK
return receive;
}