From 6afbc2e64c874b28ae82216d5aa369742d415020 Mon Sep 17 00:00:00 2001 From: Phil Howard Date: Thu, 21 Jan 2021 20:24:18 +0000 Subject: [PATCH 1/9] AS7262 driver Co-authored-by: ZodiusInfuser --- drivers/CMakeLists.txt | 1 + drivers/as7262/CMakeLists.txt | 1 + drivers/as7262/as7262.cmake | 10 + drivers/as7262/as7262.cpp | 190 ++++++++++++++++++ drivers/as7262/as7262.hpp | 133 ++++++++++++ examples/CMakeLists.txt | 1 + examples/breakout_as7262/CMakeLists.txt | 12 ++ examples/breakout_as7262/demo.cpp | 43 ++++ libraries/CMakeLists.txt | 1 + libraries/breakout_as7262/CMakeLists.txt | 1 + .../breakout_as7262/breakout_as7262.cmake | 11 + libraries/breakout_as7262/breakout_as7262.cpp | 5 + libraries/breakout_as7262/breakout_as7262.hpp | 8 + 13 files changed, 417 insertions(+) create mode 100644 drivers/as7262/CMakeLists.txt create mode 100644 drivers/as7262/as7262.cmake create mode 100644 drivers/as7262/as7262.cpp create mode 100644 drivers/as7262/as7262.hpp create mode 100644 examples/breakout_as7262/CMakeLists.txt create mode 100644 examples/breakout_as7262/demo.cpp create mode 100644 libraries/breakout_as7262/CMakeLists.txt create mode 100644 libraries/breakout_as7262/breakout_as7262.cmake create mode 100644 libraries/breakout_as7262/breakout_as7262.cpp create mode 100644 libraries/breakout_as7262/breakout_as7262.hpp diff --git a/drivers/CMakeLists.txt b/drivers/CMakeLists.txt index 3d1f0f9c..9e56c8cd 100644 --- a/drivers/CMakeLists.txt +++ b/drivers/CMakeLists.txt @@ -11,3 +11,4 @@ add_subdirectory(vl53l1x) add_subdirectory(is31fl3731) add_subdirectory(fatfs) add_subdirectory(sdcard) +add_subdirectory(as7262) diff --git a/drivers/as7262/CMakeLists.txt b/drivers/as7262/CMakeLists.txt new file mode 100644 index 00000000..da4fb7fc --- /dev/null +++ b/drivers/as7262/CMakeLists.txt @@ -0,0 +1 @@ +include(as7262.cmake) diff --git a/drivers/as7262/as7262.cmake b/drivers/as7262/as7262.cmake new file mode 100644 index 00000000..65591866 --- /dev/null +++ b/drivers/as7262/as7262.cmake @@ -0,0 +1,10 @@ +set(DRIVER_NAME as7262) +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_i2c) diff --git a/drivers/as7262/as7262.cpp b/drivers/as7262/as7262.cpp new file mode 100644 index 00000000..b2bbd218 --- /dev/null +++ b/drivers/as7262/as7262.cpp @@ -0,0 +1,190 @@ +#include +#include +#include +#include + +#include "as7262.hpp" + +namespace pimoroni { + + /***** Device registers and masks here *****/ + + enum reg { + DEVICE = 0x00, + HW_VERSION = 0x01, + FW_VERSION = 0x02, // + 0x03 + CONTROL = 0x04, + INT_T = 0x05, + TEMP = 0x06, + LED_CONTROL = 0x07, + V_HIGH = 0x08, // Violet + V_LOW = 0x09, + B_HIGH = 0x0A, // Blue + B_LOW = 0x0B, + G_HIGH = 0x0C, // Green + G_LOW = 0x0D, + Y_HIGH = 0x0E, // Yellow + Y_LOW = 0x0F, + O_HIGH = 0x10, // Orange + O_LOW = 0x11, + R_HIGH = 0x12, // Red + R_LOW = 0x13, + V_CAL_F = 0x14, // -> 0x17 Float (Violet) + B_CAL_F = 0x18, // -> 0x1B Float (Blue) + G_CAL_F = 0x1C, // -> 0x1F Float (Green) + Y_CAL_F = 0x20, // -> 0x23 Float (Yellow) + O_CAL_F = 0x24, // -> 0x27 Float (Orange) + R_CAL_F = 0x28, // -> 0x27 Float (Red) + }; + + + bool AS7262::init() { + bool succeeded = false; + + 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(); + + /***** Replace if(true) with any operations needed to initialise the device *****/ + if(true) { + succeeded = true; + } + + return succeeded; + } + + void AS7262::reset() { + i2c_reg_write_uint8(reg::CONTROL, 0b10000000); + sleep_ms(1000); + } + + void AS7262::set_gain(gain gain) { + uint8_t temp = i2c_reg_read_uint8(reg::CONTROL) & ~0b00110000; + temp |= (uint8_t)gain << 4; + i2c_reg_write_uint8(reg::CONTROL, temp); + } + + void AS7262::set_measurement_mode(measurement_mode mode) { + uint8_t temp = i2c_reg_read_uint8(reg::CONTROL) & ~0b00001100; + temp |= (uint8_t)mode << 2; + i2c_reg_write_uint8(reg::CONTROL, temp); + } + + void AS7262::set_indicator_current(indicator_current current) { + uint8_t temp = i2c_reg_read_uint8(reg::LED_CONTROL) & ~0b00000110; + temp |= (uint8_t)current << 1; + i2c_reg_write_uint8(reg::LED_CONTROL, temp); + } + + void AS7262::set_illumination_current(illumination_current current) { + uint8_t temp = i2c_reg_read_uint8(reg::LED_CONTROL) & ~0b00110000; + temp |= (uint8_t)current << 4; + i2c_reg_write_uint8(reg::LED_CONTROL, temp); + } + + void AS7262::set_leds(bool illumination, bool indicator) { + uint8_t temp = i2c_reg_read_uint8(reg::LED_CONTROL) & ~0b00001001; + temp |= indicator ? 1 : 0; + temp |= (illumination ? 1 : 0) << 3; + i2c_reg_write_uint8(reg::LED_CONTROL, temp); + } + + std::string AS7262::firmware_version() { + std::string buf; + uint16_t fw_version = i2c_reg_read_uint16(reg::FW_VERSION); + buf += std::to_string(fw_version); + return buf; + } + + bool AS7262::data_ready() { + return i2c_reg_read_uint8(reg::CONTROL) & 0b00000010; + } + + AS7262::reading AS7262::read() { + while(!data_ready()) {} + return AS7262::reading { + i2c_reg_read_float(reg::R_CAL_F), + i2c_reg_read_float(reg::O_CAL_F), + i2c_reg_read_float(reg::Y_CAL_F), + i2c_reg_read_float(reg::G_CAL_F), + i2c_reg_read_float(reg::B_CAL_F), + i2c_reg_read_float(reg::V_CAL_F) + }; + } + + uint8_t AS7262::temperature() { + return i2c_reg_read_uint8(reg::TEMP); + } + + // i2c IO wrappers around the weird virtual i2c nonsense + + void AS7262::i2c_reg_write_uint8(uint8_t reg, uint8_t value) { + _i2c_write(reg, &value, 1); + } + + float AS7262::i2c_reg_read_float(uint8_t reg) { + float value; + _i2c_read(reg, (uint8_t *)&value, 4); + return __builtin_bswap32(value); + } + + uint8_t AS7262::i2c_reg_read_uint8(uint8_t reg) { + uint8_t value; + _i2c_read(reg, &value, 1); + return value; + } + + uint16_t AS7262::i2c_reg_read_uint16(uint8_t reg) { + uint16_t value; + _i2c_read(reg, (uint8_t *)&value, 2); + return value; + } + + // Plumbing for virtual i2c + void AS7262::_i2c_reg_write_uint8(uint8_t reg, uint8_t value) { + uint8_t buffer[2] = {reg, value}; + i2c_write_blocking(i2c, address, buffer, 2, false); + } + + uint8_t AS7262::_i2c_reg_read_uint8(uint8_t reg) { + uint8_t value; + i2c_write_blocking(i2c, address, ®, 1, false); + i2c_read_blocking(i2c, address, (uint8_t *)&value, 1, false); + return value; + } + + uint8_t AS7262::_i2c_status() { + return _i2c_reg_read_uint8(0x00); + } + + int AS7262::_i2c_read(uint8_t reg, uint8_t *values, int len) { + for (auto i = 0u; i < len; i++){ + while((_i2c_status() & 0b10) != 0) {}; // Wait for write-ready + _i2c_reg_write_uint8(0x01, reg + i); // Set address pointer + while((_i2c_status() & 0b01) != 1) {}; // Wait for read-ready + values[i] = _i2c_reg_read_uint8(0x02); // Read *one* byte :| + } + return 0; + } + + int AS7262::_i2c_write(uint8_t reg, uint8_t *values, int len) { + for (auto i = 0u; i < len; i++){ + while((_i2c_status() & 0b10) != 0) {}; // Wait for write-ready + _i2c_reg_write_uint8(0x01, reg | 0x80); // Set address pointer + while ((_i2c_status() & 0b10) != 0) {}; // Wait for write-ready + _i2c_reg_write_uint8(0x01, values[i]); // Write *one* byte :| + } + return 0; + } +} \ No newline at end of file diff --git a/drivers/as7262/as7262.hpp b/drivers/as7262/as7262.hpp new file mode 100644 index 00000000..508c58d9 --- /dev/null +++ b/drivers/as7262/as7262.hpp @@ -0,0 +1,133 @@ +#pragma once + +#include + +#include "hardware/i2c.h" +#include "hardware/gpio.h" + +namespace pimoroni { + + class AS7262 { + //-------------------------------------------------- + // Constants + //-------------------------------------------------- + public: + static const uint8_t DEFAULT_I2C_ADDRESS = 0x49; + 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; + + /***** More public constants here *****/ + + private: + /***** Private constants here *****/ + + + //-------------------------------------------------- + // Enums + //-------------------------------------------------- + public: + enum class gain : uint8_t { + X1 = 0b00, + X3_7 = 0b01, + X16 = 0b10, + X64 = 0b11 + }; + + enum class illumination_current : uint8_t { + ma12 = 0b00, + ma25 = 0b01, + ma50 = 0b10, + ma100 = 0b11 + }; + + enum class indicator_current : uint8_t { + ma1 = 0b00, + ma2 = 0b01, + ma4 = 0b10, + ma8 = 0b11, + }; + + enum class measurement_mode : uint8_t { + cont_ygnv = 0b00, // yellow, green, blue, violet - continuous + cont_royg = 0b01, // red, orange, yellow, green - continuous + cont_roygbr = 0b10, // red, orange, yellow, green, violet - continuous + oneshot = 0b11 // everything - one-shot + }; + + + //-------------------------------------------------- + // Substructures + //-------------------------------------------------- + public: + struct reading { + float red; + float orange; + float yellow; + float green; + float blue; + float violet; + }; + + + //-------------------------------------------------- + // Variables + //-------------------------------------------------- + private: + i2c_inst_t *i2c = i2c0; + + // interface pins with our standard defaults where appropriate + int8_t address = DEFAULT_I2C_ADDRESS; + int8_t sda = DEFAULT_SDA_PIN; + int8_t scl = DEFAULT_SCL_PIN; + int8_t interrupt = DEFAULT_INT_PIN; + + + //-------------------------------------------------- + // Constructors/Destructor + //-------------------------------------------------- + public: + AS7262() {} + + AS7262(i2c_inst_t *i2c, uint8_t sda, uint8_t scl, uint8_t interrupt = PIN_UNUSED) : + i2c(i2c), sda(sda), scl(scl), interrupt(interrupt) {} + + + //-------------------------------------------------- + // Methods + //-------------------------------------------------- + public: + bool init(); + void reset(); + + uint8_t device_type(); + uint8_t hardware_version(); + std::string firmware_version(); + reading read(); + uint8_t temperature(); + + void set_gain(gain gain); + void set_measurement_mode(measurement_mode mode); + void set_indicator_current(indicator_current current); + void set_illumination_current(illumination_current current); + void set_leds(bool illumination, bool indicator); + + // Virtual i2c transfers, routed through read/write/status regs + uint8_t i2c_reg_read_uint8(uint8_t reg); + void i2c_reg_write_uint8(uint8_t reg, uint8_t value); + uint16_t i2c_reg_read_uint16(uint8_t reg); + float i2c_reg_read_float(uint8_t reg); + + private: + bool data_ready(); + uint8_t _i2c_status(); + int _i2c_read(uint8_t reg, uint8_t *values, int len); + int _i2c_write(uint8_t reg, uint8_t *values, int len); + + // *Real* single-byte i2c transfers + uint8_t _i2c_reg_read_uint8(uint8_t reg); + void _i2c_reg_write_uint8(uint8_t reg, uint8_t value); + }; + +} diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 27789320..61a41027 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -19,3 +19,4 @@ add_subdirectory(pico_tof_display) add_subdirectory(pico_trackball_display) add_subdirectory(pico_audio) add_subdirectory(pico_wireless) +add_subdirectory(breakout_as7262) diff --git a/examples/breakout_as7262/CMakeLists.txt b/examples/breakout_as7262/CMakeLists.txt new file mode 100644 index 00000000..49b75932 --- /dev/null +++ b/examples/breakout_as7262/CMakeLists.txt @@ -0,0 +1,12 @@ +set(OUTPUT_NAME as7262_demo) + +add_executable( + ${OUTPUT_NAME} + demo.cpp +) + +# Pull in pico libraries that we need +target_link_libraries(${OUTPUT_NAME} pico_stdlib hardware_i2c breakout_as7262) + +# create map/bin/hex file etc. +pico_add_extra_outputs(${OUTPUT_NAME}) diff --git a/examples/breakout_as7262/demo.cpp b/examples/breakout_as7262/demo.cpp new file mode 100644 index 00000000..7dd12b22 --- /dev/null +++ b/examples/breakout_as7262/demo.cpp @@ -0,0 +1,43 @@ +#include +#include +#include +#include "pico/stdlib.h" + +#include "breakout_as7262.hpp" + +using namespace pimoroni; + +BreakoutAS7262 as7262; + +int main() { + setup_default_uart(); + + as7262.init(); + + int16_t hw_version = as7262.i2c_reg_read_uint16(0x00); + int16_t fw_version = as7262.i2c_reg_read_uint16(0x02); + printf("%04x %04x \n", hw_version, fw_version); + + as7262.set_gain(AS7262::gain::X16); + as7262.set_measurement_mode(AS7262::measurement_mode::cont_roygbr); + as7262.set_illumination_current(AS7262::illumination_current::ma12); + as7262.set_indicator_current(AS7262::indicator_current::ma4); + //as7262.set_leds(false, false); + + while(true) { + + AS7262::reading reading = as7262.read(); + printf("R: %f O: %f Y: %f G: %f B: %f V: %f \n", + reading.red, + reading.orange, + reading.yellow, + reading.green, + reading.blue, + reading.violet + ); + + sleep_ms(1000); + } + + return 0; +} diff --git a/libraries/CMakeLists.txt b/libraries/CMakeLists.txt index 69378dab..9000c9b2 100644 --- a/libraries/CMakeLists.txt +++ b/libraries/CMakeLists.txt @@ -7,6 +7,7 @@ add_subdirectory(breakout_rgbmatrix5x5) add_subdirectory(breakout_matrix11x7) add_subdirectory(breakout_trackball) add_subdirectory(breakout_sgp30) +add_subdirectory(breakout_as7262) add_subdirectory(pico_graphics) add_subdirectory(pico_display) add_subdirectory(pico_unicorn) diff --git a/libraries/breakout_as7262/CMakeLists.txt b/libraries/breakout_as7262/CMakeLists.txt new file mode 100644 index 00000000..db80dc37 --- /dev/null +++ b/libraries/breakout_as7262/CMakeLists.txt @@ -0,0 +1 @@ +include(breakout_as7262.cmake) diff --git a/libraries/breakout_as7262/breakout_as7262.cmake b/libraries/breakout_as7262/breakout_as7262.cmake new file mode 100644 index 00000000..37696acd --- /dev/null +++ b/libraries/breakout_as7262/breakout_as7262.cmake @@ -0,0 +1,11 @@ +set(LIB_NAME breakout_as7262) +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 hardware_i2c as7262) diff --git a/libraries/breakout_as7262/breakout_as7262.cpp b/libraries/breakout_as7262/breakout_as7262.cpp new file mode 100644 index 00000000..fec97687 --- /dev/null +++ b/libraries/breakout_as7262/breakout_as7262.cpp @@ -0,0 +1,5 @@ +#include "breakout_as7262.hpp" + +namespace pimoroni { + +} diff --git a/libraries/breakout_as7262/breakout_as7262.hpp b/libraries/breakout_as7262/breakout_as7262.hpp new file mode 100644 index 00000000..7894124f --- /dev/null +++ b/libraries/breakout_as7262/breakout_as7262.hpp @@ -0,0 +1,8 @@ +#pragma once + +#include "../../drivers/as7262/as7262.hpp" + +namespace pimoroni { + + typedef AS7262 BreakoutAS7262; +} From 5af43311f4b8438167da8dd42a89117b4c39dc44 Mon Sep 17 00:00:00 2001 From: ZodiusInfuser Date: Thu, 22 Apr 2021 23:03:10 +0100 Subject: [PATCH 2/9] Micropython bindings and minor tweaks for AS7262 --- drivers/as7262/as7262.cpp | 26 +- drivers/as7262/as7262.hpp | 21 +- examples/breakout_as7262/CMakeLists.txt | 2 +- examples/breakout_as7262/demo.cpp | 1 + .../modules/breakout_as7262/breakout_as7262.c | 66 +++++ .../breakout_as7262/breakout_as7262.cpp | 251 ++++++++++++++++++ .../modules/breakout_as7262/breakout_as7262.h | 23 ++ .../modules/breakout_as7262/micropython.cmake | 20 ++ .../modules/breakout_as7262/micropython.mk | 13 + micropython/modules/micropython.cmake | 1 + 10 files changed, 408 insertions(+), 16 deletions(-) create mode 100644 micropython/modules/breakout_as7262/breakout_as7262.c create mode 100644 micropython/modules/breakout_as7262/breakout_as7262.cpp create mode 100644 micropython/modules/breakout_as7262/breakout_as7262.h create mode 100644 micropython/modules/breakout_as7262/micropython.cmake create mode 100755 micropython/modules/breakout_as7262/micropython.mk diff --git a/drivers/as7262/as7262.cpp b/drivers/as7262/as7262.cpp index b2bbd218..624c059d 100644 --- a/drivers/as7262/as7262.cpp +++ b/drivers/as7262/as7262.cpp @@ -69,6 +69,22 @@ namespace pimoroni { sleep_ms(1000); } + i2c_inst_t* AS7262::get_i2c() const { + return i2c; + } + + int AS7262::get_sda() const { + return sda; + } + + int AS7262::get_scl() const { + return scl; + } + + int AS7262::get_int() const { + return interrupt; + } + void AS7262::set_gain(gain gain) { uint8_t temp = i2c_reg_read_uint8(reg::CONTROL) & ~0b00110000; temp |= (uint8_t)gain << 4; @@ -168,8 +184,8 @@ namespace pimoroni { return _i2c_reg_read_uint8(0x00); } - int AS7262::_i2c_read(uint8_t reg, uint8_t *values, int len) { - for (auto i = 0u; i < len; i++){ + uint8_t AS7262::_i2c_read(uint8_t reg, uint8_t *values, uint8_t len) { + for(uint8_t i = 0; i < len; i++){ while((_i2c_status() & 0b10) != 0) {}; // Wait for write-ready _i2c_reg_write_uint8(0x01, reg + i); // Set address pointer while((_i2c_status() & 0b01) != 1) {}; // Wait for read-ready @@ -178,11 +194,11 @@ namespace pimoroni { return 0; } - int AS7262::_i2c_write(uint8_t reg, uint8_t *values, int len) { - for (auto i = 0u; i < len; i++){ + uint8_t AS7262::_i2c_write(uint8_t reg, uint8_t *values, uint8_t len) { + for(uint8_t i = 0; i < len; i++){ while((_i2c_status() & 0b10) != 0) {}; // Wait for write-ready _i2c_reg_write_uint8(0x01, reg | 0x80); // Set address pointer - while ((_i2c_status() & 0b10) != 0) {}; // Wait for write-ready + while((_i2c_status() & 0b10) != 0) {}; // Wait for write-ready _i2c_reg_write_uint8(0x01, values[i]); // Write *one* byte :| } return 0; diff --git a/drivers/as7262/as7262.hpp b/drivers/as7262/as7262.hpp index 508c58d9..495dd5ca 100644 --- a/drivers/as7262/as7262.hpp +++ b/drivers/as7262/as7262.hpp @@ -18,11 +18,6 @@ namespace pimoroni { static const uint8_t DEFAULT_INT_PIN = 22; static const uint8_t PIN_UNUSED = UINT8_MAX; - /***** More public constants here *****/ - - private: - /***** Private constants here *****/ - //-------------------------------------------------- // Enums @@ -101,8 +96,14 @@ namespace pimoroni { bool init(); void reset(); - uint8_t device_type(); - uint8_t hardware_version(); + // For print access in micropython + i2c_inst_t* get_i2c() const; + int get_sda() const; + int get_scl() const; + int get_int() const; + + uint8_t device_type(); //TODO implement + uint8_t hardware_version(); //TODO implement std::string firmware_version(); reading read(); uint8_t temperature(); @@ -113,17 +114,17 @@ namespace pimoroni { void set_illumination_current(illumination_current current); void set_leds(bool illumination, bool indicator); + //TODO make these below functions private // Virtual i2c transfers, routed through read/write/status regs uint8_t i2c_reg_read_uint8(uint8_t reg); void i2c_reg_write_uint8(uint8_t reg, uint8_t value); uint16_t i2c_reg_read_uint16(uint8_t reg); float i2c_reg_read_float(uint8_t reg); - private: bool data_ready(); uint8_t _i2c_status(); - int _i2c_read(uint8_t reg, uint8_t *values, int len); - int _i2c_write(uint8_t reg, uint8_t *values, int len); + uint8_t _i2c_read(uint8_t reg, uint8_t *values, uint8_t len); + uint8_t _i2c_write(uint8_t reg, uint8_t *values, uint8_t len); // *Real* single-byte i2c transfers uint8_t _i2c_reg_read_uint8(uint8_t reg); diff --git a/examples/breakout_as7262/CMakeLists.txt b/examples/breakout_as7262/CMakeLists.txt index 49b75932..fdc46a6b 100644 --- a/examples/breakout_as7262/CMakeLists.txt +++ b/examples/breakout_as7262/CMakeLists.txt @@ -6,7 +6,7 @@ add_executable( ) # Pull in pico libraries that we need -target_link_libraries(${OUTPUT_NAME} pico_stdlib hardware_i2c breakout_as7262) +target_link_libraries(${OUTPUT_NAME} pico_stdlib breakout_as7262) # create map/bin/hex file etc. pico_add_extra_outputs(${OUTPUT_NAME}) diff --git a/examples/breakout_as7262/demo.cpp b/examples/breakout_as7262/demo.cpp index 7dd12b22..667ad71f 100644 --- a/examples/breakout_as7262/demo.cpp +++ b/examples/breakout_as7262/demo.cpp @@ -14,6 +14,7 @@ int main() { as7262.init(); + //TODO replace with nice accessors int16_t hw_version = as7262.i2c_reg_read_uint16(0x00); int16_t fw_version = as7262.i2c_reg_read_uint16(0x02); printf("%04x %04x \n", hw_version, fw_version); diff --git a/micropython/modules/breakout_as7262/breakout_as7262.c b/micropython/modules/breakout_as7262/breakout_as7262.c new file mode 100644 index 00000000..7a8937ef --- /dev/null +++ b/micropython/modules/breakout_as7262/breakout_as7262.c @@ -0,0 +1,66 @@ +#include "breakout_as7262.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// BreakoutAS7262 Class +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/***** Methods *****/ +MP_DEFINE_CONST_FUN_OBJ_1(BreakoutAS7262_reset_obj, BreakoutAS7262_reset); +//MP_DEFINE_CONST_FUN_OBJ_1(BreakoutAS7262_device_type_obj, BreakoutAS7262_device_type); //TODO +//MP_DEFINE_CONST_FUN_OBJ_KW(BreakoutAS7262_hardware_version_obj, 2, BreakoutAS7262_hardware_version); //TODO +MP_DEFINE_CONST_FUN_OBJ_1(BreakoutAS7262_firmware_version_obj, BreakoutAS7262_firmware_version); +MP_DEFINE_CONST_FUN_OBJ_1(BreakoutAS7262_read_obj, BreakoutAS7262_read); +MP_DEFINE_CONST_FUN_OBJ_1(BreakoutAS7262_temperature_obj, BreakoutAS7262_temperature); +MP_DEFINE_CONST_FUN_OBJ_KW(BreakoutAS7262_set_gain_obj, 1, BreakoutAS7262_set_gain); +MP_DEFINE_CONST_FUN_OBJ_KW(BreakoutAS7262_set_measurement_mode_obj, 1, BreakoutAS7262_set_measurement_mode); +MP_DEFINE_CONST_FUN_OBJ_KW(BreakoutAS7262_set_indicator_current_obj, 1, BreakoutAS7262_set_indicator_current); +MP_DEFINE_CONST_FUN_OBJ_KW(BreakoutAS7262_set_illumination_current_obj, 1, BreakoutAS7262_set_illumination_current); +MP_DEFINE_CONST_FUN_OBJ_KW(BreakoutAS7262_set_leds_obj, 1, BreakoutAS7262_set_leds); + +/***** Binding of Methods *****/ +STATIC const mp_rom_map_elem_t BreakoutAS7262_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_reset), MP_ROM_PTR(&BreakoutAS7262_reset_obj) }, + //{ MP_ROM_QSTR(MP_QSTR_device_type), MP_ROM_PTR(&BreakoutAS7262_device_type_obj) }, + //{ MP_ROM_QSTR(MP_QSTR_hardware_version), MP_ROM_PTR(&BreakoutAS7262_hardware_version_obj) }, + { MP_ROM_QSTR(MP_QSTR_firmware_version), MP_ROM_PTR(&BreakoutAS7262_firmware_version_obj) }, + { MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&BreakoutAS7262_read_obj) }, + { MP_ROM_QSTR(MP_QSTR_temperature), MP_ROM_PTR(&BreakoutAS7262_temperature_obj) }, + { MP_ROM_QSTR(MP_QSTR_set_gain), MP_ROM_PTR(&BreakoutAS7262_set_gain_obj) }, + { MP_ROM_QSTR(MP_QSTR_set_measurement_mode), MP_ROM_PTR(&BreakoutAS7262_set_measurement_mode_obj) }, + { MP_ROM_QSTR(MP_QSTR_set_indicator_current), MP_ROM_PTR(&BreakoutAS7262_set_indicator_current_obj) }, + { MP_ROM_QSTR(MP_QSTR_set_illumination_current), MP_ROM_PTR(&BreakoutAS7262_set_illumination_current_obj) }, + { MP_ROM_QSTR(MP_QSTR_set_leds), MP_ROM_PTR(&BreakoutAS7262_set_leds_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(BreakoutAS7262_locals_dict, BreakoutAS7262_locals_dict_table); + +/***** Class Definition *****/ +const mp_obj_type_t breakout_as7262_BreakoutAS7262_type = { + { &mp_type_type }, + .name = MP_QSTR_breakout_matrix11x7, + .print = BreakoutAS7262_print, + .make_new = BreakoutAS7262_make_new, + .locals_dict = (mp_obj_dict_t*)&BreakoutAS7262_locals_dict, +}; + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// breakout_as7262 Module +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/***** Globals Table *****/ +STATIC const mp_map_elem_t breakout_as7262_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_breakout_as7262) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_BreakoutAS7262), (mp_obj_t)&breakout_as7262_BreakoutAS7262_type }, +}; +STATIC MP_DEFINE_CONST_DICT(mp_module_breakout_as7262_globals, breakout_as7262_globals_table); + +/***** Module Definition *****/ +const mp_obj_module_t breakout_as7262_user_cmodule = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t*)&mp_module_breakout_as7262_globals, +}; + +//////////////////////////////////////////////////////////////////////////////////////////////////// +MP_REGISTER_MODULE(MP_QSTR_breakout_as7262, breakout_as7262_user_cmodule, MODULE_BREAKOUT_AS7262_ENABLED); +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////// \ No newline at end of file diff --git a/micropython/modules/breakout_as7262/breakout_as7262.cpp b/micropython/modules/breakout_as7262/breakout_as7262.cpp new file mode 100644 index 00000000..c4e3219b --- /dev/null +++ b/micropython/modules/breakout_as7262/breakout_as7262.cpp @@ -0,0 +1,251 @@ +#include "../../../libraries/breakout_as7262/breakout_as7262.hpp" + +#define MP_OBJ_TO_PTR2(o, t) ((t *)(uintptr_t)(o)) + +// SDA/SCL on even/odd pins, I2C0/I2C1 on even/odd pairs of pins. +#define IS_VALID_SCL(i2c, pin) (((pin) & 1) == 1 && (((pin) & 2) >> 1) == (i2c)) +#define IS_VALID_SDA(i2c, pin) (((pin) & 1) == 0 && (((pin) & 2) >> 1) == (i2c)) + + +using namespace pimoroni; + +extern "C" { +#include "breakout_as7262.h" + +/***** Variables Struct *****/ +typedef struct _breakout_as7262_BreakoutAS7262_obj_t { + mp_obj_base_t base; + BreakoutAS7262 *breakout; +} breakout_as7262_BreakoutAS7262_obj_t; + +/***** Print *****/ +void BreakoutAS7262_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + (void)kind; //Unused input parameter + breakout_as7262_BreakoutAS7262_obj_t *self = MP_OBJ_TO_PTR2(self_in, breakout_as7262_BreakoutAS7262_obj_t); + BreakoutAS7262* breakout = self->breakout; + mp_print_str(print, "BreakoutAS7262("); + + mp_print_str(print, "i2c = "); + mp_obj_print_helper(print, mp_obj_new_int((breakout->get_i2c() == i2c0) ? 0 : 1), PRINT_REPR); + + mp_print_str(print, ", sda = "); + mp_obj_print_helper(print, mp_obj_new_int(breakout->get_sda()), PRINT_REPR); + + mp_print_str(print, ", scl = "); + mp_obj_print_helper(print, mp_obj_new_int(breakout->get_scl()), 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 BreakoutAS7262_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + breakout_as7262_BreakoutAS7262_obj_t *self = nullptr; + + if(n_args == 0) { + mp_arg_check_num(n_args, n_kw, 0, 0, true); + self = m_new_obj(breakout_as7262_BreakoutAS7262_obj_t); + self->base.type = &breakout_as7262_BreakoutAS7262_type; + self->breakout = new BreakoutAS7262(); + } + else { + enum { ARG_i2c, ARG_sda, ARG_scl, ARG_int }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_i2c, MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_sda, MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_scl, MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_int, MP_ARG_INT, {.u_int = BreakoutAS7262::PIN_UNUSED} }, + }; + + // 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 I2C bus. + int i2c_id = args[ARG_i2c].u_int; + if(i2c_id < 0 || i2c_id > 1) { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("I2C(%d) doesn't exist"), i2c_id); + } + + int sda = args[ARG_sda].u_int; + if (!IS_VALID_SDA(i2c_id, sda)) { + mp_raise_ValueError(MP_ERROR_TEXT("bad SDA pin")); + } + + int scl = args[ARG_scl].u_int; + if (!IS_VALID_SCL(i2c_id, scl)) { + mp_raise_ValueError(MP_ERROR_TEXT("bad SCL pin")); + } + + self = m_new_obj(breakout_as7262_BreakoutAS7262_obj_t); + self->base.type = &breakout_as7262_BreakoutAS7262_type; + + i2c_inst_t *i2c = (i2c_id == 0) ? i2c0 : i2c1; + self->breakout = new BreakoutAS7262(i2c, sda, scl, args[ARG_int].u_int); + } + + self->breakout->init(); + + return MP_OBJ_FROM_PTR(self); +} + +/***** Methods *****/ +mp_obj_t BreakoutAS7262_reset(mp_obj_t self_in) { + breakout_as7262_BreakoutAS7262_obj_t *self = MP_OBJ_TO_PTR2(self_in, breakout_as7262_BreakoutAS7262_obj_t); + self->breakout->reset(); + + return mp_const_none; +} + +//TODO +/*mp_obj_t BreakoutAS7262_device_type(mp_obj_t self_in) { + breakout_as7262_BreakoutAS7262_obj_t *self = MP_OBJ_TO_PTR2(self_in, breakout_as7262_BreakoutAS7262_obj_t); + self->breakout->reset(); + + return mp_const_none; +}*/ + +//TODO +/*mp_obj_t BreakoutAS7262_hardware_version(mp_obj_t self_in) { + breakout_as7262_BreakoutAS7262_obj_t *self = MP_OBJ_TO_PTR2(self_in, breakout_as7262_BreakoutAS7262_obj_t); + self->breakout->reset(); + + return mp_const_none; +}*/ + +mp_obj_t BreakoutAS7262_firmware_version(mp_obj_t self_in) { + breakout_as7262_BreakoutAS7262_obj_t *self = MP_OBJ_TO_PTR2(self_in, breakout_as7262_BreakoutAS7262_obj_t); + std::string fw = self->breakout->firmware_version(); + return mp_obj_new_str(fw.c_str(), fw.length()); +} + +mp_obj_t BreakoutAS7262_read(mp_obj_t self_in) { + breakout_as7262_BreakoutAS7262_obj_t *self = MP_OBJ_TO_PTR2(self_in, breakout_as7262_BreakoutAS7262_obj_t); + BreakoutAS7262::reading reading = self->breakout->read(); + + mp_obj_t tuple[6]; + tuple[0] = mp_obj_new_float(reading.red); + tuple[1] = mp_obj_new_float(reading.orange); + tuple[2] = mp_obj_new_float(reading.yellow); + tuple[3] = mp_obj_new_float(reading.green); + tuple[4] = mp_obj_new_float(reading.blue); + tuple[5] = mp_obj_new_float(reading.violet); + return mp_obj_new_tuple(6, tuple); +} + +mp_obj_t BreakoutAS7262_temperature(mp_obj_t self_in) { + breakout_as7262_BreakoutAS7262_obj_t *self = MP_OBJ_TO_PTR2(self_in, breakout_as7262_BreakoutAS7262_obj_t); + return mp_obj_new_int(self->breakout->temperature()); +} + +mp_obj_t BreakoutAS7262_set_gain(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_self, ARG_gain }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_gain, MP_ARG_REQUIRED | MP_ARG_INT }, + }; + + 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); + + //TODO expose values as constants + int gain = args[ARG_gain].u_int; + if(gain < 0 || gain > 3) { + mp_raise_ValueError("mode not a valid value. Expected 0 to 3"); + } + else { + breakout_as7262_BreakoutAS7262_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, breakout_as7262_BreakoutAS7262_obj_t); + self->breakout->set_gain((BreakoutAS7262::gain)gain); + } + + return mp_const_none; +} + +mp_obj_t BreakoutAS7262_set_measurement_mode(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_self, ARG_mode }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_mode, MP_ARG_REQUIRED | MP_ARG_INT }, + }; + + 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); + + //TODO expose values as constants + int mode = args[ARG_mode].u_int; + if(mode < 0 || mode > 3) { + mp_raise_ValueError("mode not a valid value. Expected 0 to 3"); + } + else { + breakout_as7262_BreakoutAS7262_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, breakout_as7262_BreakoutAS7262_obj_t); + self->breakout->set_measurement_mode((BreakoutAS7262::measurement_mode)mode); + } + + return mp_const_none; +} + +mp_obj_t BreakoutAS7262_set_indicator_current(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_self, ARG_current }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_current, MP_ARG_REQUIRED | MP_ARG_INT }, + }; + + 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); + + //TODO expose values as constants + int current = args[ARG_current].u_int; + if(current < 0 || current > 3) { + mp_raise_ValueError("current not a valid value. Expected 0 to 3"); + } + else { + breakout_as7262_BreakoutAS7262_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, breakout_as7262_BreakoutAS7262_obj_t); + self->breakout->set_indicator_current((BreakoutAS7262::indicator_current)current); + } + + return mp_const_none; +} + +mp_obj_t BreakoutAS7262_set_illumination_current(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_self, ARG_current }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_current, MP_ARG_REQUIRED | MP_ARG_INT }, + }; + + 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); + + //TODO expose values as constants + int current = args[ARG_current].u_int; + if(current < 0 || current > 3) { + mp_raise_ValueError("current not a valid value. Expected 0 to 3"); + } + else { + breakout_as7262_BreakoutAS7262_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, breakout_as7262_BreakoutAS7262_obj_t); + self->breakout->set_illumination_current((BreakoutAS7262::illumination_current)current); + } + + return mp_const_none; +} + +mp_obj_t BreakoutAS7262_set_leds(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_self, ARG_illumination, ARG_indicator }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_illumination, MP_ARG_REQUIRED | MP_ARG_BOOL }, + { MP_QSTR_indicator, MP_ARG_REQUIRED | MP_ARG_BOOL }, + }; + + 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_as7262_BreakoutAS7262_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, breakout_as7262_BreakoutAS7262_obj_t); + self->breakout->set_leds(args[ARG_illumination].u_bool, args[ARG_indicator].u_bool); + + return mp_const_none; +} +} \ No newline at end of file diff --git a/micropython/modules/breakout_as7262/breakout_as7262.h b/micropython/modules/breakout_as7262/breakout_as7262.h new file mode 100644 index 00000000..e79ece0f --- /dev/null +++ b/micropython/modules/breakout_as7262/breakout_as7262.h @@ -0,0 +1,23 @@ + +// Include MicroPython API. +#include "py/runtime.h" + +/***** Extern of Class Definition *****/ +extern const mp_obj_type_t breakout_as7262_BreakoutAS7262_type; + +/***** Extern of Class Methods *****/ +extern void BreakoutAS7262_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind); +extern mp_obj_t BreakoutAS7262_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 BreakoutAS7262_reset(mp_obj_t self_in); + +//extern mp_obj_t BreakoutAS7262_device_type(mp_obj_t self_in); //TODO needs C implementation +//extern mp_obj_t BreakoutAS7262_hardware_version(mp_obj_t self_in); //TODO needs C implementation +extern mp_obj_t BreakoutAS7262_firmware_version(mp_obj_t self_in); +extern mp_obj_t BreakoutAS7262_read(mp_obj_t self_in); +extern mp_obj_t BreakoutAS7262_temperature(mp_obj_t self_in); + +extern mp_obj_t BreakoutAS7262_set_gain(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); +extern mp_obj_t BreakoutAS7262_set_measurement_mode(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); +extern mp_obj_t BreakoutAS7262_set_indicator_current(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); +extern mp_obj_t BreakoutAS7262_set_illumination_current(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); +extern mp_obj_t BreakoutAS7262_set_leds(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); \ No newline at end of file diff --git a/micropython/modules/breakout_as7262/micropython.cmake b/micropython/modules/breakout_as7262/micropython.cmake new file mode 100644 index 00000000..63e03d03 --- /dev/null +++ b/micropython/modules/breakout_as7262/micropython.cmake @@ -0,0 +1,20 @@ +set(MOD_NAME breakout_as7262) +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/as7262/as7262.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}) \ No newline at end of file diff --git a/micropython/modules/breakout_as7262/micropython.mk b/micropython/modules/breakout_as7262/micropython.mk new file mode 100755 index 00000000..ed13d3a3 --- /dev/null +++ b/micropython/modules/breakout_as7262/micropython.mk @@ -0,0 +1,13 @@ +set(MOD_NAME breakout_as7262) +BREAKOUT_MOD_DIR := $(USERMOD_DIR) + +# Add our source files to the respective variables. +SRC_USERMOD += $(BREAKOUT_MOD_DIR)/${MOD_NAME}.c +SRC_USERMOD_CXX += $(BREAKOUT_MOD_DIR)/${MOD_NAME}.cpp + +# Add our module directory to the include path. +CFLAGS_USERMOD += -I$(BREAKOUT_MOD_DIR) +CXXFLAGS_USERMOD += -I$(BREAKOUT_MOD_DIR) + +# We use C++ features so have to link against the standard library. +LDFLAGS_USERMOD += -lstdc++ \ No newline at end of file diff --git a/micropython/modules/micropython.cmake b/micropython/modules/micropython.cmake index 3b6b9e5e..770ee370 100644 --- a/micropython/modules/micropython.cmake +++ b/micropython/modules/micropython.cmake @@ -1,6 +1,7 @@ include(${CMAKE_CURRENT_LIST_DIR}/breakout_dotmatrix/micropython.cmake) include(${CMAKE_CURRENT_LIST_DIR}/breakout_ltr559/micropython.cmake) include(${CMAKE_CURRENT_LIST_DIR}/breakout_colourlcd160x80/micropython.cmake) +include(${CMAKE_CURRENT_LIST_DIR}/breakout_as7262/micropython.cmake) include(${CMAKE_CURRENT_LIST_DIR}/breakout_roundlcd/micropython.cmake) include(${CMAKE_CURRENT_LIST_DIR}/breakout_rgbmatrix5x5/micropython.cmake) include(${CMAKE_CURRENT_LIST_DIR}/breakout_matrix11x7/micropython.cmake) From a6c738d018fe99f2bbd4b55fa8c1a9a8faaf814e Mon Sep 17 00:00:00 2001 From: ZodiusInfuser Date: Mon, 10 May 2021 17:22:36 +0100 Subject: [PATCH 3/9] Finished micropython bindings for AS7262, and added example --- drivers/as7262/as7262.cpp | 113 ++++++++++-------- drivers/as7262/as7262.hpp | 19 +-- examples/breakout_as7262/CMakeLists.txt | 4 + examples/breakout_as7262/demo.cpp | 15 ++- micropython/examples/breakout_as7262/demo.py | 28 +++++ .../modules/breakout_as7262/breakout_as7262.c | 26 +++- .../breakout_as7262/breakout_as7262.cpp | 49 ++++---- .../modules/breakout_as7262/breakout_as7262.h | 36 +++++- 8 files changed, 184 insertions(+), 106 deletions(-) create mode 100644 micropython/examples/breakout_as7262/demo.py diff --git a/drivers/as7262/as7262.cpp b/drivers/as7262/as7262.cpp index 624c059d..23888652 100644 --- a/drivers/as7262/as7262.cpp +++ b/drivers/as7262/as7262.cpp @@ -85,6 +85,37 @@ namespace pimoroni { return interrupt; } + uint8_t AS7262::device_type() { + return i2c_reg_read_uint8(reg::DEVICE); + } + + uint8_t AS7262::hardware_version() { + return i2c_reg_read_uint8(reg::HW_VERSION); + } + + void AS7262::firmware_version(uint8_t &major_out, uint8_t &minor_out, uint8_t &sub_out) { + uint16_t fw_version = i2c_reg_read_uint16(reg::FW_VERSION); + major_out = (fw_version & 0x00F0) >> 4; + minor_out = ((fw_version & 0x000F) << 2) | ((fw_version & 0xC000) >> 14); + sub_out = (fw_version & 0x3F00) >> 8; + } + + AS7262::reading AS7262::read() { + while(!data_ready()) {} + return AS7262::reading { + i2c_reg_read_float(reg::R_CAL_F), + i2c_reg_read_float(reg::O_CAL_F), + i2c_reg_read_float(reg::Y_CAL_F), + i2c_reg_read_float(reg::G_CAL_F), + i2c_reg_read_float(reg::B_CAL_F), + i2c_reg_read_float(reg::V_CAL_F) + }; + } + + uint8_t AS7262::temperature() { + return i2c_reg_read_uint8(reg::TEMP); + } + void AS7262::set_gain(gain gain) { uint8_t temp = i2c_reg_read_uint8(reg::CONTROL) & ~0b00110000; temp |= (uint8_t)gain << 4; @@ -116,58 +147,58 @@ namespace pimoroni { i2c_reg_write_uint8(reg::LED_CONTROL, temp); } - std::string AS7262::firmware_version() { - std::string buf; - uint16_t fw_version = i2c_reg_read_uint16(reg::FW_VERSION); - buf += std::to_string(fw_version); - return buf; - } - bool AS7262::data_ready() { return i2c_reg_read_uint8(reg::CONTROL) & 0b00000010; } - AS7262::reading AS7262::read() { - while(!data_ready()) {} - return AS7262::reading { - i2c_reg_read_float(reg::R_CAL_F), - i2c_reg_read_float(reg::O_CAL_F), - i2c_reg_read_float(reg::Y_CAL_F), - i2c_reg_read_float(reg::G_CAL_F), - i2c_reg_read_float(reg::B_CAL_F), - i2c_reg_read_float(reg::V_CAL_F) - }; - } - - uint8_t AS7262::temperature() { - return i2c_reg_read_uint8(reg::TEMP); - } - // i2c IO wrappers around the weird virtual i2c nonsense - void AS7262::i2c_reg_write_uint8(uint8_t reg, uint8_t value) { - _i2c_write(reg, &value, 1); + i2c_write(reg, &value, 1); } float AS7262::i2c_reg_read_float(uint8_t reg) { float value; - _i2c_read(reg, (uint8_t *)&value, 4); + i2c_read(reg, (uint8_t *)&value, 4); return __builtin_bswap32(value); } uint8_t AS7262::i2c_reg_read_uint8(uint8_t reg) { uint8_t value; - _i2c_read(reg, &value, 1); + i2c_read(reg, &value, 1); return value; } uint16_t AS7262::i2c_reg_read_uint16(uint8_t reg) { uint16_t value; - _i2c_read(reg, (uint8_t *)&value, 2); + i2c_read(reg, (uint8_t *)&value, 2); return value; } - // Plumbing for virtual i2c + uint8_t AS7262::i2c_status() { + return _i2c_reg_read_uint8(0x00); + } + + uint8_t AS7262::i2c_read(uint8_t reg, uint8_t *values, uint8_t len) { + for(uint8_t i = 0; i < len; i++){ + while((i2c_status() & 0b10) != 0) {}; // Wait for write-ready + _i2c_reg_write_uint8(0x01, reg + i); // Set address pointer + while((i2c_status() & 0b01) != 1) {}; // Wait for read-ready + values[i] = _i2c_reg_read_uint8(0x02); // Read *one* byte :| + } + return 0; + } + + uint8_t AS7262::i2c_write(uint8_t reg, uint8_t *values, uint8_t len) { + for(uint8_t i = 0; i < len; i++){ + while((i2c_status() & 0b10) != 0) {}; // Wait for write-ready + _i2c_reg_write_uint8(0x01, reg | 0x80); // Set address pointer + while((i2c_status() & 0b10) != 0) {}; // Wait for write-ready + _i2c_reg_write_uint8(0x01, values[i]); // Write *one* byte :| + } + return 0; + } + + // Plumbing for virtual i2c void AS7262::_i2c_reg_write_uint8(uint8_t reg, uint8_t value) { uint8_t buffer[2] = {reg, value}; i2c_write_blocking(i2c, address, buffer, 2, false); @@ -179,28 +210,4 @@ namespace pimoroni { i2c_read_blocking(i2c, address, (uint8_t *)&value, 1, false); return value; } - - uint8_t AS7262::_i2c_status() { - return _i2c_reg_read_uint8(0x00); - } - - uint8_t AS7262::_i2c_read(uint8_t reg, uint8_t *values, uint8_t len) { - for(uint8_t i = 0; i < len; i++){ - while((_i2c_status() & 0b10) != 0) {}; // Wait for write-ready - _i2c_reg_write_uint8(0x01, reg + i); // Set address pointer - while((_i2c_status() & 0b01) != 1) {}; // Wait for read-ready - values[i] = _i2c_reg_read_uint8(0x02); // Read *one* byte :| - } - return 0; - } - - uint8_t AS7262::_i2c_write(uint8_t reg, uint8_t *values, uint8_t len) { - for(uint8_t i = 0; i < len; i++){ - while((_i2c_status() & 0b10) != 0) {}; // Wait for write-ready - _i2c_reg_write_uint8(0x01, reg | 0x80); // Set address pointer - while((_i2c_status() & 0b10) != 0) {}; // Wait for write-ready - _i2c_reg_write_uint8(0x01, values[i]); // Write *one* byte :| - } - return 0; - } } \ No newline at end of file diff --git a/drivers/as7262/as7262.hpp b/drivers/as7262/as7262.hpp index 495dd5ca..bf562598 100644 --- a/drivers/as7262/as7262.hpp +++ b/drivers/as7262/as7262.hpp @@ -102,9 +102,9 @@ namespace pimoroni { int get_scl() const; int get_int() const; - uint8_t device_type(); //TODO implement - uint8_t hardware_version(); //TODO implement - std::string firmware_version(); + uint8_t device_type(); + uint8_t hardware_version(); + void firmware_version(uint8_t &major_out, uint8_t &minor_out, uint8_t &sub_out); reading read(); uint8_t temperature(); @@ -114,17 +114,18 @@ namespace pimoroni { void set_illumination_current(illumination_current current); void set_leds(bool illumination, bool indicator); - //TODO make these below functions private +private: + bool data_ready(); + // Virtual i2c transfers, routed through read/write/status regs uint8_t i2c_reg_read_uint8(uint8_t reg); void i2c_reg_write_uint8(uint8_t reg, uint8_t value); uint16_t i2c_reg_read_uint16(uint8_t reg); float i2c_reg_read_float(uint8_t reg); - private: - bool data_ready(); - uint8_t _i2c_status(); - uint8_t _i2c_read(uint8_t reg, uint8_t *values, uint8_t len); - uint8_t _i2c_write(uint8_t reg, uint8_t *values, uint8_t len); + + uint8_t i2c_status(); + uint8_t i2c_read(uint8_t reg, uint8_t *values, uint8_t len); + uint8_t i2c_write(uint8_t reg, uint8_t *values, uint8_t len); // *Real* single-byte i2c transfers uint8_t _i2c_reg_read_uint8(uint8_t reg); diff --git a/examples/breakout_as7262/CMakeLists.txt b/examples/breakout_as7262/CMakeLists.txt index fdc46a6b..12c54676 100644 --- a/examples/breakout_as7262/CMakeLists.txt +++ b/examples/breakout_as7262/CMakeLists.txt @@ -5,6 +5,10 @@ add_executable( 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_as7262) diff --git a/examples/breakout_as7262/demo.cpp b/examples/breakout_as7262/demo.cpp index 667ad71f..60d5b0e4 100644 --- a/examples/breakout_as7262/demo.cpp +++ b/examples/breakout_as7262/demo.cpp @@ -1,6 +1,3 @@ -#include -#include -#include #include "pico/stdlib.h" #include "breakout_as7262.hpp" @@ -10,14 +7,16 @@ using namespace pimoroni; BreakoutAS7262 as7262; int main() { - setup_default_uart(); + stdio_init_all(); as7262.init(); - //TODO replace with nice accessors - int16_t hw_version = as7262.i2c_reg_read_uint16(0x00); - int16_t fw_version = as7262.i2c_reg_read_uint16(0x02); - printf("%04x %04x \n", hw_version, fw_version); + uint8_t dev_type = as7262.device_type(); + uint8_t hw_version = as7262.hardware_version(); + + uint8_t major, minor, sub; + as7262.firmware_version(major, minor, sub); + printf("Device: %d, HW: %d, FW: %d.%d.%d\n", dev_type, hw_version, major, minor, sub); as7262.set_gain(AS7262::gain::X16); as7262.set_measurement_mode(AS7262::measurement_mode::cont_roygbr); diff --git a/micropython/examples/breakout_as7262/demo.py b/micropython/examples/breakout_as7262/demo.py new file mode 100644 index 00000000..a26ed22a --- /dev/null +++ b/micropython/examples/breakout_as7262/demo.py @@ -0,0 +1,28 @@ +import time +from breakout_as7262 import BreakoutAS7262 + +as7262 = BreakoutAS7262() + +dev_type = as7262.device_type(); +hw_version = as7262.hardware_version(); +fw_version = as7262.firmware_version(); + +print("Device: ", dev_type, "HW: ", hw_version, sep="", end=", ") +print("FW: ", fw_version[0], ".", fw_version[1], ".", fw_version[2]) + +as7262.set_gain(BreakoutAS7262.X16) +as7262.set_measurement_mode(BreakoutAS7262.CONT_ROYGBR) +as7262.set_illumination_current(BreakoutAS7262.MA12) +as7262.set_indicator_current(BreakoutAS7262.MA4) +#as7262.set_leds(False, False) + +while True: + reading = as7262.read() + print("R:", reading[0], end=" ") + print("O:", reading[1], end=" ") + print("Y:", reading[2], end=" ") + print("G:", reading[3], end=" ") + print("B:", reading[4], end=" ") + print("V:", reading[5]) + + time.sleep(1.0) diff --git a/micropython/modules/breakout_as7262/breakout_as7262.c b/micropython/modules/breakout_as7262/breakout_as7262.c index 7a8937ef..cacad095 100644 --- a/micropython/modules/breakout_as7262/breakout_as7262.c +++ b/micropython/modules/breakout_as7262/breakout_as7262.c @@ -6,8 +6,8 @@ /***** Methods *****/ MP_DEFINE_CONST_FUN_OBJ_1(BreakoutAS7262_reset_obj, BreakoutAS7262_reset); -//MP_DEFINE_CONST_FUN_OBJ_1(BreakoutAS7262_device_type_obj, BreakoutAS7262_device_type); //TODO -//MP_DEFINE_CONST_FUN_OBJ_KW(BreakoutAS7262_hardware_version_obj, 2, BreakoutAS7262_hardware_version); //TODO +MP_DEFINE_CONST_FUN_OBJ_1(BreakoutAS7262_device_type_obj, BreakoutAS7262_device_type); +MP_DEFINE_CONST_FUN_OBJ_1(BreakoutAS7262_hardware_version_obj, BreakoutAS7262_hardware_version); MP_DEFINE_CONST_FUN_OBJ_1(BreakoutAS7262_firmware_version_obj, BreakoutAS7262_firmware_version); MP_DEFINE_CONST_FUN_OBJ_1(BreakoutAS7262_read_obj, BreakoutAS7262_read); MP_DEFINE_CONST_FUN_OBJ_1(BreakoutAS7262_temperature_obj, BreakoutAS7262_temperature); @@ -20,8 +20,8 @@ MP_DEFINE_CONST_FUN_OBJ_KW(BreakoutAS7262_set_leds_obj, 1, BreakoutAS7262_set_le /***** Binding of Methods *****/ STATIC const mp_rom_map_elem_t BreakoutAS7262_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_reset), MP_ROM_PTR(&BreakoutAS7262_reset_obj) }, - //{ MP_ROM_QSTR(MP_QSTR_device_type), MP_ROM_PTR(&BreakoutAS7262_device_type_obj) }, - //{ MP_ROM_QSTR(MP_QSTR_hardware_version), MP_ROM_PTR(&BreakoutAS7262_hardware_version_obj) }, + { MP_ROM_QSTR(MP_QSTR_device_type), MP_ROM_PTR(&BreakoutAS7262_device_type_obj) }, + { MP_ROM_QSTR(MP_QSTR_hardware_version), MP_ROM_PTR(&BreakoutAS7262_hardware_version_obj) }, { MP_ROM_QSTR(MP_QSTR_firmware_version), MP_ROM_PTR(&BreakoutAS7262_firmware_version_obj) }, { MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&BreakoutAS7262_read_obj) }, { MP_ROM_QSTR(MP_QSTR_temperature), MP_ROM_PTR(&BreakoutAS7262_temperature_obj) }, @@ -30,6 +30,22 @@ STATIC const mp_rom_map_elem_t BreakoutAS7262_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_set_indicator_current), MP_ROM_PTR(&BreakoutAS7262_set_indicator_current_obj) }, { MP_ROM_QSTR(MP_QSTR_set_illumination_current), MP_ROM_PTR(&BreakoutAS7262_set_illumination_current_obj) }, { MP_ROM_QSTR(MP_QSTR_set_leds), MP_ROM_PTR(&BreakoutAS7262_set_leds_obj) }, + { MP_ROM_QSTR(MP_QSTR_X1), MP_ROM_INT(MP_X1) }, + { MP_ROM_QSTR(MP_QSTR_X3_7), MP_ROM_INT(MP_X3_7) }, + { MP_ROM_QSTR(MP_QSTR_X16), MP_ROM_INT(MP_X16) }, + { MP_ROM_QSTR(MP_QSTR_X64), MP_ROM_INT(MP_X64) }, + { MP_ROM_QSTR(MP_QSTR_MA12), MP_ROM_INT(MP_MA12) }, + { MP_ROM_QSTR(MP_QSTR_MA25), MP_ROM_INT(MP_MA25) }, + { MP_ROM_QSTR(MP_QSTR_MA50), MP_ROM_INT(MP_MA50) }, + { MP_ROM_QSTR(MP_QSTR_MA100), MP_ROM_INT(MP_MA100) }, + { MP_ROM_QSTR(MP_QSTR_MA1), MP_ROM_INT(MP_MA1) }, + { MP_ROM_QSTR(MP_QSTR_MA2), MP_ROM_INT(MP_MA2) }, + { MP_ROM_QSTR(MP_QSTR_MA4), MP_ROM_INT(MP_MA4) }, + { MP_ROM_QSTR(MP_QSTR_MA8), MP_ROM_INT(MP_MA8) }, + { MP_ROM_QSTR(MP_QSTR_CONT_YGNV), MP_ROM_INT(MP_CONT_YGNV) }, + { MP_ROM_QSTR(MP_QSTR_CONT_ROYG), MP_ROM_INT(MP_CONT_ROYG) }, + { MP_ROM_QSTR(MP_QSTR_CONT_ROYGBR), MP_ROM_INT(MP_CONT_ROYGBR) }, + { MP_ROM_QSTR(MP_QSTR_ONESHOT), MP_ROM_INT(MP_ONESHOT) }, }; STATIC MP_DEFINE_CONST_DICT(BreakoutAS7262_locals_dict, BreakoutAS7262_locals_dict_table); @@ -49,7 +65,7 @@ const mp_obj_type_t breakout_as7262_BreakoutAS7262_type = { /***** Globals Table *****/ STATIC const mp_map_elem_t breakout_as7262_globals_table[] = { - { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_breakout_as7262) }, + { MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_breakout_as7262) }, { MP_OBJ_NEW_QSTR(MP_QSTR_BreakoutAS7262), (mp_obj_t)&breakout_as7262_BreakoutAS7262_type }, }; STATIC MP_DEFINE_CONST_DICT(mp_module_breakout_as7262_globals, breakout_as7262_globals_table); diff --git a/micropython/modules/breakout_as7262/breakout_as7262.cpp b/micropython/modules/breakout_as7262/breakout_as7262.cpp index c4e3219b..fbbeefbc 100644 --- a/micropython/modules/breakout_as7262/breakout_as7262.cpp +++ b/micropython/modules/breakout_as7262/breakout_as7262.cpp @@ -48,7 +48,7 @@ mp_obj_t BreakoutAS7262_make_new(const mp_obj_type_t *type, size_t n_args, size_ mp_arg_check_num(n_args, n_kw, 0, 0, true); self = m_new_obj(breakout_as7262_BreakoutAS7262_obj_t); self->base.type = &breakout_as7262_BreakoutAS7262_type; - self->breakout = new BreakoutAS7262(); + self->breakout = new BreakoutAS7262(); } else { enum { ARG_i2c, ARG_sda, ARG_scl, ARG_int }; @@ -70,14 +70,14 @@ mp_obj_t BreakoutAS7262_make_new(const mp_obj_type_t *type, size_t n_args, size_ } int sda = args[ARG_sda].u_int; - if (!IS_VALID_SDA(i2c_id, sda)) { + if(!IS_VALID_SDA(i2c_id, sda)) { mp_raise_ValueError(MP_ERROR_TEXT("bad SDA pin")); } int scl = args[ARG_scl].u_int; - if (!IS_VALID_SCL(i2c_id, scl)) { + if(!IS_VALID_SCL(i2c_id, scl)) { mp_raise_ValueError(MP_ERROR_TEXT("bad SCL pin")); - } + } self = m_new_obj(breakout_as7262_BreakoutAS7262_obj_t); self->base.type = &breakout_as7262_BreakoutAS7262_type; @@ -99,26 +99,27 @@ mp_obj_t BreakoutAS7262_reset(mp_obj_t self_in) { return mp_const_none; } -//TODO -/*mp_obj_t BreakoutAS7262_device_type(mp_obj_t self_in) { +mp_obj_t BreakoutAS7262_device_type(mp_obj_t self_in) { breakout_as7262_BreakoutAS7262_obj_t *self = MP_OBJ_TO_PTR2(self_in, breakout_as7262_BreakoutAS7262_obj_t); - self->breakout->reset(); + return mp_obj_new_int(self->breakout->device_type()); +} - return mp_const_none; -}*/ - -//TODO -/*mp_obj_t BreakoutAS7262_hardware_version(mp_obj_t self_in) { +mp_obj_t BreakoutAS7262_hardware_version(mp_obj_t self_in) { breakout_as7262_BreakoutAS7262_obj_t *self = MP_OBJ_TO_PTR2(self_in, breakout_as7262_BreakoutAS7262_obj_t); - self->breakout->reset(); - - return mp_const_none; -}*/ + return mp_obj_new_int(self->breakout->hardware_version()); +} mp_obj_t BreakoutAS7262_firmware_version(mp_obj_t self_in) { breakout_as7262_BreakoutAS7262_obj_t *self = MP_OBJ_TO_PTR2(self_in, breakout_as7262_BreakoutAS7262_obj_t); - std::string fw = self->breakout->firmware_version(); - return mp_obj_new_str(fw.c_str(), fw.length()); + + uint8_t major, minor, sub; + self->breakout->firmware_version(major, minor, sub); + + mp_obj_t tuple[3]; + tuple[0] = mp_obj_new_int(major); + tuple[1] = mp_obj_new_float(minor); + tuple[2] = mp_obj_new_float(sub); + return mp_obj_new_tuple(3, tuple); } mp_obj_t BreakoutAS7262_read(mp_obj_t self_in) { @@ -150,10 +151,9 @@ mp_obj_t BreakoutAS7262_set_gain(size_t n_args, const mp_obj_t *pos_args, mp_map 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); - //TODO expose values as constants int gain = args[ARG_gain].u_int; if(gain < 0 || gain > 3) { - mp_raise_ValueError("mode not a valid value. Expected 0 to 3"); + mp_raise_ValueError("mode not a valid value. Expected 0 to 3 (X1, X3_7, X16, X64)"); } else { breakout_as7262_BreakoutAS7262_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, breakout_as7262_BreakoutAS7262_obj_t); @@ -173,10 +173,9 @@ mp_obj_t BreakoutAS7262_set_measurement_mode(size_t n_args, const mp_obj_t *pos_ 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); - //TODO expose values as constants int mode = args[ARG_mode].u_int; if(mode < 0 || mode > 3) { - mp_raise_ValueError("mode not a valid value. Expected 0 to 3"); + mp_raise_ValueError("mode not a valid value. Expected 0 to 3 (CONT_YGNV, CONT_ROYG, CONT_ROYGBR, ONESHOT)"); } else { breakout_as7262_BreakoutAS7262_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, breakout_as7262_BreakoutAS7262_obj_t); @@ -196,10 +195,9 @@ mp_obj_t BreakoutAS7262_set_indicator_current(size_t n_args, const mp_obj_t *pos 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); - //TODO expose values as constants int current = args[ARG_current].u_int; if(current < 0 || current > 3) { - mp_raise_ValueError("current not a valid value. Expected 0 to 3"); + mp_raise_ValueError("current not a valid value. Expected 0 to 3 (MA1, MA2, MA4, MA8)"); } else { breakout_as7262_BreakoutAS7262_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, breakout_as7262_BreakoutAS7262_obj_t); @@ -219,10 +217,9 @@ mp_obj_t BreakoutAS7262_set_illumination_current(size_t n_args, const mp_obj_t * 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); - //TODO expose values as constants int current = args[ARG_current].u_int; if(current < 0 || current > 3) { - mp_raise_ValueError("current not a valid value. Expected 0 to 3"); + mp_raise_ValueError("current not a valid value. Expected 0 to 3 (MA12, MA25, MA50, MA100)"); } else { breakout_as7262_BreakoutAS7262_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, breakout_as7262_BreakoutAS7262_obj_t); diff --git a/micropython/modules/breakout_as7262/breakout_as7262.h b/micropython/modules/breakout_as7262/breakout_as7262.h index e79ece0f..b5263b50 100644 --- a/micropython/modules/breakout_as7262/breakout_as7262.h +++ b/micropython/modules/breakout_as7262/breakout_as7262.h @@ -1,7 +1,35 @@ - // Include MicroPython API. #include "py/runtime.h" +/***** Constants *****/ +enum { + MP_X1 = 0b00, + MP_X3_7 = 0b01, + MP_X16 = 0b10, + MP_X64 = 0b11 +}; + +enum { + MP_MA12 = 0b00, + MP_MA25 = 0b01, + MP_MA50 = 0b10, + MP_MA100 = 0b11 +}; + +enum { + MP_MA1 = 0b00, + MP_MA2 = 0b01, + MP_MA4 = 0b10, + MP_MA8 = 0b11 +}; + +enum { + MP_CONT_YGNV = 0b00, // yellow, green, blue, violet - continuous + MP_CONT_ROYG = 0b01, // red, orange, yellow, green - continuous + MP_CONT_ROYGBR = 0b10, // red, orange, yellow, green, violet - continuous + MP_ONESHOT = 0b11 // everything - one-shot +}; + /***** Extern of Class Definition *****/ extern const mp_obj_type_t breakout_as7262_BreakoutAS7262_type; @@ -9,13 +37,11 @@ extern const mp_obj_type_t breakout_as7262_BreakoutAS7262_type; extern void BreakoutAS7262_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind); extern mp_obj_t BreakoutAS7262_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 BreakoutAS7262_reset(mp_obj_t self_in); - -//extern mp_obj_t BreakoutAS7262_device_type(mp_obj_t self_in); //TODO needs C implementation -//extern mp_obj_t BreakoutAS7262_hardware_version(mp_obj_t self_in); //TODO needs C implementation +extern mp_obj_t BreakoutAS7262_device_type(mp_obj_t self_in); +extern mp_obj_t BreakoutAS7262_hardware_version(mp_obj_t self_in); extern mp_obj_t BreakoutAS7262_firmware_version(mp_obj_t self_in); extern mp_obj_t BreakoutAS7262_read(mp_obj_t self_in); extern mp_obj_t BreakoutAS7262_temperature(mp_obj_t self_in); - extern mp_obj_t BreakoutAS7262_set_gain(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); extern mp_obj_t BreakoutAS7262_set_measurement_mode(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); extern mp_obj_t BreakoutAS7262_set_indicator_current(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); From 9cbeb79b43a28b330adcc1709a05204809407365 Mon Sep 17 00:00:00 2001 From: ZodiusInfuser Date: Mon, 10 May 2021 17:24:47 +0100 Subject: [PATCH 4/9] Micropython linting fixes --- micropython/examples/breakout_as7262/demo.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/micropython/examples/breakout_as7262/demo.py b/micropython/examples/breakout_as7262/demo.py index a26ed22a..f6fdd8d7 100644 --- a/micropython/examples/breakout_as7262/demo.py +++ b/micropython/examples/breakout_as7262/demo.py @@ -3,9 +3,9 @@ from breakout_as7262 import BreakoutAS7262 as7262 = BreakoutAS7262() -dev_type = as7262.device_type(); -hw_version = as7262.hardware_version(); -fw_version = as7262.firmware_version(); +dev_type = as7262.device_type() +hw_version = as7262.hardware_version() +fw_version = as7262.firmware_version() print("Device: ", dev_type, "HW: ", hw_version, sep="", end=", ") print("FW: ", fw_version[0], ".", fw_version[1], ".", fw_version[2]) @@ -14,7 +14,7 @@ as7262.set_gain(BreakoutAS7262.X16) as7262.set_measurement_mode(BreakoutAS7262.CONT_ROYGBR) as7262.set_illumination_current(BreakoutAS7262.MA12) as7262.set_indicator_current(BreakoutAS7262.MA4) -#as7262.set_leds(False, False) +# as7262.set_leds(False, False) while True: reading = as7262.read() @@ -24,5 +24,4 @@ while True: print("G:", reading[3], end=" ") print("B:", reading[4], end=" ") print("V:", reading[5]) - time.sleep(1.0) From ae489873ee9ded01bde8e1736e4c5f68aba74da3 Mon Sep 17 00:00:00 2001 From: Phil Howard Date: Wed, 12 May 2021 15:00:14 +0100 Subject: [PATCH 5/9] AS7262 add set_integration_time --- drivers/as7262/as7262.cpp | 5 +++++ drivers/as7262/as7262.hpp | 1 + .../modules/breakout_as7262/breakout_as7262.c | 4 +++- .../breakout_as7262/breakout_as7262.cpp | 18 ++++++++++++++++++ .../modules/breakout_as7262/breakout_as7262.h | 1 + 5 files changed, 28 insertions(+), 1 deletion(-) diff --git a/drivers/as7262/as7262.cpp b/drivers/as7262/as7262.cpp index 23888652..8f14ebfd 100644 --- a/drivers/as7262/as7262.cpp +++ b/drivers/as7262/as7262.cpp @@ -147,6 +147,11 @@ namespace pimoroni { i2c_reg_write_uint8(reg::LED_CONTROL, temp); } + void AS7262::set_integration_time(float integration_time_ms) { + uint8_t integration_time = uint8_t(integration_time_ms * 2.88); + i2c_reg_write_uint8(reg::INT_T, integration_time); + } + bool AS7262::data_ready() { return i2c_reg_read_uint8(reg::CONTROL) & 0b00000010; } diff --git a/drivers/as7262/as7262.hpp b/drivers/as7262/as7262.hpp index bf562598..d3151b51 100644 --- a/drivers/as7262/as7262.hpp +++ b/drivers/as7262/as7262.hpp @@ -112,6 +112,7 @@ namespace pimoroni { void set_measurement_mode(measurement_mode mode); void set_indicator_current(indicator_current current); void set_illumination_current(illumination_current current); + void set_integration_time(float integration_time_ms); void set_leds(bool illumination, bool indicator); private: diff --git a/micropython/modules/breakout_as7262/breakout_as7262.c b/micropython/modules/breakout_as7262/breakout_as7262.c index cacad095..43021770 100644 --- a/micropython/modules/breakout_as7262/breakout_as7262.c +++ b/micropython/modules/breakout_as7262/breakout_as7262.c @@ -15,6 +15,7 @@ MP_DEFINE_CONST_FUN_OBJ_KW(BreakoutAS7262_set_gain_obj, 1, BreakoutAS7262_set_ga MP_DEFINE_CONST_FUN_OBJ_KW(BreakoutAS7262_set_measurement_mode_obj, 1, BreakoutAS7262_set_measurement_mode); MP_DEFINE_CONST_FUN_OBJ_KW(BreakoutAS7262_set_indicator_current_obj, 1, BreakoutAS7262_set_indicator_current); MP_DEFINE_CONST_FUN_OBJ_KW(BreakoutAS7262_set_illumination_current_obj, 1, BreakoutAS7262_set_illumination_current); +MP_DEFINE_CONST_FUN_OBJ_KW(BreakoutAS7262_set_integration_time_obj, 1, BreakoutAS7262_set_integration_time); MP_DEFINE_CONST_FUN_OBJ_KW(BreakoutAS7262_set_leds_obj, 1, BreakoutAS7262_set_leds); /***** Binding of Methods *****/ @@ -29,6 +30,7 @@ STATIC const mp_rom_map_elem_t BreakoutAS7262_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_set_measurement_mode), MP_ROM_PTR(&BreakoutAS7262_set_measurement_mode_obj) }, { MP_ROM_QSTR(MP_QSTR_set_indicator_current), MP_ROM_PTR(&BreakoutAS7262_set_indicator_current_obj) }, { MP_ROM_QSTR(MP_QSTR_set_illumination_current), MP_ROM_PTR(&BreakoutAS7262_set_illumination_current_obj) }, + { MP_ROM_QSTR(MP_QSTR_set_integration_time), MP_ROM_PTR(&BreakoutAS7262_set_integration_time_obj) }, { MP_ROM_QSTR(MP_QSTR_set_leds), MP_ROM_PTR(&BreakoutAS7262_set_leds_obj) }, { MP_ROM_QSTR(MP_QSTR_X1), MP_ROM_INT(MP_X1) }, { MP_ROM_QSTR(MP_QSTR_X3_7), MP_ROM_INT(MP_X3_7) }, @@ -52,7 +54,7 @@ STATIC MP_DEFINE_CONST_DICT(BreakoutAS7262_locals_dict, BreakoutAS7262_locals_di /***** Class Definition *****/ const mp_obj_type_t breakout_as7262_BreakoutAS7262_type = { { &mp_type_type }, - .name = MP_QSTR_breakout_matrix11x7, + .name = MP_QSTR_breakout_as7262, .print = BreakoutAS7262_print, .make_new = BreakoutAS7262_make_new, .locals_dict = (mp_obj_dict_t*)&BreakoutAS7262_locals_dict, diff --git a/micropython/modules/breakout_as7262/breakout_as7262.cpp b/micropython/modules/breakout_as7262/breakout_as7262.cpp index fbbeefbc..35ae2bcd 100644 --- a/micropython/modules/breakout_as7262/breakout_as7262.cpp +++ b/micropython/modules/breakout_as7262/breakout_as7262.cpp @@ -229,6 +229,24 @@ mp_obj_t BreakoutAS7262_set_illumination_current(size_t n_args, const mp_obj_t * return mp_const_none; } +mp_obj_t BreakoutAS7262_set_integration_time(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_self, ARG_integration_time }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_integration_time, MP_ARG_REQUIRED | MP_ARG_OBJ }, + }; + + 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); + + float integration_time = mp_obj_get_float(args[ARG_integration_time].u_obj); + + breakout_as7262_BreakoutAS7262_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, breakout_as7262_BreakoutAS7262_obj_t); + self->breakout->set_integration_time(integration_time); + + return mp_const_none; +} + mp_obj_t BreakoutAS7262_set_leds(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { enum { ARG_self, ARG_illumination, ARG_indicator }; static const mp_arg_t allowed_args[] = { diff --git a/micropython/modules/breakout_as7262/breakout_as7262.h b/micropython/modules/breakout_as7262/breakout_as7262.h index b5263b50..1d7808b4 100644 --- a/micropython/modules/breakout_as7262/breakout_as7262.h +++ b/micropython/modules/breakout_as7262/breakout_as7262.h @@ -46,4 +46,5 @@ extern mp_obj_t BreakoutAS7262_set_gain(size_t n_args, const mp_obj_t *pos_args, extern mp_obj_t BreakoutAS7262_set_measurement_mode(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); extern mp_obj_t BreakoutAS7262_set_indicator_current(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); extern mp_obj_t BreakoutAS7262_set_illumination_current(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); +extern mp_obj_t BreakoutAS7262_set_integration_time(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); extern mp_obj_t BreakoutAS7262_set_leds(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); \ No newline at end of file From 8db593a819a389cc1f87f525bf5b52a911116ba1 Mon Sep 17 00:00:00 2001 From: Phil Howard Date: Wed, 12 May 2021 15:00:43 +0100 Subject: [PATCH 6/9] AS7262 convert float correctly --- drivers/as7262/as7262.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/drivers/as7262/as7262.cpp b/drivers/as7262/as7262.cpp index 8f14ebfd..3309e104 100644 --- a/drivers/as7262/as7262.cpp +++ b/drivers/as7262/as7262.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include "as7262.hpp" @@ -161,10 +162,19 @@ namespace pimoroni { i2c_write(reg, &value, 1); } + // convert the AS7262s 4-byte big-endian float value into a native float float AS7262::i2c_reg_read_float(uint8_t reg) { - float value; + uint32_t value; i2c_read(reg, (uint8_t *)&value, 4); - return __builtin_bswap32(value); + value = __builtin_bswap32(value); + + // Fails due to -Werror=strict-aliasing in MicroPython build + // return reinterpret_cast(value); + + // Assumes sizeof(uint32_t) == sizeof(float) + float result; + memcpy(&result, &value, sizeof(float)); + return result; } uint8_t AS7262::i2c_reg_read_uint8(uint8_t reg) { From c4383647c64c49357d0b8b1b3b6f8906c1ce031e Mon Sep 17 00:00:00 2001 From: Phil Howard Date: Wed, 12 May 2021 15:01:19 +0100 Subject: [PATCH 7/9] AS7262 tweak C++ demo --- examples/breakout_as7262/demo.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/examples/breakout_as7262/demo.cpp b/examples/breakout_as7262/demo.cpp index 60d5b0e4..14287634 100644 --- a/examples/breakout_as7262/demo.cpp +++ b/examples/breakout_as7262/demo.cpp @@ -18,11 +18,12 @@ int main() { as7262.firmware_version(major, minor, sub); printf("Device: %d, HW: %d, FW: %d.%d.%d\n", dev_type, hw_version, major, minor, sub); - as7262.set_gain(AS7262::gain::X16); + as7262.set_gain(AS7262::gain::X64); + as7262.set_integration_time(17.857); as7262.set_measurement_mode(AS7262::measurement_mode::cont_roygbr); as7262.set_illumination_current(AS7262::illumination_current::ma12); as7262.set_indicator_current(AS7262::indicator_current::ma4); - //as7262.set_leds(false, false); + as7262.set_leds(true, true); while(true) { From a4b464df1260bd6aac093c28d6eb02958e73d62c Mon Sep 17 00:00:00 2001 From: Phil Howard Date: Wed, 12 May 2021 15:01:39 +0100 Subject: [PATCH 8/9] AS7262 add Pico Explorer bargraph example --- .../breakout_as7262/pico_explorer_graph.py | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 micropython/examples/breakout_as7262/pico_explorer_graph.py diff --git a/micropython/examples/breakout_as7262/pico_explorer_graph.py b/micropython/examples/breakout_as7262/pico_explorer_graph.py new file mode 100644 index 00000000..24f8e6f5 --- /dev/null +++ b/micropython/examples/breakout_as7262/pico_explorer_graph.py @@ -0,0 +1,65 @@ +from breakout_as7262 import BreakoutAS7262 +import picoexplorer as display +import time + +width = display.get_width() +height = display.get_height() + +bar_width = width // 6 +bar_height = height + +display_buffer = bytearray(width * height * 2) # 2-bytes per pixel (RGB565) +display.init(display_buffer) + +as7 = BreakoutAS7262() + +integration_time = 10 # integration time in milliseconds, max ~90ms + +as7.set_gain(as7.X64) +as7.set_integration_time(integration_time) +as7.set_measurement_mode(as7.CONT_ROYGBR) +as7.set_leds(True, True) + + +def draw_bar(v, i): + current_bar_top = int(bar_height - (bar_height * v)) + # Drawing outside of the display region will cause horrible, horrible crashes + current_bar_top = max(0, current_bar_top) + current_bar_height = bar_height - current_bar_top + display.rectangle(i * bar_width, current_bar_top, bar_width, current_bar_height - 1) + + +while True: + r, o, y, g, b, v = as7.read() + m = max(r, o, y, g, b, v) + + display.set_pen(0, 0, 0) + display.clear() + + # Red + display.set_pen(255, 0, 0) + draw_bar(r / m, 0) + + # Orange + display.set_pen(255, 128, 0) + draw_bar(o / m, 1) + + # Yellow + display.set_pen(255, 255, 0) + draw_bar(y / m, 2) + + # Green + display.set_pen(0, 255, 0) + draw_bar(g / m, 3) + + # Blue + display.set_pen(0, 0, 255) + draw_bar(b / m, 4) + + # Violet + display.set_pen(255, 0, 255) + draw_bar(v / m, 5) + + display.update() + + time.sleep(integration_time / 1000.0) From 2b70c1c0d7605337d32f705bd6dcb5bbee38441d Mon Sep 17 00:00:00 2001 From: Phil Howard Date: Wed, 12 May 2021 16:04:34 +0100 Subject: [PATCH 9/9] AS7262 Add C++ explorer example --- examples/breakout_as7262/CMakeLists.txt | 18 +--- examples/breakout_as7262/basic_demo.cmake | 16 +++ .../{demo.cpp => basic_demo.cpp} | 0 .../breakout_as7262/explorer_bargraph.cmake | 16 +++ .../breakout_as7262/explorer_bargraph.cpp | 98 +++++++++++++++++++ 5 files changed, 132 insertions(+), 16 deletions(-) create mode 100644 examples/breakout_as7262/basic_demo.cmake rename examples/breakout_as7262/{demo.cpp => basic_demo.cpp} (100%) create mode 100644 examples/breakout_as7262/explorer_bargraph.cmake create mode 100644 examples/breakout_as7262/explorer_bargraph.cpp diff --git a/examples/breakout_as7262/CMakeLists.txt b/examples/breakout_as7262/CMakeLists.txt index 12c54676..04334aac 100644 --- a/examples/breakout_as7262/CMakeLists.txt +++ b/examples/breakout_as7262/CMakeLists.txt @@ -1,16 +1,2 @@ -set(OUTPUT_NAME as7262_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_as7262) - -# create map/bin/hex file etc. -pico_add_extra_outputs(${OUTPUT_NAME}) +include("${CMAKE_CURRENT_LIST_DIR}/basic_demo.cmake") +include("${CMAKE_CURRENT_LIST_DIR}/explorer_bargraph.cmake") \ No newline at end of file diff --git a/examples/breakout_as7262/basic_demo.cmake b/examples/breakout_as7262/basic_demo.cmake new file mode 100644 index 00000000..a5526ec4 --- /dev/null +++ b/examples/breakout_as7262/basic_demo.cmake @@ -0,0 +1,16 @@ +set(OUTPUT_NAME as7262_basic_demo) + +add_executable( + ${OUTPUT_NAME} + basic_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_as7262) + +# create map/bin/hex file etc. +pico_add_extra_outputs(${OUTPUT_NAME}) diff --git a/examples/breakout_as7262/demo.cpp b/examples/breakout_as7262/basic_demo.cpp similarity index 100% rename from examples/breakout_as7262/demo.cpp rename to examples/breakout_as7262/basic_demo.cpp diff --git a/examples/breakout_as7262/explorer_bargraph.cmake b/examples/breakout_as7262/explorer_bargraph.cmake new file mode 100644 index 00000000..4813df7a --- /dev/null +++ b/examples/breakout_as7262/explorer_bargraph.cmake @@ -0,0 +1,16 @@ +set(OUTPUT_NAME as7262_explorer_bargraph) + +add_executable( + ${OUTPUT_NAME} + explorer_bargraph.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_as7262 pico_explorer) + +# create map/bin/hex file etc. +pico_add_extra_outputs(${OUTPUT_NAME}) diff --git a/examples/breakout_as7262/explorer_bargraph.cpp b/examples/breakout_as7262/explorer_bargraph.cpp new file mode 100644 index 00000000..0df4063b --- /dev/null +++ b/examples/breakout_as7262/explorer_bargraph.cpp @@ -0,0 +1,98 @@ +#include "pico/stdlib.h" + +#include "breakout_as7262.hpp" +#include "pico_explorer.hpp" + +using namespace pimoroni; + +constexpr float INTEGRATION_TIME = 10.0f; + +BreakoutAS7262 as7262; +uint16_t buffer[PicoExplorer::WIDTH * PicoExplorer::HEIGHT]; +PicoExplorer pico_explorer(buffer); + +uint8_t bar_width = PicoExplorer::WIDTH / 6; +uint8_t bar_height = PicoExplorer::HEIGHT; + +void draw_bar(float scale, uint16_t channel) { + int16_t bar_top = bar_height - (bar_height * scale); + bar_top = std::max((int16_t)0, bar_top); + int16_t current_bar_height = bar_height - bar_top; + pico_explorer.rectangle(Rect(channel * bar_width, bar_top, bar_width, current_bar_height - 1)); +} + +int main() { + stdio_init_all(); + + pico_explorer.init(); + as7262.init(); + + uint8_t dev_type = as7262.device_type(); + uint8_t hw_version = as7262.hardware_version(); + + uint8_t major, minor, sub; + as7262.firmware_version(major, minor, sub); + printf("Device: %d, HW: %d, FW: %d.%d.%d\n", dev_type, hw_version, major, minor, sub); + + as7262.set_gain(AS7262::gain::X64); + as7262.set_integration_time(INTEGRATION_TIME); + as7262.set_measurement_mode(AS7262::measurement_mode::cont_roygbr); + as7262.set_illumination_current(AS7262::illumination_current::ma12); + as7262.set_indicator_current(AS7262::indicator_current::ma4); + as7262.set_leds(true, true); + + while(true) { + pico_explorer.set_pen(0, 0, 0); + pico_explorer.clear(); + + AS7262::reading reading = as7262.read(); + printf("R: %f O: %f Y: %f G: %f B: %f V: %f \n", + reading.red, + reading.orange, + reading.yellow, + reading.green, + reading.blue, + reading.violet + ); + + float m = reading.red; + if(reading.orange > m) m = reading.orange; + if(reading.yellow > m) m = reading.yellow; + if(reading.green > m) m = reading.green; + if(reading.blue > m) m = reading.blue; + if(reading.violet > m) m = reading.violet; + + pico_explorer.set_pen(0, 0, 0); + pico_explorer.clear(); + + // Red + pico_explorer.set_pen(255, 0, 0); + draw_bar(reading.red / m, 0); + + // Orange + pico_explorer.set_pen(255, 128, 0); + draw_bar(reading.orange / m, 1); + + // Yellow + pico_explorer.set_pen(255, 255, 0); + draw_bar(reading.yellow / m, 2); + + // Green + pico_explorer.set_pen(0, 255, 0); + draw_bar(reading.green / m, 3); + + // Blue + pico_explorer.set_pen(0, 0, 255); + draw_bar(reading.blue / m, 4); + + // Violet + pico_explorer.set_pen(255, 0, 255); + draw_bar(reading.violet / m, 5); + + pico_explorer.update(); + + sleep_ms(INTEGRATION_TIME); + } + + return 0; +}