diff --git a/drivers/CMakeLists.txt b/drivers/CMakeLists.txt index 9e56c8cd..ab9f9dcf 100644 --- a/drivers/CMakeLists.txt +++ b/drivers/CMakeLists.txt @@ -1,4 +1,5 @@ add_subdirectory(esp32spi) +add_subdirectory(ioexpander) add_subdirectory(ltp305) add_subdirectory(ltr559) add_subdirectory(sgp30) 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/ioexpander/ioexpander.cmake b/drivers/ioexpander/ioexpander.cmake new file mode 100644 index 00000000..dbd7a67c --- /dev/null +++ b/drivers/ioexpander/ioexpander.cmake @@ -0,0 +1,10 @@ +set(DRIVER_NAME ioexpander) +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/ioexpander/ioexpander.cpp b/drivers/ioexpander/ioexpander.cpp new file mode 100644 index 00000000..ca8cd74f --- /dev/null +++ b/drivers/ioexpander/ioexpander.cpp @@ -0,0 +1,857 @@ +#include +#include +#include +#include + +#include "ioexpander.hpp" + +namespace pimoroni { + + enum reg { + CHIP_ID_L = 0xfa, + CHIP_ID_H = 0xfb, + VERSION = 0xfc, + + // Rotary encoder + ENC_EN = 0x04, + // BIT_ENC_EN_1 = 0 + // BIT_ENC_MICROSTEP_1 = 1 + // BIT_ENC_EN_2 = 2 + // BIT_ENC_MICROSTEP_2 = 3 + // BIT_ENC_EN_3 = 4 + // BIT_ENC_MICROSTEP_3 = 5 + // BIT_ENC_EN_4 = 6 + // BIT_ENC_MICROSTEP_4 = 7 + + ENC_1_CFG = 0x05, + ENC_1_COUNT = 0x06, + ENC_2_CFG = 0x07, + ENC_2_COUNT = 0x08, + ENC_3_CFG = 0x09, + ENC_3_COUNT = 0x0A, + ENC_4_CFG = 0x0B, + ENC_4_COUNT = 0x0C, + + // Cap touch + CAPTOUCH_EN = 0x0D, + CAPTOUCH_CFG = 0x0E, + CAPTOUCH_0 = 0x0F, // First of 8 bytes from 15-22 + + // Switch counters + SWITCH_EN_P0 = 0x17, + SWITCH_EN_P1 = 0x18, + SWITCH_P00 = 0x19, // First of 8 bytes from 25-40 + SWITCH_P10 = 0x21, // First of 8 bytes from 33-49 + + USER_FLASH = 0xD0, + FLASH_PAGE = 0xF0, + DEBUG = 0xF8, + + P0 = 0x40, // protect_bits 2 # Bit addressing + SP = 0x41, // Read only + DPL = 0x42, // Read only + DPH = 0x43, // Read only + RCTRIM0 = 0x44, // Read only + RCTRIM1 = 0x45, // Read only + RWK = 0x46, + PCON = 0x47, // Read only + TCON = 0x48, + TMOD = 0x49, + TL0 = 0x4a, + TL1 = 0x4b, + TH0 = 0x4c, + TH1 = 0x4d, + CKCON = 0x4e, + WKCON = 0x4f, // Read only + P1 = 0x50, // protect_bits 3 6 # Bit addressing + SFRS = 0x51, // TA protected # Read only + CAPCON0 = 0x52, + CAPCON1 = 0x53, + CAPCON2 = 0x54, + CKDIV = 0x55, + CKSWT = 0x56, // TA protected # Read only + CKEN = 0x57, // TA protected # Read only + SCON = 0x58, + SBUF = 0x59, + SBUF_1 = 0x5a, + EIE = 0x5b, // Read only + EIE1 = 0x5c, // Read only + CHPCON = 0x5f, // TA protected # Read only + P2 = 0x60, // Bit addressing + AUXR1 = 0x62, + BODCON0 = 0x63, // TA protected + IAPTRG = 0x64, // TA protected # Read only + IAPUEN = 0x65, // TA protected # Read only + IAPAL = 0x66, // Read only + IAPAH = 0x67, // Read only + IE = 0x68, // Read only + SADDR = 0x69, + WDCON = 0x6a, // TA protected + BODCON1 = 0x6b, // TA protected + P3M1 = 0x6c, + P3S = 0xc0, // Page 1 # Reassigned from 0x6c to avoid collision + P3M2 = 0x6d, + P3SR = 0xc1, // Page 1 # Reassigned from 0x6d to avoid collision + IAPFD = 0x6e, // Read only + IAPCN = 0x6f, // Read only + P3 = 0x70, // Bit addressing + P0M1 = 0x71, // protect_bits 2 + P0S = 0xc2, // Page 1 # Reassigned from 0x71 to avoid collision + P0M2 = 0x72, // protect_bits 2 + P0SR = 0xc3, // Page 1 # Reassigned from 0x72 to avoid collision + P1M1 = 0x73, // protect_bits 3 6 + P1S = 0xc4, // Page 1 # Reassigned from 0x73 to avoid collision + P1M2 = 0x74, // protect_bits 3 6 + P1SR = 0xc5, // Page 1 # Reassigned from 0x74 to avoid collision + P2S = 0x75, + IPH = 0x77, // Read only + PWMINTC = 0xc6, // Page 1 # Read only # Reassigned from 0x77 to avoid collision + IP = 0x78, // Read only + SADEN = 0x79, + SADEN_1 = 0x7a, + SADDR_1 = 0x7b, + I2DAT = 0x7c, // Read only + I2STAT = 0x7d, // Read only + I2CLK = 0x7e, // Read only + I2TOC = 0x7f, // Read only + I2CON = 0x80, // Read only + I2ADDR = 0x81, // Read only + ADCRL = 0x82, + ADCRH = 0x83, + T3CON = 0x84, + PWM4H = 0xc7, // Page 1 # Reassigned from 0x84 to avoid collision + RL3 = 0x85, + PWM5H = 0xc8, // Page 1 # Reassigned from 0x85 to avoid collision + RH3 = 0x86, + PIOCON1 = 0xc9, // Page 1 # Reassigned from 0x86 to avoid collision + TA = 0x87, // Read only + T2CON = 0x88, + T2MOD = 0x89, + RCMP2L = 0x8a, + RCMP2H = 0x8b, + TL2 = 0x8c, + PWM4L = 0xca, // Page 1 # Reassigned from 0x8c to avoid collision + TH2 = 0x8d, + PWM5L = 0xcb, // Page 1 # Reassigned from 0x8d to avoid collision + ADCMPL = 0x8e, + ADCMPH = 0x8f, + PSW = 0x90, // Read only + PWMPH = 0x91, + PWM0H = 0x92, + PWM1H = 0x93, + PWM2H = 0x94, + PWM3H = 0x95, + PNP = 0x96, + FBD = 0x97, + PWMCON0 = 0x98, + PWMPL = 0x99, + PWM0L = 0x9a, + PWM1L = 0x9b, + PWM2L = 0x9c, + PWM3L = 0x9d, + PIOCON0 = 0x9e, + PWMCON1 = 0x9f, + ACC = 0xa0, // Read only + ADCCON1 = 0xa1, + ADCCON2 = 0xa2, + ADCDLY = 0xa3, + C0L = 0xa4, + C0H = 0xa5, + C1L = 0xa6, + C1H = 0xa7, + ADCCON0 = 0xa8, + PICON = 0xa9, // Read only + PINEN = 0xaa, // Read only + PIPEN = 0xab, // Read only + PIF = 0xac, // Read only + C2L = 0xad, + C2H = 0xae, + EIP = 0xaf, // Read only + B = 0xb0, // Read only + CAPCON3 = 0xb1, + CAPCON4 = 0xb2, + SPCR = 0xb3, + SPCR2 = 0xcc, // Page 1 # Reassigned from 0xb3 to avoid collision + SPSR = 0xb4, + SPDR = 0xb5, + AINDIDS = 0xb6, + EIPH = 0xb7, // Read only + SCON_1 = 0xb8, + PDTEN = 0xb9, // TA protected + PDTCNT = 0xba, // TA protected + PMEN = 0xbb, + PMD = 0xbc, + EIP1 = 0xbe, // Read only + EIPH1 = 0xbf, // Read only + + INT = 0xf9, + + + INT_MASK_P0 = 0x00, + INT_MASK_P1 = 0x01, + INT_MASK_P3 = 0x03, + + ADDR = 0xfd, + + CTRL = 0xfe, // 0 = Sleep, 1 = Reset, 2 = Read Flash, 3 = Write Flash, 4 = Addr Unlock + }; + + enum int_mask { + TRIG = 0x1, + OUT = 0x2, + }; + + enum int_bit { + TRIGD = 0, + OUT_EN = 1, + PIN_SWAP = 2, // 0 = P1.3, 1 = P0.0 + }; + + enum ctrl_mask { + SLEEP = 0x1, + RESET = 0x2, + FREAD = 0x4, + FWRITE = 0x8, + ADDRWR = 0x10, + }; + + static const uint8_t NUM_BIT_ADDRESSED_REGISTERS = 4; + static const uint8_t BIT_ADDRESSED_REGS[NUM_BIT_ADDRESSED_REGISTERS] = {reg::P0, reg::P1, reg::P2, reg::P3}; + 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 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 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 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"}; + + 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) { + } + + 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]) { + } + + 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) { + } + + 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]) { + } + + IOExpander::Pin IOExpander::Pin::io(uint8_t port, uint8_t pin) { + return Pin(port, pin); + } + + 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); + } + + IOExpander::Pin IOExpander::Pin::adc(uint8_t port, uint8_t pin, uint8_t channel) { + return Pin(port, pin, channel); + } + + 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 IOExpander::Pin::mode_supported(uint8_t mode) { + bool supported = false; + if((type & TYPE_PWM) && (mode == PIN_MODE_PWM)) { + supported = true; + } + else if((type & TYPE_ADC) && (mode == PIN_MODE_ADC)) { + supported = true; + } + return supported; + } + + IOExpander::Pin::IOType IOExpander::Pin::get_type() { + return type; + } + + uint8_t IOExpander::Pin::get_mode() { + return mode; + } + + void IOExpander::Pin::set_mode(uint8_t mode) { + this->mode = mode; + } + + IOExpander::IOExpander() : + IOExpander(i2c0, DEFAULT_I2C_ADDRESS, DEFAULT_SDA_PIN, DEFAULT_SCL_PIN, DEFAULT_INT_PIN) { + } + + IOExpander::IOExpander(uint8_t address, uint32_t timeout, bool debug) : + IOExpander(i2c0, address, DEFAULT_SDA_PIN, DEFAULT_SCL_PIN, DEFAULT_INT_PIN, timeout, debug) { + } + + IOExpander::IOExpander(i2c_inst_t *i2c, uint8_t address, uint8_t sda, uint8_t scl, uint8_t interrupt, uint32_t timeout, bool debug) : + i2c(i2c), address(address), sda(sda), scl(scl), interrupt(interrupt), + timeout(timeout), debug(debug), vref(3.3f), + encoder_offset{0,0,0,0}, + encoder_last{0,0,0,0}, + pins{ Pin::pwm(1, 5, 5, reg::PIOCON1), + Pin::pwm(1, 0, 2, reg::PIOCON0), + Pin::pwm(1, 2, 0, reg::PIOCON0), + Pin::pwm(1, 4, 1, reg::PIOCON1), + Pin::pwm(0, 0, 3, reg::PIOCON0), + Pin::pwm(0, 1, 4, reg::PIOCON0), + Pin::adc_or_pwm(1, 1, 7, 1, reg::PIOCON0), + Pin::adc_or_pwm(0, 3, 6, 5, reg::PIOCON0), + Pin::adc_or_pwm(0, 4, 5, 3, reg::PIOCON1), + Pin::adc(3, 0, 1), + Pin::adc(0, 6, 3), + Pin::adc_or_pwm(0, 5, 4, 2, reg::PIOCON1), + Pin::adc(0, 7, 2), + Pin::adc(1, 7, 0)} { + } + + bool IOExpander::init(bool skipChipIdCheck) { + bool succeeded = true; + + 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); + + enable_interrupt_out(true); + } + + if(!skipChipIdCheck) { + uint16_t chip_id = get_chip_id(); + if(chip_id != CHIP_ID) { + if(debug) { + printf("Chip ID invalid: %04x expected: %04x\n", chip_id, CHIP_ID); + } + succeeded = false; + } + } + + return succeeded; + } + + 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 IOExpander::set_address(uint8_t address) { + set_bit(reg::CTRL, 4); + i2c_reg_write_uint8(reg::ADDR, address); + this->address = address; + sleep_ms(250); //TODO Handle addr change IOError better + //wait_for_flash() + clr_bit(reg::CTRL, 4); + } + + float IOExpander::get_adc_vref() { + return vref; + } + + void IOExpander::set_adc_vref(float vref) { + this->vref = vref; + } + + 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 IOExpander::disable_interrupt_out() { + clr_bit(reg::INT, int_bit::OUT_EN); + } + + bool IOExpander::get_interrupt_flag() { + if(interrupt != PIN_UNUSED) + return !gpio_get(interrupt); + else + return (get_bit(reg::INT, int_bit::TRIGD) != 0); + } + + void IOExpander::clear_interrupt_flag() { + clr_bit(reg::INT, int_bit::TRIGD); + } + + 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]; + change_bit(io_pin.reg_int_mask_p, io_pin.pin, enabled); + + succeeded = true; + } + + return succeeded; + } + + 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 + + if(wait_for_load) { + while(pwm_loading()) { + sleep_ms(1); // Wait for "LOAD" to complete + if(millis() - start_time >= timeout) { + if(debug) + printf("Timed out waiting for PWM load!"); + return; + } + } + } + } + + bool IOExpander::pwm_loading() { + return get_bit(reg::PWMCON0, 6); + } + + 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) { + while(pwm_clearing()) { + sleep_ms(1); // Wait for "CLRPWM" to complete + if(millis() - start_time >= timeout) { + if(debug) + printf("Timed out waiting for PWM clear!"); + return; + } + } + } + } + + bool IOExpander::pwm_clearing() { + return get_bit(reg::PWMCON0, 4); + } + + bool IOExpander::set_pwm_control(uint8_t divider) { + bool divider_good = true; + uint8_t pwmdiv2 = 0; + switch(divider) { + case 1: pwmdiv2 = 0b000; break; + case 2: pwmdiv2 = 0b001; break; + case 4: pwmdiv2 = 0b010; break; + case 8: pwmdiv2 = 0b011; break; + case 16: pwmdiv2 = 0b100; break; + case 32: pwmdiv2 = 0b101; break; + case 64: pwmdiv2 = 0b110; break; + case 128: pwmdiv2 = 0b111; break; + + default: + if(debug) { + printf("ValueError: A clock divider of %d\n", divider); + } + divider_good = false; + break; + } + + if(divider_good) { + //TODO: This currently sets GP, PWMTYP and FBINEN to 0 + // It might be desirable to make these available to the user + // GP - Group mode enable (changes first three pairs of pAM to PWM01H and PWM01L) + // PWMTYP - PWM type select: 0 edge-aligned, 1 center-aligned + // FBINEN - Fault-break input enable + + i2c_reg_write_uint8(reg::PWMCON1, pwmdiv2); + } + + return divider_good; + } + + 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)); + + if(load) + pwm_load(); + } + + uint8_t IOExpander::get_mode(uint8_t pin) { + if(pin < 1 || pin > NUM_PINS) { + printf("ValueError: Pin should be in range 1-14.\n"); + return UINT8_MAX; + } + + return pins[pin - 1].get_mode(); + } + + 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"); + return; + } + + Pin& io_pin = pins[pin - 1]; + + uint8_t gpio_mode = mode & 0b11; + uint8_t io_type = (mode >> 2) & 0b11; + uint8_t initial_state = mode >> 4; + + if(io_pin.get_mode() == mode) { + if(debug) { + printf("Mode already is %s\n", MODE_NAMES[io_type]); + } + return; + } + + if((io_type != Pin::TYPE_IO) && !io_pin.mode_supported(mode)) { + if(debug) { + printf("Pin %d does not support %s!\n", pin, MODE_NAMES[io_type]); + } + return; + } + + io_pin.set_mode(mode); + if(debug) { + printf("Setting pin %d to mode %s %s, state: %s\n", pin, MODE_NAMES[io_type], GPIO_NAMES[gpio_mode], STATE_NAMES[initial_state]); + } + + if(mode == PIN_MODE_PWM) { + set_bit(io_pin.reg_io_pwm, io_pin.pwm_channel); + change_bit(reg::PNP, io_pin.pwm_channel, invert); + set_bit(reg::PWMCON0, 7); // Set PWMRUN bit + } + else { + if(io_pin.get_type() & Pin::TYPE_PWM) + clr_bit(io_pin.reg_io_pwm, io_pin.pwm_channel); + } + + uint8_t pm1 = i2c_reg_read_uint8(io_pin.reg_m1); + uint8_t pm2 = i2c_reg_read_uint8(io_pin.reg_m2); + + // Clear the pm1 and pm2 bits + pm1 &= 255 - (1 << io_pin.pin); + pm2 &= 255 - (1 << io_pin.pin); + + // Set the new pm1 and pm2 bits according to our gpio_mode + pm1 |= (gpio_mode >> 1) << io_pin.pin; + pm2 |= (gpio_mode & 0b1) << io_pin.pin; + + i2c_reg_write_uint8(io_pin.reg_m1, pm1); + i2c_reg_write_uint8(io_pin.reg_m2, pm2); + + // Set up Schmitt trigger mode on inputs + if(mode == PIN_MODE_PU || mode == PIN_MODE_IN) + change_bit(io_pin.reg_ps, io_pin.pin, schmitt_trigger); + + // 5th bit of mode encodes default output pin state + i2c_reg_write_uint8(io_pin.reg_p, (initial_state << 3) | io_pin.pin); + } + + int16_t IOExpander::input(uint8_t pin, uint32_t adc_timeout) { + if(pin < 1 || pin > NUM_PINS) { + if(debug) + printf("ValueError: Pin should be in range 1-14.\n"); + return -1; + } + + Pin& io_pin = pins[pin - 1]; + + if(io_pin.get_mode() == PIN_MODE_ADC) { + if(debug) { + printf("Reading ADC from pin %d\n", pin); + } + + clr_bits(reg::ADCCON0, 0x0f); + set_bits(reg::ADCCON0, io_pin.adc_channel); + i2c_reg_write_uint8(reg::AINDIDS, 0); + set_bit(reg::AINDIDS, io_pin.adc_channel); + set_bit(reg::ADCCON1, 0); + + clr_bit(reg::ADCCON0, 7); // ADCF - Clear the conversion complete flag + set_bit(reg::ADCCON0, 6); // ADCS - Set the ADC conversion start flag + + // Wait for the ADCF conversion complete flag to be set + unsigned long start_time = millis(); + while(!get_bit(reg::ADCCON0, 7)) { + sleep_ms(10); + if(millis() - start_time >= adc_timeout) { + if(debug) + printf("Timeout waiting for ADC conversion!"); + return -1; + } + } + + uint8_t hi = i2c_reg_read_uint8(reg::ADCRH); + uint8_t lo = i2c_reg_read_uint8(reg::ADCRL); + return (uint16_t)(hi << 4) | (uint16_t)lo; + } + else { + if(debug) { + printf("Reading IO from pin %d\n", pin); + } + + uint8_t pv = get_bit(io_pin.reg_p, io_pin.pin); + return (pv) ? 1 : 0; + } + } + + float IOExpander::input_as_voltage(uint8_t pin, uint32_t adc_timeout) { + if(pin < 1 || pin > NUM_PINS) { + if(debug) + printf("ValueError: Pin should be in range 1-14.\n"); + return -1; + } + + Pin& io_pin = pins[pin - 1]; + + if(io_pin.get_mode() == PIN_MODE_ADC) { + if(debug) { + printf("Reading ADC from pin %d\n", pin); + } + + clr_bits(reg::ADCCON0, 0x0f); + set_bits(reg::ADCCON0, io_pin.adc_channel); + i2c_reg_write_uint8(reg::AINDIDS, 0); + set_bit(reg::AINDIDS, io_pin.adc_channel); + set_bit(reg::ADCCON1, 0); + + + clr_bit(reg::ADCCON0, 7); // ADCF - Clear the conversion complete flag + set_bit(reg::ADCCON0, 6); // ADCS - Set the ADC conversion start flag + + // Wait for the ADCF conversion complete flag to be set + unsigned long start_time = millis(); + while(!get_bit(reg::ADCCON0, 7)) { + sleep_ms(1); + if(millis() - start_time >= adc_timeout) { + if(debug) + printf("Timeout waiting for ADC conversion!\n"); + return -1; + } + } + + uint8_t hi = i2c_reg_read_uint8(reg::ADCRH); + uint8_t lo = i2c_reg_read_uint8(reg::ADCRL); + return ((float)((uint16_t)(hi << 4) | (uint16_t)lo) / 4095.0f) * vref; + } + else { + if(debug) { + printf("Reading IO from pin %d\n", pin); + } + + uint8_t pv = get_bit(io_pin.reg_p, io_pin.pin); + return (pv) ? vref : 0.0f; + } + } + + 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; + } + + Pin& io_pin = pins[pin - 1]; + + if(io_pin.get_mode() == PIN_MODE_PWM) { + if(debug) { + printf("Outputting PWM to pin: %d\n", pin); + } + + i2c_reg_write_uint8(io_pin.reg_pwml, (uint8_t)(value & 0xff)); + i2c_reg_write_uint8(io_pin.reg_pwmh, (uint8_t)(value >> 8)); + if(load) + pwm_load(); + } + else { + 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 == HIGH) { + if(debug) { + printf("Outputting HIGH to pin: %d\n", pin); + } + + set_bit(io_pin.reg_p, io_pin.pin); + } + } + } + + void IOExpander::setup_rotary_encoder(uint8_t channel, uint8_t pin_a, uint8_t pin_b, uint8_t pin_c, bool count_microsteps) { + channel -= 1; + set_mode(pin_a, PIN_MODE_PU, true); + set_mode(pin_b, PIN_MODE_PU, true); + + if(pin_c != 0) { + set_mode(pin_c, PIN_MODE_OD); + output(pin_c, 0); + } + + i2c_reg_write_uint8(ENC_CFG[channel], pin_a | (pin_b << 4)); + change_bit(reg::ENC_EN, (channel * 2) + 1, count_microsteps); + set_bit(reg::ENC_EN, channel * 2); + + // Reset internal encoder count to zero + uint8_t reg = ENC_COUNT[channel]; + i2c_reg_write_uint8(reg, 0x00); + } + + int16_t IOExpander::read_rotary_encoder(uint8_t channel) { + channel -= 1; + int16_t last = encoder_last[channel]; + uint8_t reg = ENC_COUNT[channel]; + int16_t value = (int16_t)i2c_reg_read_uint8(reg); + + if(value & 0b10000000) + value -= 256; + + if(last > 64 && value < -64) + encoder_offset[channel] += 256; + if(last < -64 && value > 64) + encoder_offset[channel] -= 256; + + encoder_last[channel] = value; + + return encoder_offset[channel] + value; + } + + 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 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 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 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 + bool reg_in_bit_addressed_regs = false; + for(uint8_t i = 0; i < NUM_BIT_ADDRESSED_REGISTERS; i++) { + if(BIT_ADDRESSED_REGS[i] == reg) { + for(uint8_t bit = 0; bit < 8; bit++) { + if(bits & (1 << bit)) + i2c_reg_write_uint8(reg, 0b1000 | (bit & 0b111)); + } + reg_in_bit_addressed_regs = true; + break; + } + } + + // Now deal with any other registers + if(!reg_in_bit_addressed_regs) { + uint8_t value = i2c_reg_read_uint8(reg); + sleep_us(10); + i2c_reg_write_uint8(reg, value | bits); + } + } + + 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 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) { + for(uint8_t bit = 0; bit < 8; bit++) { + if(bits & (1 << bit)) + i2c_reg_write_uint8(reg, 0b0000 | (bit & 0b111)); + } + reg_in_bit_addressed_regs = true; + break; + } + } + + // Now deal with any other registers + if(!reg_in_bit_addressed_regs) { + uint8_t value = i2c_reg_read_uint8(reg); + sleep_us(10); + i2c_reg_write_uint8(reg, value & ~bits); + } + } + + 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 IOExpander::change_bit(uint8_t reg, uint8_t bit, bool state) { + // Toggle one register bit on/off. + if(state) + set_bit(reg, bit); + else + clr_bit(reg, bit); + } + + 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()) { + if(millis() - start_time > timeout) { + printf("Timed out waiting for interrupt!\n"); + return; + } + sleep_ms(1); + } + + start_time = millis(); + while(!get_interrupt_flag()) { + if(millis() - start_time > timeout) { + printf("Timed out waiting for interrupt!\n"); + return; + } + sleep_ms(1); + } + } + + uint32_t IOExpander::millis() { + return to_ms_since_boot(get_absolute_time()); + } +} \ No newline at end of file diff --git a/drivers/ioexpander/ioexpander.hpp b/drivers/ioexpander/ioexpander.hpp new file mode 100644 index 00000000..0edaa369 --- /dev/null +++ b/drivers/ioexpander/ioexpander.hpp @@ -0,0 +1,229 @@ +#pragma once + +#include "hardware/i2c.h" +#include "hardware/gpio.h" + +namespace pimoroni { + + 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) + + public: + static const uint8_t DEFAULT_I2C_ADDRESS = 0x18; + static const uint8_t DEFAULT_SDA_PIN = 20; + static const uint8_t DEFAULT_SCL_PIN = 21; + static const uint8_t DEFAULT_INT_PIN = 22; + static const uint8_t PIN_UNUSED = UINT8_MAX; + + static const 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_OD = PIN_MODE_OD; // 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 + //-------------------------------------------------- + private: + class Pin { + public: + //-------------------------------------------------- + // Enums + //-------------------------------------------------- + enum IOType { + TYPE_IO = 0b00, + TYPE_PWM = 0b01, + 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 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] + + 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; + 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; + const uint8_t reg_ps; + const uint8_t reg_int_mask_p; + + const uint8_t reg_io_pwm; + const uint8_t reg_pwml; + const uint8_t reg_pwmh; + + + //-------------------------------------------------- + // 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 + + + //-------------------------------------------------- + // 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 + + IOType get_type(void); + uint8_t get_mode(void); + void set_mode(uint8_t mode); + + bool mode_supported(uint8_t mode); + }; + + + //-------------------------------------------------- + // Variables + //-------------------------------------------------- + private: + i2c_inst_t *i2c; + + // 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; + + uint32_t timeout; + bool debug; + float vref; + int16_t encoder_offset[4]; + int16_t encoder_last[4]; + Pin pins[NUM_PINS]; + + + //-------------------------------------------------- + // Constructors/Destructor + //-------------------------------------------------- + public: + IOExpander(); + IOExpander(uint8_t address, uint32_t timeout = 1, bool debug = false); + IOExpander(i2c_inst_t *i2c, uint8_t address, uint8_t sda, uint8_t scl, uint8_t interrupt = PIN_UNUSED, uint32_t timeout = 1, bool debug = false); + + + //-------------------------------------------------- + // 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; + + uint16_t get_chip_id(); + + void set_address(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(); + bool get_interrupt_flag(); + void clear_interrupt_flag(); + bool set_pin_interrupt(uint8_t pin, bool enabled); + + 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 pin_a, uint8_t pin_b, uint8_t pin_c = 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); + + uint8_t get_bit(uint8_t reg, uint8_t bit); + void set_bits(uint8_t reg, uint8_t bits); + void set_bit(uint8_t reg, uint8_t bit); + void clr_bits(uint8_t reg, uint8_t bits); + void clr_bit(uint8_t reg, uint8_t bit); + void change_bit(uint8_t reg, uint8_t bit, bool state); + + void wait_for_flash(); + + uint32_t millis(); + }; + +} diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 3c6a2ea8..18bb4c93 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -1,9 +1,13 @@ add_subdirectory(breakout_dotmatrix) +add_subdirectory(breakout_encoder) +add_subdirectory(breakout_ioexpander) 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) @@ -13,7 +17,9 @@ add_subdirectory(pico_display) add_subdirectory(pico_unicorn) add_subdirectory(pico_unicorn_plasma) add_subdirectory(pico_scroll) +add_subdirectory(pico_enc_explorer) add_subdirectory(pico_explorer) +add_subdirectory(pico_pot_explorer) add_subdirectory(pico_rgb_keypad) add_subdirectory(pico_rtc_display) add_subdirectory(pico_tof_display) diff --git a/examples/breakout_encoder/CMakeLists.txt b/examples/breakout_encoder/CMakeLists.txt new file mode 100644 index 00000000..af459279 --- /dev/null +++ b/examples/breakout_encoder/CMakeLists.txt @@ -0,0 +1,16 @@ +set(OUTPUT_NAME encoder_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} 1) + +# 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..ae57882f --- /dev/null +++ b/examples/breakout_encoder/demo.cpp @@ -0,0 +1,80 @@ +#include "pico/stdlib.h" +#include +#include + +#include "breakout_encoder.hpp" + +using namespace pimoroni; + +static const uint8_t STEPS_PER_REV = 24; + +BreakoutEncoder enc; +bool toggle = false; + +// HSV Conversion expects float inputs in the range of 0.00-1.00 for each channel +// Outputs are rgb in the range 0-255 for each channel +void from_hsv(float h, float s, float v, uint8_t &r, uint8_t &g, uint8_t &b) { + float i = floor(h * 6.0f); + float f = h * 6.0f - i; + v *= 255.0f; + uint8_t p = v * (1.0f - s); + uint8_t q = v * (1.0f - f * s); + uint8_t t = v * (1.0f - (1.0f - f) * s); + + switch (int(i) % 6) { + case 0: r = v; g = t; b = p; break; + case 1: r = q; g = v; b = p; break; + case 2: r = p; g = v; b = t; break; + case 3: r = p; g = q; b = v; break; + case 4: r = t; g = p; b = v; break; + case 5: r = v; g = p; b = q; break; + } +} + +void count_changed(int16_t count) { + printf("Count: %d\n", count); + float h = (count % STEPS_PER_REV) / (float)STEPS_PER_REV; + uint8_t r, g, b; + from_hsv(h, 1.0f, 1.0f, r, g, b); + enc.set_led(r, g, b); +} + +int main() { + gpio_init(PICO_DEFAULT_LED_PIN); + gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_OUT); + + stdio_init_all(); + + int16_t count = 0; + if(enc.init()) { + printf("Encoder found...\n"); + + //enc.set_direction(BreakoutEncoder::DIRECTION_CCW); // Uncomment this to flip the direction + + count_changed(count); + enc.clear_interrupt_flag(); + + while(true) { + gpio_put(PICO_DEFAULT_LED_PIN, toggle); + toggle = !toggle; + + if(enc.get_interrupt_flag()) { + count = enc.read(); + enc.clear_interrupt_flag(); + + while(count < 0) + count += STEPS_PER_REV; + + count_changed(count); + } + + sleep_ms(20); + } + } + else { + printf("Encoder not found :'(\n"); + gpio_put(PICO_DEFAULT_LED_PIN, true); + } + + return 0; +} diff --git a/examples/breakout_ioexpander/CMakeLists.txt b/examples/breakout_ioexpander/CMakeLists.txt new file mode 100644 index 00000000..a6c814cd --- /dev/null +++ b/examples/breakout_ioexpander/CMakeLists.txt @@ -0,0 +1,3 @@ +add_subdirectory(adc) +add_subdirectory(button) +add_subdirectory(servo) \ No newline at end of file diff --git a/examples/breakout_ioexpander/adc/CMakeLists.txt b/examples/breakout_ioexpander/adc/CMakeLists.txt new file mode 100644 index 00000000..89a1da66 --- /dev/null +++ b/examples/breakout_ioexpander/adc/CMakeLists.txt @@ -0,0 +1,16 @@ +set(OUTPUT_NAME ioexpander_adc) + +add_executable( + ${OUTPUT_NAME} + adc.cpp +) + +# enable usb output, disable uart output +pico_enable_stdio_usb(${OUTPUT_NAME} 1) +pico_enable_stdio_uart(${OUTPUT_NAME} 1) + +# Pull in pico libraries that we need +target_link_libraries(${OUTPUT_NAME} pico_stdlib breakout_ioexpander) + +# create map/bin/hex file etc. +pico_add_extra_outputs(${OUTPUT_NAME}) diff --git a/examples/breakout_ioexpander/adc/adc.cpp b/examples/breakout_ioexpander/adc/adc.cpp new file mode 100644 index 00000000..0e8de8ef --- /dev/null +++ b/examples/breakout_ioexpander/adc/adc.cpp @@ -0,0 +1,42 @@ +#include "pico/stdlib.h" +#include + +#include "breakout_ioexpander.hpp" + +using namespace pimoroni; + +static const uint8_t IOE_ADC_PIN = 10; + +BreakoutIOExpander ioe(0x18); +bool toggle = false; + +int main() { + gpio_init(PICO_DEFAULT_LED_PIN); + gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_OUT); + + stdio_init_all(); + + if(ioe.init()) { + printf("IOExpander found...\n"); + + // ioe.set_adc_vref(5.0f); //Uncomment this if running the IOExpander off a 5V supply + ioe.set_mode(IOE_ADC_PIN, IOExpander::PIN_ADC); + + while(true) { + gpio_put(PICO_DEFAULT_LED_PIN, toggle); + toggle = !toggle; + + float voltage = ioe.input_as_voltage(IOE_ADC_PIN); + + printf("Voltage: %f\n", voltage); + + sleep_ms(20); + } + } + else { + printf("IOExpander not found :'(\n"); + gpio_put(PICO_DEFAULT_LED_PIN, true); + } + + return 0; +} diff --git a/examples/breakout_ioexpander/button/CMakeLists.txt b/examples/breakout_ioexpander/button/CMakeLists.txt new file mode 100644 index 00000000..6b702f74 --- /dev/null +++ b/examples/breakout_ioexpander/button/CMakeLists.txt @@ -0,0 +1,16 @@ +set(OUTPUT_NAME ioexpander_button) + +add_executable( + ${OUTPUT_NAME} + button.cpp +) + +# enable usb output, disable uart output +pico_enable_stdio_usb(${OUTPUT_NAME} 1) +pico_enable_stdio_uart(${OUTPUT_NAME} 1) + +# Pull in pico libraries that we need +target_link_libraries(${OUTPUT_NAME} pico_stdlib breakout_ioexpander) + +# create map/bin/hex file etc. +pico_add_extra_outputs(${OUTPUT_NAME}) diff --git a/examples/breakout_ioexpander/button/button.cpp b/examples/breakout_ioexpander/button/button.cpp new file mode 100644 index 00000000..03035c71 --- /dev/null +++ b/examples/breakout_ioexpander/button/button.cpp @@ -0,0 +1,49 @@ +#include "pico/stdlib.h" +#include + +#include "breakout_ioexpander.hpp" + +using namespace pimoroni; + +//Connect a button between this pin and ground +static const uint8_t IOE_BUTTON_PIN = 14; + +BreakoutIOExpander ioe(0x18); +bool last_state = true; + +int main() { + gpio_init(PICO_DEFAULT_LED_PIN); + gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_OUT); + + stdio_init_all(); + + if(ioe.init()) { + printf("IOExpander found...\n"); + + ioe.set_mode(IOE_BUTTON_PIN, IOExpander::PIN_IN_PULL_UP); + + while(true) { + bool state = ioe.input(IOE_BUTTON_PIN); + if(state != last_state) { + if(state) { + printf("Button has been released\n"); + gpio_put(PICO_DEFAULT_LED_PIN, false); + } + else { + printf("Button has been pressed\n"); + gpio_put(PICO_DEFAULT_LED_PIN, true); + } + + last_state = state; + } + + sleep_ms(20); + } + } + else { + printf("IOExpander not found :'(\n"); + gpio_put(PICO_DEFAULT_LED_PIN, true); + } + + return 0; +} diff --git a/examples/breakout_ioexpander/servo/CMakeLists.txt b/examples/breakout_ioexpander/servo/CMakeLists.txt new file mode 100644 index 00000000..611f2335 --- /dev/null +++ b/examples/breakout_ioexpander/servo/CMakeLists.txt @@ -0,0 +1,16 @@ +set(OUTPUT_NAME ioexpander_servo) + +add_executable( + ${OUTPUT_NAME} + servo.cpp +) + +# enable usb output, disable uart output +pico_enable_stdio_usb(${OUTPUT_NAME} 1) +pico_enable_stdio_uart(${OUTPUT_NAME} 1) + +# Pull in pico libraries that we need +target_link_libraries(${OUTPUT_NAME} pico_stdlib breakout_ioexpander) + +# create map/bin/hex file etc. +pico_add_extra_outputs(${OUTPUT_NAME}) diff --git a/examples/breakout_ioexpander/servo/servo.cpp b/examples/breakout_ioexpander/servo/servo.cpp new file mode 100644 index 00000000..860cb17f --- /dev/null +++ b/examples/breakout_ioexpander/servo/servo.cpp @@ -0,0 +1,60 @@ +#include "pico/stdlib.h" +#include +#include + +#include "breakout_ioexpander.hpp" + +using namespace pimoroni; + +static const uint8_t IOE_SERVO_PIN = 1; + +// Settings to produce a 50Hz output from the 24MHz clock. +// 24,000,000 Hz / 8 = 3,000,000 Hz +// 3,000,000 Hz / 60,000 Period = 50 Hz +static const uint8_t DIVIDER = 8; +static const uint16_t PERIOD = 60000; +static constexpr float CYCLE_TIME = 5.0f; +static constexpr float SERVO_RANGE = 1000.0f; // Between 1000 and 2000us (1-2ms) + +BreakoutIOExpander ioe(0x18); +bool toggle = false; + +int main() { + gpio_init(PICO_DEFAULT_LED_PIN); + gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_OUT); + + stdio_init_all(); + + if(ioe.init()) { + printf("IOExpander found...\n"); + + ioe.set_pwm_period(PERIOD); + ioe.set_pwm_control(DIVIDER); + + ioe.set_mode(IOE_SERVO_PIN, IOExpander::PIN_PWM); + + while(true) { + gpio_put(PICO_DEFAULT_LED_PIN, toggle); + toggle = !toggle; + + absolute_time_t at = get_absolute_time(); + float t = to_us_since_boot(at) / 1000000.0f; + float s = sinf((t * M_PI * 2.0f) / CYCLE_TIME) / 2.0f; + float servo_us = 1500.0f + (s * SERVO_RANGE); + + float duty_per_microsecond = (float)PERIOD / (float)(20 * 1000); // Default is 3 LSB per microsecond + + uint16_t duty_cycle = (uint16_t)(roundf(servo_us * duty_per_microsecond)); + printf("Cycle Time: %.2f, Pulse: %.1fus, Duty Cycle: %d\n", fmodf(t, CYCLE_TIME), servo_us, duty_cycle); + ioe.output(IOE_SERVO_PIN, duty_cycle); + + sleep_ms(20); + } + } + else { + printf("IOExpander not found :'(\n"); + gpio_put(PICO_DEFAULT_LED_PIN, true); + } + + 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..31a9de42 --- /dev/null +++ b/examples/breakout_mics6814/demo.cpp @@ -0,0 +1,27 @@ +#include "pico/stdlib.h" +#include "stdio.h" + +#include "breakout_mics6814.hpp" + +using namespace pimoroni; + +BreakoutMICS6814 mics6814; + +int main() { + stdio_init_all(); + + gpio_init(PICO_DEFAULT_LED_PIN); + gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_OUT); + + mics6814.init(); + mics6814.set_heater(true); + mics6814.set_led(0, 0, 255); + + while(true) { + BreakoutMICS6814::Reading reading = mics6814.read_all(); + printf("OX: %f Red: %f NH3: %f\n", reading.oxidising, reading.reducing, reading.nh3); + 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..a2484df1 --- /dev/null +++ b/examples/breakout_potentiometer/CMakeLists.txt @@ -0,0 +1,16 @@ +set(OUTPUT_NAME potentiometer_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} 1) + +# 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..2892f314 --- /dev/null +++ b/examples/breakout_potentiometer/demo.cpp @@ -0,0 +1,64 @@ +#include "pico/stdlib.h" +#include +#include + +#include "breakout_potentiometer.hpp" + +using namespace pimoroni; + +BreakoutPotentiometer pot; +bool toggle = false; + +// HSV Conversion expects float inputs in the range of 0.00-1.00 for each channel +// Outputs are rgb in the range 0-255 for each channel +void from_hsv(float h, float s, float v, uint8_t &r, uint8_t &g, uint8_t &b) { + float i = floor(h * 6.0f); + float f = h * 6.0f - i; + v *= 255.0f; + uint8_t p = v * (1.0f - s); + uint8_t q = v * (1.0f - f * s); + uint8_t t = v * (1.0f - (1.0f - f) * s); + + switch (int(i) % 6) { + case 0: r = v; g = t; b = p; break; + case 1: r = q; g = v; b = p; break; + case 2: r = p; g = v; b = t; break; + case 3: r = p; g = q; b = v; break; + case 4: r = t; g = p; b = v; break; + case 5: r = v; g = p; b = q; break; + } +} + +int main() { + gpio_init(PICO_DEFAULT_LED_PIN); + gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_OUT); + + stdio_init_all(); + + int16_t count = 0; + if(pot.init()) { + printf("Potentiometer found...\n"); + + //pot.set_direction(BreakoutPotentiometer::DIRECTION_CCW); // Uncomment this to flip the direction + + while(true) { + gpio_put(PICO_DEFAULT_LED_PIN, toggle); + toggle = !toggle; + + float percent = pot.read(); + + printf("Percent: %d\n", (int)(percent * 100)); + uint8_t r, g, b; + from_hsv(percent, 1.0f, 1.0f, r, g, b); + pot.set_led(r, g, b); + + sleep_ms(20); + } + } + else { + printf("Potentiometer not found :'(\n"); + gpio_put(PICO_DEFAULT_LED_PIN, true); + } + + return 0; +} diff --git a/examples/pico_enc_explorer/CMakeLists.txt b/examples/pico_enc_explorer/CMakeLists.txt new file mode 100644 index 00000000..a082bef9 --- /dev/null +++ b/examples/pico_enc_explorer/CMakeLists.txt @@ -0,0 +1,12 @@ +set(OUTPUT_NAME encoder_explorer) + +add_executable( + ${OUTPUT_NAME} + demo.cpp +) + +# Pull in pico libraries that we need +target_link_libraries(${OUTPUT_NAME} pico_stdlib breakout_encoder pico_explorer) + +# create map/bin/hex file etc. +pico_add_extra_outputs(${OUTPUT_NAME}) diff --git a/examples/pico_enc_explorer/demo.cpp b/examples/pico_enc_explorer/demo.cpp new file mode 100644 index 00000000..75250a30 --- /dev/null +++ b/examples/pico_enc_explorer/demo.cpp @@ -0,0 +1,127 @@ +#include "pico/stdlib.h" +#include +#include +#include +#include + +#include "pico_explorer.hpp" +#include "breakout_encoder.hpp" + +using namespace pimoroni; + +uint16_t buffer[PicoExplorer::WIDTH * PicoExplorer::HEIGHT]; +PicoExplorer pico_explorer(buffer); + +static const uint8_t STEPS_PER_REV = 24; + +BreakoutEncoder enc; +bool toggle = false; + +// HSV Conversion expects float inputs in the range of 0.00-1.00 for each channel +// Outputs are rgb in the range 0-255 for each channel +void from_hsv(float h, float s, float v, uint8_t &r, uint8_t &g, uint8_t &b) { + float i = floor(h * 6.0f); + float f = h * 6.0f - i; + v *= 255.0f; + uint8_t p = v * (1.0f - s); + uint8_t q = v * (1.0f - f * s); + uint8_t t = v * (1.0f - (1.0f - f) * s); + + switch (int(i) % 6) { + case 0: r = v; g = t; b = p; break; + case 1: r = q; g = v; b = p; break; + case 2: r = p; g = v; b = t; break; + case 3: r = p; g = q; b = v; break; + case 4: r = t; g = p; b = v; break; + case 5: r = v; g = p; b = q; break; + } +} + +void count_changed(int16_t count) { + printf("Count: %d\n", count); + float h = (count % STEPS_PER_REV) / (float)STEPS_PER_REV; + uint8_t r, g, b; + from_hsv(h, 1.0f, 1.0f, r, g, b); + enc.set_led(r, g, b); + + pico_explorer.set_pen(0, 0, 0); + pico_explorer.clear(); + + { + pico_explorer.set_pen(255, 0, 0); + std::ostringstream ss; + ss << "R = "; + ss << (int)r; + std::string s(ss.str()); + pico_explorer.text(s, Point(10, 10), 220, 6); + } + + { + pico_explorer.set_pen(0, 255, 0); + std::ostringstream ss; + ss << "G = "; + ss << (int)g; + std::string s(ss.str()); + pico_explorer.text(s, Point(10, 70), 220, 6); + } + + { + pico_explorer.set_pen(0, 0, 255); + std::ostringstream ss; + ss << "B = "; + ss << (int)b; + std::string s(ss.str()); + pico_explorer.text(s, Point(10, 130), 220, 6); + } + + { + pico_explorer.set_pen(r, g, b); + std::ostringstream ss; + ss << "#"; + ss << std::uppercase << std::hex << std::setfill('0') << std::setw(2) << (int)r; + ss << std::uppercase << std::hex << std::setfill('0') << std::setw(2) << (int)g; + ss << std::uppercase << std::hex << std::setfill('0') << std::setw(2) << (int)b; + std::string s(ss.str()); + pico_explorer.text(s, Point(10, 190), 220, 5); + } + pico_explorer.update(); +} + +int main() { + gpio_init(PICO_DEFAULT_LED_PIN); + gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_OUT); + + stdio_init_all(); + + pico_explorer.init(); + pico_explorer.update(); + + int16_t count = 0; + if(enc.init()) { + printf("Encoder found...\n"); + + count_changed(count); + enc.clear_interrupt_flag(); + + while(true) { + gpio_put(PICO_DEFAULT_LED_PIN, toggle); + toggle = !toggle; + + if(enc.get_interrupt_flag()) { + count = enc.read(); + enc.clear_interrupt_flag(); + + while(count < 0) + count += STEPS_PER_REV; + + count_changed(count); + } + } + } + else { + printf("Encoder not found :'(\n"); + gpio_put(PICO_DEFAULT_LED_PIN, true); + } + + return 0; +} diff --git a/examples/pico_pot_explorer/CMakeLists.txt b/examples/pico_pot_explorer/CMakeLists.txt new file mode 100644 index 00000000..dc5649d4 --- /dev/null +++ b/examples/pico_pot_explorer/CMakeLists.txt @@ -0,0 +1,12 @@ +set(OUTPUT_NAME potentiometer_explorer) + +add_executable( + ${OUTPUT_NAME} + demo.cpp +) + +# Pull in pico libraries that we need +target_link_libraries(${OUTPUT_NAME} pico_stdlib breakout_potentiometer pico_explorer) + +# create map/bin/hex file etc. +pico_add_extra_outputs(${OUTPUT_NAME}) diff --git a/examples/pico_pot_explorer/demo.cpp b/examples/pico_pot_explorer/demo.cpp new file mode 100644 index 00000000..14752a3c --- /dev/null +++ b/examples/pico_pot_explorer/demo.cpp @@ -0,0 +1,111 @@ +#include "pico/stdlib.h" +#include +#include +#include +#include + +#include "pico_explorer.hpp" +#include "breakout_potentiometer.hpp" + +using namespace pimoroni; + +uint16_t buffer[PicoExplorer::WIDTH * PicoExplorer::HEIGHT]; +PicoExplorer pico_explorer(buffer); + +BreakoutPotentiometer pot; +bool toggle = false; + +// HSV Conversion expects float inputs in the range of 0.00-1.00 for each channel +// Outputs are rgb in the range 0-255 for each channel +void from_hsv(float h, float s, float v, uint8_t &r, uint8_t &g, uint8_t &b) { + float i = floor(h * 6.0f); + float f = h * 6.0f - i; + v *= 255.0f; + uint8_t p = v * (1.0f - s); + uint8_t q = v * (1.0f - f * s); + uint8_t t = v * (1.0f - (1.0f - f) * s); + + switch (int(i) % 6) { + case 0: r = v; g = t; b = p; break; + case 1: r = q; g = v; b = p; break; + case 2: r = p; g = v; b = t; break; + case 3: r = p; g = q; b = v; break; + case 4: r = t; g = p; b = v; break; + case 5: r = v; g = p; b = q; break; + } +} + +int main() { + gpio_init(PICO_DEFAULT_LED_PIN); + gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_OUT); + + stdio_init_all(); + + pico_explorer.init(); + pico_explorer.update(); + + int16_t count = 0; + if(pot.init()) { + printf("Potentiometer found...\n"); + + while(true) { + gpio_put(PICO_DEFAULT_LED_PIN, toggle); + toggle = !toggle; + + float percent = pot.read(); + + printf("Percent: %d\n", (int)(percent * 100)); + uint8_t r, g, b; + from_hsv(percent, 1.0f, 1.0f, r, g, b); + pot.set_led(r, g, b); + + pico_explorer.set_pen(0, 0, 0); + pico_explorer.clear(); + + { + pico_explorer.set_pen(255, 0, 0); + std::ostringstream ss; + ss << "R = "; + ss << (int)r; + std::string s(ss.str()); + pico_explorer.text(s, Point(10, 10), 220, 6); + } + + { + pico_explorer.set_pen(0, 255, 0); + std::ostringstream ss; + ss << "G = "; + ss << (int)g; + std::string s(ss.str()); + pico_explorer.text(s, Point(10, 70), 220, 6); + } + + { + pico_explorer.set_pen(0, 0, 255); + std::ostringstream ss; + ss << "B = "; + ss << (int)b; + std::string s(ss.str()); + pico_explorer.text(s, Point(10, 130), 220, 6); + } + + { + pico_explorer.set_pen(r, g, b); + std::ostringstream ss; + ss << "#"; + ss << std::uppercase << std::hex << std::setfill('0') << std::setw(2) << (int)r; + ss << std::uppercase << std::hex << std::setfill('0') << std::setw(2) << (int)g; + ss << std::uppercase << std::hex << std::setfill('0') << std::setw(2) << (int)b; + std::string s(ss.str()); + pico_explorer.text(s, Point(10, 190), 220, 5); + } + pico_explorer.update(); + } + } + else { + printf("Encoder Potentiometer found :'(\n"); + gpio_put(PICO_DEFAULT_LED_PIN, true); + } + + return 0; +} diff --git a/libraries/CMakeLists.txt b/libraries/CMakeLists.txt index 62bd90c5..197851e9 100644 --- a/libraries/CMakeLists.txt +++ b/libraries/CMakeLists.txt @@ -1,10 +1,14 @@ add_subdirectory(breakout_dotmatrix) +add_subdirectory(breakout_encoder) +add_subdirectory(breakout_ioexpander) add_subdirectory(breakout_ltr559) add_subdirectory(breakout_colourlcd160x80) add_subdirectory(breakout_colourlcd240x240) 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_as7262) diff --git a/libraries/breakout_encoder/CMakeLists.txt b/libraries/breakout_encoder/CMakeLists.txt new file mode 100644 index 00000000..3f504139 --- /dev/null +++ b/libraries/breakout_encoder/CMakeLists.txt @@ -0,0 +1 @@ +include(breakout_encoder.cmake) diff --git a/libraries/breakout_encoder/breakout_encoder.cmake b/libraries/breakout_encoder/breakout_encoder.cmake new file mode 100644 index 00000000..765e58ab --- /dev/null +++ b/libraries/breakout_encoder/breakout_encoder.cmake @@ -0,0 +1,11 @@ +set(LIB_NAME breakout_encoder) +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 ioexpander) diff --git a/libraries/breakout_encoder/breakout_encoder.cpp b/libraries/breakout_encoder/breakout_encoder.cpp new file mode 100644 index 00000000..0882dd14 --- /dev/null +++ b/libraries/breakout_encoder/breakout_encoder.cpp @@ -0,0 +1,94 @@ +#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_address(uint8_t address) { + ioe.set_address(address); + } + + bool BreakoutEncoder::get_interrupt_flag() { + return ioe.get_interrupt_flag(); + } + + void BreakoutEncoder::clear_interrupt_flag() { + ioe.clear_interrupt_flag(); + } + + BreakoutEncoder::Direction BreakoutEncoder::get_direction() { + return direction; + } + + void BreakoutEncoder::set_direction(Direction direction) { + this->direction = direction; + } + + 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 != 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 new file mode 100644 index 00000000..e85099d8 --- /dev/null +++ b/libraries/breakout_encoder/breakout_encoder.hpp @@ -0,0 +1,95 @@ +#pragma once + +#include "drivers/ioexpander/ioexpander.hpp" + +namespace pimoroni { + + 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 Direction 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; + Direction direction = 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_address(uint8_t address); + bool get_interrupt_flag(); + void clear_interrupt_flag(); + + // Encoder breakout specific + Direction get_direction(); + void set_direction(Direction direction); + + 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/CMakeLists.txt b/libraries/breakout_ioexpander/CMakeLists.txt new file mode 100644 index 00000000..1483b234 --- /dev/null +++ b/libraries/breakout_ioexpander/CMakeLists.txt @@ -0,0 +1 @@ +include(breakout_ioexpander.cmake) diff --git a/libraries/breakout_ioexpander/breakout_ioexpander.cmake b/libraries/breakout_ioexpander/breakout_ioexpander.cmake new file mode 100644 index 00000000..d2a20c35 --- /dev/null +++ b/libraries/breakout_ioexpander/breakout_ioexpander.cmake @@ -0,0 +1,11 @@ +set(LIB_NAME breakout_ioexpander) +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 ioexpander) diff --git a/libraries/breakout_ioexpander/breakout_ioexpander.cpp b/libraries/breakout_ioexpander/breakout_ioexpander.cpp new file mode 100644 index 00000000..8f0f2524 --- /dev/null +++ b/libraries/breakout_ioexpander/breakout_ioexpander.cpp @@ -0,0 +1,5 @@ +#include "breakout_ioexpander.hpp" + +namespace pimoroni { + +} diff --git a/libraries/breakout_ioexpander/breakout_ioexpander.hpp b/libraries/breakout_ioexpander/breakout_ioexpander.hpp new file mode 100644 index 00000000..e3e2ab09 --- /dev/null +++ b/libraries/breakout_ioexpander/breakout_ioexpander.hpp @@ -0,0 +1,8 @@ +#pragma once + +#include "drivers/ioexpander/ioexpander.hpp" + +namespace pimoroni { + + typedef IOExpander BreakoutIOExpander; +} diff --git a/libraries/breakout_mics6814/CMakeLists.txt b/libraries/breakout_mics6814/CMakeLists.txt new file mode 100644 index 00000000..4ebc0c12 --- /dev/null +++ b/libraries/breakout_mics6814/CMakeLists.txt @@ -0,0 +1 @@ +include(breakout_mics6814.cmake) diff --git a/libraries/breakout_mics6814/breakout_mics6814.cmake b/libraries/breakout_mics6814/breakout_mics6814.cmake new file mode 100644 index 00000000..31b28c2e --- /dev/null +++ b/libraries/breakout_mics6814/breakout_mics6814.cmake @@ -0,0 +1,11 @@ +set(LIB_NAME breakout_mics6814) +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 ioexpander) diff --git a/libraries/breakout_mics6814/breakout_mics6814.cpp b/libraries/breakout_mics6814/breakout_mics6814.cpp new file mode 100644 index 00000000..4b1c886c --- /dev/null +++ b/libraries/breakout_mics6814/breakout_mics6814.cpp @@ -0,0 +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_OD); + 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_address(uint8_t address) { + ioe.set_address(address); + } + + 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 new file mode 100644 index 00000000..6940e5dd --- /dev/null +++ b/libraries/breakout_mics6814/breakout_mics6814.hpp @@ -0,0 +1,103 @@ +#pragma once + +#include "drivers/ioexpander/ioexpander.hpp" + +namespace pimoroni { + + 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 + //-------------------------------------------------- + public: + 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_address(uint8_t address); + + 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_msa301/breakout_msa301.hpp b/libraries/breakout_msa301/breakout_msa301.hpp index c16c34b4..54c6647c 100644 --- a/libraries/breakout_msa301/breakout_msa301.hpp +++ b/libraries/breakout_msa301/breakout_msa301.hpp @@ -1,6 +1,6 @@ #pragma once -#include "../../drivers/msa301/msa301.hpp" +#include "drivers/msa301/msa301.hpp" namespace pimoroni { diff --git a/libraries/breakout_potentiometer/CMakeLists.txt b/libraries/breakout_potentiometer/CMakeLists.txt new file mode 100644 index 00000000..ec84da9c --- /dev/null +++ b/libraries/breakout_potentiometer/CMakeLists.txt @@ -0,0 +1 @@ +include(breakout_potentiometer.cmake) diff --git a/libraries/breakout_potentiometer/breakout_potentiometer.cmake b/libraries/breakout_potentiometer/breakout_potentiometer.cmake new file mode 100644 index 00000000..434970ce --- /dev/null +++ b/libraries/breakout_potentiometer/breakout_potentiometer.cmake @@ -0,0 +1,11 @@ +set(LIB_NAME breakout_potentiometer) +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 ioexpander) diff --git a/libraries/breakout_potentiometer/breakout_potentiometer.cpp b/libraries/breakout_potentiometer/breakout_potentiometer.cpp new file mode 100644 index 00000000..445d3f92 --- /dev/null +++ b/libraries/breakout_potentiometer/breakout_potentiometer.cpp @@ -0,0 +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 == 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_address(uint8_t address) { + ioe.set_address(address); + } + + float BreakoutPotentiometer::get_adc_vref() { + return ioe.get_adc_vref(); + } + + void BreakoutPotentiometer::set_adc_vref(float vref) { + ioe.set_adc_vref(vref); + } + + BreakoutPotentiometer::Direction BreakoutPotentiometer::get_direction() { + return direction; + } + + void BreakoutPotentiometer::set_direction(Direction direction) { + if(direction == 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); + } + this->direction = direction; + } + + 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 + } + +float BreakoutPotentiometer::read(uint32_t adc_timeout) { + return (ioe.input_as_voltage(POT_INPUT, adc_timeout) / ioe.get_adc_vref()); + } + + int16_t BreakoutPotentiometer::read_raw(uint32_t adc_timeout) { + return ioe.input(POT_INPUT, adc_timeout); + } +} \ No newline at end of file diff --git a/libraries/breakout_potentiometer/breakout_potentiometer.hpp b/libraries/breakout_potentiometer/breakout_potentiometer.hpp new file mode 100644 index 00000000..93263384 --- /dev/null +++ b/libraries/breakout_potentiometer/breakout_potentiometer.hpp @@ -0,0 +1,93 @@ +#pragma once + +#include "drivers/ioexpander/ioexpander.hpp" + +namespace pimoroni { + + 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 Direction 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; + Direction direction = 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_address(uint8_t address); + + float get_adc_vref(); + void set_adc_vref(float vref); + + // Potentiometer breakout specific + Direction get_direction(); + void set_direction(Direction direction); + + void set_brightness(float brightness); + void set_led(uint8_t r, uint8_t g, uint8_t b); + + float read(uint32_t adc_timeout = DEFAULT_ADC_TIMEOUT); + int16_t read_raw(uint32_t adc_timeout = DEFAULT_ADC_TIMEOUT); + }; + +} \ No newline at end of file diff --git a/micropython/examples/breakout_encoder/demo.py b/micropython/examples/breakout_encoder/demo.py new file mode 100644 index 00000000..c8337453 --- /dev/null +++ b/micropython/examples/breakout_encoder/demo.py @@ -0,0 +1,55 @@ +from breakout_encoder import BreakoutEncoder + +steps_per_rev = 24 + +enc = BreakoutEncoder() + +enc.set_brightness(1.0) +# enc.set_direction(BreakoutEncoder.DIRECTION_CCW) # Uncomment this to flip the direction + + +# From CPython Lib/colorsys.py +def hsv_to_rgb(h, s, v): + if s == 0.0: + return v, v, v + i = int(h * 6.0) + f = (h * 6.0) - i + p = v * (1.0 - s) + q = v * (1.0 - s * f) + t = v * (1.0 - s * (1.0 - f)) + i = i % 6 + if i == 0: + return v, t, p + if i == 1: + return q, v, p + if i == 2: + return p, v, t + if i == 3: + return p, q, v + if i == 4: + return t, p, v + if i == 5: + return v, p, q + + +def count_changed(count): + print("Count: ", count, sep="") + h = ((count % steps_per_rev) * 360.0) / steps_per_rev # Convert the count to a colour hue + r, g, b = [int(255 * c) for c in hsv_to_rgb(h / 360.0, 1.0, 1.0)] # rainbow magic + enc.set_led(r, g, b) + + +count = 0 + +count_changed(count) +enc.clear_interrupt_flag() + +while True: + if enc.get_interrupt_flag(): + count = enc.read() + enc.clear_interrupt_flag() + + while count < 0: + count += steps_per_rev + + count_changed(count) diff --git a/micropython/examples/breakout_ioexpander/adc.py b/micropython/examples/breakout_ioexpander/adc.py new file mode 100644 index 00000000..290a83ba --- /dev/null +++ b/micropython/examples/breakout_ioexpander/adc.py @@ -0,0 +1,14 @@ +import time +from breakout_ioexpander import BreakoutIOExpander + +ioe_adc_pin = 10 + +ioe = BreakoutIOExpander(0x18) + +ioe.set_mode(ioe_adc_pin, BreakoutIOExpander.PIN_ADC) + +while True: + voltage = ioe.input_as_voltage(ioe_adc_pin) + print("Voltage: ", voltage, sep="") + + time.sleep(0.02) diff --git a/micropython/examples/breakout_ioexpander/button.py b/micropython/examples/breakout_ioexpander/button.py new file mode 100644 index 00000000..624306a6 --- /dev/null +++ b/micropython/examples/breakout_ioexpander/button.py @@ -0,0 +1,21 @@ +import time +from breakout_ioexpander import BreakoutIOExpander + +ioe_button_pin = 14 + +ioe = BreakoutIOExpander(0x18) + +ioe.set_mode(ioe_button_pin, BreakoutIOExpander.PIN_IN_PU) + +last_state = True + +while True: + state = ioe.input(ioe_button_pin) + if state is not last_state: + if state: + print("Button has been released") + else: + print("Button has been pressed") + last_state = state + + time.sleep(0.02) diff --git a/micropython/examples/breakout_ioexpander/servo.py b/micropython/examples/breakout_ioexpander/servo.py new file mode 100644 index 00000000..7ca012fc --- /dev/null +++ b/micropython/examples/breakout_ioexpander/servo.py @@ -0,0 +1,35 @@ +import time +import math +from breakout_ioexpander import BreakoutIOExpander + +ioe_servo_pin = 1 + +# Settings to produce a 50Hz output from the 24MHz clock. +# 24,000,000 Hz / 8 = 3,000,000 Hz +# 3,000,000 Hz / 60,000 Period = 50 Hz +divider = 8 +period = 60000 +cycle_time = 5.0 +servo_range = 1000.0 # Between 1000 and 2000us (1-2ms) + +ioe = BreakoutIOExpander(0x18) + +ioe.set_pwm_period(period) +ioe.set_pwm_control(divider) + +ioe.set_mode(ioe_servo_pin, BreakoutIOExpander.PIN_PWM) + +t = 0 + +while True: + s = math.sin((t * math.pi * 2.0) / cycle_time) / 2.0 + servo_us = 1500.0 + (s * servo_range) + + duty_per_microsecond = period / (20 * 1000) # Default is 3 LSB per microsecond + + duty_cycle = round(servo_us * duty_per_microsecond) + print("Cycle Time: ", t % cycle_time, ", Pulse: ", servo_us, ", Duty Cycle: ", duty_cycle, sep="") + ioe.output(ioe_servo_pin, duty_cycle) + + time.sleep(0.02) + t += 0.02 diff --git a/micropython/examples/breakout_mics6814/demo.py b/micropython/examples/breakout_mics6814/demo.py new file mode 100644 index 00000000..b8af41c6 --- /dev/null +++ b/micropython/examples/breakout_mics6814/demo.py @@ -0,0 +1,54 @@ +import time +from breakout_mics6814 import BreakoutMICS6814 + +OUTPUT_FREQUENCY = 0.5 + +gas = BreakoutMICS6814() + +gas.set_brightness(1.0) + + +# From CPython Lib/colorsys.py +def hsv_to_rgb(h, s, v): + if s == 0.0: + return v, v, v + i = int(h * 6.0) + f = (h * 6.0) - i + p = v * (1.0 - s) + q = v * (1.0 - s * f) + t = v * (1.0 - s * (1.0 - f)) + i = i % 6 + if i == 0: + return v, t, p + if i == 1: + return q, v, p + if i == 2: + return p, v, t + if i == 3: + return p, q, v + if i == 4: + return t, p, v + if i == 5: + return v, p, q + + +h = 0 + +last_output = time.time() + +while True: + oxd = gas.read_oxidising() + red = gas.read_reducing() + nh3 = gas.read_nh3() + + r, g, b = [int(255 * c) for c in hsv_to_rgb(h / 360.0, 1.0, 1.0)] # rainbow magic + + if time.time() - last_output > OUTPUT_FREQUENCY: + print("OX: {} Red: {} NH3: {}".format(oxd, red, nh3)) + last_output = time.time() + + gas.set_led(r, g, b) + + h += 1 + + time.sleep(0.02) diff --git a/micropython/examples/breakout_potentiometer/demo.py b/micropython/examples/breakout_potentiometer/demo.py new file mode 100644 index 00000000..ec354b72 --- /dev/null +++ b/micropython/examples/breakout_potentiometer/demo.py @@ -0,0 +1,45 @@ +import time +from breakout_potentiometer import BreakoutPotentiometer + +pot = BreakoutPotentiometer() + +pot.set_brightness(1.0) +# pot.set_direction(BreakoutPotentiometer.DIRECTION_CCW) # Uncomment this to flip the direction + + +# From CPython Lib/colorsys.py +def hsv_to_rgb(h, s, v): + if s == 0.0: + return v, v, v + i = int(h * 6.0) + f = (h * 6.0) - i + p = v * (1.0 - s) + q = v * (1.0 - s * f) + t = v * (1.0 - s * (1.0 - f)) + i = i % 6 + if i == 0: + return v, t, p + if i == 1: + return q, v, p + if i == 2: + return p, v, t + if i == 3: + return p, q, v + if i == 4: + return t, p, v + if i == 5: + return v, p, q + + +val = 0 + +while True: + val = pot.read() + h = val * 240.0 # Colour range from red to blue + + r, g, b = [int(255 * c) for c in hsv_to_rgb(h / 360.0, 1.0, 1.0)] # rainbow magic + + print("Percent: ", int(val * 100), "%", sep="") + pot.set_led(r, g, b) + + time.sleep(0.02) diff --git a/micropython/modules/breakout_encoder/breakout_encoder.c b/micropython/modules/breakout_encoder/breakout_encoder.c new file mode 100644 index 00000000..dd3973a5 --- /dev/null +++ b/micropython/modules/breakout_encoder/breakout_encoder.c @@ -0,0 +1,64 @@ +#include "breakout_encoder.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// BreakoutEncoder Class +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/***** Methods *****/ +MP_DEFINE_CONST_FUN_OBJ_KW(BreakoutEncoder_set_address_obj, 2, BreakoutEncoder_set_address); +MP_DEFINE_CONST_FUN_OBJ_1(BreakoutEncoder_get_interrupt_flag_obj, BreakoutEncoder_get_interrupt_flag); +MP_DEFINE_CONST_FUN_OBJ_1(BreakoutEncoder_clear_interrupt_flag_obj, BreakoutEncoder_clear_interrupt_flag); +MP_DEFINE_CONST_FUN_OBJ_1(BreakoutEncoder_get_direction_obj, BreakoutEncoder_get_direction); +MP_DEFINE_CONST_FUN_OBJ_KW(BreakoutEncoder_set_direction_obj, 2, BreakoutEncoder_set_direction); +MP_DEFINE_CONST_FUN_OBJ_KW(BreakoutEncoder_set_brightness_obj, 2, BreakoutEncoder_set_brightness); +MP_DEFINE_CONST_FUN_OBJ_KW(BreakoutEncoder_set_led_obj, 4, BreakoutEncoder_set_led); +MP_DEFINE_CONST_FUN_OBJ_1(BreakoutEncoder_available_obj, BreakoutEncoder_available); +MP_DEFINE_CONST_FUN_OBJ_1(BreakoutEncoder_read_obj, BreakoutEncoder_read); + +/***** Binding of Methods *****/ +STATIC const mp_rom_map_elem_t BreakoutEncoder_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_set_address), MP_ROM_PTR(&BreakoutEncoder_set_address_obj) }, + { MP_ROM_QSTR(MP_QSTR_get_interrupt_flag), MP_ROM_PTR(&BreakoutEncoder_get_interrupt_flag_obj) }, + { MP_ROM_QSTR(MP_QSTR_clear_interrupt_flag), MP_ROM_PTR(&BreakoutEncoder_clear_interrupt_flag_obj) }, + { MP_ROM_QSTR(MP_QSTR_get_direction), MP_ROM_PTR(&BreakoutEncoder_get_direction_obj) }, + { MP_ROM_QSTR(MP_QSTR_set_direction), MP_ROM_PTR(&BreakoutEncoder_set_direction_obj) }, + { MP_ROM_QSTR(MP_QSTR_set_brightness), MP_ROM_PTR(&BreakoutEncoder_set_brightness_obj) }, + { MP_ROM_QSTR(MP_QSTR_set_led), MP_ROM_PTR(&BreakoutEncoder_set_led_obj) }, + { MP_ROM_QSTR(MP_QSTR_available), MP_ROM_PTR(&BreakoutEncoder_available_obj) }, + { MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&BreakoutEncoder_read_obj) }, + { MP_ROM_QSTR(MP_QSTR_DIRECTION_CW), MP_ROM_INT(1) }, + { MP_ROM_QSTR(MP_QSTR_DIRECTION_CCW), MP_ROM_INT(0) }, +}; +STATIC MP_DEFINE_CONST_DICT(BreakoutEncoder_locals_dict, BreakoutEncoder_locals_dict_table); + +/***** Class Definition *****/ +const mp_obj_type_t breakout_encoder_BreakoutEncoder_type = { + { &mp_type_type }, + .name = MP_QSTR_breakout_encoder, + .print = BreakoutEncoder_print, + .make_new = BreakoutEncoder_make_new, + .locals_dict = (mp_obj_dict_t*)&BreakoutEncoder_locals_dict, +}; + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// breakout_encoder Module +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/***** Globals Table *****/ +STATIC const mp_map_elem_t breakout_encoder_globals_table[] = { + { MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_breakout_encoder) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_BreakoutEncoder), (mp_obj_t)&breakout_encoder_BreakoutEncoder_type }, +}; +STATIC MP_DEFINE_CONST_DICT(mp_module_breakout_encoder_globals, breakout_encoder_globals_table); + +/***** Module Definition *****/ +const mp_obj_module_t breakout_encoder_user_cmodule = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t*)&mp_module_breakout_encoder_globals, +}; + +//////////////////////////////////////////////////////////////////////////////////////////////////// +MP_REGISTER_MODULE(MP_QSTR_breakout_encoder, breakout_encoder_user_cmodule, MODULE_BREAKOUT_ENCODER_ENABLED); +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////// \ No newline at end of file diff --git a/micropython/modules/breakout_encoder/breakout_encoder.cpp b/micropython/modules/breakout_encoder/breakout_encoder.cpp new file mode 100644 index 00000000..0b442df0 --- /dev/null +++ b/micropython/modules/breakout_encoder/breakout_encoder.cpp @@ -0,0 +1,224 @@ +#include "libraries/breakout_encoder/breakout_encoder.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_encoder.h" + +/***** Variables Struct *****/ +typedef struct _breakout_encoder_BreakoutEncoder_obj_t { + mp_obj_base_t base; + BreakoutEncoder *breakout; +} breakout_encoder_BreakoutEncoder_obj_t; + +/***** Print *****/ +void BreakoutEncoder_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + (void)kind; //Unused input parameter + breakout_encoder_BreakoutEncoder_obj_t *self = MP_OBJ_TO_PTR2(self_in, breakout_encoder_BreakoutEncoder_obj_t); + BreakoutEncoder* breakout = self->breakout; + mp_print_str(print, "BreakoutEncoder("); + + 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 BreakoutEncoder_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + breakout_encoder_BreakoutEncoder_obj_t *self = nullptr; + + if(n_args == 0) { + mp_arg_check_num(n_args, n_kw, 0, 0, true); + self = m_new_obj(breakout_encoder_BreakoutEncoder_obj_t); + self->base.type = &breakout_encoder_BreakoutEncoder_type; + self->breakout = new BreakoutEncoder(); + } + else if(n_args == 1) { + enum { ARG_address }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_address, MP_ARG_REQUIRED | MP_ARG_INT }, + }; + + // Parse args. + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + self = m_new_obj(breakout_encoder_BreakoutEncoder_obj_t); + self->base.type = &breakout_encoder_BreakoutEncoder_type; + + self->breakout = new BreakoutEncoder(args[ARG_address].u_int); + } + else { + enum { ARG_i2c, ARG_address, ARG_sda, ARG_scl, ARG_interrupt }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_i2c, MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_address, 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_interrupt, MP_ARG_INT, {.u_int = BreakoutEncoder::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_encoder_BreakoutEncoder_obj_t); + self->base.type = &breakout_encoder_BreakoutEncoder_type; + + i2c_inst_t *i2c = (i2c_id == 0) ? i2c0 : i2c1; + self->breakout = new BreakoutEncoder(i2c, args[ARG_address].u_int, sda, scl, args[ARG_interrupt].u_int); + } + + if(!self->breakout->init()) { + mp_raise_msg(&mp_type_RuntimeError, "Encoder breakout not found when initialising"); + } + + return MP_OBJ_FROM_PTR(self); +} + +/***** Methods *****/ +mp_obj_t BreakoutEncoder_set_address(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_self, ARG_address }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_address, 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); + + breakout_encoder_BreakoutEncoder_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, breakout_encoder_BreakoutEncoder_obj_t); + + self->breakout->set_address(args[ARG_address].u_int); + + return mp_const_none; +} + +mp_obj_t BreakoutEncoder_get_interrupt_flag(mp_obj_t self_in) { + breakout_encoder_BreakoutEncoder_obj_t *self = MP_OBJ_TO_PTR2(self_in, breakout_encoder_BreakoutEncoder_obj_t); + return mp_obj_new_bool(self->breakout->get_interrupt_flag()); +} + +mp_obj_t BreakoutEncoder_clear_interrupt_flag(mp_obj_t self_in) { + breakout_encoder_BreakoutEncoder_obj_t *self = MP_OBJ_TO_PTR2(self_in, breakout_encoder_BreakoutEncoder_obj_t); + self->breakout->clear_interrupt_flag(); + + return mp_const_none; +} + +mp_obj_t BreakoutEncoder_get_direction(mp_obj_t self_in) { + breakout_encoder_BreakoutEncoder_obj_t *self = MP_OBJ_TO_PTR2(self_in, breakout_encoder_BreakoutEncoder_obj_t); + return mp_obj_new_bool(self->breakout->get_direction()); +} + +mp_obj_t BreakoutEncoder_set_direction(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_self, ARG_clockwise }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_clockwise, 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_encoder_BreakoutEncoder_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, breakout_encoder_BreakoutEncoder_obj_t); + + self->breakout->set_direction(args[ARG_clockwise].u_bool ? BreakoutEncoder::DIRECTION_CW : BreakoutEncoder::DIRECTION_CCW); + + return mp_const_none; +} + +mp_obj_t BreakoutEncoder_set_brightness(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_self, ARG_brightness }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_brightness, 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); + + breakout_encoder_BreakoutEncoder_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, breakout_encoder_BreakoutEncoder_obj_t); + + float brightness = mp_obj_get_float(args[ARG_brightness].u_obj); + if(brightness < 0 || brightness > 1.0f) + mp_raise_ValueError("brightness out of range. Expected 0.0 to 1.0"); + else + self->breakout->set_brightness(brightness); + + return mp_const_none; +} + +mp_obj_t BreakoutEncoder_set_led(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_self, ARG_r, ARG_g, ARG_b, ARG_w }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_r, MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_g, MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_b, 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); + + breakout_encoder_BreakoutEncoder_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, breakout_encoder_BreakoutEncoder_obj_t); + + int r = args[ARG_r].u_int; + int g = args[ARG_g].u_int; + int b = args[ARG_b].u_int; + + if(r < 0 || r > 255) + mp_raise_ValueError("r out of range. Expected 0 to 255"); + else if(g < 0 || g > 255) + mp_raise_ValueError("g out of range. Expected 0 to 255"); + else if(b < 0 || b > 255) + mp_raise_ValueError("b out of range. Expected 0 to 255"); + else + self->breakout->set_led(r, g, b); + + return mp_const_none; +} + +mp_obj_t BreakoutEncoder_available(mp_obj_t self_in) { + breakout_encoder_BreakoutEncoder_obj_t *self = MP_OBJ_TO_PTR2(self_in, breakout_encoder_BreakoutEncoder_obj_t); + return mp_obj_new_bool(self->breakout->available()); +} + +mp_obj_t BreakoutEncoder_read(mp_obj_t self_in) { + breakout_encoder_BreakoutEncoder_obj_t *self = MP_OBJ_TO_PTR2(self_in, breakout_encoder_BreakoutEncoder_obj_t); + return mp_obj_new_int(self->breakout->read()); +} +} \ No newline at end of file diff --git a/micropython/modules/breakout_encoder/breakout_encoder.h b/micropython/modules/breakout_encoder/breakout_encoder.h new file mode 100644 index 00000000..50c88e0a --- /dev/null +++ b/micropython/modules/breakout_encoder/breakout_encoder.h @@ -0,0 +1,18 @@ +// Include MicroPython API. +#include "py/runtime.h" + +/***** Extern of Class Definition *****/ +extern const mp_obj_type_t breakout_encoder_BreakoutEncoder_type; + +/***** Extern of Class Methods *****/ +extern void BreakoutEncoder_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind); +extern mp_obj_t BreakoutEncoder_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 BreakoutEncoder_set_address(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); +extern mp_obj_t BreakoutEncoder_get_interrupt_flag(mp_obj_t self_in); +extern mp_obj_t BreakoutEncoder_clear_interrupt_flag(mp_obj_t self_in); +extern mp_obj_t BreakoutEncoder_get_direction(mp_obj_t self_in); +extern mp_obj_t BreakoutEncoder_set_direction(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); +extern mp_obj_t BreakoutEncoder_set_brightness(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); +extern mp_obj_t BreakoutEncoder_set_led(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); +extern mp_obj_t BreakoutEncoder_available(mp_obj_t self_in); +extern mp_obj_t BreakoutEncoder_read(mp_obj_t self_in); \ No newline at end of file diff --git a/micropython/modules/breakout_encoder/micropython.cmake b/micropython/modules/breakout_encoder/micropython.cmake new file mode 100644 index 00000000..f1d80836 --- /dev/null +++ b/micropython/modules/breakout_encoder/micropython.cmake @@ -0,0 +1,20 @@ +set(MOD_NAME breakout_encoder) +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/ioexpander/ioexpander.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_encoder/micropython.mk b/micropython/modules/breakout_encoder/micropython.mk new file mode 100755 index 00000000..674b2889 --- /dev/null +++ b/micropython/modules/breakout_encoder/micropython.mk @@ -0,0 +1,13 @@ +set(MOD_NAME breakout_encoder) +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/breakout_ioexpander/breakout_ioexpander.c b/micropython/modules/breakout_ioexpander/breakout_ioexpander.c new file mode 100644 index 00000000..3ee8e6e4 --- /dev/null +++ b/micropython/modules/breakout_ioexpander/breakout_ioexpander.c @@ -0,0 +1,95 @@ +#include "breakout_ioexpander.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// BreakoutIOExpander Class +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/***** Methods *****/ +MP_DEFINE_CONST_FUN_OBJ_1(BreakoutIOExpander_get_chip_id_obj, BreakoutIOExpander_get_chip_id); +MP_DEFINE_CONST_FUN_OBJ_KW(BreakoutIOExpander_set_address_obj, 2, BreakoutIOExpander_set_address); +MP_DEFINE_CONST_FUN_OBJ_1(BreakoutIOExpander_get_adc_vref_obj, BreakoutIOExpander_get_adc_vref); +MP_DEFINE_CONST_FUN_OBJ_KW(BreakoutIOExpander_set_adc_vref_obj, 2, BreakoutIOExpander_set_adc_vref); +MP_DEFINE_CONST_FUN_OBJ_KW(BreakoutIOExpander_enable_interrupt_out_obj, 1, BreakoutIOExpander_enable_interrupt_out); +MP_DEFINE_CONST_FUN_OBJ_1(BreakoutIOExpander_disable_interrupt_out_obj, BreakoutIOExpander_disable_interrupt_out); +MP_DEFINE_CONST_FUN_OBJ_1(BreakoutIOExpander_get_interrupt_flag_obj, BreakoutIOExpander_get_interrupt_flag); +MP_DEFINE_CONST_FUN_OBJ_1(BreakoutIOExpander_clear_interrupt_flag_obj, BreakoutIOExpander_clear_interrupt_flag); +MP_DEFINE_CONST_FUN_OBJ_KW(BreakoutIOExpander_set_pin_interrupt_obj, 3, BreakoutIOExpander_set_pin_interrupt); +MP_DEFINE_CONST_FUN_OBJ_KW(BreakoutIOExpander_pwm_load_obj, 1, BreakoutIOExpander_pwm_load); +MP_DEFINE_CONST_FUN_OBJ_1(BreakoutIOExpander_pwm_loading_obj, BreakoutIOExpander_pwm_loading); +MP_DEFINE_CONST_FUN_OBJ_KW(BreakoutIOExpander_pwm_clear_obj, 1, BreakoutIOExpander_pwm_clear); +MP_DEFINE_CONST_FUN_OBJ_1(BreakoutIOExpander_pwm_clearing_obj, BreakoutIOExpander_pwm_clearing); +MP_DEFINE_CONST_FUN_OBJ_KW(BreakoutIOExpander_set_pwm_control_obj, 2, BreakoutIOExpander_set_pwm_control); +MP_DEFINE_CONST_FUN_OBJ_KW(BreakoutIOExpander_set_pwm_period_obj, 2, BreakoutIOExpander_set_pwm_period); +MP_DEFINE_CONST_FUN_OBJ_KW(BreakoutIOExpander_set_mode_obj, 3, BreakoutIOExpander_set_mode); +MP_DEFINE_CONST_FUN_OBJ_KW(BreakoutIOExpander_input_obj, 2, BreakoutIOExpander_input); +MP_DEFINE_CONST_FUN_OBJ_KW(BreakoutIOExpander_input_as_voltage_obj, 2, BreakoutIOExpander_input_as_voltage); +MP_DEFINE_CONST_FUN_OBJ_KW(BreakoutIOExpander_output_obj, 3, BreakoutIOExpander_output); +MP_DEFINE_CONST_FUN_OBJ_KW(BreakoutIOExpander_setup_rotary_encoder_obj, 4, BreakoutIOExpander_setup_rotary_encoder); +MP_DEFINE_CONST_FUN_OBJ_KW(BreakoutIOExpander_read_rotary_encoder_obj, 2, BreakoutIOExpander_read_rotary_encoder); + +/***** Binding of Methods *****/ +STATIC const mp_rom_map_elem_t BreakoutIOExpander_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_get_chip_id), MP_ROM_PTR(&BreakoutIOExpander_get_chip_id_obj) }, + { MP_ROM_QSTR(MP_QSTR_set_address), MP_ROM_PTR(&BreakoutIOExpander_set_address_obj) }, + { MP_ROM_QSTR(MP_QSTR_get_adc_vref), MP_ROM_PTR(&BreakoutIOExpander_get_adc_vref_obj) }, + { MP_ROM_QSTR(MP_QSTR_set_adc_vref), MP_ROM_PTR(&BreakoutIOExpander_set_adc_vref_obj) }, + { MP_ROM_QSTR(MP_QSTR_enable_interrupt_out), MP_ROM_PTR(&BreakoutIOExpander_enable_interrupt_out_obj) }, + { MP_ROM_QSTR(MP_QSTR_disable_interrupt_out), MP_ROM_PTR(&BreakoutIOExpander_disable_interrupt_out_obj) }, + { MP_ROM_QSTR(MP_QSTR_get_interrupt_flag), MP_ROM_PTR(&BreakoutIOExpander_get_interrupt_flag_obj) }, + { MP_ROM_QSTR(MP_QSTR_clear_interrupt_flag), MP_ROM_PTR(&BreakoutIOExpander_clear_interrupt_flag_obj) }, + { MP_ROM_QSTR(MP_QSTR_set_pin_interrupt), MP_ROM_PTR(&BreakoutIOExpander_set_pin_interrupt_obj) }, + { MP_ROM_QSTR(MP_QSTR_pwm_load), MP_ROM_PTR(&BreakoutIOExpander_pwm_load_obj) }, + { MP_ROM_QSTR(MP_QSTR_pwm_loading), MP_ROM_PTR(&BreakoutIOExpander_pwm_loading_obj) }, + { MP_ROM_QSTR(MP_QSTR_pwm_clear), MP_ROM_PTR(&BreakoutIOExpander_pwm_clear_obj) }, + { MP_ROM_QSTR(MP_QSTR_pwm_clearing), MP_ROM_PTR(&BreakoutIOExpander_pwm_clearing_obj) }, + { MP_ROM_QSTR(MP_QSTR_set_pwm_control), MP_ROM_PTR(&BreakoutIOExpander_set_pwm_control_obj) }, + { MP_ROM_QSTR(MP_QSTR_set_pwm_period), MP_ROM_PTR(&BreakoutIOExpander_set_pwm_period_obj) }, + { MP_ROM_QSTR(MP_QSTR_set_mode), MP_ROM_PTR(&BreakoutIOExpander_set_mode_obj) }, + { MP_ROM_QSTR(MP_QSTR_input), MP_ROM_PTR(&BreakoutIOExpander_input_obj) }, + { MP_ROM_QSTR(MP_QSTR_input_as_voltage), MP_ROM_PTR(&BreakoutIOExpander_input_as_voltage_obj) }, + { MP_ROM_QSTR(MP_QSTR_output), MP_ROM_PTR(&BreakoutIOExpander_output_obj) }, + { MP_ROM_QSTR(MP_QSTR_setup_rotary_encoder), MP_ROM_PTR(&BreakoutIOExpander_setup_rotary_encoder_obj) }, + { MP_ROM_QSTR(MP_QSTR_read_rotary_encoder), MP_ROM_PTR(&BreakoutIOExpander_read_rotary_encoder_obj) }, + { MP_ROM_QSTR(MP_QSTR_PIN_IN), MP_ROM_INT(IOE_PIN_IN) }, + { MP_ROM_QSTR(MP_QSTR_PIN_IN_PU), MP_ROM_INT(IOE_PIN_IN_PU) }, + { MP_ROM_QSTR(MP_QSTR_PIN_OUT), MP_ROM_INT(IOE_PIN_OUT) }, + { MP_ROM_QSTR(MP_QSTR_PIN_OD), MP_ROM_INT(IOE_PIN_OD) }, + { MP_ROM_QSTR(MP_QSTR_PIN_PWM), MP_ROM_INT(IOE_PIN_PWM) }, + { MP_ROM_QSTR(MP_QSTR_PIN_ADC), MP_ROM_INT(IOE_PIN_ADC) }, + { MP_ROM_QSTR(MP_QSTR_NUM_PINS), MP_ROM_INT(IOE_NUM_PINS) }, + { MP_ROM_QSTR(MP_QSTR_LOW), MP_ROM_INT(IOE_LOW) }, + { MP_ROM_QSTR(MP_QSTR_HIGH), MP_ROM_INT(IOE_HIGH) }, +}; +STATIC MP_DEFINE_CONST_DICT(BreakoutIOExpander_locals_dict, BreakoutIOExpander_locals_dict_table); + +/***** Class Definition *****/ +const mp_obj_type_t breakout_ioexpander_BreakoutIOExpander_type = { + { &mp_type_type }, + .name = MP_QSTR_breakout_ioexpander, + .print = BreakoutIOExpander_print, + .make_new = BreakoutIOExpander_make_new, + .locals_dict = (mp_obj_dict_t*)&BreakoutIOExpander_locals_dict, +}; + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// breakout_ioexpander Module +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/***** Globals Table *****/ +STATIC const mp_map_elem_t breakout_ioexpander_globals_table[] = { + { MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_breakout_ioexpander) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_BreakoutIOExpander), (mp_obj_t)&breakout_ioexpander_BreakoutIOExpander_type }, +}; +STATIC MP_DEFINE_CONST_DICT(mp_module_breakout_ioexpander_globals, breakout_ioexpander_globals_table); + +/***** Module Definition *****/ +const mp_obj_module_t breakout_ioexpander_user_cmodule = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t*)&mp_module_breakout_ioexpander_globals, +}; + +//////////////////////////////////////////////////////////////////////////////////////////////////// +MP_REGISTER_MODULE(MP_QSTR_breakout_ioexpander, breakout_ioexpander_user_cmodule, MODULE_BREAKOUT_IOEXPANDER_ENABLED); +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////// \ No newline at end of file diff --git a/micropython/modules/breakout_ioexpander/breakout_ioexpander.cpp b/micropython/modules/breakout_ioexpander/breakout_ioexpander.cpp new file mode 100644 index 00000000..785caf58 --- /dev/null +++ b/micropython/modules/breakout_ioexpander/breakout_ioexpander.cpp @@ -0,0 +1,482 @@ +#include "libraries/breakout_ioexpander/breakout_ioexpander.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_ioexpander.h" + +/***** Variables Struct *****/ +typedef struct _breakout_ioexpander_BreakoutIOExpander_obj_t { + mp_obj_base_t base; + BreakoutIOExpander *breakout; +} breakout_ioexpander_BreakoutIOExpander_obj_t; + +/***** Print *****/ +void BreakoutIOExpander_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + (void)kind; //Unused input parameter + breakout_ioexpander_BreakoutIOExpander_obj_t *self = MP_OBJ_TO_PTR2(self_in, breakout_ioexpander_BreakoutIOExpander_obj_t); + BreakoutIOExpander* breakout = self->breakout; + mp_print_str(print, "BreakoutIOExpander("); + + 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 BreakoutIOExpander_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + breakout_ioexpander_BreakoutIOExpander_obj_t *self = nullptr; + + if(n_args == 0) { + mp_arg_check_num(n_args, n_kw, 0, 0, true); + self = m_new_obj(breakout_ioexpander_BreakoutIOExpander_obj_t); + self->base.type = &breakout_ioexpander_BreakoutIOExpander_type; + self->breakout = new BreakoutIOExpander(); + } + else if(n_args == 1) { + enum { ARG_address }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_address, MP_ARG_REQUIRED | MP_ARG_INT }, + }; + + // Parse args. + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + self = m_new_obj(breakout_ioexpander_BreakoutIOExpander_obj_t); + self->base.type = &breakout_ioexpander_BreakoutIOExpander_type; + + self->breakout = new BreakoutIOExpander(args[ARG_address].u_int); + } + else { + enum { ARG_i2c, ARG_address, ARG_sda, ARG_scl, ARG_interrupt }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_i2c, MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_address, 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_interrupt, MP_ARG_INT, {.u_int = BreakoutIOExpander::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_ioexpander_BreakoutIOExpander_obj_t); + self->base.type = &breakout_ioexpander_BreakoutIOExpander_type; + + i2c_inst_t *i2c = (i2c_id == 0) ? i2c0 : i2c1; + self->breakout = new BreakoutIOExpander(i2c, args[ARG_address].u_int, sda, scl, args[ARG_interrupt].u_int); + } + + if(!self->breakout->init()) { + mp_raise_msg(&mp_type_RuntimeError, "IOExpander breakout not found when initialising"); + } + + return MP_OBJ_FROM_PTR(self); +} + +/***** Methods *****/ +mp_obj_t BreakoutIOExpander_get_chip_id(mp_obj_t self_in) { + breakout_ioexpander_BreakoutIOExpander_obj_t *self = MP_OBJ_TO_PTR2(self_in, breakout_ioexpander_BreakoutIOExpander_obj_t); + return mp_obj_new_int(self->breakout->get_chip_id()); +} + +mp_obj_t BreakoutIOExpander_set_address(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_self, ARG_address }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_address, 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); + + breakout_ioexpander_BreakoutIOExpander_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, breakout_ioexpander_BreakoutIOExpander_obj_t); + + self->breakout->set_address(args[ARG_address].u_int); + + return mp_const_none; +} + +mp_obj_t BreakoutIOExpander_get_adc_vref(mp_obj_t self_in) { + breakout_ioexpander_BreakoutIOExpander_obj_t *self = MP_OBJ_TO_PTR2(self_in, breakout_ioexpander_BreakoutIOExpander_obj_t); + return mp_obj_new_float(self->breakout->get_adc_vref()); +} + +mp_obj_t BreakoutIOExpander_set_adc_vref(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_self, ARG_vref }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_vref, 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); + + breakout_ioexpander_BreakoutIOExpander_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, breakout_ioexpander_BreakoutIOExpander_obj_t); + + float vref = mp_obj_get_float(args[ARG_vref].u_obj); + self->breakout->set_adc_vref(vref); + + return mp_const_none; +} + +mp_obj_t BreakoutIOExpander_enable_interrupt_out(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_self, ARG_pin_swap }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_pin_swap, MP_ARG_BOOL, {.u_bool = false} }, + }; + + 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_ioexpander_BreakoutIOExpander_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, breakout_ioexpander_BreakoutIOExpander_obj_t); + + bool pin_swap = args[ARG_pin_swap].u_bool; + self->breakout->enable_interrupt_out(pin_swap); + + return mp_const_none; +} + +mp_obj_t BreakoutIOExpander_disable_interrupt_out(mp_obj_t self_in) { + breakout_ioexpander_BreakoutIOExpander_obj_t *self = MP_OBJ_TO_PTR2(self_in, breakout_ioexpander_BreakoutIOExpander_obj_t); + self->breakout->disable_interrupt_out(); + + return mp_const_none; +} + +mp_obj_t BreakoutIOExpander_get_interrupt_flag(mp_obj_t self_in) { + breakout_ioexpander_BreakoutIOExpander_obj_t *self = MP_OBJ_TO_PTR2(self_in, breakout_ioexpander_BreakoutIOExpander_obj_t); + return mp_obj_new_bool(self->breakout->get_interrupt_flag()); +} + +mp_obj_t BreakoutIOExpander_clear_interrupt_flag(mp_obj_t self_in) { + breakout_ioexpander_BreakoutIOExpander_obj_t *self = MP_OBJ_TO_PTR2(self_in, breakout_ioexpander_BreakoutIOExpander_obj_t); + self->breakout->clear_interrupt_flag(); + + return mp_const_none; +} + +mp_obj_t BreakoutIOExpander_set_pin_interrupt(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_self, ARG_pin, ARG_enabled }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_exp_pin, MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_enabled, 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_ioexpander_BreakoutIOExpander_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, breakout_ioexpander_BreakoutIOExpander_obj_t); + + int pin = args[ARG_pin].u_int; + bool enabled = args[ARG_enabled].u_bool; + if(!self->breakout->set_pin_interrupt(pin, enabled)) { + mp_raise_ValueError("pin out of range. Expected 1 to 14"); + } + + return mp_const_none; +} + +mp_obj_t BreakoutIOExpander_pwm_load(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_self, ARG_wait_for_load }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_wait_for_load, MP_ARG_BOOL, {.u_bool = true} }, + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + breakout_ioexpander_BreakoutIOExpander_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, breakout_ioexpander_BreakoutIOExpander_obj_t); + + bool wait_for_load = args[ARG_wait_for_load].u_bool; + self->breakout->pwm_load(wait_for_load); + + return mp_const_none; +} + +mp_obj_t BreakoutIOExpander_pwm_loading(mp_obj_t self_in) { + breakout_ioexpander_BreakoutIOExpander_obj_t *self = MP_OBJ_TO_PTR2(self_in, breakout_ioexpander_BreakoutIOExpander_obj_t); + return mp_obj_new_bool(self->breakout->pwm_loading()); +} + +mp_obj_t BreakoutIOExpander_pwm_clear(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_self, ARG_wait_for_clear }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_wait_for_clear, MP_ARG_BOOL, {.u_bool = true} }, + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + breakout_ioexpander_BreakoutIOExpander_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, breakout_ioexpander_BreakoutIOExpander_obj_t); + + bool wait_for_clear = args[ARG_wait_for_clear].u_bool; + self->breakout->pwm_clear(wait_for_clear); + + return mp_const_none; +} + +mp_obj_t BreakoutIOExpander_pwm_clearing(mp_obj_t self_in) { + breakout_ioexpander_BreakoutIOExpander_obj_t *self = MP_OBJ_TO_PTR2(self_in, breakout_ioexpander_BreakoutIOExpander_obj_t); + return mp_obj_new_bool(self->breakout->pwm_clearing()); +} + +mp_obj_t BreakoutIOExpander_set_pwm_control(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_self, ARG_divider }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_divider, 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); + + breakout_ioexpander_BreakoutIOExpander_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, breakout_ioexpander_BreakoutIOExpander_obj_t); + + int divider = args[ARG_divider].u_int; + if(!self->breakout->set_pwm_control(divider)) { + mp_raise_ValueError("divider not valid. Available options are: 1, 2, 4, 8, 16, 32, 64, 128"); + } + + return mp_const_none; +} + +mp_obj_t BreakoutIOExpander_set_pwm_period(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_self, ARG_value, ARG_load }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_value, MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_load, MP_ARG_BOOL, {.u_bool = true} }, + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + breakout_ioexpander_BreakoutIOExpander_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, breakout_ioexpander_BreakoutIOExpander_obj_t); + + int value = args[ARG_value].u_int; + bool load = args[ARG_load].u_bool; + if(value < 0 || value > 65535) + mp_raise_ValueError("value out of range. Expected 0 to 65535"); + else + self->breakout->set_pwm_period(value, load); + + return mp_const_none; +} + +mp_obj_t BreakoutIOExpander_get_mode(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_self, ARG_pin, ARG_enabled }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_exp_pin, 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); + + breakout_ioexpander_BreakoutIOExpander_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, breakout_ioexpander_BreakoutIOExpander_obj_t); + + int pin = args[ARG_pin].u_int; + uint8_t mode = self->breakout->get_mode(pin); + if(mode == UINT8_MAX) + mp_raise_ValueError("pin out of range. Expected 1 to 14"); + + return mp_obj_new_int(mode); +} + +mp_obj_t BreakoutIOExpander_set_mode(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_self, ARG_pin, ARG_mode, ARG_schmitt_trigger, ARG_invert }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_exp_pin, MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_mode, MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_schmitt_trigger, MP_ARG_BOOL, {.u_bool = false} }, + { MP_QSTR_invert, MP_ARG_BOOL, {.u_bool = false} }, + }; + + 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_ioexpander_BreakoutIOExpander_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, breakout_ioexpander_BreakoutIOExpander_obj_t); + + int pin = args[ARG_pin].u_int; + int mode = args[ARG_mode].u_int; + bool schmitt_trigger = args[ARG_schmitt_trigger].u_bool; + bool invert = args[ARG_invert].u_bool; + + if(pin < 1 || pin > IOExpander::NUM_PINS) + mp_raise_ValueError("pin out of range. Expected 1 to 14"); + else + self->breakout->set_mode(pin, mode, schmitt_trigger, invert); + + return mp_const_none; +} + +mp_obj_t BreakoutIOExpander_input(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_self, ARG_pin }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_exp_pin, 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); + + breakout_ioexpander_BreakoutIOExpander_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, breakout_ioexpander_BreakoutIOExpander_obj_t); + + int pin = args[ARG_pin].u_int; + + if(pin < 1 || pin > IOExpander::NUM_PINS) + mp_raise_ValueError("pin out of range. Expected 1 to 14"); + else + return mp_obj_new_int(self->breakout->input(pin)); + + return mp_const_none; +} + +mp_obj_t BreakoutIOExpander_input_as_voltage(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_self, ARG_pin }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_exp_pin, 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); + + breakout_ioexpander_BreakoutIOExpander_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, breakout_ioexpander_BreakoutIOExpander_obj_t); + + int pin = args[ARG_pin].u_int; + + if(pin < 1 || pin > IOExpander::NUM_PINS) + mp_raise_ValueError("pin out of range. Expected 1 to 14"); + else + return mp_obj_new_float(self->breakout->input_as_voltage(pin)); + + return mp_const_none; +} + +mp_obj_t BreakoutIOExpander_output(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_self, ARG_pin, ARG_value, ARG_load }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_exp_pin, MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_value, MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_load, MP_ARG_BOOL, {.u_bool = true} }, + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + breakout_ioexpander_BreakoutIOExpander_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, breakout_ioexpander_BreakoutIOExpander_obj_t); + + int pin = args[ARG_pin].u_int; + int value = args[ARG_value].u_int; + bool load = args[ARG_load].u_bool; + + if(pin < 1 || pin > IOExpander::NUM_PINS) + mp_raise_ValueError("pin out of range. Expected 1 to 14"); + else if(value < 0 || value > 65535) + mp_raise_ValueError("value out of range. Expected 0 to 65535"); + else + self->breakout->output(pin, value, load); + + return mp_const_none; +} + +mp_obj_t BreakoutIOExpander_setup_rotary_encoder(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_self, ARG_channel, ARG_pin_a, ARG_pin_b, ARG_pin_c, ARG_count_microsteps }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_channel, MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_pin_a, MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_pin_b, MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_pin_c, MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_count_microsteps, MP_ARG_BOOL, {.u_bool = false} }, + }; + + 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_ioexpander_BreakoutIOExpander_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, breakout_ioexpander_BreakoutIOExpander_obj_t); + + int channel = args[ARG_channel].u_int; + int pin_a = args[ARG_pin_a].u_int; + int pin_b = args[ARG_pin_b].u_int; + int pin_c = args[ARG_pin_c].u_int; + bool count_microsteps = args[ARG_count_microsteps].u_bool; + + if(channel < 1 || channel > 4) + mp_raise_ValueError("channel out of range. Expected 1 to 4"); + else if(pin_a < 1 || pin_a > 14) + mp_raise_ValueError("pin_a out of range. Expected 1 to 14"); + else if(pin_b < 1 || pin_b > 14) + mp_raise_ValueError("pin_b out of range. Expected 1 to 14"); + else if(pin_c < 1 || pin_c > 14) + mp_raise_ValueError("pin_c out of range. Expected 1 to 14"); + else + self->breakout->setup_rotary_encoder(channel, pin_a, pin_b, pin_c, count_microsteps); + + return mp_const_none; +} + +mp_obj_t BreakoutIOExpander_read_rotary_encoder(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_self, ARG_channel, ARG_value, ARG_load }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_channel, 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); + + breakout_ioexpander_BreakoutIOExpander_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, breakout_ioexpander_BreakoutIOExpander_obj_t); + + int channel = args[ARG_channel].u_int; + if(channel < 1 || channel > 4) + mp_raise_ValueError("channel out of range. Expected 1 to 4"); + else + return mp_obj_new_int(self->breakout->read_rotary_encoder(channel)); + + return mp_const_none; +} +} \ No newline at end of file diff --git a/micropython/modules/breakout_ioexpander/breakout_ioexpander.h b/micropython/modules/breakout_ioexpander/breakout_ioexpander.h new file mode 100644 index 00000000..7d8a48e7 --- /dev/null +++ b/micropython/modules/breakout_ioexpander/breakout_ioexpander.h @@ -0,0 +1,45 @@ +// Include MicroPython API. +#include "py/runtime.h" + +enum { + IOE_PIN_IN = 0b00010, + IOE_PIN_IN_PU = 0b10000, + IOE_PIN_OUT = 0b00001, + IOE_PIN_OD = 0b00011, + IOE_PIN_PWM = 0b00101, + IOE_PIN_ADC = 0b01010, + + IOE_NUM_PINS = 14, + + IOE_LOW = 0, + IOE_HIGH = 1, +}; + +/***** Extern of Class Definition *****/ +extern const mp_obj_type_t breakout_ioexpander_BreakoutIOExpander_type; + +/***** Extern of Class Methods *****/ +extern void BreakoutIOExpander_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind); +extern mp_obj_t BreakoutIOExpander_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 BreakoutIOExpander_get_chip_id(mp_obj_t self_in); +extern mp_obj_t BreakoutIOExpander_set_address(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); +extern mp_obj_t BreakoutIOExpander_get_adc_vref(mp_obj_t self_in); +extern mp_obj_t BreakoutIOExpander_set_adc_vref(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); +extern mp_obj_t BreakoutIOExpander_enable_interrupt_out(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); +extern mp_obj_t BreakoutIOExpander_disable_interrupt_out(mp_obj_t self_in); +extern mp_obj_t BreakoutIOExpander_get_interrupt_flag(mp_obj_t self_in); +extern mp_obj_t BreakoutIOExpander_clear_interrupt_flag(mp_obj_t self_in); +extern mp_obj_t BreakoutIOExpander_set_pin_interrupt(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); +extern mp_obj_t BreakoutIOExpander_pwm_load(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); +extern mp_obj_t BreakoutIOExpander_pwm_loading(mp_obj_t self_in); +extern mp_obj_t BreakoutIOExpander_pwm_clear(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); +extern mp_obj_t BreakoutIOExpander_pwm_clearing(mp_obj_t self_in); +extern mp_obj_t BreakoutIOExpander_set_pwm_control(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); +extern mp_obj_t BreakoutIOExpander_set_pwm_period(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); +extern mp_obj_t BreakoutIOExpander_get_mode(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); +extern mp_obj_t BreakoutIOExpander_set_mode(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); +extern mp_obj_t BreakoutIOExpander_input(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); +extern mp_obj_t BreakoutIOExpander_input_as_voltage(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); +extern mp_obj_t BreakoutIOExpander_output(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); +extern mp_obj_t BreakoutIOExpander_setup_rotary_encoder(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); +extern mp_obj_t BreakoutIOExpander_read_rotary_encoder(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_ioexpander/micropython.cmake b/micropython/modules/breakout_ioexpander/micropython.cmake new file mode 100644 index 00000000..e41f93ef --- /dev/null +++ b/micropython/modules/breakout_ioexpander/micropython.cmake @@ -0,0 +1,20 @@ +set(MOD_NAME breakout_ioexpander) +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/ioexpander/ioexpander.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_ioexpander/micropython.mk b/micropython/modules/breakout_ioexpander/micropython.mk new file mode 100755 index 00000000..f632c809 --- /dev/null +++ b/micropython/modules/breakout_ioexpander/micropython.mk @@ -0,0 +1,13 @@ +set(MOD_NAME breakout_ioexpander) +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/breakout_mics6814/breakout_mics6814.c b/micropython/modules/breakout_mics6814/breakout_mics6814.c new file mode 100644 index 00000000..6f657490 --- /dev/null +++ b/micropython/modules/breakout_mics6814/breakout_mics6814.c @@ -0,0 +1,80 @@ +#include "breakout_mics6814.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// BreakoutMICS6814 Class +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/***** Methods *****/ +MP_DEFINE_CONST_FUN_OBJ_KW(BreakoutMICS6814_set_address_obj, 2, BreakoutMICS6814_set_address); +MP_DEFINE_CONST_FUN_OBJ_1(BreakoutMICS6814_get_adc_vref_obj, BreakoutMICS6814_get_adc_vref); +MP_DEFINE_CONST_FUN_OBJ_KW(BreakoutMICS6814_set_adc_vref_obj, 2, BreakoutMICS6814_set_adc_vref); +MP_DEFINE_CONST_FUN_OBJ_KW(BreakoutMICS6814_set_brightness_obj, 2, BreakoutMICS6814_set_brightness); +MP_DEFINE_CONST_FUN_OBJ_KW(BreakoutMICS6814_set_led_obj, 4, BreakoutMICS6814_set_led); +MP_DEFINE_CONST_FUN_OBJ_KW(BreakoutMICS6814_set_heater_obj, 2, BreakoutMICS6814_set_heater); +MP_DEFINE_CONST_FUN_OBJ_1(BreakoutMICS6814_disable_heater_obj, BreakoutMICS6814_disable_heater); +MP_DEFINE_CONST_FUN_OBJ_1(BreakoutMICS6814_get_raw_ref_obj, BreakoutMICS6814_get_raw_ref); +MP_DEFINE_CONST_FUN_OBJ_1(BreakoutMICS6814_get_raw_red_obj, BreakoutMICS6814_get_raw_red); +MP_DEFINE_CONST_FUN_OBJ_1(BreakoutMICS6814_get_raw_nh3_obj, BreakoutMICS6814_get_raw_nh3); +MP_DEFINE_CONST_FUN_OBJ_1(BreakoutMICS6814_get_raw_oxd_obj, BreakoutMICS6814_get_raw_oxd); +MP_DEFINE_CONST_FUN_OBJ_1(BreakoutMICS6814_read_all_obj, BreakoutMICS6814_read_all); +MP_DEFINE_CONST_FUN_OBJ_1(BreakoutMICS6814_read_ref_obj, BreakoutMICS6814_read_ref); +MP_DEFINE_CONST_FUN_OBJ_1(BreakoutMICS6814_read_reducing_obj, BreakoutMICS6814_read_reducing); +MP_DEFINE_CONST_FUN_OBJ_1(BreakoutMICS6814_read_nh3_obj, BreakoutMICS6814_read_nh3); +MP_DEFINE_CONST_FUN_OBJ_1(BreakoutMICS6814_read_oxidising_obj, BreakoutMICS6814_read_oxidising); + +/***** Binding of Methods *****/ +STATIC const mp_rom_map_elem_t BreakoutMICS6814_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_set_address), MP_ROM_PTR(&BreakoutMICS6814_set_address_obj) }, + { MP_ROM_QSTR(MP_QSTR_get_adc_vref), MP_ROM_PTR(&BreakoutMICS6814_get_adc_vref_obj) }, + { MP_ROM_QSTR(MP_QSTR_set_adc_vref), MP_ROM_PTR(&BreakoutMICS6814_set_adc_vref_obj) }, + { MP_ROM_QSTR(MP_QSTR_set_brightness), MP_ROM_PTR(&BreakoutMICS6814_set_brightness_obj) }, + { MP_ROM_QSTR(MP_QSTR_set_led), MP_ROM_PTR(&BreakoutMICS6814_set_led_obj) }, + { MP_ROM_QSTR(MP_QSTR_set_heater), MP_ROM_PTR(&BreakoutMICS6814_set_heater_obj) }, + { MP_ROM_QSTR(MP_QSTR_disable_heater), MP_ROM_PTR(&BreakoutMICS6814_disable_heater_obj) }, + { MP_ROM_QSTR(MP_QSTR_get_raw_ref), MP_ROM_PTR(&BreakoutMICS6814_get_raw_ref_obj) }, + { MP_ROM_QSTR(MP_QSTR_get_raw_red), MP_ROM_PTR(&BreakoutMICS6814_get_raw_red_obj) }, + { MP_ROM_QSTR(MP_QSTR_get_raw_nh3), MP_ROM_PTR(&BreakoutMICS6814_get_raw_nh3_obj) }, + { MP_ROM_QSTR(MP_QSTR_get_raw_oxd), MP_ROM_PTR(&BreakoutMICS6814_get_raw_oxd_obj) }, + { MP_ROM_QSTR(MP_QSTR_read_all), MP_ROM_PTR(&BreakoutMICS6814_read_all_obj) }, + { MP_ROM_QSTR(MP_QSTR_read_ref), MP_ROM_PTR(&BreakoutMICS6814_read_ref_obj) }, + { MP_ROM_QSTR(MP_QSTR_read_reducing), MP_ROM_PTR(&BreakoutMICS6814_read_reducing_obj) }, + { MP_ROM_QSTR(MP_QSTR_read_nh3), MP_ROM_PTR(&BreakoutMICS6814_read_nh3_obj) }, + { MP_ROM_QSTR(MP_QSTR_read_oxidising), MP_ROM_PTR(&BreakoutMICS6814_read_oxidising_obj) }, + { MP_ROM_QSTR(MP_QSTR_REF), MP_ROM_INT(REF) }, + { MP_ROM_QSTR(MP_QSTR_REDUCING), MP_ROM_INT(REDUCING) }, + { MP_ROM_QSTR(MP_QSTR_NH3), MP_ROM_INT(NH3) }, + { MP_ROM_QSTR(MP_QSTR_OXIDISING), MP_ROM_INT(OXIDISING) }, +}; +STATIC MP_DEFINE_CONST_DICT(BreakoutMICS6814_locals_dict, BreakoutMICS6814_locals_dict_table); + +/***** Class Definition *****/ +const mp_obj_type_t breakout_mics6814_BreakoutMICS6814_type = { + { &mp_type_type }, + .name = MP_QSTR_breakout_mics6814, + .print = BreakoutMICS6814_print, + .make_new = BreakoutMICS6814_make_new, + .locals_dict = (mp_obj_dict_t*)&BreakoutMICS6814_locals_dict, +}; + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// breakout_mics6814 Module +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/***** Globals Table *****/ +STATIC const mp_map_elem_t breakout_mics6814_globals_table[] = { + { MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_breakout_mics6814) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_BreakoutMICS6814), (mp_obj_t)&breakout_mics6814_BreakoutMICS6814_type }, +}; +STATIC MP_DEFINE_CONST_DICT(mp_module_breakout_mics6814_globals, breakout_mics6814_globals_table); + +/***** Module Definition *****/ +const mp_obj_module_t breakout_mics6814_user_cmodule = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t*)&mp_module_breakout_mics6814_globals, +}; + +//////////////////////////////////////////////////////////////////////////////////////////////////// +MP_REGISTER_MODULE(MP_QSTR_breakout_mics6814, breakout_mics6814_user_cmodule, MODULE_BREAKOUT_MICS6814_ENABLED); +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////// \ No newline at end of file diff --git a/micropython/modules/breakout_mics6814/breakout_mics6814.cpp b/micropython/modules/breakout_mics6814/breakout_mics6814.cpp new file mode 100644 index 00000000..5b89d6b7 --- /dev/null +++ b/micropython/modules/breakout_mics6814/breakout_mics6814.cpp @@ -0,0 +1,280 @@ +#include "libraries/breakout_mics6814/breakout_mics6814.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_mics6814.h" + +/***** Variables Struct *****/ +typedef struct _breakout_mics6814_BreakoutMICS6814_obj_t { + mp_obj_base_t base; + BreakoutMICS6814 *breakout; +} breakout_mics6814_BreakoutMICS6814_obj_t; + +/***** Print *****/ +void BreakoutMICS6814_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + (void)kind; //Unused input parameter + breakout_mics6814_BreakoutMICS6814_obj_t *self = MP_OBJ_TO_PTR2(self_in, breakout_mics6814_BreakoutMICS6814_obj_t); + BreakoutMICS6814* breakout = self->breakout; + mp_print_str(print, "BreakoutMICS6814("); + + 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 BreakoutMICS6814_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + breakout_mics6814_BreakoutMICS6814_obj_t *self = nullptr; + + if(n_args == 0) { + mp_arg_check_num(n_args, n_kw, 0, 0, true); + self = m_new_obj(breakout_mics6814_BreakoutMICS6814_obj_t); + self->base.type = &breakout_mics6814_BreakoutMICS6814_type; + self->breakout = new BreakoutMICS6814(); + } + else if(n_args == 1) { + enum { ARG_address }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_address, MP_ARG_REQUIRED | MP_ARG_INT }, + }; + + // Parse args. + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + self = m_new_obj(breakout_mics6814_BreakoutMICS6814_obj_t); + self->base.type = &breakout_mics6814_BreakoutMICS6814_type; + + self->breakout = new BreakoutMICS6814(args[ARG_address].u_int); + } + else { + enum { ARG_i2c, ARG_address, ARG_sda, ARG_scl, ARG_interrupt }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_i2c, MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_address, 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_interrupt, MP_ARG_INT, {.u_int = BreakoutMICS6814::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_mics6814_BreakoutMICS6814_obj_t); + self->base.type = &breakout_mics6814_BreakoutMICS6814_type; + + i2c_inst_t *i2c = (i2c_id == 0) ? i2c0 : i2c1; + self->breakout = new BreakoutMICS6814(i2c, args[ARG_address].u_int, sda, scl, args[ARG_interrupt].u_int); + } + + if(!self->breakout->init()) { + mp_raise_msg(&mp_type_RuntimeError, "MICS6814 breakout not found when initialising"); + } + + return MP_OBJ_FROM_PTR(self); +} + +/***** Methods *****/ +mp_obj_t BreakoutMICS6814_set_address(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_self, ARG_address }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_address, 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); + + breakout_mics6814_BreakoutMICS6814_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, breakout_mics6814_BreakoutMICS6814_obj_t); + + self->breakout->set_address(args[ARG_address].u_int); + + return mp_const_none; +} + +mp_obj_t BreakoutMICS6814_get_adc_vref(mp_obj_t self_in) { + breakout_mics6814_BreakoutMICS6814_obj_t *self = MP_OBJ_TO_PTR2(self_in, breakout_mics6814_BreakoutMICS6814_obj_t); + return mp_obj_new_float(self->breakout->get_adc_vref()); +} + +mp_obj_t BreakoutMICS6814_set_adc_vref(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_self, ARG_vref }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_vref, 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); + + breakout_mics6814_BreakoutMICS6814_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, breakout_mics6814_BreakoutMICS6814_obj_t); + + float vref = mp_obj_get_float(args[ARG_vref].u_obj); + self->breakout->set_adc_vref(vref); + + return mp_const_none; +} + +mp_obj_t BreakoutMICS6814_set_brightness(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_self, ARG_brightness }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_brightness, 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); + + breakout_mics6814_BreakoutMICS6814_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, breakout_mics6814_BreakoutMICS6814_obj_t); + + float brightness = mp_obj_get_float(args[ARG_brightness].u_obj); + if(brightness < 0 || brightness > 1.0f) + mp_raise_ValueError("brightness out of range. Expected 0.0 to 1.0"); + else + self->breakout->set_brightness(brightness); + + return mp_const_none; +} + +mp_obj_t BreakoutMICS6814_set_led(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_self, ARG_r, ARG_g, ARG_b, ARG_w }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_r, MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_g, MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_b, 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); + + breakout_mics6814_BreakoutMICS6814_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, breakout_mics6814_BreakoutMICS6814_obj_t); + + int r = args[ARG_r].u_int; + int g = args[ARG_g].u_int; + int b = args[ARG_b].u_int; + + if(r < 0 || r > 255) + mp_raise_ValueError("r out of range. Expected 0 to 255"); + else if(g < 0 || g > 255) + mp_raise_ValueError("g out of range. Expected 0 to 255"); + else if(b < 0 || b > 255) + mp_raise_ValueError("b out of range. Expected 0 to 255"); + else + self->breakout->set_led(r, g, b); + + return mp_const_none; +} + +mp_obj_t BreakoutMICS6814_set_heater(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_self, ARG_on }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_on, 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_mics6814_BreakoutMICS6814_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, breakout_mics6814_BreakoutMICS6814_obj_t); + + self->breakout->set_heater(args[ARG_on].u_bool); + + return mp_const_none; +} + +mp_obj_t BreakoutMICS6814_disable_heater(mp_obj_t self_in) { + breakout_mics6814_BreakoutMICS6814_obj_t *self = MP_OBJ_TO_PTR2(self_in, breakout_mics6814_BreakoutMICS6814_obj_t); + self->breakout->disable_heater(); + + return mp_const_none; +} + +mp_obj_t BreakoutMICS6814_get_raw_ref(mp_obj_t self_in) { + breakout_mics6814_BreakoutMICS6814_obj_t *self = MP_OBJ_TO_PTR2(self_in, breakout_mics6814_BreakoutMICS6814_obj_t); + return mp_obj_new_float(self->breakout->get_raw_ref()); +} + +mp_obj_t BreakoutMICS6814_get_raw_red(mp_obj_t self_in) { + breakout_mics6814_BreakoutMICS6814_obj_t *self = MP_OBJ_TO_PTR2(self_in, breakout_mics6814_BreakoutMICS6814_obj_t); + return mp_obj_new_float(self->breakout->get_raw_red()); +} + +mp_obj_t BreakoutMICS6814_get_raw_nh3(mp_obj_t self_in) { + breakout_mics6814_BreakoutMICS6814_obj_t *self = MP_OBJ_TO_PTR2(self_in, breakout_mics6814_BreakoutMICS6814_obj_t); + return mp_obj_new_float(self->breakout->get_raw_nh3()); +} + +mp_obj_t BreakoutMICS6814_get_raw_oxd(mp_obj_t self_in) { + breakout_mics6814_BreakoutMICS6814_obj_t *self = MP_OBJ_TO_PTR2(self_in, breakout_mics6814_BreakoutMICS6814_obj_t); + return mp_obj_new_float(self->breakout->get_raw_oxd()); +} + +mp_obj_t BreakoutMICS6814_read_all(mp_obj_t self_in) { + breakout_mics6814_BreakoutMICS6814_obj_t *self = MP_OBJ_TO_PTR2(self_in, breakout_mics6814_BreakoutMICS6814_obj_t); + BreakoutMICS6814::Reading reading = self->breakout->read_all(); + + mp_obj_t tuple[4]; + tuple[REF] = mp_obj_new_float(reading.ref); + tuple[REDUCING] = mp_obj_new_float(reading.reducing); + tuple[NH3] = mp_obj_new_float(reading.nh3); + tuple[OXIDISING] = mp_obj_new_float(reading.oxidising); + + return mp_obj_new_tuple(4, tuple); +} + +mp_obj_t BreakoutMICS6814_read_ref(mp_obj_t self_in) { + breakout_mics6814_BreakoutMICS6814_obj_t *self = MP_OBJ_TO_PTR2(self_in, breakout_mics6814_BreakoutMICS6814_obj_t); + return mp_obj_new_float(self->breakout->read_ref()); +} + +mp_obj_t BreakoutMICS6814_read_reducing(mp_obj_t self_in) { + breakout_mics6814_BreakoutMICS6814_obj_t *self = MP_OBJ_TO_PTR2(self_in, breakout_mics6814_BreakoutMICS6814_obj_t); + return mp_obj_new_float(self->breakout->read_reducing()); +} + +mp_obj_t BreakoutMICS6814_read_nh3(mp_obj_t self_in) { + breakout_mics6814_BreakoutMICS6814_obj_t *self = MP_OBJ_TO_PTR2(self_in, breakout_mics6814_BreakoutMICS6814_obj_t); + return mp_obj_new_float(self->breakout->read_nh3()); +} + +mp_obj_t BreakoutMICS6814_read_oxidising(mp_obj_t self_in) { + breakout_mics6814_BreakoutMICS6814_obj_t *self = MP_OBJ_TO_PTR2(self_in, breakout_mics6814_BreakoutMICS6814_obj_t); + return mp_obj_new_float(self->breakout->read_oxidising()); +} +} \ No newline at end of file diff --git a/micropython/modules/breakout_mics6814/breakout_mics6814.h b/micropython/modules/breakout_mics6814/breakout_mics6814.h new file mode 100644 index 00000000..7526fe1d --- /dev/null +++ b/micropython/modules/breakout_mics6814/breakout_mics6814.h @@ -0,0 +1,32 @@ +// Include MicroPython API. +#include "py/runtime.h" + +enum { + REF = 0, + REDUCING, + NH3, + OXIDISING +}; + +/***** Extern of Class Definition *****/ +extern const mp_obj_type_t breakout_mics6814_BreakoutMICS6814_type; + +/***** Extern of Class Methods *****/ +extern void BreakoutMICS6814_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind); +extern mp_obj_t BreakoutMICS6814_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 BreakoutMICS6814_set_address(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); +extern mp_obj_t BreakoutMICS6814_get_adc_vref(mp_obj_t self_in); +extern mp_obj_t BreakoutMICS6814_set_adc_vref(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); +extern mp_obj_t BreakoutMICS6814_set_brightness(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); +extern mp_obj_t BreakoutMICS6814_set_led(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); +extern mp_obj_t BreakoutMICS6814_set_heater(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); +extern mp_obj_t BreakoutMICS6814_disable_heater(mp_obj_t self_in); +extern mp_obj_t BreakoutMICS6814_get_raw_ref(mp_obj_t self_in); +extern mp_obj_t BreakoutMICS6814_get_raw_red(mp_obj_t self_in); +extern mp_obj_t BreakoutMICS6814_get_raw_nh3(mp_obj_t self_in); +extern mp_obj_t BreakoutMICS6814_get_raw_oxd(mp_obj_t self_in); +extern mp_obj_t BreakoutMICS6814_read_all(mp_obj_t self_in); +extern mp_obj_t BreakoutMICS6814_read_ref(mp_obj_t self_in); +extern mp_obj_t BreakoutMICS6814_read_reducing(mp_obj_t self_in); +extern mp_obj_t BreakoutMICS6814_read_nh3(mp_obj_t self_in); +extern mp_obj_t BreakoutMICS6814_read_oxidising(mp_obj_t self_in); \ No newline at end of file diff --git a/micropython/modules/breakout_mics6814/micropython.cmake b/micropython/modules/breakout_mics6814/micropython.cmake new file mode 100644 index 00000000..73850414 --- /dev/null +++ b/micropython/modules/breakout_mics6814/micropython.cmake @@ -0,0 +1,20 @@ +set(MOD_NAME breakout_mics6814) +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/ioexpander/ioexpander.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_mics6814/micropython.mk b/micropython/modules/breakout_mics6814/micropython.mk new file mode 100755 index 00000000..8a533b71 --- /dev/null +++ b/micropython/modules/breakout_mics6814/micropython.mk @@ -0,0 +1,13 @@ +set(MOD_NAME breakout_mics6814) +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/breakout_msa301/breakout_msa301.cpp b/micropython/modules/breakout_msa301/breakout_msa301.cpp index 17f91cf7..3b07f0d3 100644 --- a/micropython/modules/breakout_msa301/breakout_msa301.cpp +++ b/micropython/modules/breakout_msa301/breakout_msa301.cpp @@ -1,4 +1,4 @@ -#include "../../../libraries/breakout_msa301/breakout_msa301.hpp" +#include "libraries/breakout_msa301/breakout_msa301.hpp" #define MP_OBJ_TO_PTR2(o, t) ((t *)(uintptr_t)(o)) diff --git a/micropython/modules/breakout_potentiometer/breakout_potentiometer.c b/micropython/modules/breakout_potentiometer/breakout_potentiometer.c new file mode 100644 index 00000000..67ada0d6 --- /dev/null +++ b/micropython/modules/breakout_potentiometer/breakout_potentiometer.c @@ -0,0 +1,64 @@ +#include "breakout_potentiometer.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// BreakoutPotentiometer Class +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/***** Methods *****/ +MP_DEFINE_CONST_FUN_OBJ_KW(BreakoutPotentiometer_set_address_obj, 2, BreakoutPotentiometer_set_address); +MP_DEFINE_CONST_FUN_OBJ_1(BreakoutPotentiometer_get_adc_vref_obj, BreakoutPotentiometer_get_adc_vref); +MP_DEFINE_CONST_FUN_OBJ_KW(BreakoutPotentiometer_set_adc_vref_obj, 2, BreakoutPotentiometer_set_adc_vref); +MP_DEFINE_CONST_FUN_OBJ_1(BreakoutPotentiometer_get_direction_obj, BreakoutPotentiometer_get_direction); +MP_DEFINE_CONST_FUN_OBJ_KW(BreakoutPotentiometer_set_direction_obj, 2, BreakoutPotentiometer_set_direction); +MP_DEFINE_CONST_FUN_OBJ_KW(BreakoutPotentiometer_set_brightness_obj, 2, BreakoutPotentiometer_set_brightness); +MP_DEFINE_CONST_FUN_OBJ_KW(BreakoutPotentiometer_set_led_obj, 4, BreakoutPotentiometer_set_led); +MP_DEFINE_CONST_FUN_OBJ_1(BreakoutPotentiometer_read_obj, BreakoutPotentiometer_read); +MP_DEFINE_CONST_FUN_OBJ_1(BreakoutPotentiometer_read_raw_obj, BreakoutPotentiometer_read_raw); + +/***** Binding of Methods *****/ +STATIC const mp_rom_map_elem_t BreakoutPotentiometer_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_set_address), MP_ROM_PTR(&BreakoutPotentiometer_set_address_obj) }, + { MP_ROM_QSTR(MP_QSTR_get_adc_vref), MP_ROM_PTR(&BreakoutPotentiometer_get_adc_vref_obj) }, + { MP_ROM_QSTR(MP_QSTR_set_adc_vref), MP_ROM_PTR(&BreakoutPotentiometer_set_adc_vref_obj) }, + { MP_ROM_QSTR(MP_QSTR_get_direction), MP_ROM_PTR(&BreakoutPotentiometer_get_direction_obj) }, + { MP_ROM_QSTR(MP_QSTR_set_direction), MP_ROM_PTR(&BreakoutPotentiometer_set_direction_obj) }, + { MP_ROM_QSTR(MP_QSTR_set_brightness), MP_ROM_PTR(&BreakoutPotentiometer_set_brightness_obj) }, + { MP_ROM_QSTR(MP_QSTR_set_led), MP_ROM_PTR(&BreakoutPotentiometer_set_led_obj) }, + { MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&BreakoutPotentiometer_read_obj) }, + { MP_ROM_QSTR(MP_QSTR_read_raw), MP_ROM_PTR(&BreakoutPotentiometer_read_raw_obj) }, + { MP_ROM_QSTR(MP_QSTR_DIRECTION_CW), MP_ROM_INT(1) }, + { MP_ROM_QSTR(MP_QSTR_DIRECTION_CCW), MP_ROM_INT(0) }, +}; +STATIC MP_DEFINE_CONST_DICT(BreakoutPotentiometer_locals_dict, BreakoutPotentiometer_locals_dict_table); + +/***** Class Definition *****/ +const mp_obj_type_t breakout_potentiometer_BreakoutPotentiometer_type = { + { &mp_type_type }, + .name = MP_QSTR_breakout_potentiometer, + .print = BreakoutPotentiometer_print, + .make_new = BreakoutPotentiometer_make_new, + .locals_dict = (mp_obj_dict_t*)&BreakoutPotentiometer_locals_dict, +}; + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// breakout_potentiometer Module +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/***** Globals Table *****/ +STATIC const mp_map_elem_t breakout_potentiometer_globals_table[] = { + { MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_breakout_potentiometer) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_BreakoutPotentiometer), (mp_obj_t)&breakout_potentiometer_BreakoutPotentiometer_type }, +}; +STATIC MP_DEFINE_CONST_DICT(mp_module_breakout_potentiometer_globals, breakout_potentiometer_globals_table); + +/***** Module Definition *****/ +const mp_obj_module_t breakout_potentiometer_user_cmodule = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t*)&mp_module_breakout_potentiometer_globals, +}; + +//////////////////////////////////////////////////////////////////////////////////////////////////// +MP_REGISTER_MODULE(MP_QSTR_breakout_potentiometer, breakout_potentiometer_user_cmodule, MODULE_BREAKOUT_POTENTIOMETER_ENABLED); +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////// \ No newline at end of file diff --git a/micropython/modules/breakout_potentiometer/breakout_potentiometer.cpp b/micropython/modules/breakout_potentiometer/breakout_potentiometer.cpp new file mode 100644 index 00000000..d505b846 --- /dev/null +++ b/micropython/modules/breakout_potentiometer/breakout_potentiometer.cpp @@ -0,0 +1,235 @@ +#include "libraries/breakout_potentiometer/breakout_potentiometer.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_potentiometer.h" + +/***** Variables Struct *****/ +typedef struct _breakout_potentiometer_BreakoutPotentiometer_obj_t { + mp_obj_base_t base; + BreakoutPotentiometer *breakout; +} breakout_potentiometer_BreakoutPotentiometer_obj_t; + +/***** Print *****/ +void BreakoutPotentiometer_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + (void)kind; //Unused input parameter + breakout_potentiometer_BreakoutPotentiometer_obj_t *self = MP_OBJ_TO_PTR2(self_in, breakout_potentiometer_BreakoutPotentiometer_obj_t); + BreakoutPotentiometer* breakout = self->breakout; + mp_print_str(print, "BreakoutPotentiometer("); + + 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 BreakoutPotentiometer_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + breakout_potentiometer_BreakoutPotentiometer_obj_t *self = nullptr; + + if(n_args == 0) { + mp_arg_check_num(n_args, n_kw, 0, 0, true); + self = m_new_obj(breakout_potentiometer_BreakoutPotentiometer_obj_t); + self->base.type = &breakout_potentiometer_BreakoutPotentiometer_type; + self->breakout = new BreakoutPotentiometer(); + } + else if(n_args == 1) { + enum { ARG_address }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_address, MP_ARG_REQUIRED | MP_ARG_INT }, + }; + + // Parse args. + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + self = m_new_obj(breakout_potentiometer_BreakoutPotentiometer_obj_t); + self->base.type = &breakout_potentiometer_BreakoutPotentiometer_type; + + self->breakout = new BreakoutPotentiometer(args[ARG_address].u_int); + } + else { + enum { ARG_i2c, ARG_address, ARG_sda, ARG_scl, ARG_interrupt }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_i2c, MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_address, 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_interrupt, MP_ARG_INT, {.u_int = BreakoutPotentiometer::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_potentiometer_BreakoutPotentiometer_obj_t); + self->base.type = &breakout_potentiometer_BreakoutPotentiometer_type; + + i2c_inst_t *i2c = (i2c_id == 0) ? i2c0 : i2c1; + self->breakout = new BreakoutPotentiometer(i2c, args[ARG_address].u_int, sda, scl, args[ARG_interrupt].u_int); + } + + if(!self->breakout->init()) { + mp_raise_msg(&mp_type_RuntimeError, "Potentiometer breakout not found when initialising"); + } + + return MP_OBJ_FROM_PTR(self); +} + +/***** Methods *****/ +mp_obj_t BreakoutPotentiometer_set_address(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_self, ARG_address }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_address, 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); + + breakout_potentiometer_BreakoutPotentiometer_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, breakout_potentiometer_BreakoutPotentiometer_obj_t); + + self->breakout->set_address(args[ARG_address].u_int); + + return mp_const_none; +} + +mp_obj_t BreakoutPotentiometer_get_adc_vref(mp_obj_t self_in) { + breakout_potentiometer_BreakoutPotentiometer_obj_t *self = MP_OBJ_TO_PTR2(self_in, breakout_potentiometer_BreakoutPotentiometer_obj_t); + return mp_obj_new_float(self->breakout->get_adc_vref()); +} + +mp_obj_t BreakoutPotentiometer_set_adc_vref(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_self, ARG_vref }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_vref, 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); + + breakout_potentiometer_BreakoutPotentiometer_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, breakout_potentiometer_BreakoutPotentiometer_obj_t); + + float vref = mp_obj_get_float(args[ARG_vref].u_obj); + self->breakout->set_adc_vref(vref); + + return mp_const_none; +} + +mp_obj_t BreakoutPotentiometer_get_direction(mp_obj_t self_in) { + breakout_potentiometer_BreakoutPotentiometer_obj_t *self = MP_OBJ_TO_PTR2(self_in, breakout_potentiometer_BreakoutPotentiometer_obj_t); + return mp_obj_new_bool(self->breakout->get_direction()); +} + +mp_obj_t BreakoutPotentiometer_set_direction(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_self, ARG_clockwise }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_clockwise, 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_potentiometer_BreakoutPotentiometer_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, breakout_potentiometer_BreakoutPotentiometer_obj_t); + + self->breakout->set_direction(args[ARG_clockwise].u_bool ? BreakoutPotentiometer::DIRECTION_CW : BreakoutPotentiometer::DIRECTION_CCW); + + return mp_const_none; +} + +mp_obj_t BreakoutPotentiometer_set_brightness(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_self, ARG_brightness }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_brightness, 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); + + breakout_potentiometer_BreakoutPotentiometer_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, breakout_potentiometer_BreakoutPotentiometer_obj_t); + + float brightness = mp_obj_get_float(args[ARG_brightness].u_obj); + if(brightness < 0 || brightness > 1.0f) + mp_raise_ValueError("brightness out of range. Expected 0.0 to 1.0"); + else + self->breakout->set_brightness(brightness); + + return mp_const_none; +} + +mp_obj_t BreakoutPotentiometer_set_led(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_self, ARG_r, ARG_g, ARG_b, ARG_w }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_r, MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_g, MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_b, 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); + + breakout_potentiometer_BreakoutPotentiometer_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, breakout_potentiometer_BreakoutPotentiometer_obj_t); + + int r = args[ARG_r].u_int; + int g = args[ARG_g].u_int; + int b = args[ARG_b].u_int; + + if(r < 0 || r > 255) + mp_raise_ValueError("r out of range. Expected 0 to 255"); + else if(g < 0 || g > 255) + mp_raise_ValueError("g out of range. Expected 0 to 255"); + else if(b < 0 || b > 255) + mp_raise_ValueError("b out of range. Expected 0 to 255"); + else + self->breakout->set_led(r, g, b); + + return mp_const_none; +} + +mp_obj_t BreakoutPotentiometer_read(mp_obj_t self_in) { + breakout_potentiometer_BreakoutPotentiometer_obj_t *self = MP_OBJ_TO_PTR2(self_in, breakout_potentiometer_BreakoutPotentiometer_obj_t); + return mp_obj_new_float(self->breakout->read()); +} + +mp_obj_t BreakoutPotentiometer_read_raw(mp_obj_t self_in) { + breakout_potentiometer_BreakoutPotentiometer_obj_t *self = MP_OBJ_TO_PTR2(self_in, breakout_potentiometer_BreakoutPotentiometer_obj_t); + return mp_obj_new_int(self->breakout->read_raw()); +} +} \ No newline at end of file diff --git a/micropython/modules/breakout_potentiometer/breakout_potentiometer.h b/micropython/modules/breakout_potentiometer/breakout_potentiometer.h new file mode 100644 index 00000000..7aca57a6 --- /dev/null +++ b/micropython/modules/breakout_potentiometer/breakout_potentiometer.h @@ -0,0 +1,18 @@ +// Include MicroPython API. +#include "py/runtime.h" + +/***** Extern of Class Definition *****/ +extern const mp_obj_type_t breakout_potentiometer_BreakoutPotentiometer_type; + +/***** Extern of Class Methods *****/ +extern void BreakoutPotentiometer_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind); +extern mp_obj_t BreakoutPotentiometer_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 BreakoutPotentiometer_set_address(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); +extern mp_obj_t BreakoutPotentiometer_get_adc_vref(mp_obj_t self_in); +extern mp_obj_t BreakoutPotentiometer_set_adc_vref(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); +extern mp_obj_t BreakoutPotentiometer_get_direction(mp_obj_t self_in); +extern mp_obj_t BreakoutPotentiometer_set_direction(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); +extern mp_obj_t BreakoutPotentiometer_set_brightness(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); +extern mp_obj_t BreakoutPotentiometer_set_led(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); +extern mp_obj_t BreakoutPotentiometer_read(mp_obj_t self_in); +extern mp_obj_t BreakoutPotentiometer_read_raw(mp_obj_t self_in); \ No newline at end of file diff --git a/micropython/modules/breakout_potentiometer/micropython.cmake b/micropython/modules/breakout_potentiometer/micropython.cmake new file mode 100644 index 00000000..dac1d6c9 --- /dev/null +++ b/micropython/modules/breakout_potentiometer/micropython.cmake @@ -0,0 +1,20 @@ +set(MOD_NAME breakout_potentiometer) +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/ioexpander/ioexpander.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_potentiometer/micropython.mk b/micropython/modules/breakout_potentiometer/micropython.mk new file mode 100755 index 00000000..74d29ae6 --- /dev/null +++ b/micropython/modules/breakout_potentiometer/micropython.mk @@ -0,0 +1,13 @@ +set(MOD_NAME breakout_potentiometer) +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 5b0ca6db..3493560c 100644 --- a/micropython/modules/micropython.cmake +++ b/micropython/modules/micropython.cmake @@ -1,6 +1,8 @@ include_directories(${CMAKE_CURRENT_LIST_DIR}/../../) include(${CMAKE_CURRENT_LIST_DIR}/breakout_dotmatrix/micropython.cmake) +include(${CMAKE_CURRENT_LIST_DIR}/breakout_encoder/micropython.cmake) +include(${CMAKE_CURRENT_LIST_DIR}/breakout_ioexpander/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) @@ -8,6 +10,8 @@ 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) include(${CMAKE_CURRENT_LIST_DIR}/breakout_msa301/micropython.cmake) +include(${CMAKE_CURRENT_LIST_DIR}/breakout_mics6814/micropython.cmake) +include(${CMAKE_CURRENT_LIST_DIR}/breakout_potentiometer/micropython.cmake) include(${CMAKE_CURRENT_LIST_DIR}/breakout_trackball/micropython.cmake) include(${CMAKE_CURRENT_LIST_DIR}/breakout_sgp30/micropython.cmake) include(${CMAKE_CURRENT_LIST_DIR}/breakout_colourlcd240x240/micropython.cmake)