Merge pull request #206 from pimoroni/driver/pwm3901

C++ and MP drivers and examples for Optical Flow sensors
pull/214/head
Philip Howard 2021-10-06 11:19:29 +01:00 zatwierdzone przez GitHub
commit 1a9e2802fe
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 4AEE18F83AFDEB23
31 zmienionych plików z 1767 dodań i 0 usunięć

Wyświetl plik

@ -3,6 +3,7 @@ add_subdirectory(esp32spi)
add_subdirectory(ioexpander)
add_subdirectory(ltp305)
add_subdirectory(ltr559)
add_subdirectory(pmw3901)
add_subdirectory(sgp30)
add_subdirectory(st7735)
add_subdirectory(st7789)

Wyświetl plik

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

Wyświetl plik

@ -0,0 +1,10 @@
set(DRIVER_NAME pmw3901)
add_library(${DRIVER_NAME} INTERFACE)
target_sources(${DRIVER_NAME} INTERFACE
${CMAKE_CURRENT_LIST_DIR}/${DRIVER_NAME}.cpp)
target_include_directories(${DRIVER_NAME} INTERFACE ${CMAKE_CURRENT_LIST_DIR})
# Pull in pico libraries that we need
target_link_libraries(${DRIVER_NAME} INTERFACE pico_stdlib hardware_spi hardware_pwm hardware_dma)

Wyświetl plik

