本文目录
- 前述:
- 一、IIC子系统框架
- 二、I2C设备驱动层
- 1. i2c_client编写(C语言版-旧内核)
- 2. i2c_client编写(设备树版-新内核)
前述:
对于IIC的基础知识,这里不做过多的介绍,详细情况查看下面的两篇文章。
文章一:超详细!新手必看!STM32基础-IIC串行通信协议-IO口模拟IIC操作BMP180。
文章二:Linux应用编程(四)IIC(获取BMP180温度/气压数据)。
一、IIC子系统框架
二、I2C设备驱动层
1. i2c_client编写(C语言版-旧内核)
这里的i2c_client相当于平台总线的设备层,即描述硬件资源,使用如下:
i2c_client.c
#include <linux/module.h>
#include <linux/init.h>
#include <linux/i2c.h>
struct i2c_adapter *i2c_ada;
struct i2c_board_info ft5x06[] = {
{ I2C_BOARD_INFO("my-ft5x06", 0x38) },
};
static int __init ft5x06_client_init(void) {
struct i2c_client *client;
i2c_ada = i2c_get_adapter(1); //获取i2c1的控制器
if (!i2c_ada) {
printk(KERN_ERR "Failed to get i2c adapter\n");
return -ENODEV;
}
client = i2c_new_device(i2c_ada, ft5x06); //创建一个新设备
if (!client) {
printk(KERN_ERR "Failed to create new i2c device\n");
i2c_put_adapter(i2c_ada);
return -ENODEV;
}
return 0;
}
static void __exit ft5x06_client_exit(void) {
// Put the adapter back after use
i2c_put_adapter(i2c_ada);
}
module_init(ft5x06_client_init);
module_exit(ft5x06_client_exit);
MODULE_LICENSE("GPL");
i2c_driver.c
这里是一个简单示例,为了演示如何匹配。
#include <linux/init.h>
#include <linux/module.h>
#include <linux/i2c.h>
// Probe function
static int ft5x06_probe(struct i2c_client *client, const struct i2c_device_id *id) {
printk(KERN_INFO "This is ft5x06_probe\n");
return 0; // Return 0 to indicate successful probing
}
// Remove function
static int ft5x06_remove(struct i2c_client *client) {
return 0;
}
const struct i2c_device_id table[] = {
{"my-ft5x06", 0},
{}
};
static struct i2c_driver ft5x06_driver = {
.driver = {
.owner = THIS_MODULE,
.name = "my-ft5x06",
},
.probe = ft5x06_probe,
.remove = ft5x06_remove,
.id_table = table, //匹配的设备列表
};
static int __init ft5x06_driver_init(void) {
int ret;
ret = i2c_add_driver(&ft5x06_driver);
if (ret < 0) {
printk(KERN_ERR "i2c_add_driver is error\n");
return ret;
}
return 0;
}
// Module exit function
static void __exit ft5x06_driver_exit(void)
{
i2c_del_driver(&ft5x06_driver);
}
module_init(ft5x06_driver_init);
module_exit(ft5x06_driver_exit);
MODULE_LICENSE("GPL");
2. i2c_client编写(设备树版-新内核)
这部分相当于平台总线的设备层代码,即用于描述硬件。当然可以使用设备树来替代。那么我们在使用设备树描述时该怎么写呢?
答:首先我们确定使用哪个I2C控制器,每个控制器都在设备树通用文件中对应一个节点(内核以有)。假设我们使用i2c控制器1。使用如下所示。
i2c_client设备树节点。
&i2c{ //相当于在i2c1节点下添加节点。
status = "okay"; //使能i2c1控制器
myft5x06:my-ft5x06@38{
compatible="my-ft5x06";
reg=<0x38>;
//下面的内容主要为了演示而添加的,实际并不存在。
//复位引脚
reset-gpio==<&gpio0 RX_PB5 0>; //gpio0 PB5 引脚,初始为低电平。
//中断引脚
interrupt-parent=<&gpio3>; //使用gpio_c中断控制器。
interrupts-gpio = <&gpio3 RX_PA2 0>; //gpio3 PA2引脚,初始为低电平。
interrupts=<36 IRQF_TRIGGER_FALLING>; //中断号为36,触发方式为下降沿触发。
pinctrl-names = "default"; //固定属性名称,这个属性定义了引脚控制器配置的名称,default 表示默认配置。
pinctrl-0 = <&ft5x06_pinctrl>;
};
};
&pinctrl{
ft5x06_pinctrl:ft5x06-pinctrl{ //将两个引脚都复用为GPIO功能。
rockchip,pins = <0 RK_PB5 RK_FUNC_GPIO &pcfg_pull_none>,<3 RK_PA2 RK_FUNC_GPIO &pcfg_pull_none>;
};
}
i2c_driver.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
struct gpio_desc *reset_gpio;
struct gpio_desc *irq_gpio;
extern struct i2c_client *ft5x06_client;
extern int ft5x06_read_reg(u8 addr);
extern void ft5x06_write_reg(u8 addr, u8 data, u16 len);
irqreturn_t interrupt_handler1(int irq, void *arg)
{
printk("interrupt_handler1!\n");
return IRQ_HANDLED;
}
static int ft5x06_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
int value;
int ret;
printk(KERN_INFO "This is ft5x06_probe\n");
ft5x06_client = client;
//获取硬件gpio资源
reset_gpio = gpiod_get_optional(&client->dev, "reset", 0);
if (IS_ERR(reset_gpio)) {
dev_err(&client->dev, "Failed to get GPIO for reset\n");
return PTR_ERR(reset_gpio);
}
irq_gpio = gpiod_get_optional(&client->dev, "interrupts", 0);
if (IS_ERR(irq_gpio)) {
dev_err(&client->dev, "Failed to get GPIO for interrupts\n");
return PTR_ERR(irq_gpio);
}
//复位一次
gpiod_direction_output(reset_gpio, 0);
mdelay(5);
gpiod_direction_output(reset_gpio, 1);
//申请中断
ret = request_irq(client->irq, interrupt_handler1, IRQF_TRIGGER_FALLING, "test_interrupt", client); //这里触发方式要和设备树一致!
if (ret < 0) {
printk("request_irq error!\n");
return ret;
}
//测试iic读写函数
ft5x06_write_reg(0x80, 0x4b, 1);
value = ft5x06_read_reg(0x80);
printk("reg0x80的值:%x\n", value);
return 0;
}
static int ft5x06_remove(struct i2c_client *client)
{
free_irq(client->irq, client);
return 0;
}
static const struct of_device_id ft5x06_table[] = {
{ .compatible = "my-ft5x06" },
{}
};
static struct i2c_driver ft5x06_driver = {
.driver = {
.owner = THIS_MODULE,
.name = "my-ft5x06",
.of_match_table = ft5x06_table, //用于与设备树匹配。
},
.probe = ft5x06_probe,
.remove = ft5x06_remove,
};
static int __init ft5x06_driver_init(void)
{
return i2c_add_driver(&ft5x06_driver);
}
static void __exit ft5x06_driver_exit(void)
{
i2c_del_driver(&ft5x06_driver);
}
module_init(ft5x06_driver_init);
module_exit(ft5x06_driver_exit);
MODULE_LICENSE("GPL");
msg.c
#include <linux/i2c.h>
struct i2c_client *ft5x06_client;
int ft5x06_read_reg(u8 addr)
{
u8 data;
struct i2c_msg msgs[] = {
[0] = {
.addr = ft5x06_client->addr,
.flags = 0,
.buf = &addr,
.len = sizeof(addr),
},
[1] = {
.addr = ft5x06_client->addr,
.flags = I2C_M_RD,
.buf = &data,
.len = sizeof(data),
},
};
i2c_transfer(ft5x06_client->adapter, msgs, 2);
return data;
}
void ft5x06_write_reg(u8 addr, u8 data, u16 len)
{
u8 buff[256];
struct i2c_msg msgs[] = {
[0] = {
.addr = ft5x06_client->addr,
.flags = 0,
.buf = buff,
.len = len + 1, // len + 1 to include the addr byte
},
};
buff[0] = addr;
memcpy(&buff[1], &data ,len);
i2c_transfer(ft5x06_client->adapter, msgs, 1);
}