Compare commits

...

6 commits

20 changed files with 1332 additions and 0 deletions

13
.gitignore vendored Normal file
View file

@ -0,0 +1,13 @@
.pio
*~
*credential.h
# -*- mode: gitignore; -*-
*~
\#*\#
/.emacs.desktop
/.emacs.desktop.lock
*.elc
auto-save-list
tramp
.\#*

View file

@ -0,0 +1,25 @@
// HSPI pins
#define HSPI_MISO 12
#define HSPI_MOSI 13
#define HSPI_SCLK 14
#define HSPI_SS 15
// ePaper display
#define EPD_DC 25
#define EPD_CS 27
#define EPD_BUSY 35
#define SRAM_CS -1
#define EPD_RESET 26
#define EPD_SPI &SPI3
// I2C
#define ESP_SDA 22
#define ESP_SCL 38
// ATMEL UART Bridge
#define UART_RXD (33u)
#define UART_TXD (32U)
// IMU
#define ESP32_I2C_IMU_SDA ((int)22)
#define ESP32_I2C_IMU_SCL ((int)19)

344
esp32/include/icm20602.h Normal file
View file

@ -0,0 +1,344 @@
#ifndef _ICM20602_H
#define _ICM20602_H
/***** Includes *****/
#include <stdint.h>
#include <stdbool.h>
/***** Macros *****/
/** This initializer will zero out the ICM20602 struct for initialization
* purposes. This is advised as to avoid having uninitialized garbage values
* left within the struct */
#define ICM20602_INIT() \
{ \
.id = 0, \
.hal_wr = NULL, \
.hal_rd = NULL, \
.hal_sleep = NULL, \
.mutex_lock = NULL, \
.mutex_unlock = NULL, \
.use_accel = false, \
.accel_fifo = false, \
.accel_dlpf = 0, \
.accel_g = 0, \
.use_gyro = false, \
.gyro_fifo = false, \
.gyro_dlpf = 0, \
.gyro_dps = 0, \
.sample_rate_div = 1, \
.i2c_disable = false, \
}
/** This initializer will configure the ICM20602 with settings that should
* yield some actual output on both the gyroscope and the accelerometer. All
* that the developer should need to set manually are the "hal_wr", "hal_rd",
* and "hal_sleep" function pointers. It is recommended to use this for
* testing purposes. */
#define ICM20602_DEFAULT_INIT() \
{ \
.id = 0, \
.hal_wr = NULL, \
.hal_rd = NULL, \
.hal_sleep = NULL, \
.mutex_lock = NULL, \
.mutex_unlock = NULL, \
.use_accel = true, \
.accel_fifo = false, \
.accel_dlpf = ICM20602_ACCEL_DLPF_10_2_HZ, \
.accel_g = ICM20602_ACCEL_RANGE_4G, \
.use_gyro = true, \
.gyro_fifo = false, \
.gyro_dlpf = ICM20602_GYRO_DLPF_10_HZ, \
.gyro_dps = ICM20602_GYRO_RANGE_2000_DPS, \
.sample_rate_div = 100, \
.i2c_disable = false, \
}
/***** Typedefs *****/
/** \brief Function pointer for write function.
* \param id the ID value of the icm20602 device struct
* \param reg ICM20602 register address to target
* \param data pointer to data to write
* \param len number of bytes to write
* \return zero on success, anything else is an error
*/
typedef int8_t (*icm20602_hal_wr)(uint8_t id, uint8_t reg, uint8_t * data,
uint16_t len);
/** \brief Function pointer for read function.
* \param id the ID value of the icm20602 device struct
* \param reg ICM20602 register address to target
* \param data pointer to data to read
* \param len number of bytes to read
* \return zero on success, anything else is an error
*/
typedef int8_t (*icm20602_hal_rd)(uint8_t id, uint8_t reg, uint8_t * data,
uint16_t len);
/** \brief Function pointer for sleep function.
* \param ms the total number of milliseconds to sleep for
* \return void
*/
typedef void (*icm20602_hal_sleep)(uint32_t ms);
/** \brief Function pointer for mutex locking function.
* \param id the ID value of the icm20602 device struct
* \return void
*/
typedef void (*icm20602_mutex_lock)(uint8_t id);
/** \brief Function pointer for mutex unlocking function.
* \param id the ID value of the icm20602 device struct
* \return void
*/
typedef void (*icm20602_mutex_unlock)(uint8_t id);
/***** Enums *****/
/** Enumerated value corresponds with A_DLPF_CFG in the ACCEL_CONFIG2 register
* unless BYPASS is specified in the name. If BYPASS is used, the DLPF is
* removed from the signal path and ACCEL_FCHOICE_B is set in the
* ACCEL_CONFIG2 register. */
enum icm20602_accel_dlpf {
ICM20602_ACCEL_DLPF_218_1_HZ = 0, // data clocked at 1kHz
ICM20602_ACCEL_DLPF_99_HZ = 2, // data clocked at 1kHz
ICM20602_ACCEL_DLPF_44_8_HZ = 3, // data clocked at 1kHz
ICM20602_ACCEL_DLPF_21_2_HZ = 4, // data clocked at 1kHz
ICM20602_ACCEL_DLPF_10_2_HZ = 5, // data clocked at 1kHz
ICM20602_ACCEL_DLPF_5_1_HZ = 6, // data clocked at 1kHz
ICM20602_ACCEL_DLPF_420_HZ = 7, // data clocked at 1kHz
ICM20602_ACCEL_DLPF_BYPASS_1046_HZ, // no filter, data clocked at 4kHz
};
/** Enumerated value corresponds with ACCEL_FS_SEL in the ACCEL_CONFIG
* register. Values listed are the full +/- G range. */
enum icm20602_accel_g {
ICM20602_ACCEL_RANGE_2G = 0,
ICM20602_ACCEL_RANGE_4G = 1,
ICM20602_ACCEL_RANGE_8G = 2,
ICM20602_ACCEL_RANGE_16G = 3,
};
/** Enumerated value corresponds with DLPF_CFG in the CONFIG register unless
* BYPASS is specified in the name. If BYPASS is used, the DLPF is removed
* from the signal path and FCHOICE_B is set in GYRO_CONFIG register. */
enum icm20602_gyro_dlpf {
ICM20602_GYRO_DLPF_250_HZ = 0, // data clocked at 8kHz
ICM20602_GYRO_DLPF_176_HZ = 1, // data clocked at 1kHz
ICM20602_GYRO_DLPF_92_HZ = 2, // data clocked at 1kHz
ICM20602_GYRO_DLPF_41_HZ = 3, // data clocked at 1kHz
ICM20602_GYRO_DLPF_20_HZ = 4, // data clocked at 1kHz
ICM20602_GYRO_DLPF_10_HZ = 5, // data clocked at 1kHz
ICM20602_GYRO_DLPF_5_HZ = 6, // data clocked at 1kHz
ICM20602_GYRO_DLPF_3281_HZ = 7, // data clocked at 8kHz
ICM20602_GYRO_DLPF_BYPASS_3281_HZ, // no filter, data clocked at 32kHz
ICM20602_GYRO_DLPF_BYPASS_8173_HZ, // no filter, data clocked at 32kHz
};
/** Enumerated value corresponds with FS_SEL in the GYRO_CONFIG register.
* Values listed are the full +/- DPS range. */
enum icm20602_gyro_dps {
ICM20602_GYRO_RANGE_250_DPS = 0,
ICM20602_GYRO_RANGE_500_DPS = 1,
ICM20602_GYRO_RANGE_1000_DPS = 2,
ICM20602_GYRO_RANGE_2000_DPS = 3,
};
/***** Structs *****/
struct icm20602_dev {
/// Identifier, can be I2C address, SPI CS line, or some other unique value.
uint8_t id;
/// Required function pointer for register write function.
icm20602_hal_wr hal_wr;
/// Required function pointer for register read function.
icm20602_hal_rd hal_rd;
/// Required function pointer for system sleep/delay.
icm20602_hal_sleep hal_sleep;
/// Optional function pointer to mutex lock if needed, NULL otherwise.
icm20602_mutex_lock mutex_lock;
/// Optional function pointer to mutex unlocking if needed, NULL otherwise.
icm20602_mutex_lock mutex_unlock;
/// Set to "true" to configure the accelerometer.
bool use_accel;
/// Enable or disable fifo for accelerometer.
bool accel_fifo;
/// Select the digital low pass filter to use with the accelerometer.
enum icm20602_accel_dlpf accel_dlpf;
/// Select the accelerometer's g-force range.
enum icm20602_accel_g accel_g;
/// Set to "true" to configure the gyroscope.
bool use_gyro;
/// Enable or disable fifo for gyroscope.
bool gyro_fifo;
/// Select the digital low pass filter to use with the gyroscope.
enum icm20602_gyro_dlpf gyro_dlpf;
/// Select the gyroscope's degrees per second range.
enum icm20602_gyro_dps gyro_dps;
/// Divides the data clock for both the accelerometer and gyroscope.
uint8_t sample_rate_div;
/// Disable hardware I2C communications to chip, recommeded if using SPI.
bool i2c_disable;
};
/***** Global Functions *****/
/** \brief Initializes the ICM20602 sensor.
* \param config pointer to configuration struct
* \return zero on success, anything else is an error
*/
extern int8_t
icm20602_init(struct icm20602_dev * dev);
/** \brief Reads current G-force values of accelerometer.
* \param p_x destination for x G value
* \param p_y destination for y G value
* \param p_z destination for z G value
* \return zero on success, anything else is an error
*/
extern int8_t
icm20602_read_accel(struct icm20602_dev * dev, float * p_x, float * p_y,
float * p_z);
/** \brief Reads current degrees per second values of gyroscope.
* \param p_x destination for x value
* \param p_y destination for y value
* \param p_z destination for z value
* \return zero on success, anything else is an error
*/
extern int8_t
icm20602_read_gyro(struct icm20602_dev * dev, float * p_x, float * p_y,
float * p_z);
/** \brief Reads current values of accelerometer and gyroscope.
* \param p_ax destination for accelerometer x G value
* \param p_ay destination for accelerometer y G value
* \param p_az destination for accelerometer z G value
* \param p_gx destination for gyroscope x DPS value
* \param p_gy destination for gyroscope y DPS value
* \param p_gz destination for gyroscope z DPS value
* \param p_t destination for temperature degrees C value
* \return zero on success, anything else is an error
*/
extern int8_t
icm20602_read_data(struct icm20602_dev * dev, float * p_ax, float * p_ay,
float * p_az, float * p_gx, float * p_gy, float * p_gz, float * p_t);
/** \brief Reads current raw values of accelerometer.
* \param p_x destination for x value
* \param p_y destination for y value
* \param p_z destination for z value
* \return zero on success, anything else is an error
*/
extern int8_t
icm20602_read_accel_raw(struct icm20602_dev * dev, int16_t * p_x, int16_t * p_y,
int16_t * p_z);
/** \brief Reads current raw values of gyroscope.
* \param p_x destination for x value
* \param p_y destination for y value
* \param p_z destination for z value
* \return zero on success, anything else is an error
*/
extern int8_t
icm20602_read_gyro_raw(struct icm20602_dev * dev, int16_t * p_x, int16_t * p_y,
int16_t * p_z);
/** \brief Reads current raw values of accelerometer and gyroscope.
* \param p_ax destination for accelerometer x value
* \param p_ay destination for accelerometer y value
* \param p_az destination for accelerometer z value
* \param p_gx destination for gyroscope x value
* \param p_gy destination for gyroscope y value
* \param p_gz destination for gyroscope z value
* \param p_t destination for temperature value
* \return zero on success, anything else is an error
*/
extern int8_t
icm20602_read_data_raw(struct icm20602_dev * dev, int16_t * p_ax,
int16_t * p_ay, int16_t * p_az, int16_t * p_gx, int16_t * p_gy,
int16_t * p_gz, int16_t * p_t);
/** \brief Reads FIFO G-force values of accelerometer.
* \param p_x destination for x G value
* \param p_y destination for y G value
* \param p_z destination for z G value
* \return zero on success, anything else is an error
*/
extern int8_t
icm20602_read_accel_fifo(struct icm20602_dev * dev, float * x, float * y,
float * z);
/** \brief Reads FIFO degrees per second values of gyroscope.
* \param p_x destination for x value
* \param p_y destination for y value
* \param p_z destination for z value
* \return zero on success, anything else is an error
*/
extern int8_t
icm20602_read_gyro_fifo(struct icm20602_dev * dev, float * x, float * y,
float * z);
/** \brief Reads FIFO values of accelerometer and gyroscope. Note, both
* accelerometer and gyroscope fifos should be enabled if this
* function is to be used.
* \param p_ax destination for accelerometer x G value
* \param p_ay destination for accelerometer y G value
* \param p_az destination for accelerometer z G value
* \param p_gx destination for gyroscope x DPS value
* \param p_gy destination for gyroscope y DPS value
* \param p_gz destination for gyroscope z DPS value
* \param p_t destination for temperature degrees C value
* \return zero on success, anything else is an error
*/
extern int8_t
icm20602_read_fifo_data(struct icm20602_dev * dev, float * p_ax, float * p_ay,
float * p_az, float * p_gx, float * p_gy, float * p_gz, float * p_t);
/** \brief Reads FIFO raw values of accelerometer.
* \param p_x destination for x value
* \param p_y destination for y value
* \param p_z destination for z value
* \return zero on success, anything else is an error
*/
extern int8_t
icm20602_read_fifo_accel_raw(struct icm20602_dev * dev, int16_t * p_x,
int16_t * p_y, int16_t * p_z);
/** \brief Reads FIFO raw values of gyroscope.
* \param p_x destination for x value
* \param p_y destination for y value
* \param p_z destination for z value
* \return zero on success, anything else is an error
*/
extern int8_t
icm20602_read_fifo_gyro_raw(struct icm20602_dev * dev, int16_t * p_x,
int16_t * p_y, int16_t * p_z);
/** \brief Reads FIFO raw values of accelerometer and gyroscope. Note, both
* accelerometer and gyroscope fifos should be enabled if this
* function is to be used.
* \param p_ax destination for accelerometer x value
* \param p_ay destination for accelerometer y value
* \param p_az destination for accelerometer z value
* \param p_gx destination for gyroscope x value
* \param p_gy destination for gyroscope y value
* \param p_gz destination for gyroscope z value
* \param p_t destination for temperature value
* \return zero on success, anything else is an error
*/
extern int8_t
icm20602_read_fifo_data_raw(struct icm20602_dev * dev, int16_t * p_ax,
int16_t * p_ay, int16_t * p_az, int16_t * p_gx, int16_t * p_gy,
int16_t * p_gz, int16_t * p_t);
#endif