@ -0,0 +1,587 @@
#include "pmw3901.hpp"
#include <cstdlib>
#include <math.h>
#include <cstdio>
#include <algorithm>
#include <cstring>
namespace pimoroni {
enum reg : uint8_t {
ID = 0x00,
REVISION = 0x01,
DATA_READY = 0x02,
MOTION_BURST = 0x16,
POWER_UP_RESET = 0x3a,
ORIENTATION = 0x5b,
RESOLUTION = 0x4e, // PAA5100 only
RAWDATA_GRAB = 0x58,
RAWDATA_GRAB_STATUS = 0x59,
};
bool PMW3901::init() {
// configure spi interface and pins
spi_init(spi, spi_baud);
gpio_set_function(cs, GPIO_FUNC_SIO);
gpio_set_dir(cs, GPIO_OUT);
gpio_set_function(sck, GPIO_FUNC_SPI);
gpio_set_function(mosi, GPIO_FUNC_SPI);
gpio_set_function(miso, GPIO_FUNC_SPI);
if(interrupt != PIN_UNUSED) {
gpio_set_function(interrupt, GPIO_FUNC_SIO);
gpio_set_dir(interrupt, GPIO_IN);
gpio_pull_up(interrupt);
}
cs_select();
sleep_ms(50);
cs_deselect();
write_register(reg::POWER_UP_RESET, 0x5a);
sleep_ms(20);
for(uint8_t offset = 0; offset < 5; offset++) {
uint8_t data = 0;
read_registers(reg::DATA_READY + offset, &data, 1);
}
secret_sauce();
uint8_t product_id = get_id();
uint8_t revision = get_revision();
printf("Product ID and Revision for PMW3901: %d, %d\n", product_id, revision);
if(product_id != 0x49 || revision != 0x00) {
return false;
}
return true;
}
spi_inst_t* PMW3901::get_spi() const {
return spi;
}
int PMW3901::get_cs() const {
return cs;
}
int PMW3901::get_sck() const {
return sck;
}
int PMW3901::get_mosi() const {
return mosi;
}
int PMW3901::get_miso() const {
return miso;
}
int PMW3901::get_int() const {
return interrupt;
}
uint8_t PMW3901::get_id() {
uint8_t data = 0;
read_registers(reg::ID, &data, 1);
return data;
}
uint8_t PMW3901::get_revision() {
uint8_t data = 0;
read_registers(reg::REVISION, &data, 1);
return data;
}
void PMW3901::set_rotation(Degrees degrees) {
switch(degrees) {
default:
case DEGREES_0:
set_orientation(true, true, true);
break;
case DEGREES_90:
set_orientation(false, true, false);
break;
case DEGREES_180:
set_orientation(false, false, true);
break;
case DEGREES_270:
set_orientation(true, false, false);
break;
}
}
void PMW3901::set_orientation(bool invert_x, bool invert_y, bool swap_xy) {
uint8_t value = 0;
if(swap_xy)
value |= 0b10000000;
if(invert_y)
value |= 0b01000000;
if(invert_x)
value |= 0b00100000;
write_register(reg::ORIENTATION, value);
}
bool PMW3901::get_motion(int16_t& x_out, int16_t& y_out, uint16_t timeout_ms) {
uint32_t start_time = millis();
while(millis() - start_time < timeout_ms) {
uint8_t buf[12];
read_registers(reg::MOTION_BURST, buf, 12);
uint8_t dr = buf[0];
//uint8_t obs = buf[1];
x_out = (int16_t)((int32_t)buf[3] << 8 | buf[2]);
y_out = (int16_t)((int32_t)buf[5] << 8 | buf[4]);
uint8_t quality = buf[6];
//uint8_t raw_sum = buf[7];
//uint8_t raw_max = buf[8];
//uint8_t raw_min = buf[9];
uint8_t shutter_upper = buf[10];
//uint8_t shutter_lower = buf[11];
if((dr & 0b10000000) && !((quality < 0x19) && (shutter_upper == 0x1f)))
return true;
sleep_ms(1);
}
return false;
}
bool PMW3901::get_motion_slow(int16_t& x_out, int16_t& y_out, uint16_t timeout_ms) {
uint32_t start_time = millis();
while(millis() - start_time < timeout_ms) {
uint8_t buf[5];
read_registers(reg::DATA_READY, buf, 5);
uint8_t dr = buf[0];
x_out = (int16_t)((int32_t)buf[2] << 8 | buf[1]);
y_out = (int16_t)((int32_t)buf[4] << 8 | buf[3]);
if(dr & 0b10000000)
return true;
sleep_ms(1);
}
return false;
}
bool PMW3901::frame_capture(uint8_t (&data_out)[FRAME_BYTES], uint16_t& data_size_out, uint16_t timeout_ms) {
bool success = false;
data_size_out = 0;
uint8_t buf[] = {
0x7f, 0x07,
0x4c, 0x00,
0x7f, 0x08,
0x6a, 0x38,
0x7f, 0x00,
0x55, 0x04,
0x40, 0x80,
0x4d, 0x11,
WAIT, 0x0a,
0x7f, 0x00,
0x58, 0xff
};
write_buffer(buf, sizeof(buf));
uint8_t status = 0;
uint32_t start_time = millis();
while(millis() - start_time < timeout_ms) {
status = read_register(reg::RAWDATA_GRAB_STATUS);
if(status & 0b11000000) {
success = true;
break;
}
}
if(success) {
write_register(reg::RAWDATA_GRAB, 0x00);
memset(data_out, 0, FRAME_BYTES * sizeof(uint8_t));
uint8_t data = 0;
uint16_t x = 0;
success = false;
start_time = millis();
while(millis() - start_time < timeout_ms) {
data = read_register(reg::RAWDATA_GRAB);
if((data & 0b11000000) == 0b01000000) { // Upper 6-bits
data_out[x] &= ~0b11111100;
data_out[x] |= (data & 0b00111111) << 2; // Held in 5:0
}
if((data & 0b11000000) == 0b10000000) { // Lower 2-bits
data_out[x] &= ~0b00000011;
data_out[x] |= (data & 0b00001100) >> 2; // Held in 3:2
x++;
}
if(x == FRAME_BYTES) {
success = true;
break;
}
}
data_size_out = x;
}
return success;
}
void PMW3901::cs_select() {
gpio_put(cs, false); // Active low
}
void PMW3901::cs_deselect() {
sleep_us(1);
gpio_put(cs, true);
sleep_us(1);
}
void PMW3901::write_register(uint8_t reg, uint8_t data) {
uint8_t buf[2];
buf[0] = reg | 0x80;
buf[1] = data;
cs_select();
spi_write_blocking(spi, buf, 2);
cs_deselect();
}
void PMW3901::write_buffer(uint8_t *buf, uint16_t len) {
for(uint8_t i = 0; i < len; i += 2) {
if(buf[i] == WAIT)
sleep_ms(buf[i + 1]);
else
write_register(buf[i], buf[i + 1]);
}
}
void PMW3901::read_registers(uint8_t reg, uint8_t *buf, uint16_t len) {
cs_select();
spi_write_blocking(spi, &reg, 1);
spi_read_blocking(spi, 0, buf, len);
cs_deselect();
}
uint8_t PMW3901::read_register(uint8_t reg) {
uint8_t data = 0;
cs_select();
spi_write_blocking(spi, &reg, 1);
spi_read_blocking(spi, 0, &data, 1);
cs_deselect();
return data;
}
void PMW3901::secret_sauce() {
uint8_t buf[] = {
0x7f, 0x00,
0x55, 0x01,
0x50, 0x07,
0x7f, 0x0e,
0x43, 0x10
};
write_buffer(buf, sizeof(buf));
if(read_register(0x67) & 0b10000000)
write_register(0x48, 0x04);
else
write_register(0x48, 0x02);
uint8_t buf2[] = {
0x7f, 0x00,
0x51, 0x7b,
0x50, 0x00,
0x55, 0x00,
0x7f, 0x0E
};
write_buffer(buf2, sizeof(buf2));
if(read_register(0x73) == 0x00) {
uint8_t c1 = read_register(0x70);
uint8_t c2 = read_register(0x71);
if(c1 <= 28)
c1 += 14;
if(c1 > 28)
c1 += 11;
c1 = std::max((uint8_t)0, std::min((uint8_t)0x3F, c1));
c2 = (c2 * 45) / 100;
uint8_t buf3[] = {
0x7f, 0x00,
0x61, 0xad,
0x51, 0x70,
0x7f, 0x0e
};
write_buffer(buf3, sizeof(buf3));
write_register(0x70, c1);
write_register(0x71, c2);
}
uint8_t buf4[] = {
0x7f, 0x00,
0x61, 0xad,
0x7f, 0x03,
0x40, 0x00,
0x7f, 0x05,
0x41, 0xb3,
0x43, 0xf1,
0x45, 0x14,
0x5b, 0x32,
0x5f, 0x34,
0x7b, 0x08,
0x7f, 0x06,
0x44, 0x1b,
0x40, 0xbf,
0x4e, 0x3f,
0x7f, 0x08,
0x65, 0x20,
0x6a, 0x18,
0x7f, 0x09,
0x4f, 0xaf,
0x5f, 0x40,
0x48, 0x80,
0x49, 0x80,
0x57, 0x77,
0x60, 0x78,
0x61, 0x78,
0x62, 0x08,
0x63, 0x50,
0x7f, 0x0a,
0x45, 0x60,
0x7f, 0x00,
0x4d, 0x11,
0x55, 0x80,
0x74, 0x21,
0x75, 0x1f,
0x4a, 0x78,
0x4b, 0x78,
0x44, 0x08,
0x45, 0x50,
0x64, 0xff,
0x65, 0x1f,
0x7f, 0x14,
0x65, 0x67,
0x66, 0x08,
0x63, 0x70,
0x7f, 0x15,
0x48, 0x48,
0x7f, 0x07,
0x41, 0x0d,
0x43, 0x14,
0x4b, 0x0e,
0x45, 0x0f,
0x44, 0x42,
0x4c, 0x80,
0x7f, 0x10,
0x5b, 0x02,
0x7f, 0x07,
0x40, 0x41,
0x70, 0x00,
WAIT, 0x0A, // Sleep for 10ms
0x32, 0x44,
0x7f, 0x07,
0x40, 0x40,
0x7f, 0x06,
0x62, 0xf0,
0x63, 0x00,
0x7f, 0x0d,
0x48, 0xc0,
0x6f, 0xd5,
0x7f, 0x00,
0x5b, 0xa0,
0x4e, 0xa8,
0x5a, 0x50,
0x40, 0x80,
WAIT, 0xF0,
0x7f, 0x14, // Enable LED_N pulsing
0x6f, 0x1c,
0x7f, 0x00
};
write_buffer(buf4, sizeof(buf4));
}
uint32_t PMW3901::millis() {
return to_ms_since_boot(get_absolute_time());
}
void PAA5100::secret_sauce() {
uint8_t buf[] = {
0x7f, 0x00,
0x55, 0x01,
0x50, 0x07,
0x7f, 0x0e,
0x43, 0x10
};
write_buffer(buf, sizeof(buf));
if(read_register(0x67) & 0b10000000)
write_register(0x48, 0x04);
else
write_register(0x48, 0x02);
uint8_t buf2[] = {
0x7f, 0x00,
0x51, 0x7b,
0x50, 0x00,
0x55, 0x00,
0x7f, 0x0E
};
write_buffer(buf2, sizeof(buf2));
if(read_register(0x73) == 0x00) {
uint8_t c1 = read_register(0x70);
uint8_t c2 = read_register(0x71);
if(c1 <= 28)
c1 += 14;
if(c1 > 28)
c1 += 11;
c1 = std::max((uint8_t)0, std::min((uint8_t)0x3F, c1));
c2 = (c2 * 45) / 100;
uint8_t buf3[] = {
0x7f, 0x00,
0x61, 0xad,
0x51, 0x70,
0x7f, 0x0e
};
write_buffer(buf3, sizeof(buf3));
write_register(0x70, c1);
write_register(0x71, c2);
}
uint8_t buf4[] = {
0x7f, 0x00,
0x61, 0xad,
0x7f, 0x03,
0x40, 0x00,
0x7f, 0x05,
0x41, 0xb3,
0x43, 0xf1,
0x45, 0x14,
0x5f, 0x34,
0x7b, 0x08,
0x5e, 0x34,
0x5b, 0x11,
0x6d, 0x11,
0x45, 0x17,
0x70, 0xe5,
0x71, 0xe5,
0x7f, 0x06,
0x44, 0x1b,
0x40, 0xbf,
0x4e, 0x3f,
0x7f, 0x08,
0x66, 0x44,
0x65, 0x20,
0x6a, 0x3a,
0x61, 0x05,
0x62, 0x05,
0x7f, 0x09,
0x4f, 0xaf,
0x5f, 0x40,
0x48, 0x80,
0x49, 0x80,
0x57, 0x77,
0x60, 0x78,
0x61, 0x78,
0x62, 0x08,
0x63, 0x50,
0x7f, 0x0a,
0x45, 0x60,
0x7f, 0x00,
0x4d, 0x11,
0x55, 0x80,
0x74, 0x21,
0x75, 0x1f,
0x4a, 0x78,
0x4b, 0x78,
0x44, 0x08,
0x45, 0x50,
0x64, 0xff,
0x65, 0x1f,
0x7f, 0x14,
0x65, 0x67,
0x66, 0x08,
0x63, 0x70,
0x6f, 0x1c,
0x7f, 0x15,
0x48, 0x48,
0x7f, 0x07,
0x41, 0x0d,
0x43, 0x14,
0x4b, 0x0e,
0x45, 0x0f,
0x44, 0x42,
0x4c, 0x80,
0x7f, 0x10,
0x5b, 0x02,
0x7f, 0x07,
0x40, 0x41,
WAIT, 0x0a, // Sleep for 10ms
0x7f, 0x00,
0x32, 0x00,
0x7f, 0x07,
0x40, 0x40,
0x7f, 0x06,
0x68, 0xf0,
0x69, 0x00,
0x7f, 0x0d,
0x48, 0xc0,
0x6f, 0xd5,
0x7f, 0x00,
0x5b, 0xa0,
0x4e, 0xa8,
0x5a, 0x90,
0x40, 0x80,
0x73, 0x1f,
WAIT, 0x0a, // Sleep for 10ms
0x73, 0x00
};
write_buffer(buf4, sizeof(buf4));
}
}

Wyświetl plik

