From f211fe67e67382f26c2594fc5605b559f89a8f29 Mon Sep 17 00:00:00 2001 From: Jonathan Williamson Date: Thu, 13 May 2021 13:12:30 +0100 Subject: [PATCH] Added MSA301 driver, library, and example --- drivers/msa301/msa301.cpp | 80 ++++++++++-- drivers/msa301/msa301.hpp | 119 ++++++++++++++++-- examples/CMakeLists.txt | 1 + examples/breakout_msa301/CMakeLists.txt | 16 +++ examples/breakout_msa301/demo.cpp | 27 ++++ libraries/CMakeLists.txt | 1 + libraries/breakout_msa301/CMakeLists.txt | 1 + .../breakout_msa301/breakout_msa301.cmake | 11 ++ libraries/breakout_msa301/breakout_msa301.cpp | 5 + libraries/breakout_msa301/breakout_msa301.hpp | 8 ++ 10 files changed, 247 insertions(+), 22 deletions(-) create mode 100644 examples/breakout_msa301/CMakeLists.txt create mode 100644 examples/breakout_msa301/demo.cpp create mode 100644 libraries/breakout_msa301/CMakeLists.txt create mode 100644 libraries/breakout_msa301/breakout_msa301.cmake create mode 100644 libraries/breakout_msa301/breakout_msa301.cpp create mode 100644 libraries/breakout_msa301/breakout_msa301.hpp diff --git a/drivers/msa301/msa301.cpp b/drivers/msa301/msa301.cpp index dedc17d7..f7f3d756 100644 --- a/drivers/msa301/msa301.cpp +++ b/drivers/msa301/msa301.cpp @@ -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, ®, 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, ®, 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)); + } + } \ No newline at end of file diff --git a/drivers/msa301/msa301.hpp b/drivers/msa301/msa301.hpp index 95befd3d..b9df4975 100644 --- a/drivers/msa301/msa301.hpp +++ b/drivers/msa301/msa301.hpp @@ -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; }; - } + diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 61a41027..3c6a2ea8 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -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) diff --git a/examples/breakout_msa301/CMakeLists.txt b/examples/breakout_msa301/CMakeLists.txt new file mode 100644 index 00000000..b4b02fa0 --- /dev/null +++ b/examples/breakout_msa301/CMakeLists.txt @@ -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}) diff --git a/examples/breakout_msa301/demo.cpp b/examples/breakout_msa301/demo.cpp new file mode 100644 index 00000000..4939898f --- /dev/null +++ b/examples/breakout_msa301/demo.cpp @@ -0,0 +1,27 @@ +#include +#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; +} diff --git a/libraries/CMakeLists.txt b/libraries/CMakeLists.txt index 9000c9b2..62bd90c5 100644 --- a/libraries/CMakeLists.txt +++ b/libraries/CMakeLists.txt @@ -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) diff --git a/libraries/breakout_msa301/CMakeLists.txt b/libraries/breakout_msa301/CMakeLists.txt new file mode 100644 index 00000000..82ba846f --- /dev/null +++ b/libraries/breakout_msa301/CMakeLists.txt @@ -0,0 +1 @@ +include(breakout_msa301.cmake) diff --git a/libraries/breakout_msa301/breakout_msa301.cmake b/libraries/breakout_msa301/breakout_msa301.cmake new file mode 100644 index 00000000..15f3acec --- /dev/null +++ b/libraries/breakout_msa301/breakout_msa301.cmake @@ -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) diff --git a/libraries/breakout_msa301/breakout_msa301.cpp b/libraries/breakout_msa301/breakout_msa301.cpp new file mode 100644 index 00000000..438b48fd --- /dev/null +++ b/libraries/breakout_msa301/breakout_msa301.cpp @@ -0,0 +1,5 @@ +#include "breakout_msa301.hpp" + +namespace pimoroni { + +} diff --git a/libraries/breakout_msa301/breakout_msa301.hpp b/libraries/breakout_msa301/breakout_msa301.hpp new file mode 100644 index 00000000..c16c34b4 --- /dev/null +++ b/libraries/breakout_msa301/breakout_msa301.hpp @@ -0,0 +1,8 @@ +#pragma once + +#include "../../drivers/msa301/msa301.hpp" + +namespace pimoroni { + + typedef MSA301 BreakoutMSA301; +}