还没有调试,但是认为比较靠谱的I2C读写例子

来源:互联网 发布:网络规划设计任务书 编辑:程序博客网 时间:2024/05/17 13:10

 

1,在ICCAVR上编译通过。

2,今天用M8进行试验,试验成功了TIMER0的定时中断,结果自己乱点锁死了ATMEGA8,只能等待新的芯片回来。

3,为什么说这个代码靠谱,这里没有推换的1输出,符合I2C的物理规范。这样才能真正实现多I2C主器件,虽然这里只有一个主器件。

4,这里面讲ACK得检测放在每次的字节写里面非常合适。

5,Try_Write_I2C是我添加的函数,用来实现自动等待器件就绪,尝试次数n应该设置得大一些。

6,我看这里延迟应该不大,所以我觉得这里可以做一下延迟时间的调试,一般来说SCL时钟不要高于100KHZ。这里需要调试一下。

7,这里DELAY调用了一个函数,可以很方便的修改延迟时间。

8,这里用到也页读写,其实就是连续读写,这不错。

9,这个代码很好,可以修改,移植到几乎所有双向IO的单片机器系统里。


 

 

//文件名:I2C.h
//描述:AVR模拟I2C读写24C02的相关函数
#include<iom16v.h>
#include<macros.h>
#include"I2C.h"
//外部上拉电阻,PORTC.0--SCL,PORTC.1--SDA,模拟I2C协议
//当DDRC.0和DDRC.1置为输出时,拉低SDA;置为输入时,外部上拉拉高SDA
#define SCL_0 DDRC|=BIT(0)
#define SCL_1 DDRC&=~BIT(0)
#define SDA_0 DDRC|=BIT(1)
#define SDA_1 DDRC&=~BIT(1)
#define SDA_in (PINC&0X02)  //判断SDA的电平
#define Page_size 8   //一页大小,8字节

//函数名;I2C_inti
//输入:无
//输出:无
//描述:初始化SDA和SCL
void I2C_inti(void)
{
    PORTC&=~(BIT(0)|BIT(1));
    SCL_1;
    SDA_1;
}

//函数名;Delay
//输入:无
//输出:无
//描述:延时1us

void Delay(void) 
{
    NOP();
    NOP();
    NOP();
    NOP();
}

//函数名;delay_ms
//输入:无
//输出:无
//描述:延时1ms

void delay_ms(uint ms)
{   
    uint i,j;
    for(i=0;i<ms;i++)
        for(j=0;j<564;j++);
}

//函数名;I2C_Start
//输入:无
//输出:无
//描述:I2C起始条件

uchar I2C_Start(void)
{
    SDA_1;
    SCL_1;
    Delay();
    SDA_0;
    Delay();
    SCL_0;
    Delay();
    return 1;
}

//函数名;I2C_Stop
//输入:无
//输出:无
//描述:I2C结束条件

void I2C_Stop(void)
{
    SDA_0;
    SCL_1;
    Delay();
    SDA_1;
    Delay();
}

//函数名;Write_I2C
//输入:待写的一字节数据data
//输出:有无应答,有应答--1;无应答--0
//描述:发送一字节,返回有无应答

uchar Write_I2C(uchar data)
{
    uchar ack,i;
    for(i=0;i<8;i++)
    {
        if(data&0X80)
        {
            SDA_1;
        }
        else
        {
            SDA_0;
        }
        SCL_1;
        Delay();
        SCL_0;
        data<<=1;
        Delay();
    }
    Delay();
    SDA_1;
    Delay();
    SCL_1;
    Delay();
    if(SDA_in)
    {
        ack=0;
    }
    else
    {
        ack=1;
    }
    SCL_0;
    Delay();
    return ack;
}

//函数名;Try_Write_I2C
//输入:待写的一字节数据data,如果无应答,尝试发送n次。
//输出:最终有无应答,有应答--1;无应答--0
//描述:尝试n发送一个字节,最终发送成功就返回1,n次全失败就返回0
//备注:主要用在检测器件是否就绪并等待。李伟添加 2014-4-28

uchar Try_Write_I2C(uchar data,int times)
{
uchar r ;
while(times--){
r = Write_I2C(data);
if (r==1)return 1;
}
return 0;
}

//函数名;Read_I2C
//输入:是否应答ack,0--不应答,1--应答
//输出:接受的一字节数据
//描述:接收一字节数据

uchar Read_I2C(uchar ack)
{
    uchar i,temp;
    temp=0;
    SDA_1;
    for(i=0;i<8;i++)
    {
        Delay();
        SCL_0;
        Delay();
        SCL_1;
        Delay();
        temp<<=1;
        if(SDA_in)
        {
            temp++;
        }
    }
    SCL_0;
    Delay();
    if(!ack)
    {
        SDA_1;
    }
    else
    {
        SDA_0;
    }
    Delay();
    SCL_1;
    Delay();
    SCL_0;
    Delay();
    return temp;
}