@ -0,0 +1,119 @@
#pragma once
#include "hardware/spi.h"
#include "hardware/gpio.h"
#include "../../common/pimoroni_common.hpp"
namespace pimoroni {
class PMW3901 {
spi_inst_t *spi = PIMORONI_SPI_DEFAULT_INSTANCE;
//--------------------------------------------------
// Constants
//--------------------------------------------------
public:
static const uint8_t FRAME_SIZE = 35;
static const uint16_t FRAME_BYTES = 1225;
static const uint16_t DEFAULT_MOTION_TIMEOUT_MS = 5000;
static const uint16_t DEFAULT_FRAME_CAPTURE_TIMEOUT_MS = 10000;
protected:
static const uint8_t WAIT = -1;
//--------------------------------------------------
// Enums
//--------------------------------------------------
public:
enum Degrees {
DEGREES_0 = 0,
DEGREES_90,
DEGREES_180,
DEGREES_270,
};
//--------------------------------------------------
// Variables
//--------------------------------------------------
private:
// interface pins with our standard defaults where appropriate
uint cs = SPI_BG_FRONT_CS;
uint sck = SPI_DEFAULT_SCK;
uint mosi = SPI_DEFAULT_MOSI;
uint miso = SPI_DEFAULT_MISO;
uint interrupt = SPI_BG_FRONT_PWM;
uint32_t spi_baud = 400000;
//--------------------------------------------------
// Constructors/Destructor
//--------------------------------------------------
public:
PMW3901() {}
PMW3901(BG_SPI_SLOT slot) {
switch(slot) {
case PICO_EXPLORER_ONBOARD: // Don't read too much into this, the case is just here to avoid a compile error
cs = SPI_BG_FRONT_CS;
interrupt = PIN_UNUSED;
break;
case BG_SPI_FRONT:
cs = SPI_BG_FRONT_CS;
interrupt = SPI_BG_FRONT_PWM;
break;
case BG_SPI_BACK:
cs = SPI_BG_BACK_CS;
interrupt = SPI_BG_BACK_PWM;
break;
}
}
PMW3901(spi_inst_t *spi,
uint cs, uint sck, uint mosi, uint miso, uint interrupt) :
spi(spi),
cs(cs), sck(sck), mosi(mosi), miso(miso), interrupt(interrupt) {}
virtual ~PMW3901() {}
//--------------------------------------------------
// Methods
//--------------------------------------------------
public:
bool init();
spi_inst_t* get_spi() const;
int get_cs() const;
int get_sck() const;
int get_mosi() const;
int get_miso() const;
int get_int() const;
uint8_t get_id();
uint8_t get_revision();
void set_rotation(Degrees degrees = DEGREES_0);
void set_orientation(bool invert_x = true, bool invert_y = true, bool swap_xy = true);
bool get_motion(int16_t& x_out, int16_t& y_out, uint16_t timeout_ms = DEFAULT_MOTION_TIMEOUT_MS);
bool get_motion_slow(int16_t& x_out, int16_t& y_out, uint16_t timeout_ms = DEFAULT_MOTION_TIMEOUT_MS);
bool frame_capture(uint8_t (&data_out)[FRAME_BYTES], uint16_t& data_size_out, uint16_t timeout_ms = DEFAULT_FRAME_CAPTURE_TIMEOUT_MS);
protected:
virtual void secret_sauce();
void cs_select();
void cs_deselect();
void write_register(uint8_t reg, uint8_t data);
void write_buffer(uint8_t *buf, uint16_t len);
void read_registers(uint8_t reg, uint8_t *buf, uint16_t len);
uint8_t read_register(uint8_t reg);
uint32_t millis();
};
class PAA5100 : public PMW3901 {
public:
virtual ~PAA5100() {}
protected:
virtual void secret_sauce();
};
}

Wyświetl plik

@ -7,6 +7,7 @@ add_subdirectory(breakout_roundlcd)
add_subdirectory(breakout_rgbmatrix5x5)
add_subdirectory(breakout_matrix11x7)
add_subdirectory(breakout_mics6814)
add_subdirectory(breakout_pmw3901)
add_subdirectory(breakout_potentiometer)
add_subdirectory(breakout_rtc)
add_subdirectory(breakout_trackball)

Wyświetl plik

@ -0,0 +1,2 @@
add_subdirectory(motion)
add_subdirectory(frame_capture)

Wyświetl plik

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

Wyświetl plik

@ -0,0 +1,84 @@
#include <stdio.h>
#include "pico/stdlib.h"
#include <string>
// Uncomment the below line to switch from the PMW3901 to the PAA5100
//#define USE_PAA5100
#ifndef USE_PAA5100
#include "breakout_pmw3901.hpp"
#else
#include "breakout_paa5100.hpp"
#endif
using namespace pimoroni;
#ifndef USE_PAA5100
typedef BreakoutPMW3901 FlowSensor;
#else
typedef BreakoutPAA5100 FlowSensor;
#endif
FlowSensor flo(BG_SPI_FRONT);
FlowSensor::Degrees rotation = FlowSensor::DEGREES_0;
const uint8_t SIZE = FlowSensor::FRAME_SIZE;
uint8_t data[FlowSensor::FRAME_BYTES];
std::string value_to_char(uint8_t value) {
const std::string charmap = " .:-=+*#%@";
float val = (float)value / 255.0f;
val *= charmap.length() - 1;
std::string chosen_char = charmap.substr((uint8_t)val, 1);
return chosen_char.append(chosen_char); // Double chars to - sort of - correct aspect ratio
}
int main() {
stdio_init_all();
flo.init();
flo.set_rotation(rotation);
uint8_t offset = 0;
uint8_t value = 0;
while(true) {
printf("Capturing...\n");
uint16_t data_size = 0;
if(flo.frame_capture(data, data_size)) {
for(uint8_t y = 0; y < SIZE; y++) {
if(rotation == FlowSensor::DEGREES_180 || rotation == FlowSensor::DEGREES_270)
y = SIZE - y - 1;
for(uint8_t x = 0; x < SIZE; x++) {
if(rotation == FlowSensor::DEGREES_180 || rotation == FlowSensor::DEGREES_90)
x = SIZE - x - 1;
if(rotation == FlowSensor::DEGREES_90 || rotation == FlowSensor::DEGREES_270)
offset = (x * 35) + y;
else
offset = (y * 35) + x;
value = data[offset];
printf("%s", value_to_char(value).c_str());
}
printf("\n");
}
}
else {
printf("Capture failed. %d bytes received, of %d. Recapturing in ", data_size, FlowSensor::FRAME_BYTES);
}
printf("5...\n");
sleep_ms(1000);
printf("4...\n");
sleep_ms(1000);
printf("3...\n");
sleep_ms(1000);
printf("2...\n");
sleep_ms(1000);
printf("Get Ready!\n");
sleep_ms(1000);
};
return 0;
}

Wyświetl plik

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

Wyświetl plik

@ -0,0 +1,39 @@
#include <stdio.h>
#include "pico/stdlib.h"
// Uncomment the below line to switch from the PMW3901 to the PAA5100
//#define USE_PAA5100
#ifndef USE_PAA5100
#include "breakout_pmw3901.hpp"
#else
#include "breakout_paa5100.hpp"
#endif
using namespace pimoroni;
#ifndef USE_PAA5100
typedef BreakoutPMW3901 FlowSensor;
#else
typedef BreakoutPAA5100 FlowSensor;
#endif
FlowSensor flo(BG_SPI_FRONT);
int main() {
stdio_init_all();
flo.init();
flo.set_rotation(FlowSensor::DEGREES_0);
int16_t tx = 0, ty = 0;
int16_t x = 0, y = 0;
while(true) {
if(flo.get_motion(x, y)) {
tx += x;
ty += y;
printf("Relative: x %6d, y %6d | Absolute: tx %6d, ty %6d\n", x, y, tx, ty);
}
sleep_ms(10);
};
return 0;
}

Wyświetl plik

@ -8,6 +8,8 @@ add_subdirectory(breakout_roundlcd)
add_subdirectory(breakout_rgbmatrix5x5)
add_subdirectory(breakout_matrix11x7)
add_subdirectory(breakout_mics6814)
add_subdirectory(breakout_paa5100)
add_subdirectory(breakout_pmw3901)
add_subdirectory(breakout_potentiometer)
add_subdirectory(breakout_rtc)
add_subdirectory(breakout_trackball)

