diff --git a/drivers/CMakeLists.txt b/drivers/CMakeLists.txt index fd0ddca6..ab9f9dcf 100644 --- a/drivers/CMakeLists.txt +++ b/drivers/CMakeLists.txt @@ -1,11 +1,11 @@ add_subdirectory(esp32spi) +add_subdirectory(ioexpander) add_subdirectory(ltp305) add_subdirectory(ltr559) add_subdirectory(sgp30) add_subdirectory(st7735) add_subdirectory(st7789) add_subdirectory(msa301) -add_subdirectory(nuvoton) add_subdirectory(rv3028) add_subdirectory(trackball) add_subdirectory(vl53l1x) diff --git a/drivers/ioexpander/CMakeLists.txt b/drivers/ioexpander/CMakeLists.txt new file mode 100644 index 00000000..57a5ff84 --- /dev/null +++ b/drivers/ioexpander/CMakeLists.txt @@ -0,0 +1 @@ +include(ioexpander.cmake) diff --git a/drivers/nuvoton/nuvoton.cmake b/drivers/ioexpander/ioexpander.cmake similarity index 91% rename from drivers/nuvoton/nuvoton.cmake rename to drivers/ioexpander/ioexpander.cmake index bcc5ab15..dbd7a67c 100644 --- a/drivers/nuvoton/nuvoton.cmake +++ b/drivers/ioexpander/ioexpander.cmake @@ -1,4 +1,4 @@ -set(DRIVER_NAME nuvoton) +set(DRIVER_NAME ioexpander) add_library(${DRIVER_NAME} INTERFACE) target_sources(${DRIVER_NAME} INTERFACE diff --git a/drivers/nuvoton/nuvoton.cpp b/drivers/ioexpander/ioexpander.cpp similarity index 80% rename from drivers/nuvoton/nuvoton.cpp rename to drivers/ioexpander/ioexpander.cpp index 59b3a5af..057cea57 100644 --- a/drivers/nuvoton/nuvoton.cpp +++ b/drivers/ioexpander/ioexpander.cpp @@ -3,7 +3,7 @@ #include #include -#include "nuvoton.hpp" +#include "ioexpander.hpp" namespace pimoroni { @@ -220,61 +220,61 @@ namespace pimoroni { static const uint8_t ENC_CFG[4] = {reg::ENC_1_CFG, reg::ENC_2_CFG, reg::ENC_3_CFG, reg::ENC_4_CFG}; static const uint8_t ENC_COUNT[4] = {reg::ENC_1_COUNT, reg::ENC_2_COUNT, reg::ENC_3_COUNT, reg::ENC_4_COUNT}; - const uint8_t Nuvoton::Pin::PxM1[4] = {reg::P0M1, reg::P1M1, (uint8_t)-1, reg::P3M1}; - const uint8_t Nuvoton::Pin::PxM2[4] = {reg::P0M2, reg::P1M2, (uint8_t)-1, reg::P3M2}; - const uint8_t Nuvoton::Pin::Px[4] = {reg::P0, reg::P1, (uint8_t)-1, reg::P3}; + const uint8_t IOExpander::Pin::PxM1[4] = {reg::P0M1, reg::P1M1, (uint8_t)-1, reg::P3M1}; + const uint8_t IOExpander::Pin::PxM2[4] = {reg::P0M2, reg::P1M2, (uint8_t)-1, reg::P3M2}; + const uint8_t IOExpander::Pin::Px[4] = {reg::P0, reg::P1, (uint8_t)-1, reg::P3}; - const uint8_t Nuvoton::Pin::PxS[4] = {reg::P0S, reg::P1S, (uint8_t)-1, reg::P3S}; - const uint8_t Nuvoton::Pin::MASK_P[4] = {reg::INT_MASK_P0, reg::INT_MASK_P1, (uint8_t)-1, reg::INT_MASK_P3}; + const uint8_t IOExpander::Pin::PxS[4] = {reg::P0S, reg::P1S, (uint8_t)-1, reg::P3S}; + const uint8_t IOExpander::Pin::MASK_P[4] = {reg::INT_MASK_P0, reg::INT_MASK_P1, (uint8_t)-1, reg::INT_MASK_P3}; - const uint8_t Nuvoton::Pin::PWML[6] = {reg::PWM0L, reg::PWM1L, reg::PWM2L, reg::PWM3L, reg::PWM4L, reg::PWM5L}; - const uint8_t Nuvoton::Pin::PWMH[6] = {reg::PWM0H, reg::PWM1H, reg::PWM2H, reg::PWM3H, reg::PWM4H, reg::PWM5H}; + const uint8_t IOExpander::Pin::PWML[6] = {reg::PWM0L, reg::PWM1L, reg::PWM2L, reg::PWM3L, reg::PWM4L, reg::PWM5L}; + const uint8_t IOExpander::Pin::PWMH[6] = {reg::PWM0H, reg::PWM1H, reg::PWM2H, reg::PWM3H, reg::PWM4H, reg::PWM5H}; static const char* MODE_NAMES[3] = {"IO", "PWM", "ADC"}; static const char* GPIO_NAMES[4] = {"QB", "PP", "IN", "OD"}; static const char* STATE_NAMES[2] = {"LOW", "HIGH"}; - Nuvoton::Pin::Pin(uint8_t port, uint8_t pin) : + IOExpander::Pin::Pin(uint8_t port, uint8_t pin) : type(TYPE_IO), mode(0), port(port), pin(pin), adc_channel(0), pwm_channel(0), reg_m1(PxM1[port]), reg_m2(PxM2[port]), reg_p(Px[port]), reg_ps(PxS[port]), reg_int_mask_p(MASK_P[port]), reg_io_pwm(0), reg_pwml(0), reg_pwmh(0) { } - Nuvoton::Pin::Pin(uint8_t port, uint8_t pin, uint8_t pwm_channel, uint8_t reg_io_pwm) : + IOExpander::Pin::Pin(uint8_t port, uint8_t pin, uint8_t pwm_channel, uint8_t reg_io_pwm) : type(TYPE_PWM), mode(0), port(port), pin(pin), adc_channel(0), pwm_channel(pwm_channel), reg_m1(PxM1[port]), reg_m2(PxM2[port]), reg_p(Px[port]), reg_ps(PxS[port]), reg_int_mask_p(MASK_P[port]), reg_io_pwm(reg_io_pwm), reg_pwml(PWML[pwm_channel]), reg_pwmh(PWMH[pwm_channel]) { } - Nuvoton::Pin::Pin(uint8_t port, uint8_t pin, uint8_t adc_channel) : + IOExpander::Pin::Pin(uint8_t port, uint8_t pin, uint8_t adc_channel) : type(TYPE_ADC), mode(0), port(port), pin(pin), adc_channel(adc_channel), pwm_channel(0), reg_m1(PxM1[port]), reg_m2(PxM2[port]), reg_p(Px[port]), reg_ps(PxS[port]), reg_int_mask_p(MASK_P[port]), reg_io_pwm(0), reg_pwml(0), reg_pwmh(0) { } - Nuvoton::Pin::Pin(uint8_t port, uint8_t pin, uint8_t adc_channel, uint8_t pwm_channel, uint8_t reg_io_pwm) : + IOExpander::Pin::Pin(uint8_t port, uint8_t pin, uint8_t adc_channel, uint8_t pwm_channel, uint8_t reg_io_pwm) : type(TYPE_ADC_OR_PWM), mode(0), port(port), pin(pin), adc_channel(adc_channel), pwm_channel(pwm_channel), reg_m1(PxM1[port]), reg_m2(PxM2[port]), reg_p(Px[port]), reg_ps(PxS[port]), reg_int_mask_p(MASK_P[port]), reg_io_pwm(reg_io_pwm), reg_pwml(PWML[pwm_channel]), reg_pwmh(PWMH[pwm_channel]) { } - Nuvoton::Pin Nuvoton::Pin::io(uint8_t port, uint8_t pin) { + IOExpander::Pin IOExpander::Pin::io(uint8_t port, uint8_t pin) { return Pin(port, pin); } - Nuvoton::Pin Nuvoton::Pin::pwm(uint8_t port, uint8_t pin, uint8_t channel, uint8_t reg_iopwm) { + IOExpander::Pin IOExpander::Pin::pwm(uint8_t port, uint8_t pin, uint8_t channel, uint8_t reg_iopwm) { return Pin(port, pin, channel, reg_iopwm); } - Nuvoton::Pin Nuvoton::Pin::adc(uint8_t port, uint8_t pin, uint8_t channel) { + IOExpander::Pin IOExpander::Pin::adc(uint8_t port, uint8_t pin, uint8_t channel) { return Pin(port, pin, channel); } - Nuvoton::Pin Nuvoton::Pin::adc_or_pwm(uint8_t port, uint8_t pin, uint8_t adc_channel, uint8_t pwm_channel, uint8_t reg_iopwm) { + IOExpander::Pin IOExpander::Pin::adc_or_pwm(uint8_t port, uint8_t pin, uint8_t adc_channel, uint8_t pwm_channel, uint8_t reg_iopwm) { return Pin(port, pin, adc_channel, pwm_channel, reg_iopwm); } - bool Nuvoton::Pin::mode_supported(uint8_t mode) { + bool IOExpander::Pin::mode_supported(uint8_t mode) { bool supported = false; if((type & TYPE_PWM) && (mode == PIN_MODE_PWM)) { supported = true; @@ -285,19 +285,19 @@ namespace pimoroni { return supported; } - Nuvoton::Pin::IOType Nuvoton::Pin::get_type() { + IOExpander::Pin::IOType IOExpander::Pin::get_type() { return type; } - uint8_t Nuvoton::Pin::get_mode() { + uint8_t IOExpander::Pin::get_mode() { return mode; } - void Nuvoton::Pin::set_mode(uint8_t mode) { + void IOExpander::Pin::set_mode(uint8_t mode) { this->mode = mode; } - Nuvoton::Nuvoton(i2c_inst_t *i2c, uint8_t sda, uint8_t scl, uint8_t interrupt, uint8_t address, uint32_t timeout, bool debug) : + IOExpander::IOExpander(i2c_inst_t *i2c, uint8_t sda, uint8_t scl, uint8_t interrupt, uint8_t address, uint32_t timeout, bool debug) : i2c(i2c), sda(sda), scl(scl), interrupt(interrupt), address(address), timeout(timeout), debug(debug), vref(3.3f), encoder_offset{0,0,0,0}, @@ -318,11 +318,11 @@ namespace pimoroni { Pin::adc(1, 7, 0)} { } - Nuvoton::Nuvoton(uint8_t address, uint32_t timeout, bool debug) : - Nuvoton(i2c0, DEFAULT_SDA_PIN, DEFAULT_SCL_PIN, DEFAULT_INT_PIN, address, timeout, debug) { + IOExpander::IOExpander(uint8_t address, uint32_t timeout, bool debug) : + IOExpander(i2c0, DEFAULT_SDA_PIN, DEFAULT_SCL_PIN, DEFAULT_INT_PIN, address, timeout, debug) { } - bool Nuvoton::init(bool skipChipIdCheck) { + bool IOExpander::init(bool skipChipIdCheck) { bool succeeded = true; i2c_init(i2c, 400000); @@ -348,11 +348,27 @@ namespace pimoroni { return succeeded; } - uint16_t Nuvoton::get_chip_id() { + i2c_inst_t* IOExpander::get_i2c() const { + return i2c; + } + + int IOExpander::get_sda() const { + return sda; + } + + int IOExpander::get_scl() const { + return scl; + } + + int IOExpander::get_int() const { + return interrupt; + } + + uint16_t IOExpander::get_chip_id() { return ((uint16_t)i2c_reg_read_uint8(reg::CHIP_ID_H) << 8) | (uint16_t)i2c_reg_read_uint8(reg::CHIP_ID_L); } - void Nuvoton::set_addr(uint8_t address) { + void IOExpander::set_addr(uint8_t address) { set_bit(reg::CTRL, 4); i2c_reg_write_uint8(reg::ADDR, address); this->address = address; @@ -361,35 +377,35 @@ namespace pimoroni { clr_bit(reg::CTRL, 4); } - float Nuvoton::get_adc_vref() { + float IOExpander::get_adc_vref() { return vref; } - void Nuvoton::set_adc_vref(float vref) { + void IOExpander::set_adc_vref(float vref) { this->vref = vref; } - void Nuvoton::enable_interrupt_out(bool pin_swap) { + void IOExpander::enable_interrupt_out(bool pin_swap) { set_bit(reg::INT, int_bit::OUT_EN); change_bit(reg::INT, int_bit::PIN_SWAP, pin_swap); } - void Nuvoton::disable_interrupt_out() { + void IOExpander::disable_interrupt_out() { clr_bit(reg::INT, int_bit::OUT_EN); } - uint8_t Nuvoton::get_interrupt_flag() { + uint8_t IOExpander::get_interrupt_flag() { if(interrupt != 0) return !gpio_get(interrupt); else return get_bit(reg::INT, int_bit::TRIGD); } - void Nuvoton::clear_interrupt_flag() { + void IOExpander::clear_interrupt_flag() { clr_bit(reg::INT, int_bit::TRIGD); } - bool Nuvoton::set_pin_interrupt(uint8_t pin, bool enabled) { + bool IOExpander::set_pin_interrupt(uint8_t pin, bool enabled) { bool succeeded = false; if(pin >= 1 && pin <= NUM_PINS) { Pin& io_pin = pins[pin - 1]; @@ -401,14 +417,14 @@ namespace pimoroni { return succeeded; } - void Nuvoton::set_interrupt_callback(void (*callback)()) { + void IOExpander::set_interrupt_callback(void (*callback)()) { if(interrupt != 0 && callback != nullptr) { //attachInterrupt(digitalPinToInterrupt(_interruptPin), callback, FALLING); clear_interrupt_flag(); } } - void Nuvoton::pwm_load(bool wait_for_load) { + void IOExpander::pwm_load(bool wait_for_load) { //Load new period and duty registers into buffer uint32_t start_time = millis(); set_bit(reg::PWMCON0, 6); //Set the "LOAD" bit of PWMCON0 @@ -425,11 +441,11 @@ namespace pimoroni { } } - bool Nuvoton::pwm_loading() { + bool IOExpander::pwm_loading() { return get_bit(reg::PWMCON0, 6); } - void Nuvoton::pwm_clear(bool wait_for_clear) { + void IOExpander::pwm_clear(bool wait_for_clear) { uint32_t start_time = millis(); set_bit(reg::PWMCON0, 4); //Set the "CLRPWM" bit of PWMCON0 if(wait_for_clear) { @@ -444,11 +460,11 @@ namespace pimoroni { } } - bool Nuvoton::pwm_clearing() { + bool IOExpander::pwm_clearing() { return get_bit(reg::PWMCON0, 4); } - bool Nuvoton::set_pwm_control(uint8_t divider) { + bool IOExpander::set_pwm_control(uint8_t divider) { bool divider_good = true; uint8_t pwmdiv2 = 0; switch(divider) { @@ -482,7 +498,7 @@ namespace pimoroni { return divider_good; } - void Nuvoton::set_pwm_period(uint16_t value, bool load) { + void IOExpander::set_pwm_period(uint16_t value, bool load) { value &= 0xffff; i2c_reg_write_uint8(reg::PWMPL, (uint8_t)(value & 0xff)); i2c_reg_write_uint8(reg::PWMPH, (uint8_t)(value >> 8)); @@ -491,11 +507,11 @@ namespace pimoroni { pwm_load(); } - uint8_t Nuvoton::get_mode(uint8_t pin) { + uint8_t IOExpander::get_mode(uint8_t pin) { return pins[pin - 1].get_mode(); } - void Nuvoton::set_mode(uint8_t pin, uint8_t mode, bool schmitt_trigger, bool invert) { + void IOExpander::set_mode(uint8_t pin, uint8_t mode, bool schmitt_trigger, bool invert) { if(pin < 1 || pin > NUM_PINS) { printf("ValueError: Pin should be in range 1-14.\n"); @@ -559,7 +575,7 @@ namespace pimoroni { i2c_reg_write_uint8(io_pin.reg_p, (initial_state << 3) | io_pin.pin); } - int16_t Nuvoton::input(uint8_t pin, uint32_t adc_timeout) { + int16_t IOExpander::input(uint8_t pin, uint32_t adc_timeout) { if(pin < 1 || pin > NUM_PINS) { if(debug) @@ -608,7 +624,7 @@ namespace pimoroni { } } - float Nuvoton::input_as_voltage(uint8_t pin, uint32_t adc_timeout) { + float IOExpander::input_as_voltage(uint8_t pin, uint32_t adc_timeout) { if(pin < 1 || pin > NUM_PINS) { if(debug) @@ -658,7 +674,7 @@ namespace pimoroni { } } - void Nuvoton::output(uint8_t pin, uint16_t value, bool load) { + void IOExpander::output(uint8_t pin, uint16_t value, bool load) { if(pin < 1 || pin > NUM_PINS) { printf("Pin should be in range 1-14."); return; @@ -677,14 +693,14 @@ namespace pimoroni { pwm_load(); } else { - if(value == 0) { + if(value == LOW) { if(debug) { printf("Outputting LOW to pin: %d\n", pin); } clr_bit(io_pin.reg_p, io_pin.pin); } - else if(value == 1) { + else if(value == HIGH) { if(debug) { printf("Outputting HIGH to pin: %d\n", pin); } @@ -694,7 +710,7 @@ namespace pimoroni { } } - void Nuvoton::setup_rotary_encoder(uint8_t channel, uint8_t pinA, uint8_t pinB, uint8_t pinC, bool count_microsteps) { + void IOExpander::setup_rotary_encoder(uint8_t channel, uint8_t pinA, uint8_t pinB, uint8_t pinC, bool count_microsteps) { channel -= 1; set_mode(pinA, PIN_MODE_PU, true); set_mode(pinB, PIN_MODE_PU, true); @@ -713,7 +729,7 @@ namespace pimoroni { i2c_reg_write_uint8(reg, 0x00); } - int16_t Nuvoton::read_rotary_encoder(uint8_t channel) { + int16_t IOExpander::read_rotary_encoder(uint8_t channel) { channel -= 1; int16_t last = encoder_last[channel]; uint8_t reg = ENC_COUNT[channel]; @@ -732,24 +748,24 @@ namespace pimoroni { return encoder_offset[channel] + value; } - uint8_t Nuvoton::i2c_reg_read_uint8(uint8_t reg) { + uint8_t IOExpander::i2c_reg_read_uint8(uint8_t reg) { uint8_t value; i2c_write_blocking(i2c, address, ®, 1, true); i2c_read_blocking(i2c, address, (uint8_t *)&value, 1, false); return value; } - void Nuvoton::i2c_reg_write_uint8(uint8_t reg, uint8_t value) { + void IOExpander::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 Nuvoton::get_bit(uint8_t reg, uint8_t bit) { + uint8_t IOExpander::get_bit(uint8_t reg, uint8_t bit) { //Returns the specified bit (nth position from right) from a register return i2c_reg_read_uint8(reg) & (1 << bit); } - void Nuvoton::set_bits(uint8_t reg, uint8_t bits) { + void IOExpander::set_bits(uint8_t reg, uint8_t bits) { //Set the specified bits (using a mask) in a register. //Deal with special case registers first @@ -773,12 +789,12 @@ namespace pimoroni { } } - void Nuvoton::set_bit(uint8_t reg, uint8_t bit) { + void IOExpander::set_bit(uint8_t reg, uint8_t bit) { //Set the specified bit (nth position from right) in a register. set_bits(reg, (1 << bit)); } - void Nuvoton::clr_bits(uint8_t reg, uint8_t bits) { + void IOExpander::clr_bits(uint8_t reg, uint8_t bits) { bool reg_in_bit_addressed_regs = false; for(uint8_t i = 0; i < NUM_BIT_ADDRESSED_REGISTERS; i++) { if(BIT_ADDRESSED_REGS[i] == reg) { @@ -799,12 +815,12 @@ namespace pimoroni { } } - void Nuvoton::clr_bit(uint8_t reg, uint8_t bit) { + void IOExpander::clr_bit(uint8_t reg, uint8_t bit) { //Clear the specified bit (nth position from right) in a register. clr_bits(reg, (1 << bit)); } - void Nuvoton::change_bit(uint8_t reg, uint8_t bit, bool state) { + void IOExpander::change_bit(uint8_t reg, uint8_t bit, bool state) { //Toggle one register bit on/off. if(state) set_bit(reg, bit); @@ -812,7 +828,7 @@ namespace pimoroni { clr_bit(reg, bit); } - void Nuvoton::wait_for_flash(void) { + void IOExpander::wait_for_flash(void) { //Wait for the IOE to finish writing non-volatile memory. unsigned long start_time = millis(); while(get_interrupt_flag()) { @@ -833,7 +849,7 @@ namespace pimoroni { } } - uint32_t Nuvoton::millis() { + uint32_t IOExpander::millis() { return to_ms_since_boot(get_absolute_time()); } } \ No newline at end of file diff --git a/drivers/nuvoton/nuvoton.hpp b/drivers/ioexpander/ioexpander.hpp similarity index 56% rename from drivers/nuvoton/nuvoton.hpp rename to drivers/ioexpander/ioexpander.hpp index a295ffa6..31e1f017 100644 --- a/drivers/nuvoton/nuvoton.hpp +++ b/drivers/ioexpander/ioexpander.hpp @@ -5,24 +5,24 @@ namespace pimoroni { - class Nuvoton { + class IOExpander { //-------------------------------------------------- // Constants //-------------------------------------------------- private: - //These values encode our desired pin function: IO, ADC, PWM - //alongwide the GPIO MODE for that port and pin (section 8.1) - //1st and 2nd bits encode the gpio state - //3rd and 4th bits encode the IO mode (i.e. IO, PWM, ADC) - //the 5th bit additionally encodes the default output state - static const uint8_t PIN_MODE_IO = 0b00000; //General IO mode, IE: not ADC or PWM - static const uint8_t PIN_MODE_QB = 0b00000; //Output, Quasi-Bidirectional mode - static const uint8_t PIN_MODE_PP = 0b00001; //Output, Push-Pull mode - static const uint8_t PIN_MODE_IN = 0b00010; //Input-only (high-impedance) - static const uint8_t PIN_MODE_PU = 0b10000; //Input (with pull-up) - static const uint8_t PIN_MODE_OD = 0b00011; //Output, Open-Drain mode - static const uint8_t PIN_MODE_PWM = 0b00101; //PWM, Output, Push-Pull mode - static const uint8_t PIN_MODE_ADC = 0b01010; //ADC, Input-only (high-impedance) + // These values encode our desired pin function: IO, ADC, PWM + // alongwide the GPIO MODE for that port and pin (section 8.1) + // 1st and 2nd bits encode the gpio state + // 3rd and 4th bits encode the IO mode (i.e. IO, PWM, ADC) + // the 5th bit additionally encodes the default output state + static const uint8_t PIN_MODE_IO = 0b00000; // General IO mode, IE: not ADC or PWM + static const uint8_t PIN_MODE_QB = 0b00000; // Output, Quasi-Bidirectional mode + static const uint8_t PIN_MODE_PP = 0b00001; // Output, Push-Pull mode + static const uint8_t PIN_MODE_IN = 0b00010; // Input-only (high-impedance) + static const uint8_t PIN_MODE_PU = 0b10000; // Input (with pull-up) + static const uint8_t PIN_MODE_OD = 0b00011; // Output, Open-Drain mode + static const uint8_t PIN_MODE_PWM = 0b00101; // PWM, Output, Push-Pull mode + static const uint8_t PIN_MODE_ADC = 0b01010; // ADC, Input-only (high-impedance) public: static const uint8_t DEFAULT_I2C_ADDRESS = 0x18; @@ -33,15 +33,18 @@ namespace pimoroni { static const uint16_t CHIP_ID = 0xE26A; static const uint8_t CHIP_VERSION = 2; - static const uint8_t PIN_IN = PIN_MODE_IN; //0b00010 - static const uint8_t PIN_IN_PULL_UP = PIN_MODE_PU; //0b10000 - static const uint8_t PIN_IN_PU = PIN_MODE_PU; //0b10000 - static const uint8_t PIN_OUT = PIN_MODE_PP; //0b00001 - static const uint8_t PIN_PWM = PIN_MODE_PWM; //0b00101 - static const uint8_t PIN_ADC = PIN_MODE_ADC; //0b01010 + static const uint8_t PIN_IN = PIN_MODE_IN; // 0b00010 + static const uint8_t PIN_IN_PULL_UP = PIN_MODE_PU; // 0b10000 + static const uint8_t PIN_IN_PU = PIN_MODE_PU; // 0b10000 + static const uint8_t PIN_OUT = PIN_MODE_PP; // 0b00001 + static const uint8_t PIN_PWM = PIN_MODE_PWM; // 0b00101 + static const uint8_t PIN_ADC = PIN_MODE_ADC; // 0b01010 static const uint8_t NUM_PINS = 14; + static const uint16_t LOW = 0; + static const uint16_t HIGH = 1; + //-------------------------------------------------- // Subclasses @@ -58,43 +61,43 @@ namespace pimoroni { TYPE_ADC = 0b10, TYPE_ADC_OR_PWM = 0b11 }; - + //-------------------------------------------------- // Constants //-------------------------------------------------- private: - //The PxM1 and PxM2 registers encode GPIO MODE - //0 0 = Quasi-bidirectional - //0 1 = Push-pull - //1 0 = Input-only (high-impedance) - //1 1 = Open-drain - static const uint8_t PxM1[4]; //[reg::P0M1, reg::P1M1, -1, reg::P3M1] - static const uint8_t PxM2[4]; //[reg::P0M2, reg::P1M2, -1, reg::P3M2] + // The PxM1 and PxM2 registers encode GPIO MODE + // 0 0 = Quasi-bidirectional + // 0 1 = Push-pull + // 1 0 = Input-only (high-impedance) + // 1 1 = Open-drain + static const uint8_t PxM1[4]; // [reg::P0M1, reg::P1M1, -1, reg::P3M1] + static const uint8_t PxM2[4]; // [reg::P0M2, reg::P1M2, -1, reg::P3M2] - //The Px input register - static const uint8_t Px[4]; //[reg::P0, reg::P1, -1, reg::P3] + // The Px input register + static const uint8_t Px[4]; // [reg::P0, reg::P1, -1, reg::P3] - //The PxS Schmitt trigger register - static const uint8_t PxS[4]; //[reg::P0S, reg::P1S, -1, reg::P3S] - static const uint8_t MASK_P[4]; //[reg::INT_MASK_P0, reg::INT_MASK_P1, -1, reg::INT_MASK_P3] + // The PxS Schmitt trigger register + static const uint8_t PxS[4]; // [reg::P0S, reg::P1S, -1, reg::P3S] + static const uint8_t MASK_P[4]; // [reg::INT_MASK_P0, reg::INT_MASK_P1, -1, reg::INT_MASK_P3] - static const uint8_t PWML[6]; //[reg::PWM0L, reg::PWM1L, reg::PWM2L, reg::PWM3L, reg::PWM4L, reg::PWM5L] - static const uint8_t PWMH[6]; //[reg::PWM0H, reg::PWM1H, reg::PWM2H, reg::PWM3H, reg::PWM4H, reg::PWM5H] + static const uint8_t PWML[6]; // [reg::PWM0L, reg::PWM1L, reg::PWM2L, reg::PWM3L, reg::PWM4L, reg::PWM5L] + static const uint8_t PWMH[6]; // [reg::PWM0H, reg::PWM1H, reg::PWM2H, reg::PWM3H, reg::PWM4H, reg::PWM5H] //-------------------------------------------------- // Variables //-------------------------------------------------- private: - const IOType type; + const IOType type; uint8_t mode; public: const uint8_t port; const uint8_t pin; const uint8_t adc_channel; const uint8_t pwm_channel; - + const uint8_t reg_m1; const uint8_t reg_m2; const uint8_t reg_p; @@ -110,24 +113,22 @@ namespace pimoroni { // Constructors //-------------------------------------------------- private: - Pin(uint8_t port, uint8_t pin); //Constructor for IO pin - Pin(uint8_t port, uint8_t pin, uint8_t pwm_channel, uint8_t reg_iopwm); //Constructor for PWM pin - Pin(uint8_t port, uint8_t pin, uint8_t adc_channel); //Constructor for ADC pin - Pin(uint8_t port, uint8_t pin, uint8_t adc_channel, uint8_t pwm_channel, uint8_t reg_iopwm); //Constructor for ADC or PWM pin + Pin(uint8_t port, uint8_t pin); // Constructor for IO pin + Pin(uint8_t port, uint8_t pin, uint8_t pwm_channel, uint8_t reg_iopwm); // Constructor for PWM pin + Pin(uint8_t port, uint8_t pin, uint8_t adc_channel); // Constructor for ADC pin + Pin(uint8_t port, uint8_t pin, uint8_t adc_channel, uint8_t pwm_channel, uint8_t reg_iopwm); // Constructor for ADC or PWM pin //-------------------------------------------------- // Methods //-------------------------------------------------- public: - static Pin io(uint8_t port, uint8_t pin); //Nicer function for creating an IO pin - static Pin pwm(uint8_t port, uint8_t pin, uint8_t channel, uint8_t reg_iopwm); //Nicer function for creating a PWM pin - static Pin adc(uint8_t port, uint8_t pin, uint8_t channel); //Nicer function for creating an ADC pin - static Pin adc_or_pwm(uint8_t port, uint8_t pin, uint8_t adc_channel, uint8_t pwm_channel, uint8_t reg_iopwm); //Nicer function for creating an ADC or PWM pin + static Pin io(uint8_t port, uint8_t pin); // Nicer function for creating an IO pin + static Pin pwm(uint8_t port, uint8_t pin, uint8_t channel, uint8_t reg_iopwm); // Nicer function for creating a PWM pin + static Pin adc(uint8_t port, uint8_t pin, uint8_t channel); // Nicer function for creating an ADC pin + static Pin adc_or_pwm(uint8_t port, uint8_t pin, uint8_t adc_channel, uint8_t pwm_channel, uint8_t reg_iopwm); // Nicer function for creating an ADC or PWM pin - //-------------------------------------------------- - - IOType get_type(void); + IOType get_type(void); uint8_t get_mode(void); void set_mode(uint8_t mode); @@ -149,7 +150,7 @@ namespace pimoroni { uint32_t timeout; bool debug; - float vref; + float vref; int16_t encoder_offset[4];; int16_t encoder_last[4]; Pin pins[NUM_PINS]; @@ -159,8 +160,8 @@ namespace pimoroni { // Constructors/Destructor //-------------------------------------------------- public: - Nuvoton(i2c_inst_t *i2c, uint8_t sda, uint8_t scl, uint8_t interrupt, uint8_t address = DEFAULT_I2C_ADDRESS, uint32_t timeout = 1, bool debug = false); - Nuvoton(uint8_t address = DEFAULT_I2C_ADDRESS, uint32_t timeout = 1, bool debug = false); + IOExpander(i2c_inst_t *i2c, uint8_t sda, uint8_t scl, uint8_t interrupt, uint8_t address = DEFAULT_I2C_ADDRESS, uint32_t timeout = 1, bool debug = false); + IOExpander(uint8_t address = DEFAULT_I2C_ADDRESS, uint32_t timeout = 1, bool debug = false); //-------------------------------------------------- @@ -169,53 +170,44 @@ namespace pimoroni { public: bool init(bool skip_chip_id_check = false); - //-------------------------------------------------- + // For print access in micropython + i2c_inst_t* get_i2c() const; + int get_sda() const; + int get_scl() const; + int get_int() const; uint16_t get_chip_id(); - + void set_addr(uint8_t address); float get_adc_vref(); void set_adc_vref(float vref); - - //-------------------------------------------------- - + void enable_interrupt_out(bool pin_swap = false); void disable_interrupt_out(); - uint8_t get_interrupt_flag(); void clear_interrupt_flag(); - bool set_pin_interrupt(uint8_t pin, bool enabled); void set_interrupt_callback(void (*callback)()); - //-------------------------------------------------- - void pwm_load(bool wait_for_load = true); bool pwm_loading(); - void pwm_clear(bool wait_for_clear = true); bool pwm_clearing(); - bool set_pwm_control(uint8_t divider); void set_pwm_period(uint16_t value, bool load = true); - //-------------------------------------------------- - uint8_t get_mode(uint8_t pin); void set_mode(uint8_t pin, uint8_t mode, bool schmitt_trigger = false, bool invert = false); int16_t input(uint8_t pin, uint32_t adc_timeout = 1); float input_as_voltage(uint8_t pin, uint32_t adc_timeout = 1); - + void output(uint8_t pin, uint16_t value, bool load = true); - //-------------------------------------------------- - void setup_rotary_encoder(uint8_t channel, uint8_t pinA, uint8_t pinB, uint8_t pinC = 0, bool count_microsteps = false); int16_t read_rotary_encoder(uint8_t channel); - //-------------------------------------------------- private: uint8_t i2c_reg_read_uint8(uint8_t reg); void i2c_reg_write_uint8(uint8_t reg, uint8_t value); diff --git a/drivers/nuvoton/CMakeLists.txt b/drivers/nuvoton/CMakeLists.txt deleted file mode 100644 index 395cdc9a..00000000 --- a/drivers/nuvoton/CMakeLists.txt +++ /dev/null @@ -1 +0,0 @@ -include(nuvoton.cmake) diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 3c6a2ea8..2b11e0ec 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -1,9 +1,12 @@ add_subdirectory(breakout_dotmatrix) +add_subdirectory(breakout_encoder) add_subdirectory(breakout_ltr559) add_subdirectory(breakout_colourlcd160x80) add_subdirectory(breakout_roundlcd) add_subdirectory(breakout_rgbmatrix5x5) add_subdirectory(breakout_matrix11x7) +add_subdirectory(breakout_mics6814) +add_subdirectory(breakout_potentiometer) add_subdirectory(breakout_trackball) add_subdirectory(breakout_sgp30) add_subdirectory(breakout_colourlcd240x240) diff --git a/examples/breakout_encoder/CMakeLists.txt b/examples/breakout_encoder/CMakeLists.txt new file mode 100644 index 00000000..46729be4 --- /dev/null +++ b/examples/breakout_encoder/CMakeLists.txt @@ -0,0 +1,12 @@ +set(OUTPUT_NAME encoder_demo) + +add_executable( + ${OUTPUT_NAME} + demo.cpp +) + +# Pull in pico libraries that we need +target_link_libraries(${OUTPUT_NAME} pico_stdlib breakout_encoder) + +# create map/bin/hex file etc. +pico_add_extra_outputs(${OUTPUT_NAME}) diff --git a/examples/breakout_encoder/demo.cpp b/examples/breakout_encoder/demo.cpp new file mode 100644 index 00000000..b8e0665d --- /dev/null +++ b/examples/breakout_encoder/demo.cpp @@ -0,0 +1,23 @@ +#include "pico/stdlib.h" + +#include "breakout_encoder.hpp" + +using namespace pimoroni; + +BreakoutEncoder enc; + +int main() { + gpio_init(PICO_DEFAULT_LED_PIN); + gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_OUT); + + enc.init(); + + while(true) { + gpio_put(PICO_DEFAULT_LED_PIN, true); + sleep_ms(1000); + gpio_put(PICO_DEFAULT_LED_PIN, false); + sleep_ms(1000); + } + + return 0; +} diff --git a/examples/breakout_ioexpander/CMakeLists.txt b/examples/breakout_ioexpander/CMakeLists.txt new file mode 100644 index 00000000..eeda7d77 --- /dev/null +++ b/examples/breakout_ioexpander/CMakeLists.txt @@ -0,0 +1,12 @@ +set(OUTPUT_NAME ioexpander_demo) + +add_executable( + ${OUTPUT_NAME} + demo.cpp +) + +# Pull in pico libraries that we need +target_link_libraries(${OUTPUT_NAME} pico_stdlib breakout_ioexpander) + +# create map/bin/hex file etc. +pico_add_extra_outputs(${OUTPUT_NAME}) diff --git a/examples/breakout_ioexpander/demo.cpp b/examples/breakout_ioexpander/demo.cpp new file mode 100644 index 00000000..d6477bf0 --- /dev/null +++ b/examples/breakout_ioexpander/demo.cpp @@ -0,0 +1,23 @@ +#include "pico/stdlib.h" + +#include "breakout_ioexpander.hpp" + +using namespace pimoroni; + +BreakoutIOExpander ioe; + +int main() { + gpio_init(PICO_DEFAULT_LED_PIN); + gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_OUT); + + ioe.init(); + + while(true) { + gpio_put(PICO_DEFAULT_LED_PIN, true); + sleep_ms(1000); + gpio_put(PICO_DEFAULT_LED_PIN, false); + sleep_ms(1000); + } + + return 0; +} diff --git a/examples/breakout_mics6814/CMakeLists.txt b/examples/breakout_mics6814/CMakeLists.txt new file mode 100644 index 00000000..e8e3c20c --- /dev/null +++ b/examples/breakout_mics6814/CMakeLists.txt @@ -0,0 +1,12 @@ +set(OUTPUT_NAME mics6814_demo) + +add_executable( + ${OUTPUT_NAME} + demo.cpp +) + +# Pull in pico libraries that we need +target_link_libraries(${OUTPUT_NAME} pico_stdlib breakout_mics6814) + +# create map/bin/hex file etc. +pico_add_extra_outputs(${OUTPUT_NAME}) diff --git a/examples/breakout_mics6814/demo.cpp b/examples/breakout_mics6814/demo.cpp new file mode 100644 index 00000000..6d4d0c88 --- /dev/null +++ b/examples/breakout_mics6814/demo.cpp @@ -0,0 +1,23 @@ +#include "pico/stdlib.h" + +#include "breakout_mics6814.hpp" + +using namespace pimoroni; + +BreakoutMICS6814 ioe; + +int main() { + gpio_init(PICO_DEFAULT_LED_PIN); + gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_OUT); + + ioe.init(); + + while(true) { + gpio_put(PICO_DEFAULT_LED_PIN, true); + sleep_ms(1000); + gpio_put(PICO_DEFAULT_LED_PIN, false); + sleep_ms(1000); + } + + return 0; +} diff --git a/examples/breakout_potentiometer/CMakeLists.txt b/examples/breakout_potentiometer/CMakeLists.txt new file mode 100644 index 00000000..edbf0709 --- /dev/null +++ b/examples/breakout_potentiometer/CMakeLists.txt @@ -0,0 +1,12 @@ +set(OUTPUT_NAME potentiometer_demo) + +add_executable( + ${OUTPUT_NAME} + demo.cpp +) + +# Pull in pico libraries that we need +target_link_libraries(${OUTPUT_NAME} pico_stdlib breakout_potentiometer) + +# create map/bin/hex file etc. +pico_add_extra_outputs(${OUTPUT_NAME}) diff --git a/examples/breakout_potentiometer/demo.cpp b/examples/breakout_potentiometer/demo.cpp new file mode 100644 index 00000000..a921bcb1 --- /dev/null +++ b/examples/breakout_potentiometer/demo.cpp @@ -0,0 +1,23 @@ +#include "pico/stdlib.h" + +#include "breakout_potentiometer.hpp" + +using namespace pimoroni; + +BreakoutPotentiometer pot; + +int main() { + gpio_init(PICO_DEFAULT_LED_PIN); + gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_OUT); + + pot.init(); + + while(true) { + gpio_put(PICO_DEFAULT_LED_PIN, true); + sleep_ms(1000); + gpio_put(PICO_DEFAULT_LED_PIN, false); + sleep_ms(1000); + } + + return 0; +} diff --git a/libraries/breakout_encoder/breakout_encoder.cmake b/libraries/breakout_encoder/breakout_encoder.cmake index d3a8f311..765e58ab 100644 --- a/libraries/breakout_encoder/breakout_encoder.cmake +++ b/libraries/breakout_encoder/breakout_encoder.cmake @@ -8,4 +8,4 @@ target_sources(${LIB_NAME} INTERFACE 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 nuvoton) +target_link_libraries(${LIB_NAME} INTERFACE pico_stdlib ioexpander) diff --git a/libraries/breakout_encoder/breakout_encoder.cpp b/libraries/breakout_encoder/breakout_encoder.cpp index 1536bba0..08bc0959 100644 --- a/libraries/breakout_encoder/breakout_encoder.cpp +++ b/libraries/breakout_encoder/breakout_encoder.cpp @@ -1,5 +1,86 @@ #include "breakout_encoder.hpp" +#include namespace pimoroni { -} + bool BreakoutEncoder::init(bool skip_chip_id_check) { + bool success = false; + if(ioe.init(skip_chip_id_check)) { + + if(interrupt_pin != PIN_UNUSED) + ioe.enable_interrupt_out(true); + + ioe.setup_rotary_encoder(ENC_CHANNEL, ENC_TERM_A, ENC_TERM_B, ENC_TERM_C); + + // Calculate a period large enough to get 0-255 steps at the desired brightness + uint16_t period = (uint16_t)(255.0f / brightness); + + ioe.set_pwm_period(period); + ioe.set_pwm_control(2); // PWM as fast as we can to avoid LED flicker + + ioe.set_mode(LED_R, IOExpander::PIN_PWM, false, INVERT_OUTPUT); + ioe.set_mode(LED_G, IOExpander::PIN_PWM, false, INVERT_OUTPUT); + ioe.set_mode(LED_B, IOExpander::PIN_PWM, false, INVERT_OUTPUT); + + success = true; + } + + return success; + } + + i2c_inst_t* BreakoutEncoder::get_i2c() const { + return ioe.get_i2c(); + } + + int BreakoutEncoder::get_sda() const { + return ioe.get_sda(); + } + + int BreakoutEncoder::get_scl() const { + return ioe.get_scl(); + } + + int BreakoutEncoder::get_int() const { + return ioe.get_int(); + } + + void BreakoutEncoder::set_addr(uint8_t i2c_addr) { + ioe.set_addr(i2c_addr); + } + + bool BreakoutEncoder::get_direction(void) { + return direction_cw; + } + + void BreakoutEncoder::set_direction(bool clockwise) { + direction_cw = clockwise; + } + + void BreakoutEncoder::set_brightness(float brightness) { + this->brightness = std::min(std::max(brightness, 0.01f), 1.0f); + + // Calculate a period large enough to get 0-255 steps at the desired brightness + uint16_t period = (uint16_t)(255.0f / this->brightness); + + ioe.set_pwm_period(period); + } + + void BreakoutEncoder::set_led(uint8_t r, uint8_t g, uint8_t b) { + ioe.output(LED_R, r, false); // Hold off pwm load until the last + ioe.output(LED_G, g, false); // Hold off pwm load until the last + ioe.output(LED_B, b); // Loads all 3 pwms + } + + bool BreakoutEncoder::available() { + return (ioe.get_interrupt_flag() > 0); + } + + int16_t BreakoutEncoder::read() { + int16_t count = ioe.read_rotary_encoder(ENC_CHANNEL); + if(!direction_cw) + count = 0 - count; + + ioe.clear_interrupt_flag(); + return count; + } +} \ No newline at end of file diff --git a/libraries/breakout_encoder/breakout_encoder.hpp b/libraries/breakout_encoder/breakout_encoder.hpp index 65af31bc..22a3b391 100644 --- a/libraries/breakout_encoder/breakout_encoder.hpp +++ b/libraries/breakout_encoder/breakout_encoder.hpp @@ -1,8 +1,93 @@ #pragma once -#include "../../drivers/nuvoton/nuvoton.hpp" +#include "../../drivers/ioexpander/ioexpander.hpp" namespace pimoroni { - typedef Nuvoton BreakoutEncoder; -} + class BreakoutEncoder { + //-------------------------------------------------- + // Enums + //-------------------------------------------------- + public: + enum Direction : bool { + DIRECTION_CW = true, + DIRECTION_CCW = false + }; + + + //-------------------------------------------------- + // Constants + //-------------------------------------------------- + public: + static const uint8_t DEFAULT_I2C_ADDRESS = 0x0F; + static constexpr float DEFAULT_BRIGHTNESS = 1.0f; //Effectively the maximum fraction of the period that the LED will be on + static const bool DEFAULT_DIRECTION = DIRECTION_CW; + static const uint8_t PIN_UNUSED = UINT8_MAX; + static const uint32_t DEFAULT_TIMEOUT = 1; + + private: + static const uint8_t LED_R = 1; + static const uint8_t LED_G = 7; + static const uint8_t LED_B = 2; + + static const uint8_t ENC_TERM_A = 12; + static const uint8_t ENC_TERM_B = 3; + static const uint8_t ENC_TERM_C = 11; + + static const uint8_t ENC_CHANNEL = 1; + + static const bool INVERT_OUTPUT = true; //true for common cathode, false for common anode + + + //-------------------------------------------------- + // Variables + //-------------------------------------------------- + private: + IOExpander ioe; + bool direction_cw = DEFAULT_DIRECTION; + float brightness = DEFAULT_BRIGHTNESS; + uint8_t interrupt_pin = PIN_UNUSED; // A local copy of the value passed to the IOExpander, used in initialisation + + + //-------------------------------------------------- + // Constructors/Destructor + //-------------------------------------------------- + public: + BreakoutEncoder() : + ioe(DEFAULT_I2C_ADDRESS) {} + + BreakoutEncoder(uint8_t address) : + ioe(address) {} + + BreakoutEncoder(i2c_inst_t *i2c, uint8_t address, uint8_t sda, uint8_t scl, uint8_t interrupt = PIN_UNUSED, uint32_t timeout = DEFAULT_TIMEOUT) : + ioe(i2c, address, sda, scl, interrupt, timeout), + interrupt_pin(interrupt) {} + + + //-------------------------------------------------- + // Methods + //-------------------------------------------------- + public: + bool init(bool skip_chip_id_check = false); + + // For print access in micropython + i2c_inst_t* get_i2c() const; + int get_sda() const; + int get_scl() const; + int get_int() const; + + // Calls through to IOExpander class + void set_addr(uint8_t i2c_addr); + + // Encoder breakout specific + bool get_direction(); + void set_direction(bool clockwise); + + void set_brightness(float brightness); + void set_led(uint8_t r, uint8_t g, uint8_t b); + + bool available(); + int16_t read(); + }; + +} \ No newline at end of file diff --git a/libraries/breakout_ioexpander/breakout_ioexpander.cmake b/libraries/breakout_ioexpander/breakout_ioexpander.cmake index 8ee2bb90..d2a20c35 100644 --- a/libraries/breakout_ioexpander/breakout_ioexpander.cmake +++ b/libraries/breakout_ioexpander/breakout_ioexpander.cmake @@ -8,4 +8,4 @@ target_sources(${LIB_NAME} INTERFACE 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 nuvoton) +target_link_libraries(${LIB_NAME} INTERFACE pico_stdlib ioexpander) diff --git a/libraries/breakout_ioexpander/breakout_ioexpander.hpp b/libraries/breakout_ioexpander/breakout_ioexpander.hpp index 09bf993b..e6ebd0e5 100644 --- a/libraries/breakout_ioexpander/breakout_ioexpander.hpp +++ b/libraries/breakout_ioexpander/breakout_ioexpander.hpp @@ -1,8 +1,8 @@ #pragma once -#include "../../drivers/nuvoton/nuvoton.hpp" +#include "../../drivers/ioexpander/ioexpander.hpp" namespace pimoroni { - typedef Nuvoton BreakoutIOExpander; + typedef IOExpander BreakoutIOExpander; } diff --git a/libraries/breakout_mics6814/breakout_mics6814.cmake b/libraries/breakout_mics6814/breakout_mics6814.cmake index e977a979..31b28c2e 100644 --- a/libraries/breakout_mics6814/breakout_mics6814.cmake +++ b/libraries/breakout_mics6814/breakout_mics6814.cmake @@ -8,4 +8,4 @@ target_sources(${LIB_NAME} INTERFACE 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 nuvoton) +target_link_libraries(${LIB_NAME} INTERFACE pico_stdlib ioexpander) diff --git a/libraries/breakout_mics6814/breakout_mics6814.cpp b/libraries/breakout_mics6814/breakout_mics6814.cpp index 64a37ebb..c801827a 100644 --- a/libraries/breakout_mics6814/breakout_mics6814.cpp +++ b/libraries/breakout_mics6814/breakout_mics6814.cpp @@ -1,5 +1,154 @@ #include "breakout_mics6814.hpp" +#include namespace pimoroni { -} + bool BreakoutMICS6814::init(bool skip_chip_id_check) { + bool success = false; + if(ioe.init(skip_chip_id_check)) { + + ioe.set_mode(MICS_VREF, IOExpander::PIN_ADC); + ioe.set_mode(MICS_RED, IOExpander::PIN_ADC); + ioe.set_mode(MICS_NH3, IOExpander::PIN_ADC); + ioe.set_mode(MICS_OX, IOExpander::PIN_ADC); + + ioe.set_mode(MICS_HEATER_EN, IOExpander::PIN_OUT); + ioe.output(MICS_HEATER_EN, IOExpander::LOW); + + // Calculate a period large enough to get 0-255 steps at the desired brightness + uint16_t period = (uint16_t)(255.0f / brightness); + + ioe.set_pwm_period(period); + ioe.set_pwm_control(2); // PWM as fast as we can to avoid LED flicker + + ioe.set_mode(LED_R, IOExpander::PIN_PWM, false, INVERT_OUTPUT); + ioe.set_mode(LED_G, IOExpander::PIN_PWM, false, INVERT_OUTPUT); + ioe.set_mode(LED_B, IOExpander::PIN_PWM, false, INVERT_OUTPUT); + + success = true; + } + + return success; + } + + i2c_inst_t* BreakoutMICS6814::get_i2c() const { + return ioe.get_i2c(); + } + + int BreakoutMICS6814::get_sda() const { + return ioe.get_sda(); + } + + int BreakoutMICS6814::get_scl() const { + return ioe.get_scl(); + } + + int BreakoutMICS6814::get_int() const { + return ioe.get_int(); + } + + void BreakoutMICS6814::set_addr(uint8_t i2c_addr) { + ioe.set_addr(i2c_addr); + } + + float BreakoutMICS6814::get_adc_vref(void) { + return ioe.get_adc_vref(); + } + + void BreakoutMICS6814::set_adc_vref(float vref) { + ioe.set_adc_vref(vref); + } + + void BreakoutMICS6814::set_brightness(float brightness) { + this->brightness = std::min(std::max(brightness, 0.01f), 1.0f); + + // Calculate a period large enough to get 0-255 steps at the desired brightness + uint16_t period = (uint16_t)(255.0f / this->brightness); + + ioe.set_pwm_period(period); + } + + void BreakoutMICS6814::set_led(uint8_t r, uint8_t g, uint8_t b) { + ioe.output(LED_R, r, false); // Hold off pwm load until the last + ioe.output(LED_G, g, false); // Hold off pwm load until the last + ioe.output(LED_B, b); // Loads all 3 pwms + } + + void BreakoutMICS6814::set_heater(bool on) { + ioe.output(MICS_HEATER_EN, on ? IOExpander::LOW : IOExpander::HIGH); + } + + void BreakoutMICS6814::disable_heater() { + ioe.output(MICS_HEATER_EN, IOExpander::HIGH); + ioe.set_mode(MICS_HEATER_EN, IOExpander::PIN_IN); + } + + float BreakoutMICS6814::get_raw_ref(uint32_t adc_timeout) { + return ioe.input_as_voltage(MICS_VREF, adc_timeout); + } + + float BreakoutMICS6814::get_raw_red(uint32_t adc_timeout) { + return ioe.input_as_voltage(MICS_RED, adc_timeout); + } + + float BreakoutMICS6814::get_raw_nh3(uint32_t adc_timeout) { + return ioe.input_as_voltage(MICS_NH3, adc_timeout); + } + + float BreakoutMICS6814::get_raw_oxd(uint32_t adc_timeout) { + return ioe.input_as_voltage(MICS_OX, adc_timeout); + } + + BreakoutMICS6814::Reading BreakoutMICS6814::read_all(uint32_t adc_timeout) { + BreakoutMICS6814::Reading reading; + reading.reducing = read_reducing(adc_timeout); + reading.nh3 = read_nh3(adc_timeout); + reading.oxidising = read_oxidising(adc_timeout); + reading.ref = read_ref(adc_timeout); + return reading; + } + + float BreakoutMICS6814::read_ref(uint32_t adc_timeout) { + float ref = get_raw_ref(adc_timeout); + if(ref == -1) + ref = 0; + + return ref; + } + + float BreakoutMICS6814::read_reducing(uint32_t adc_timeout) { + float vref = ioe.get_adc_vref(); + + float red = get_raw_red(adc_timeout); + if((red != -1) && (vref != red)) + red = (red * 56000.0f) / (vref - red); + else + red = 0; + + return red; + } + + float BreakoutMICS6814::read_nh3(uint32_t adc_timeout) { + float vref = ioe.get_adc_vref(); + + float nh3 = get_raw_red(adc_timeout); + if((nh3 != -1) && (vref != nh3)) + nh3 = (nh3 * 56000.0f) / (vref - nh3); + else + nh3 = 0; + + return nh3; + } + + float BreakoutMICS6814::read_oxidising(uint32_t adc_timeout) { + float vref = ioe.get_adc_vref(); + + float oxd = get_raw_red(adc_timeout); + if((oxd != -1) && (vref != oxd)) + oxd = (oxd * 56000.0f) / (vref - oxd); + else + oxd = 0; + + return oxd; + } +} \ No newline at end of file diff --git a/libraries/breakout_mics6814/breakout_mics6814.hpp b/libraries/breakout_mics6814/breakout_mics6814.hpp index 82e015ea..bcd3f8e9 100644 --- a/libraries/breakout_mics6814/breakout_mics6814.hpp +++ b/libraries/breakout_mics6814/breakout_mics6814.hpp @@ -1,8 +1,103 @@ #pragma once -#include "../../drivers/nuvoton/nuvoton.hpp" +#include "../../drivers/ioexpander/ioexpander.hpp" namespace pimoroni { - typedef Nuvoton BreakoutMICS6814; -} + class BreakoutMICS6814 { + //-------------------------------------------------- + // Constants + //-------------------------------------------------- + public: + static const uint8_t DEFAULT_I2C_ADDRESS = 0x19; + static constexpr float DEFAULT_BRIGHTNESS = 1.0f; //Effectively the maximum fraction of the period that the LED will be on + static const uint8_t PIN_UNUSED = UINT8_MAX; + static const uint32_t DEFAULT_TIMEOUT = 1; + static const uint32_t DEFAULT_ADC_TIMEOUT = 1; + + private: + static const uint8_t LED_R = 3; + static const uint8_t LED_G = 7; + static const uint8_t LED_B = 2; + + static const uint8_t MICS_VREF = 14; + static const uint8_t MICS_RED = 12; + static const uint8_t MICS_NH3 = 11; + static const uint8_t MICS_OX = 13; + static const uint8_t MICS_HEATER_EN = 1; + + static const bool INVERT_OUTPUT = true; //true for common cathode, false for common anode + + + //-------------------------------------------------- + // Substructures + //-------------------------------------------------- + private: + struct Reading { + float ref; + float reducing; + float nh3; + float oxidising; + }; + + + //-------------------------------------------------- + // Variables + //-------------------------------------------------- + private: + IOExpander ioe; + float brightness = DEFAULT_BRIGHTNESS; + + + //-------------------------------------------------- + // Constructors/Destructor + //-------------------------------------------------- + public: + BreakoutMICS6814() : + ioe(DEFAULT_I2C_ADDRESS) {} + + BreakoutMICS6814(uint8_t address) : + ioe(address) {} + + BreakoutMICS6814(i2c_inst_t *i2c, uint8_t address, uint8_t sda, uint8_t scl, uint8_t interrupt = PIN_UNUSED, uint32_t timeout = DEFAULT_TIMEOUT) : + ioe(i2c, address, sda, scl, interrupt, timeout) {} + + + //-------------------------------------------------- + // Methods + //-------------------------------------------------- + public: + bool init(bool skip_chip_id_check = false); + + // For print access in micropython + i2c_inst_t* get_i2c() const; + int get_sda() const; + int get_scl() const; + int get_int() const; + + // Calls through to IOExpander class + void set_addr(uint8_t i2c_addr); + + float get_adc_vref(); + void set_adc_vref(float vref); + + // MICS breakout specific + void set_brightness(float brightness); + void set_led(uint8_t r, uint8_t g, uint8_t b); + + void set_heater(bool on); + void disable_heater(); + + float get_raw_ref(uint32_t adc_timeout = DEFAULT_ADC_TIMEOUT); + float get_raw_red(uint32_t adc_timeout = DEFAULT_ADC_TIMEOUT); + float get_raw_nh3(uint32_t adc_timeout = DEFAULT_ADC_TIMEOUT); + float get_raw_oxd(uint32_t adc_timeout = DEFAULT_ADC_TIMEOUT); + + Reading read_all(uint32_t adc_timeout = DEFAULT_ADC_TIMEOUT); + float read_ref(uint32_t adc_timeout = DEFAULT_ADC_TIMEOUT); + float read_reducing(uint32_t adc_timeout = DEFAULT_ADC_TIMEOUT); + float read_nh3(uint32_t adc_timeout = DEFAULT_ADC_TIMEOUT); + float read_oxidising(uint32_t adc_timeout = DEFAULT_ADC_TIMEOUT); + }; + +} \ No newline at end of file diff --git a/libraries/breakout_potentiometer/breakout_potentiometer.cmake b/libraries/breakout_potentiometer/breakout_potentiometer.cmake index 53f4863e..434970ce 100644 --- a/libraries/breakout_potentiometer/breakout_potentiometer.cmake +++ b/libraries/breakout_potentiometer/breakout_potentiometer.cmake @@ -8,4 +8,4 @@ target_sources(${LIB_NAME} INTERFACE 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 nuvoton) +target_link_libraries(${LIB_NAME} INTERFACE pico_stdlib ioexpander) diff --git a/libraries/breakout_potentiometer/breakout_potentiometer.cpp b/libraries/breakout_potentiometer/breakout_potentiometer.cpp index d4240571..b5aec5a4 100644 --- a/libraries/breakout_potentiometer/breakout_potentiometer.cpp +++ b/libraries/breakout_potentiometer/breakout_potentiometer.cpp @@ -1,5 +1,109 @@ #include "breakout_potentiometer.hpp" +#include namespace pimoroni { -} + bool BreakoutPotentiometer::init(bool skip_chip_id_check) { + bool success = false; + if(ioe.init(skip_chip_id_check)) { + + ioe.set_mode(POT_TERM_A, IOExpander::PIN_OUT); + ioe.set_mode(POT_TERM_B, IOExpander::PIN_OUT); + ioe.set_mode(POT_INPUT, IOExpander::PIN_ADC); + + if(direction_cw) { + // Clockwise increasing + ioe.output(POT_TERM_A, IOExpander::LOW); + ioe.output(POT_TERM_B, IOExpander::HIGH); + } + else { + // Counter clockwise increasing + ioe.output(POT_TERM_A, IOExpander::HIGH); + ioe.output(POT_TERM_B, IOExpander::LOW); + } + + // Calculate a period large enough to get 0-255 steps at the desired brightness + uint16_t period = (uint16_t)(255.0f / brightness); + + ioe.set_pwm_period(period); + ioe.set_pwm_control(2); // PWM as fast as we can to avoid LED flicker + + ioe.set_mode(LED_R, IOExpander::PIN_PWM, false, INVERT_OUTPUT); + ioe.set_mode(LED_G, IOExpander::PIN_PWM, false, INVERT_OUTPUT); + ioe.set_mode(LED_B, IOExpander::PIN_PWM, false, INVERT_OUTPUT); + + success = true; + } + + return success; + } + + i2c_inst_t* BreakoutPotentiometer::get_i2c() const { + return ioe.get_i2c(); + } + + int BreakoutPotentiometer::get_sda() const { + return ioe.get_sda(); + } + + int BreakoutPotentiometer::get_scl() const { + return ioe.get_scl(); + } + + int BreakoutPotentiometer::get_int() const { + return ioe.get_int(); + } + + void BreakoutPotentiometer::set_addr(uint8_t i2c_addr) { + ioe.set_addr(i2c_addr); + } + + float BreakoutPotentiometer::get_adc_vref(void) { + return ioe.get_adc_vref(); + } + + void BreakoutPotentiometer::set_adc_vref(float vref) { + ioe.set_adc_vref(vref); + } + + bool BreakoutPotentiometer::get_direction(void) { + return direction_cw; + } + + void BreakoutPotentiometer::set_direction(bool clockwise) { + if(clockwise) { + // Clockwise increasing + ioe.output(POT_TERM_A, IOExpander::LOW); + ioe.output(POT_TERM_B, IOExpander::HIGH); + } + else { + // Counter clockwise increasing + ioe.output(POT_TERM_A, IOExpander::HIGH); + ioe.output(POT_TERM_B, IOExpander::LOW); + } + direction_cw = clockwise; + } + + void BreakoutPotentiometer::set_brightness(float brightness) { + this->brightness = std::min(std::max(brightness, 0.01f), 1.0f); + + // Calculate a period large enough to get 0-255 steps at the desired brightness + uint16_t period = (uint16_t)(255.0f / this->brightness); + + ioe.set_pwm_period(period); + } + + void BreakoutPotentiometer::set_led(uint8_t r, uint8_t g, uint8_t b) { + ioe.output(LED_R, r, false); // Hold off pwm load until the last + ioe.output(LED_G, g, false); // Hold off pwm load until the last + ioe.output(LED_B, b); // Loads all 3 pwms + } + + int16_t BreakoutPotentiometer::read(uint32_t adc_timeout) { + return ioe.input(POT_INPUT, adc_timeout); + } + + float BreakoutPotentiometer::read_as_percent(uint32_t adc_timeout) { + return (ioe.input_as_voltage(POT_INPUT, adc_timeout) / ioe.get_adc_vref()); + } +} \ No newline at end of file diff --git a/libraries/breakout_potentiometer/breakout_potentiometer.hpp b/libraries/breakout_potentiometer/breakout_potentiometer.hpp index 7ff338f5..28b899b3 100644 --- a/libraries/breakout_potentiometer/breakout_potentiometer.hpp +++ b/libraries/breakout_potentiometer/breakout_potentiometer.hpp @@ -1,8 +1,93 @@ #pragma once -#include "../../drivers/nuvoton/nuvoton.hpp" +#include "../../drivers/ioexpander/ioexpander.hpp" namespace pimoroni { - typedef Nuvoton BreakoutPotentiometer; -} + class BreakoutPotentiometer { + //-------------------------------------------------- + // Enums + //-------------------------------------------------- + public: + enum Direction : bool { + DIRECTION_CW = true, + DIRECTION_CCW = false + }; + + + //-------------------------------------------------- + // Constants + //-------------------------------------------------- + public: + static const uint8_t DEFAULT_I2C_ADDRESS = 0x0E; + static constexpr float DEFAULT_BRIGHTNESS = 1.0f; //Effectively the maximum fraction of the period that the LED will be on + static const bool DEFAULT_DIRECTION = DIRECTION_CW; + static const uint8_t PIN_UNUSED = UINT8_MAX; + static const uint32_t DEFAULT_TIMEOUT = 1; + static const uint32_t DEFAULT_ADC_TIMEOUT = 1; + + private: + static const uint8_t LED_R = 1; + static const uint8_t LED_G = 7; + static const uint8_t LED_B = 2; + + static const uint8_t POT_TERM_A = 12; + static const uint8_t POT_TERM_B = 3; + static const uint8_t POT_INPUT = 11; + + static const bool INVERT_OUTPUT = true; //true for common cathode, false for common anode + + + //-------------------------------------------------- + // Variables + //-------------------------------------------------- + private: + IOExpander ioe; + bool direction_cw = DEFAULT_DIRECTION; + float brightness = DEFAULT_BRIGHTNESS; + + + //-------------------------------------------------- + // Constructors/Destructor + //-------------------------------------------------- + public: + BreakoutPotentiometer() : + ioe(DEFAULT_I2C_ADDRESS) {} + + BreakoutPotentiometer(uint8_t address) : + ioe(address) {} + + BreakoutPotentiometer(i2c_inst_t *i2c, uint8_t address, uint8_t sda, uint8_t scl, uint8_t interrupt = PIN_UNUSED, uint32_t timeout = DEFAULT_TIMEOUT) : + ioe(i2c, address, sda, scl, interrupt, timeout) {} + + + //-------------------------------------------------- + // Methods + //-------------------------------------------------- + public: + bool init(bool skip_chip_id_check = false); + + // For print access in micropython + i2c_inst_t* get_i2c() const; + int get_sda() const; + int get_scl() const; + int get_int() const; + + // Calls through to IOExpander class + void set_addr(uint8_t i2c_addr); + + float get_adc_vref(); + void set_adc_vref(float vref); + + // Potentiometer breakout specific + bool get_direction(); + void set_direction(bool clockwise); + + void set_brightness(float brightness); + void set_led(uint8_t r, uint8_t g, uint8_t b); + + int16_t read(uint32_t adc_timeout = DEFAULT_ADC_TIMEOUT); + float read_as_percent(uint32_t adc_timeout = DEFAULT_ADC_TIMEOUT); + }; + +} \ No newline at end of file