sensor: add driver for AK8975 magnetometer

Add driver for the AK8975 3-axis electonic compass.

Datasheet:
	http://www.akm.com/akm/en/file/datasheet/AK8975.pdf

Change-Id: I427f954396c8991a153e4447d746b855d19cb60d
Origin: Original
Signed-off-by: Bogdan Davidoaia <bogdan.m.davidoaia@intel.com>
This commit is contained in:
Bogdan Davidoaia 2016-05-27 11:33:29 +03:00
parent 01062d97aa
commit c4c01a92f3
5 changed files with 287 additions and 0 deletions

View file

@ -23,6 +23,8 @@ menuconfig SENSOR
help
Include sensor drivers in system config
source "drivers/sensor/Kconfig.ak8975"
source "drivers/sensor/Kconfig.bma280"
source "drivers/sensor/Kconfig.bmc150_magn"

View file

@ -0,0 +1,77 @@
# Kconfig.ak8975 - AK8975 magnetometer configuration options
#
# Copyright (c) 2016 Intel Corporation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
menuconfig AK8975
bool
prompt "AK8975 Magnetometer"
depends on SENSOR && I2C
default n
help
Enable driver for AK8975 magnetometer.
config AK8975_SYS_LOG_LEVEL
int "AK8975 Log level"
depends on SYS_LOG && AK8975
default 0
range 0 4
help
Sets log level for AK8975 driver.
Levels are:
0 OFF, do not write
1 ERROR, only write SYS_LOG_ERR
2 WARNING, write SYS_LOG_WRN in addition to previous level
3 INFO, write SYS_LOG_INF in addition to previous levels
4 DEBUG, write SYS_LOG_DBG in addition to previous levels
config AK8975_NAME
string
prompt "Driver name"
default "AK8975"
depends on AK8975
help
Device name with which the AK8975 sensor is identified.
config AK8975_INIT_PRIORITY
int
prompt "Init priority"
depends on AK8975
default 70
help
Device driver initialization priority.
config AK8975_I2C_ADDR
hex
prompt "I2C address"
depends on AK8975
default 0x0C
range 0x0C 0x0F
help
I2C address of the AK8975 sensor. Choose:
- 0x0C if CAD1 connected to GND nad CAD0 is connected to GND
- 0x0D if CAD1 connected to GND nad CAD0 is connected to VDD
- 0x0E if CAD1 connected to VDD nad CAD0 is connected to GND
- 0x0F if CAD1 connected to VDD nad CAD0 is connected to VDD
config AK8975_I2C_MASTER_DEV_NAME
string
prompt "I2C master where AK8975 is connected"
depends on AK8975
default "I2C_0"
help
Specify the device name of the I2C master device to which the
AK8975 chip is connected.

View file

@ -1,5 +1,6 @@
ccflags-y +=-I$(srctree)/drivers
obj-$(CONFIG_AK8975) += sensor_ak8975.o
obj-$(CONFIG_BMA280) += sensor_bma280.o
obj-$(CONFIG_BMA280_TRIGGER) += sensor_bma280_trigger.o
obj-$(CONFIG_BMC150_MAGN) += sensor_bmc150_magn.o

View file