Wyświetl plik

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

Wyświetl plik

@ -0,0 +1,11 @@
set(LIB_NAME breakout_paa5100)
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 pmw3901)

Wyświetl plik

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

Wyświetl plik

@ -0,0 +1,8 @@
#pragma once
#include "drivers/pmw3901/pmw3901.hpp"
namespace pimoroni {
typedef PMW3901 BreakoutPAA5100;
}

Wyświetl plik

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

Wyświetl plik

@ -0,0 +1,11 @@
set(LIB_NAME breakout_pmw3901)
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 pmw3901)

Wyświetl plik

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

Wyświetl plik

@ -0,0 +1,8 @@
#pragma once
#include "drivers/pmw3901/pmw3901.hpp"
namespace pimoroni {
typedef PMW3901 BreakoutPMW3901;
}

Wyświetl plik

@ -0,0 +1,68 @@
import time
# Pick *one* sensor type by uncommenting the relevant line below:
# PMW3901
from breakout_pmw3901 import BreakoutPMW3901 as FlowSensor
# PAA5100
# from breakout_paa5100 import BreakoutPAA5100 as FlowSensor
flo = FlowSensor()
rotation = FlowSensor.DEGREES_0
SIZE = FlowSensor.FRAME_SIZE
BYTES = FlowSensor.FRAME_BYTES
data = bytearray(BYTES)
flo.set_rotation(rotation)
offset = 0
value = 0
def value_to_char(value):
charmap = " .:-=+*#%@"
val = float(value) / 255.0
val *= len(charmap) - 1
chosen_char = charmap[int(val)]
return chosen_char * 2 # Double chars to - sort of - correct aspect ratio
while(True):
print("Capturing...")
time.sleep(0.1)
# Warning! The frame capture function below can take up to 10 seconds to run! Also, it often fails to capture all bytes.
# A shorter timeout (in seconds) can be set with the 'timeout' keyword e.g. frame_capture(data, timeout=6.0)
data_size = flo.frame_capture(data)
if data_size == BYTES:
for y in range(0, SIZE):
if rotation == FlowSensor.DEGREES_180 or rotation == FlowSensor.DEGREES_270:
y = SIZE - y - 1
for x in range(0, SIZE):
if rotation == FlowSensor.DEGREES_180 or rotation == FlowSensor.DEGREES_90:
x = SIZE - x - 1
if rotation == FlowSensor.DEGREES_90 or rotation == FlowSensor.DEGREES_270:
offset = (x * 35) + y
else:
offset = (y * 35) + x
value = data[offset]
print(value_to_char(value), end="")
print()
else:
print("Capture failed. {} bytes received, of {}. Recapturing in ".format(data_size, BYTES))
print("5...")
time.sleep(1.0)
print("4...")
time.sleep(1.0)
print("3...")
time.sleep(1.0)
print("2...")
time.sleep(1.0)
print("Get Ready!")
time.sleep(1.0)

Wyświetl plik

@ -0,0 +1,27 @@
import time
# Pick *one* sensor type by uncommenting the relevant line below:
# PMW3901
from breakout_pmw3901 import BreakoutPMW3901 as FlowSensor
# PAA5100
# from breakout_paa5100 import BreakoutPAA5100 as FlowSensor
flo = FlowSensor()
flo.set_rotation(FlowSensor.DEGREES_0)
tx = 0
ty = 0
x = 0
y = 0
while(True):
delta = flo.get_motion()
if delta is not None:
x = delta[0]
y = delta[1]
tx += x
ty += y
print("Relative: x {}, y {} | Absolute: tx {}, ty {}".format(x, y, tx, ty))
time.sleep(0.1)

Wyświetl plik

@ -0,0 +1,64 @@
#include "breakout_paa5100.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
// BreakoutPAA5100 Class
////////////////////////////////////////////////////////////////////////////////////////////////////
/***** Methods *****/
MP_DEFINE_CONST_FUN_OBJ_1(BreakoutPAA5100_get_id_obj, BreakoutPAA5100_get_id);
MP_DEFINE_CONST_FUN_OBJ_1(BreakoutPAA5100_get_revision_obj, BreakoutPAA5100_get_revision);
MP_DEFINE_CONST_FUN_OBJ_KW(BreakoutPAA5100_set_rotation_obj, 1, BreakoutPAA5100_set_rotation);
MP_DEFINE_CONST_FUN_OBJ_KW(BreakoutPAA5100_set_orientation_obj, 1, BreakoutPAA5100_set_orientation);
MP_DEFINE_CONST_FUN_OBJ_KW(BreakoutPAA5100_get_motion_obj, 1, BreakoutPAA5100_get_motion);
MP_DEFINE_CONST_FUN_OBJ_KW(BreakoutPAA5100_get_motion_slow_obj, 1, BreakoutPAA5100_get_motion_slow);
MP_DEFINE_CONST_FUN_OBJ_KW(BreakoutPAA5100_frame_capture_obj, 2, BreakoutPAA5100_frame_capture);
/***** Binding of Methods *****/
STATIC const mp_rom_map_elem_t BreakoutPAA5100_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_get_id), MP_ROM_PTR(&BreakoutPAA5100_get_id_obj) },
{ MP_ROM_QSTR(MP_QSTR_get_revision), MP_ROM_PTR(&BreakoutPAA5100_get_revision_obj) },
{ MP_ROM_QSTR(MP_QSTR_set_rotation), MP_ROM_PTR(&BreakoutPAA5100_set_rotation_obj) },
{ MP_ROM_QSTR(MP_QSTR_set_orientation), MP_ROM_PTR(&BreakoutPAA5100_set_orientation_obj) },
{ MP_ROM_QSTR(MP_QSTR_get_motion), MP_ROM_PTR(&BreakoutPAA5100_get_motion_obj) },
{ MP_ROM_QSTR(MP_QSTR_get_motion_slow), MP_ROM_PTR(&BreakoutPAA5100_get_motion_slow_obj) },
{ MP_ROM_QSTR(MP_QSTR_frame_capture), MP_ROM_PTR(&BreakoutPAA5100_frame_capture_obj) },
{ MP_ROM_QSTR(MP_QSTR_DEGREES_0), MP_ROM_INT(0x00) },
{ MP_ROM_QSTR(MP_QSTR_DEGREES_90), MP_ROM_INT(0x01) },
{ MP_ROM_QSTR(MP_QSTR_DEGREES_180), MP_ROM_INT(0x02) },
{ MP_ROM_QSTR(MP_QSTR_DEGREES_270), MP_ROM_INT(0x03) },
{ MP_ROM_QSTR(MP_QSTR_FRAME_SIZE), MP_ROM_INT(35) },
{ MP_ROM_QSTR(MP_QSTR_FRAME_BYTES), MP_ROM_INT(1225) },
};
STATIC MP_DEFINE_CONST_DICT(BreakoutPAA5100_locals_dict, BreakoutPAA5100_locals_dict_table);
/***** Class Definition *****/
const mp_obj_type_t breakout_paa5100_BreakoutPAA5100_type = {
{ &mp_type_type },
.name = MP_QSTR_breakout_paa5100,
.print = BreakoutPAA5100_print,
.make_new = BreakoutPAA5100_make_new,
.locals_dict = (mp_obj_dict_t*)&BreakoutPAA5100_locals_dict,
};
////////////////////////////////////////////////////////////////////////////////////////////////////
// breakout_paa5100 Module
////////////////////////////////////////////////////////////////////////////////////////////////////
/***** Globals Table *****/
STATIC const mp_map_elem_t breakout_paa5100_globals_table[] = {
{ MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_breakout_paa5100) },
{ MP_OBJ_NEW_QSTR(MP_QSTR_BreakoutPAA5100), (mp_obj_t)&breakout_paa5100_BreakoutPAA5100_type },
};
STATIC MP_DEFINE_CONST_DICT(mp_module_breakout_paa5100_globals, breakout_paa5100_globals_table);
/***** Module Definition *****/
const mp_obj_module_t breakout_paa5100_user_cmodule = {
.base = { &mp_type_module },
.globals = (mp_obj_dict_t*)&mp_module_breakout_paa5100_globals,
};
////////////////////////////////////////////////////////////////////////////////////////////////////
MP_REGISTER_MODULE(MP_QSTR_breakout_paa5100, breakout_paa5100_user_cmodule, MODULE_BREAKOUT_PAA5100_ENABLED);
////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////

