1、AP3216簡(jiǎn)介
AP3216C 芯片集成了光強(qiáng)傳感器( ALS: Ambient Light Sensor),接近傳感器( PS: Proximity Sensor),還有一個(gè)紅外 LED( IR LED)。
這個(gè)芯片設(shè)計(jì)的用途是給手機(jī)之類的使用,比如:返回當(dāng)前環(huán)境光強(qiáng)以便調(diào)整屏幕亮度;用戶接聽(tīng)電話時(shí),將手機(jī)放置在耳邊后,自動(dòng)關(guān)閉屏幕避免用戶誤觸碰 。

2、IIC驅(qū)動(dòng)簡(jiǎn)介
Linux下IIC有兩種驅(qū)動(dòng)方式:一種是按照字符設(shè)備驅(qū)動(dòng)方式來(lái)驅(qū)動(dòng)IIC;另一種是走Linux下IIC的框架。按照字符設(shè)備驅(qū)動(dòng)的方式可以查閱這一篇文章:Linux IIC 字符設(shè)備 驅(qū)動(dòng)例子。
這里我們了解學(xué)習(xí)一下第二種方式,因?yàn)檎业降腁P3216的驅(qū)動(dòng)就是基于IIC驅(qū)動(dòng)框架的,哈哈。
整個(gè)IIC的驅(qū)動(dòng)框架相關(guān)代碼在driversi2c中,包含的內(nèi)容有:

IIC驅(qū)動(dòng)框架圖如(圖片來(lái)源于網(wǎng)絡(luò),鏈接見(jiàn)文末參考資料):

IIC驅(qū)動(dòng)框架可大體分為兩大部分:
① ?I2C 總線驅(qū)動(dòng):SOC 的 I2C 控制器驅(qū)動(dòng),也叫做 I2C 適配器驅(qū)動(dòng)。
② ?I2C 設(shè)備驅(qū)動(dòng):針對(duì)具體的 I2C 設(shè)備而編寫(xiě)的驅(qū)動(dòng)。
其中,訪問(wèn)抽象層與I2C核心層數(shù)據(jù)I2C 總線驅(qū)動(dòng)部分;driver驅(qū)動(dòng)層屬于I2C設(shè)備驅(qū)動(dòng)部分。
上面框圖對(duì)應(yīng)的代碼調(diào)用層次圖如:

下面的AP3216驅(qū)動(dòng)可以對(duì)照這張圖來(lái)看看。
4、AP3216實(shí)驗(yàn)
我們使用設(shè)備樹(shù)來(lái)描述AP3216設(shè)備信息,首先我們沒(méi)有在設(shè)備樹(shù)中添加AP3216相關(guān)節(jié)點(diǎn)時(shí),我們系統(tǒng)的I2C設(shè)備如:

添加I2C pinctrl,板子上AP3216接的是I2C1:


配置寄存器的值都設(shè)為0x4001b8b0,這一段是什么意思我們?cè)?strong>什么是Pinctrl子系統(tǒng)及GPIO子系統(tǒng)?這篇筆記中也有寫(xiě)到,就是幾個(gè)寄存器及其配置。
接下來(lái)在i2c1節(jié)點(diǎn)下添加ap3216節(jié)點(diǎn):

編譯設(shè)備樹(shù),傳到開(kāi)發(fā)板上,重啟。此時(shí)我們系統(tǒng)的I2C設(shè)備有:

