当前位置:网站首页>Baiwen driving Daquan learning (II) I2C driving
Baiwen driving Daquan learning (II) I2C driving
2022-07-27 06:04:00 【qq_ three million three hundred and twenty-two thousand nine hu】
Baiwen.com drives Daquan learning ( Two )I2C drive
i2c The driver of is divided into two aspects :
- chip I2C The driver of the controller
- I2C Peripheral driver
One 、I2C Peripheral driver
Let's first look at I2C How to write peripheral drivers , We use 100ask The production of STM32MP157 For example, board , There is a three in one sensor module on the board , adopt I2C Protocol to communicate with it .
We refer to at24.c To write a simple I2C Driver framework i2c_drv_example.c, The complete code will be given in Appendix 1
( One ) ordinary I2C Driver framework
First, copy the header file , Then copy the entry function and exit function , As shown below
static int __init at24_init(void)
{
if (!at24_io_limit) {
pr_err("at24: at24_io_limit must not be 0!\n");
return -EINVAL;
}
at24_io_limit = rounddown_pow_of_two(at24_io_limit);
return i2c_add_driver(&at24_driver);
}
module_init(at24_init);
static void __exit at24_exit(void)
{
i2c_del_driver(&at24_driver);
}
module_exit(at24_exit);
The above code is at24.c The entry function and exit function of , In the entry function , call i2c_add_driver Registered a at24_driver The structural variable of , Let's take a look at this at24_driver What is a variable
static struct i2c_driver at24_driver = {
.driver = {
.name = "at24",
.of_match_table = at24_of_match,
.acpi_match_table = ACPI_PTR(at24_acpi_ids),
},
.probe_new = at24_probe,
.remove = at24_remove,
.id_table = at24_ids,
};
It's a i2c_driver Structural variable , There are id_table and of_match_table, These two variables are I2C drive and I2C equipment An important reference for matching . When the device is matched with the driver , Will call i2c_driver Inside probe_new function ( stay 4.9 The kernel is probe function ). stay probe_new Function to complete the allocation of character devices 、 Set up 、 register .
Let's take a look first of_match_table
static const struct of_device_id at24_of_match[] = {
{
.compatible = "atmel,24c00", .data = &at24_data_24c00 },
{
.compatible = "atmel,24c01", .data = &at24_data_24c01 },
{
.compatible = "atmel,24cs01", .data = &at24_data_24cs01 },
{
.compatible = "atmel,24c02", .data = &at24_data_24c02 },
{
.compatible = "atmel,24cs02", .data = &at24_data_24cs02 },
{
.compatible = "atmel,24mac402", .data = &at24_data_24mac402 },
{
.compatible = "atmel,24mac602", .data = &at24_data_24mac602 },
{
.compatible = "atmel,spd", .data = &at24_data_spd },
{
.compatible = "atmel,24c04", .data = &at24_data_24c04 },
{
.compatible = "atmel,24cs04", .data = &at24_data_24cs04 },
{
.compatible = "atmel,24c08", .data = &at24_data_24c08 },
{
.compatible = "atmel,24cs08", .data = &at24_data_24cs08 },
{
.compatible = "atmel,24c16", .data = &at24_data_24c16 },
{
.compatible = "atmel,24cs16", .data = &at24_data_24cs16 },
{
.compatible = "atmel,24c32", .data = &at24_data_24c32 },
{
.compatible = "atmel,24cs32", .data = &at24_data_24cs32 },
{
.compatible = "atmel,24c64", .data = &at24_data_24c64 },
{
.compatible = "atmel,24cs64", .data = &at24_data_24cs64 },
{
.compatible = "atmel,24c128", .data = &at24_data_24c128 },
{
.compatible = "atmel,24c256", .data = &at24_data_24c256 },
{
.compatible = "atmel,24c512", .data = &at24_data_24c512 },
{
.compatible = "atmel,24c1024", .data = &at24_data_24c1024 },
{
.compatible = "atmel,24c2048", .data = &at24_data_24c2048 },
{
/* END OF LIST */ },
};
This is a at24.c Of of_match_table,compatible The general format of the attribute is manufacturer , Product name , We are i2c_drv_example.c Written in
static const struct of_device_id i2c_example_of_match[] = {
{
.compatible = "lite-on,ap3216c", .data = NULL },
{
/* END OF LIST */ },
};
also id_table, We write it in the following form
static const struct i2c_device_id i2c_example_ids[] = {
{
"ap3216c", (long unsigned int)NULL },
{
/* END OF LIST */ }
};
And then put probe Functions and remove Bring the function, too , Change it to our own probe Functions and remove function , Here is only a simple print
static int i2c_example_probe(struct i2c_client *client)
{
printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
return 0;
}
static int i2c_example_remove(struct i2c_client *client)
{
printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
return 0;
}
Put the whole i2c_drv_example.c Translate it into .ko file , And in 157 Installed on , You also need to add a device node manually , To match our i2c_drv_example.c. We use the following command to add i2c Device node
echo ap3216c 0x1e > /sys/bus/i2c/devices/i2c-0/new_device
When you run this command , It will match our driver , And will print out probe Information about the function being called
To delete this i2c The device node is also very simple , Similar commands are as follows
echo 0x1e > /sys/bus/i2c/devices/i2c-0/delete_device
Run this command , Our driver remove The function will be called
We are simple I2C That's all for the driver framework , next , We are going to write and read ap3216c Data driver and test application
( Two )ap3216c The driver
We are i2c_drv_example.c Based on ap3216c The driver , This step is also very simple , It is the routine of character device drive , Assign master equipment number , Create a class , Create device nodes , The core is file_operations Setting of structure variables , Among them open and read Function is the core of our character device driver , Driver code and test code are given in Appendix II .
And then in open and read In the function , You can use i2c-core-smbus.c Inside i2c_smbus_read_byte_data Functions and i2c_smbus_write_byte Function and ap3216c Module to communicate , The whole procedure is also very simple .
Two 、I2C Controller drive
Updating
Appendix a 、 ordinary I2C Driver framework
i2c_drv_example.c
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/mutex.h>
#include <linux/mod_devicetable.h>
#include <linux/bitops.h>
#include <linux/jiffies.h>
#include <linux/property.h>
#include <linux/acpi.h>
#include <linux/i2c.h>
#include <linux/nvmem-provider.h>
#include <linux/regmap.h>
#include <linux/pm_runtime.h>
#include <linux/gpio/consumer.h>
static const struct i2c_device_id i2c_example_ids[] = {
{
"ap3216c", (long unsigned int)NULL },
{
/* END OF LIST */ }
};
static const struct of_device_id i2c_example_of_match[] = {
{
.compatible = "lite-on,ap3216c", .data = NULL },
{
/* END OF LIST */ },
};
MODULE_DEVICE_TABLE(of, i2c_example_of_match);
static int i2c_example_probe(struct i2c_client *client)
{
printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
return 0;
}
static int i2c_example_remove(struct i2c_client *client)
{
printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
return 0;
}
static struct i2c_driver i2c_example_driver = {
.driver = {
.name = "ap3216c",
.of_match_table = i2c_example_of_match,
},
.probe_new = i2c_example_probe,
.remove = i2c_example_remove,
.id_table = i2c_example_ids,
};
static int __init i2c_example_init(void)
{
return i2c_add_driver(&i2c_example_driver);
}
module_init(i2c_example_init);
static void __exit i2c_example_exit(void)
{
i2c_del_driver(&i2c_example_driver);
}
module_exit(i2c_example_exit);
MODULE_AUTHOR("www.100ask.net");
MODULE_LICENSE("GPL");
Appendix 2 、ap3216c The driver
Driver code
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/mutex.h>
#include <linux/mod_devicetable.h>
#include <linux/bitops.h>
#include <linux/jiffies.h>
#include <linux/property.h>
#include <linux/acpi.h>
#include <linux/i2c.h>
#include <linux/nvmem-provider.h>
#include <linux/regmap.h>
#include <linux/pm_runtime.h>
#include <linux/gpio/consumer.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
static int major = 0;
static struct class *ap3216c_class;
static struct i2c_client * ap3216c_client;
int ap3216c_open(struct inode *inode, struct file *file)
{
printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
i2c_smbus_write_byte_data(ap3216c_client, 0, 0x4);
mdelay(20); /* delay 20ms */
i2c_smbus_write_byte_data(ap3216c_client, 0, 0x3);
mdelay(250); /* delay 20ms */
return 0;
}
ssize_t ap3216c_read(struct file *filp, char __user *buf,
size_t size, loff_t *pos)
{
char kernel_buf[6];
int ret;
printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
ret = i2c_smbus_read_word_data(ap3216c_client, 0xA);
kernel_buf[0] = ret & 0xff;
kernel_buf[1] = (ret >> 8) & 0xff;
mdelay(20); /* delay 20ms */
ret = i2c_smbus_read_word_data(ap3216c_client, 0xC);
kernel_buf[2] = ret & 0xff;
kernel_buf[3] = (ret >> 8) & 0xff;
mdelay(20); /* delay 20ms */
ret = i2c_smbus_read_word_data(ap3216c_client, 0xE);
kernel_buf[4] = ret & 0xff;
kernel_buf[5] = (ret >> 8) & 0xff;
mdelay(20); /* delay 20ms */
copy_to_user(buf, kernel_buf, size);
return size;
}
static struct file_operations ap3216c_fops = {
.owner = THIS_MODULE,
.open = ap3216c_open,
.read = ap3216c_read,
};
static const struct i2c_device_id ap3216c_ids[] = {
{
"ap3216c", (long unsigned int)NULL },
{
/* END OF LIST */ }
};
static const struct of_device_id ap3216c_of_match[] = {
{
.compatible = "lite-on,ap3216c", .data = NULL },
{
/* END OF LIST */ },
};
MODULE_DEVICE_TABLE(of, ap3216c_of_match);
static int ap3216c_probe(struct i2c_client *client)
{
printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
ap3216c_client = client;
/* Register character device */
major = register_chrdev(0, "ap3216c", &ap3216c_fops);
ap3216c_class = class_create(THIS_MODULE, "ap3216c");
device_create(ap3216c_class, NULL, MKDEV(major, 0), NULL, "ap3216c"); // /dev/ap3216c
return 0;
}
static int ap3216c_remove(struct i2c_client *client)
{
printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
device_destroy(ap3216c_class, MKDEV(major, 0));
class_destroy(ap3216c_class);
unregister_chrdev(major, "ap3216c");
return 0;
}
static struct i2c_driver ap3216c_driver = {
.driver = {
.name = "ap3216c",
.of_match_table = ap3216c_of_match,
},
.probe_new = ap3216c_probe,
.remove = ap3216c_remove,
.id_table = ap3216c_ids,
};
static int __init ap3216c_init(void)
{
return i2c_add_driver(&ap3216c_driver);
}
module_init(ap3216c_init);
static void __exit ap3216c_exit(void)
{
i2c_del_driver(&ap3216c_driver);
}
module_exit(ap3216c_exit);
MODULE_AUTHOR("www.100ask.net");
MODULE_LICENSE("GPL");
Test code
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
/* ./ap3216c_test <i2c_bus_num> r */
int main(int argc, char **argv)
{
int fd = 0;
char buf[6];
int ret = 0;
if(argc != 3)
{
printf("Usage:\n");
printf("%s <i2c_bus_num> r\n", argv[0]);
return -1;
}
fd = open("/dev/ap3216c", O_RDONLY);
if(fd < 0)
{
printf("Can't open /dev/ap3216c\n");
return -1;
}
ret = read(fd, buf, 6);
if(ret < 0)
{
printf("Can't read /dev/ap3216c\n");
return 0;
}
printf("IR: %02x %02x\n", buf[0], buf[1]);
printf("ALS: %02x %02x\n", buf[2], buf[3]);
printf("PS: %02x %02x\n", buf[4], buf[5]);
return 0;
}
边栏推荐
猜你喜欢
随机推荐
Digital image processing -- Chapter 9 morphological image processing
【头歌】重生之我在py入门实训中(9):异常处理
制作视频特效必备工具:NUKE 13
4.张量数据类型和创建Tensor
Gbase 8C - SQL reference 6 SQL syntax (6)
Gbase 8C - SQL reference 6 SQL syntax (14)
System Design的相关准备材料
Day 2. Depressive symptoms, post-traumatic stress symptoms and suicide risk among graduate students
ps 2022 六月更新,都新增了哪些功能
小技巧-彻底删除U盘中的文件
Day 17.The role of news sentiment in oil futures returns and volatility forecasting
operator() 用法之一
代码随想录笔记_哈希_242有效的字母异位词
Emoji Emoji for text emotion analysis -improving sentimental analysis accuracy with Emoji embedding
数字图像处理第五章——图像复原与重建
Live Home 3D Pro室内家居设计工具
Digital image processing Chapter 8 - image compression
What has been updated in the Chinese version of XMIND mind map 2022 v12.0.3?
【头歌】重生之我在py入门实训中(5):列表
Gbase 8C - SQL reference 6 SQL syntax (4)









