I2C驱动详解

发布者:Meiren520最新更新时间:2024-07-17 来源: elecfans关键字:I2C  驱动  JZ2440 手机看文章 扫描二维码
随时随地手机看文章

I2C讲解:

在JZ2440开发板上,I2C是由两条数据线构成的SCL,SDA;SCL作为时钟总线,SDA作为数据总线;两条线上可挂载I2C设备,如:AT24C08

两条线连接ARM9 I2C控制器,通过控制来控制I2C设备的识别设备地址、读、写操作;如图所示

从中所知:I2C线上可以挂载很多个I2C设备;挂载简单,只需要一根数据线和一根时钟线就可以挂载上去,通过地址来去别每个设备的区别;

 

I2C操作:

对I2C操作主要思想为:1、找到设备  2、进行读写操作 主要原理为:

1、发送开始信号S 然后紧接着发生设备8位地址如:0x50,然后等待设备发出一个应答ACK信号

2、当控制器接收到ACK信号后,表面找到这条I2C总线上确实有这个设备,然后发出数据,是进行读还是进行写,由第8位来决定

 原理如下图:

JZ2440对I2c驱动框架

正常的设备驱动程序,大体框架为:

1、通过应用程序open、read、write、ioctl函数去对硬件操作

2、在内核里,接收到应用程序发送过来的open、read、write、ioctl命令,出发中断,进而会跳到一个字符描述符的数组(open时返回值)当中,查找驱动程序ID(驱动程序进行注册);通过该ID找到相对应的drv_open、drv_read、drv_wrte等操作;

3、drv_open、drv_read、drv_wrte等操作跟字符设备的硬件驱动程序紧密相关,会最终会跳到字符设备驱动程序file_operation结构体进行硬件的操作过程。

4、在此之前,需要编写I2C驱动程序,驱动程序要有主入口、出口、file_openration结构体、进行注册等操作;

具体字符设备驱动程序框架为:

而I2C设备驱动采用的是总线—设备-驱动模型

总线-设备-驱动模型:

    总线-设备-驱动模型:其原理为在一条总线上分为两部分,一部分作为设备链表对象的,另一部分作为driver链表;设备链表对象就是

注册一个设备client对象挂载到这条链表上,但注册的设备名需要与右边driver链表注册的驱动名相比配才有意义,才能对进入driver链表

上与设备名相匹配的驱动程序里面的probe函数进行i2c驱动操作;举个例子:将驱动程序比作学生,驱动程序的名字比作学生的宿舍号码

管理员相当于总线,总线知道两个信息:1、学生 2、宿舍号 ;当有一个学生进行入住,学生想住A10-208,好,经过管理员同意好,就

住进A10-208(注册一个i2c_driver)了,当管理员想找到那个学生,则需要通过A10-208这个名字去查找到学生所在住处,通过管理员

管理的学生信息本(注册一个设备)找到名字,进而去到宿舍找到这个学生,让他扫地、打扫卫生什么的;主要原理图如下:

 

i2c-总线-驱动模型

 

应用程序是如何调用的呢?如图所示

 

i2c进行open、read、write、ioctl等操作会进入内核太,找到相关设备驱动函数,然后就进入了总线控制层,I2c总线层两部分组成,核心层

里面已经定义了对i2c提供的操作函数,不需要用户自己定义,适配器就是具体的对2440的i2c硬件操作或者定义其它的芯片的硬件操作;

 

总结:总得来说,我们需要进行的操作为

 

1、注册一个设备,里面要有设备名,以及该设备id,并挂载到这条设备链表上

2、注册一个驱动,该设备驱动需要有设备名,probe函数、id_table设备地址,然后挂载到驱动链表上

3、比较两条链表上的设备名字是否相同,如果有相同的话,就去到相关的驱动设备上的probe函数,进行

    操作

4、probe函数里面就是我们正常的驱动程序的编程程序了,主入口、出口、file_operation结构体的构造等等操作;最终应用程序会进入这里操作硬件对象

 

-----------------------------------------------------------------------------------------------------------------------------------------------

如图代码所示:新建一个设备有多种方法,该方法为第二种i2c_new_probed_device

at24cxx_dev:注册一个新设备

#include

#include

#include

#include

#include

#include

#include


static struct i2c_client *at24cxx_client;


static const unsigned short addr_list[] = { 0x60, 0x50, I2C_CLIENT_END };


static int at24cxx_dev_init(void)