549
esp32/src/icm20602.c Normal file
View file

@ -0,0 +1,549 @@
/***** Includes *****/
#include <stdlib.h>
#include "icm20602.h"
/***** Defines *****/
#define REG_XG_OFFS_TC_H 0x04
#define REG_XG_OFFS_TC_L 0x05
#define REG_YG_OFFS_TC_H 0x07
#define REG_YG_OFFS_TC_L 0x08
#define REG_ZG_OFFS_TC_H 0x0A
#define REG_ZG_OFFS_TC_L 0x0B
#define REG_SELF_TEST_X_ACCEL 0x0D
#define REG_SELF_TEST_Y_ACCEL 0x0E
#define REG_SELF_TEST_Z_ACCEL 0x0F
#define REG_XG_OFFS_USRH 0x13
#define REG_XG_OFFS_USRL 0x14
#define REG_YG_OFFS_USRH 0x15
#define REG_YG_OFFS_USRL 0x16
#define REG_ZG_OFFS_USRH 0x17
#define REG_ZG_OFFS_USRL 0x18
#define REG_SMPLRT_DIV 0x19
#define REG_CONFIG 0x1A
#define REG_GYRO_CONFIG 0x1B
#define REG_ACCEL_CONFIG 0x1C
#define REG_ACCEL_CONFIG_2 0x1D
#define REG_LP_MODE_CFG 0x1E
#define REG_ACCEL_WOM_X_THR 0x20
#define REG_ACCEL_WOM_Y_THR 0x21
#define REG_ACCEL_WOM_Z_THR 0x22
#define REG_FIFO_EN 0x23
#define REG_FSYNC_INT 0x36
#define REG_INT_PIN_CFG 0x37
#define REG_INT_ENABLE 0x38
#define REG_FIFO_WM_INT_STATUS 0x39
#define REG_INT_STATUS 0x3A
#define REG_ACCEL_XOUT_H 0x3B
#define REG_ACCEL_XOUT_L 0x3C
#define REG_ACCEL_YOUT_H 0x3D
#define REG_ACCEL_YOUT_L 0x3E
#define REG_ACCEL_ZOUT_H 0x3F
#define REG_ACCEL_ZOUT_L 0x40
#define REG_TEMP_OUT_H 0x41
#define REG_TEMP_OUT_L 0x42
#define REG_GYRO_XOUT_H 0x43
#define REG_GYRO_XOUT_L 0x44
#define REG_GYRO_YOUT_H 0x45
#define REG_GYRO_YOUT_L 0x46
#define REG_GYRO_ZOUT_H 0x47
#define REG_GYRO_ZOUT_L 0x48
#define REG_SELF_TEST_X_GYRO 0x50
#define REG_SELF_TEST_Y_GYRO 0x51
#define REG_SELF_TEST_Z_GYRO 0x52
#define REG_FIFO_WM_TH1 0x60
#define REG_FIFO_WM_TH2 0x61
#define REG_SIGNAL_PATH_RESET 0x68
#define REG_ACCEL_INTEL_CTRL 0x69
#define REG_USER_CTRL 0x6A
#define REG_PWR_MGMT_1 0x6B
#define REG_PWR_MGMT_2 0x6C
#define REG_I2C_IF 0x70
#define REG_FIFO_COUNTH 0x72
#define REG_FIFO_COUNTL 0x73
#define REG_FIFO_R_W 0x74
#define REG_WHO_AM_I 0x75
#define REG_XA_OFFSET_H 0x77
#define REG_XA_OFFSET_L 0x78
#define REG_YA_OFFSET_H 0x7A
#define REG_YA_OFFSET_L 0x7B
#define REG_ZA_OFFSET_H 0x7D
#define REG_ZA_OFFSET_L 0x7E
#define REG_WHO_AM_I_CONST 0X12
/***** Macros *****/
#define ON_ERROR_GOTO(cond, symbol) \
if (!(cond)) { goto symbol; }
// extra steps, but I'm paranoid about some compilers/systems not handling the
// conversion from uint8_t to int16_t correctly
#define UINT8_TO_INT16(dst, src_high, src_low) \
do { \
dst = (src_high); \
dst <<= 8; \
dst |= (src_low); \
} while (0);
/***** Local Data *****/
// TODO: Look into getting real temp sensitivity.
static float _temp_sensitivity = 326.8;
/***** Local Functions *****/
/// Used to convert raw accelerometer readings to G-force.
float
_get_accel_sensitivity(enum icm20602_accel_g accel_g)
{
float f = 0.0;
switch (accel_g) {
case (ICM20602_ACCEL_RANGE_2G):
f = 16384.0;
break;
case (ICM20602_ACCEL_RANGE_4G):
f = 8192.0;
break;
case (ICM20602_ACCEL_RANGE_8G):
f = 4096.0;
break;
case (ICM20602_ACCEL_RANGE_16G):
f = 2048.0;
break;
}
return f;
}
/// Used to convert raw gyroscope readings to degrees per second.
float
_get_gyro_sensitivity(enum icm20602_gyro_dps gyro_dps)
{
float f = 0;
switch (gyro_dps) {
case (ICM20602_GYRO_RANGE_250_DPS):
f = 131.0;
break;
case (ICM20602_GYRO_RANGE_500_DPS):
f = 65.5;
break;
case (ICM20602_GYRO_RANGE_1000_DPS):
f = 32.8;
break;
case (ICM20602_GYRO_RANGE_2000_DPS):
f = 16.4;
break;
}
return f;
}
int8_t
_read_data(struct icm20602_dev * dev, uint8_t reg, uint8_t * buf, uint32_t len)
{
int8_t r = 0;
if ((!dev->hal_wr) || (!dev->hal_rd) || (!dev->hal_sleep)) {
return false;
}
if (dev->mutex_lock) {
dev->mutex_lock(dev->id);
}
r = dev->hal_rd(dev->id, reg, buf, len);
if (dev->mutex_unlock) {
dev->mutex_unlock(dev->id);
}
return r;
}
/***** Global Functions *****/
int8_t
icm20602_init(struct icm20602_dev * dev)
{
uint8_t tmp = 0;
int8_t r = 0;
if ((!dev->hal_wr) || (!dev->hal_rd) || (!dev->hal_sleep)) {
return false;
}
// General Procedure:
// 1. reset chip
// 2. set clock for PLL for optimum performance as documented in datasheet
// 3. place accelerometer and gyroscope into standby
// 4. disable fifo
// 5. configure chip
// 6. enable accelerometer and gyroscope
if (dev->mutex_lock) {
dev->mutex_lock(dev->id);
}
// full reset of chip
tmp = 0x80;
r = dev->hal_wr(dev->id, REG_PWR_MGMT_1, &tmp, 1);
ON_ERROR_GOTO((0 == r), return_err);
// TODO: better reset delay value
dev->hal_sleep(1000);
// verify we are able to read from the chip
r = dev->hal_rd(dev->id, REG_WHO_AM_I, &tmp, 1);
ON_ERROR_GOTO((0 == r), return_err);
if (REG_WHO_AM_I_CONST != tmp) {
r = -1;
ON_ERROR_GOTO((0 == r), return_err);
}
// set clock to internal PLL
tmp = 0x01;
r = dev->hal_wr(dev->id, REG_PWR_MGMT_1, &tmp, 1);
ON_ERROR_GOTO((0 == r), return_err);
// place accel and gyro on standby
tmp = 0x3F;
r = dev->hal_wr(dev->id, REG_PWR_MGMT_2, &tmp, 1);
ON_ERROR_GOTO((0 == r), return_err);
// disable fifo
tmp = 0x00;
r = dev->hal_wr(dev->id, REG_USER_CTRL, &tmp, 1);
ON_ERROR_GOTO((0 == r), return_err);
if (dev->i2c_disable) {
// disable chip I2C communications
tmp = 0x40;
r = dev->hal_wr(dev->id, REG_I2C_IF, &tmp, 1);
ON_ERROR_GOTO((0 == r), return_err);
}
if (dev->use_accel) {
if (ICM20602_ACCEL_DLPF_BYPASS_1046_HZ == dev->accel_dlpf) {
tmp = (1 << 3);
r = dev->hal_wr(dev->id, REG_ACCEL_CONFIG_2, &tmp, 1);
ON_ERROR_GOTO((0 == r), return_err);
}
else {
tmp = dev->accel_dlpf;
r = dev->hal_wr(dev->id, REG_ACCEL_CONFIG_2, &tmp, 1);
ON_ERROR_GOTO((0 == r), return_err);
}
tmp = (dev->accel_g) << 2;
r = dev->hal_wr(dev->id, REG_ACCEL_CONFIG, &tmp, 1);
ON_ERROR_GOTO((0 == r), return_err);
}
if (dev->use_gyro) {
if (ICM20602_GYRO_DLPF_BYPASS_3281_HZ == dev->gyro_dlpf) {
// bypass dpf and set dps
tmp = 0x00;
r = dev->hal_wr(dev->id, REG_CONFIG, &tmp, 1);
ON_ERROR_GOTO((0 == r), return_err);
tmp = (dev->gyro_dps << 3) | 0x02; // see table page 37 of datasheet
r = dev->hal_wr(dev->id, REG_GYRO_CONFIG, &tmp, 1);
ON_ERROR_GOTO((0 == r), return_err);
}
else if (ICM20602_GYRO_DLPF_BYPASS_8173_HZ == dev->gyro_dlpf) {
// bypass dpf and set dps
tmp = 0x00;
r = dev->hal_wr(dev->id, REG_CONFIG, &tmp, 1);
ON_ERROR_GOTO((0 == r), return_err);
tmp = (dev->gyro_dps << 3) | 0x01; // see table page 37 of datasheet
r = dev->hal_wr(dev->id, REG_GYRO_CONFIG, &tmp, 1);
ON_ERROR_GOTO((0 == r), return_err);
}
else {
// configure dpf and set dps
tmp = dev->gyro_dlpf;
r = dev->hal_wr(dev->id, REG_CONFIG, &tmp, 1);
ON_ERROR_GOTO((0 == r), return_err);
tmp = dev->gyro_dps << 3;
r = dev->hal_wr(dev->id, REG_GYRO_CONFIG, &tmp, 1);
ON_ERROR_GOTO((0 == r), return_err);
}
}
// enable FIFO if requested
tmp = ((dev->use_accel) && (dev->accel_fifo)) ? 0x08 : 0x00;
tmp |= ((dev->use_gyro) && (dev->gyro_fifo)) ? 0x10 : 0x00;
r = dev->hal_wr(dev->id, REG_FIFO_EN, &tmp, 1);
ON_ERROR_GOTO((0 == r), return_err);
// configure sample rate divider (TODO: is this gyro only?)
// note: SAMPLE_RATE = INTERNAL_SAMPLE_RATE / (1 + SMPLRT_DIV)
tmp = (0 != dev->sample_rate_div) ? dev->sample_rate_div - 1 : 1;
r = dev->hal_wr(dev->id, REG_SMPLRT_DIV, &tmp, 1);
ON_ERROR_GOTO((0 == r), return_err);
tmp = 0;
tmp |= (dev->use_gyro) ? 0 : 0x07; // 0 - on, 1 - disabled
tmp |= (dev->use_accel) ? 0 : 0x38; // 0 - on, 1 - disabled
r = dev->hal_wr(dev->id, REG_PWR_MGMT_2, &tmp, 1);
ON_ERROR_GOTO((0 == r), return_err);
return_err:
if (dev->mutex_unlock) {
dev->mutex_unlock(dev->id);
}
return r;
}
int8_t
icm20602_read_accel(struct icm20602_dev * dev, float * p_x, float * p_y,
float * p_z)
{
float accel_sensitivity;
int16_t x, y, z;
int8_t r = 0;
accel_sensitivity = _get_accel_sensitivity(dev->accel_g);
r = icm20602_read_accel_raw(dev, &x, &y, &z);
if (0 == r) {
*p_x = ((float) x) / accel_sensitivity;
*p_y = ((float) y) / accel_sensitivity;
*p_z = ((float) z) / accel_sensitivity;
}
return r;
}
int8_t
icm20602_read_gyro(struct icm20602_dev * dev, float * p_x, float * p_y,
float * p_z)
{
float gyro_sensitivity;
int16_t x, y, z;
int8_t r = 0;
gyro_sensitivity = _get_gyro_sensitivity(dev->gyro_dps);
r = icm20602_read_gyro_raw(dev, &x, &y, &z);
if (0 == r) {
*p_x = ((float) x) / gyro_sensitivity;
*p_y = ((float) y) / gyro_sensitivity;
*p_z = ((float) z) / gyro_sensitivity;
}
return r;
}
int8_t
icm20602_read_data(struct icm20602_dev * dev, float * p_ax, float * p_ay,
float * p_az, float * p_gx, float * p_gy, float * p_gz, float * p_t)
{
float accel_sensitivity;
float gyro_sensitivity;
int16_t ax, ay, az, gx, gy, gz, t;
int8_t r = 0;
accel_sensitivity = _get_accel_sensitivity(dev->accel_g);
gyro_sensitivity = _get_gyro_sensitivity(dev->gyro_dps);
r = icm20602_read_data_raw(dev, &ax, &ay, &az, &gx, &gy, &gz, &t);
if (0 == r) {
*p_ax = ((float) ax) / accel_sensitivity;
*p_ay = ((float) ay) / accel_sensitivity;
*p_az = ((float) az) / accel_sensitivity;
*p_gx = ((float) gx) / gyro_sensitivity;
*p_gy = ((float) gy) / gyro_sensitivity;
*p_gz = ((float) gz) / gyro_sensitivity;
*p_t = ((float) t) / _temp_sensitivity;
}
return r;
}
int8_t
icm20602_read_accel_raw(struct icm20602_dev * dev, int16_t * p_x, int16_t * p_y,
int16_t * p_z)
{
uint8_t buf[8] = {0};
int8_t r = 0;
r = _read_data(dev, REG_ACCEL_XOUT_H, buf, 8);
if (0 == r) {
UINT8_TO_INT16(*p_x, buf[0], buf[1]);
UINT8_TO_INT16(*p_y, buf[2], buf[3]);
UINT8_TO_INT16(*p_z, buf[4], buf[5]);
// buf[6] and buf[7] hold temperature
}
return r;
}
int8_t
icm20602_read_gyro_raw(struct icm20602_dev * dev, int16_t * p_x, int16_t * p_y,
int16_t * p_z)
{
uint8_t buf[6] = {0};
int8_t r = 0;
r = _read_data(dev, REG_GYRO_XOUT_H, buf, 6);
if (0 == r) {
UINT8_TO_INT16(*p_x, buf[0], buf[1]);
UINT8_TO_INT16(*p_y, buf[2], buf[3]);
UINT8_TO_INT16(*p_z, buf[4], buf[5]);
}
return r;
}
int8_t
icm20602_read_data_raw(struct icm20602_dev * dev, int16_t * p_ax,
int16_t * p_ay, int16_t * p_az, int16_t * p_gx, int16_t * p_gy,
int16_t * p_gz, int16_t * p_t)
{
uint8_t buf[14] = {0};
int8_t r = 0;
r = _read_data(dev, REG_ACCEL_XOUT_H, buf, 14);
if (0 == r) {
UINT8_TO_INT16(*p_ax, buf[0], buf[1]);
UINT8_TO_INT16(*p_ay, buf[2], buf[3]);
UINT8_TO_INT16(*p_az, buf[4], buf[5]);
UINT8_TO_INT16(*p_t, buf[6], buf[7]);
UINT8_TO_INT16(*p_gx, buf[8], buf[9]);
UINT8_TO_INT16(*p_gy, buf[10], buf[11]);
UINT8_TO_INT16(*p_gz, buf[12], buf[13]);
}
return r;
}
int8_t
icm20602_read_accel_fifo(struct icm20602_dev * dev, float * p_x, float * p_y,
float * p_z)
{
float accel_sensitivity;
int16_t x, y, z;
int8_t r = 0;
accel_sensitivity = _get_accel_sensitivity(dev->accel_g);
r = icm20602_read_fifo_accel_raw(dev, &x, &y, &z);
if (0 == r) {
*p_x = ((float) x) / accel_sensitivity;
*p_y = ((float) y) / accel_sensitivity;
*p_z = ((float) z) / accel_sensitivity;
}
return r;
}
int8_t
icm20602_read_gyro_fifo(struct icm20602_dev * dev, float * p_x, float * p_y,
float * p_z)
{
float gyro_sensitivity;
int16_t x, y, z;
int8_t r = 0;
gyro_sensitivity = _get_gyro_sensitivity(dev->gyro_dps);
r = icm20602_read_fifo_gyro_raw(dev, &x, &y, &z);
if (0 == r) {
*p_x = ((float) x) / gyro_sensitivity;
*p_y = ((float) y) / gyro_sensitivity;
*p_z = ((float) z) / gyro_sensitivity;
}
return r;
}
int8_t
icm20602_read_fifo_data(struct icm20602_dev * dev, float * p_ax, float * p_ay,
float * p_az, float * p_gx, float * p_gy, float * p_gz, float * p_t)
{
float accel_sensitivity;
float gyro_sensitivity;
int16_t ax, ay, az, gx, gy, gz, t;
int8_t r = 0;
accel_sensitivity = _get_accel_sensitivity(dev->accel_g);
gyro_sensitivity = _get_gyro_sensitivity(dev->gyro_dps);
r = icm20602_read_fifo_data_raw(dev, &ax, &ay, &az, &gx, &gy, &gz, &t);
if (0 == r) {
*p_ax = ((float) ax) / accel_sensitivity;
*p_ay = ((float) ay) / accel_sensitivity;
*p_az = ((float) az) / accel_sensitivity;
*p_gx = ((float) gx) / gyro_sensitivity;
*p_gy = ((float) gy) / gyro_sensitivity;
*p_gz = ((float) gz) / gyro_sensitivity;
*p_t = ((float) t) / _temp_sensitivity;
}
return r;
}
int8_t
icm20602_read_fifo_accel_raw(struct icm20602_dev * dev, int16_t * p_x,
int16_t * p_y, int16_t * p_z)
{
uint8_t buf[6] = {0};
int8_t r = 0;
r = _read_data(dev, REG_FIFO_R_W, buf, 6);
if (0 == r) {
UINT8_TO_INT16(*p_x, buf[0], buf[1]);
UINT8_TO_INT16(*p_y, buf[2], buf[3]);
UINT8_TO_INT16(*p_z, buf[4], buf[5]);
}
return r;
}
int8_t
icm20602_read_fifo_gyro_raw(struct icm20602_dev * dev, int16_t * p_x,
int16_t * p_y, int16_t * p_z)
{
uint8_t buf[6] = {0};
int8_t r = 0;
r = _read_data(dev, REG_FIFO_R_W, buf, 6);
if (0 == r) {
UINT8_TO_INT16(*p_x, buf[0], buf[1]);
UINT8_TO_INT16(*p_y, buf[2], buf[3]);
UINT8_TO_INT16(*p_z, buf[4], buf[5]);
}
return r;
}
int8_t
icm20602_read_fifo_data_raw(struct icm20602_dev * dev, int16_t * p_ax,
int16_t * p_ay, int16_t * p_az, int16_t * p_gx, int16_t * p_gy,
int16_t * p_gz, int16_t * p_t)
{
uint8_t buf[14] = {0};
int8_t r = 0;
r = _read_data(dev, REG_FIFO_R_W, buf, 14);
if (0 == r) {
UINT8_TO_INT16(*p_ax, buf[0], buf[1]);
UINT8_TO_INT16(*p_ay, buf[2], buf[3]);
UINT8_TO_INT16(*p_az, buf[4], buf[5]);
UINT8_TO_INT16(*p_t, buf[6], buf[7]);
UINT8_TO_INT16(*p_gx, buf[8], buf[9]);
UINT8_TO_INT16(*p_gy, buf[10], buf[11]);
UINT8_TO_INT16(*p_gz, buf[12], buf[13]);
}
return r;
}

