资讯详情

高通AKM8975地磁传感器分析

1. 概述

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

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

libsensors\AkmSensor.cpp,与其它传感器不同的是,地磁还需要第三方库文件,AKM8975的库文件路径是文件夹\qics1003\external\akmd软件流程如下图所示:

图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.中断处理

以下为akm禁止中断8975中断服务程序,然后调用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))

&nbp;                  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;

      

       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;

    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()

:

     mPendingMask(0),

     mInputReader(32)

{

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

                   mEnabled[i]= 0;

                   mDelay[i]= 0;

         }

****

    if (data_fd){

         } 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);

         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{

                                    

                                     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  电子罗盘布局与电源线的距离推荐

标签: nb地磁传感器地磁传感器能反正吗

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台