当前位置: 代码网 > 服务器>服务器>Linux > 详解AT24CXX驱动开发(linux platform tree - i2c应用)

详解AT24CXX驱动开发(linux platform tree - i2c应用)

2024年08月06日 Linux 我要评论
本文以AT24C02为例,对该类型芯片做全面剖析,详细介绍该芯片的硬件特性和软件设计方面的方法和技巧,其中包括单字节的读和写,连续字节的读写方法、以I2C波形分析。并且在linux平台上,使用I2C接口,编写一个实用案例,实现该芯片的驱动程序。并使用逻辑分析仪工具,详尽解析其工作的波形。

目录

概述

1 认识at24cxx

1.1 at24cxx的特性

1.2 at24cxx描述

1.2.1 引脚

1.2.2 容量描述

1.2.3 设备地址

1.3 操作时序

1.3.1 写单个字节时序

1.3.2 写page字节时序

1.3.3 读取当前数据时序

1.3.4 随机读取数据

1.3.5 连续读取多个数据

2 驱动开发

2.1 硬件接口

2.2 代码实现

2.2.1 查看设备信息

2.2.2 编写代码

2.2.3 编写makefile 

2.3 编译代码

3 测试

4 逻辑分析仪查看波形

4.1 写数据波形

4.2 读数据波形


概述

        本文以at24c02为例,对该类型芯片做全面剖析,详细介绍该芯片的硬件特性和软件设计方面的方法和技巧,其中包括单字节的读和写,连续字节的读写方法、以i2c波形分析。并且在linux平台上,使用i2c接口,编写一个实用案例,实现该芯片的驱动程序。并使用逻辑分析仪工具,详尽解析其工作的波形。

1 认识at24cxx

1.1 at24cxx特性

at24cxx是一款可进行多次擦除和写数据的串行eeprom,采用标准的i2c驱动方式,非常方便电路设计和软件程序的设计。总结其特点如下:

1)宽电压:该芯片的工作电压范围为 1.8v ~ 5.5v。可以满足不同mcu平台的设计要求。

2)写保护功能:at24cxx提供一个引脚wp用于enable或者disable写数据功能,防止由于操作不当,导致存储数据被改写。

3)其可同时支持100k bit/s 和 400k bit/s.

使用时注意: 100 khz (1.8v, 2.5v, 2.7v) , 400 khz (5v)

4)使用寿命很长: 擦写次数可达到100 0000 次, 数据保存可达100年

1.2 at24cxx硬件描述

1.2.1 引脚

          at24cxx有很多类型,例如: at24c02a/04a/08a/16a/32a 等待,单其封装形式,基本上一致。标准引脚如下表:

1)wp 为写保护引脚,当wp为低电平是,读写功能被enable;当wp为高电平时, 写数据功能被disable, 这时,只能进行读操作。

2)a0 ~ a2为 地址引脚,对于at24c02,其可表达的地址空间为:000 ~ 111

at24cxx芯片封装形式如下图:

1.2.2 容量描述

下面以 at24c02a/04a/08a 为例,描述其存储空间

芯片型号容量空间( bit)页数( byte)
at24c02a2048256
at24c04a4096512
at24c08a81921024

1.2.3 设备地址

参看设备地址表,可知不同型号的芯片,其可使用的地址空间是不一样的。

1) at24c02: a0a1a2三个地址引脚都可以使用,其可表达的地址范围为: 000 ~ 111

2) at24c04: a2a1可用, 其可表达的地址范围为: 00 ~ 11

3) at24c04: a2可用, 其可表达的地址范围为: 0 ~ 1

4) bit: r/w

(r/w)bit 位的值含义
1读数据
0写数据

1.3 操作时序

1.3.1 写单个字节时序

下图为写一个字节的波形图形:

剖析操作步骤如下:

step - 1: master sda发起start信号

step - 2: master 发送设备地址信息, 最低位 bit0 置位为 bit0=0,表示可进行写操作。

step - 3: at24cxx 响应ack

step - 4: master 发送写数据的地址位

step - 5: at24cxx 响应ack

