linux 触摸结构体,xboot-x4412&ibox项目实战54-Linux触摸屏驱动之I2C驱动实验 - Powered by Discuz!...

前面我们分析了linux触摸屏驱动的input子系统机制,本章节分析linux触摸屏驱动的i2c机制。

驱动源码路径:

kernel/drivers/input/touchscreen/ft5x06_touch.c

kernel/drivers/input/touchscreen/ft5x06_firmware_1024_600.h

kernel/drivers/input/touchscreen/ft5x06_firmware_800_480.h复制代码

在探测函数ft5x06_ts_probe中,定义了两个结构体指针adapter和client,分别指向i2c的主设备结构体i2c_adapter和i2c的从设备结构体i2c_client。另外定义了一个记录i2c从设备信息的结构体info,用于记录从设备的名称和设备地址。

首先通过i2c_get_adapter函数获得一个i2c_adapter指针:

adapter = i2c_get_adapter(FT5X06_I2C_BUS);复制代码

函数的传入参数为I2C的通道,这里为通道1,因为x4412开发板上对应的触摸屏芯片接在I2C的1通道。

再将从设备的名称和地址填充到结构体info:

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

info.addr = FT5X06_I2C_ADDRESS;//填充i2c_board_info结构体中I2C的地址

strlcpy(info.type, "ft5x06-iic", I2C_NAME_SIZE);复制代码

注意,这里的设备名称为ft5x06-iic,后面在i2c_driver结构体中一定要有和它匹配的名称,否则i2c驱动的probe函数将无法执行。

接着使用i2c_new_device函数将i2c的主设备和从设备关联起来,组成一个客户端,并返回一个指向i2c_client的结构体client。这个结构体非常重要,它将通过i2c_add_driver函数传给i2c对应的probe函数ft5x06_iic_probe,后面整个i2c操作都离不开它。

前面通过i2c_get_adapter函数获得了adapter结构体后,一旦通过adapter获得了从设备的结构体client,它的任务即已经完成,需要使用i2c_put_adapter函数释放该指针:

i2c_put_adapter(adapter);复制代码

最后通过i2c_add_driver函数注册一个i2c驱动:

ret = i2c_add_driver(&ft5x06_iic_driver);

ft5x06_iic_driver对应的结构体为:

static struct i2c_driver ft5x06_iic_driver = {

.driver                  = {

.name         = "ft5x06-iic",//结构体中已经存在id_table,故匹配名称时以id_table为准

},

.probe                  = ft5x06_iic_probe,

.remove               = ft5x06_iic_remove,

.suspend     = ft5x06_iic_suspend,

.resume                = ft5x06_iic_resume,

.id_table     = ft5x06_iic_id,

};复制代码

注意,该结构体中已经存在id_talbe,platform匹配时,将不再认成员driver中的name,而是会在id_table中查找是否有和前面我们定义的i2c名称“ft5x06_iic_probe”相同。

id_table对应内容如下:

static const struct i2c_device_id ft5x06_iic_id[] = {

{ "ft5x06-iic", 0},//该名称与i2c_board_info结构体中的驱动名称匹配,则调用probe函数

{ }

};复制代码

可见,内核将会成功匹配,i2c的探测函数ft5x06_iic_probe得以运行。在该探测函数中,首先通过i2c_check_functionality函数检查i2c主设备的驱动能力:

if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA))//查看I2C适配器的能力

return -ENODEV;复制代码

再将函数中定义的指向ft5x06_ts结构体的指针ts的数据存放在client->dev->p->driver_data,以备调用:

ts->client = client;//将从设备i2c_client数据赋值给ts

i2c_set_clientdata(client, ts);复制代码

在配置完触摸屏的input子系统以及触摸屏中断后,就开始通过i2c读取和写入数据了。

函数ft5x06_read_fw_ver用于读取FT5x06芯片的firmware版本:

uc_reg_value = ft5x06_read_fw_ver(client);复制代码

这里传入了前面申请的结构体client,其函数原型如下:

static uint8_t ft5x06_read_fw_ver(struct i2c_client * client)

{

uint8_t ver;

ft5x06_read_reg(client, FT5X0X_REG_FIRMID, &ver);

return (ver);

}

static int ft5x06_read_reg(struct i2c_client * client, uint8_t addr, uint8_t * data)

{

uint8_t buf[2];

struct i2c_msg msgs[2];

int ret;

buf[0] = addr;

msgs[0].addr = client->addr;//i2c芯片地址

msgs[0].flags = 0; //0表示写

msgs[0].len = 1;   //要写的字节数为1

msgs[0].buf = buf; //需要读的寄存器地址

msgs[1].addr = client->addr;//i2c芯片地址

msgs[1].flags = I2C_M_RD;//1表示读,I2C_M_RD=1

msgs[1].len = 1;   //要读的字节数为1

msgs[1].buf = buf;         //读取的数据保存到buf

ret = i2c_transfer(client->adapter, msgs, 2);//传输2个msg

if(ret < 0)

printk("msg i2c read error\n");

*data = buf[0];

return ret;

}复制代码

