下面是PCF8591的介紹:
PCF8591 是一個(gè)單片集成、單獨(dú)供電、低功耗、8-bit CMOS數(shù)據(jù)獲取器件。PCF8591 具有 4 個(gè)模擬輸入、1 個(gè)模擬輸出和 1個(gè)串行 I2C 總線接口。PCF8591 的 3 個(gè)地址引腳 A0, A1 和 A2 可用于硬件地址編程,允許在同個(gè) I2C 總線上接入 8 個(gè) PCF8591 器件,而無(wú)需額外的硬件。在 PCF8591 器件上輸入輸出的地址、控制和數(shù)據(jù)信號(hào)都是通過(guò)雙線雙向 I2C 總線以串行的方式進(jìn)行傳輸。
下圖是PCF8591的框圖
本篇討論其linux驅(qū)動(dòng)的以下幾種實(shí)現(xiàn)方式
- Hardware Monitoring framework (hwmon)
- 雜項(xiàng)字符設(shè)備 (misc)
- 通過(guò)memmap, IOmap在用戶空間直接操作processor i2c
- Industrial IO framework (iio)
通過(guò)hwmon框架實(shí)現(xiàn)
在linux中已經(jīng)支持通過(guò)hwmon框架方式實(shí)現(xiàn)pcf8591驅(qū)動(dòng)的代碼。
下面只貼出部分代碼,具體請(qǐng)參見(jiàn)鏈接
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);
在編譯內(nèi)核modules需要把模塊添加進(jìn)去,或者單獨(dú)編譯好模塊再?gòu)?fù)制到樹(shù)莓派版中。
下圖為通過(guò)menuconfig配置模塊: 圖中“Philips PCF8591 ADC/DAC"選項(xiàng)
安裝模塊驅(qū)動(dòng)
sudo insmod pcf8591.ko
安裝后可以在sysfs下查看,/sys/bus/i2c/drivers下多了一個(gè)pcf8591, 見(jiàn)下圖:
文中提到了hwmon設(shè)備常通過(guò)i2cbus探測(cè)的方式。但是我們卻可以看到pcf8591.c中并沒(méi)有detect函數(shù):
static struct i2c_driver pcf8591_driver = {
.driver = {
.name = "pcf8591",
},
.probe = pcf8591_probe,
.remove = pcf8591_remove,
.id_table = pcf8591_id,
};
這是因?yàn)閜cf8591沒(méi)有“制造商和設(shè)備ID寄存器”。
因此我們可以通過(guò)方式1,方式2和方式4
- 方法1:靜態(tài)聲明I2C設(shè)備
- 通過(guò)devicetree聲明I2C設(shè)備
- 在板級(jí)文件中聲明I2C設(shè)備
- 方法2:顯式實(shí)例化設(shè)備
- 方法3:對(duì)某些設(shè)備進(jìn)行I2C總線探測(cè)
- 方法4:從用戶空間實(shí)例化
為了避免麻煩,這里就選擇方法4。
先查看地址,然后通過(guò)sysfs, new_device的方式實(shí)例化(參見(jiàn)上篇)
實(shí)例化成功后,如上圖,便可以通過(guò)sysfs來(lái)獲取adc的值,以及設(shè)置dac的值。
cat /sys/class/hwmon/hwmon1/device/in0_input
**misc驅(qū)動(dòng)框架實(shí)現(xiàn) **
misc的意思是混合、雜項(xiàng)的,因此MISC驅(qū)動(dòng)也叫做雜項(xiàng)驅(qū)動(dòng),也就是當(dāng)我們板子上的某些外設(shè)無(wú)法進(jìn)行分類(lèi)的時(shí)候就可以使用MISC驅(qū)動(dòng)。misc設(shè)備也是一個(gè)字符設(shè)備,在misc的初始化函數(shù)中注冊(cè)了一個(gè)字符設(shè)備,主設(shè)備號(hào)為MISC_MAJOR (10)。
驅(qū)動(dòng)分為兩部分:i2c設(shè)備驅(qū)動(dòng),i2c設(shè)備顯式實(shí)例化
在i2c設(shè)備顯示實(shí)例化中調(diào)用,i2c_new_probed_device函數(shù)
i2c_new_probed_device(i2c_adap,&i2c_info,i2c_addr_list,NULL);
在某些設(shè)備中,比如需要后期插入i2c模塊板,而預(yù)先不知道I2C總線的編號(hào),則可以顯式地實(shí)例化I2C設(shè)備。這是通過(guò)填充結(jié)構(gòu)體i2c_board_info并調(diào)用i2c_new_client_device()來(lái)完成。
如下圖,分別將,i2c設(shè)備驅(qū)動(dòng),i2c設(shè)備顯式實(shí)例化,編譯成pcf8591.ko 和pcf8591_dev.ko, 然后通過(guò)Insmod加載
再另外編寫(xiě)應(yīng)用代碼,open misc設(shè)備,進(jìn)行讀寫(xiě)操作。
通過(guò)memmap, IOmap在用戶空間直接操作processor i2c
樹(shù)莓派的一些庫(kù),如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設(shè)備驅(qū)動(dòng),在應(yīng)用代碼中進(jìn)行1ms采樣時(shí),cpu的占用率5.2%。
而使用bcm2835庫(kù)進(jìn)行10ms周期的采樣時(shí),cpu的占用率52.1%。
IIO
IIO 全稱(chēng)是 Industrial I/O,當(dāng)你使用的傳感器本質(zhì)是 ADC 或 DAC 器件的時(shí)候,可以?xún)?yōu)先考慮使用 IIO 驅(qū)動(dòng)框架。比如常用的陀螺儀、加速度計(jì)、電壓/電流測(cè)量芯片、光照傳感器、壓力傳感器等內(nèi)部都是有個(gè) ADC,內(nèi)部 ADC 將原始的模擬數(shù)據(jù)轉(zhuǎn)換為數(shù)字量,然后通過(guò)其他的通信接口,比如 IIC、SPI 等傳輸給 SOC。Linux 內(nèi)核為了管理這些日益增多的 ADC 類(lèi)傳感器,特地推出了 IIO 子系統(tǒng)。
iio 支持多種標(biāo)準(zhǔn)的 Linux 設(shè)備訪問(wèn)接口:char device, sysfs, configfs, debugfs。
IIO的4種接口
1). sysfs interface
- /sys/bus/iio/devices/iio:deviceX;
- 可用于配置 /dev/iio:deviceX 接口的 events / data
- 可用于輪循的方式低速地直接讀/寫(xiě) IIO 設(shè)備;
- Documentation/ABI/testing/sysfs-bus-iio;
2). character device
- /dev/iio:deviceX,該接口在 IIO 子系統(tǒng)里是可選非必要的;
- 標(biāo)準(zhǔn)的文件 IO API: open(), read(), write(), close().
- 用于讀取 events 和 data;
3). configfs
- 用于配置額外的 IIO 特性,例如:軟件 triggers 或者 hrtimer triggers;
- 詳細(xì)說(shuō)明:
- Documentation/ABI/testing/configfs-iio;
- Documentation/iio/iio_configfs.txt;
4). debugfs
- 一些調(diào)試功能,例如 direct_reg_access 節(jié)點(diǎn)可用于讀寫(xiě)寄存器;
具體的代碼實(shí)現(xiàn)便不再花時(shí)間了,可以參考drivers下iio部分代碼。
-
adc
+關(guān)注
關(guān)注
99文章
6705瀏覽量
549183 -
CMOS器件
+關(guān)注
關(guān)注
0文章
72瀏覽量
11881 -
DAC芯片
+關(guān)注
關(guān)注
1文章
34瀏覽量
14969 -
Linux驅(qū)動(dòng)
+關(guān)注
關(guān)注
0文章
43瀏覽量
10248 -
樹(shù)莓派
+關(guān)注
關(guān)注
121文章
2005瀏覽量
107446 -
PCF8591芯片
+關(guān)注
關(guān)注
2文章
8瀏覽量
7464
發(fā)布評(píng)論請(qǐng)先 登錄
PCF8591 DA轉(zhuǎn)換
藍(lán)橋杯單片機(jī)——PCF8591 ADC/DAC模塊 精選資料分享
PCF8591是什么?怎樣去設(shè)計(jì)PCF8591電路?
怎樣通過(guò)pcf8591芯片實(shí)現(xiàn)AD/DA轉(zhuǎn)換
PCF8591的相關(guān)資料推薦
PCF8591 ADC和DAC芯片的數(shù)據(jù)手冊(cè)免費(fèi)下載

微雪電子ADC DAC AD DA轉(zhuǎn)換PCF8591 PCF8591T簡(jiǎn)介

將PCF8591 ADC與Arduino連接的方法

Linux驅(qū)動(dòng)開(kāi)發(fā)-編寫(xiě)PCF8591(ADC)芯片驅(qū)動(dòng)

評(píng)論