//函数名;Write_24c02
//输入:存储地址add,待写的一字节数据data
//输出:无
//描述:向24c02指定地址写一字节的数据

void Write_24c02(uchar add,uchar data)
{
    I2C_Start();
    Write_I2C(0XAE);
    Write_I2C(add);
    Write_I2C(data);
    I2C_Stop();
    delay_ms(10);
}

//函数名;Read_24c02
//输入:存储地址add
//输出:读出的一字节数据
//描述:向24c02指定地址读一字节的数据

uchar Read_24c02(uchar add)
{
    uchar data;
    I2C_Start();
    Write_I2C(0XAE);
    Write_I2C(add);
    I2C_Start();
    Write_I2C(0XAF);
    data=Read_I2C(0);
    I2C_Stop();
    return data;
}

//函数名;Write_24c02_page
//输入:存储地址add,待写缓冲区的首地址arr
//输出:无
//描述:主机写24c02指定地址开始n个字节的数据(n<8),在一页内

void Write_24c02_page(uchar add,uchar n,uchar* arr)
{
    uchar i;
    I2C_Start();
    Write_I2C(0XAE);
    Write_I2C(add);
    for(i=0;i<n;i++)
    {
        Write_I2C(*arr++);
    }
    I2C_Stop();
    delay_ms(10);
}
  
//函数名;Write_m_24c02
//输入:存储地址add,待写缓冲区的首地址arr,待写字节数n
//输出:无
//描述:主机写24c02指定地址开始n个字节的数据
void Write_m_24c02(uchar add,uchar n,uchar* arr)
{
    uchar n_left;
    n_left=(uchar)Page_size-(uchar)(add&0X07);    //本页剩余空间
    if(n<=n_left)
    {
        Write_24c02_page(add,n,arr);
    }
    else
    {
        Write_24c02_page(add,n_left,arr);      //写完本页
        add+=n_left;
        n-=n_left;
        arr+=n_left;
        while(n>=Page_size)             // 连续写一整页
        {
            delay_ms(150);
            Write_24c02_page(add,Page_size,arr);
            add+=Page_size;
            n-=Page_size;
            arr+=Page_size;
        }
        if(n!=0)
        {
            Write_24c02_page(add,n,arr);
        }
    }
}

//函数名;Read_m_24c02
//输入:存储地址add,存储缓冲区的首地址arr,待读字节数n
//输出:无
//描述:主机读24c02指定地址开始n个字节的数据
void Read_m_24c02(uchar add,uchar n,uchar* arr)
{   
    uchar i;
    I2C_Start();
    Write_I2C(0XAE);
    Write_I2C(add);
    I2C_Start();
    Write_I2C(0XAF);
    for(i=0;i<n-1;i++)
    {
        *arr++=Read_I2C(1);
    }
    *arr=Read_I2C(0);
    I2C_Stop();
}  




--------------------------------------------------------------------------以下函数是修改过的并且试验通过的了------------------------------------------------------------------------------------------

//文件名:I2C.h
//描述:AVR模拟I2C读写24C32的相关函数
#include<iom8v.h>
#include<macros.h>
//外部上拉电阻,PORTD.2--SCL,PORTD.3--SDA,模拟I2C协议
//当DDRD.2和DDRD.3置为输出时,拉低SDA;置为输入时,外部上拉拉高SDA
#define SCL_0 DDRD|=BIT(2)
#define SCL_1 DDRD&=~BIT(2)
#define SDA_0 DDRD|=BIT(3)
#define SDA_1 DDRD&=~BIT(3)
#define SDA_in (PIND&0X08)  
#define Page_size 8    


#define uchar unsigned char 
#define uint unsigned int 


//函数名;I2C_inti
//输入:无
//输出:无
//描述:初始化SDA和SCL
void I2C_init(void)
{
    PORTD&=~(BIT(2)|BIT(3));
    SCL_1;
    SDA_1;
}


//函数名;Delay
//输入:无
//输出:无
//描述:延时1us


void Delay(void)  
{
   NOP();
   NOP();
   NOP();
   NOP();
}






//函数名;I2C_Start
//输入:无
//输出:无
//描述:I2C起始条件


static uchar I2C_Start(void)
{
    SDA_1;
    SCL_1;
    Delay();
    SDA_0;
    Delay();
    SCL_0;
    Delay();
    return 1;
}


//函数名;I2C_Stop
//输入:无
//输出:无
//描述:I2C结束条件


static void I2C_Stop(void)
{
    SDA_0;
    SCL_1;
    Delay();
    SDA_1;
    Delay();
}


//函数名;Write_I2C
//输入:待写的一字节数据data
//输出:有无应答,有应答--1;无应答--0
//描述:发送一字节,返回有无应答


static uchar Write_I2C(uchar data)
{
    uchar ack,i;
    for(i=0;i<8;i++)
    {
        if(data&0X80)
        {
            SDA_1;
        }
        else
        {
            SDA_0;
        }
        SCL_1;
        Delay();
        SCL_0;
        data<<=1;
        Delay();
    }
    Delay();
    SDA_1;
    Delay();
    SCL_1;
    Delay();
    if(SDA_in)
    {
        ack=0;
    }
    else
    {
        ack=1;
    }
    SCL_0;
    Delay();
    return ack;
}