Wyświetl plik

@ -0,0 +1,269 @@
#include "libraries/breakout_paa5100/breakout_paa5100.hpp"
#define MP_OBJ_TO_PTR2(o, t) ((t *)(uintptr_t)(o))
#define IS_VALID_PERIPH(spi, pin) ((((pin) & 8) >> 3) == (spi))
#define IS_VALID_SCK(spi, pin) (((pin) & 3) == 2 && IS_VALID_PERIPH(spi, pin))
#define IS_VALID_MOSI(spi, pin) (((pin) & 3) == 3 && IS_VALID_PERIPH(spi, pin))
#define IS_VALID_MISO(spi, pin) (((pin) & 3) == 0 && IS_VALID_PERIPH(spi, pin))
using namespace pimoroni;
extern "C" {
#include "breakout_paa5100.h"
#include <cstring>
/***** Variables Struct *****/
typedef struct _breakout_paa5100_BreakoutPAA5100_obj_t {
mp_obj_base_t base;
BreakoutPAA5100 *breakout;
} breakout_paa5100_BreakoutPAA5100_obj_t;
/***** Print *****/
void BreakoutPAA5100_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
(void)kind; //Unused input parameter
breakout_paa5100_BreakoutPAA5100_obj_t *self = MP_OBJ_TO_PTR2(self_in, breakout_paa5100_BreakoutPAA5100_obj_t);
BreakoutPAA5100* breakout = self->breakout;
mp_print_str(print, "BreakoutPAA5100(");
mp_print_str(print, "spi = ");
mp_obj_print_helper(print, mp_obj_new_int((breakout->get_spi() == spi0) ? 0 : 1), PRINT_REPR);
mp_print_str(print, ", cs = ");
mp_obj_print_helper(print, mp_obj_new_int(breakout->get_cs()), PRINT_REPR);
mp_print_str(print, ", sck = ");
mp_obj_print_helper(print, mp_obj_new_int(breakout->get_sck()), PRINT_REPR);
mp_print_str(print, ", mosi = ");
mp_obj_print_helper(print, mp_obj_new_int(breakout->get_mosi()), PRINT_REPR);
mp_print_str(print, ", miso = ");
mp_obj_print_helper(print, mp_obj_new_int(breakout->get_miso()), PRINT_REPR);
mp_print_str(print, ", int = ");
mp_obj_print_helper(print, mp_obj_new_int(breakout->get_int()), PRINT_REPR);
mp_print_str(print, ")");
}
/***** Constructor *****/
mp_obj_t BreakoutPAA5100_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
breakout_paa5100_BreakoutPAA5100_obj_t *self = nullptr;
if(n_args + n_kw == 1) {
enum { ARG_slot };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_slot, MP_ARG_REQUIRED | MP_ARG_INT },
};
// Parse args.
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
int slot = args[ARG_slot].u_int;
if(slot == BG_SPI_FRONT || slot == BG_SPI_BACK) {
self = m_new_obj_with_finaliser(breakout_paa5100_BreakoutPAA5100_obj_t);
self->base.type = &breakout_paa5100_BreakoutPAA5100_type;
self->breakout = new BreakoutPAA5100((BG_SPI_SLOT)slot);
}
else {
mp_raise_ValueError("slot not a valid value. Expected 0 to 1");
}
}
else {
enum { ARG_spi, ARG_cs, ARG_sck, ARG_mosi, ARG_miso, ARG_interrupt };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_spi, MP_ARG_INT, {.u_int = -1} },
{ MP_QSTR_cs, MP_ARG_INT, {.u_int = pimoroni::SPI_BG_FRONT_CS} },
{ MP_QSTR_sck, MP_ARG_INT, {.u_int = pimoroni::SPI_DEFAULT_SCK} },
{ MP_QSTR_mosi, MP_ARG_INT, {.u_int = pimoroni::SPI_DEFAULT_MOSI} },
{ MP_QSTR_miso, MP_ARG_INT, {.u_int = pimoroni::SPI_DEFAULT_MISO} },
{ MP_QSTR_interrupt, MP_ARG_INT, {.u_int = pimoroni::SPI_BG_FRONT_PWM} },
};
// Parse args.
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
// Get SPI bus.
int spi_id = args[ARG_spi].u_int;
int sck = args[ARG_sck].u_int;
int mosi = args[ARG_mosi].u_int;
int miso = args[ARG_miso].u_int;
if(spi_id == -1) {
spi_id = (sck >> 3) & 0b1; // If no spi specified, choose the one for the given SCK pin
}
if(spi_id < 0 || spi_id > 1) {
mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("SPI(%d) doesn't exist"), spi_id);
}
if(!IS_VALID_SCK(spi_id, sck)) {
mp_raise_ValueError(MP_ERROR_TEXT("bad SCK pin"));
}
if(!IS_VALID_MOSI(spi_id, mosi)) {
mp_raise_ValueError(MP_ERROR_TEXT("bad MOSI pin"));
}
if(!IS_VALID_MISO(spi_id, miso)) {
mp_raise_ValueError(MP_ERROR_TEXT("bad MISO pin"));
}
self = m_new_obj_with_finaliser(breakout_paa5100_BreakoutPAA5100_obj_t);
self->base.type = &breakout_paa5100_BreakoutPAA5100_type;
spi_inst_t *spi = (spi_id == 0) ? spi0 : spi1;
self->breakout = new BreakoutPAA5100(spi, args[ARG_cs].u_int, sck, mosi, miso, args[ARG_interrupt].u_int);
}
self->breakout->init();
return MP_OBJ_FROM_PTR(self);
}
/***** Destructor ******/
mp_obj_t BreakoutPAA5100___del__(mp_obj_t self_in) {
breakout_paa5100_BreakoutPAA5100_obj_t *self = MP_OBJ_TO_PTR2(self_in, breakout_paa5100_BreakoutPAA5100_obj_t);
delete self->breakout;
return mp_const_none;
}
/***** Methods *****/
mp_obj_t BreakoutPAA5100_get_id(mp_obj_t self_in) {
breakout_paa5100_BreakoutPAA5100_obj_t *self = MP_OBJ_TO_PTR2(self_in, breakout_paa5100_BreakoutPAA5100_obj_t);
return mp_obj_new_int(self->breakout->get_id());
}
mp_obj_t BreakoutPAA5100_get_revision(mp_obj_t self_in) {
breakout_paa5100_BreakoutPAA5100_obj_t *self = MP_OBJ_TO_PTR2(self_in, breakout_paa5100_BreakoutPAA5100_obj_t);
return mp_obj_new_int(self->breakout->get_revision());
}
mp_obj_t BreakoutPAA5100_set_rotation(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
enum { ARG_self, ARG_degrees };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_degrees, MP_ARG_INT, {.u_int = (uint8_t)BreakoutPAA5100::DEGREES_0} },
};
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
breakout_paa5100_BreakoutPAA5100_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, breakout_paa5100_BreakoutPAA5100_obj_t);
int degrees = args[ARG_degrees].u_int;
if(degrees < 0 || degrees > 3)
mp_raise_ValueError("degrees out of range. Expected 0 (0), 1 (90), 2 (180) or 3 (270)");
else
self->breakout->set_rotation((BreakoutPAA5100::Degrees)degrees);
return mp_const_none;
}
mp_obj_t BreakoutPAA5100_set_orientation(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
enum { ARG_self, ARG_invert_x, ARG_invert_y, ARG_swap_xy };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_invert_x, MP_ARG_BOOL, {.u_bool = true} },
{ MP_QSTR_invert_y, MP_ARG_BOOL, {.u_bool = true} },
{ MP_QSTR_swap_xy, MP_ARG_BOOL, {.u_bool = true} },
};
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
breakout_paa5100_BreakoutPAA5100_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, breakout_paa5100_BreakoutPAA5100_obj_t);
bool invert_x = args[ARG_invert_x].u_int;
bool invert_y = args[ARG_invert_y].u_int;
bool swap_xy = args[ARG_swap_xy].u_int;
self->breakout->set_orientation(invert_x, invert_y, swap_xy);
return mp_const_none;
}
mp_obj_t BreakoutPAA5100_get_motion(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
enum { ARG_self, ARG_timeout };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_timeout, MP_ARG_OBJ, {.u_obj = mp_obj_new_float((float)BreakoutPAA5100::DEFAULT_MOTION_TIMEOUT_MS / 1000)} },
};
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
breakout_paa5100_BreakoutPAA5100_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, breakout_paa5100_BreakoutPAA5100_obj_t);
float timeout = mp_obj_get_float(args[ARG_timeout].u_obj);
uint16_t timeout_ms = (uint16_t)(timeout * 1000.0f);
int16_t x = 0;
int16_t y = 0;
if(self->breakout->get_motion(x, y, timeout_ms)) {
mp_obj_t tuple[2];
tuple[0] = mp_obj_new_int(x);
tuple[1] = mp_obj_new_int(y);
return mp_obj_new_tuple(2, tuple);
}
return mp_const_none;
}
mp_obj_t BreakoutPAA5100_get_motion_slow(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
enum { ARG_self, ARG_timeout };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_timeout, MP_ARG_OBJ, {.u_obj = mp_obj_new_float((float)BreakoutPAA5100::DEFAULT_MOTION_TIMEOUT_MS / 1000)} },
};
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
breakout_paa5100_BreakoutPAA5100_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, breakout_paa5100_BreakoutPAA5100_obj_t);
float timeout = mp_obj_get_float(args[ARG_timeout].u_obj);
uint16_t timeout_ms = (uint16_t)(timeout * 1000.0f);
int16_t x = 0;
int16_t y = 0;
if(self->breakout->get_motion_slow(x, y, timeout_ms)) {
mp_obj_t tuple[2];
tuple[0] = mp_obj_new_int(x);
tuple[1] = mp_obj_new_int(y);
return mp_obj_new_tuple(2, tuple);
}
return mp_const_none;
}
mp_obj_t BreakoutPAA5100_frame_capture(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
enum { ARG_self, ARG_buffer, ARG_timeout };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_buffer, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_timeout, MP_ARG_OBJ, {.u_obj = mp_obj_new_float((float)BreakoutPAA5100::DEFAULT_MOTION_TIMEOUT_MS / 1000)} },
};
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
breakout_paa5100_BreakoutPAA5100_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, breakout_paa5100_BreakoutPAA5100_obj_t);
mp_buffer_info_t bufinfo;
mp_get_buffer_raise(args[ARG_buffer].u_obj, &bufinfo, MP_BUFFER_RW);
uint8_t *buffer = (uint8_t *)bufinfo.buf;
if(bufinfo.len != (size_t)(BreakoutPAA5100::FRAME_BYTES)) {
mp_raise_ValueError("Supplied buffer is the wrong size for frame capture. Needs to be 1225.");
}
float timeout = mp_obj_get_float(args[ARG_timeout].u_obj);
uint16_t timeout_ms = (uint16_t)(timeout * 1000.0f);
uint16_t data_size = 0;
uint8_t data[BreakoutPAA5100::FRAME_BYTES];
if(self->breakout->frame_capture(data, data_size, timeout_ms)) {
memcpy(buffer, data, BreakoutPAA5100::FRAME_BYTES);
}
return mp_obj_new_int(data_size);
}
}