39
samd/include/README Normal file
View file

@ -0,0 +1,39 @@
This directory is intended for project header files.
A header file is a file containing C declarations and macro definitions
to be shared between several project source files. You request the use of a
header file in your project source file (C, C++, etc) located in `src` folder
by including it, with the C preprocessing directive `#include'.
```src/main.c
#include "header.h"
int main (void)
{
...
}
```
Including a header file produces the same results as copying the header file
into each source file that needs it. Such copying would be time-consuming
and error-prone. With a header file, the related declarations appear
in only one place. If they need to be changed, they can be changed in one
place, and programs that include the header file will automatically use the
new version when next recompiled. The header file eliminates the labor of
finding and changing all the copies as well as the risk that a failure to
find one copy will result in inconsistencies within a program.
In C, the usual convention is to give header files names that end with `.h'.
It is most portable to use only letters, digits, dashes, and underscores in
header file names, and at most one dot.
Read more about using header files in official GCC documentation:
* Include Syntax
* Include Operation
* Once-Only Headers
* Computed Includes
https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html

64
samd/include/RgbDriver.h Normal file
View file

@ -0,0 +1,64 @@
/*
* rgbDriver.h - Library for controlling the TI LT5024 RGB LED driver on the BSides Badge 2020
* Reference: https://www.ti.com/lit/ds/symlink/lp5024.pdf
* Created by Jordan Johnson, 23 February 2020
*/
#ifndef RGB_DRIVER_H_
#define RGB_DRIVER_H_
#include "Arduino.h"
class RgbDriver
{
public:
// Set the slave address of the LP5024 driver
RgbDriver(char address);
// Set Chip_EN=0 to enter STANDBY mode
int selectStandbyMode();
// Set Chip_EN=1 to enter normal mode
int selectNormalMode();
// Reset the LP5024
int reset();
// Set the LP5024 configuration
int setDeviceConfig(bool Log_Scale_EN,
bool Power_Save_EN,
bool Auto_Incr_EN,
bool PWM_Dithering_EN,
bool Max_Current_Option,
bool LED_Global_Off);
// Enable or disable individual LEDs
int setLedConfig(bool LED7_Bank_EN,
bool LED6_Bank_EN,
bool LED5_Bank_EN,
bool LED4_Bank_EN,
bool LED3_Bank_EN,
bool LED2_Bank_EN,
bool LED1_Bank_EN,
bool LED0_Bank_EN);
// Set the brightness for all LEDs
int setBankBrightness(char bank_brightness);
// Set the colour for all LEDs
int setBankColour(char red, char green, char blue);
// Set the brightness for an individual LED
int setLedBrightness(char led, char brightness);
// Set the colour for an individual LED
int setLedColour(char led, char red, char green, char blue);
private:
// Transmit data to the LP5025 via I2C
int transmit(char addr, char data);
// Address registers
const char DEVICE_CONFIG0 = 0x00;
const char DEVICE_CONFIG1 = 0x01;
const char LED_CONFIG0 = 0x02;
const char BANK_BRIGHTNESS = 0x03;
const char BANK_A_COLOR = 0x04;
const char BANK_B_COLOR = 0x05;
const char BANK_C_COLOR = 0x06;
const char RESET = 0x027;
// The address of the LP5024
char slaveAddress;
};
#endif // RGB_DRIVER_H_