{

    struct i2c_adapter *i2c_adap;

    struct i2c_board_info at24cxx_info;


    memset(&at24cxx_info, 0, sizeof(struct i2c_board_info));    

    strlcpy(at24cxx_info.type, 'at24c08', I2C_NAME_SIZE);


    i2c_adap = i2c_get_adapter(0);

    at24cxx_client = i2c_new_probed_device(i2c_adap, &at24cxx_info, addr_list, NULL);

    i2c_put_adapter(i2c_adap);


    if (at24cxx_client)

        return 0;

    else

        return -ENODEV;

}


static void at24cxx_dev_exit(void)

{

    i2c_unregister_device(at24cxx_client);

}



module_init(at24cxx_dev_init);

module_exit(at24cxx_dev_exit);

MODULE_LICENSE('GPL');


at24cxx_drv:注册一个新驱动  注:probe函数并没有进行i2c的·操作,只是打印一些信息而已


#include

#include

#include

#include

#include

#include

#include



static int __devinit at24cxx_probe(struct i2c_client *client,

                  const struct i2c_device_id *id)

{

    printk('%s %s %dn', __FILE__, __FUNCTION__, __LINE__);

    return 0;

}


static int __devexit at24cxx_remove(struct i2c_client *client)

{

    printk('%s %s %dn', __FILE__, __FUNCTION__, __LINE__);

    return 0;

}


static const struct i2c_device_id at24cxx_id_table[] = {

    { 'at24c08', 0 },

    {}

};



/* 1. 分配/设置i2c_driver */

static struct i2c_driver at24cxx_driver = {

    .driver    = {

        .name    = '100ask',

        .owner    = THIS_MODULE,

    },

    .probe        = at24cxx_probe,

    .remove        = __devexit_p(at24cxx_remove),

    .id_table    = at24cxx_id_table,

};


static int at24cxx_drv_init(void)

{

    /* 2. 注册i2c_driver */

    i2c_add_driver(&at24cxx_driver);

    

    return 0;

}


static void at24cxx_drv_exit(void)

{

    i2c_del_driver(&at24cxx_driver);

}



module_init(at24cxx_drv_init);

module_exit(at24cxx_drv_exit);

MODULE_LICENSE('GPL');


Makefile:


KERN_DIR = /work/system/linux-3.4.2


all:

    make -C $(KERN_DIR) M=`pwd` modules 


clean:

    make -C $(KERN_DIR) M=`pwd` modules clean

    rm -rf modules.order


obj-m    += at24cxx_dev.o

obj-m    += at24cxx_drv.o

#obj-m    += i2c_bus_s3c2440.o


-----------------------------------------------------------------------------------------------------------------------------------------------


对I2c驱动的操作


前面已经讲过对于I2c的操作可以调用总线上现成的函数来操作i2c_smbus_write_byte_data


at24cxx_dev:


#include

#include

#include

#include

#include

#include

#include



static struct i2c_board_info at24cxx_info = {    

    I2C_BOARD_INFO('at24c08', 0x50),

};


static struct i2c_client *at24cxx_client;


static int at24cxx_dev_init(void)

{

    struct i2c_adapter *i2c_adap;


    i2c_adap = i2c_get_adapter(0);

    at24cxx_client = i2c_new_device(i2c_adap, &at24cxx_info);

    i2c_put_adapter(i2c_adap);

    

    return 0;

}


static void at24cxx_dev_exit(void)

{

    i2c_unregister_device(at24cxx_client);

}



module_init(at24cxx_dev_init);

module_exit(at24cxx_dev_exit);

MODULE_LICENSE('GPL');


at24cxx_drv:注册驱动程序,进行I2c操作


#include

#include

#include

#include

#include

#include

#include

#include

#include



static int major;

static struct class *class;

static struct i2c_client *at24cxx_client;


/* 传入: buf[0] : addr

 * 输出: buf[0] : data

 */

static ssize_t at24cxx_read(struct file * file, char __user *buf, size_t count, loff_t *off)

{

    unsigned char addr, data;

    

    copy_from_user(&addr, buf, 1);

    data = i2c_smbus_read_byte_data(at24cxx_client, addr);

    copy_to_user(buf, &data, 1);

    return 1;

}


/* buf[0] : addr

 * buf[1] : data

 */

static ssize_t at24cxx_write(struct file *file, const char __user *buf, size_t count, loff_t *off)

{

    unsigned char ker_buf[2];

    unsigned char addr, data;


    copy_from_user(ker_buf, buf, 2);

    addr = ker_buf[0];

    data = ker_buf[1];


    printk('addr = 0x%02x, data = 0x%02xn', addr, data);


    if (!i2c_smbus_write_byte_data(at24cxx_client, addr, data))

        return 2;

    else

        return -EIO;    

}


static struct file_operations at24cxx_fops = {

    .owner = THIS_MODULE,

    .read  = at24cxx_read,

