451 lines
12 KiB
C
451 lines
12 KiB
C
|
/****************************************************************************
|
||
|
* drivers/sensors/fxos8700cq.c
|
||
|
* Driver for Motion Sensor FXOS8700CQ (NXP)
|
||
|
*
|
||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||
|
* contributor license agreements. See the NOTICE file distributed with
|
||
|
* this work for additional information regarding copyright ownership. The
|
||
|
* ASF licenses this file to you 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.
|
||
|
*
|
||
|
****************************************************************************/
|
||
|
|
||
|
/****************************************************************************
|
||
|
* Included Files
|
||
|
****************************************************************************/
|
||
|
|
||
|
#include <nuttx/config.h>
|
||
|
|
||
|
#include <stdlib.h>
|
||
|
#include <errno.h>
|
||
|
|
||
|
#include <nuttx/kmalloc.h>
|
||
|
#include <nuttx/i2c/i2c_master.h>
|
||
|
|
||
|
#include <nuttx/sensors/fxos8700cq.h>
|
||
|
|
||
|
#if defined(CONFIG_SENSORS_FXOS8700CQ)
|
||
|
|
||
|
/****************************************************************************
|
||
|
* Pre-processor Definitions
|
||
|
****************************************************************************/
|
||
|
|
||
|
#define FXOS8700CQ_I2C_ADDR 0x1d
|
||
|
#define FXOS8700CQ_I2C_FREQ 400000
|
||
|
|
||
|
/* I2C Regs */
|
||
|
|
||
|
#define FXOS8700CQ_STATUS 0x00
|
||
|
#define FXOS8700CQ_WHOAMI 0x0D
|
||
|
#define FXOS8700CQ_XYZ_DATA_CFG 0x0E
|
||
|
#define FXOS8700CQ_CTRL_REG1 0x2A
|
||
|
#define FXOS8700CQ_CTRL_REG2 0x2B
|
||
|
#define FXOS8700CQ_CTRL_REG3 0x2C
|
||
|
#define FXOS8700CQ_CTRL_REG4 0x2D
|
||
|
#define FXOS8700CQ_CTRL_REG5 0x2E
|
||
|
|
||
|
#define FXOS8700CQ_M_CTRL_REG1 0x5B
|
||
|
#define FXOS8700CQ_M_CTRL_REG2 0x5C
|
||
|
|
||
|
#define FXOS8700CQ_PULSE_CFG (0x21)
|
||
|
#define FXOS8700CQ_PULSE_SRC (0x22)
|
||
|
#define FXOS8700CQ_PULSE_THSX (0x23)
|
||
|
|
||
|
/* Values */
|
||
|
#define FXOS8700CQ_WHOAMI_VAL 0xC7
|
||
|
|
||
|
/** status byte + x,y,z for accelerometer and magnetometer */
|
||
|
#define FXOS8700CQ_READ_LEN ((8 + (16 * 3 + 16 * 3)) / 8)
|
||
|
|
||
|
/****************************************************************************
|
||
|
* Private Types
|
||
|
****************************************************************************/
|
||
|
|
||
|
struct fxos8700cq_dev_s
|
||
|
{
|
||
|
FAR struct i2c_master_s *i2c;
|
||
|
uint8_t addr;
|
||
|
int freq;
|
||
|
};
|
||
|
|
||
|
/****************************************************************************
|
||
|
* Private Functions
|
||
|
****************************************************************************/
|
||
|
|
||
|
static uint8_t fxos8700cq_getreg8(FAR struct fxos8700cq_dev_s *priv,
|
||
|
uint8_t regaddr);
|
||
|
|
||
|
static int fxos8700cq_putreg8(FAR struct fxos8700cq_dev_s *priv,
|
||
|
uint8_t regaddr, uint8_t regval);
|
||
|
|
||
|
static int fxos8700cq_getregs(FAR struct fxos8700cq_dev_s *priv,
|
||
|
uint8_t regaddr, uint8_t *regval, int len);
|
||
|
|
||
|
/* Character driver methods */
|
||
|
|
||
|
static int fxos8700cq_open(FAR struct file *filep);
|
||
|
static int fxos8700cq_close(FAR struct file *filep);
|
||
|
static ssize_t fxos8700cq_read(FAR struct file *filep,
|
||
|
FAR char *buffer, size_t len);
|
||
|
static int fxos8700cq_checkid(FAR struct fxos8700cq_dev_s *priv);
|
||
|
|
||
|
/****************************************************************************
|
||
|
* Private Data
|
||
|
****************************************************************************/
|
||
|
|
||
|
/** vtable that supports the character driver interface */
|
||
|
|
||
|
static const struct file_operations g_fxos8700cqfops =
|
||
|
{
|
||
|
fxos8700cq_open,
|
||
|
fxos8700cq_close,
|
||
|
fxos8700cq_read,
|
||
|
0, /* write */
|
||
|
0, /* seek */
|
||
|
0, /* ioctl */
|
||
|
};
|
||
|
|
||
|
/****************************************************************************
|
||
|
* Name: fxos8700cq_getreg8
|
||
|
*
|
||
|
* Description:
|
||
|
* Read from an 8-bit FXOS8700CQ register
|
||
|
*
|
||
|
****************************************************************************/
|
||
|
|
||
|
static uint8_t fxos8700cq_getreg8(FAR struct fxos8700cq_dev_s *priv,
|
||
|
uint8_t regaddr)
|
||
|
{
|
||
|
uint8_t regval = 0;
|
||
|
|
||
|
struct i2c_msg_s msg[2];
|
||
|
int ret;
|
||
|
|
||
|
msg[0].frequency = priv->freq;
|
||
|
msg[0].addr = priv->addr;
|
||
|
msg[0].flags = 0;
|
||
|
msg[0].buffer = ®addr;
|
||
|
msg[0].length = 1;
|
||
|
|
||
|
msg[1].frequency = priv->freq;
|
||
|
msg[1].addr = priv->addr;
|
||
|
msg[1].flags = I2C_M_READ;
|
||
|
msg[1].buffer = ®val;
|
||
|
msg[1].length = 1;
|
||
|
|
||
|
ret = I2C_TRANSFER(priv->i2c, msg, 2);
|
||
|
|
||
|
if (ret < 0)
|
||
|
{
|
||
|
snerr("I2C_TRANSFER failed: %d\n", ret);
|
||
|
}
|
||
|
|
||
|
return regval;
|
||
|
}
|
||
|
|
||
|
/****************************************************************************
|
||
|
* Name: fxos8700cq_putreg8
|
||
|
*
|
||
|
* Description:
|
||
|
* Write a value to an 8-bit FXOS8700CQ register
|
||
|
*
|
||
|
****************************************************************************/
|
||
|
|
||
|
static int fxos8700cq_putreg8(FAR struct fxos8700cq_dev_s *priv,
|
||
|
uint8_t regaddr, uint8_t regval)
|
||
|
{
|
||
|
struct i2c_msg_s msg[2];
|
||
|
int ret;
|
||
|
uint8_t txbuffer[2];
|
||
|
|
||
|
txbuffer[0] = regaddr;
|
||
|
txbuffer[1] = regval;
|
||
|
|
||
|
msg[0].frequency = priv->freq;
|
||
|
msg[0].addr = priv->addr;
|
||
|
msg[0].flags = 0;
|
||
|
msg[0].buffer = txbuffer;
|
||
|
msg[0].length = 2;
|
||
|
|
||
|
ret = I2C_TRANSFER(priv->i2c, msg, 1);
|
||
|
if (ret < 0)
|
||
|
{
|
||
|
snerr("I2C_TRANSFER failed: %d\n", ret);
|
||
|
}
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
/****************************************************************************
|
||
|
* Name: fxos8700cq_getregs
|
||
|
*
|
||
|
* Description:
|
||
|
* Read cnt bytes from specified dev_addr and reg_addr
|
||
|
*
|
||
|
****************************************************************************/
|
||
|
|
||
|
static int fxos8700cq_getregs(FAR struct fxos8700cq_dev_s *priv,
|
||
|
uint8_t regaddr, uint8_t *regval, int len)
|
||
|
{
|
||
|
struct i2c_msg_s msg[2];
|
||
|
int ret;
|
||
|
|
||
|
msg[0].frequency = priv->freq;
|
||
|
msg[0].addr = priv->addr;
|
||
|
msg[0].flags = 0;
|
||
|
msg[0].buffer = ®addr;
|
||
|
msg[0].length = 1;
|
||
|
|
||
|
msg[1].frequency = priv->freq;
|
||
|
msg[1].addr = priv->addr;
|
||
|
msg[1].flags = I2C_M_READ;
|
||
|
msg[1].buffer = regval;
|
||
|
msg[1].length = len;
|
||
|
|
||
|
ret = I2C_TRANSFER(priv->i2c, msg, 2);
|
||
|
if (ret < 0)
|
||
|
{
|
||
|
snerr("I2C_TRANSFER failed: %d\n", ret);
|
||
|
}
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
/****************************************************************************
|
||
|
* Name: fxos8700cq_open
|
||
|
*
|
||
|
* Description:
|
||
|
* Standard character driver open method.
|
||
|
*
|
||
|
****************************************************************************/
|
||
|
|
||
|
static int fxos8700cq_open(FAR struct file *filep)
|
||
|
{
|
||
|
FAR struct inode *inode = filep->f_inode;
|
||
|
FAR struct fxos8700cq_dev_s *priv = inode->i_private;
|
||
|
|
||
|
uint8_t regval;
|
||
|
int ret = 0;
|
||
|
ret = fxos8700cq_getreg8(priv, FXOS8700CQ_WHOAMI);
|
||
|
if (ret != FXOS8700CQ_WHOAMI_VAL)
|
||
|
{
|
||
|
snerr("ERROR: Invalid device identification %02x\n", regval);
|
||
|
return -ENODEV;
|
||
|
}
|
||
|
|
||
|
regval = 0x00; /* active: standbye */
|
||
|
ret = fxos8700cq_putreg8(priv, FXOS8700CQ_CTRL_REG1, regval);
|
||
|
|
||
|
if (ret < 0)
|
||
|
{
|
||
|
snerr("ERROR: %02x\n", regval);
|
||
|
return (ret);
|
||
|
}
|
||
|
|
||
|
/* m_hms[1:0]: Hybrid mode, both accelerometer and magnetometer
|
||
|
* m_os[2:0]: Oversample ratio (OSR) for magnetometer data
|
||
|
* m_ost: One-shot triggered magnetic measurement mode
|
||
|
* m_rst: One-shot magnetic reset degauss control bit
|
||
|
* m_acal: Magnetic hard-iron offset auto-calibration enable
|
||
|
*/
|
||
|
|
||
|
regval = 0
|
||
|
| (0b11 << 0)
|
||
|
| (0b11 << 3)
|
||
|
| (0b1 << 5)
|
||
|
| (0b0 << 6)
|
||
|
| (0b0 << 7)
|
||
|
;
|
||
|
ret = fxos8700cq_putreg8(priv, FXOS8700CQ_M_CTRL_REG1, regval);
|
||
|
if (ret < 0)
|
||
|
{
|
||
|
return (ret);
|
||
|
}
|
||
|
|
||
|
/* hyb_autoinc_mode */
|
||
|
|
||
|
regval = 0b1 << 5;
|
||
|
ret = fxos8700cq_putreg8(priv, FXOS8700CQ_M_CTRL_REG2, regval);
|
||
|
if (ret < 0)
|
||
|
{
|
||
|
return (ret);
|
||
|
}
|
||
|
|
||
|
/* fs[1:0]: Accelerometer full-scale range : 4G */
|
||
|
|
||
|
regval = 0b01;
|
||
|
ret = fxos8700cq_putreg8(priv, FXOS8700CQ_XYZ_DATA_CFG, regval);
|
||
|
if (ret < 0)
|
||
|
{
|
||
|
return (ret);
|
||
|
}
|
||
|
|
||
|
/* active , lnoise , dr[2:0] */
|
||
|
|
||
|
regval = 0b1
|
||
|
| (0b1 < 2)
|
||
|
| (0b001 < 5)
|
||
|
;
|
||
|
ret = fxos8700cq_putreg8(priv, FXOS8700CQ_CTRL_REG1, regval);
|
||
|
if (ret < 0)
|
||
|
{
|
||
|
return (ret);
|
||
|
}
|
||
|
|
||
|
return OK;
|
||
|
}
|
||
|
|
||
|
/****************************************************************************
|
||
|
* Name: fxos8700cq_close
|
||
|
*
|
||
|
* Description:
|
||
|
* Standard character driver close method.
|
||
|
*
|
||
|
****************************************************************************/
|
||
|
|
||
|
static int fxos8700cq_close(FAR struct file *filep)
|
||
|
{
|
||
|
FAR struct inode *inode = filep->f_inode;
|
||
|
FAR struct fxos8700cq_dev_s *priv = inode->i_private;
|
||
|
|
||
|
uint8_t regval = 0b0; /* active */
|
||
|
int ret = fxos8700cq_putreg8(priv, FXOS8700CQ_CTRL_REG1, regval);
|
||
|
if (ret < 0)
|
||
|
{
|
||
|
return (ret);
|
||
|
}
|
||
|
|
||
|
return OK;
|
||
|
}
|
||
|
|
||
|
/****************************************************************************
|
||
|
* Name: fxos8700cq_read
|
||
|
*
|
||
|
* Description:
|
||
|
* Standard character driver read method.
|
||
|
*
|
||
|
****************************************************************************/
|
||
|
|
||
|
static ssize_t fxos8700cq_read(FAR struct file *filep, FAR char *buffer,
|
||
|
size_t len)
|
||
|
{
|
||
|
FAR struct inode *inode = filep->f_inode;
|
||
|
FAR struct fxos8700cq_dev_s *priv = inode->i_private;
|
||
|
|
||
|
FAR fxos8700cq_data * p = (FAR fxos8700cq_data *) buffer;
|
||
|
if (len < sizeof(fxos8700cq_data))
|
||
|
{
|
||
|
snerr("Expected buffer size is %d\n", sizeof(fxos8700cq_data));
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
uint8_t data[FXOS8700CQ_READ_LEN];
|
||
|
int status = fxos8700cq_getregs(priv, FXOS8700CQ_STATUS,
|
||
|
(FAR uint8_t *)data, FXOS8700CQ_READ_LEN);
|
||
|
if (status < 0)
|
||
|
{
|
||
|
snerr("Error: read status=%d", status);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/* Copy the 14 bit accelerometer byte data into 16 bit words */
|
||
|
|
||
|
p->accel.x = (int16_t)(((data[1] << 8) | data[2])) >> 2;
|
||
|
p->accel.y = (int16_t)(((data[3] << 8) | data[4])) >> 2;
|
||
|
p->accel.z = (int16_t)(((data[5] << 8) | data[6])) >> 2;
|
||
|
|
||
|
/* Copy the magnetometer byte data into 16 bit words */
|
||
|
|
||
|
p->magn.x = (int16_t)(((data[7] << 8) | data[8]));
|
||
|
p->magn.y = (int16_t)(((data[9] << 8) | data[10]));
|
||
|
p->magn.z = (int16_t)(((data[11] << 8) | data[12]));
|
||
|
|
||
|
return len;
|
||
|
}
|
||
|
|
||
|
/****************************************************************************
|
||
|
* Name: fxos8700cq_checkid
|
||
|
*
|
||
|
* Description:
|
||
|
* Read and verify the FXOS8700CQ chip ID
|
||
|
*
|
||
|
****************************************************************************/
|
||
|
|
||
|
static int fxos8700cq_checkid(FAR struct fxos8700cq_dev_s *priv)
|
||
|
{
|
||
|
uint8_t devid = 0;
|
||
|
devid = fxos8700cq_getreg8(priv, FXOS8700CQ_WHOAMI);
|
||
|
sninfo("devid: %04x\n", devid);
|
||
|
if (devid != (uint16_t) FXOS8700CQ_WHOAMI_VAL)
|
||
|
{
|
||
|
return -ENODEV;
|
||
|
}
|
||
|
|
||
|
return OK;
|
||
|
}
|
||
|
|
||
|
/****************************************************************************
|
||
|
* Name: fxos8700cq_register
|
||
|
*
|
||
|
* Description:
|
||
|
* Register the FXOS8700CQ character device as 'devpath'
|
||
|
*
|
||
|
* Input Parameters:
|
||
|
* devpath - The full path to the driver to register. E.g., "/dev/accel0"
|
||
|
* dev - An instance of the I2C interface to communicate with device
|
||
|
*
|
||
|
*
|
||
|
* Returned Value:
|
||
|
* Zero (OK) on success; a negated errno value on failure.
|
||
|
*
|
||
|
****************************************************************************/
|
||
|
|
||
|
int fxos8700cq_register(FAR const char *devpath,
|
||
|
FAR struct i2c_master_s *dev)
|
||
|
{
|
||
|
FAR struct fxos8700cq_dev_s *priv;
|
||
|
int ret;
|
||
|
|
||
|
priv = (FAR struct fxos8700cq_dev_s *)
|
||
|
kmm_malloc(sizeof(struct fxos8700cq_dev_s));
|
||
|
|
||
|
if (!priv)
|
||
|
{
|
||
|
snerr("Failed to allocate instance\n");
|
||
|
return -ENOMEM;
|
||
|
}
|
||
|
|
||
|
priv->i2c = dev;
|
||
|
priv->addr = FXOS8700CQ_I2C_ADDR;
|
||
|
priv->freq = FXOS8700CQ_I2C_FREQ;
|
||
|
|
||
|
ret = fxos8700cq_checkid(priv);
|
||
|
if (ret < 0)
|
||
|
{
|
||
|
snerr("Wrong Device ID!\n");
|
||
|
kmm_free(priv);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
ret = register_driver(devpath, &g_fxos8700cqfops, 0666, priv);
|
||
|
if (ret < 0)
|
||
|
{
|
||
|
snerr("Failed to register driver: %d\n", ret);
|
||
|
kmm_free(priv);
|
||
|
}
|
||
|
|
||
|
sninfo("FXOS8700CQ driver loaded successfully!\n");
|
||
|
return OK;
|
||
|
}
|
||
|
|
||
|
#endif /* CONFIG_SENSORS_FXOS8700CQ */
|