【摘要】 pcf8591是一个iic总线接口的adc/dac转换芯片,功能比较强大,这篇文章就介绍在linux系统里如何编写一个pcf8591的驱动,完成adc数据采集,dac数据输出。
1. pcf8591介绍 pcf8591是一个iic总线接口的adc/dac转换芯片,功能比较强大,这篇文章就介绍在linux系统里如何编写一个pcf8591的驱动,完成adc数据采集,dac数据输出。
下面是pcf8591的官方介绍-摘自中文手册:
pcf8591是具有i2c总线接口的8位a/d及d/a转换器。有4路a/d转换输入,1路d/a模拟输出。这就是说,它既可以作a/d转换也可以作d/a转换,a/d转换为逐次比较型。
pcf8591采用典型的i2c总线接口器件寻址方法,即总线地址由器件地址、引脚地址和方向位组成。飞利蒲公司规定a/d器件地址为1001。引脚地址为a2a1a0,其值由用户选择,因此i2c系统中最多可接8个具有i2c总线接口的a/d器件。地址的最后一位为方向位r/w,当主控器对a/d器件进行读操作时为1,进行写操作时为0。总线操作时,由器件地址、引脚地址和方向位组成的从地址为主控器发送的第一字节。
2. 硬件环境介绍 当前的开发板采用友善之臂tiny4412开发板,采用三星的exynos-4412芯片,下面是开发板与pcf8591的硬件连线图:
下面是pcf8591的原理图,介绍了每个引脚详细功能:
3. 驱动案例代码 下面是pcf8591的驱动代码,采用iic子系统框架编程,驱动代码分为设备端、驱动端两部分。
驱动框架采用杂项字符设备完成注册,给应用层提供访问的设备节点,详细的说明在代码路写了完整的注释。
3.1 驱动端代码#include #include #include #include #include /*注册中断相关*/#include /*中断边沿类型定义*/#include /*中断io口定义*/#include /*工作队列相关*/#include /*互斥信号量头文件*/#include #include /*杂项设备相关结构体*/#include /*文件操作集合头文件*/#include /*使用copy_to_user和copy_from_user*/#define ain0 0x40#define ain1 0x41#define ain2 0x42#define ain3 0x43static struct i2c_client *pcf8591_client; /*iic设备总线*//*读取pcf8591 adc数据*/unsigned char pcf8591_readadc(unsigned char ch){ return i2c_smbus_read_byte_data(pcf8591_client,ch); }static int pcf8591_open(struct inode *my_inode, struct file *my_file){ return 0;}static ssize_t pcf8591_read(struct file *my_file, char __user *buf, size_t my_len, loff_t * my_loff){ unsigned char data=pcf8591_readadc(ain0); copy_to_user(buf,&data,1); data=pcf8591_readadc(ain1); printk(1:%d\r\n,data); data=pcf8591_readadc(ain2); printk(2:%d\r\n,data); data=pcf8591_readadc(ain3); printk(3:%d\r\n,data); return 0;}static ssize_t pcf8591_write(struct file *my_file, const char __user *buf, size_t my_len, loff_t *my_loff){ //dac输出 i2c_smbus_write_byte_data(pcf8591_client,0x40,100); return 0;}static int pcf8591_release(struct inode *my_inode, struct file *my_file){ return 0;}/*定义一个文件操作集合结构体*/static struct file_operations ops_pcf8591={ .owner = this_module, .read=pcf8591_read, /*读函数-被应用层read函数调用*/ .write=pcf8591_write, /*写函数-被应用层write函数调用*/ .open=pcf8591_open, /*打开函数-被应用层open函数调用*/ .release=pcf8591_release, /*释放函数*/};/*定义一个杂项设备结构体*/static struct miscdevice misce_pcf8591={ .minor =misc_dynamic_minor, /*自动分配次设备号*/ .name = tiny4412_pcf8591, /*名称 在dev/目录下边可以找到*/ .fops = &ops_pcf8591, /*文件操作集合*/};static int i2c_probe(struct i2c_client *client, const struct i2c_device_id *device_id)//匹配成功时调用{ pcf8591_client=client; printk(驱动端iic匹配的地址=0x%x\n,client->addr); /* 检测适配器是否支持smbus字节读写函数 */ if(i2c_check_functionality(client->adapter, i2c_func_smbus_byte_data)) { printk(适配器支持smbus字节读写函数\n); } /*注册*/ misc_register(&misce_pcf8591); return 0;}static int i2c_remove(struct i2c_client *client){ misc_deregister(&misce_pcf8591);/*注销*/ printk(i2c_驱动端卸载成功!!!\n); return 0;}/*iic驱动端*/static const struct i2c_device_id i2c_id[] ={ {tiny4412_pcf8591,0},//设备端的名字为my_pcf8591,后面的表示需要私有数据 {}};struct i2c_driver i2c_drv ={ .driver= { .name = pcf8591, .owner = this_module, }, .probe = i2c_probe, .remove = i2c_remove, .id_table = i2c_id,};static int __init i2c_drv_init(void){ i2c_add_driver(&i2c_drv);//向iic总线注册一个驱动 return 0;}static void __exit i2c_drv_exit(void)//平台设备端的出口函数{ i2c_del_driver(&i2c_drv);}module_init(i2c_drv_init);module_exit(i2c_drv_exit);module_license(gpl); 3.2 设备端代码#include #include #include #include /*获取总线*/struct i2c_adapter *i2c_adap; //获取到的总线存放在这个结构体static struct i2c_client *i2cclient = null;//pcf8591固定地址 b1001//pcf8591硬件地址 b000//组合:b1001000 = 0x48//注意:iic标准地址是7位static unsigned short const i2c_addr_list[] = { 0x48, i2c_client_end};//地址队列static int __init i2c_dev_init(void){ struct i2c_board_info i2c_info;//设备描述结构体,里面存放着欲设备的名字还有地址 i2c_adap = i2c_get_adapter(0); //获取0号总线 if(i2c_adap==null) { printk(pcf8591--ii总线0 获取失败!!\n); } memset(&i2c_info,0,sizeof(struct i2c_board_info));//把设备描述结构体清空结构体清空 strlcpy(i2c_info.type,tiny4412_pcf8591,i2c_name_size);//把设备的名字赋值给i2c_info i2cclient = i2c_new_probed_device(i2c_adap,&i2c_info,i2c_addr_list,null); if(i2cclient==null) { printk(pcf8591 0x%x:地址不可用!!\n,i2c_addr_list[0]); } i2c_put_adapter(i2c_adap); printk(pcf8591_dev_init初始化成功!!\n); return 0;}static void __exit i2c_dev_exit(void)//平台设备端的出口函数{ /*注销设备*/ i2c_unregister_device(i2cclient); i2c_release_client(i2cclient); printk(pcf8591_dev_exit ok!!\n);}module_init(i2c_dev_init);module_exit(i2c_dev_exit);module_license(gpl); 3.3 应用层代码#include #include #include #include /*pcf8591 应用层测试代码*/int main(int argc,char **argv){ unsigned char data=0; int fp; float tmp; // tmp=5.34v 0.34 int a; int b; fp=open(/dev/tiny4412_pcf8591,o_rdwr); if(fp<0) /*判断文件是否打开成功*/ { printf(pcf8591 driver open error!\n); return -1; } while(1) { read(fp,&data,1); write(fp,&data,1); printf(adc1=%d\n,data); tmp=(float)data*(5.0/255); //电压= 采集的数字量*(参考电压/分辨率); a=tmp; //a=5 tmp=5.3 b=(int)((tmp-a)*1000); //b=0.34 printf(adc1=%d.%dv\r\n,(int)a,(int)b); sleep(1); } close(fp); return 0;}
英伟达宣布GTX10606GB及以上显卡即将支持光追效果
“携号转网”办理全流程
旭明推出具有优异色温控制技术的C35 LED系列
华为畅享10Plus正式发布 售价1499元起
OPPO R9s Plus旗舰级智能手机感受,12月8日线下发售3499元
Linux驱动开发-编写PCF8591(ADC)芯片驱动
AMD与Intel新处理器接口成迷 或为未来产品预留
关于光伏逆变器5种最常见报错的分析和介绍
自动驾驶汽车在公共道路上行驶,但仍存异常驾驶行为
在电路设计中驱动器与接收器的选择解决方案
为何开关频率越高,变频器输出波形质量越好?
我们可以从韩国在5G方面的发展中学习什么
创维等IBM多家客户荣获鼎革奖 打造数字化转型创新典范
昨天HTCU11预约人数达到12万,6g+128g+骁龙835配置无敌,华为三星恐慌了!
凯迪拉克超级智能驾驶系统正式发布
应用光纤控制技术和网络控制技术的固定调光器
新能源作为汽车未来发展方向,正逐渐改变着市场的规则
浙大引进哈佛的VR课程,用于探索金字塔的技术与历史
转盘型数粒仪是什么,有哪些特点
对STM32内部FLASH编程时遇到的ADC异常问题