46
samd/lib/README Normal file
View file

@ -0,0 +1,46 @@
This directory is intended for project specific (private) libraries.
PlatformIO will compile them to static libraries and link into executable file.
The source code of each library should be placed in a an own separate directory
("lib/your_library_name/[here are source files]").
For example, see a structure of the following two libraries `Foo` and `Bar`:
|--lib
| |
| |--Bar
| | |--docs
| | |--examples
| | |--src
| | |- Bar.c
| | |- Bar.h
| | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html
| |
| |--Foo
| | |- Foo.c
| | |- Foo.h
| |
| |- README --> THIS FILE
|
|- platformio.ini
|--src
|- main.c
and a contents of `src/main.c`:
```
#include <Foo.h>
#include <Bar.h>
int main (void)
{
...
}
```
PlatformIO Library Dependency Finder will find automatically dependent
libraries scanning project source files.
More information about PlatformIO Library Dependency Finder
- https://docs.platformio.org/page/librarymanager/ldf.html

19
samd/platformio.ini Normal file
View file

@ -0,0 +1,19 @@
; PlatformIO Project Configuration File
;
; Build options: build flags, source filter
; Upload options: custom upload port, speed and extra flags
; Library options: dependencies, extra library storages
; Advanced options: extra scripting
;
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html
[env:samd21g18a]
platform = atmelsam
board = samd21g18a
framework = arduino
[env:mkrzero]
platform = atmelsam
board = mkrzero
framework = arduino

