1. 概述

地磁使用的是AKM8975地磁传感器,其kernel部分的驱动代码路径是\kernel\drivers\

misc\akm8975.c,android的HAL层的路径是qics1003\hardware\libhardware\modules\

libsensors\AkmSensor.cpp,还有不同于其他传感器的是,地磁还需要第三方的库文件,AKM8975的库文件路径是在文件夹\qics1003\external\akmd8975下,主要提供了对计算磁场和方向的一些函数接口,其软件流程如下图所示:

图1.1  AKM8975软件流程

本文档主要对kernel部分和HAL部分进行分析,还有就是对库文件进行部分分析。

2. Kernel部分驱动

在kernel部分主要完成了创建input设备用来上报地磁数据、注册/dev/akm8975_dev设备用于完成第三方库文件对驱动的IOCTL控制,创建/sys/class/compass/akm8975/下的各个节点用于对重力方向地磁的delay与enable设置,读取akm8975中的数据、接收HAL层设置的重力数据。

1.        初始化

首先运行的是akm8975_probe()这个函数,在这里进行了以下关键操作:

u  s_akm->layout = pdata->layout;根据芯片贴片位置设置layout值(layout的取值方式在文档AK8975_Android_Porting_Guide_E.pdf第12页);

u  调用akm8975_i2c_check_device( )检测AKM8975的ID号( 0x48);

u  调用akm8975_input_init( )设置input输入系统,用于上报地磁方向的数据;

u  INIT_DELAYED_WORK(&s_akm->work, akm8975_delayed_work);初始化一个延时工作队列,用于在中断服务程序响应后读取数据;

u  调用request_threaded_irq( )添加中断服务程序akm8975_irq( );

u  调用misc_register( );注册一个杂项设备(/dev/akm8975_dev);

u  调用create_sysfs_interfaces( )创建了/sys/class/compass/akm8975/下的所有节点。

2.        中断处理

以下为akm8975的中断服务程序,首先禁止中断然后调用schedule_delayed_work()执行函数akm8975_delayed_work()完成读数据的操作,并且调用wake_up(&akm->drdy_wq)唤醒drdy_wq等待队列表示数据准备完成,并且调用enable_irq(akm->irq)使能中断。

static irqreturn_t akm8975_irq(int irq, void *handle)

{

structakm8975_data *akm = handle;

disable_irq_nosync(irq);

schedule_delayed_work(&akm->work, 0);

returnIRQ_HANDLED;

}

3.        /sys/class/compass/akm8975/下的节点

代码中通过调用create_device_attributes(akm->class_dev,akm8975_attributes);来创建了以下节点:

static struct device_attribute akm8975_attributes[] ={

__ATTR(enable_acc,0660, akm8975_enable_acc_show, akm8975_enable_acc_store),

__ATTR(enable_mag,0660, akm8975_enable_mag_show, akm8975_enable_mag_store),

__ATTR(enable_ori,0660, akm8975_enable_ori_show, akm8975_enable_ori_store),

__ATTR(delay_acc,  0660, akm8975_delay_acc_show,  akm8975_delay_acc_store),

__ATTR(delay_mag,  0660, akm8975_delay_mag_show,  akm8975_delay_mag_store),

__ATTR(delay_ori,  0660, akm8975_delay_ori_show,  akm8975_delay_ori_store),

#ifdef AKM8975_DEBUG_IF

__ATTR(mode,  0220, NULL, akm8975_mode_store),

__ATTR(bdata,0440, akm8975_bdata_show, NULL),

__ATTR(asa,   0440, akm8975_asa_show, NULL),

#endif

__ATTR_NULL,

};

这些节点的enable、delay函数的实现方式都非常接近,下面就介绍一下enable_acc节点的enable_store函数的操作:

static ssize_t akm8975_enable_acc_store(

structdevice *dev, struct device_attribute *attr,char const *buf, size_t count)

{

returnakm8975_sysfs_enable_store(dev_get_drvdata(dev), buf, count, ACC_DATA_FLAG);

}

static ssize_t akm8975_sysfs_enable_store(

structakm8975_data *akm, char const *buf, size_t count, int pos)

{

int en =0;

if (NULL== buf)

return-EINVAL;

if (0 ==count)

return0;

if(false == get_value_as_int(buf, count, &en))

return-EINVAL;

en = en? 1 : 0;

mutex_lock(&akm->val_mutex);

akm->enable_flag&= ~(1<<pos);

akm->enable_flag|= ((uint32_t)(en))<<pos;

mutex_unlock(&akm->val_mutex);

akm8975_sysfs_update_active_status(akm);

returncount;

}

