gec210 i2c程序io模拟方式实现

来源:互联网 发布:李涛疯狂淘宝上市 编辑:程序博客网 时间:2024/05/16 14:18

开发环境:ubuntu arm-linux-gcc4.4.1

开发板: GEC210开发板


原理图

i2c芯片:FM24CL04

与cpu的连接,GPD1的0,1号引脚。0号引脚对应SDA功能,1号引脚对应SCL引脚


实现:

io方式模拟i2c通信,没有使用i2c控制器

需要使用io引脚输出高低电平模拟i2c信号

或者需要改变为输入模式服务数据


//宏定义

#define GPD1DAT (*(volatile unsigned long*)0xe02000c4)

#define GPD1CON (*(volatile unsigned long*)0xe02000c0)
#define UTXH0   (*(volatile unsigned char*)0xe2900020)
#define UTRSTAT0 (*(volatile unsigned long*)0xe2900010)


//函数声明

void sda_set(unsigned char x);
void scl_set(unsigned char x);
void delay(int n);
void i2c_start(void);
void i2c_stop(void);
void sda_input(void);
void sda_output(void);
void scl_output(void);
unsigned char read_sda(void);
unsigned char wait_ack(void);
void send_ack(unsigned char ack);
unsigned char data_read(unsigned char ack_flag);
unsigned char data_write(unsigned char buf);
unsigned char data_write_buf(unsigned short addr,unsigned char*buf,int len);
unsigned char rand_read_buf(unsigned short addr,unsigned char*buf,int len);
void print_hex(int n);
void print_string(char *);

//测试程序
void i2c_main(void)
{
    unsigned char buf[32];
    int i;

    sda_output();
    scl_output();

    print_string("2017\n\r");
    for(i=0;i<32;i++)
    {
        buf[i] = i + 0x20;
    }
    
    data_write_buf(0x0,buf,30);   //向0地址写入30个值
    
    rand_read_buf(10,buf,20);     //从10地址读取20个值
    
    for(i=0;i<20;i++)
    {
        print_hex(buf[i]);
    }
    print_string("i2c io test over\n\r");
    return;    
}




//SDA输出高或低电平
//参数 x非0时输出高电平,x为0时输出低电平
void sda_set(unsigned char x)
{
    if(x)
    {    
        GPD1DAT |= 1;    //输出高电平
    }
    else
    {
        GPD1DAT &= ~1;   //输出低电平
    }    
}

//SCL信号输出高电平或低电平
//参数 x非0时输出高电平,x为0时输出低电平
void scl_set(unsigned char x)
{
    if(x)
    {    
        GPD1DAT |= 1<<1;      //输出高电平
    }
    else
    {
        GPD1DAT &= ~(1<<1);    //输出低电平
    }    
}

//延时函数
void delay(int n)
{
    n = 100*n;
    while(n--);     //直到n减为0
}

//i2c的开始时序
//在SCL为高电平时,SDA由高变为低电平
//尽量保证所有的函数进入时SCL为0,函数返回时SCL也为低
void i2c_start(void)
{
    scl_set(0);      //SCL = 0
    sda_set(1);      //SDA = 1
    scl_set(1);      //SCL = 1
    delay(1);
    sda_set(0);   //SDA = 0
    delay(1);
    scl_set(0);      //SCL = 0
}

//i2c的停止时序
//在SCL为高电平时,SDA由低变为高电平
//最后进入空闲状态,SCL和SDA都保持为高电平
void i2c_stop(void)
{
    scl_set(0);      //SCL = 0
    sda_set(0);      //SDA = 0;
    scl_set(1);      //SCL = 1;    
    delay(1);
    sda_set(1);      //SDA = 1
    scl_set(1);      //SCL = 1
}

//SDA切换为输入模式
//在接收数据或接收应答的时候
//SDA为GPD1 io引脚的0号引脚
void sda_input(void)
{
    GPD1CON &= ~0xf;   //输入模式
}


//SDA切换为输入模式
//在发送数据的时候
//SDA为GPD1 io引脚的0号引脚
void sda_output(void)
{
    GPD1CON &= ~0xf;
    GPD1CON |= 1;   //输出模式
}

//SCL作为主机模式的时候,只需要设置为输出模式
//SCL为GPD1 io引脚的1号引脚
void scl_output(void)
{
    GPD1CON &= ~0xf0;
    GPD1CON |= 1<<4;   //输出模式
}

//在输入模式时,读取SDA引脚的信号
//只需要最低位,因为最低位才是SDA引脚的对应信号
unsigned char read_sda(void)
{
    return GPD1DAT & 1;
}