127
samd/src/RgbDriver.cpp Normal file
View file

@ -0,0 +1,127 @@
/*
* rgbDriver.h - Library for controlling the TI LT5024 RGB LED driver on the BSides Badge 2020
* Reference: https://www.ti.com/lit/ds/symlink/lp5024.pdf
* Created by Jordan Johnson, 23 February 2020
*/
#include "Arduino.h"
#include "Wire.h"
#include "RgbDriver.h"
// Set the slave address of the LP5024 driver
RgbDriver::RgbDriver(char address)
{
slaveAddress = address;
}
// Public Methods
// Set Chip_EN=0 to enter STANDBY mode
int RgbDriver::selectStandbyMode() {
return transmit(DEVICE_CONFIG0, 0x00);
}
// Set Chip_EN=1 to enter normal mode
int RgbDriver::selectNormalMode() {
Wire.begin();
return transmit(DEVICE_CONFIG0, 0x40);
}
// Reset the LP5024
int RgbDriver::reset() {
int status = transmit(RESET, 0xFF);
if (status == 0) {
status = transmit(RESET, 0x00);
}
return status;
}
// Set the LP5024 configuration
int RgbDriver::setDeviceConfig(bool Log_Scale_EN,
bool Power_Save_EN,
bool Auto_Incr_EN,
bool PWM_Dithering_EN,
bool Max_Current_Option,
bool LED_Global_Off) {
char config = Log_Scale_EN * 32 +
Power_Save_EN * 16 +
Auto_Incr_EN * 8 +
PWM_Dithering_EN * 4 +
Max_Current_Option * 2 +
LED_Global_Off;
return transmit(DEVICE_CONFIG1, config);
}
// Enable or disable individual LEDs
int RgbDriver::setLedConfig(bool LED7_Bank_EN,
bool LED6_Bank_EN,
bool LED5_Bank_EN,
bool LED4_Bank_EN,
bool LED3_Bank_EN,
bool LED2_Bank_EN,
bool LED1_Bank_EN,
bool LED0_Bank_EN) {
char config = LED7_Bank_EN * 128 +
LED6_Bank_EN * 64 +
LED5_Bank_EN * 32 +
LED4_Bank_EN * 16 +
LED3_Bank_EN * 8 +
LED2_Bank_EN * 4 +
LED1_Bank_EN * 2 +
LED0_Bank_EN;
return transmit(LED_CONFIG0, config);
}
// Set the brightness for all LEDs
int RgbDriver::setBankBrightness(char bank_brightness) {
return transmit(BANK_BRIGHTNESS, bank_brightness);
}
// Set the colour for all LEDs
int RgbDriver::setBankColour(char red, char green, char blue) {
// RED
int status = transmit(BANK_A_COLOR, red);
// GREEN
if (status == 0) {
transmit(BANK_B_COLOR, green);
}
// BLUE
if (status == 0) {
transmit(BANK_C_COLOR, blue);
}
return status;
}
// Set the brightness for an individual LED
int RgbDriver::setLedBrightness(char led, char brightness) {
char addr = led + 0x07;
return transmit(addr, brightness);
}
// Set the colour for an individual LED
int RgbDriver::setLedColour(char led, char red, char green, char blue) {
char redAddr = led * 3 + 0x0F;
char greenAddr = led * 3 + 0x10;
char blueAddr = led * 3 + 0x11;
// RED
int status = transmit(redAddr, red);
// GREEN
if (status == 0) {
transmit(greenAddr, green);
}
// BLUE
if (status == 0) {
transmit(blueAddr, blue);
}
return status;
}
// Private Methods
// Transmit data to the LP5025 via I2C
int RgbDriver::transmit(char addr, char data) {
Wire.beginTransmission(slaveAddress);
Wire.write(addr);
Wire.write(data);
return Wire.endTransmission();
}