真正干活的是函数ft5x06_read_reg,它通过i2c_transfer函数读取寄存器值。传入参数addr对应需要读取的寄存器的地址,*data返回从寄存器中读取的寄存器值。ft5x06_read_reg函数是一个典型的利用i2c_transfer读取寄存器值的模板,它定义了两个i2c_msg结构体msgs,msgs[0]用于写寄存器的地址,msgs[1]用于读该寄存器的值。I2C每次读寄存器的值,都需要先对该寄存器的地址发写命令,再发读命令返回写入寄存器的地址对应的寄存器值。

i2c_msg结构体内容如下:

struct i2c_msg {

__u16 addr;         /* slave address                      */

__u16 flags;

#define I2C_M_TEN             0x0010       /* this is a ten bit chip address */

#define I2C_M_RD               0x0001       /* read data, from slave to master */

#define I2C_M_NOSTART            0x4000       /* if I2C_FUNC_PROTOCOL_MANGLING */

#define I2C_M_REV_DIR_ADDR        0x2000       /* if I2C_FUNC_PROTOCOL_MANGLING */

#define I2C_M_IGNORE_NAK    0x1000       /* if I2C_FUNC_PROTOCOL_MANGLING */

#define I2C_M_NO_RD_ACK               0x0800       /* if I2C_FUNC_PROTOCOL_MANGLING */

#define I2C_M_RECV_LEN                   0x0400       /* length will be first received byte */

__u16 len;           /* msg length                                   */

__u8 *buf;           /* pointer to msg data                      */

};复制代码

这里addr表示从设备的地址,flags表示I2C需要执行的动作,为0表示写,为1表示读。len表示写入或读出的寄存器的字节数。当为写时,buf表示需要写入的寄存器地址,当为读时,buf返回从寄存器读出的值。i2c_transfer函数的第三个参数表示传入的msg的个数,通常读一个寄存器时,该值为2,写一个寄存器时,该值为1。

ft5x06_write_reg函数用于写寄存器,其源码如下:

static int ft5x06_write_reg(struct i2c_client * client, uint8_t addr, uint8_t data)

{

uint8_t buf[3];

int ret = -1;

buf[0] = addr;

buf[1] = data;

ret = ft5x06_i2c_txdata(client, buf, 2);

if (ret < 0)

{

printk("write reg failed! %#x ret: %d", buf[0], ret);

return -1;

}

return 0;

}

static int ft5x06_i2c_txdata(struct i2c_client * client, char * txdata, int length)

{

int ret;

struct i2c_msg msg[] = {

{

.addr     = client->addr,     //i2c芯片地址

.flags     = 0,            //0表示写寄存器

.len        = length,     //要写入的数据的长度为2

.buf       = txdata,     //寄存器地址和该地址要写入的数据

},

};

ret = i2c_transfer(client->adapter, msg, 1);//传输1个msg

if (ret < 0)

printk("%s i2c write error: %d\n", __func__, ret);

return ret;

}复制代码

在ft5x06_write_reg函数中,通过形参将要写入的寄存器的地址和数据保存到buf数组,在ft5x06_i2c_txdata函数中定义了一个i2c_msg结构体msg,它的flags标志为0,表示写寄存器,buf[0]和buf[1]分别为要写入的寄存器的地址和数据,写入的长度len为2。最后通过i2c_transfer函数写入寄存器。

在触摸屏芯片第一次执行时,需要通过I2C给芯片里面的固件升级,这时需要成批的操作寄存器。

FTS_BOOLbyte_write函数用于批量写寄存器,其原型如下:

static FTS_BOOL byte_write(struct i2c_client * client, FTS_BYTE* pbt_buf, FTS_DWRD dw_len)

{

return i2c_write_interface(client, FT5X06_I2C_ADDRESS, pbt_buf, dw_len);

}

static FTS_BOOL i2c_write_interface(struct i2c_client * client, FTS_BYTE bt_ctpm_addr, FTS_BYTE* pbt_buf, FTS_DWRD dw_lenth)

{

int ret;

ret = i2c_master_send(client, pbt_buf, dw_lenth);//一次性写入多个寄存器

if(ret<=0)

{

printk("[FTS]i2c_write_interface error line = %d, ret = %d\n", __LINE__, ret);

return FTS_FALSE;

}

return FTS_TRUE;

}复制代码

FTS_BOOLbyte_write函数的传入参数中,pbt_buf指向要写入的寄存器值表,通常对应一个头文件,如ft5x06_firmware_1024_600.h。dw_len表示一次性要写入的寄存器的数量。

byte_read函数用于批量读寄存器,其原型如下:

static FTS_BOOL byte_read(struct i2c_client * client, FTS_BYTE* pbt_buf, FTS_BYTE bt_len)

{

return i2c_read_interface(client, FT5X06_I2C_ADDRESS, pbt_buf, bt_len);

}

