下面是PCF8591的介绍:
PCF8591 是一个单片集成、单独供电、低功耗、8-bit CMOS数据获取器件。PCF8591 具有 4 个模拟输入、1 个模拟输出和 1个串行 I2C 总线接口。PCF8591 的 3 个地址引脚 A0, A1 和 A2 可用于硬件地址编程,允许在同个 I2C 总线上接入 8 个 PCF8591 器件,而无需额外的硬件。在 PCF8591 器件上输入输出的地址、控制和数据信号都是通过双线双向 I2C 总线以串行的方式进行传输。
下图是PCF8591的框图
本篇讨论其linux驱动的以下几种实现方式
- Hardware Monitoring framework (hwmon)
- 杂项字符设备 (misc)
- 通过memmap, IOmap在用户空间直接操作processor i2c
- Industrial IO framework (iio)
通过hwmon框架实现
在linux中已经支持通过hwmon框架方式实现pcf8591驱动的代码。
下面只贴出部分代码,具体请参见链接
static int pcf8591_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct pcf8591_data *data;
int err;
data = devm_kzalloc(&client- >dev, sizeof(struct pcf8591_data),
GFP_KERNEL);
if (!data)
return -ENOMEM;
i2c_set_clientdata(client, data);
mutex_init(&data- >update_lock);
/* Initialize the PCF8591 chip */
pcf8591_init_client(client);
/* Register sysfs hooks */
err = sysfs_create_group(&client- >dev.kobj, &pcf8591_attr_group);
if (err)
return err;
/* Register input2 if not in "two differential inputs" mode */
if (input_mode != 3) {
err = device_create_file(&client- >dev, &dev_attr_in2_input);
if (err)
goto exit_sysfs_remove;
}
/* Register input3 only in "four single ended inputs" mode */
if (input_mode == 0) {
err = device_create_file(&client- >dev, &dev_attr_in3_input);
if (err)
goto exit_sysfs_remove;
}
data- >hwmon_dev = hwmon_device_register(&client- >dev);
if (IS_ERR(data- >hwmon_dev)) {
err = PTR_ERR(data- >hwmon_dev);
goto exit_sysfs_remove;
}
return 0;
exit_sysfs_remove:
sysfs_remove_group(&client- >dev.kobj, &pcf8591_attr_group_opt);
sysfs_remove_group(&client- >dev.kobj, &pcf8591_attr_group);
return err;
}
static const struct i2c_device_id pcf8591_id[] = {
{ "pcf8591", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, pcf8591_id);
static struct i2c_driver pcf8591_driver = {
.driver = {
.name = "pcf8591",
},
.probe = pcf8591_probe,
.remove = pcf8591_remove,
.id_table = pcf8591_id,
};
static int __init pcf8591_init(void)
{
if (input_mode < 0 || input_mode > 3) {
pr_warn("invalid input_mode (%d)n", input_mode);
input_mode = 0;
}
return i2c_add_driver(&pcf8591_driver);
}
static void __exit pcf8591_exit(void)
{
i2c_del_driver(&pcf8591_driver);
}
MODULE_AUTHOR("Aurelien Jarno < aurelien@aurel32.net >");
MODULE_DESCRIPTION("PCF8591 driver");
MODULE_LICENSE("GPL");
module_init(pcf8591_init);
module_exit(pcf8591_exit);
在编译内核modules需要把模块添加进去,或者单独编译好模块再复制到树莓派版中。
下图为通过menuconfig配置模块: 图中“Philips PCF8591 ADC/DAC"选项
安装模块驱动
sudo insmod pcf8591.ko
安装后可以在sysfs下查看,/sys/bus/i2c/drivers下多了一个pcf8591, 见下图:
文中提到了hwmon设备常通过i2cbus探测的方式。但是我们却可以看到pcf8591.c中并没有detect函数:
static struct i2c_driver pcf8591_driver = {
.driver = {
.name = "pcf8591",
},
.probe = pcf8591_probe,
.remove = pcf8591_remove,
.id_table = pcf8591_id,
};
这是因为pcf8591没有“制造商和设备ID寄存器”。
因此我们可以通过方式1,方式2和方式4
- 方法1:静态声明I2C设备
- 通过devicetree声明I2C设备
- 在板级文件中声明I2C设备
- 方法2:显式实例化设备
- 方法3:对某些设备进行I2C总线探测
- 方法4:从用户空间实例化
为了避免麻烦,这里就选择方法4。
先查看地址,然后通过sysfs, new_device的方式实例化(参见上篇)
实例化成功后,如上图,便可以通过sysfs来获取adc的值,以及设置dac的值。
cat /sys/class/hwmon/hwmon1/device/in0_input
**misc驱动框架实现 **
misc的意思是混合、杂项的,因此MISC驱动也叫做杂项驱动,也就是当我们板子上的某些外设无法进行分类的时候就可以使用MISC驱动。misc设备也是一个字符设备,在misc的初始化函数中注册了一个字符设备,主设备号为MISC_MAJOR (10)。
驱动分为两部分:i2c设备驱动,i2c设备显式实例化
在i2c设备显示实例化中调用,i2c_new_probed_device函数
i2c_new_probed_device(i2c_adap,&i2c_info,i2c_addr_list,NULL);
在某些设备中,比如需要后期插入i2c模块板,而预先不知道I2C总线的编号,则可以显式地实例化I2C设备。这是通过填充结构体i2c_board_info并调用i2c_new_client_device()来完成。
如下图,分别将,i2c设备驱动,i2c设备显式实例化,编译成pcf8591.ko 和pcf8591_dev.ko, 然后通过Insmod加载
再另外编写应用代码,open misc设备,进行读写操作。
通过memmap, IOmap在用户空间直接操作processor i2c
树莓派的一些库,如bcm2835, wiringpi等
/* Open the master /dev/mem device */
if ((memfd = open("/dev/mem", O_RDWR | O_SYNC) ) < 0)
{
fprintf(stderr, "bcm2835_init: Unable to open /dev/mem: %sn",
strerror(errno)) ;
goto exit;
}
/* Base of the peripherals block is mapped to VM */
bcm2835_peripherals = mapmem("gpio", bcm2835_peripherals_size, memfd, (off_t)bcm2835_peripherals_base);
if (bcm2835_peripherals == MAP_FAILED) goto exit;
/* Now compute the base addresses of various peripherals,
// which are at fixed offsets within the mapped peripherals block
// Caution: bcm2835_peripherals is uint32_t*, so divide offsets by 4
*/
bcm2835_gpio = bcm2835_peripherals + BCM2835_GPIO_BASE/4;
bcm2835_pwm = bcm2835_peripherals + BCM2835_GPIO_PWM/4;
bcm2835_clk = bcm2835_peripherals + BCM2835_CLOCK_BASE/4;
bcm2835_pads = bcm2835_peripherals + BCM2835_GPIO_PADS/4;
bcm2835_spi0 = bcm2835_peripherals + BCM2835_SPI0_BASE/4;
bcm2835_bsc0 = bcm2835_peripherals + BCM2835_BSC0_BASE/4; /* I2C */
bcm2835_bsc1 = bcm2835_peripherals + BCM2835_BSC1_BASE/4; /* I2C */
bcm2835_st = bcm2835_peripherals + BCM2835_ST_BASE/4;
bcm2835_aux = bcm2835_peripherals + BCM2835_AUX_BASE/4;
bcm2835_spi1 = bcm2835_peripherals + BCM2835_SPI1_BASE/4;
效率比较
下图为使用misc设备驱动,在应用代码中进行1ms采样时,cpu的占用率5.2%。
而使用bcm2835库进行10ms周期的采样时,cpu的占用率52.1%。
IIO
IIO 全称是 Industrial I/O,当你使用的传感器本质是 ADC 或 DAC 器件的时候,可以优先考虑使用 IIO 驱动框架。比如常用的陀螺仪、加速度计、电压/电流测量芯片、光照传感器、压力传感器等内部都是有个 ADC,内部 ADC 将原始的模拟数据转换为数字量,然后通过其他的通信接口,比如 IIC、SPI 等传输给 SOC。Linux 内核为了管理这些日益增多的 ADC 类传感器,特地推出了 IIO 子系统。
iio 支持多种标准的 Linux 设备访问接口:char device, sysfs, configfs, debugfs。
IIO的4种接口
1). sysfs interface
- /sys/bus/iio/devices/iio:deviceX;
- 可用于配置 /dev/iio:deviceX 接口的 events / data
- 可用于轮循的方式低速地直接读/写 IIO 设备;
- Documentation/ABI/testing/sysfs-bus-iio;
2). character device
- /dev/iio:deviceX,该接口在 IIO 子系统里是可选非必要的;
- 标准的文件 IO API: open(), read(), write(), close().
- 用于读取 events 和 data;
3). configfs
- 用于配置额外的 IIO 特性,例如:软件 triggers 或者 hrtimer triggers;
- 详细说明:
- Documentation/ABI/testing/configfs-iio;
- Documentation/iio/iio_configfs.txt;
4). debugfs
- 一些调试功能,例如 direct_reg_access 节点可用于读写寄存器;
具体的代码实现便不再花时间了,可以参考drivers下iio部分代码。
-
adc
+关注
关注
98文章
6429浏览量
544039 -
CMOS器件
+关注
关注
0文章
71浏览量
11500 -
DAC芯片
+关注
关注
1文章
32浏览量
14588 -
Linux驱动
+关注
关注
0文章
43浏览量
9950 -
树莓派
+关注
关注
116文章
1697浏览量
105502 -
PCF8591芯片
+关注
关注
2文章
8浏览量
7364
发布评论请先 登录
相关推荐
评论