GPIO模拟I2C程序实现.
2013-11-07 09:21
465 查看
GPIO模拟I2C程序实现.
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将视情况减慢或结束数据传送。
7,I2C规程运用主/从双向通讯。器件发送数据到总线上,则定义为发送器,器件接收据
定义为接收器。主器件和从器件都可以工作于接收和发送状态。 总线必须由主器件(通常
为微控制器)控制,主器件产生串行时钟(SCL)控制总线的传输方向,并产生起始和停止
条件。SDA线上的数据状态仅在SCL为低电平的期间才能改变,SCL为高电平的期间,SDA
状态的改变被用来表示起始和停止条件。
在实际应用中,并没有强制规定数据接收方必须对于发送的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++;}
}
/********************************************************************/
第二个例子
函数定义:
gpio_iic.h:
#ifndef __IIC_GPIO__
#define __IIC_GPIO__
void delay();
/*
设置scl引脚电平,0低电平,1高电平,其他值无效,返回值一直为0,留着它用。
*/
int set_scl( int value );
/*
得到scl引脚电平,0低电平,1高电平,必须是这两个值,其他函数需要调用。
*/
int get_scl();
/*
设置sda引脚电平,0低电平,1高电平,其他值无效,返回值一直为0,留着它用。
*/
int set_sda( int value );
/*
得到sda引脚电平,0低电平,1高电平,必须是这两个值,其他函数需要调用。
*/
int get_sda();
/*
重新发送iic start位,这个是在传送数据过程中使用。
*/
void iic_restart();
/*
发送iic start位,这里假设总线空闲,此时SDL与SCL都为高电平。
*/
void iic_start();
/*
发送stop位,这里假设scl此时为低电平。
*/
void iic_stop();
/*
发送一个bit0,这里假设scl此时为低电平,sda电平不定。
*/
void send_bit0();
/*
发送一个bit 1,这里假设scl此时为低电平,sda电平不定。
*/
void send_bit1();
/*
接收一个bit位,返回值只能是0或1。
*/
int receive_bit();
/*
发送ACK,实际上是发送一个bit0.
*/
void send_ack();
/*
接收ACK。
*/
int receive_ack();
/*
接收一个字节。
*/
char receive_byte();
/*
接收一个buf,返回值总是为0,它不能保证从器件一定能收到ACK,也不能保证从器件正在工作。
*/
int receive_buf( char *buf, int buf_size );
/*
发送一个字节,返回ACK的值,发送时,没有收到ACK会重试n次,这是常。
*/
int send_byte( char data_byte );
/*
发送一个buf,返回值是成功发送,收到ACK的字节数量。
*/
int send_buf( char *buf, int buf_size );
#endif
gpio_iic.c:
#include "gpio_iic.h"
//#define __80C52__
#define __MINI2440__
#ifdef __MINI2440__
#include <linux/gpio.h>
#include <linux/delay.h>
#include <mach/regs-gpio.h>
#endif
#ifdef __80C52__
#include <reg52.h>
sbit SCL = P1^0;
sbit SDA = P1^1;
#endif
void delay()
{
#ifdef __MINI2440__
udelay(1);
#endif
#ifdef __80C52__
int i = 0;
for( i = 0; i < 10000; i ++ );
#endif
}
/*
设置scl引脚电平,0低电平,1高电平,其他值无效,返回值一直为0,留着它用。
*/
int set_scl( int value )
{
#ifdef __MINI2440__
// s3c2410_gpio_cfgpin(S3C2410_GPE(15), S3C2410_GPE15_IICSDA);
// s3c2410_gpio_cfgpin(S3C2410_GPE(14), S3C2410_GPE14_IICSCL);
s3c2410_gpio_cfgpin(S3C2410_GPE(14), S3C2410_GPIO_OUTPUT);
switch( value )
{
case 0:
s3c2410_gpio_setpin(S3C2410_GPE(14), 0); // IICSCL
break;
case 1:
s3c2410_gpio_setpin(S3C2410_GPE(14), 1); // IICSCL
break;
default:
break;
}
#endif
#ifdef __80C52__
if ( 0 == value )
SCL = 0;
else if ( 1 == value )
SCL = 1;
#endif
return 0;
}
/*
得到scl引脚电平,0低电平,1高电平,必须是这两个值,其他函数需要调用。
*/
int get_scl()
{
#ifdef __MINI2440__
s3c2410_gpio_cfgpin(S3C2410_GPE(14), S3C2410_GPIO_INPUT);
return s3c2410_gpio_getpin(S3C2410_GPE(14)) > 0 ? 1: 0;
#endif
#ifdef __80C52__
return SCL;
#endif
}
/*
设置sda引脚电平,0低电平,1高电平,其他值无效,返回值一直为0,留着它用。
*/
int set_sda( int value )
{
#ifdef __MINI2440__
s3c2410_gpio_cfgpin(S3C2410_GPE(15), S3C2410_GPIO_OUTPUT);
switch( value )
{
case 0:
s3c2410_gpio_setpin(S3C2410_GPE(15), 0); // IICSDA
break;
case 1:
s3c2410_gpio_setpin(S3C2410_GPE(15), 1); // IICSDA
break;
default:
break;
}
#endif
#ifdef __80C52__
if ( 0 == value )
SDA = 0;
else if ( 1 == value )
SDA = 1;
#endif
return 0;
}
/*
得到sda引脚电平,0低电平,1高电平,必须是这两个值,其他函数需要调用。
*/
int get_sda()
{
#ifdef __MINI2440__
int sda_pin = 0;
s3c2410_gpio_cfgpin(S3C2410_GPE(15), S3C2410_GPIO_INPUT);
sda_pin = s3c2410_gpio_getpin(S3C2410_GPE(15)) > 0 ? 1:0;
return sda_pin;
#endif
#ifdef __80C52__
return SDA;
#endif
}
/*
重新发送iic start位,这个是在传送数据过程中使用。
*/
void iic_restart()
{
set_scl( 0 );
delay();
set_sda( 1 );
delay();
set_scl( 1 );
delay();
set_sda( 0 );
delay();
set_scl( 0 );
delay();
}
/*
发送iic start位,这里假设总线空闲,此时SDL与SCL都为高电平。
*/
void iic_start()
{
set_sda( 0 );
delay();
set_scl( 0 );
delay();
}
/*
发送stop位,这里假设scl此时为低电平。
*/
void iic_stop()
{
set_sda( 0 );
delay();
set_scl( 1 );
delay();
set_sda( 1 );
delay();
}
/*
发送一个bit0,这里假设scl此时为低电平,sda电平不定。
*/
void send_bit0()
{
set_sda( 0 );
delay();
set_scl( 1 );
delay();
set_scl( 0 );
delay();
}
/*
发送一个bit 1,这里假设scl此时为低电平,sda电平不定。
*/
void send_bit1()
{
set_sda( 1 );
delay();
set_scl( 1 );
delay();
set_scl( 0 );
delay();
}
/*
接收一个bit位,返回值只能是0或1。
*/
int receive_bit()
{
int value = -1;
set_sda( 1 );
delay();
set_scl( 0 );
delay();
set_scl( 1 );
delay();
value = get_sda();
set_scl( 0 );
return value;
}
/*
发送ACK,实际上是发送一个bit0.
*/
void send_ack( )
{
send_bit0();
}
/*
接收ACK。
*/
int receive_ack()
{
int ack = 1;
set_sda( 1 );
delay();
set_scl( 1 );
delay();
ack = get_sda();
delay();
set_scl( 0 );
return ack;
}
/*
接收一个字节。
*/
char receive_byte( )
{
int i = 0;
int recv_data = 0;
for ( i = 0; i < 8; i++ )
{
recv_data = recv_data | receive_bit();
if ( 7 == i )
break;
recv_data <<= 1;
}
send_ack();
return recv_data;
}
/*
接收一个buf,返回值总是为0,它不能保证从器件一定能收到ACK,也不能保证从器件正在工作。
*/
int receive_buf( char *buf, int buf_size )
{
int i = 0;
for( i = 0; i < buf_size; i ++ )
{
buf[i] = receive_byte();
}
return buf_size;
}
/*
发送一个字节,返回ACK的值,发送时,没有收到ACK会重试n次,这是常。
*/
int send_byte( char data_byte )
{
int retry_count = 8;//重试次数。
int i = 0;
int ack = 1;
char send_data = 0;
send_data = data_byte;
do{
for ( i = 0; i < 8; i ++ )
{
if ( 0x80 & send_data )
send_bit1();
else
send_bit0();
send_data <<= 1;
}
ack = receive_ack();
if ( 0 == ack )
break;
send_data = data_byte;
retry_count --;
}
while( retry_count >= 0 );
return ack;
}
/*
发送一个buf,返回值是成功发送,收到ACK的字节数量。
*/
int send_buf( char *buf, int buf_size )
{
int i = 0;
int count = 0;
for ( i = 0; i < buf_size; i ++ )
{
if (0 != send_byte( buf[i] ) )
break;
count ++;
}
return count;
}
下面是可以用来读写at24c02的测试代码,在mini2440板测试通过,
static void m24c02_send( int addr, char *buf, int buf_size)
{
int count = 0;
int rev = -1;
iic_start();
rev = send_byte( 0xa0 );
rev = send_byte( (addr >> 0) & 0xff );
count = send_buf( buf, buf_size );
iic_stop();
}
static void m24c02_recv( int addr , char *buf, int buf_size )
{
iic_start();
send_byte( 0xa0 );
send_byte( (addr >> 0) & 0xff );
iic_restart();
send_byte( 0xa1 );
receive_buf( buf, buf_size );
iic_stop();
}
(1)基础宏定义
#define GPIO_SCL S3C2410_GPF3
#define GPIO_SDA S3C2410_GPF0
#define GPIO_SDA_OUTP S3C2410_GPF0_OUTP //设定SDA输出
#define GPIO_SDA_INP S3C2410_GPF0_INP //设定SDA输入
#define GPIO_SCL_OUTP S3C2410_GPF3_OUTP //设定SCL输出
void I2C_SCL_OUTP( void )
{
s3c2410_gpio_cfgpin(GPIO_SCL,GPIO_SCL_OUTP);
}
void I2C_SCL_Output(u8 value)
{
if(value)
{
s3c2410_gpio_setpin(GPIO_SCL,value);
}
else
{
s3c2410_gpio_setpin(GPIO_SCL,value );
}
}
void I2C_SDA_Mode(u8 v_mode) //SDA输出方向
{
if(v_mode)
{
s3c2410_gpio_cfgpin(GPIO_SDA, GPIO_SDA_OUTP);
}
else
{
s3c2410_gpio_cfgpin(GPIO_SDA, GPIO_SDA_INP);
}
}
void I2C_SDA_Output(u8 value)
{
if(value)
{
s3c2410_gpio_setpin(GPIO_SDA,value);
}
else
{
s3c2410_gpio_setpin(GPIO_SDA,value );
}
}
u8 I2C_SDA_Read(void) //SDA读数据
{
return s3c2410_gpio_getpin(GPIO_SDA);
}
(2)基础段
void I2C_Init(void)
{
I2C_SDA_Output(1);
I2C_SCL_Output(1); //默认拉高
}
void I2C_Wait(void)
{
u16 i;
for(i=0;i<200;i++);
}
void I2C_Start(void)
{
I2C_SDA_Output(1);
I2C_SCL_Output(1);
I2C_Wait();
I2C_SDA_Output(0);
I2C_Wait();
I2C_SCL_Output(0);
}
void I2C_Stop(void)
{
I2C_SDA_Output(0);
I2C_Wait();
I2C_SCL_Output(1);
I2C_Wait();
I2C_SDA_Output(1);
}
(3)读写单个字节的段
u8 I2C_Send_Byte(u8 bytedata)
{
u8 i,ack;
I2C_SDA_Mode(1); //SDA输出
I2C_SCL_OUTP();
for (i = 0; i < 8; i++)
{
if (bytedata & 0x80)
{
I2C_SDA_Output(1);
}
else
{
I2C_SDA_Output(0);
}
bytedata <<= 1;
I2C_SCL_Output(1);
udelay(3);
I2C_SCL_Output(0);
udelay(1);
}
I2C_SDA_Output(1); //release
udelay(3);
I2C_SDA_Mode(0); //设定SDA输入
I2C_SCL_Output(1);
udelay(3);
ack = I2C_SDA_Read(); //读应答
I2C_SDA_Mode(1);
I2C_SCL_Output(0);
udelay(3);
return ack;
}
u8 I2C_Receive_Byte(void)
{
u8 i;
u8 bytedata = 0x00;
u8 temp;
I2C_SDA_Mode(0);
for ( i = 0; i < 8; i++)
{
I2C_SCL_Output(1);
udelay(3);
bytedata <<= 1;
temp = I2C_SDA_Read();
printk("reda SDA'value is:%d\n",temp);
if (temp)
bytedata |= 0x01;
printk(" bytedata is:%x\n",bytedata);
I2C_SCL_Output(0);
udelay(1);
}
I2C_SDA_Mode(1);
return bytedata;
}
(4)读写单个字节的I2C应用函数
u8 I2C_Byte_Write(u8 device_ID,u8 address,u8 bytedata)
{
u8 ack;
printk("device_ID is:%x\n",device_ID);
printk("address is:%x\n",address);
printk("date is:%x\n",bytedata);
I2C_Start();
ack=I2C_Send_Byte(device_ID);
printk("ack is:%d\n",ack);
if(ack)
I2C_Stop();
I2C_Send_Byte(address);
I2C_Send_Byte(bytedata);
I2C_Stop();
I2C_Wait();
return 0;
}
u8 I2C_Byte_Read(u8 device_ID,u8 address)
{
u8 bytedata;
I2C_Start();
I2C_Send_Byte(device_ID);
I2C_Send_Byte(address);
I2C_Start();
I2C_Send_Byte(device_ID+1);
bytedata = I2C_Receive_Byte(); //读单个字节,不需要再发应答
I2C_Stop();
return bytedata;
}
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将视情况减慢或结束数据传送。
7,I2C规程运用主/从双向通讯。器件发送数据到总线上,则定义为发送器,器件接收据
定义为接收器。主器件和从器件都可以工作于接收和发送状态。 总线必须由主器件(通常
为微控制器)控制,主器件产生串行时钟(SCL)控制总线的传输方向,并产生起始和停止
条件。SDA线上的数据状态仅在SCL为低电平的期间才能改变,SCL为高电平的期间,SDA
状态的改变被用来表示起始和停止条件。
在实际应用中,并没有强制规定数据接收方必须对于发送的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++;}
}
/********************************************************************/
第二个例子
函数定义:
gpio_iic.h:
#ifndef __IIC_GPIO__
#define __IIC_GPIO__
void delay();
/*
设置scl引脚电平,0低电平,1高电平,其他值无效,返回值一直为0,留着它用。
*/
int set_scl( int value );
/*
得到scl引脚电平,0低电平,1高电平,必须是这两个值,其他函数需要调用。
*/
int get_scl();
/*
设置sda引脚电平,0低电平,1高电平,其他值无效,返回值一直为0,留着它用。
*/
int set_sda( int value );
/*
得到sda引脚电平,0低电平,1高电平,必须是这两个值,其他函数需要调用。
*/
int get_sda();
/*
重新发送iic start位,这个是在传送数据过程中使用。
*/
void iic_restart();
/*
发送iic start位,这里假设总线空闲,此时SDL与SCL都为高电平。
*/
void iic_start();
/*
发送stop位,这里假设scl此时为低电平。
*/
void iic_stop();
/*
发送一个bit0,这里假设scl此时为低电平,sda电平不定。
*/
void send_bit0();
/*
发送一个bit 1,这里假设scl此时为低电平,sda电平不定。
*/
void send_bit1();
/*
接收一个bit位,返回值只能是0或1。
*/
int receive_bit();
/*
发送ACK,实际上是发送一个bit0.
*/
void send_ack();
/*
接收ACK。
*/
int receive_ack();
/*
接收一个字节。
*/
char receive_byte();
/*
接收一个buf,返回值总是为0,它不能保证从器件一定能收到ACK,也不能保证从器件正在工作。
*/
int receive_buf( char *buf, int buf_size );
/*
发送一个字节,返回ACK的值,发送时,没有收到ACK会重试n次,这是常。
*/
int send_byte( char data_byte );
/*
发送一个buf,返回值是成功发送,收到ACK的字节数量。
*/
int send_buf( char *buf, int buf_size );
#endif
gpio_iic.c:
#include "gpio_iic.h"
//#define __80C52__
#define __MINI2440__
#ifdef __MINI2440__
#include <linux/gpio.h>
#include <linux/delay.h>
#include <mach/regs-gpio.h>
#endif
#ifdef __80C52__
#include <reg52.h>
sbit SCL = P1^0;
sbit SDA = P1^1;
#endif
void delay()
{
#ifdef __MINI2440__
udelay(1);
#endif
#ifdef __80C52__
int i = 0;
for( i = 0; i < 10000; i ++ );
#endif
}
/*
设置scl引脚电平,0低电平,1高电平,其他值无效,返回值一直为0,留着它用。
*/
int set_scl( int value )
{
#ifdef __MINI2440__
// s3c2410_gpio_cfgpin(S3C2410_GPE(15), S3C2410_GPE15_IICSDA);
// s3c2410_gpio_cfgpin(S3C2410_GPE(14), S3C2410_GPE14_IICSCL);
s3c2410_gpio_cfgpin(S3C2410_GPE(14), S3C2410_GPIO_OUTPUT);
switch( value )
{
case 0:
s3c2410_gpio_setpin(S3C2410_GPE(14), 0); // IICSCL
break;
case 1:
s3c2410_gpio_setpin(S3C2410_GPE(14), 1); // IICSCL
break;
default:
break;
}
#endif
#ifdef __80C52__
if ( 0 == value )
SCL = 0;
else if ( 1 == value )
SCL = 1;
#endif
return 0;
}
/*
得到scl引脚电平,0低电平,1高电平,必须是这两个值,其他函数需要调用。
*/
int get_scl()
{
#ifdef __MINI2440__
s3c2410_gpio_cfgpin(S3C2410_GPE(14), S3C2410_GPIO_INPUT);
return s3c2410_gpio_getpin(S3C2410_GPE(14)) > 0 ? 1: 0;
#endif
#ifdef __80C52__
return SCL;
#endif
}
/*
设置sda引脚电平,0低电平,1高电平,其他值无效,返回值一直为0,留着它用。
*/
int set_sda( int value )
{
#ifdef __MINI2440__
s3c2410_gpio_cfgpin(S3C2410_GPE(15), S3C2410_GPIO_OUTPUT);
switch( value )
{
case 0:
s3c2410_gpio_setpin(S3C2410_GPE(15), 0); // IICSDA
break;
case 1:
s3c2410_gpio_setpin(S3C2410_GPE(15), 1); // IICSDA
break;
default:
break;
}
#endif
#ifdef __80C52__
if ( 0 == value )
SDA = 0;
else if ( 1 == value )
SDA = 1;
#endif
return 0;
}
/*
得到sda引脚电平,0低电平,1高电平,必须是这两个值,其他函数需要调用。
*/
int get_sda()
{
#ifdef __MINI2440__
int sda_pin = 0;
s3c2410_gpio_cfgpin(S3C2410_GPE(15), S3C2410_GPIO_INPUT);
sda_pin = s3c2410_gpio_getpin(S3C2410_GPE(15)) > 0 ? 1:0;
return sda_pin;
#endif
#ifdef __80C52__
return SDA;
#endif
}
/*
重新发送iic start位,这个是在传送数据过程中使用。
*/
void iic_restart()
{
set_scl( 0 );
delay();
set_sda( 1 );
delay();
set_scl( 1 );
delay();
set_sda( 0 );
delay();
set_scl( 0 );
delay();
}
/*
发送iic start位,这里假设总线空闲,此时SDL与SCL都为高电平。
*/
void iic_start()
{
set_sda( 0 );
delay();
set_scl( 0 );
delay();
}
/*
发送stop位,这里假设scl此时为低电平。
*/
void iic_stop()
{
set_sda( 0 );
delay();
set_scl( 1 );
delay();
set_sda( 1 );
delay();
}
/*
发送一个bit0,这里假设scl此时为低电平,sda电平不定。
*/
void send_bit0()
{
set_sda( 0 );
delay();
set_scl( 1 );
delay();
set_scl( 0 );
delay();
}
/*
发送一个bit 1,这里假设scl此时为低电平,sda电平不定。
*/
void send_bit1()
{
set_sda( 1 );
delay();
set_scl( 1 );
delay();
set_scl( 0 );
delay();
}
/*
接收一个bit位,返回值只能是0或1。
*/
int receive_bit()
{
int value = -1;
set_sda( 1 );
delay();
set_scl( 0 );
delay();
set_scl( 1 );
delay();
value = get_sda();
set_scl( 0 );
return value;
}
/*
发送ACK,实际上是发送一个bit0.
*/
void send_ack( )
{
send_bit0();
}
/*
接收ACK。
*/
int receive_ack()
{
int ack = 1;
set_sda( 1 );
delay();
set_scl( 1 );
delay();
ack = get_sda();
delay();
set_scl( 0 );
return ack;
}
/*
接收一个字节。
*/
char receive_byte( )
{
int i = 0;
int recv_data = 0;
for ( i = 0; i < 8; i++ )
{
recv_data = recv_data | receive_bit();
if ( 7 == i )
break;
recv_data <<= 1;
}
send_ack();
return recv_data;
}
/*
接收一个buf,返回值总是为0,它不能保证从器件一定能收到ACK,也不能保证从器件正在工作。
*/
int receive_buf( char *buf, int buf_size )
{
int i = 0;
for( i = 0; i < buf_size; i ++ )
{
buf[i] = receive_byte();
}
return buf_size;
}
/*
发送一个字节,返回ACK的值,发送时,没有收到ACK会重试n次,这是常。
*/
int send_byte( char data_byte )
{
int retry_count = 8;//重试次数。
int i = 0;
int ack = 1;
char send_data = 0;
send_data = data_byte;
do{
for ( i = 0; i < 8; i ++ )
{
if ( 0x80 & send_data )
send_bit1();
else
send_bit0();
send_data <<= 1;
}
ack = receive_ack();
if ( 0 == ack )
break;
send_data = data_byte;
retry_count --;
}
while( retry_count >= 0 );
return ack;
}
/*
发送一个buf,返回值是成功发送,收到ACK的字节数量。
*/
int send_buf( char *buf, int buf_size )
{
int i = 0;
int count = 0;
for ( i = 0; i < buf_size; i ++ )
{
if (0 != send_byte( buf[i] ) )
break;
count ++;
}
return count;
}
下面是可以用来读写at24c02的测试代码,在mini2440板测试通过,
static void m24c02_send( int addr, char *buf, int buf_size)
{
int count = 0;
int rev = -1;
iic_start();
rev = send_byte( 0xa0 );
rev = send_byte( (addr >> 0) & 0xff );
count = send_buf( buf, buf_size );
iic_stop();
}
static void m24c02_recv( int addr , char *buf, int buf_size )
{
iic_start();
send_byte( 0xa0 );
send_byte( (addr >> 0) & 0xff );
iic_restart();
send_byte( 0xa1 );
receive_buf( buf, buf_size );
iic_stop();
}
(1)基础宏定义
#define GPIO_SCL S3C2410_GPF3
#define GPIO_SDA S3C2410_GPF0
#define GPIO_SDA_OUTP S3C2410_GPF0_OUTP //设定SDA输出
#define GPIO_SDA_INP S3C2410_GPF0_INP //设定SDA输入
#define GPIO_SCL_OUTP S3C2410_GPF3_OUTP //设定SCL输出
void I2C_SCL_OUTP( void )
{
s3c2410_gpio_cfgpin(GPIO_SCL,GPIO_SCL_OUTP);
}
void I2C_SCL_Output(u8 value)
{
if(value)
{
s3c2410_gpio_setpin(GPIO_SCL,value);
}
else
{
s3c2410_gpio_setpin(GPIO_SCL,value );
}
}
void I2C_SDA_Mode(u8 v_mode) //SDA输出方向
{
if(v_mode)
{
s3c2410_gpio_cfgpin(GPIO_SDA, GPIO_SDA_OUTP);
}
else
{
s3c2410_gpio_cfgpin(GPIO_SDA, GPIO_SDA_INP);
}
}
void I2C_SDA_Output(u8 value)
{
if(value)
{
s3c2410_gpio_setpin(GPIO_SDA,value);
}
else
{
s3c2410_gpio_setpin(GPIO_SDA,value );
}
}
u8 I2C_SDA_Read(void) //SDA读数据
{
return s3c2410_gpio_getpin(GPIO_SDA);
}
(2)基础段
void I2C_Init(void)
{
I2C_SDA_Output(1);
I2C_SCL_Output(1); //默认拉高
}
void I2C_Wait(void)
{
u16 i;
for(i=0;i<200;i++);
}
void I2C_Start(void)
{
I2C_SDA_Output(1);
I2C_SCL_Output(1);
I2C_Wait();
I2C_SDA_Output(0);
I2C_Wait();
I2C_SCL_Output(0);
}
void I2C_Stop(void)
{
I2C_SDA_Output(0);
I2C_Wait();
I2C_SCL_Output(1);
I2C_Wait();
I2C_SDA_Output(1);
}
(3)读写单个字节的段
u8 I2C_Send_Byte(u8 bytedata)
{
u8 i,ack;
I2C_SDA_Mode(1); //SDA输出
I2C_SCL_OUTP();
for (i = 0; i < 8; i++)
{
if (bytedata & 0x80)
{
I2C_SDA_Output(1);
}
else
{
I2C_SDA_Output(0);
}
bytedata <<= 1;
I2C_SCL_Output(1);
udelay(3);
I2C_SCL_Output(0);
udelay(1);
}
I2C_SDA_Output(1); //release
udelay(3);
I2C_SDA_Mode(0); //设定SDA输入
I2C_SCL_Output(1);
udelay(3);
ack = I2C_SDA_Read(); //读应答
I2C_SDA_Mode(1);
I2C_SCL_Output(0);
udelay(3);
return ack;
}
u8 I2C_Receive_Byte(void)
{
u8 i;
u8 bytedata = 0x00;
u8 temp;
I2C_SDA_Mode(0);
for ( i = 0; i < 8; i++)
{
I2C_SCL_Output(1);
udelay(3);
bytedata <<= 1;
temp = I2C_SDA_Read();
printk("reda SDA'value is:%d\n",temp);
if (temp)
bytedata |= 0x01;
printk(" bytedata is:%x\n",bytedata);
I2C_SCL_Output(0);
udelay(1);
}
I2C_SDA_Mode(1);
return bytedata;
}
(4)读写单个字节的I2C应用函数
u8 I2C_Byte_Write(u8 device_ID,u8 address,u8 bytedata)
{
u8 ack;
printk("device_ID is:%x\n",device_ID);
printk("address is:%x\n",address);
printk("date is:%x\n",bytedata);
I2C_Start();
ack=I2C_Send_Byte(device_ID);
printk("ack is:%d\n",ack);
if(ack)
I2C_Stop();
I2C_Send_Byte(address);
I2C_Send_Byte(bytedata);
I2C_Stop();
I2C_Wait();
return 0;
}
u8 I2C_Byte_Read(u8 device_ID,u8 address)
{
u8 bytedata;
I2C_Start();
I2C_Send_Byte(device_ID);
I2C_Send_Byte(address);
I2C_Start();
I2C_Send_Byte(device_ID+1);
bytedata = I2C_Receive_Byte(); //读单个字节,不需要再发应答
I2C_Stop();
return bytedata;
}
相关文章推荐
- div css 实现tabs标签
- 开箱即用!Android四款系统架构工具
- 清华同方台试机Bioss默认密码
- 汇编语言程序设计读书笔记(3)- 程序范例
- 安全软件应该具备的,你具备了吗?
- TCP连接状态详解
- Makefile详解
- 调整VirtualBox的VDI硬盘文件大小
- 多线程与并发服务器设计(23 - 2 )
- muduo_net库源码分析(25)
- LINK : warning LNK4068: /MACHINE not specified; defaulting to IX86
- 关于Oracle EBS R12 表格XLA_TRANSACTION_ENTITIES 的安全策略(VPD)组研究
- ORACLE 等待事件的分类
- 多线程与并发服务器设计(23-1)
- ora_00845
- PHP识别电脑还是手机访问网站
- eclipse3.6默认指向WebContent目录修改为webRoot 设置说明 .
- tomcat6版本虚拟目录详细配置
- LogFile (22)
- traits 技术(21)