    .write = at24cxx_write,

};


static int __devinit at24cxx_probe(struct i2c_client *client,

                  const struct i2c_device_id *id)

{

    at24cxx_client = client;

        

    //printk('%s %s %dn', __FILE__, __FUNCTION__, __LINE__);

    major = register_chrdev(0, 'at24cxx', &at24cxx_fops);

    class = class_create(THIS_MODULE, 'at24cxx');

    device_create(class, NULL, MKDEV(major, 0), NULL, 'at24cxx'); /* /dev/at24cxx */

    

    return 0;

}


static int __devexit at24cxx_remove(struct i2c_client *client)

{

    //printk('%s %s %dn', __FILE__, __FUNCTION__, __LINE__);

    device_destroy(class, MKDEV(major, 0));

    class_destroy(class);

    unregister_chrdev(major, 'at24cxx');

        

    return 0;

}


static const struct i2c_device_id at24cxx_id_table[] = {

    { 'at24c08', 0 },

    {}

};



/* 1. 分配/设置i2c_driver */

static struct i2c_driver at24cxx_driver = {

    .driver    = {

        .name    = '100ask',

        .owner    = THIS_MODULE,

    },

    .probe        = at24cxx_probe,

    .remove        = __devexit_p(at24cxx_remove),

    .id_table    = at24cxx_id_table,

};


static int at24cxx_drv_init(void)

{

    /* 2. 注册i2c_driver */

    i2c_add_driver(&at24cxx_driver);

    

    return 0;

}


static void at24cxx_drv_exit(void)

{

    i2c_del_driver(&at24cxx_driver);

}



module_init(at24cxx_drv_init);

module_exit(at24cxx_drv_exit);

MODULE_LICENSE('GPL');


应用测试程序i2c_test:


#include

#include

#include

#include

#include

#include



/* i2c_test r addr

 * i2c_test w addr val

 */


void print_usage(char *file)

{

    printf('%s r addrn', file);

    printf('%s w addr valn', file);

}


int main(int argc, char **argv)