//主机读取从机发回的应答信号
//
unsigned char wait_ack(void)
{    
    unsigned char tmp;
    scl_set(0);      //SCL = 0;
    sda_set(1);   //SDA = 1    ,释放SDA总线
    sda_input();  //SDA切换为输入模式
    delay(1);
    scl_set(1);      //SCL = 1;
    delay(1);
    tmp = read_sda();  //读取SDA信号
    
    scl_set(0);    
    delay(1);
    sda_output();  //SDA切换为输出模式
    return tmp;
}



//主机发送应答信号
//参数ack为0时表示需要应答,为1时不应答
//ack是一个时序过程,就是数据发完的第9位,不管主机要不要发送应答
//这个第9位的时序都要出现在8个数据位之后
void send_ack(unsigned char ack)
{
    scl_set(0);      //SCL = 0;
    delay(1);
    sda_set(ack & 1);   //SDA = 0;
    delay(1);
    scl_set(1);    
    delay(3);
    scl_set(0);
}





//写8bits数据时序
unsigned char data_write(unsigned char buf)
{
    unsigned char i;
    scl_set(0);      //SCL = 0;
    delay(1);
    for(i=0;i<8;i++)
    {
        sda_set((buf>>(7-i)) & 1);
        delay(1);
        scl_set(1);   //SCL = 1;
        delay(5);
        scl_set(0);      //SCL = 0;
        delay(1);
    }
    return wait_ack();  //应答时序
}






//当前地址读操作
//fm24cl04读取数据的操作,当前地址的话,重新上电后的当前地址为0
//所以调试的时候注意以下,尽量断电重启
unsigned char data_read(unsigned char ack_flag)
{
    unsigned char i;
    unsigned char buf = 0;

    scl_set(0);      //SCL = 0;
    sda_input();  //sda输入模式
    delay(1);
    for(i=0;i<8;i++)
    {
        buf<<=1;
        scl_set(1);      //SCL = 1;
        delay(1);
        buf |= read_sda();        
//        delay(1);    
        scl_set(0);      //SCL = 0;    
        delay(1);
    }

    sda_output();  //sda输出模式
    send_ack(ack_flag);   //发送应答
    
    return buf;  //返回收到的数据
}



void i2c_read_buf(unsigned char page,unsigned char*buf,int len)
{
    int i;
    i2c_start();    //起始信号
    while(data_write(0xa1 | (page<<1)) != 0);   //设备地址,读操作,页选择
    
    for(i=0;i<len;i++)
    {
        buf[i] = data_read(i==len-1);  //只有i=9才传入1,其他情况为0
    }
    i2c_stop();
    return;    
}




//写操作
//fm24cl04写数据的操作
unsigned char data_write_buf(unsigned short addr,unsigned char*buf,int len)
{
    int i;
    unsigned char page = addr>>8;   //第9位为fm24cl04的页选择
    i2c_start();    //起始信号
    while(data_write(0xa0 | (page << 1)) != 0);   //设备地址,写操作
    
    data_write(addr & 0xff);   //发送空间地址
    
    if(len == 0)   //为随机读创造时序,参看随机读函数
    {
        return;
    }
    
    for(i=0;i<len;i++)
    {
        data_write(buf[i]);  //写入数据
    }
    i2c_stop();
}


//随机读函数操作,与当前地址读的差异
//fm24cl04写数据的操作
unsigned char rand_read_buf(unsigned short addr,unsigned char*buf,int len)
{
    data_write_buf(addr,buf,0);
    i2c_read_buf(addr>>8,buf,len);
}




/*******************************************************************************************/
/*
分割线
以下为串口打印函数,不完整。但是使用uboot的换是可以使用的。
暂时不做详细介绍

*/

void uart0_send_byte(unsigned char buf)
{
    while((UTRSTAT0 & (1<<2)) == 0);  //靠靠靠靠靠靠靠靠
    UTXH0 = buf;
}




void print_string(char*str)
{
    while(*str)
    {
        uart0_send_byte(*str++);
    }
}





void print_hex(int n)
{
    unsigned char arr[8];
    unsigned char i;
    unsigned char tmp;

    for(i=0;i<8;i++)
    {
        tmp = n & 0xf;   //靠靠靠靠4靠靠靠?6靠靠靠靠靠靠靠
        if(tmp > 9)
        {
            arr[i] = tmp + 'a' - 10;   //靠靠靠ASCII?
        }
        else
        {
            arr[i] = tmp + '0';
        }
        n >>= 4;
    }
    print_string("0x");

    for(i=0;i<8;i++)
    {
        uart0_send_byte(arr[7-i]);
    }
    print_string("\n\r");
}





原创粉丝点击