kopia lustrzana https://github.com/pimoroni/pimoroni-pico
Added MSA301 driver, library, and example
rodzic
15b85d1ee2
commit
f211fe67e6
|
@ -7,24 +7,22 @@
|
||||||
|
|
||||||
namespace pimoroni {
|
namespace pimoroni {
|
||||||
|
|
||||||
enum reg {
|
|
||||||
RESET = 0x00,
|
|
||||||
X_AXIS = 0x02,
|
|
||||||
Y_AXIS = 0x04,
|
|
||||||
Z_AXIS = 0x06
|
|
||||||
// todo: lots of other features
|
|
||||||
};
|
|
||||||
|
|
||||||
void MSA301::init() {
|
void MSA301::init() {
|
||||||
i2c_init(i2c, 400000);
|
i2c_init(i2c, 400000);
|
||||||
|
|
||||||
gpio_set_function(sda, GPIO_FUNC_I2C); gpio_pull_up(sda);
|
gpio_set_function(sda, GPIO_FUNC_I2C); gpio_pull_up(sda);
|
||||||
gpio_set_function(scl, GPIO_FUNC_I2C); gpio_pull_up(scl);
|
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();
|
reset();
|
||||||
|
|
||||||
i2c_reg_write_uint8(0x11, 0x00); // set power mode
|
set_power_mode(PowerMode::NORMAL);
|
||||||
i2c_reg_write_uint8(0x0f, 0x00); // set range & resolution
|
set_range_and_resolution(Range::G_2, Resolution::BITS_14);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MSA301::i2c_reg_write_uint8(uint8_t reg, uint8_t value) {
|
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);
|
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 MSA301::i2c_reg_read_int16(uint8_t reg) {
|
||||||
int16_t value;
|
int16_t value;
|
||||||
i2c_write_blocking(i2c, address, ®, 1, true);
|
i2c_write_blocking(i2c, address, ®, 1, true);
|
||||||
|
@ -40,15 +45,19 @@ namespace pimoroni {
|
||||||
}
|
}
|
||||||
|
|
||||||
void MSA301::reset() {
|
void MSA301::reset() {
|
||||||
i2c_reg_write_uint8(reg::RESET, 0b00100100);
|
i2c_reg_write_uint8(SOFT_RESET, 0b00100100);
|
||||||
sleep_ms(1);
|
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) {
|
if(sample_count > 1) {
|
||||||
int32_t total = 0;
|
int32_t total = 0;
|
||||||
for(uint8_t i = 0; i < sample_count; i++) {
|
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;
|
total /= sample_count;
|
||||||
return total / 16384.0f;
|
return total / 16384.0f;
|
||||||
|
@ -57,4 +66,49 @@ namespace pimoroni {
|
||||||
return i2c_reg_read_int16(axis) / 16384.0f;
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -6,13 +6,37 @@
|
||||||
namespace pimoroni {
|
namespace pimoroni {
|
||||||
|
|
||||||
class MSA301 {
|
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;
|
i2c_inst_t *i2c = i2c0;
|
||||||
|
|
||||||
// interface pins with our standard defaults where appropriate
|
// interface pins with our standard defaults where appropriate
|
||||||
int8_t address = 0x26;
|
int8_t address = DEFAULT_I2C_ADDRESS;
|
||||||
int8_t sda = 20;
|
int8_t sda = DEFAULT_SDA_PIN;
|
||||||
int8_t scl = 21;
|
int8_t scl = DEFAULT_SCL_PIN;
|
||||||
int8_t interrupt = 22;
|
int8_t interrupt = DEFAULT_INT_PIN;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
MSA301() {}
|
MSA301() {}
|
||||||
|
@ -24,13 +48,90 @@ namespace pimoroni {
|
||||||
void reset();
|
void reset();
|
||||||
|
|
||||||
void i2c_reg_write_uint8(uint8_t reg, uint8_t value);
|
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);
|
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;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ add_subdirectory(breakout_matrix11x7)
|
||||||
add_subdirectory(breakout_trackball)
|
add_subdirectory(breakout_trackball)
|
||||||
add_subdirectory(breakout_sgp30)
|
add_subdirectory(breakout_sgp30)
|
||||||
add_subdirectory(breakout_colourlcd240x240)
|
add_subdirectory(breakout_colourlcd240x240)
|
||||||
|
add_subdirectory(breakout_msa301)
|
||||||
|
|
||||||
add_subdirectory(pico_display)
|
add_subdirectory(pico_display)
|
||||||
add_subdirectory(pico_unicorn)
|
add_subdirectory(pico_unicorn)
|
||||||
|
|
|
@ -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})
|
|
@ -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;
|
||||||
|
}
|
|
@ -8,6 +8,7 @@ add_subdirectory(breakout_matrix11x7)
|
||||||
add_subdirectory(breakout_trackball)
|
add_subdirectory(breakout_trackball)
|
||||||
add_subdirectory(breakout_sgp30)
|
add_subdirectory(breakout_sgp30)
|
||||||
add_subdirectory(breakout_as7262)
|
add_subdirectory(breakout_as7262)
|
||||||
|
add_subdirectory(breakout_msa301)
|
||||||
add_subdirectory(pico_graphics)
|
add_subdirectory(pico_graphics)
|
||||||
add_subdirectory(pico_display)
|
add_subdirectory(pico_display)
|
||||||
add_subdirectory(pico_unicorn)
|
add_subdirectory(pico_unicorn)
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
include(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)
|
|
@ -0,0 +1,5 @@
|
||||||
|
#include "breakout_msa301.hpp"
|
||||||
|
|
||||||
|
namespace pimoroni {
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "../../drivers/msa301/msa301.hpp"
|
||||||
|
|
||||||
|
namespace pimoroni {
|
||||||
|
|
||||||
|
typedef MSA301 BreakoutMSA301;
|
||||||
|
}
|
Ładowanie…
Reference in New Issue