95
samd/src/main.cpp Normal file
View file

@ -0,0 +1,95 @@
#include <Arduino.h>
#include "RgbDriver.h"
RgbDriver rgbDriver(0b0101000);
int mapLEDIndex(int idx) {
switch(idx) {
case 0: return 1;
case 1: return 0;
case 2: return 7;
case 3: return 6;
case 4: return 5;
case 5: return 4;
case 6: return 3;
case 7: return 2;
default: return -1;
}
}
int r = 255;
int g = 0;
int b = 200;
void setup() {
int idx;
Serial.begin(115200);
delay(5000);
Serial.println("Starting up!");
rgbDriver.selectNormalMode();
rgbDriver.setDeviceConfig(true, true, true, true, false, false);
rgbDriver.setLedConfig(false, false, false, false, false, false, false, false);
Serial.println("Lighting LEDS");
for (int i = 0; i< 8; i++) {
idx = mapLEDIndex(i);
rgbDriver.setLedBrightness(idx, 0x1f);
rgbDriver.setLedColour(idx, r, g, b);
delay(500);
}
Serial.println("LEDS Lit!");
}
void loop() {
int idx;
if (Serial.available()) {
char ch = Serial.read();
switch (ch) {
case 'r':
r += 10;
if (r > 255) {r = 255;}
case 'R':
r -= 10;
if (r < 0) {r=0;}
case 'g':
g += 10;
if (g > 255) {g = 255;}
case 'G':
g -= 10;
if (g < 0) {g=0;}
case 'b':
b += 10;
if (b > 255) {b = 255;}
case 'B':
b -= 10;
if (b < 0) {b=0;}
default:
break;
}
Serial.print("R: "); Serial.print(r);
Serial.print(" B: "); Serial.print(g);
Serial.print(" G: "); Serial.println(b);
for (int i = 0; i<8; i++) {
idx = mapLEDIndex(i);
rgbDriver.setLedBrightness(idx, 0x1f);
rgbDriver.setLedColour(idx, r, g, b);
}
}
}

11
samd/test/README Normal file
View file

@ -0,0 +1,11 @@
This directory is intended for PlatformIO Test Runner and project tests.
Unit Testing is a software testing method by which individual units of
source code, sets of one or more MCU program modules together with associated
control data, usage procedures, and operating procedures, are tested to
determine whether they are fit for use. Unit testing finds problems early
in the development cycle.
More information about PlatformIO Unit Testing:
- https://docs.platformio.org/en/latest/advanced/unit-testing/index.html