Wyświetl plik

@ -0,0 +1,18 @@
// Include MicroPython API.
#include "py/runtime.h"
#include "py/objstr.h"
/***** Extern of Class Definition *****/
extern const mp_obj_type_t breakout_paa5100_BreakoutPAA5100_type;
/***** Extern of Class Methods *****/
extern void BreakoutPAA5100_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind);
extern mp_obj_t BreakoutPAA5100_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args);
extern mp_obj_t BreakoutPAA5100___del__(mp_obj_t self_in);
extern mp_obj_t BreakoutPAA5100_get_id(mp_obj_t self_in);
extern mp_obj_t BreakoutPAA5100_get_revision(mp_obj_t self_in);
extern mp_obj_t BreakoutPAA5100_set_rotation(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args);
extern mp_obj_t BreakoutPAA5100_set_orientation(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args);
extern mp_obj_t BreakoutPAA5100_get_motion(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args);
extern mp_obj_t BreakoutPAA5100_get_motion_slow(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args);
extern mp_obj_t BreakoutPAA5100_frame_capture(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args);

Wyświetl plik

@ -0,0 +1,20 @@
set(MOD_NAME breakout_paa5100)
string(TOUPPER ${MOD_NAME} MOD_NAME_UPPER)
add_library(usermod_${MOD_NAME} INTERFACE)
target_sources(usermod_${MOD_NAME} INTERFACE
${CMAKE_CURRENT_LIST_DIR}/${MOD_NAME}.c
${CMAKE_CURRENT_LIST_DIR}/${MOD_NAME}.cpp
${CMAKE_CURRENT_LIST_DIR}/../../../libraries/${MOD_NAME}/${MOD_NAME}.cpp
${CMAKE_CURRENT_LIST_DIR}/../../../drivers/pmw3901/pmw3901.cpp
)
target_include_directories(usermod_${MOD_NAME} INTERFACE
${CMAKE_CURRENT_LIST_DIR}
)
target_compile_definitions(usermod_${MOD_NAME} INTERFACE
-DMODULE_${MOD_NAME_UPPER}_ENABLED=1
)
target_link_libraries(usermod INTERFACE usermod_${MOD_NAME})

Wyświetl plik

@ -0,0 +1,64 @@
#include "breakout_pmw3901.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
// BreakoutPMW3901 Class
////////////////////////////////////////////////////////////////////////////////////////////////////
/***** Methods *****/
MP_DEFINE_CONST_FUN_OBJ_1(BreakoutPMW3901_get_id_obj, BreakoutPMW3901_get_id);
MP_DEFINE_CONST_FUN_OBJ_1(BreakoutPMW3901_get_revision_obj, BreakoutPMW3901_get_revision);
MP_DEFINE_CONST_FUN_OBJ_KW(BreakoutPMW3901_set_rotation_obj, 1, BreakoutPMW3901_set_rotation);
MP_DEFINE_CONST_FUN_OBJ_KW(BreakoutPMW3901_set_orientation_obj, 1, BreakoutPMW3901_set_orientation);
MP_DEFINE_CONST_FUN_OBJ_KW(BreakoutPMW3901_get_motion_obj, 1, BreakoutPMW3901_get_motion);
MP_DEFINE_CONST_FUN_OBJ_KW(BreakoutPMW3901_get_motion_slow_obj, 1, BreakoutPMW3901_get_motion_slow);
MP_DEFINE_CONST_FUN_OBJ_KW(BreakoutPMW3901_frame_capture_obj, 2, BreakoutPMW3901_frame_capture);
/***** Binding of Methods *****/
STATIC const mp_rom_map_elem_t BreakoutPMW3901_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_get_id), MP_ROM_PTR(&BreakoutPMW3901_get_id_obj) },
{ MP_ROM_QSTR(MP_QSTR_get_revision), MP_ROM_PTR(&BreakoutPMW3901_get_revision_obj) },
{ MP_ROM_QSTR(MP_QSTR_set_rotation), MP_ROM_PTR(&BreakoutPMW3901_set_rotation_obj) },
{ MP_ROM_QSTR(MP_QSTR_set_orientation), MP_ROM_PTR(&BreakoutPMW3901_set_orientation_obj) },
{ MP_ROM_QSTR(MP_QSTR_get_motion), MP_ROM_PTR(&BreakoutPMW3901_get_motion_obj) },
{ MP_ROM_QSTR(MP_QSTR_get_motion_slow), MP_ROM_PTR(&BreakoutPMW3901_get_motion_slow_obj) },
{ MP_ROM_QSTR(MP_QSTR_frame_capture), MP_ROM_PTR(&BreakoutPMW3901_frame_capture_obj) },
{ MP_ROM_QSTR(MP_QSTR_DEGREES_0), MP_ROM_INT(0x00) },
{ MP_ROM_QSTR(MP_QSTR_DEGREES_90), MP_ROM_INT(0x01) },
{ MP_ROM_QSTR(MP_QSTR_DEGREES_180), MP_ROM_INT(0x02) },
{ MP_ROM_QSTR(MP_QSTR_DEGREES_270), MP_ROM_INT(0x03) },
{ MP_ROM_QSTR(MP_QSTR_FRAME_SIZE), MP_ROM_INT(35) },
{ MP_ROM_QSTR(MP_QSTR_FRAME_BYTES), MP_ROM_INT(1225) },
};
STATIC MP_DEFINE_CONST_DICT(BreakoutPMW3901_locals_dict, BreakoutPMW3901_locals_dict_table);
/***** Class Definition *****/
const mp_obj_type_t breakout_pmw3901_BreakoutPMW3901_type = {
{ &mp_type_type },
.name = MP_QSTR_breakout_pmw3901,
.print = BreakoutPMW3901_print,
.make_new = BreakoutPMW3901_make_new,
.locals_dict = (mp_obj_dict_t*)&BreakoutPMW3901_locals_dict,
};
////////////////////////////////////////////////////////////////////////////////////////////////////
// breakout_pmw3901 Module
////////////////////////////////////////////////////////////////////////////////////////////////////
/***** Globals Table *****/
STATIC const mp_map_elem_t breakout_pmw3901_globals_table[] = {
{ MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_breakout_pmw3901) },
{ MP_OBJ_NEW_QSTR(MP_QSTR_BreakoutPMW3901), (mp_obj_t)&breakout_pmw3901_BreakoutPMW3901_type },
};
STATIC MP_DEFINE_CONST_DICT(mp_module_breakout_pmw3901_globals, breakout_pmw3901_globals_table);
/***** Module Definition *****/
const mp_obj_module_t breakout_pmw3901_user_cmodule = {
.base = { &mp_type_module },
.globals = (mp_obj_dict_t*)&mp_module_breakout_pmw3901_globals,
};
////////////////////////////////////////////////////////////////////////////////////////////////////
MP_REGISTER_MODULE(MP_QSTR_breakout_pmw3901, breakout_pmw3901_user_cmodule, MODULE_BREAKOUT_PMW3901_ENABLED);
////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////