可見(jiàn),新增的AP3216 I2C設(shè)備名就是我們?cè)O(shè)備樹(shù)里設(shè)置的。
下面編寫(xiě)AP3216驅(qū)動(dòng)(以下代碼來(lái)源于網(wǎng)絡(luò)):
ap3216.c:
#include?
#include?
#include?
#include?
#include?
#include?
#include?
#include?
#include?
#include?
#include?
#include?
#include?
#include?
#include?
#include?
#include?
#include?"ap3216creg.h"
/***************************************************************
文件名??:?ap3216c.c
描述?????:?AP3216C驅(qū)動(dòng)程序
***************************************************************/
#define?AP3216C_CNT?1
#define?AP3216C_NAME?"ap3216c"
?
struct?ap3216c_dev?{
?dev_t?devid;???/*?設(shè)備號(hào)???*/
?struct?cdev?cdev;??/*?cdev??*/
?struct?class?*class;?/*?類???*/
?struct?device?*device;?/*?設(shè)備???*/
?struct?device_node?*nd;?/*?設(shè)備節(jié)點(diǎn)?*/
?int?major;???/*?主設(shè)備號(hào)?*/
?void?*private_data;?/*?私有數(shù)據(jù)?*/
?unsigned?short?ir,?als,?ps;??/*?三個(gè)光傳感器數(shù)據(jù)?*/
};
?
static?struct?ap3216c_dev?ap3216cdev;
?
/*
?*?@description?:?從ap3216c讀取多個(gè)寄存器數(shù)據(jù)
?*?@param?-?dev:??ap3216c設(shè)備
?*?@param?-?reg:??要讀取的寄存器首地址
?*?@param?-?val:??讀取到的數(shù)據(jù)
?*?@param?-?len:??要讀取的數(shù)據(jù)長(zhǎng)度
?*?@return???:?操作結(jié)果
?*/
static?int?ap3216c_read_regs(struct?ap3216c_dev?*dev,?u8?reg,?void?*val,?int?len)
{
?int?ret;
?struct?i2c_msg?msg[2];
?struct?i2c_client?*client?=?(struct?i2c_client?*)dev->private_data;
?
?/*?msg[0]為發(fā)送要讀取的首地址?*/
?msg[0].addr?=?client->addr;???/*?ap3216c地址?*/
?msg[0].flags?=?0;?????/*?標(biāo)記為發(fā)送數(shù)據(jù)?*/
?msg[0].buf?=???????/*?讀取的首地址?*/
?msg[0].len?=?1;??????/*?reg長(zhǎng)度*/
?
?/*?msg[1]讀取數(shù)據(jù)?*/
?msg[1].addr?=?client->addr;???/*?ap3216c地址?*/
?msg[1].flags?=?I2C_M_RD;???/*?標(biāo)記為讀取數(shù)據(jù)*/
?msg[1].buf?=?val;?????/*?讀取數(shù)據(jù)緩沖區(qū)?*/
?msg[1].len?=?len;?????/*?要讀取的數(shù)據(jù)長(zhǎng)度*/
?
?ret?=?i2c_transfer(client->adapter,?msg,?2);
?if(ret?==?2)?{
??ret?=?0;
?}?else?{
??printk("i2c?rd?failed=%d?reg=%06x?len=%d
",ret,?reg,?len);
??ret?=?-EREMOTEIO;
?}
?return?ret;
}
?
/*
?*?@description?:?向ap3216c多個(gè)寄存器寫(xiě)入數(shù)據(jù)
?*?@param?-?dev:??ap3216c設(shè)備
?*?@param?-?reg:??要寫(xiě)入的寄存器首地址
?*?@param?-?val:??要寫(xiě)入的數(shù)據(jù)緩沖區(qū)
?*?@param?-?len:??要寫(xiě)入的數(shù)據(jù)長(zhǎng)度
?*?@return????:???操作結(jié)果
?*/
static?s32?ap3216c_write_regs(struct?ap3216c_dev?*dev,?u8?reg,?u8?*buf,?u8?len)
{
?u8?b[256];
?struct?i2c_msg?msg;
?struct?i2c_client?*client?=?(struct?i2c_client?*)dev->private_data;
?
?b[0]?=?reg;?????/*?寄存器首地址?*/
?memcpy(&b[1],buf,len);??/*?將要寫(xiě)入的數(shù)據(jù)拷貝到數(shù)組b里面?*/
??
?msg.addr?=?client->addr;?/*?ap3216c地址?*/
?msg.flags?=?0;????/*?標(biāo)記為寫(xiě)數(shù)據(jù)?*/
?
?msg.buf?=?b;????/*?要寫(xiě)入的數(shù)據(jù)緩沖區(qū)?*/
?msg.len?=?len?+?1;???/*?要寫(xiě)入的數(shù)據(jù)長(zhǎng)度?*/
?
?return?i2c_transfer(client->adapter,?&msg,?1);
}
?
/*
?*?@description?:?讀取ap3216c指定寄存器值,讀取一個(gè)寄存器
?*?@param?-?dev:??ap3216c設(shè)備
?*?@param?-?reg:??要讀取的寄存器
?*?@return????:???讀取到的寄存器值
?*/
static?unsigned?char?ap3216c_read_reg(struct?ap3216c_dev?*dev,?u8?reg)
{
?u8?data?=?0;
?
?ap3216c_read_regs(dev,?reg,?&data,?1);
?return?data;
?
#if?0
?struct?i2c_client?*client?=?(struct?i2c_client?*)dev->private_data;
?return?i2c_smbus_read_byte_data(client,?reg);
#endif
}
?
/*
?*?@description?:?向ap3216c指定寄存器寫(xiě)入指定的值,寫(xiě)一個(gè)寄存器
?*?@param?-?dev:??ap3216c設(shè)備
?*?@param?-?reg:??要寫(xiě)的寄存器
?*?@param?-?data:?要寫(xiě)入的值
?*?@return???:????無(wú)
?*/
static?void?ap3216c_write_reg(struct?ap3216c_dev?*dev,?u8?reg,?u8?data)
{
?u8?buf?=?0;
?buf?=?data;
?ap3216c_write_regs(dev,?reg,?&buf,?1);
}
?
/*
?*?@description :?讀取AP3216C的數(shù)據(jù),讀取原始數(shù)據(jù),包括ALS,PS和IR, 注意!
?*????:?如果同時(shí)打開(kāi)ALS,IR+PS的話兩次數(shù)據(jù)讀取的時(shí)間間隔要大于112.5ms
?*?@param?-?ir?:?ir數(shù)據(jù)
?*?@param?-?ps??:?ps數(shù)據(jù)
?*?@param?-?ps??:?als數(shù)據(jù)?
?*?@return ??:?無(wú)。
?*/
void?ap3216c_readdata(struct?ap3216c_dev?*dev)
{
?unsigned?char?i?=0;
????unsigned?char?buf[6];
?
?/*?循環(huán)讀取所有傳感器數(shù)據(jù)?*/
????for(i?=?0;?i?6;?i++)?
????{
????????buf[i]?=?ap3216c_read_reg(dev,?AP3216C_IRDATALOW?+?i);?
????}
?
????if(buf[0]?&?0X80)??/*?IR_OF位為1,則數(shù)據(jù)無(wú)效?*/
??dev->ir?=?0;?????
?else?????/*?讀取IR傳感器的數(shù)據(jù)?????*/
??dev->ir?=?((unsigned?short)buf[1]?<2)?|?(buf[0]?&?0X03);????
?
?dev->als?=?((unsigned?short)buf[3]?<8)?|?buf[2];?/*?讀取ALS傳感器的數(shù)據(jù)?????*/??
?
????if(buf[4]?&?0x40)?/*?IR_OF位為1,則數(shù)據(jù)無(wú)效????*/
??dev->ps?=?0;?????????????????
?else?????/*?讀取PS傳感器的數(shù)據(jù)????*/
??dev->ps?=?((unsigned?short)(buf[5]?&?0X3F)?<4)?|?(buf[4]?&?0X0F);?
}
?
/*
?*?@description??:?打開(kāi)設(shè)備
?*?@param?-?inode??:?傳遞給驅(qū)動(dòng)的inode
?*?@param?-?filp??:?設(shè)備文件,file結(jié)構(gòu)體有個(gè)叫做private_data的成員變量
?*????????一般在open的時(shí)候?qū)rivate_data指向設(shè)備結(jié)構(gòu)體。
?*?@return????:?0?成功;其他?失敗
?*/
static?int?ap3216c_open(struct?inode?*inode,?struct?file?*filp)
{
?filp->private_data?=?&ap3216cdev;
?
?/*?初始化AP3216C?*/
?ap3216c_write_reg(&ap3216cdev,?AP3216C_SYSTEMCONG,?0x04);??/*?復(fù)位AP3216C????*/
?mdelay(50);??????????????/*?AP3216C復(fù)位最少10ms??*/
?ap3216c_write_reg(&ap3216cdev,?AP3216C_SYSTEMCONG,?0X03);??/*?開(kāi)啟ALS、PS+IR???*/
?return?0;
}
?
/*
?*?@description??:?從設(shè)備讀取數(shù)據(jù)?
?*?@param?-?filp??:?要打開(kāi)的設(shè)備文件(文件描述符)
?*?@param?-?buf??:?返回給用戶空間的數(shù)據(jù)緩沖區(qū)
?*?@param?-?cnt??:?要讀取的數(shù)據(jù)長(zhǎng)度
?*?@param?-?offt??:?相對(duì)于文件首地址的偏移
?*?@return????:?讀取的字節(jié)數(shù),如果為負(fù)值,表示讀取失敗
?*/
static?ssize_t?ap3216c_read(struct?file?*filp,?char?__user?*buf,?size_t?cnt,?loff_t?*off)
{
?short?data[3];
?long?err?=?0;
?
?struct?ap3216c_dev?*dev?=?(struct?ap3216c_dev?*)filp->private_data;
?
?ap3216c_readdata(dev);
?
?data[0]?=?dev->ir;
?data[1]?=?dev->als;
?data[2]?=?dev->ps;
?err?=?copy_to_user(buf,?data,?sizeof(data));
?return?0;
}
?
/*
?*?@description??:?關(guān)閉/釋放設(shè)備
?*?@param?-?filp??:?要關(guān)閉的設(shè)備文件(文件描述符)
?*?@return????:?0?成功;其他?失敗
?*/
static?int?ap3216c_release(struct?inode?*inode,?struct?file?*filp)
{
?return?0;
}
?
/*?AP3216C操作函數(shù)?*/
static?const?struct?file_operations?ap3216c_ops?=?{
?.owner?=?THIS_MODULE,
?.open?=?ap3216c_open,
?.read?=?ap3216c_read,
?.release?=?ap3216c_release,
};
?
?/*
??*?@description?????:?i2c驅(qū)動(dòng)的probe函數(shù),當(dāng)驅(qū)動(dòng)與
??*????????????????????設(shè)備匹配以后此函數(shù)就會(huì)執(zhí)行
??*?@param?-?client??:?i2c設(shè)備
??*?@param?-?id??????:?i2c設(shè)備ID
??*?@return??????????:?0,成功;其他負(fù)值,失敗
??*/
static?int?ap3216c_probe(struct?i2c_client?*client,?const?struct?i2c_device_id?*id)
{
?/*?1、構(gòu)建設(shè)備號(hào)?*/
?if?(ap3216cdev.major)?{
??ap3216cdev.devid?=?MKDEV(ap3216cdev.major,?0);
??register_chrdev_region(ap3216cdev.devid,?AP3216C_CNT,?AP3216C_NAME);
?}?else?{
??alloc_chrdev_region(&ap3216cdev.devid,?0,?AP3216C_CNT,?AP3216C_NAME);
??ap3216cdev.major?=?MAJOR(ap3216cdev.devid);
?}
?
?/*?2、注冊(cè)設(shè)備?*/
?cdev_init(&ap3216cdev.cdev,?&ap3216c_ops);
?cdev_add(&ap3216cdev.cdev,?ap3216cdev.devid,?AP3216C_CNT);
?
?/*?3、創(chuàng)建類?*/
?ap3216cdev.class?=?class_create(THIS_MODULE,?AP3216C_NAME);
?if?(IS_ERR(ap3216cdev.class))?{
??return?PTR_ERR(ap3216cdev.class);
?}
?
?/*?4、創(chuàng)建設(shè)備?*/
?ap3216cdev.device?=?device_create(ap3216cdev.class,?NULL,?ap3216cdev.devid,?NULL,?AP3216C_NAME);
?if?(IS_ERR(ap3216cdev.device))?{
??return?PTR_ERR(ap3216cdev.device);
?}
?
?ap3216cdev.private_data?=?client;
?
?return?0;
}
?
/*
?*?@description?????:?i2c驅(qū)動(dòng)的remove函數(shù),移除i2c驅(qū)動(dòng)的時(shí)候此函數(shù)會(huì)執(zhí)行
?*?@param?-?client??:?i2c設(shè)備
?*?@return??????????:?0,成功;其他負(fù)值,失敗
?*/
static?int?ap3216c_remove(struct?i2c_client?*client)
{
?/*?刪除設(shè)備?*/
?cdev_del(&ap3216cdev.cdev);
?unregister_chrdev_region(ap3216cdev.devid,?AP3216C_CNT);
?
?/*?注銷(xiāo)掉類和設(shè)備?*/
?device_destroy(ap3216cdev.class,?ap3216cdev.devid);
?class_destroy(ap3216cdev.class);
?return?0;
}
?
/*?傳統(tǒng)匹配方式ID列表?*/
static?const?struct?i2c_device_id?ap3216c_id[]?=?{
?{"iot,ap3216c",?0},??
?{}
};
?
/*?設(shè)備樹(shù)匹配列表?*/
static?const?struct?of_device_id?ap3216c_of_match[]?=?{
?{?.compatible?=?"iot,ap3216c"?},
?{?/*?Sentinel?*/?}
};
?
/*?i2c驅(qū)動(dòng)結(jié)構(gòu)體?*/?
static?struct?i2c_driver?ap3216c_driver?=?{
?.probe?=?ap3216c_probe,
?.remove?=?ap3216c_remove,
?.driver?=?{
???.owner?=?THIS_MODULE,
??????.name?=?"ap3216c",
??????.of_match_table?=?ap3216c_of_match,?
?????},
?.id_table?=?ap3216c_id,
};
?????
/*
?*?@description?:?驅(qū)動(dòng)入口函數(shù)
?*?@param???:?無(wú)
?*?@return???:?無(wú)
?*/
static?int?__init?ap3216c_init(void)
{
?int?ret?=?0;
?
?ret?=?i2c_add_driver(&ap3216c_driver);
?return?ret;
}
?
/*
?*?@description?:?驅(qū)動(dòng)出口函數(shù)
?*?@param???:?無(wú)
?*?@return???:?無(wú)
?*/
static?void?__exit?ap3216c_exit(void)
{
?i2c_del_driver(&ap3216c_driver);
}
?
/*?module_i2c_driver(ap3216c_driver)?*/
?
module_init(ap3216c_init);
module_exit(ap3216c_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("pjw");
驅(qū)動(dòng)詳解可查閱注釋及配合上訴的I2C驅(qū)動(dòng)框架的框圖及數(shù)據(jù)手冊(cè)理解。
ap3216creg.h:
#ifndef?AP3216C_H #define?AP3216C_H /*************************************************************** 文件名??:?ap3216creg.h 描述?????:?AP3216C寄存器地址描述頭文件 ***************************************************************/ #define?AP3216C_ADDR?????0X1E?/*?AP3216C器件地址??*/ /*?AP3316C寄存器?*/ #define?AP3216C_SYSTEMCONG?0x00?/*?配置寄存器???????*/ #define?AP3216C_INTSTATUS?0X01?/*?中斷狀態(tài)寄存器???*/ #define?AP3216C_INTCLEAR?0X02?/*?中斷清除寄存器???*/ #define?AP3216C_IRDATALOW?0x0A?/*?IR數(shù)據(jù)低字節(jié)?????*/ #define?AP3216C_IRDATAHIGH?0x0B?/*?IR數(shù)據(jù)高字節(jié)?????*/ #define?AP3216C_ALSDATALOW?0x0C?/*?ALS數(shù)據(jù)低字節(jié)????*/ #define?AP3216C_ALSDATAHIGH?0X0D?/*?ALS數(shù)據(jù)高字節(jié)????*/ #define?AP3216C_PSDATALOW?0X0E?/*?PS數(shù)據(jù)低字節(jié)?????*/ #define?AP3216C_PSDATAHIGH?0X0F?/*?PS數(shù)據(jù)高字節(jié)?????*/ #endif
ap3216應(yīng)用:
ap3216cApp.c:
#include?"stdio.h"
#include?"unistd.h"
#include?"sys/types.h"
#include?"sys/stat.h"
#include?"sys/ioctl.h"
#include?"fcntl.h"
#include?"stdlib.h"
#include?"string.h"
#include?
#include?
#include?
#include?
#include?
/***************************************************************
文件名??:?ap3216cApp.c
描述?????: ap3216c設(shè)備測(cè)試APP。
使用方法??:./ap3216cApp /dev/ap3216c
***************************************************************/
?
/*
?*?@description??:?main主程序
?*?@param?-?argc??:?argv數(shù)組元素個(gè)數(shù)
?*?@param?-?argv??:?具體參數(shù)
?*?@return????:?0?成功;其他?失敗
?*/
int?main(int?argc,?char?*argv[])
{
?int?fd;
?char?*filename;
?unsigned?short?databuf[3];
?unsigned?short?ir,?als,?ps;
?int?ret?=?0;
?
?if?(argc?!=?2)?{
??printf("Error?Usage!
");
??return?-1;
?}
?
?filename?=?argv[1];
?fd?=?open(filename,?O_RDWR);
?if(fd?0)?{
??printf("can't?open?file?%s
",?filename);
??return?-1;
?}
?
?while?(1)?{
??ret?=?read(fd,?databuf,?sizeof(databuf));
??if(ret?==?0)?{????/*?數(shù)據(jù)讀取成功?*/
???ir?=??databuf[0];??/*?ir傳感器數(shù)據(jù)?*/
???als?=?databuf[1];??/*?als傳感器數(shù)據(jù)?*/
???ps?=??databuf[2];??/*?ps傳感器數(shù)據(jù)?*/
???printf("ir?=?%d,?als?=?%d,?ps?=?%d
",?ir,?als,?ps);
??}
??usleep(200000);?/*100ms?*/
?}
?close(fd);?/*?關(guān)閉文件?*/?
?return?0;
}
編寫(xiě)Makefile,從之前的文章拷貝過(guò)來(lái)修改:
KERN_DIR?=?/home/book/100ask_imx6ull-sdk/Linux-4.9.88 all: ?make?-C?$(KERN_DIR)?M=`pwd`?modules? ?$(CROSS_COMPILE)gcc?-o?ap3216cApp?ap3216cApp.c? clean: ?make?-C?$(KERN_DIR)?M=`pwd`?modules?clean ?rm?-rf?modules.order ?rm?-f?ap3216cApp #?參考內(nèi)核源碼drivers/char/ipmi/Makefile #?要想把a(bǔ).c,?b.c編譯成ab.ko,?可以這樣指定: #?ab-y?:=?a.o?b.o #?obj-m?+=?ab.o obj-m?+=?ap3216.o
編譯得到ap3216.ko及ap3216cApp,傳到板子上運(yùn)行:

以上就是本次的實(shí)驗(yàn)分享,如果文章對(duì)你有幫助,歡迎轉(zhuǎn)發(fā),謝謝!
編輯:黃飛
?
電子發(fā)燒友App






















評(píng)論