step - 6: master 发送写数据

step - 7: at24cxx 响应ack

step - 8: master sda发起stop信号

1.3.2 写page字节时序

下图为写一个page的波形图形:

剖析操作步骤如下:

step - 1: master sda发起start信号

step - 2: master 发送设备地址信息, 最低位 bit0 置位为 bit0=0,表示可进行写操作。

step - 3: at24cxx 响应ack

step - 4: master 发送写数据的起始地址

step - 5: 连续发送数据

for( i = 0; i < n; i++)

{

      master_send_data();

      slave_response_ack();

}

step - 6: master sda发起stop信号

1.3.3 读取当前数据时序

下图为读当前地址的波形图形:

剖析操作步骤如下:

step - 1: master sda发起start信号

step - 2: master 发送设备地址信息, 最低位 bit0 置位为 bit0=1,表示可进行读操作。

step - 3: at24cxx 响应ack

step - 4: at24cxx 发送写数据

step - 5: master 发送no ack

step - 6: master sda发起stop信号

1.3.4 随机读取数据

下图为随机读取数据的波形图形:

剖析操作步骤如下:

step - 1: master sda发起start信号

step - 2: master 发送设备地址信息, 最低位 bit0 置位为 bit0=1,表示可进行读操作。

step - 3: at24cxx 响应ack

step - 4: master 发送读字节地址

step - 5: at24cxx 响应ack

step - 6: at24cxx 发送数据

step - 7: master收到数据后,发送no ack

step - 8: master sda发起stop信号

1.3.5 连续读取多个数据

下图为连续读取多个数据的波形图形:

操作逻辑如下:

step - 1: master sda发起start信号

step - 2: master 发送设备地址信息, 最低位 bit0 置位为 bit0=1,表示可进行读操作。

step - 3: at24cxx 响应ack

step - 4: at24cxx 发送数据

for( i = 0; i < n; i++)

{

         at24cxx_send_data();

         master_response_ack();

}

step - 5: master发送no ack

step - 6: master sda发起stop信号

2 驱动开发

2.1 硬件接口

在板卡atk-dl6y2c上,i2c2的对应接口:

        pinctrl_i2c2: i2c2grp {
            fsl,pins = <
                mx6ul_pad_uart5_tx_data__i2c2_scl 0x4001b8b0
                mx6ul_pad_uart5_rx_data__i2c2_sda 0x4001b8b0
            >;
        };

硬件实物图:

at24cxx硬件工作电路

2.2 代码实现

       本程序主要应用linux platform 驱动下的i2c接口,在用户层调用read 和write 函数直接操作芯片。linux内核已经实现i2c相关的驱动程序。用户只需在.dts中配置参数即可。

2.2.1 查看设备信息

使用i2c-tools 查看系统的i2信息,然后在/proc/device-tree目录下查看板卡i2c device tree,使用如下命令:

cd /sys/bus/i2c/devices
ls

可以看见: at24c02挂载i2c-1总线下,其地址位0x50

查看地址下设备名称

cat 1-0050/name

2.2.2 编写代码

 创建drv_15_at24cxx.c,并在该文件中编写驱动程序

/***************************************************************
copyright  2024-2029. all rights reserved.
文件名     : test_15_at24cxx.c
作者       : tangmingfei2013@126.com
版本       : v1.0
描述       : 测试at24cxx驱动程序
其他       : 无
日志       : 初版v1.0 2024/02/15

***************************************************************/
#include <sys/types.h>
#include <sys/stat.h>
#include <linux/types.h>
#include <linux/i2c-dev.h>
#include <linux/i2c.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <stdlib.h>
#include <linux/fs.h>
#include <unistd.h>
#include <errno.h>
#include <assert.h>
#include <string.h>
#include <time.h>

#define i2c_dev                              "/dev/i2c-1"
#define at24cxx_addr                         0x50


static int fd = -1;