在akm8975_enable_acc_store中调用了函数akm8975_sysfs_enable_store(),通过ACC_DATA_FLAG标识完成对enable_flag中G-sensor的使能标志位置1。这个标志主要是用来在上报数据时判断对应的标志是否使能。调用akm8975_sysfs_update_active_status()完成对akm->active变量置1,并且调用wake_up(&akm->open_wq)唤醒akm->open_wq工作队列。

还有一个节点是:

static struct bin_attribute akm8975_bin_attributes[] ={

__BIN_ATTR(accel,0220, 6, NULL,NULL, akm8975_bin_accel_write),

__BIN_ATTR_NULL

};

主要是实现HAL层向将G-sensor数据写入到akm8975中,供第三方库文件读取。

4.        /dev/akm8975_dev设备节点

驱动中注册了一个杂项设备akm8975_dev,主要实现了ioctl的功能供第三方的库文件操作,主要包含以下操作:

#define ECS_IOCTL_READ              _IOWR(AKMIO, 0x01, char*)

#define ECS_IOCTL_WRITE             _IOW(AKMIO, 0x02, char*)

#define ECS_IOCTL_SET_MODE          _IOW(AKMIO, 0x03, short)

#define ECS_IOCTL_GETDATA           _IOR(AKMIO, 0x04, char[SENSOR_DATA_SIZE])

#define ECS_IOCTL_SET_YPR           _IOW(AKMIO, 0x05,int[YPR_DATA_SIZE])

#define ECS_IOCTL_GET_OPEN_STATUS   _IOR(AKMIO, 0x06, int)

#define ECS_IOCTL_GET_CLOSE_STATUS  _IOR(AKMIO, 0x07, int)

#define ECS_IOCTL_GET_DELAY         _IOR(AKMIO, 0x08, long longint[AKM_NUM_SENSORS])

#define ECS_IOCTL_GET_LAYOUT        _IOR(AKMIO, 0x09, char)

#define ECS_IOCTL_GET_ACCEL                          _IOR(AKMIO, 0x30, short[3])

²  ECS_IOCTL_READ:通过I2C读取AKM8975寄存器中的数据

²  ECS_IOCTL_WRITE:通过I2C向AKM8975寄存器写入数据

²  ECS_IOCTL_SET_MODE:设置AKM8975的工作模式

²  ECS_IOCTL_GETDATA:读取地磁数据

²  ECS_IOCTL_SET_YPR:将第三方库计算的结果保存到驱动中,并产生input事件,在这里是通过调用AKECS_SetYPR( ),判断akm->enable_flag来确定是否上报

²  ECS_IOCTL_GET_OPEN_STATUS:查看akm8975的打开状态

²  ECS_IOCTL_GET_CLOSE_STATUS:查看akm8975的关闭状态

²  ECS_IOCTL_GET_DELAY:获得驱动设置的延时事件

²  ECS_IOCTL_GET_LAYOUT:获得芯片的贴片位置

²  ECS_IOCTL_GET_ACCEL:获得G-sensor的数据

3. HAL部分

地磁的HAL部分的代码在\qics1003\hardware\libhardware\modules\libsensors\AkmSensor.cpp中, 其提供了函数接口供qics1003\hardware\libhardware\modules\libsensors\

sensors.cpp调用,在sensors.cpp中调用open_sensors( ):

static int open_sensors(const struct hw_module_t*module, const char* id,struct hw_device_t** device)

{

intstatus = -EINVAL;

 sensors_poll_context_t *dev = new sensors_poll_context_t();

memset(&dev->device, 0, sizeof(sensors_poll_device_t));

******

*device= &dev->device.common;

status =0;

returnstatus;

}

其中通过new sensors_poll_context_t( )创建了一个sensors_poll_context_t实例,首先执行构造函数sensors_poll_context_t( ):

sensors_poll_context_t::sensors_poll_context_t()

{

mSensors[acc]= new AcclerSensor();

mPollFds[acc].fd= mSensors[acc]->getFd();

mPollFds[acc].events= POLLIN;

mPollFds[acc].revents= 0;

    mSensors[akm] = new AkmSensor();

mPollFds[akm].fd= mSensors[akm]->getFd();

mPollFds[akm].events = POLLIN;

mPollFds[akm].revents= 0;

******

mWritePipeFd= wakeFds[1];

mPollFds[wake].fd = wakeFds[0];

mPollFds[wake].events = POLLIN;

mPollFds[wake].revents = 0;

}

在函数中又通过调用mSensors[akm] = new AkmSensor( )创建了一个AkmSensor实例,class AkmSensor则实现在上文提到的AkmSensor.cpp中。首先运行构造函数AkmSensor( ):