Wyświetl plik

@ -0,0 +1,269 @@
#include "libraries/breakout_pmw3901/breakout_pmw3901.hpp"
#define MP_OBJ_TO_PTR2(o, t) ((t *)(uintptr_t)(o))
#define IS_VALID_PERIPH(spi, pin) ((((pin) & 8) >> 3) == (spi))
#define IS_VALID_SCK(spi, pin) (((pin) & 3) == 2 && IS_VALID_PERIPH(spi, pin))
#define IS_VALID_MOSI(spi, pin) (((pin) & 3) == 3 && IS_VALID_PERIPH(spi, pin))
#define IS_VALID_MISO(spi, pin) (((pin) & 3) == 0 && IS_VALID_PERIPH(spi, pin))
using namespace pimoroni;
extern "C" {
#include "breakout_pmw3901.h"
#include <cstring>
/***** Variables Struct *****/
typedef struct _breakout_pmw3901_BreakoutPMW3901_obj_t {
mp_obj_base_t base;
BreakoutPMW3901 *breakout;
} breakout_pmw3901_BreakoutPMW3901_obj_t;
/***** Print *****/
void BreakoutPMW3901_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
(void)kind; //Unused input parameter
breakout_pmw3901_BreakoutPMW3901_obj_t *self = MP_OBJ_TO_PTR2(self_in, breakout_pmw3901_BreakoutPMW3901_obj_t);
BreakoutPMW3901* breakout = self->breakout;
mp_print_str(print, "BreakoutPMW3901(");
mp_print_str(print, "spi = ");
mp_obj_print_helper(print, mp_obj_new_int((breakout->get_spi() == spi0) ? 0 : 1), PRINT_REPR);
mp_print_str(print, ", cs = ");
mp_obj_print_helper(print, mp_obj_new_int(breakout->get_cs()), PRINT_REPR);
mp_print_str(print, ", sck = ");
mp_obj_print_helper(print, mp_obj_new_int(breakout->get_sck()), PRINT_REPR);
mp_print_str(print, ", mosi = ");
mp_obj_print_helper(print, mp_obj_new_int(breakout->get_mosi()), PRINT_REPR);
mp_print_str(print, ", miso = ");
mp_obj_print_helper(print, mp_obj_new_int(breakout->get_miso()), PRINT_REPR);
mp_print_str(print, ", int = ");
mp_obj_print_helper(print, mp_obj_new_int(breakout->get_int()), PRINT_REPR);
mp_print_str(print, ")");
}
/***** Constructor *****/
mp_obj_t BreakoutPMW3901_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
breakout_pmw3901_BreakoutPMW3901_obj_t *self = nullptr;
if(n_args + n_kw == 1) {
enum { ARG_slot };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_slot, MP_ARG_REQUIRED | MP_ARG_INT },
};
// Parse args.
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
int slot = args[ARG_slot].u_int;
if(slot == BG_SPI_FRONT || slot == BG_SPI_BACK) {
self = m_new_obj_with_finaliser(breakout_pmw3901_BreakoutPMW3901_obj_t);
self->base.type = &breakout_pmw3901_BreakoutPMW3901_type;
self->breakout = new BreakoutPMW3901((BG_SPI_SLOT)slot);
}
else {
mp_raise_ValueError("slot not a valid value. Expected 0 to 1");
}
}
else {
enum { ARG_spi, ARG_cs, ARG_sck, ARG_mosi, ARG_miso, ARG_interrupt };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_spi, MP_ARG_INT, {.u_int = -1} },
{ MP_QSTR_cs, MP_ARG_INT, {.u_int = pimoroni::SPI_BG_FRONT_CS} },
{ MP_QSTR_sck, MP_ARG_INT, {.u_int = pimoroni::SPI_DEFAULT_SCK} },
{ MP_QSTR_mosi, MP_ARG_INT, {.u_int = pimoroni::SPI_DEFAULT_MOSI} },
{ MP_QSTR_miso, MP_ARG_INT, {.u_int = pimoroni::SPI_DEFAULT_MISO} },
{ MP_QSTR_interrupt, MP_ARG_INT, {.u_int = pimoroni::SPI_BG_FRONT_PWM} },
};
// Parse args.
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
// Get SPI bus.
int spi_id = args[ARG_spi].u_int;
int sck = args[ARG_sck].u_int;
int mosi = args[ARG_mosi].u_int;
int miso = args[ARG_miso].u_int;
if(spi_id == -1) {
spi_id = (sck >> 3) & 0b1; // If no spi specified, choose the one for the given SCK pin
}
if(spi_id < 0 || spi_id > 1) {
mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("SPI(%d) doesn't exist"), spi_id);
}
if(!IS_VALID_SCK(spi_id, sck)) {
mp_raise_ValueError(MP_ERROR_TEXT("bad SCK pin"));
}
if(!IS_VALID_MOSI(spi_id, mosi)) {
mp_raise_ValueError(MP_ERROR_TEXT("bad MOSI pin"));
}
if(!IS_VALID_MISO(spi_id, miso)) {
mp_raise_ValueError(MP_ERROR_TEXT("bad MISO pin"));
}
self = m_new_obj_with_finaliser(breakout_pmw3901_BreakoutPMW3901_obj_t);
self->base.type = &breakout_pmw3901_BreakoutPMW3901_type;
spi_inst_t *spi = (spi_id == 0) ? spi0 : spi1;
self->breakout = new BreakoutPMW3901(spi, args[ARG_cs].u_int, sck, mosi, miso, args[ARG_interrupt].u_int);
}
self->breakout->init();
return MP_OBJ_FROM_PTR(self);
}
/***** Destructor ******/
mp_obj_t BreakoutPMW3901___del__(mp_obj_t self_in) {
breakout_pmw3901_BreakoutPMW3901_obj_t *self = MP_OBJ_TO_PTR2(self_in, breakout_pmw3901_BreakoutPMW3901_obj_t);
delete self->breakout;
return mp_const_none;
}
/***** Methods *****/
mp_obj_t BreakoutPMW3901_get_id(mp_obj_t self_in) {
breakout_pmw3901_BreakoutPMW3901_obj_t *self = MP_OBJ_TO_PTR2(self_in, breakout_pmw3901_BreakoutPMW3901_obj_t);
return mp_obj_new_int(self->breakout->get_id());
}
mp_obj_t BreakoutPMW3901_get_revision(mp_obj_t self_in) {
breakout_pmw3901_BreakoutPMW3901_obj_t *self = MP_OBJ_TO_PTR2(self_in, breakout_pmw3901_BreakoutPMW3901_obj_t);
return mp_obj_new_int(self->breakout->get_revision());
}
mp_obj_t BreakoutPMW3901_set_rotation(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
enum { ARG_self, ARG_degrees };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_degrees, MP_ARG_INT, {.u_int = (uint8_t)BreakoutPMW3901::DEGREES_0} },
};
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
breakout_pmw3901_BreakoutPMW3901_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, breakout_pmw3901_BreakoutPMW3901_obj_t);
int degrees = args[ARG_degrees].u_int;
if(degrees < 0 || degrees > 3)
mp_raise_ValueError("degrees out of range. Expected 0 (0), 1 (90), 2 (180) or 3 (270)");
else
self->breakout->set_rotation((BreakoutPMW3901::Degrees)degrees);
return mp_const_none;
}
mp_obj_t BreakoutPMW3901_set_orientation(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
enum { ARG_self, ARG_invert_x, ARG_invert_y, ARG_swap_xy };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_invert_x, MP_ARG_BOOL, {.u_bool = true} },
{ MP_QSTR_invert_y, MP_ARG_BOOL, {.u_bool = true} },
{ MP_QSTR_swap_xy, MP_ARG_BOOL, {.u_bool = true} },
};
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
breakout_pmw3901_BreakoutPMW3901_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, breakout_pmw3901_BreakoutPMW3901_obj_t);
bool invert_x = args[ARG_invert_x].u_int;
bool invert_y = args[ARG_invert_y].u_int;
bool swap_xy = args[ARG_swap_xy].u_int;
self->breakout->set_orientation(invert_x, invert_y, swap_xy);
return mp_const_none;
}
mp_obj_t BreakoutPMW3901_get_motion(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
enum { ARG_self, ARG_timeout };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_timeout, MP_ARG_OBJ, {.u_obj = mp_obj_new_float((float)BreakoutPMW3901::DEFAULT_MOTION_TIMEOUT_MS / 1000)} },
};
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
breakout_pmw3901_BreakoutPMW3901_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, breakout_pmw3901_BreakoutPMW3901_obj_t);
float timeout = mp_obj_get_float(args[ARG_timeout].u_obj);
uint16_t timeout_ms = (uint16_t)(timeout * 1000.0f);
int16_t x = 0;
int16_t y = 0;
if(self->breakout->get_motion(x, y, timeout_ms)) {
mp_obj_t tuple[2];
tuple[0] = mp_obj_new_int(x);
tuple[1] = mp_obj_new_int(y);
return mp_obj_new_tuple(2, tuple);
}
return mp_const_none;
}
mp_obj_t BreakoutPMW3901_get_motion_slow(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
enum { ARG_self, ARG_timeout };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_timeout, MP_ARG_OBJ, {.u_obj = mp_obj_new_float((float)BreakoutPMW3901::DEFAULT_MOTION_TIMEOUT_MS / 1000)} },
};
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
breakout_pmw3901_BreakoutPMW3901_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, breakout_pmw3901_BreakoutPMW3901_obj_t);
float timeout = mp_obj_get_float(args[ARG_timeout].u_obj);
uint16_t timeout_ms = (uint16_t)(timeout * 1000.0f);
int16_t x = 0;
int16_t y = 0;
if(self->breakout->get_motion_slow(x, y, timeout_ms)) {
mp_obj_t tuple[2];
tuple[0] = mp_obj_new_int(x);
tuple[1] = mp_obj_new_int(y);
return mp_obj_new_tuple(2, tuple);
}
return mp_const_none;
}
mp_obj_t BreakoutPMW3901_frame_capture(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
enum { ARG_self, ARG_buffer, ARG_timeout };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_buffer, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_timeout, MP_ARG_OBJ, {.u_obj = mp_obj_new_float((float)BreakoutPMW3901::DEFAULT_MOTION_TIMEOUT_MS / 1000)} },
};
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
breakout_pmw3901_BreakoutPMW3901_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, breakout_pmw3901_BreakoutPMW3901_obj_t);
mp_buffer_info_t bufinfo;
mp_get_buffer_raise(args[ARG_buffer].u_obj, &bufinfo, MP_BUFFER_RW);
uint8_t *buffer = (uint8_t *)bufinfo.buf;
if(bufinfo.len != (size_t)(BreakoutPMW3901::FRAME_BYTES)) {
mp_raise_ValueError("Supplied buffer is the wrong size for frame capture. Needs to be 1225.");
}
float timeout = mp_obj_get_float(args[ARG_timeout].u_obj);
uint16_t timeout_ms = (uint16_t)(timeout * 1000.0f);
uint16_t data_size = 0;
uint8_t data[BreakoutPMW3901::FRAME_BYTES];
if(self->breakout->frame_capture(data, data_size, timeout_ms)) {
memcpy(buffer, data, BreakoutPMW3901::FRAME_BYTES);
}
return mp_obj_new_int(data_size);
}
}

