Linux 编写I2C驱动简单模板
I2C驱动简单模板
- 1、开发环境
- 2、驱动编写模板
- 2.1 加载/卸载模块
- 2.2 定义一个i2c_driver结构体和驱动自身使用的结构体
- 2.3 设备探测函数和设备移除函数
- 2.4 获取设备树中的参数值
- 2.5 读取/写入数据
- 3.总结
1、开发环境
Linux版本:Linux3.10以上的内核即可
适用驱动:I2C类型的驱动
2、驱动编写模板
2.1 加载/卸载模块
static int __init xxx_init(void)
{
return i2c_add_driver(xxx_i2c_driver);
}
static void __exit xxx_exit(void)
{
i2c_del_driver(&xxx_i2c_driver);
}
2.2 定义一个i2c_driver结构体和驱动自身使用的结构体
struct i2c_device_id of_i2c_match[] = {
{I2C_DEVICE_NAME,0},
{}
};
//设备树匹配
struct of_device_id of_device_match[] = {
{.compatible = "xxxx_dts_name"},
{}
};
static struct i2c_driver xxx_i2c_driver = {
.driver = {
.owner = THIS_MODULE,
.of_match_table = of_device_match,
.name = I2C_DEVICE_NAME,
},
.id_table = of_i2c_match,
.probe = xxx_i2c_probe,
.remove = xxx_i2c_remove,
};
struct xxx_dev
{
struct device *dev;
struct i2c_client *client;
#ifdef
struct regmap *regmap;
#endif
};
2.3 设备探测函数和设备移除函数
static int xxx_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
struct xxx_dev *lt;
struct device *dev = &client->dev;
pdev = kzalloc(sizeof(*pdev),GFP_KERNEL);
if (pdev == NULL)
{
printk("kzalloc GFP_KERNEL Failed!\n");
return -ENOMEM;
}
//判断该I2C总线是否可以使用
if (! i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
{
printk("i2c check Failed!\n");
return -EINVAL;
}
lt->dev = dev;
lt->client = client;
//如果有设备树节点的话
if(dev->of_node)
{
if (get_parse_dt(&client->dev))
{
printk("get dt info Failed!\n");
return -EINVAL;
}
}
#ifdef REGMAP
//如果通过regmap_write/regmap_read访问设备的寄存器的话
struct regmap * regmap;//放在函数开头
regmap = devm_regmap_init_i2c(client, &xxx_i2c_regmap_config);
lt->regmap = regmap;
#endif
i2c_set_clientdata(client, lt);//
return 0;
}
int xxx_i2c_remove(struct i2c_client *client)
{
return 0;
}
2.4 获取设备树中的参数值
static bool get_parse_dt(struct device *dev)
{
struct device_node *np = dev->of_node;
int ret = 0;
int value;
//使用一些内核函数接口去获取设备树定义的值
//如gpio
int gpio = of_get_named_gpio(np, "xxx_gpio", 0);
if (gpio)
{
ret = gpio_request(gpio, "xxx_gpio");
if (ret)
{
printk("Failed to request xxx_gpio !\n");
return false;
}
}
//获取数值
of_property_read_u32(np, "xxx_name", &value);
return true;
}
2.5 读取/写入数据
这种方法适合需要初始化或读写比较多寄存器的设备
#ifdef
//如果通过regmap_write/regmap_read访问设备的寄存器的话
static const struct regmap_config xxx_i2c_regmap_config = {
.reg_bits = 8;
.val_bits = 8;
.max_register = 0xff;
};
void read_xxx_register(struct i2c_client *client)
{
int value;
struct xxx_dev *lt = i2c_get_clientdata(client);
regmap_read (lt->regmap, 0x00, value);
}
void write_xxx_register(struct i2c_client *client)
{
struct xxx_dev *lt = i2c_get_clientdata(client);
regmap_write(lt->regmap, 0x00 , 0x00);
}
#endif
第二种方法:第一种方法通过了内核相关函数调用i2c_transfer,现在是直接使用i2c_transfer函数
u32 i2c_read_byte(struct i2c_client *client, u8 *buf, u32 len)
{
#define EXECUTE_CNT 2
#define I2C_ADDR_LENGTH 1 int ret;
struct i2c_msg msg[2];
msg[0].flag = !I2C_M_RD;
msg[0].addr = client->addr;
msg[0].len = I2C_ADDR_LENGTH;
msg[0].buf = &buf[0];
msg[1].flag = I2C_M_RD;
msg[1].addr = client->addr;
msg[1].len = len - I2C_ADDR_LENGTH;
msg[1].buf = &buf[I2C_ADDR_LENGTH];
ret = i2c_transfer(client->adapter, &msg, EXECUTE_CNT);
if (ret == EXECUTE_CNT)
{
for(int i = I2C_ADDR_LENGTH; i < len; i++)
printk("read success!,%d-%d\n",i,buf[i]);
}
}
u32 i2c_write_byte(struct i2c_client* client,u8 *buf, s32 len)
{
#define EXECUTE_CNT 1
int ret;
struct i2c_msg msg;
msg.flag = !I2C_M_RD;//读
msg.addr = client->addr;
msg.len = len;
msg.buf = buf;
ret = i2c_transfer(client->adapter, &msg , EXECUTE_CNT);
if (ret == EXECUTE_CNT)
{
printk("write success!\n");
}
}
3.总结
为了方便以后写关于I2C的驱动和调试驱动。后面调试和编写遇到不同的情况再继续完善吧。