AkmSensor::AkmSensor()

: SensorBase(NULL,"compass"),

mPendingMask(0),

mInputReader(32)

{

for (inti=0; i<numSensors; i++) {

mEnabled[i]= 0;

mDelay[i]= 0;

}

****

if (data_fd){

                   strcpy(input_sysfs_path,"/sys/class/compass/akm8975/");

                   input_sysfs_path_len =strlen(input_sysfs_path);

} else {

input_sysfs_path[0]= '\0';

input_sysfs_path_len= 0;

}

}

在SensorBase( )中,通过调用data_fd = openInput(data_name)将找到compass在input中对应的节点位置,并返回文件的句柄保存到data_fd中,在AkmSensor.cpp中就可以用data_fd句柄读取input系统中的数据。后面将/sys/class/compass/akm8975/路径保存到input_sysfs_path中,这个路径主要是用来在setEnable( )、setDelay( )时对enable和delay的节点进行设置。

同时在这个路径下还有一个节点“accel“,这个节点就是完成将G-sensor的数据写回到akm8975的驱动中,其代码如下:

int AkmSensor::setAccel(sensors_event_t* data)

{

int err;

int16_tacc[3];

acc[0] =(int16_t)(data->acceleration.x / GRAVITY_EARTH * AKSC_LSG);

acc[1] =(int16_t)(data->acceleration.y / GRAVITY_EARTH * AKSC_LSG);

acc[2] =(int16_t)(data->acceleration.z / GRAVITY_EARTH * AKSC_LSG);

         strcpy(&input_sysfs_path[input_sysfs_path_len],"accel");

         err =write_sys_attribute(input_sysfs_path, (char*)acc, 6);

if (err< 0) {

LOGD("AkmSensor:%s write failed.",

&input_sysfs_path[input_sysfs_path_len]);

}

returnerr;

}

这个写G-sensor的数据到akm8975的驱动中的函数在sensors.cpp的activate( ),pollEvents()中:

if (mSensors[akm]->getEnable(ID_M) ||mSensors[akm]->getEnable(ID_O)) {

if(!mag_enable_gsensor){

if(mSensors[acc]->getEnable(ID_A)){

gsensor_enable_flag= 1;

}else{

err = mSensors[acc]->setEnable(ID_A, 1);

mag_enable_gsensor= 1;

}

}

}

如果打开了地磁触感器就会判断mSensors[acc]->getEnable(ID_A)标志G-sensor是否打开,如果没有打开这会mSensors[acc]->setEnable(ID_A, 1)调用打开G-sensor。并且在pollEvents( )中通过(mSensors[akm]))->setAccel(&data[nb-1])将G-sensor的数据写入到akm8975的驱动中。

读取input子系统中的数据主要是通过函数readEvents( )调用processEvent( )完成读取input中的数据。

4. 第三方库文件

第三方库文件路径在qics1003\external\akmd8975文件夹下,首先进入man.c的main()函数大致流程如下:

l AKD_InitDevice()  打开"/dev/akm8975_dev"设备节点

l ReadAK8975FUSEROM() 读取AKM8975的ID

l MeasureSNGLoop() 进入地磁测量代码

在MeasureSNGLoop()中完成了读取在驱动中设置的delay、读取地磁sensor中的数据、计算地磁方位等操作,代码大致流程如下:

l GetInterval() 读取驱动中设置的delay

l AKD_SetMode() 通过IOCTL中的cmd:ECS_IOCTL_SET_MODE设置AKM8975到采样模式

l AKD_GetMagneticData() 通过IOCTL中的cmd:ECS_IOCTL_GETDATA获取采样数据

l AKD_GetAccelerationData()  在AOT_GetAccelerationData函数中通过IOCTL中的cmd:ECS_IOCTL_GET_ACCEL获得G-sensor数据

l CalcDirection() 计算方向,在这里调用了libAK8975.a中的库函数计算地磁方向

l Disp_MeasurementResultHook() 在AKD_SetYPR( )中通过过IOCTL中的cmd:ECS_IOCTL_SET_YPR设置计算结果到驱动中,并触发input上报数据。

5. 附件

磁性零件

PCB布局标准推荐距离(mm)

磁性开关

30

扬声器

10 ~ 20

振动电机

10

摄像头模组

10

记忆卡插槽

5

屏蔽件

10

表 4.1  电子罗盘布局与磁性元件的距离推荐

电流波动(mA)

距离电源线PCB布局

标准推荐距离(mm)

2

0.2

10

1

50

5

100

10

200

20

表 4.2  电子罗盘布局与电源线的距离推荐

