Added MSA301 driver, library, and example

This commit is contained in:
Jonathan Williamson 2021-05-13 13:12:30 +01:00 committed by Phil Howard
parent 15b85d1ee2
commit f211fe67e6
10 changed files with 247 additions and 22 deletions

View File

@ -7,24 +7,22 @@
namespace pimoroni {
enum reg {
RESET = 0x00,
X_AXIS = 0x02,
Y_AXIS = 0x04,
Z_AXIS = 0x06
// todo: lots of other features
};
void MSA301::init() {
i2c_init(i2c, 400000);
gpio_set_function(sda, GPIO_FUNC_I2C); gpio_pull_up(sda);
gpio_set_function(scl, GPIO_FUNC_I2C); gpio_pull_up(scl);
if(interrupt != PIN_UNUSED) {
gpio_set_function(interrupt, GPIO_FUNC_SIO);
gpio_set_dir(interrupt, GPIO_IN);
gpio_pull_up(interrupt);
}
reset();
i2c_reg_write_uint8(0x11, 0x00); // set power mode
i2c_reg_write_uint8(0x0f, 0x00); // set range & resolution
set_power_mode(PowerMode::NORMAL);
set_range_and_resolution(Range::G_2, Resolution::BITS_14);
}
void MSA301::i2c_reg_write_uint8(uint8_t reg, uint8_t value) {
@ -32,6 +30,13 @@ namespace pimoroni {
i2c_write_blocking(i2c, address, buffer, 2, false);
}
uint8_t MSA301::i2c_reg_read_uint8(uint8_t reg) {
uint8_t value;
i2c_write_blocking(i2c, address, &reg, 1, true);
i2c_read_blocking(i2c, address, (uint8_t *)&value, 1, false);
return value;
}
int16_t MSA301::i2c_reg_read_int16(uint8_t reg) {
int16_t value;
i2c_write_blocking(i2c, address, &reg, 1, true);
@ -40,15 +45,19 @@ namespace pimoroni {
}
void MSA301::reset() {
i2c_reg_write_uint8(reg::RESET, 0b00100100);
i2c_reg_write_uint8(SOFT_RESET, 0b00100100);
sleep_ms(1);
}
float MSA301::get_axis(uint8_t axis, uint8_t sample_count) {
uint8_t MSA301::part_id() {
return i2c_reg_read_uint8(PART_ID);
}
float MSA301::get_axis(Axis axis, uint8_t sample_count) {
if(sample_count > 1) {
int32_t total = 0;
for(uint8_t i = 0; i < sample_count; i++) {
total += i2c_reg_read_int16(axis);
total += i2c_reg_read_int16(int(axis));
}
total /= sample_count;
return total / 16384.0f;
@ -57,4 +66,49 @@ namespace pimoroni {
return i2c_reg_read_int16(axis) / 16384.0f;
}
MSA301::Orientation MSA301::get_orientation() {
return (Orientation)((i2c_reg_read_uint8(ORIENTATION_STATUS) >> 4) & 0b11);
}
void MSA301::set_power_mode(MSA301::PowerMode power_mode) {
i2c_reg_write_uint8(POWER_MODE_BANDWIDTH, power_mode);
}
void MSA301::set_range_and_resolution(Range range, MSA301::Resolution resolution) {
i2c_reg_write_uint8(RESOLUTION_RANGE, range | resolution);
}
void MSA301::set_axis_polarity(int polarity) {
i2c_reg_write_uint8(SET_AXIS_POLARITY, polarity);
}
void MSA301::disable_all_interrupts() {
enable_interrupts(MSA301::Interrupt::NONE);
}
void MSA301::enable_interrupts(int interrupts) {
i2c_reg_write_uint8(INTERRUPT_ENABLE_0, interrupts & 0xff);
i2c_reg_write_uint8(INTERRUPT_ENABLE_1, (interrupts & 0xff00) >> 8);
}
bool MSA301::read_interrupt(Interrupt interrupt) {
if(interrupt == NEW_DATA) {
return i2c_reg_read_uint8(DATA_INTERRUPT) & 0b1;
}
// determine which bit indicates the status of this interrupt
uint8_t bit = 0;
if(interrupt == FREEFALL) bit = 0;
if(interrupt == ACTIVE) bit = 2;
if(interrupt == DOUBLE_TAP) bit = 4;
if(interrupt == SINGLE_TAP) bit = 5;
if(interrupt == ORIENTATION) bit = 6;
return i2c_reg_read_uint8(MOTION_INTERRUPT) & (1U << bit);
}
void MSA301::set_interrupt_latch(MSA301::InterruptLatchPeriod latch_period, bool reset_latched = false) {
i2c_reg_write_uint8(INTERRUPT_LATCH_PERIOD, latch_period | (reset_latched ? 0b10000000: 0b0));
}
}

View File

@ -6,13 +6,37 @@
namespace pimoroni {
class MSA301 {
//--------------------------------------------------
// Constants
//--------------------------------------------------
public:
static const uint8_t DEFAULT_I2C_ADDRESS = 0x26;
static const uint8_t DEFAULT_SDA_PIN = 20;
static const uint8_t DEFAULT_SCL_PIN = 21;
static const uint8_t DEFAULT_INT_PIN = 22;
static const uint8_t PIN_UNUSED = UINT8_MAX;
static const uint8_t SOFT_RESET = 0x00;
static const uint8_t PART_ID = 0x01;
static const uint8_t MOTION_INTERRUPT = 0x09;
static const uint8_t DATA_INTERRUPT = 0x0a;
static const uint8_t ORIENTATION_STATUS = 0x0c;
static const uint8_t RESOLUTION_RANGE = 0x0f;
static const uint8_t POWER_MODE_BANDWIDTH = 0x11;
static const uint8_t SET_AXIS_POLARITY = 0x12;
static const uint8_t INTERRUPT_ENABLE_0 = 0x16;
static const uint8_t INTERRUPT_ENABLE_1 = 0x17;
static const uint8_t INTERRUPT_LATCH_PERIOD = 0x21;
static const uint8_t FREEFALL_DURATION = 0x22;
private:
i2c_inst_t *i2c = i2c0;
// interface pins with our standard defaults where appropriate
int8_t address = 0x26;
int8_t sda = 20;
int8_t scl = 21;
int8_t interrupt = 22;
int8_t address = DEFAULT_I2C_ADDRESS;
int8_t sda = DEFAULT_SDA_PIN;
int8_t scl = DEFAULT_SCL_PIN;
int8_t interrupt = DEFAULT_INT_PIN;
public:
MSA301() {}
@ -24,13 +48,90 @@ namespace pimoroni {
void reset();
void i2c_reg_write_uint8(uint8_t reg, uint8_t value);
uint8_t i2c_reg_read_uint8(uint8_t reg);
int16_t i2c_reg_read_int16(uint8_t reg);
float get_axis(uint8_t axis, uint8_t sample_count = 1);
enum Axis {
X = 0x02,
Y = 0x04,
Z = 0x06
};
enum Orientation {
PORTRAIT = 0b00,
PORTRAIT_INVERTED = 0b01,
LANDSCAPE = 0b10,
LANDSCAPE_INVERTED = 0b11
};
enum PowerMode {
NORMAL = 0b00,
LOW = 0b01,
SUSPEND = 0b10
};
enum Range {
G_2 = 0b00,
G_4 = 0b01,
G_8 = 0b10,
G_16 = 0b11
};
enum Resolution {
BITS_14 = 0b0000,
BITS_12 = 0b0100,
BITS_10 = 0b1000,
BITS_8 = 0b1100
};
enum AxisPolarity {
INVERT_X = 0b1000,
INVERT_Y = 0b0100,
INVERT_Z = 0b0010,
XY_SWAP = 0b0001
};
enum Interrupt {
NONE = 0,
ACTIVE = 0b0000111,
NEW_DATA = 0b1000000000000,
FREEFALL = 0b0100000000000,
ORIENTATION = 0b1000000,
SINGLE_TAP = 0b0100000,
DOUBLE_TAP = 0b0010000,
Z_ACTIVE = 0b0000100,
Y_ACTIVE = 0b0000010,
X_ACTIVE = 0b0000001
};
enum InterruptLatchPeriod {
LATCH_1MS = 0b1001,
LATCH_2MS = 0b1011,
LATCH_25MS = 0b1100,
LATCH_50MS = 0b1101,
LATCH_100MS = 0b1110,
LATCH_250MS = 0b0001,
LATCH_500MS = 0b0010,
LATCH_1S = 0b0011,
LATCH_2S = 0b0100,
LATCH_4S = 0b0101,
LATCH_8S = 0b0110
};
uint8_t part_id();
float get_axis(Axis axis, uint8_t sample_count = 1);
float get_x_axis(uint8_t sample_count = 1) { return this->get_axis(MSA301::X, sample_count); }
float get_y_axis(uint8_t sample_count = 1) { return this->get_axis(MSA301::Y, sample_count); }
float get_z_axis(uint8_t sample_count = 1) { return this->get_axis(MSA301::Z, sample_count); }
Orientation get_orientation();
void set_power_mode(MSA301::PowerMode power_mode);
void set_range_and_resolution(Range range, MSA301::Resolution resolution);
void set_axis_polarity(int polarity);
void disable_all_interrupts();
void enable_interrupts(int interrupts);
void set_interrupt_latch(InterruptLatchPeriod latch_period, bool reset_latched);
bool read_interrupt(Interrupt interrupt);
const uint8_t X = 0x02;
const uint8_t Y = 0x04;
const uint8_t Z = 0x06;
};
}

View File

@ -7,6 +7,7 @@ add_subdirectory(breakout_matrix11x7)
add_subdirectory(breakout_trackball)
add_subdirectory(breakout_sgp30)
add_subdirectory(breakout_colourlcd240x240)
add_subdirectory(breakout_msa301)
add_subdirectory(pico_display)
add_subdirectory(pico_unicorn)

View File

@ -0,0 +1,16 @@
set(OUTPUT_NAME msa301_demo)
add_executable(
${OUTPUT_NAME}
demo.cpp
)
# enable usb output, disable uart output
pico_enable_stdio_usb(${OUTPUT_NAME} 1)
pico_enable_stdio_uart(${OUTPUT_NAME} 0)
# Pull in pico libraries that we need
target_link_libraries(${OUTPUT_NAME} pico_stdlib breakout_msa301)
# create map/bin/hex file etc.
pico_add_extra_outputs(${OUTPUT_NAME})

View File

@ -0,0 +1,27 @@
#include <stdio.h>
#include "pico/stdlib.h"
#include "breakout_msa301.hpp"
using namespace pimoroni;
BreakoutMSA301 msa301;
int main() {
stdio_init_all();
msa301.init();
uint8_t part_id = msa301.part_id();
printf("Found MSA301. Part ID: 0x%02x\n", part_id);
msa301.enable_interrupts(MSA301::FREEFALL | MSA301::ORIENTATION);
while(true){
printf("%d\n", msa301.read_interrupt(MSA301::FREEFALL));
printf("X: %f, Y: %f, Z: %f\n", msa301.get_x_axis(), msa301.get_y_axis(), msa301.get_z_axis());
printf("%d\n", msa301.get_orientation());
sleep_ms(100);
};
return 0;
}

View File

@ -8,6 +8,7 @@ add_subdirectory(breakout_matrix11x7)
add_subdirectory(breakout_trackball)
add_subdirectory(breakout_sgp30)
add_subdirectory(breakout_as7262)
add_subdirectory(breakout_msa301)
add_subdirectory(pico_graphics)
add_subdirectory(pico_display)
add_subdirectory(pico_unicorn)

View File

@ -0,0 +1 @@
include(breakout_msa301.cmake)

View File

@ -0,0 +1,11 @@
set(LIB_NAME breakout_msa301)
add_library(${LIB_NAME} INTERFACE)
target_sources(${LIB_NAME} INTERFACE
${CMAKE_CURRENT_LIST_DIR}/${LIB_NAME}.cpp
)
target_include_directories(${LIB_NAME} INTERFACE ${CMAKE_CURRENT_LIST_DIR})
# Pull in pico libraries that we need
target_link_libraries(${LIB_NAME} INTERFACE pico_stdlib msa301)

View File

@ -0,0 +1,5 @@
#include "breakout_msa301.hpp"
namespace pimoroni {
}

View File

@ -0,0 +1,8 @@
#pragma once
#include "../../drivers/msa301/msa301.hpp"
namespace pimoroni {
typedef MSA301 BreakoutMSA301;
}