static int at24cxx_drv_init(void)
{
    fd = open(i2c_dev, o_rdwr);
    if( fd < 0 )
    {
        close( fd );
        printf("%s %s i2c device open failure: %s\n",
         __file__, __function__, strerror(errno));
        return -1;
    }

    ioctl(fd, i2c_tenbit, 0);
    ioctl(fd, i2c_slave, at24cxx_addr);
    
    printf("init at24cxx!\r\n");
    return fd;
}


int read_page( unsigned char page, unsigned char *rbuff,  unsigned char length )
{
    unsigned char   tempbuff[1];
    int i = 0, ret;
    
    tempbuff[0] = page;  // address 
    ret = write(fd, tempbuff, 1);
    if( ret < 0 )
    {
       printf("%d %s %s i2c device write address fail: %s\n",
          __line__, __file__, __function__, strerror(errno));
       close(fd);
       return -1;
    }
    
    ret = read(fd, rbuff, length);
    if( ret < 0 )
    {
        printf("%d %s %s i2c device read data fail: %s\n", 
         __line__ , __file__, __function__, strerror(errno));
        close(fd);
        return -1;
    }
    printf(" read data to at24c02 in address = %d \r\n ", page);
    for( i=0; i< length; i++ )
    {
        printf(" %x \t ", rbuff[i]);
    }
    printf(" \r\n ");
    
    return 0;
}

int write_page(  unsigned char page,  unsigned char *buff, unsigned char len)
{
    unsigned char   tempbuff[len+1];
    int i = 0, ret;
    
    printf(" write data to at24c02 in address = %d \r\n ", page);
    // step-1: write 
    tempbuff[0] = page;
    for( i=1; i< sizeof(tempbuff); i++ )
    {
        tempbuff[i] = buff[i-1];
        printf(" %x \t ", tempbuff[i]);
    }
    printf(" \r\n ");
    
    ret = write(fd, tempbuff, sizeof(tempbuff));
    if( ret < 0 )
    {
       printf("%d %s %s i2c device write data failure: %s\n",
         __line__,  __file__, __function__, strerror(errno));
       close(fd);
       return -1;
    }
    
    return 0;
}


int main(void)
{
    #define len    8
    unsigned startpage = 8;
    unsigned char buff[len];
    unsigned char rbuff[len];
    int i = 0, index = 0, j= 0;
    
    fd = at24cxx_drv_init();
    
    for( j=0; j < 5; j++ )
    {
        // write data 
        for(i = 0; i < sizeof( buff); i++ )
        {
            buff[i] = 0x20 + index;
            index++;
        }
        
        write_page( startpage, buff, sizeof( buff)); 
        printf(" \r\n \r\n ");
        startpage += 8;
        usleep(20000);   //20ms 
    }
   
    startpage = 8;
    for( j=0; j < 5; j++ )
    {
        read_page( startpage, rbuff, sizeof( rbuff));
        printf(" \r\n \r\n ");
        startpage += 8;
        usleep(20000); //20ms 
    }
    
    return 0;
}




2.2.3 编写makefile 

创建makefile文件,编写代码

cflags= -wall -o2
cc=/home/ctools/gcc-linaro-4.9.4-arm-linux-gnueabihf/bin/arm-linux-gnueabihf-gcc
strip=/home/ctools/gcc-linaro-4.9.4-arm-linux-gnueabihf/bin/arm-linux-gnueabihf-strip


test_15_at24cxx: test_15_at24cxx.o
	$(cc) $(cflags) -o test_15_at24cxx test_15_at24cxx.o
	$(strip) -s test_15_at24cxx

clean:
	rm -f test_15_at24cxx test_15_at24cxx.o

2.3 编译代码

       使用make命令编译代码,然后将生成的执行文件copy到nfs下的共享目录中,方便在板卡中执行代码:

在板卡中可以看见:

3 测试

在板卡中运行程序后,可以看见:

4 逻辑分析仪查看波形

4.1 写数据波形

以连续写多个数据为例,分析芯片的驱动波形:

1)写地址波形

2)写数据波形

 

3)stop 信号

4.2 读数据波形

以连续读多个数据为例,分析芯片的驱动波形:

1)写起始地址

3) 读数据byte

 4) 结束波形

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com