@ -0,0 +1,155 @@
/*
* Copyright (c) 2016 Intel Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <device.h>
#include <i2c.h>
#include <nanokernel.h>
#include <sensor.h>
#include <misc/__assert.h>
#include <misc/byteorder.h>
#include <misc/util.h>
#include "sensor_ak8975.h"
static int ak8975_sample_fetch(struct device *dev, enum sensor_channel chan)
{
struct ak8975_data *drv_data = dev->driver_data;
uint8_t buf[6];
__ASSERT(chan == SENSOR_CHAN_ALL);
if (i2c_reg_write_byte(drv_data->i2c, CONFIG_AK8975_I2C_ADDR,
AK8975_REG_CNTL, AK8975_MODE_MEASURE) < 0) {
SYS_LOG_ERR("Failed to start measurement.");
return -EIO;
}
sys_thread_busy_wait(AK8975_MEASURE_TIME_US);
if (i2c_burst_read(drv_data->i2c, CONFIG_AK8975_I2C_ADDR,
AK8975_REG_DATA_START, buf, 6) < 0) {
SYS_LOG_ERR("Failed to read sample data.");
return -EIO;
}
drv_data->x_sample = sys_le16_to_cpu(buf[0] | (buf[1] << 8));
drv_data->y_sample = sys_le16_to_cpu(buf[2] | (buf[3] << 8));
drv_data->z_sample = sys_le16_to_cpu(buf[4] | (buf[5] << 8));
return 0;
}
static void ak8975_convert(struct sensor_value *val, int16_t sample,
uint8_t adjustment)
{
int32_t conv_val;
conv_val = sample * AK8975_MICRO_GAUSS_PER_BIT *
((uint16_t)adjustment + 128) / 256;
val->type = SENSOR_VALUE_TYPE_INT_PLUS_MICRO;
val->val1 = conv_val / 1000000;
val->val2 = conv_val % 1000000;
}
static int ak8975_channel_get(struct device *dev,
enum sensor_channel chan,
struct sensor_value *val)
{
struct ak8975_data *drv_data = dev->driver_data;
__ASSERT(chan == SENSOR_CHAN_MAGN_ANY || chan == SENSOR_CHAN_MAGN_X ||
chan == SENSOR_CHAN_MAGN_Y || chan == SENSOR_CHAN_MAGN_Z);
if (chan == SENSOR_CHAN_MAGN_ANY) {
ak8975_convert(val, drv_data->x_sample, drv_data->x_adj);
ak8975_convert(val + 1, drv_data->y_sample, drv_data->y_adj);
ak8975_convert(val + 2, drv_data->z_sample, drv_data->z_adj);
} else if (chan == SENSOR_CHAN_MAGN_X) {
ak8975_convert(val, drv_data->x_sample, drv_data->x_adj);
} else if (chan == SENSOR_CHAN_MAGN_Y) {
ak8975_convert(val, drv_data->y_sample, drv_data->y_adj);
} else { /* chan == SENSOR_CHAN_MAGN_Z */
ak8975_convert(val, drv_data->z_sample, drv_data->z_adj);
}
return 0;
}
static struct sensor_driver_api ak8975_driver_api = {
.sample_fetch = ak8975_sample_fetch,
.channel_get = ak8975_channel_get,
};
static int ak8975_read_adjustment_data(struct ak8975_data *drv_data)
{
uint8_t buf[3];
if (i2c_reg_write_byte(drv_data->i2c, CONFIG_AK8975_I2C_ADDR,
AK8975_REG_CNTL, AK8975_MODE_FUSE_ACCESS) < 0) {
SYS_LOG_ERR("Failed to set chip in fuse access mode.");
return -EIO;
}
if (i2c_burst_read(drv_data->i2c, CONFIG_AK8975_I2C_ADDR,
AK8975_REG_ADJ_DATA_START, buf, 3) < 0) {
SYS_LOG_ERR("Failed to read adjustment data.");
return -EIO;
}
drv_data->x_adj = buf[0];
drv_data->y_adj = buf[1];
drv_data->z_adj = buf[2];
return 0;
}
int ak8975_init(struct device *dev)
{
struct ak8975_data *drv_data = dev->driver_data;
uint8_t id;
drv_data->i2c = device_get_binding(CONFIG_AK8975_I2C_MASTER_DEV_NAME);
if (drv_data->i2c == NULL) {
SYS_LOG_ERR("Failed to get pointer to %s device!",
CONFIG_AK8975_I2C_MASTER_DEV_NAME);
return -EINVAL;
}
/* check chip ID */
if (i2c_reg_read_byte(drv_data->i2c, CONFIG_AK8975_I2C_ADDR,
AK8975_REG_CHIP_ID, &id) < 0) {
SYS_LOG_ERR("Failed to read chip ID.");
return -EIO;
}
if (id != AK8975_CHIP_ID) {
SYS_LOG_ERR("Invalid chip ID.");
return -EINVAL;
}
if (ak8975_read_adjustment_data(drv_data) < 0) {
return -EIO;
}
dev->driver_api = &ak8975_driver_api;
return 0;
}
struct ak8975_data ak8975_data;
DEVICE_INIT(ak8975, CONFIG_AK8975_NAME, ak8975_init, &ak8975_data,
NULL, SECONDARY, CONFIG_AK8975_INIT_PRIORITY);

View file

@ -0,0 +1,52 @@
/*
* Copyright (c) 2016 Intel Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef __SENSOR_AK8975__
#define __SENSOR_AK8975__
#include <device.h>
#define SYS_LOG_DOMAIN "AK8975"
#define SYS_LOG_LEVEL CONFIG_AK8975_SYS_LOG_LEVEL
#include <misc/sys_log.h>
#define AK8975_REG_CHIP_ID 0x00
#define AK8975_CHIP_ID 0x48
#define AK8975_REG_DATA_START 0x03
#define AK8975_REG_CNTL 0x0A
#define AK8975_MODE_MEASURE 0x01
#define AK8975_MODE_FUSE_ACCESS 0x0F
#define AK8975_REG_ADJ_DATA_START 0x10
#define AK8975_MEASURE_TIME_US 9000
#define AK8975_MICRO_GAUSS_PER_BIT 3000
struct ak8975_data {
struct device *i2c;
int16_t x_sample;
int16_t y_sample;
int16_t z_sample;
uint8_t x_adj;
uint8_t y_adj;
uint8_t z_adj;
};
#endif /* __SENSOR_AK8975__ */