//函数名;Read_I2C
//输入:是否应答ack,0--不应答,1--应答
//输出:接受的一字节数据
//描述:接收一字节数据


static uchar Read_I2C(uchar ack)
{
    uchar i,temp;
    temp=0;
    SDA_1;
    for(i=0;i<8;i++)
    {
        Delay();
        SCL_0;
        Delay();
        SCL_1;
        Delay();
        temp<<=1;
        if(SDA_in)
        {
            temp|=1;
        }
    }
    SCL_0;
    Delay();
    if(!ack)
    {
        SDA_1;
    }
    else
    {
        SDA_0;
    }
    Delay();
    SCL_1;
    Delay();
    SCL_0;
    Delay();
    return temp;
}


//函数名;Write_24c32
//输入:存储地址add,待写的一字节数据data
//输出:无
//描述:向24c32指定地址写一字节的数据
 
uchar  Write_24c32(uchar dev_addr ,uint  addr,uchar data)
{
uchar i ;
uchar r, addr_low ,addr_hig ;
addr_low = addr &0xff ;
addr = addr<<8 ;
addr_hig = addr&0xff;
dev_addr =  dev_addr<<1;
i=10;
do {
    I2C_Start();
    r=Write_I2C(dev_addr);
i--;
if(i==0)return 0;
}while(r==0) ;
Write_I2C(addr_hig);

Write_I2C(addr_low);
    Write_I2C(data);
    I2C_Stop();
return 1 ; 
}
   
//函数名;Read_24c02
//输入:存储地址add
//输出:读出的一字节数据
//描述:向24c02指定地址读一字节的数据


uchar Read_24c32(uchar dev_addr , uint  byte_addr,uchar *dat)
{
uchar i ;
uchar r, addr_low ,addr_hig ;
addr_low = byte_addr &0xff ;
byte_addr = byte_addr<<8 ;
addr_hig = byte_addr&0xff;
dev_addr =  dev_addr<<1 ;
i=100;
do {
    I2C_Start();
    r=Write_I2C(dev_addr);
i--;
if(i==0)return 0;
}while(r==0) ;

Write_I2C(addr_hig);

Write_I2C(addr_low);
    I2C_Start();
dev_addr++; 
    Write_I2C(dev_addr);
    *dat=Read_I2C(0);
    I2C_Stop();
    return 1;
}


   
main(){
char hex[] ={0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}; 
unsigned int c ,i;
I2C_init();
while(1)
{
    Write_24c32(0x50 ,c,(1+c&0xff)); ///  写c+1 到C这个位置 
i=Read_24c32(0x50,c,hex);     c=hex[0]; //读出C这个位置的数据并且保存在C这个变量里面
}

}


1,红色部分的主代码实现了单字节的写入和读出。

2,Write_24c32和Read_24c32函数实现的时候已经加入了对器件是否忙碌的等待,写函数之紧接的是读函数,没有使用延迟函数,下图是逻辑分析仪抓取并分析的片段,我们看到连续对器件询问了三次都没有得到应答,最后在第四次得到了应答,经历时间是1.6毫秒。这1.6秒的等待如何在代码里面实现呢?大多数的代码都是直接DELAY上级个毫秒,而我们这里使用了查询,实现代码如下 :

i=10;
do {
    I2C_Start();
    r=Write_I2C(dev_addr);
i--;
if(i==0)return 0;
}while(r==0) ;  

我们观测带查询了3次就得到了器件的应答ACK,所以我们这里设置最大查询册数得10.

,

3,EEPROM的写入要经过大约1.6毫秒的时间,这点我们已经通过上面的截图测得。也就是说一次写EEPROM的操作之后,1.6毫秒内器件不再响应对他的询问。

4,我们看一下一次读操作之后,是否需要延迟后面的操作呢?先猜测一下,我觉得不需要的。看下图:


我们看到,在红点之前,是对EEPROM进行了读操作,之后对器件的询问,我们发现器件立即响应了。大家用编程器可能有个体验,读比烧写快很多,也就是这个原因。

5,如果要修改成24C02这样的单字节地址的EEPROM,只需要屏蔽掉蓝色的代码。

6,这个代码可以轻易实现页读写,就是在写数据的时候连续写多个之后再发停止位,在读数据的时候连读多个,最后一个字节才发NACK,其他的都发ACK。在大多情况下随机读写已经足够用了,因此不在这里实现页读写。








我的话:

1,I2C EEPROM 读写函数已经很完善了。

2,下步要整理一下思路,精简一下,做一个使用逻辑分析仪调试I2C的专题,可能不提到器件等待这部分。

3,下步要做的是,使用逻辑分析仪进行精密IO延迟的测定。

4,UART协议的实现。

5,TIMER的调试。








0 0
原创粉丝点击