"); //-->
I2C是由Philips公司发明的一种串行数据通信协议,仅使用两根信号线:SerialClock(简称SCL)和SerialData(简称SDA)。I2C是总线结构,1个Master,1个或多个Slave,各Slave设备以7位地址区分,地址后面再跟1位读写位,表示读(=1)或者写(=0),所以我们有时也可看到8位形式的设备地址,此时每个设备有读、写两个地址,高7位地址其实是相同的。
I2C数据格式如下:
无数据:SCL=1,SDA=1;
开始位(Start):当SCL=1时,SDA由1向0跳变;
停止位(Stop):当SCL=1时,SDA由0向1跳变;
数据位:当SCL由0向1跳变时,由发送方控制SDA,此时SDA为有效数据,不可随意改变SDA;
当SCL保持为0时,SDA上的数据可随意改变;
地址位:定义同数据位,但只由Master发给Slave;
应答位(ACK):当发送方传送完8位时,发送方释放SDA,由接收方控制SDA,且SDA=0;
否应答位(NACK):当发送方传送完8位时,发送方释放SDA,由接收方控制SDA,且SDA=1。
当数据为单字节传送时,格式为:
开始位,8位地址位(含1位读写位),应答,8位数据,应答,停止位。
当数据为一串字节传送时,格式为:
开始位,8位地址位(含1位读写位),应答,8位数据,应答,8位数据,应答,……,8位数据,应答,停止位。
需要注意的是:
1,SCL一直由Master控制,SDA依照数据传送的方向,读数据时由Slave控制SDA,写数据时由Master控制SDA。当8位数据传送完毕之后,应答位或者否应答位的SDA控制权与数据位传送时相反。
2,开始位“Start”和停止位“Stop”,只能由Master来发出。
3,地址的8位传送完毕后,成功配置地址的Slave设备必须发送“ACK”。否则否则一定时间之后Master视为超时,将放弃数据传送,发送“Stop”。
4,当写数据的时候,Master每发送完8个数据位,Slave设备如果还有空间接受下一个字节应该回答“ACK”,Slave设备如果没有空间接受更多的字节应该回答“NACK”,Master当收到“NACK”或者一定时间之后没收到任何数据将视为超时,此时Master放弃数据传送,发送“Stop”。
5,当读数据的时候,Slave设备每发送完8个数据位,如果Master希望继续读下一个字节,Master应该回答“ACK”以提示Slave准备下一个数据,如果Master不希望读取更多字节,Master应该回答“NACK”以提示Slave设备准备接收Stop信号。
6,当Master速度过快Slave端来不及处理时,Slave设备可以拉低SCL不放(SCL=0将发生“线与”)以阻止Master发送更多的数据。此时Master将视情况减慢或结束数据传送。
在实际应用中,并没有强制规定数据接收方必须对于发送的8位数据做出回应,尤其是在Master和Slave端都是用GPIO软件模拟的方法来实现的情况下,编程者可以事先约定数据传送的长度,不发送ACK,有时可以起到减少系统开销的效果。
源码:
/********************************************************************/
void i2c_init(void)
{
PACNT_init;
PADDR_init;
PADAT_init;
SCL_high;
SDA_high;
}
/********************************************************************/
uint8 i2c_write(uint8 slave_address, uint8 *buffer, int byte_count, int freq)
{
uint8 out_mask = 0x80;
uint8 value = 0x00;
uint8 send_byte = 0x00;
uint8 status = 0x81;
int count = 8;
int clk_count = 0;
int i = 0;
/* Set delay value based on frequency. */
int D = (int) ((4000/freq) - 14);
slave_address = (slave_address & 0xFE);
i2c_start();
delay(500);
send_byte = slave_address;
for(i = 0; i <= byte_count; i++)
{
count = 8;
out_mask = 0x80;
/* Send data bytes one bit at a time. */
while(count > 0)
{
value = ((send_byte & out_mask) ? 1 : 0);
if (value == 1)
{
PADAT_init;
SDA_high;}
else
{
PADAT_init;
SDA_low;}
delay(D);
PADAT_init;
SCL_high;
/* Clock stretching wait statement. Wait until clock is released
by slave. Only effects program on first iteration. */
while (((GPIO_PADAT & 0x0200) ? 1 : 0) == 0){;}
delay(2*D);
PADAT_init;
SCL_low;
delay(D);
out_mask >>= 1;
count--;
}
PADAT_init;
SDA_high; /* Let go of data pin. */
delay(D);
if (((GPIO_PADAT & 0x0400) ? 1 : 0) == 1)
{
status = 0xA1; /* Transfer complete, bus busy, acknowledge not received. */
break; } /* If not acknowledged, exit loop. */
PADAT_init;
SCL_high;
delay(2*D);
PADAT_init;
SCL_low;
status = 0xA0; /* Transfer complete, bus busy, acknowledge received. */
delay(D);
send_byte = buffer[i];
}
PADAT_init;
SDA_high;
SCL_low;
delay(100);
return(status);
}
/********************************************************************/
uint8 i2c_read(uint8 slave_address, uint8 *buffer, int byte_count, int freq)
{
uint8 input_byte = 0x00;
uint8 value = 0x00;
uint8 out_mask = 0x80;
uint8 status = 0x81;
int count = 8;
int clk_count = 0;
int i = 0;
/* Set delay value based on frequency. */
int D = (int) ((4000/freq) - 14);
slave_address = (slave_address | 0x01);
i2c_start();
delay(500);
/********** Write Address Procedure **********/
while(count > 0)
{
value = ((slave_address & out_mask) ? 1 : 0);
if (value == 1)
{
PADAT_init;
SDA_high;}
else
{
PADAT_init;
SDA_low;}
delay(D);
PADAT_init;
SCL_high;
/* Clock stretching wait. Wait until clock is released
by slave. */
while (((GPIO_PADAT & 0x0200) ? 1 : 0) == 0){;}
delay(2*D);
PADAT_init;
SCL_low;
delay(D);
out_mask >>= 1;
count--;
}
PADAT_init;
SDA_high; /* Let go of data pin. */
delay(D);
SCL_high;
delay(2*D);
/* If not acknowleged, set status accordingly and exit read process. */
if (((GPIO_PADAT & 0x0400) ? 1 : 0) == 1)
{
status = 0xA1;
return(status);}
PADAT_init;
SCL_low;
delay(D);
/********** Begin Read Procedure **********/
/* Release SDA and SCL to initiate transfer. */
PADAT_init;
SDA_high;
SCL_high;
for(i = 0; i < byte_count; i++)
{
count = 8;
input_byte = 0x00;
PADAT_init;
SCL_high;
/* Clock stretching wait. Wait until clock is released
by slave. */
while (((GPIO_PADAT & 0x0200) ? 1 : 0) == 0){;}
/* Loop for bit-by-bit read of data. */
while(count > 0)
{
PADAT_init;
SCL_high;
delay(D);
delay(4); /* Required to make read and write clocks the same freq. */
if ((GPIO_PADAT & 0x0600) == 0x0600)
input_byte++;
delay(D);
PADAT_init;
SCL_low;
delay(2*D);
if (count == 1)
break;
else
input_byte <<= 1;
count--;
}
/* Write input byte to "read_buffer". */
buffer[i] = input_byte;
if(i == (byte_count - 1))
break;
/* Below is the acknowledge procedure. */
PADAT_init;
SDA_low;
delay(D);
SCL_high;
delay(2*D);
PADAT_init;
SCL_low;
delay(D);
SDA_high;
status = 0xA0;
}
/* Standard protocol calls for the last read byte to
not receive an acknowledge from the master. */
PADAT_init;
SDA_high;
SCL_high;
delay(2*D);
PADAT_init;
SCL_low;
delay(D);
SDA_high;
status = 0xA1;
return(status);
}
/********************************************************************/
void i2c_start(void)
{
int clk_count = 0;
uint8 compare = 0x00;
PADAT_init;
SDA_high;
delay(100);
PADAT_init;
SCL_high;
delay(100);
/* Clock stretching wait. Wait until clock is released
by slave. */
while (((GPIO_PADAT & 0x0200) ? 1 : 0) == 0){;}
PADAT_init;
SDA_low;
delay(100);
PADAT_init;
SCL_low;
delay(100);
}
/********************************************************************/
uint8 i2c_stop(void)
{
uint8 status = 0x00;
int clk_count = 0;
PADAT_init;
SCL_low;
delay(100);
PADAT_init;
SDA_low;
delay(100);
PADAT_init;
SCL_high;
/* Clock stretching wait statement. Wait until clock is released
by slave. */
while (((GPIO_PADAT & 0x0200) ? 1 : 0) == 0){;}
delay(100);
PADAT_init;
SDA_high;
status = 0x81; /* Set bus idle. */
return(status);
}
/********************************************************************/
void delay(int value)
{
int clk_count = 0;
while (clk_count < value)
{clk_count++;}
}
/********************************************************************/
专栏文章内容及配图由作者撰写发布,仅供工程师学习之用,如有侵权或者其他违规问题,请联系本站处理。 联系我们
相关推荐
AURIX™ TC4x虚拟化技术助力下一代汽车EE架构设计
汽车音响开关电源电路
世界第一个修补大脑的芯片
在与伪科学的辩论中科学处境不利
三菱FX系列PLC教程 74 —— FX系列的十六键输入指令
三菱FX系列PLC教程 76 —— FX系列的外部IO制备指令
基于ST的VIPERGAN65和SRK1004的65W QR USB PD适配器参考设计
研华智慧产科解决方案助力厦门长庚医院打造AI智慧产房
世纪联星U073-6B电脑刻绘机开关电源电路
IBM造出世界上最小的晶体管
基于DSP的CompactFlash卡接口设计
2002年全球十大主机板厂商排名
三菱FX系列PLC教程 77 —— FX系列的PID运算指令
基于dsp的h.324终端实现
三菱FX系列PLC教程 73 —— FX系列的七段译码指令
国外LMDS发展现状分析
基于ST SPC58 的智能驾驶ADAS解决方案
基于DSP的PC加密卡
基于PI InnoMux-2 IMX2379F设计的62W三输出定电压反激电源解决方案
现代HSS-100型数字卫星电视接收机电源电路
探索 AUTOSAR 与 英飞凌 AURIX™ TC4x MCAL 解决方案-下
基于DSP的DMA控制技术
研华模块化电脑SOM-7583:打造人形机器人超强“小脑”控制器
基于DSP的H.324可视电话研究与实现
基于TouchGFX的STM32 GUI显示方案
交流净化稳压电源电路
解决模拟输入IEC系统保护问题
三菱FX系列PLC教程 75 —— FX系列的数字开关指令
高精度线性直流稳压电源1502D电路
缘起今生:英飞凌车规MCU全系支持Rust 语言开发