IIC简介
IIC总线是同步通信的一种特殊形式,是一种串行,半双工的通信,I2C 总线只有两根双向信号线。一根是数据线 SDA,另一根是时钟线 SCL。IIC分为硬件IIC和软件IIC,DSP中有硬件IIC,但是不方便拓展,所以日常使用时使用软件IIC居多。
IIC总线通信过程
- 主机发送起始信号启用总线
- 主机发送一个字节数据指明从机地址和后续字节的传送方向
- 被寻址的从机发送应答信号回应主机
- 发送器发送一个字节数据
- 接收器发送应答信号回应发送器
- …………(循环4.5)
- 通信完成后主机发送停止信号释放总线
IIC总线寻址方式
主机在发送起始信号后必须先发送一个字节的数据,该数据的高7位为从机地址,最低位表示后续字节的传送方向,'0'表示主机发送数据,'1'表示主机接收数据;总线上所有的从机接收到该字节数据后都将这7位地址与自己的地址进行比较,如果相同,则认为自己被主机寻址,然后再根据第8位将自己定为发送器或接收器。
起始信号和停止信号
- SCL为高电平时,SDA由高变低表示起始信号
- SCL为高电平时,SDA由低变高表示停止信号
- 起始信号和停止信号都是由主机发出,起始信号产生后总线处于占用状态停止信号产生后总线处于空闲状态
字节传送与应答
- IIC总线通信时每个字节为8位长度,数据传送时,先传送最高位,后传送低位,发送器发送完一个字节数据后接收器必须发送1位应答位来回应发送器即一帧共有9位
典型IIC时序
- 主机向从机发送数据
- 从机向主机发送数据
- 主机先向从机发送数据,然后从机再向主机发送数据
注:阴影部分表示数据由主机向从机传送,无阴影部分则表示数据由从机向主机传送;A表示应答, A非表示非应答,S表示起始信号,P表示终止信号
代码分析
IIC驱动代码
/*******************************************************************************
* 头文件
*******************************************************************************/
#define IIC_SCL_SETH (GpioDataRegs.GPBSET.bit.GPIO33=1)
#define IIC_SCL_SETL (GpioDataRegs.GPBCLEAR.bit.GPIO33=1)
#define IIC_SDA_SETH (GpioDataRegs.GPBSET.bit.GPIO32=1)
#define IIC_SDA_SETL (GpioDataRegs.GPBCLEAR.bit.GPIO32=1)
#define READ_SDA (GpioDataRegs.GPBDAT.bit.GPIO32)
/*******************************************************************************
* 函 数 名 : SDA_OUT
* 函数功能 : SDA输出配置
* 输 入 : 无
* 输 出 : 无
*******************************************************************************/
void SDA_OUT(void)
{
EALLOW;
GpioCtrlRegs.GPBDIR.bit.GPIO32=1; //Output. SDA
EDIS;
}
/*******************************************************************************
* 函 数 名 : SDA_IN
* 函数功能 : SDA输入配置
* 输 入 : 无
* 输 出 : 无
*******************************************************************************/
void SDA_IN(void)
{
EALLOW;
GpioCtrlRegs.GPBDIR.bit.GPIO32=0; //Input, SDA
EDIS;
}
/*******************************************************************************
* 函 数 名 : IIC_Start
* 函数功能 : 产生IIC起始信号
* 输 入 : 无
* 输 出 : 无
*******************************************************************************/
void IIC_Start(void)
{
SDA_OUT(); //sda线输出
IIC_SDA_SETH;
IIC_SCL_SETH;
DELAY_US(5);
IIC_SDA_SETL;//START:when CLK is high,DATA change form high to low
DELAY_US(6);
IIC_SCL_SETL;//钳住I2C总线,准备发送或接收数据
}
/*******************************************************************************
* 函 数 名 : IIC_Stop
* 函数功能 : 产生IIC停止信号
* 输 入 : 无
* 输 出 : 无
*******************************************************************************/
void IIC_Stop(void)
{
SDA_OUT();//sda线输出
IIC_SCL_SETL;
IIC_SDA_SETL;//STOP:when CLK is high DATA change form low to high
IIC_SCL_SETH;
DELAY_US(6);
IIC_SDA_SETH;//发送I2C总线结束信号
DELAY_US(6);
}
/*******************************************************************************
* 函 数 名 : IIC_Wait_Ack
* 函数功能 : 等待应答信号到来
* 输 入 : 无
* 输 出 : 1,接收应答失败
0,接收应答成功
*******************************************************************************/
unsigned char IIC_Wait_Ack(void)
{
unsigned char tempTime=0;
IIC_SDA_SETH;
DELAY_US(1);
SDA_IN(); //SDA设置为输入
IIC_SCL_SETH;
DELAY_US(1);
while(READ_SDA)
{
tempTime++;
if(tempTime>250)
{
IIC_Stop();
return 1;
}
}
IIC_SCL_SETL;//时钟输出0
return 0;
}
/*******************************************************************************
* 函 数 名 : IIC_Ack
* 函数功能 : 产生ACK应答
* 输 入 : 无
* 输 出 : 无
*******************************************************************************/
void IIC_Ack(void)
{
IIC_SCL_SETL;
SDA_OUT();
IIC_SDA_SETL; //SDA置低电平,表示应答
DELAY_US(2);
IIC_SCL_SETH;
DELAY_US(5);
IIC_SCL_SETL;
}
/*******************************************************************************
* 函 数 名 : IIC_NAck
* 函数功能 : 产生NACK非应答
* 输 入 : 无
* 输 出 : 无
*******************************************************************************/
void IIC_NAck(void)
{
IIC_SCL_SETL;
SDA_OUT();
IIC_SDA_SETH; //SDA保持高电平,表示非应答
DELAY_US(2);
IIC_SCL_SETH;
DELAY_US(5);
IIC_SCL_SETL;
}
/*******************************************************************************
* 函 数 名 : IIC_Send_Byte
* 函数功能 : IIC发送一个字节
* 输 入 : txd:发送一个字节
* 输 出 : 无
*******************************************************************************/
void IIC_Send_Byte(unsigned char txd)
{
unsigned char t;
SDA_OUT();
IIC_SCL_SETL;//拉低时钟开始数据传输
for(t=0;t<8;t++)
{
if((txd&0x80)>0) //0x80 1000 0000
IIC_SDA_SETH;
else
IIC_SDA_SETL;
txd<<=1;
DELAY_US(2); //对TEA5767这三个延时都是必须的
IIC_SCL_SETH;
DELAY_US(2);
IIC_SCL_SETL;
DELAY_US(2);
}
}
/*******************************************************************************
* 函 数 名 : IIC_Read_Byte
* 函数功能 : IIC读一个字节
* 输 入 : ack=1时,发送ACK,ack=0,发送nACK
* 输 出 : 应答或非应答
*******************************************************************************/
unsigned char IIC_Read_Byte(unsigned char ack)
{
unsigned char i,receive=0;
SDA_IN();//SDA设置为输入
for(i=0;i<8;i++ )
{
IIC_SCL_SETL;
DELAY_US(2);
IIC_SCL_SETH;
receive<<=1;
if(READ_SDA)receive++;
DELAY_US(1);
}
if (!ack)
IIC_NAck();//发送nACK
else
IIC_Ack(); //发送ACK
return receive;
}
初始化代码
IIC的初始化一般是初始化两个GPIO,分别给SCL和SDA使用。
void IICA_Init(void)
{
EALLOW;
SysCtrlRegs.PCLKCR3.bit.GPIOINENCLK = 1;// 开启GPIO时钟
GpioCtrlRegs.GPBPUD.bit.GPIO32 = 0; //上拉
GpioCtrlRegs.GPBDIR.bit.GPIO32 = 1; // 输出端口
GpioCtrlRegs.GPBMUX1.bit.GPIO32 = 0; // IO口
GpioCtrlRegs.GPBQSEL1.bit.GPIO32 = 3; // 不同步
GpioCtrlRegs.GPBPUD.bit.GPIO33 = 0; //上拉
GpioCtrlRegs.GPBDIR.bit.GPIO33 = 1; // 输出端口
GpioCtrlRegs.GPBMUX1.bit.GPIO33 = 0; // IO口
GpioCtrlRegs.GPBQSEL1.bit.GPIO33 = 3; // 不同步
EDIS;
}
IIC在AT24C02中的应用
/*******************************************************************************
* 函 数 名 : AT24CXX_ReadOneByte
* 函数功能 : 在AT24CXX指定地址读出一个数据
* 输 入 : ReadAddr:开始读数的地址
* 输 出 : 读到的数据
*******************************************************************************/
unsigned char AT24CXX_ReadOneByte(Uint16 ReadAddr)
{
unsigned char temp=0;
IIC_Start();
if(EE_TYPE>AT24C16)
{
IIC_Send_Byte(0XA0); //发送写命令
IIC_Wait_Ack();
IIC_Send_Byte(ReadAddr>>8);//发送高地址
}
else
{
IIC_Send_Byte(0XA0+((ReadAddr/256)<<1)); //发送器件地址0XA0,写数据
}
IIC_Wait_Ack();
IIC_Send_Byte(ReadAddr%256); //发送低地址
IIC_Wait_Ack();
IIC_Start();
IIC_Send_Byte(0XA1); //进入接收模式
IIC_Wait_Ack();
temp=IIC_Read_Byte(0);
IIC_Stop();//产生一个停止条件
return temp;
}
/*******************************************************************************
* 函 数 名 : AT24CXX_WriteOneByte
* 函数功能 : 在AT24CXX指定地址写入一个数据
* 输 入 : WriteAddr :写入数据的目的地址
DataToWrite:要写入的数据
* 输 出 : 无
*******************************************************************************/
void AT24CXX_WriteOneByte(Uint16 WriteAddr,unsigned char DataToWrite)
{
IIC_Start();
if(EE_TYPE>AT24C16)
{
IIC_Send_Byte(0XA0); //发送写命令
IIC_Wait_Ack();
IIC_Send_Byte(WriteAddr>>8);//发送高地址
}
else
{
IIC_Send_Byte(0XA0+((WriteAddr/256)<<1)); //发送器件地址0XA0,写数据
}
IIC_Wait_Ack();
IIC_Send_Byte(WriteAddr%256); //发送低地址
IIC_Wait_Ack();
IIC_Send_Byte(DataToWrite); //发送字节
IIC_Wait_Ack();
IIC_Stop();//产生一个停止条件
DELAY_US(10*1000);
}
标签:SCL,SETL,void,笔记,DSP,IIC,bit,SDA
From: https://www.cnblogs.com/filosefer/p/18186147