高通AKM8975地磁传感器分析相关推荐

  1. linux驱动由浅入深系列:高通sensor架构实例分析之一

    点击打开链接 本系列导航: linux驱动由浅入深系列:高通sensor架构实例分析之一(整体概览+AP侧代码分析) linux驱动由浅入深系列:高通sensor架构实例分析之二(adsp驱动代码结构 ...

  2. linux驱动由浅入深系列:高通sensor架构实例分析之三(adsp上报数据详解、校准流程详解)

    本系列导航: linux驱动由浅入深系列:高通sensor架构实例分析之一(整体概览+AP侧代码分析) linux驱动由浅入深系列:高通sensor架构实例分析之二(adsp驱动代码结构)

  3. 高通8155/8295 boot分析

    目录 前言 通用boot流程 8155/8295 boot流程概述 前言 本文将基于高通8155/8295 Q+A hypervisor平台分析整个boot的启动流程.高通其他SOC芯片的启动流程大致 ...

  4. 高通Android display架构分析

    目录(?)[-] Kernel Space Display架构介绍 函数和数据结构介绍 函数和数据结构介绍 函数和数据结构介绍 数据流分析 初始化过程分析 User Space display接口 K ...

  5. 高通MSM8255 GPS 调试分析

    没事做整理一下GPS的流程,也算给自己一个交代. 1.硬件抽象层: 高通MSM的硬件层代码在:hardware/qcom/gps/loc_api下,高通的GPS集成在baseband侧,与Modem部 ...

  6. 高通MSM8255 GPS 调试分析Android系统之Broadcom GPS 移植

    http://blog.csdn.net/dwyane_zhang/article/details/6775738 没事做整理一下GPS的流程,也算给自己一个交代. 1.硬件抽象层: 高通MSM的硬件 ...

  7. 1.高通SEE 虚拟sensor分析

    在SEE中,处理硬件sensor,高通实现的platform sensor,还有一类是通过硬件sensor等各种数据计算,抽象出来的虚拟sensor. 我们以高通提供的参考为例,看如何添加一个虚拟se ...

  8. 高通骁龙传感器核心特征

    一,框架介绍 SEE framework:新的芯片组支持此软件框架 Legacy framework:旧芯片组所支持的传统DDF/SMGR/SAM框架 二,特征: 1,运动检测 a,绝对运动检测 (A ...

  9. 高通msm8953平台I2C分析

    今天遇到了个I2C不通的问题,用的是I2C_8 msm8953.dtsi i2c8 = &i2c_8;i2c_8: i2c@7af8000 { /* BLSP2 QUP3 */compatib ...

最新文章

  1. 10玩rust_有趣的 Rust 类型系统: Trait
  2. Java DecimalFormat 用法 小数位的处理 公司内部处理小数位
  3. lk启动流程详细分析
  4. 勒索病毒GANDCRAB新变种GANDCRAB V5.2新变种来袭 你中招了吗?
  5. 尴尬:原来java中有两个 ModelAndView类
  6. 吴恩达深度学习CNN作业:Convolutional Neural Networks: Step by Step
  7. VB小程序:生成十个不重复的随机数
  8. Ubuntu图形化数据库连接工具
  9. linux抓本来端口包,Linux抓包
  10. 邮箱 手机号 格式要求
  11. 使用vim修改只读文件
  12. 【SSL】2021-08-19 1100.神秘数列
  13. 微信与php什么区别,微信小店和微信小程序的区别是什么?
  14. 智能停车场[简易版]
  15. 水溶性富勒醇/羟基化富勒烯(Fullerenols)主要成分碳、氧、氢
  16. 试试多线程(java)
  17. 06 - 微信公众号的菜单配置
  18. 组态王虚拟服务器,组态王客户端服务器是什么
  19. 清风电子—keil5,没有找到No ULINK Device found
  20. 你学或不学,知识就在那里,只增不减!

热门文章

  1. 智能清晰锐化磨皮图片处理软件Perfectly Clear 3.8.0.1665 汉化版
  2. 4.13 使用扇贝工具制作猥琐的老鼠 [Illustrator CC教程]
  3. MaC 修改MySQL密码
  4. 任正非在持股员工代表会上讲:我的家人永不会进入接班人序列
  5. 什么是MapReduce(入门篇)
  6. Linux 启动定时任务配置
  7. 癌症来临,应该怎么办?
  8. module.exports 与 exports.xxx 的区别
  9. Spring Messaging 远程代码执行漏洞分析(CVE-2018-1270)
  10. matlab怎么开始使用方法,matlab怎么使用,matlab基本使用方法