Wyświetl plik

@ -0,0 +1,18 @@
// Include MicroPython API.
#include "py/runtime.h"
#include "py/objstr.h"
/***** Extern of Class Definition *****/
extern const mp_obj_type_t breakout_pmw3901_BreakoutPMW3901_type;
/***** Extern of Class Methods *****/
extern void BreakoutPMW3901_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind);
extern mp_obj_t BreakoutPMW3901_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args);
extern mp_obj_t BreakoutPMW3901___del__(mp_obj_t self_in);
extern mp_obj_t BreakoutPMW3901_get_id(mp_obj_t self_in);
extern mp_obj_t BreakoutPMW3901_get_revision(mp_obj_t self_in);
extern mp_obj_t BreakoutPMW3901_set_rotation(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args);
extern mp_obj_t BreakoutPMW3901_set_orientation(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args);
extern mp_obj_t BreakoutPMW3901_get_motion(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args);
extern mp_obj_t BreakoutPMW3901_get_motion_slow(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args);
extern mp_obj_t BreakoutPMW3901_frame_capture(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args);

Wyświetl plik

@ -0,0 +1,20 @@
set(MOD_NAME breakout_pmw3901)
string(TOUPPER ${MOD_NAME} MOD_NAME_UPPER)
add_library(usermod_${MOD_NAME} INTERFACE)
target_sources(usermod_${MOD_NAME} INTERFACE
${CMAKE_CURRENT_LIST_DIR}/${MOD_NAME}.c
${CMAKE_CURRENT_LIST_DIR}/${MOD_NAME}.cpp
${CMAKE_CURRENT_LIST_DIR}/../../../libraries/${MOD_NAME}/${MOD_NAME}.cpp
${CMAKE_CURRENT_LIST_DIR}/../../../drivers/pmw3901/pmw3901.cpp
)
target_include_directories(usermod_${MOD_NAME} INTERFACE
${CMAKE_CURRENT_LIST_DIR}
)
target_compile_definitions(usermod_${MOD_NAME} INTERFACE
-DMODULE_${MOD_NAME_UPPER}_ENABLED=1
)
target_link_libraries(usermod INTERFACE usermod_${MOD_NAME})

Wyświetl plik

@ -15,6 +15,8 @@ include(breakout_roundlcd/micropython)
include(breakout_rgbmatrix5x5/micropython)
include(breakout_matrix11x7/micropython)
include(breakout_msa301/micropython)
include(breakout_paa5100/micropython)
include(breakout_pmw3901/micropython)
include(breakout_mics6814/micropython)
include(breakout_potentiometer/micropython)
include(breakout_rtc/micropython)