static FTS_BOOL i2c_read_interface(struct i2c_client * client, FTS_BYTE bt_ctpm_addr, FTS_BYTE* pbt_buf, FTS_DWRD dw_lenth)

{

int ret;

ret = i2c_master_recv(client, pbt_buf, dw_lenth);//一次性读取多个寄存器

if(ret<=0)

{

printk("[FTS]i2c_read_interface error\n");

return FTS_FALSE;

}

return FTS_TRUE;

}复制代码

这里pbt_buf用于存储批量读取的寄存器值,bt_len表示一次性读取的寄存器数。

通过本实例可以一句话总结出使用linux内核的I2C机制编写I2C设备驱动的方法,首先需要获取一个i2c_client结构体,再通过i2c_transfer函数读写寄存器,或通过i2c_master_send和i2c_master_recv函数批量读写寄存器。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: `gd` 结构体是 `U-Boot` 中的全局数据结构。它包含了 `U-Boot` 运行期间所需要的一些全局变量和指针,例如内存大小、环境变量指针等。 下面是一个 `U-Boot` 中的 `gd` 结构体的定义: ```c struct global_data { ulong flags; /* state flags for this record */ ulong baudrate; /* console baudrate */ ulong cpu_clk; /* CPU clock in Hz */ ulong bus_clk; /* Bus clock in Hz */ ulong mem_clk; /* Mem clock in Hz */ ulong arch_number; /* unique id for this board */ ulong ram_size; /* RAM size */ ulong flash_size; /* Flash size */ ulong flash_offset; /* start of FLASH */ int env_valid; /* true if env area is valid */ char env_buf[ENV_SIZE]; /* buffer for getenv() */ ulong env_addr; /* Address of Environment struct */ ulong env_sect_size; /* size of one complete sector */ ulong env_sectors; /* number of sectors available */ uchar env_flash[CFG_ENV_ADDR_REDUND] __aligned(ARCH_DMA_MINALIGN); /* Image environment in FLASH */ uchar env_redund[CFG_ENV_ADDR_REDUND] __aligned(ARCH_DMA_MINALIGN); /* Image redundant environment in FLASH*/ bd_t *bd; /* board description pointer */ }; ``` `gd` 结构体中的变量和指针的具体含义可以参考注释。 ### 回答2: Linux中的gd结构体是指全局数据结构(global data structure),用于存储截至到当前时间点的系统全局信息。gd结构体位于内存中的一个固定地址,它是Linux内核启动时创建的,包含了许多与系统运行相关的信息。gd结构体中包含了物理内存的起始地址和大小、设备树的地址、文件系统根目录、驱动程序的初始化函数列表等。 而U-boot中的gd结构体是指全局数据结构(global data structure),用于存储与启动过程相关的全局信息。U-boot是一种开源的嵌入式系统引导加载器,用于加载运行操作系统或其他应用程序。gd结构体存储了关于启动和引导过程的重要信息,如内存布局、启动设备、命令行参数、环境变量等。gd结构体是在U-boot启动时动态创建的,通过它可以在运行过程中获取和设置各种全局变量,以支持系统的引导和启动流程。 无论是在Linux还是U-boot中,gd结构体都扮演着重要的角色,它提供了一个全局的数据存储空间,方便在系统运行过程中存储和访问各种全局变量。通过gd结构体,可以在系统的整个生命周期内记录和获取运行时的全局信息,为系统的正常运行和用户的操作提供了必要的数据支持。 ### 回答3: gd结构体Linux内核中的一个重要数据结构,它在内核初始化期间被创建,用于管理全局数据。它定义在头文件"include/linux/gd.h"中。gd结构体包含了许多字段,下面是一些重要的字段及其功能: 1. flags:包含了一些标志位,用于表示一些系统状态信息,比如是启动时的标志。 2. env_addr:存储着环境变量的起始地址。 3. env_valid:标志着环境变量的有效性,为1时表示有效。 4. ram_base、ram_size:存储着内存的起始地址和大小。 5. arch:用于存储体系结构相关的信息。 6. baudrate:串口通信的波特率。 7. bootfile:引导该系统的启动文件名。 8. boot_device:标志着启动设备的类型和编号。 9. lcd_color_index:存储了LCD显示颜色索引。 除了上述字段外,gd结构体还包含了一些用于存储引导时加载的设备树、已加载内核的前一级地址以及其他一些系统配置信息的字段。 而在U-boot中,也有类似的gd结构体用于管理全局数据。U-boot的gd结构体提供了与Linux内核中的gd结构体类似的功能,并且额外包含一些U-boot特有的字段。这些字段用于记录U-boot启动的相关信息,比如启动设备类型和编号、启动设备的起始地址、U-boot的启动参数等。 总的来说,gd结构体Linux和U-boot中是非常重要的数据结构,它们提供了全局数据的管理和存储,方便操作系统和引导程序进行相关的启动和配置。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值