{

    int fd;

    unsigned char buf[2];

    

    if ((argc != 3) && (argc != 4))

    {

        print_usage(argv[0]);

        return -1;

    }


    fd = open('/dev/at24cxx', O_RDWR);

    if (fd < 0)

    {

        printf('can't open /dev/at24cxxn');

        return -1;

    }


    if (strcmp(argv[1], 'r') == 0)

    {

[1] [2]
关键字:I2C  驱动  JZ2440 引用地址:I2C驱动详解

上一篇:ARM应用调试思路、方法总结、笔记
下一篇:LCD驱动程序(一)

推荐阅读最新更新时间:2026-03-20 13:16

JZ2440串口打印字符作为调试
/* * 初始化UART0 * 57600,8N1,无流控 */ void uart0_init(void) { GPHCON |= 0xa0; // GPH2,GPH3用作TXD0,RXD0 GPHUP = 0x0c; // GPH2,GPH3内部上拉 ULCON0 = 0x03; // 8N1(8个数据位,无较验,1个停止位) UCON0 = 0x05; // 查询方式,UART时钟源为PCLK UFCON0 = 0x00; // 不使用FIFO UMCON0 = 0x00; // 不使用流控 UBRDIV0 = UART_BRD; // 波特率为115200 }
[单片机]
移植U-boot_2016.09到JZ2440开发板
一.下载源码:U-Boot源代码下载地址 http://www.linuxidc.com/Linux/2011-07/38897.htm 二.初始化编译:   ①make smdk2410_defconfig  #首先使用默认配置,减少后续的配置工作   ②make menuconfig   #根据自身需求进一步配置   ③修改Makefile ,开头只能架构和编译器:      ARCH=arm      CROSS_COMPILE=arm-linux- ④修改uboot代码:    1. 设置PLL的时钟的函数在_main中的board_init_f中初始化函数列表中的 boad_early_init_f 中,
[单片机]
STM32:打造高效且小巧的I2C驱动程序
在嵌入式系统开发中,STM32系列微控制器凭借其强大的性能和丰富的外设接口,成为了众多开发者的首选。其中,I2C(Inter-Integrated Circuit)接口作为一种常用的串行通信协议,广泛应用于各种传感器、存储器等外设的连接。本文旨在介绍如何为STM32设计一款高效且小巧的I2C驱动程序,以满足嵌入式系统中对资源利用和性能优化的双重需求。 一、STM32 I2C接口概述 STM32系列微控制器内置了多个I2C接口,如STM32F411CEx型号就配备了I2C1、I2C2和I2C3三个模块。这些模块支持7位和10位地址的发送与接收,能够生成100KHz、400KHz的时钟频率,并可在特定条件下提升至1MHz。ST
[单片机]
迅为imx6ull开发板Linux I2C驱动实验-应用程序与I2C通信
本章内容对应视频讲解链接(在线观看): 程序源码在网盘资料“imx6ull 驱动程序配套资料21-Linux I2C 驱动实验”路径下。 我们可以先来体验一下,在 Linux 上操作 I2C 是多么的容易,我们可以先来看一下系统里面都有哪些 I2C的节点,这里以终结者 imx6ull 开发板为例。如下图所示: Linux 有一个非常重要的概念叫一切皆文件,那么我们能不能在应用层通过 open 这些节点来操作 I2C 来跟外设 I2C 通信的芯片进行一个数据交流呢?当然是可以的,我们来一起看一下,这里我们以 7 寸 RGB 屏幕上的触摸芯片 FT5X06 为例,迅为所有开发板都是支持迅为 7 寸 RGB 屏幕屏的,所有都是可以进
[单片机]
迅为imx6ull开发板Linux <font color='red'>I2C</font><font color='red'>驱动</font>实验-应用程序与<font color='red'>I2C</font>通信
STM32 I2C写入驱动数码管显示实验
实验环境 Matlab版本: 2021b 操作系统 :Win10专业版 硬件平台 :YF-STM32-ALPHA 1R4 实验内容 :I2C驱动数码管芯片显示‘0’~‘A’字符 模型与原理图 本次实验所用到的simulink模型如图5.2所示,电路原理图如图5.3所示,实验效果为数码管从‘0’~‘A’轮流显示,如图5.1所示。 图5.1 数码管驱动显示实验效果 图5.2 数码管显示I2C驱动模型 图5.3 CH422G共阴数码管电路连接 基础模型介绍与分析 1、数码管简介 数码管简单来说就是将多个LED有规律的排列在一起,实验中用到的数码管外观如图5.4所示,其型号为:FJ3461AH,三维尺寸:30.00mmx1
[单片机]
STM32 <font color='red'>I2C</font>写入<font color='red'>驱动</font>数码管显示实验
[JZ2440] 使用 oflash + OpenJTAG 烧写 U-Boot
在 光盘资料 - 烧写工具 - 裸机 - eop&op 目录下可以获取本篇文章所有相关驱动文件。 一、安装 oflash oflash 安装包路径:eop&op - 调试工具 - 01.OpenOCD with GUI setup.exe 使用 管理员权限 安装 OpenOCD with GUI setup.exe 即可在 cmd.exe 命令行里执行 oflash 程序。 验证驱动 oflash 是否安装成功,依次执行:Win + R - 输入 cmd 调起控制台 - 输入 oflash 执行,出现以下提示说明 oflash 安装成功。 C:Usersuser oflash +---
[单片机]
[<font color='red'>JZ2440</font>] 使用 oflash + OpenJTAG 烧写 U-Boot
[JZ2440] 配置编译 linux-2.6 内核
一、相关资料获取   我个人在学习 mini2440 开发板时是跟着韦东山老师的视频学习的,视频和资料可以访问百问网论坛获取。   百问网论坛链接: http://www.100ask.net/bbs/forum.php   下载好资料后在 JZ2440资料光盘 文件中 systems 目录下可以获取到 linux-2.6.22.6.tar.bz2(内核源码包)和 linux-2.6.22.6_jz2440.patch(韦东山老师移植 linux-2.6 到 JZ2440 开发板的 patch 包)。在第一期视频目录下可以获取到 韦东山Linux视频第1第2期所有源码文档图片芯片手册.rar 压缩包。 二、配置编译
[单片机]
JZ2440点亮LED电路图设计案例
在JZ2440中,点亮LED就是给LED的控制位设置为输出,数据位设置为低电平,而通过按键点亮LED,就需要将按键对应的控制位设置为输出。 下面是JZ2440的3个LED电路图: 下面是JZ2440的3个按键的电路图 通过查找nLED_1,nLED_2,nLED_4对应的引脚,发现它们分别对应GPF4,GPF5,GPF6,如图: 通过查找EINT0,EINT2,EINT11对应的引脚,发现它们分别对应GPF0,GPF2,GPG3,如图: 由此,我们再去看2440的Datasheet,查看它们的控制位和数据位的信息,首先是GPF4,GPF5,GPF6的控制位信息,如图: 我们发现,当GPFCON 寄存器 的GP
[单片机]
<font color='red'>JZ2440</font>点亮LED电路图设计案例
小广播
最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

厂商技术中心

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

 
机器人开发圈

电子工程世界版权所有 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2026 EEWORLD.com.cn, Inc. All rights reserved