diff --git a/ports/raspberrypi/bindings/cyw43/__init__.c b/ports/raspberrypi/bindings/cyw43/__init__.c index d00612518e686..a156253875351 100644 --- a/ports/raspberrypi/bindings/cyw43/__init__.c +++ b/ports/raspberrypi/bindings/cyw43/__init__.c @@ -117,10 +117,7 @@ STATIC mp_obj_t cyw43_get_power_management() { STATIC MP_DEFINE_CONST_FUN_OBJ_0(cyw43_get_power_management_obj, cyw43_get_power_management); const mcu_pin_obj_t *validate_obj_is_pin_including_cyw43(mp_obj_t obj, qstr arg_name) { - if (!mp_obj_is_type(obj, &mcu_pin_type) && !mp_obj_is_type(obj, &cyw43_pin_type)) { - mp_raise_TypeError_varg(translate("%q must be of type %q or %q, not %q"), arg_name, mcu_pin_type.name, cyw43_pin_type.name, mp_obj_get_type(obj)->name); - } - return MP_OBJ_TO_PTR(obj); + return MP_OBJ_TO_PTR(mp_arg_validate_type_or_type(obj, &mcu_pin_type, &cyw43_pin_type, arg_name)); } const mcu_pin_obj_t *validate_obj_is_free_pin_or_gpio29(mp_obj_t obj, qstr arg_name) { diff --git a/ports/raspberrypi/boards/pimoroni_yukon/board.c b/ports/raspberrypi/boards/pimoroni_yukon/board.c new file mode 100644 index 0000000000000..da356b66eb9da --- /dev/null +++ b/ports/raspberrypi/boards/pimoroni_yukon/board.c @@ -0,0 +1,61 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2023 Christopher Parrott for Pimoroni Ltd + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "supervisor/board.h" + +#include "py/gc.h" +#include "py/runtime.h" + +#include "shared-bindings/busio/I2C.h" +#include "shared-bindings/board/__init__.h" +#include "shared-bindings/tca9555/__init__.h" +#include "shared-module/displayio/__init__.h" + +void board_init(void) { +} + +void board_deinit(void) { +} + +void reset_board(void) { + // Set the first IO expander's initial state + common_hal_tca_set_output_port(0, 0x8800); // Disable the two ADC Muxes + common_hal_tca_set_polarity_port(0, 0x0000); + common_hal_tca_set_config_port(0, 0x07BF); + + // Set the second IO expander's initial state + common_hal_tca_set_output_port(1, 0x0000); + common_hal_tca_set_polarity_port(1, 0x0000); + common_hal_tca_set_config_port(1, 0xFCE6); + + // Releasing displays, as if one is set up with the + // intended IO expander LCD pins then it will carry over + // into other user program runs, causing poor performance + // if they don't realise the screen is still being driven + common_hal_displayio_release_displays(); +} + +// Use the MP_WEAK supervisor/shared/board.c versions of routines not defined here. diff --git a/ports/raspberrypi/boards/pimoroni_yukon/mpconfigboard.h b/ports/raspberrypi/boards/pimoroni_yukon/mpconfigboard.h new file mode 100644 index 0000000000000..b77f1e4a65527 --- /dev/null +++ b/ports/raspberrypi/boards/pimoroni_yukon/mpconfigboard.h @@ -0,0 +1,14 @@ +#define MICROPY_HW_BOARD_NAME "Pimoroni Yukon" +#define MICROPY_HW_MCU_NAME "rp2040" + +#define CIRCUITPY_DIGITALIO_HAVE_INVALID_PULL (1) +#define CIRCUITPY_DIGITALIO_HAVE_INVALID_DRIVE_MODE (1) + +#define CIRCUITPY_BOARD_I2C (1) +#define CIRCUITPY_BOARD_I2C_PIN {{.scl = &pin_GPIO25, .sda = &pin_GPIO24}} +#define CIRCUITPY_BOARD_I2C_FREQ (400000) + +#define TCA9555_CHIP_COUNT (2) +#define TCA9555_CHIP_ADDRESSES { 0x20, 0x26 } +#define TCA9555_LOCAL_MEMORY (1) +#define TCA9555_READ_INTERNALS (1) diff --git a/ports/raspberrypi/boards/pimoroni_yukon/mpconfigboard.mk b/ports/raspberrypi/boards/pimoroni_yukon/mpconfigboard.mk new file mode 100644 index 0000000000000..cafe402ecb436 --- /dev/null +++ b/ports/raspberrypi/boards/pimoroni_yukon/mpconfigboard.mk @@ -0,0 +1,18 @@ +USB_VID = 0x2E8A +USB_PID = 0x105B +USB_PRODUCT = "Yukon" +USB_MANUFACTURER = "Pimoroni" + +CHIP_VARIANT = RP2040 +CHIP_FAMILY = rp2 + +EXTERNAL_FLASH_DEVICES = "W25Q128JVxQ" + +CIRCUITPY__EVE = 1 + +CIRCUITPY_TCA9555 = 1 + +# Include these Python libraries in firmware. +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Motor +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_DotStar +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_NeoPixel diff --git a/ports/raspberrypi/boards/pimoroni_yukon/pico-sdk-configboard.h b/ports/raspberrypi/boards/pimoroni_yukon/pico-sdk-configboard.h new file mode 100644 index 0000000000000..36da55d457197 --- /dev/null +++ b/ports/raspberrypi/boards/pimoroni_yukon/pico-sdk-configboard.h @@ -0,0 +1 @@ +// Put board-specific pico-sdk definitions here. This file must exist. diff --git a/ports/raspberrypi/boards/pimoroni_yukon/pins.c b/ports/raspberrypi/boards/pimoroni_yukon/pins.c new file mode 100644 index 0000000000000..08404aac1d77a --- /dev/null +++ b/ports/raspberrypi/boards/pimoroni_yukon/pins.c @@ -0,0 +1,170 @@ +#include "shared-bindings/board/__init__.h" +#include "shared-bindings/tca9555/__init__.h" +#include "py/objtuple.h" + +STATIC const qstr board_slot_fields[] = { + MP_QSTR_ID, + MP_QSTR_FAST1, + MP_QSTR_FAST2, + MP_QSTR_FAST3, + MP_QSTR_FAST4, + MP_QSTR_SLOW1, + MP_QSTR_SLOW2, + MP_QSTR_SLOW3, + MP_QSTR_ADC1_ADDR, + MP_QSTR_ADC2_THERM_ADDR +}; + +STATIC MP_DEFINE_ATTRTUPLE( + board_slot1_obj, + board_slot_fields, + 10, + MP_ROM_INT(1), + (mp_obj_t)&pin_GPIO0, + (mp_obj_t)&pin_GPIO1, + (mp_obj_t)&pin_GPIO2, + (mp_obj_t)&pin_GPIO3, + (mp_obj_t)&pin_TCA0_3, + (mp_obj_t)&pin_TCA0_4, + (mp_obj_t)&pin_TCA0_5, + MP_ROM_INT(0), // 0b0000 + MP_ROM_INT(3) // 0b0011 + ); + +STATIC MP_DEFINE_ATTRTUPLE( + board_slot2_obj, + board_slot_fields, + 10, + MP_ROM_INT(2), + (mp_obj_t)&pin_GPIO4, + (mp_obj_t)&pin_GPIO5, + (mp_obj_t)&pin_GPIO6, + (mp_obj_t)&pin_GPIO7, + (mp_obj_t)&pin_TCA0_0, + (mp_obj_t)&pin_TCA0_1, + (mp_obj_t)&pin_TCA0_2, + MP_ROM_INT(1), // 0b0001 + MP_ROM_INT(6) // 0b0110 + ); + +STATIC MP_DEFINE_ATTRTUPLE( + board_slot3_obj, + board_slot_fields, + 10, + MP_ROM_INT(3), + (mp_obj_t)&pin_GPIO8, + (mp_obj_t)&pin_GPIO9, + (mp_obj_t)&pin_GPIO10, + (mp_obj_t)&pin_GPIO11, + (mp_obj_t)&pin_TCA0_8, + (mp_obj_t)&pin_TCA0_9, + (mp_obj_t)&pin_TCA0_10, + MP_ROM_INT(4), // 0b0100 + MP_ROM_INT(2) // 0b0010 + ); + +STATIC MP_DEFINE_ATTRTUPLE( + board_slot4_obj, + board_slot_fields, + 10, + MP_ROM_INT(4), + (mp_obj_t)&pin_GPIO12, + (mp_obj_t)&pin_GPIO13, + (mp_obj_t)&pin_GPIO14, + (mp_obj_t)&pin_GPIO15, + (mp_obj_t)&pin_TCA1_7, + (mp_obj_t)&pin_TCA1_5, + (mp_obj_t)&pin_TCA1_6, + MP_ROM_INT(5), // 0b0101 + MP_ROM_INT(7) // 0b0111 + ); + +STATIC MP_DEFINE_ATTRTUPLE( + board_slot5_obj, + board_slot_fields, + 10, + MP_ROM_INT(5), + (mp_obj_t)&pin_GPIO16, + (mp_obj_t)&pin_GPIO17, + (mp_obj_t)&pin_GPIO18, + (mp_obj_t)&pin_GPIO19, + (mp_obj_t)&pin_TCA1_15, + (mp_obj_t)&pin_TCA1_14, + (mp_obj_t)&pin_TCA1_13, + MP_ROM_INT(8), // 0b1000 + MP_ROM_INT(11) // 0b1011 + ); + +STATIC MP_DEFINE_ATTRTUPLE( + board_slot6_obj, + board_slot_fields, + 10, + MP_ROM_INT(6), + (mp_obj_t)&pin_GPIO20, + (mp_obj_t)&pin_GPIO21, + (mp_obj_t)&pin_GPIO22, + (mp_obj_t)&pin_GPIO23, + (mp_obj_t)&pin_TCA1_10, + (mp_obj_t)&pin_TCA1_12, + (mp_obj_t)&pin_TCA1_11, + MP_ROM_INT(9), // 0b1001 + MP_ROM_INT(10) // 0b1010 + ); + +STATIC const mp_rom_map_elem_t board_module_globals_table[] = { + CIRCUITPYTHON_BOARD_DICT_STANDARD_ITEMS + + { MP_ROM_QSTR(MP_QSTR_SLOT1), MP_ROM_PTR(&board_slot1_obj) }, + { MP_ROM_QSTR(MP_QSTR_SLOT2), MP_ROM_PTR(&board_slot2_obj) }, + { MP_ROM_QSTR(MP_QSTR_SLOT3), MP_ROM_PTR(&board_slot3_obj) }, + { MP_ROM_QSTR(MP_QSTR_SLOT4), MP_ROM_PTR(&board_slot4_obj) }, + { MP_ROM_QSTR(MP_QSTR_SLOT5), MP_ROM_PTR(&board_slot5_obj) }, + { MP_ROM_QSTR(MP_QSTR_SLOT6), MP_ROM_PTR(&board_slot6_obj) }, + { MP_ROM_QSTR(MP_QSTR_NUM_SLOTS), MP_ROM_INT(6) }, + + { MP_ROM_QSTR(MP_QSTR_SDA), MP_ROM_PTR(&pin_GPIO24) }, + { MP_ROM_QSTR(MP_QSTR_SCL), MP_ROM_PTR(&pin_GPIO25) }, + + { MP_ROM_QSTR(MP_QSTR_EX_I2C_SDA), MP_ROM_PTR(&pin_GPIO26) }, + { MP_ROM_QSTR(MP_QSTR_EX_SPI_SCK), MP_ROM_PTR(&pin_GPIO26) }, + { MP_ROM_QSTR(MP_QSTR_GP26_A0), MP_ROM_PTR(&pin_GPIO26) }, + { MP_ROM_QSTR(MP_QSTR_GP26), MP_ROM_PTR(&pin_GPIO26) }, + { MP_ROM_QSTR(MP_QSTR_A0), MP_ROM_PTR(&pin_GPIO26) }, + + { MP_ROM_QSTR(MP_QSTR_EX_I2C_SCL), MP_ROM_PTR(&pin_GPIO27) }, + { MP_ROM_QSTR(MP_QSTR_EX_SPI_MOSI), MP_ROM_PTR(&pin_GPIO27) }, + { MP_ROM_QSTR(MP_QSTR_GP27_A1), MP_ROM_PTR(&pin_GPIO27) }, + { MP_ROM_QSTR(MP_QSTR_GP27), MP_ROM_PTR(&pin_GPIO27) }, + { MP_ROM_QSTR(MP_QSTR_A1), MP_ROM_PTR(&pin_GPIO27) }, + + { MP_ROM_QSTR(MP_QSTR_INT), MP_ROM_PTR(&pin_GPIO28) }, + + { MP_ROM_QSTR(MP_QSTR_SHARED_ADC), MP_ROM_PTR(&pin_GPIO29) }, + + { MP_ROM_QSTR(MP_QSTR_MAIN_EN), MP_ROM_PTR(&pin_TCA0_6) }, + { MP_ROM_QSTR(MP_QSTR_USER_SW), MP_ROM_PTR(&pin_TCA0_7) }, + + { MP_ROM_QSTR(MP_QSTR_ADC_ADDR_1), MP_ROM_PTR(&pin_TCA0_12) }, + { MP_ROM_QSTR(MP_QSTR_ADC_ADDR_2), MP_ROM_PTR(&pin_TCA0_13) }, + { MP_ROM_QSTR(MP_QSTR_ADC_ADDR_3), MP_ROM_PTR(&pin_TCA0_14) }, + { MP_ROM_QSTR(MP_QSTR_ADC_MUX_EN_1), MP_ROM_PTR(&pin_TCA0_15) }, + { MP_ROM_QSTR(MP_QSTR_ADC_MUX_EN_2), MP_ROM_PTR(&pin_TCA0_11) }, + + { MP_ROM_QSTR(MP_QSTR_SW_A), MP_ROM_PTR(&pin_TCA1_1) }, + { MP_ROM_QSTR(MP_QSTR_SW_B), MP_ROM_PTR(&pin_TCA1_2) }, + { MP_ROM_QSTR(MP_QSTR_LED_A), MP_ROM_PTR(&pin_TCA1_3) }, + { MP_ROM_QSTR(MP_QSTR_LED_B), MP_ROM_PTR(&pin_TCA1_4) }, + + { MP_ROM_QSTR(MP_QSTR_LCD_BL), MP_ROM_PTR(&pin_TCA1_0) }, + { MP_ROM_QSTR(MP_QSTR_LCD_DC), MP_ROM_PTR(&pin_TCA1_8) }, + { MP_ROM_QSTR(MP_QSTR_LCD_CS), MP_ROM_PTR(&pin_TCA1_9) }, + + { MP_ROM_QSTR(MP_QSTR_CURRENT_SENSE_ADDR), MP_ROM_INT(12) }, // 0b1100 + { MP_ROM_QSTR(MP_QSTR_TEMP_SENSE_ADDR), MP_ROM_INT(13) }, // 0b1101 + { MP_ROM_QSTR(MP_QSTR_VOLTAGE_OUT_SENSE_ADDR), MP_ROM_INT(14) }, // 0b1110 + { MP_ROM_QSTR(MP_QSTR_VOLTAGE_IN_SENSE_ADDR), MP_ROM_INT(15) }, // 0b1111 + + { MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&board_i2c_obj) }, + { MP_ROM_QSTR(MP_QSTR_STEMMA_I2C), MP_ROM_PTR(&board_i2c_obj) }, +}; +MP_DEFINE_CONST_DICT(board_module_globals, board_module_globals_table); diff --git a/ports/raspberrypi/common-hal/digitalio/DigitalInOut.c b/ports/raspberrypi/common-hal/digitalio/DigitalInOut.c index d90c0db81b9f6..f7f9044e4b568 100644 --- a/ports/raspberrypi/common-hal/digitalio/DigitalInOut.c +++ b/ports/raspberrypi/common-hal/digitalio/DigitalInOut.c @@ -42,8 +42,21 @@ #include "bindings/cyw43/__init__.h" #define IS_CYW(self) ((self)->pin->base.type == &cyw43_pin_type) -const mcu_pin_obj_t *common_hal_digitalio_validate_pin(mp_obj_t obj) { - return validate_obj_is_free_pin_including_cyw43(obj, MP_QSTR_pin); +const mcu_pin_obj_t *common_hal_digitalio_validate_pin(mp_obj_t obj, qstr arg_name) { + return validate_obj_is_free_pin_including_cyw43(obj, arg_name); +} +#endif + +#if CIRCUITPY_TCA9555 +#include "shared-bindings/tca9555/__init__.h" +#define IS_TCA(self) ((self)->pin->base.type == &tca_pin_type) + +const mcu_pin_obj_t *common_hal_digitalio_validate_pin(mp_obj_t obj, qstr arg_name) { + return validate_obj_is_free_pin_including_tca(obj, arg_name); +} + +const mcu_pin_obj_t *common_hal_digitalio_validate_pin_or_none(mp_obj_t obj, qstr arg_name) { + return validate_obj_is_free_pin_including_tca_or_none(obj, arg_name); } #endif @@ -59,6 +72,15 @@ digitalinout_result_t common_hal_digitalio_digitalinout_construct( return DIGITALINOUT_OK; } #endif + #if CIRCUITPY_TCA9555 + if (IS_TCA(self)) { + if (common_hal_tca_gpio_get_config(self->pin->number) == GPIO_OUT) { + self->output = true; + self->open_drain = false; + } + return DIGITALINOUT_OK; + } + #endif // Set to input. No output value. gpio_init(pin->number); @@ -103,6 +125,21 @@ digitalinout_result_t common_hal_digitalio_digitalinout_switch_to_output( return DIGITALINOUT_OK; } #endif + #if CIRCUITPY_TCA9555 + if (IS_TCA(self)) { + if (drive_mode != DRIVE_MODE_PUSH_PULL) { + return DIGITALINOUT_INVALID_DRIVE_MODE; + } + // Set the direction -after- setting the pin value, + // to avoid a glitch which might occur if the old value was + // different and the pin was previously set to input. + common_hal_tca_gpio_set_output(self->pin->number, value); + common_hal_tca_gpio_set_config(self->pin->number, GPIO_OUT); + self->output = true; + self->open_drain = false; + return DIGITALINOUT_OK; + } + #endif const uint8_t pin = self->pin->number; gpio_disable_pulls(pin); @@ -133,6 +170,12 @@ void common_hal_digitalio_digitalinout_set_value( return; } #endif + #if CIRCUITPY_TCA9555 + if (IS_TCA(self)) { + common_hal_tca_gpio_set_output(pin, value); + return; + } + #endif if (self->open_drain && value) { // If true and open-drain, set the direction -before- setting // the pin value, to to avoid a high glitch on the pin before @@ -155,6 +198,15 @@ bool common_hal_digitalio_digitalinout_get_value( return cyw43_arch_gpio_get(self->pin->number); } #endif + #if CIRCUITPY_TCA9555 + if (IS_TCA(self)) { + if (self->output) { + return common_hal_tca_gpio_get_output(self->pin->number); + } else { + return common_hal_tca_gpio_get_input(self->pin->number); + } + } + #endif return gpio_get(self->pin->number); } @@ -168,6 +220,13 @@ digitalinout_result_t common_hal_digitalio_digitalinout_set_drive_mode( } } #endif + #if CIRCUITPY_TCA9555 + if (IS_TCA(self)) { + if (drive_mode != DRIVE_MODE_PUSH_PULL) { + return DIGITALINOUT_INVALID_DRIVE_MODE; + } + } + #endif bool value = common_hal_digitalio_digitalinout_get_value(self); self->open_drain = drive_mode == DRIVE_MODE_OPEN_DRAIN; // True is implemented differently between modes so reset the value to make @@ -199,6 +258,16 @@ digitalinout_result_t common_hal_digitalio_digitalinout_set_pull( return DIGITALINOUT_OK; } #endif + #if CIRCUITPY_TCA9555 + if (IS_TCA(self)) { + if (pull != PULL_NONE) { + return DIGITALINOUT_INVALID_PULL; + } + common_hal_tca_gpio_set_config(self->pin->number, GPIO_IN); + self->output = false; + return DIGITALINOUT_OK; + } + #endif const uint8_t pin = self->pin->number; gpio_set_pulls(pin, pull == PULL_UP, pull == PULL_DOWN); gpio_set_dir(pin, GPIO_IN); @@ -213,6 +282,11 @@ digitalio_pull_t common_hal_digitalio_digitalinout_get_pull( mp_raise_AttributeError(translate("Cannot get pull while in output mode")); return PULL_NONE; } else { + #if CIRCUITPY_TCA9555 + if (IS_TCA(self)) { + return PULL_NONE; + } + #endif if (gpio_is_pulled_up(pin)) { return PULL_UP; } else if (gpio_is_pulled_down(pin)) { @@ -232,6 +306,11 @@ volatile uint32_t *common_hal_digitalio_digitalinout_get_reg(digitalio_digitalin return NULL; } #endif + #if CIRCUITPY_TCA9555 + if (IS_TCA(self)) { + return NULL; + } + #endif const uint8_t pin = self->pin->number; *mask = 1u << pin; diff --git a/ports/raspberrypi/common-hal/microcontroller/Pin.c b/ports/raspberrypi/common-hal/microcontroller/Pin.c index f0a9a7c29c080..65c46e7f387d7 100644 --- a/ports/raspberrypi/common-hal/microcontroller/Pin.c +++ b/ports/raspberrypi/common-hal/microcontroller/Pin.c @@ -43,6 +43,17 @@ void reset_pin_number_cyw(uint8_t pin_no) { } #endif +#if CIRCUITPY_TCA9555 +#include "shared-bindings/tca9555/__init__.h" + +bool tca_ever_init; +static uint32_t tca_pin_claimed; + +void reset_pin_number_tca(uint8_t pin_no) { + tca_pin_claimed &= ~(1 << pin_no); +} +#endif + STATIC uint32_t never_reset_pins; void reset_all_pins(void) { @@ -61,6 +72,16 @@ void reset_all_pins(void) { } cyw_pin_claimed = 0; #endif + #if CIRCUITPY_TCA9555 + if (tca_ever_init) { + // reset LED and SMPS_MODE to Low; don't touch VBUS_SENSE + // otherwise it is switched to output mode forever! + // tca9555_gpio_put(0, 0); + // tca9555_gpio_put(1, 0); + // TODO This is where we would turn off the main output of Yukon + } + tca_pin_claimed = 0; + #endif } void never_reset_pin_number(uint8_t pin_number) { @@ -98,6 +119,12 @@ void common_hal_reset_pin(const mcu_pin_obj_t *pin) { return; } #endif + #if CIRCUITPY_TCA9555 + if (pin->base.type == &tca_pin_type) { + reset_pin_number_tca(pin->number); + return; + } + #endif reset_pin_number(pin->number); } @@ -107,6 +134,11 @@ void claim_pin(const mcu_pin_obj_t *pin) { cyw_pin_claimed |= (1 << pin->number); } #endif + #if CIRCUITPY_TCA9555 + if (pin->base.type == &tca_pin_type) { + tca_pin_claimed |= (1 << pin->number); + } + #endif // Nothing to do because all changes will set the GPIO settings. } @@ -126,6 +158,11 @@ bool common_hal_mcu_pin_is_free(const mcu_pin_obj_t *pin) { return !(cyw_pin_claimed & (1 << pin->number)); } #endif + #if CIRCUITPY_TCA9555 + if (pin->base.type == &tca_pin_type) { + return !(tca_pin_claimed & (1 << pin->number)); + } + #endif return pin_number_is_free(pin->number); } diff --git a/ports/raspberrypi/common-hal/microcontroller/Pin.h b/ports/raspberrypi/common-hal/microcontroller/Pin.h index 1c400fe48e551..331ff2e6bc781 100644 --- a/ports/raspberrypi/common-hal/microcontroller/Pin.h +++ b/ports/raspberrypi/common-hal/microcontroller/Pin.h @@ -46,5 +46,9 @@ bool pin_number_is_free(uint8_t pin_number); extern bool cyw_ever_init; void reset_pin_number_cyw(uint8_t pin_number); #endif +#if CIRCUITPY_TCA9555 +extern bool tca_ever_init; +void reset_pin_number_tca(uint8_t pin_number); +#endif #endif // MICROPY_INCLUDED_RASPBERRYPI_COMMON_HAL_MICROCONTROLLER_PIN_H diff --git a/py/argcheck.c b/py/argcheck.c index c3bde804d441f..23e9abc76ac3b 100644 --- a/py/argcheck.c +++ b/py/argcheck.c @@ -288,6 +288,13 @@ mp_int_t mp_arg_validate_type_int(mp_obj_t obj, qstr arg_name) { return an_int; } +mp_obj_t mp_arg_validate_type_or_type(mp_obj_t obj, const mp_obj_type_t *type1, const mp_obj_type_t *type2, qstr arg_name) { + if (!mp_obj_is_type(obj, type1) && !mp_obj_is_type(obj, type2)) { + mp_raise_TypeError_varg(translate("%q must be of type %q or %q, not %q"), arg_name, type1->name, type2->name, mp_obj_get_type(obj)->name); + } + return obj; +} + NORETURN void mp_arg_error_invalid(qstr arg_name) { mp_raise_ValueError_varg(translate("Invalid %q"), arg_name); } diff --git a/py/circuitpy_defns.mk b/py/circuitpy_defns.mk index 8be49dca0d555..96defb837668a 100644 --- a/py/circuitpy_defns.mk +++ b/py/circuitpy_defns.mk @@ -347,6 +347,9 @@ endif ifeq ($(CIRCUITPY_FONTIO),1) SRC_PATTERNS += fontio/% endif +ifeq ($(CIRCUITPY_TCA9555),1) +SRC_PATTERNS += tca9555/% +endif ifeq ($(CIRCUITPY_TIME),1) SRC_PATTERNS += time/% endif @@ -656,6 +659,7 @@ SRC_SHARED_MODULE_ALL = \ synthio/Note.c \ synthio/Synthesizer.c \ synthio/__init__.c \ + tca9555/__init__.c \ terminalio/Terminal.c \ terminalio/__init__.c \ time/__init__.c \ diff --git a/py/circuitpy_mpconfig.mk b/py/circuitpy_mpconfig.mk index c400707ac1fe5..d78bed3ba73a2 100644 --- a/py/circuitpy_mpconfig.mk +++ b/py/circuitpy_mpconfig.mk @@ -187,6 +187,9 @@ CFLAGS += -DCIRCUITPY_COMPUTED_GOTO_SAVE_SPACE=$(CIRCUITPY_COMPUTED_GOTO_SAVE_SP CIRCUITPY_CYW43 ?= 0 CFLAGS += -DCIRCUITPY_CYW43=$(CIRCUITPY_CYW43) +CIRCUITPY_TCA9555 ?= 0 +CFLAGS += -DCIRCUITPY_TCA9555=$(CIRCUITPY_TCA9555) + CIRCUITPY_DIGITALIO ?= 1 CFLAGS += -DCIRCUITPY_DIGITALIO=$(CIRCUITPY_DIGITALIO) diff --git a/py/runtime.h b/py/runtime.h index 5ecab91e99ab8..1b4bdd23fdb2b 100644 --- a/py/runtime.h +++ b/py/runtime.h @@ -127,6 +127,7 @@ mp_obj_t mp_arg_validate_type_in(mp_obj_t obj, const mp_obj_type_t *type, qstr a mp_obj_t mp_arg_validate_type_or_none(mp_obj_t obj, const mp_obj_type_t *type, qstr arg_name); mp_int_t mp_arg_validate_type_int(mp_obj_t obj, qstr arg_name); mp_obj_t mp_arg_validate_type_string(mp_obj_t obj, qstr arg_name); +mp_obj_t mp_arg_validate_type_or_type(mp_obj_t obj, const mp_obj_type_t *type1, const mp_obj_type_t *type2, qstr arg_name); static MP_INLINE mp_obj_dict_t *mp_locals_get(void) { return MP_STATE_THREAD(dict_locals); diff --git a/shared-bindings/digitalio/DigitalInOut.c b/shared-bindings/digitalio/DigitalInOut.c index 832f16558ac16..b8ef94137e7fb 100644 --- a/shared-bindings/digitalio/DigitalInOut.c +++ b/shared-bindings/digitalio/DigitalInOut.c @@ -68,8 +68,12 @@ STATIC void check_result(digitalinout_result_t result) { } } -MP_WEAK const mcu_pin_obj_t *common_hal_digitalio_validate_pin(mp_obj_t obj) { - return validate_obj_is_free_pin(obj, MP_QSTR_pin); +MP_WEAK const mcu_pin_obj_t *common_hal_digitalio_validate_pin(mp_obj_t obj, qstr arg_name) { + return validate_obj_is_free_pin(obj, arg_name); +} + +MP_WEAK const mcu_pin_obj_t *common_hal_digitalio_validate_pin_or_none(mp_obj_t obj, qstr arg_name) { + return validate_obj_is_free_pin_or_none(obj, arg_name); } //| class DigitalInOut: @@ -92,7 +96,7 @@ STATIC mp_obj_t digitalio_digitalinout_make_new(const mp_obj_type_t *type, digitalio_digitalinout_obj_t *self = mp_obj_malloc(digitalio_digitalinout_obj_t, &digitalio_digitalinout_type); - const mcu_pin_obj_t *pin = common_hal_digitalio_validate_pin(args[0]); + const mcu_pin_obj_t *pin = common_hal_digitalio_validate_pin(args[0], MP_QSTR_pin); common_hal_digitalio_digitalinout_construct(self, pin); return MP_OBJ_FROM_PTR(self); diff --git a/shared-bindings/digitalio/DigitalInOut.h b/shared-bindings/digitalio/DigitalInOut.h index 79a700c9059cc..debcb0c5747d7 100644 --- a/shared-bindings/digitalio/DigitalInOut.h +++ b/shared-bindings/digitalio/DigitalInOut.h @@ -57,7 +57,8 @@ typedef enum { DIGITALINOUT_REG_TOGGLE, } digitalinout_reg_op_t; -const mcu_pin_obj_t *common_hal_digitalio_validate_pin(mp_obj_t obj); +const mcu_pin_obj_t *common_hal_digitalio_validate_pin(mp_obj_t obj, qstr arg_name); +const mcu_pin_obj_t *common_hal_digitalio_validate_pin_or_none(mp_obj_t obj, qstr arg_name); digitalinout_result_t common_hal_digitalio_digitalinout_construct(digitalio_digitalinout_obj_t *self, const mcu_pin_obj_t *pin); void common_hal_digitalio_digitalinout_deinit(digitalio_digitalinout_obj_t *self); bool common_hal_digitalio_digitalinout_deinited(digitalio_digitalinout_obj_t *self); diff --git a/shared-bindings/displayio/FourWire.c b/shared-bindings/displayio/FourWire.c index 29cdf09299d41..ad20de2836d70 100644 --- a/shared-bindings/displayio/FourWire.c +++ b/shared-bindings/displayio/FourWire.c @@ -89,9 +89,9 @@ STATIC mp_obj_t displayio_fourwire_make_new(const mp_obj_type_t *type, size_t n_ 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); - const mcu_pin_obj_t *command = validate_obj_is_free_pin_or_none(args[ARG_command].u_obj, MP_QSTR_command); - const mcu_pin_obj_t *chip_select = validate_obj_is_free_pin(args[ARG_chip_select].u_obj, MP_QSTR_chip_select); - const mcu_pin_obj_t *reset = validate_obj_is_free_pin_or_none(args[ARG_reset].u_obj, MP_QSTR_reset); + const mcu_pin_obj_t *command = common_hal_digitalio_validate_pin_or_none(args[ARG_command].u_obj, MP_QSTR_command); + const mcu_pin_obj_t *chip_select = common_hal_digitalio_validate_pin(args[ARG_chip_select].u_obj, MP_QSTR_chip_select); + const mcu_pin_obj_t *reset = common_hal_digitalio_validate_pin_or_none(args[ARG_reset].u_obj, MP_QSTR_reset); mp_obj_t spi = mp_arg_validate_type(args[ARG_spi_bus].u_obj, &busio_spi_type, MP_QSTR_spi_bus); diff --git a/shared-bindings/tca9555/__init__.c b/shared-bindings/tca9555/__init__.c new file mode 100644 index 0000000000000..9a45440e7a619 --- /dev/null +++ b/shared-bindings/tca9555/__init__.c @@ -0,0 +1,357 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2023 Christopher Parrott for Pimoroni Ltd + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "py/runtime.h" + +#include "shared-bindings/board/__init__.h" +#include "shared-bindings/tca9555/__init__.h" +#include "shared-module/tca9555/__init__.h" + +//| class TcaPin: +//| """A class that represents a GPIO pin attached to a TCA9555 IO expander chip. +//| +//| Cannot be constructed at runtime, but may be the type of a pin object +//| in :py:mod:`board`. A `TcaPin` can be used as a DigitalInOut, but not with other +//| peripherals such as `PWMOut`.""" +//| + +const mp_obj_type_t tca_pin_type = { + { &mp_type_type }, + .flags = MP_TYPE_FLAG_EXTENDED, + .name = MP_QSTR_TcaPin, + .print = shared_bindings_tca9555_pin_print, + MP_TYPE_EXTENDED_FIELDS( + .unary_op = mp_generic_unary_op, + ) +}; + +const mcu_pin_obj_t *validate_obj_is_pin_including_tca(mp_obj_t obj, qstr arg_name) { + return MP_OBJ_TO_PTR(mp_arg_validate_type_or_type(obj, &mcu_pin_type, &tca_pin_type, arg_name)); +} + +const mcu_pin_obj_t *validate_obj_is_free_pin_including_tca(mp_obj_t obj, qstr arg_name) { + const mcu_pin_obj_t *pin = validate_obj_is_pin_including_tca(obj, arg_name); + assert_pin_free(pin); + return pin; +} + +// Validate that the obj is a free pin or None. Return an mcu_pin_obj_t* or NULL, correspondingly. +const mcu_pin_obj_t *validate_obj_is_free_pin_including_tca_or_none(mp_obj_t obj, qstr arg_name) { + if (obj == mp_const_none) { + return NULL; + } + return validate_obj_is_free_pin_including_tca(obj, arg_name); +} + +//| def get_number(pin: TcaPin) -> int: +//| """Get the TCA9555 pin number (from 0 to 15) of the provided TcaPin. +//| +//| :param TcaPin pin: a TCA pin object""" +//| ... +//| +STATIC mp_obj_t tca_pin_get_number(mp_obj_t pin_obj) { + mcu_pin_obj_t *pin = MP_OBJ_TO_PTR(mp_arg_validate_type(pin_obj, &tca_pin_type, MP_QSTR_pin)); + uint8_t tca_gpio = mp_arg_validate_index_range(pin->number, 0, TCA9555_VIRTUAL_GPIO_COUNT - 1, MP_QSTR_pin_number); + + return mp_obj_new_int(tca_gpio % TCA9555_GPIO_COUNT); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(tca_pin_get_number_obj, tca_pin_get_number); + +//| def get_chip(pin: TcaPin) -> int: +//| """Get the TCA9555 chip number of the provided TcaPin. +//| +//| :param TcaPin pin: a TCA pin object""" +//| ... +//| +STATIC mp_obj_t tca_pin_get_chip(mp_obj_t pin_obj) { + mcu_pin_obj_t *pin = MP_OBJ_TO_PTR(mp_arg_validate_type(pin_obj, &tca_pin_type, MP_QSTR_pin)); + uint8_t tca_gpio = mp_arg_validate_index_range(pin->number, 0, TCA9555_VIRTUAL_GPIO_COUNT - 1, MP_QSTR_pin_number); + + return mp_obj_new_int(CHIP_FROM_GPIO(tca_gpio)); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(tca_pin_get_chip_obj, tca_pin_get_chip); + +//| def change_output_mask(chip: int, mask: int, state: int) -> None: +//| """Change the output state of a set of pins on a single TCA9555 chip. +//| +//| :param int chip: the number of the TCA9555 chip to use +//| :param int mask: which of the 16 pins on the chip to modify +//| :param int state: the state the masked pins should have""" +//| ... +//| +STATIC mp_obj_t tca_pin_change_output_mask(mp_obj_t chip_obj, mp_obj_t mask_obj, mp_obj_t state_obj) { + uint8_t chip = mp_arg_validate_int_range(mp_obj_get_int(chip_obj), 0, TCA9555_CHIP_COUNT - 1, MP_QSTR_chip); + uint16_t mask = mp_arg_validate_int_range(mp_obj_get_int(mask_obj), 0, UINT16_MAX, MP_QSTR_mask); + uint16_t state = mp_arg_validate_int_range(mp_obj_get_int(state_obj), 0, UINT16_MAX, MP_QSTR_state); + + common_hal_tca_change_output_mask(chip, mask, state); + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_3(tca_pin_change_output_mask_obj, tca_pin_change_output_mask); + +//| def change_config_mask(chip: int, mask: int, state: int) -> None: +//| """Change the config state of a set of pins on a single TCA9555 chip. +//| +//| :param int chip: the number of the TCA9555 chip to use +//| :param int mask: which of the 16 pins on the chip to change the direction of +//| :param int state: the directions the masked pins should have""" +//| ... +//| +STATIC mp_obj_t tca_pin_change_config_mask(mp_obj_t chip_obj, mp_obj_t mask_obj, mp_obj_t state_obj) { + uint8_t chip = mp_arg_validate_int_range(mp_obj_get_int(chip_obj), 0, TCA9555_CHIP_COUNT - 1, MP_QSTR_chip); + uint16_t mask = mp_arg_validate_int_range(mp_obj_get_int(mask_obj), 0, UINT16_MAX, MP_QSTR_mask); + uint16_t state = mp_arg_validate_int_range(mp_obj_get_int(state_obj), 0, UINT16_MAX, MP_QSTR_state); + + common_hal_tca_change_config_mask(chip, mask, state); + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_3(tca_pin_change_config_mask_obj, tca_pin_change_config_mask); + +//| def change_polarity_mask(chip: int, mask: int, state: int) -> None: +//| """Change the output polarity state of a set of pins on a single TCA9555 chip. +//| +//| :param int chip: the number of the TCA9555 chip to use +//| :param int mask: which of the 16 pins on the chip to modify +//| :param int state: the polarity state the masked pins should have""" +//| ... +//| +STATIC mp_obj_t tca_pin_change_polarity_mask(mp_obj_t chip_obj, mp_obj_t mask_obj, mp_obj_t state_obj) { + uint8_t chip = mp_arg_validate_int_range(mp_obj_get_int(chip_obj), 0, TCA9555_CHIP_COUNT - 1, MP_QSTR_chip); + uint16_t mask = mp_arg_validate_int_range(mp_obj_get_int(mask_obj), 0, UINT16_MAX, MP_QSTR_mask); + uint16_t state = mp_arg_validate_int_range(mp_obj_get_int(state_obj), 0, UINT16_MAX, MP_QSTR_state); + + common_hal_tca_change_polarity_mask(chip, mask, state); + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_3(tca_pin_change_polarity_mask_obj, tca_pin_change_polarity_mask); + +#if TCA9555_READ_INTERNALS +//| def read_input(chip: int) -> int: +//| """Read the input state of a single TCA9555 chip. +//| +//| :param int chip: the number of the TCA9555 chip to read""" +//| ... +//| +STATIC mp_obj_t tca_port_read_input_state(mp_obj_t chip_obj) { + uint chip = mp_arg_validate_int_range(mp_obj_get_int(chip_obj), 0, TCA9555_CHIP_COUNT - 1, MP_QSTR_chip); + return mp_obj_new_int(common_hal_tca_get_input_port(chip)); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(tca_port_read_input_state_obj, tca_port_read_input_state); + +//| def read_output(chip: int) -> int: +//| """Read the output state of a single TCA9555 chip. +//| +//| :param int chip: the number of the TCA9555 chip to read""" +//| ... +//| +STATIC mp_obj_t tca_port_read_output_state(mp_obj_t chip_obj) { + uint chip = mp_arg_validate_int_range(mp_obj_get_int(chip_obj), 0, TCA9555_CHIP_COUNT - 1, MP_QSTR_chip); + return mp_obj_new_int(common_hal_tca_get_output_port(chip)); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(tca_port_read_output_state_obj, tca_port_read_output_state); + +//| def read_config(chip: int) -> int: +//| """Read the config state of a single TCA9555 chip. +//| +//| :param int chip: the number of the TCA9555 chip to read""" +//| ... +//| +STATIC mp_obj_t tca_port_read_config_state(mp_obj_t chip_obj) { + uint chip = mp_arg_validate_int_range(mp_obj_get_int(chip_obj), 0, TCA9555_CHIP_COUNT - 1, MP_QSTR_chip); + return mp_obj_new_int(common_hal_tca_get_config_port(chip)); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(tca_port_read_config_state_obj, tca_port_read_config_state); + +//| def read_polarity(chip: int) -> int: +//| """Read the polarity state of a single TCA9555 chip. +//| +//| :param int chip: the number of the TCA9555 chip to read""" +//| ... +//| +STATIC mp_obj_t tca_port_read_polarity_state(mp_obj_t chip_obj) { + uint chip = mp_arg_validate_int_range(mp_obj_get_int(chip_obj), 0, TCA9555_CHIP_COUNT - 1, MP_QSTR_chip); + return mp_obj_new_int(common_hal_tca_get_polarity_port(chip)); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(tca_port_read_polarity_state_obj, tca_port_read_polarity_state); + +#if TCA9555_LOCAL_MEMORY +//| def stored_output(chip: int) -> int: +//| """Read the stored output state of a single TCA9555 chip. +//| +//| :param int chip: the number of the TCA9555 chip to read the stored state of""" +//| ... +//| +STATIC mp_obj_t tca_port_stored_output_state(mp_obj_t chip_obj) { + uint chip = mp_arg_validate_int_range(mp_obj_get_int(chip_obj), 0, TCA9555_CHIP_COUNT - 1, MP_QSTR_chip); + return mp_obj_new_int((tca9555_output_state[HIGH_BYTE(chip)] << 8) | tca9555_output_state[LOW_BYTE(chip)]); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(tca_port_stored_output_state_obj, tca_port_stored_output_state); + +//| def stored_config(chip: int) -> int: +//| """Read the stored config state of a single TCA9555 chip. +//| +//| :param int chip: the number of the TCA9555 chip to read the stored state of""" +//| ... +//| +STATIC mp_obj_t tca_port_stored_config_state(mp_obj_t chip_obj) { + uint chip = mp_arg_validate_int_range(mp_obj_get_int(chip_obj), 0, TCA9555_CHIP_COUNT - 1, MP_QSTR_chip); + return mp_obj_new_int((tca9555_config_state[HIGH_BYTE(chip)] << 8) | tca9555_config_state[LOW_BYTE(chip)]); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(tca_port_stored_config_state_obj, tca_port_stored_config_state); + +//| def stored_polarity(chip: int) -> int: +//| """Read the stored polarity state of a single TCA9555 chip. +//| +//| :param int chip: the number of the TCA9555 chip to read the stored state of""" +//| ... +//| +STATIC mp_obj_t tca_port_stored_polarity_state(mp_obj_t chip_obj) { + uint chip = mp_arg_validate_int_range(mp_obj_get_int(chip_obj), 0, TCA9555_CHIP_COUNT - 1, MP_QSTR_chip); + return mp_obj_new_int((tca9555_polarity_state[HIGH_BYTE(chip)] << 8) | tca9555_polarity_state[LOW_BYTE(chip)]); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(tca_port_stored_polarity_state_obj, tca_port_stored_polarity_state); +#endif +#endif + +#define TCA_PIN(exp_number, p_number) \ + const mcu_pin_obj_t pin_TCA##exp_number##_##p_number = { \ + { &tca_pin_type }, \ + .number = p_number + (exp_number << 4) \ + } + +#define TCA_PINS(exp_number) \ + TCA_PIN(exp_number, 0); \ + TCA_PIN(exp_number, 1); \ + TCA_PIN(exp_number, 2); \ + TCA_PIN(exp_number, 3); \ + TCA_PIN(exp_number, 4); \ + TCA_PIN(exp_number, 5); \ + TCA_PIN(exp_number, 6); \ + TCA_PIN(exp_number, 7); \ + TCA_PIN(exp_number, 8); \ + TCA_PIN(exp_number, 9); \ + TCA_PIN(exp_number, 10); \ + TCA_PIN(exp_number, 11); \ + TCA_PIN(exp_number, 12); \ + TCA_PIN(exp_number, 13); \ + TCA_PIN(exp_number, 14); \ + TCA_PIN(exp_number, 15) + +#if CIRCUITPY_TCA9555 +TCA_PINS(0); +TCA_PINS(1); +#endif + +#define TCA_ENTRY(exp_number, p_number) \ + { MP_ROM_QSTR(MP_QSTR_TCA##exp_number##_##p_number), MP_ROM_PTR(&pin_TCA##exp_number##_##p_number) } + +#define TCA_ENTRIES(exp_number) \ + TCA_ENTRY(exp_number, 0), \ + TCA_ENTRY(exp_number, 1), \ + TCA_ENTRY(exp_number, 2), \ + TCA_ENTRY(exp_number, 3), \ + TCA_ENTRY(exp_number, 4), \ + TCA_ENTRY(exp_number, 5), \ + TCA_ENTRY(exp_number, 6), \ + TCA_ENTRY(exp_number, 7), \ + TCA_ENTRY(exp_number, 8), \ + TCA_ENTRY(exp_number, 9), \ + TCA_ENTRY(exp_number, 10), \ + TCA_ENTRY(exp_number, 11), \ + TCA_ENTRY(exp_number, 12), \ + TCA_ENTRY(exp_number, 13), \ + TCA_ENTRY(exp_number, 14), \ + TCA_ENTRY(exp_number, 15) + +STATIC const mp_rom_map_elem_t tca_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_tca) }, + { MP_ROM_QSTR(MP_QSTR_TcaPin), MP_ROM_PTR(&tca_pin_type) }, + { MP_ROM_QSTR(MP_QSTR_get_number), &tca_pin_get_number_obj }, + { MP_ROM_QSTR(MP_QSTR_get_chip), &tca_pin_get_chip_obj }, + { MP_ROM_QSTR(MP_QSTR_change_output_mask), &tca_pin_change_output_mask_obj }, + { MP_ROM_QSTR(MP_QSTR_change_config_mask), &tca_pin_change_config_mask_obj }, + { MP_ROM_QSTR(MP_QSTR_change_polarity_mask), &tca_pin_change_polarity_mask_obj }, + #if TCA9555_READ_INTERNALS + { MP_ROM_QSTR(MP_QSTR_read_input), &tca_port_read_input_state_obj }, + { MP_ROM_QSTR(MP_QSTR_read_output), &tca_port_read_output_state_obj }, + { MP_ROM_QSTR(MP_QSTR_read_config), &tca_port_read_config_state_obj }, + { MP_ROM_QSTR(MP_QSTR_read_polarity), &tca_port_read_polarity_state_obj }, + #if TCA9555_LOCAL_MEMORY + { MP_ROM_QSTR(MP_QSTR_stored_output), &tca_port_stored_output_state_obj }, + { MP_ROM_QSTR(MP_QSTR_stored_config), &tca_port_stored_config_state_obj }, + { MP_ROM_QSTR(MP_QSTR_stored_polarity), &tca_port_stored_polarity_state_obj }, + #endif + #endif + TCA_ENTRIES(0), + TCA_ENTRIES(1) +}; + +MP_DEFINE_CONST_DICT(tca_module_globals, tca_module_globals_table); + +const mp_obj_module_t tca_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&tca_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_tca, tca_module); + +static void get_pin_name(const mcu_pin_obj_t *self, qstr *package, qstr *module, qstr *name) { + const mp_map_t *board_map = &board_module_globals.map; + for (uint8_t i = 0; i < board_map->alloc; i++) { + if (board_map->table[i].value == MP_OBJ_FROM_PTR(self)) { + *package = 0; + *module = MP_QSTR_board; + *name = MP_OBJ_QSTR_VALUE(board_map->table[i].key); + return; + } + } + const mp_map_t *tca_map = &tca_module_globals.map; + for (uint8_t i = 0; i < tca_map->alloc; i++) { + if (tca_map->table[i].value == MP_OBJ_FROM_PTR(self)) { + *package = 0; + *module = MP_QSTR_tca; + *name = MP_OBJ_QSTR_VALUE(tca_map->table[i].key); + return; + } + } +} + +void shared_bindings_tca9555_pin_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + mcu_pin_obj_t *self = MP_OBJ_TO_PTR(self_in); + qstr package = MP_QSTR_Pin; + qstr module = MP_QSTR_pin; + qstr name = MP_QSTR_Pin; + + get_pin_name(self, &package, &module, &name); + if (package) { + mp_printf(print, "%q.%q.%q", package, module, name); + } else { + mp_printf(print, "%q.%q", module, name); + } +} diff --git a/shared-bindings/tca9555/__init__.h b/shared-bindings/tca9555/__init__.h new file mode 100644 index 0000000000000..9f9b69945835e --- /dev/null +++ b/shared-bindings/tca9555/__init__.h @@ -0,0 +1,119 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2023 Christopher Parrott for Pimoroni Ltd + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef MICROPY_INCLUDED_SHARED_BINDINGS_TCA9555___INIT___H +#define MICROPY_INCLUDED_SHARED_BINDINGS_TCA9555___INIT___H + +#include "common-hal/microcontroller/Pin.h" + +// Type object used in Python. Should be shared between ports. +extern const mp_obj_dict_t tca_pin_globals; +extern const mp_obj_type_t tca_pin_type; + +const mcu_pin_obj_t *validate_obj_is_free_pin_including_tca(mp_obj_t obj, qstr arg_name); +const mcu_pin_obj_t *validate_obj_is_free_pin_including_tca_or_none(mp_obj_t obj, qstr arg_name); +const mcu_pin_obj_t *validate_obj_is_pin_including_tca(mp_obj_t obj, qstr arg_name); + +extern bool common_hal_tca_gpio_get_input(uint tca_gpio); +extern bool common_hal_tca_gpio_get_output(uint tca_gpio); +extern bool common_hal_tca_gpio_get_config(uint tca_gpio); +extern bool common_hal_tca_gpio_get_polarity(uint tca_gpio); + +extern void common_hal_tca_gpio_set_output(uint tca_gpio, bool value); +extern void common_hal_tca_gpio_set_config(uint tca_gpio, bool output); +extern void common_hal_tca_gpio_set_polarity(uint tca_gpio, bool polarity); + +extern uint16_t common_hal_tca_get_input_port(uint tca_index); +extern uint8_t common_hal_tca_get_input_port_low(uint tca_index); +extern uint8_t common_hal_tca_get_input_port_high(uint tca_index); + +extern uint16_t common_hal_tca_get_output_port(uint tca_index); +extern uint8_t common_hal_tca_get_output_port_low(uint tca_index); +extern uint8_t common_hal_tca_get_output_port_high(uint tca_index); + +extern uint16_t common_hal_tca_get_config_port(uint tca_index); +extern uint8_t common_hal_tca_get_config_port_low(uint tca_index); +extern uint8_t common_hal_tca_get_config_port_high(uint tca_index); + +extern uint16_t common_hal_tca_get_polarity_port(uint tca_index); +extern uint8_t common_hal_tca_get_polarity_port_low(uint tca_index); +extern uint8_t common_hal_tca_get_polarity_port_high(uint tca_index); + +extern void common_hal_tca_set_output_port(uint tca_index, uint16_t output_state); +extern void common_hal_tca_set_output_port_low(uint tca_index, uint8_t output_state); +extern void common_hal_tca_set_output_port_high(uint tca_index, uint8_t output_state); + +extern void common_hal_tca_set_config_port(uint tca_index, uint16_t config_state); +extern void common_hal_tca_set_config_port_low(uint tca_index, uint8_t config_state); +extern void common_hal_tca_set_config_port_high(uint tca_index, uint8_t config_state); + +extern void common_hal_tca_set_polarity_port(uint tca_index, uint16_t polarity_state); +extern void common_hal_tca_set_polarity_port_low(uint tca_index, uint8_t polarity_state); +extern void common_hal_tca_set_polarity_port_high(uint tca_index, uint8_t polarity_state); + +extern void common_hal_tca_change_output_mask(uint8_t chip, uint16_t mask, uint16_t state); +extern void common_hal_tca_change_config_mask(uint8_t chip, uint16_t mask, uint16_t state); +extern void common_hal_tca_change_polarity_mask(uint8_t chip, uint16_t mask, uint16_t state); + +void shared_bindings_tca9555_pin_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind); + +#if CIRCUITPY_TCA9555 +extern const mcu_pin_obj_t pin_TCA0_0; +extern const mcu_pin_obj_t pin_TCA0_1; +extern const mcu_pin_obj_t pin_TCA0_2; +extern const mcu_pin_obj_t pin_TCA0_3; +extern const mcu_pin_obj_t pin_TCA0_4; +extern const mcu_pin_obj_t pin_TCA0_5; +extern const mcu_pin_obj_t pin_TCA0_6; +extern const mcu_pin_obj_t pin_TCA0_7; +extern const mcu_pin_obj_t pin_TCA0_8; +extern const mcu_pin_obj_t pin_TCA0_9; +extern const mcu_pin_obj_t pin_TCA0_10; +extern const mcu_pin_obj_t pin_TCA0_11; +extern const mcu_pin_obj_t pin_TCA0_12; +extern const mcu_pin_obj_t pin_TCA0_13; +extern const mcu_pin_obj_t pin_TCA0_14; +extern const mcu_pin_obj_t pin_TCA0_15; + +extern const mcu_pin_obj_t pin_TCA1_0; +extern const mcu_pin_obj_t pin_TCA1_1; +extern const mcu_pin_obj_t pin_TCA1_2; +extern const mcu_pin_obj_t pin_TCA1_3; +extern const mcu_pin_obj_t pin_TCA1_4; +extern const mcu_pin_obj_t pin_TCA1_5; +extern const mcu_pin_obj_t pin_TCA1_6; +extern const mcu_pin_obj_t pin_TCA1_7; +extern const mcu_pin_obj_t pin_TCA1_8; +extern const mcu_pin_obj_t pin_TCA1_9; +extern const mcu_pin_obj_t pin_TCA1_10; +extern const mcu_pin_obj_t pin_TCA1_11; +extern const mcu_pin_obj_t pin_TCA1_12; +extern const mcu_pin_obj_t pin_TCA1_13; +extern const mcu_pin_obj_t pin_TCA1_14; +extern const mcu_pin_obj_t pin_TCA1_15; +#endif + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_TCA9555___INIT___H diff --git a/shared-module/board/__init__.c b/shared-module/board/__init__.c index 2d5a8a07e4e05..fd7881af4b356 100644 --- a/shared-module/board/__init__.c +++ b/shared-module/board/__init__.c @@ -84,7 +84,11 @@ mp_obj_t common_hal_board_create_i2c(const mp_int_t instance) { assert_pin_free(i2c_pin[instance].scl); assert_pin_free(i2c_pin[instance].sda); + #ifdef CIRCUITPY_BOARD_I2C_FREQ + common_hal_busio_i2c_construct(self, i2c_pin[instance].scl, i2c_pin[instance].sda, CIRCUITPY_BOARD_I2C_FREQ, 255); + #else common_hal_busio_i2c_construct(self, i2c_pin[instance].scl, i2c_pin[instance].sda, 100000, 255); + #endif i2c_obj_created[instance] = true; return &i2c_obj[instance]; diff --git a/shared-module/tca9555/__init__.c b/shared-module/tca9555/__init__.c new file mode 100644 index 0000000000000..5c3d148833cbf --- /dev/null +++ b/shared-module/tca9555/__init__.c @@ -0,0 +1,548 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2023 Christopher Parrott for Pimoroni Ltd + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "shared-bindings/board/__init__.h" +#include "shared-bindings/tca9555/__init__.h" +#include "shared-bindings/busio/I2C.h" +#include "shared-module/tca9555/__init__.h" + +// PICO_CONFIG: PARAM_ASSERTIONS_ENABLED_TCA9555, Enable/disable assertions in the pico_tca9555 module, type=bool, default=0, group=pico_tca9555 +#ifndef PARAM_ASSERTIONS_ENABLED_TCA9555 +#define PARAM_ASSERTIONS_ENABLED_TCA9555 0 +#endif + +static const uint8_t tca9555_addresses[TCA9555_CHIP_COUNT] = TCA9555_CHIP_ADDRESSES; +#if TCA9555_LOCAL_MEMORY +uint8_t tca9555_output_state[TCA9555_CHIP_COUNT * 2] = {0}; +uint8_t tca9555_config_state[TCA9555_CHIP_COUNT * 2] = {0}; +uint8_t tca9555_polarity_state[TCA9555_CHIP_COUNT * 2] = {0}; +#endif + +bool common_hal_tca_gpio_get_input(uint tca_gpio) { + invalid_params_if(TCA9555, tca_gpio >= TCA9555_VIRTUAL_GPIO_COUNT); + busio_i2c_obj_t *i2c = common_hal_board_create_i2c(0); + uint8_t address = ADDRESS_FROM_GPIO(tca_gpio); + + uint8_t reg = IS_PORT1(tca_gpio) ? INPUT_PORT1 : INPUT_PORT0; + uint8_t input_state = 0x00; + common_hal_busio_i2c_write_read(i2c, address, ®, 1, &input_state, 1); + return (input_state & GPIO_BIT_MASK(tca_gpio)) != 0; +} + +bool common_hal_tca_gpio_get_output(uint tca_gpio) { + invalid_params_if(TCA9555, tca_gpio >= TCA9555_VIRTUAL_GPIO_COUNT); + busio_i2c_obj_t *i2c = common_hal_board_create_i2c(0); + uint8_t address = ADDRESS_FROM_GPIO(tca_gpio); + + uint8_t reg = IS_PORT1(tca_gpio) ? OUTPUT_PORT1 : OUTPUT_PORT0; + uint8_t output_state = 0x00; + common_hal_busio_i2c_write_read(i2c, address, ®, 1, &output_state, 1); + #if TCA9555_LOCAL_MEMORY + tca9555_output_state[GPIO_BYTE(tca_gpio)] = output_state; + #endif + return (output_state & GPIO_BIT_MASK(tca_gpio)) != 0; +} + +bool common_hal_tca_gpio_get_config(uint tca_gpio) { + invalid_params_if(TCA9555, tca_gpio >= TCA9555_VIRTUAL_GPIO_COUNT); + busio_i2c_obj_t *i2c = common_hal_board_create_i2c(0); + uint8_t address = ADDRESS_FROM_GPIO(tca_gpio); + + uint8_t reg = IS_PORT1(tca_gpio) ? CONFIGURATION_PORT1 : CONFIGURATION_PORT0; + uint8_t config_state = 0x00; + common_hal_busio_i2c_write_read(i2c, address, ®, 1, &config_state, 1); + #if TCA9555_LOCAL_MEMORY + tca9555_config_state[GPIO_BYTE(tca_gpio)] = config_state; + #endif + return (config_state & GPIO_BIT_MASK(tca_gpio)) == 0; +} + +bool common_hal_tca_gpio_get_polarity(uint tca_gpio) { + invalid_params_if(TCA9555, tca_gpio >= TCA9555_VIRTUAL_GPIO_COUNT); + busio_i2c_obj_t *i2c = common_hal_board_create_i2c(0); + uint8_t address = ADDRESS_FROM_GPIO(tca_gpio); + + uint8_t reg = IS_PORT1(tca_gpio) ? POLARITY_PORT1 : POLARITY_PORT0; + uint8_t polarity_state = 0x00; + common_hal_busio_i2c_write_read(i2c, address, ®, 1, &polarity_state, 1); + #if TCA9555_LOCAL_MEMORY + tca9555_polarity_state[GPIO_BYTE(tca_gpio)] = polarity_state; + #endif + return (polarity_state & GPIO_BIT_MASK(tca_gpio)) != 0; +} + +void common_hal_tca_gpio_set_output(uint tca_gpio, bool value) { + invalid_params_if(TCA9555, tca_gpio >= TCA9555_VIRTUAL_GPIO_COUNT); + busio_i2c_obj_t *i2c = common_hal_board_create_i2c(0); + uint8_t address = ADDRESS_FROM_GPIO(tca_gpio); + + uint8_t reg = IS_PORT1(tca_gpio) ? OUTPUT_PORT1 : OUTPUT_PORT0; + #if TCA9555_LOCAL_MEMORY + uint8_t output_state = tca9555_output_state[GPIO_BYTE(tca_gpio)]; + #else + uint8_t output_state = 0x00; + common_hal_busio_i2c_write_read(i2c, address, ®, 1, &output_state, 1); + #endif + uint8_t new_output_state; + if (value) { + new_output_state = output_state | GPIO_BIT_MASK(tca_gpio); + } else { + new_output_state = output_state & ~GPIO_BIT_MASK(tca_gpio); + } + + if (new_output_state != output_state) { + uint8_t reg_and_data[2] = { reg, new_output_state }; + common_hal_busio_i2c_write(i2c, address, reg_and_data, 2); + #if TCA9555_LOCAL_MEMORY + tca9555_output_state[GPIO_BYTE(tca_gpio)] = new_output_state; + #endif + } +} + +void common_hal_tca_gpio_set_config(uint tca_gpio, bool output) { + invalid_params_if(TCA9555, tca_gpio >= TCA9555_VIRTUAL_GPIO_COUNT); + busio_i2c_obj_t *i2c = common_hal_board_create_i2c(0); + uint8_t address = ADDRESS_FROM_GPIO(tca_gpio); + + uint8_t reg = IS_PORT1(tca_gpio) ? CONFIGURATION_PORT1 : CONFIGURATION_PORT0; + #if TCA9555_LOCAL_MEMORY + uint8_t config_state = tca9555_config_state[GPIO_BYTE(tca_gpio)]; + #else + uint8_t config_state = 0x00; + common_hal_busio_i2c_write_read(i2c, address, ®, 1, &config_state, 1); + #endif + uint8_t new_config_state; + if (output) { + new_config_state = config_state & ~GPIO_BIT_MASK(tca_gpio); + } else { + new_config_state = config_state | GPIO_BIT_MASK(tca_gpio); + } + + if (new_config_state != config_state) { + uint8_t reg_and_data[2] = { reg, new_config_state }; + common_hal_busio_i2c_write(i2c, address, reg_and_data, 2); + #if TCA9555_LOCAL_MEMORY + tca9555_config_state[GPIO_BYTE(tca_gpio)] = new_config_state; + #endif + } +} + +void common_hal_tca_gpio_set_polarity(uint tca_gpio, bool polarity) { + invalid_params_if(TCA9555, tca_gpio >= TCA9555_VIRTUAL_GPIO_COUNT); + busio_i2c_obj_t *i2c = common_hal_board_create_i2c(0); + uint8_t address = ADDRESS_FROM_GPIO(tca_gpio); + + uint8_t reg = IS_PORT1(tca_gpio) ? POLARITY_PORT1 : POLARITY_PORT0; + #if TCA9555_LOCAL_MEMORY + uint8_t polarity_state = tca9555_polarity_state[GPIO_BYTE(tca_gpio)]; + #else + uint8_t polarity_state = 0x00; + common_hal_busio_i2c_write_read(i2c, address, ®, 1, &polarity_state, 1); + #endif + uint8_t new_polarity_state; + if (polarity) { + new_polarity_state = polarity_state | GPIO_BIT_MASK(tca_gpio); + } else { + new_polarity_state = polarity_state & ~GPIO_BIT_MASK(tca_gpio); + } + + if (new_polarity_state != polarity_state) { + uint8_t reg_and_data[2] = { reg, new_polarity_state }; + common_hal_busio_i2c_write(i2c, address, reg_and_data, 2); + #if TCA9555_LOCAL_MEMORY + tca9555_output_state[GPIO_BYTE(tca_gpio)] = new_polarity_state; + #endif + } +} + +uint16_t common_hal_tca_get_input_port(uint tca_index) { + invalid_params_if(TCA9555, tca_index >= TCA9555_CHIP_COUNT); + busio_i2c_obj_t *i2c = common_hal_board_create_i2c(0); + + uint8_t reg = INPUT_PORT0; + uint16_t input_state = 0x0000; + common_hal_busio_i2c_write_read(i2c, tca9555_addresses[tca_index], ®, 1, (uint8_t *)&input_state, 2); + return input_state; +} + +uint8_t common_hal_tca_get_input_port_low(uint tca_index) { + invalid_params_if(TCA9555, tca_index >= TCA9555_CHIP_COUNT); + busio_i2c_obj_t *i2c = common_hal_board_create_i2c(0); + + uint8_t reg = INPUT_PORT0; + uint8_t input_state = 0x00; + common_hal_busio_i2c_write_read(i2c, tca9555_addresses[tca_index], ®, 1, &input_state, 1); + return input_state; +} + +uint8_t common_hal_tca_get_input_port_high(uint tca_index) { + invalid_params_if(TCA9555, tca_index >= TCA9555_CHIP_COUNT); + busio_i2c_obj_t *i2c = common_hal_board_create_i2c(0); + + uint8_t reg = INPUT_PORT1; + uint8_t input_state = 0x00; + common_hal_busio_i2c_write_read(i2c, tca9555_addresses[tca_index], ®, 1, &input_state, 1); + return input_state; +} + +uint16_t common_hal_tca_get_output_port(uint tca_index) { + invalid_params_if(TCA9555, tca_index >= TCA9555_CHIP_COUNT); + busio_i2c_obj_t *i2c = common_hal_board_create_i2c(0); + + uint8_t reg = OUTPUT_PORT0; + uint16_t output_state = 0x0000; + common_hal_busio_i2c_write_read(i2c, tca9555_addresses[tca_index], ®, 1, (uint8_t *)&output_state, 2); + #if TCA9555_LOCAL_MEMORY + tca9555_output_state[HIGH_BYTE(tca_index)] = (output_state >> 8); + tca9555_output_state[LOW_BYTE(tca_index)] = (output_state & 0xFF); + #endif + return output_state; +} + +uint8_t common_hal_tca_get_output_port_low(uint tca_index) { + invalid_params_if(TCA9555, tca_index >= TCA9555_CHIP_COUNT); + busio_i2c_obj_t *i2c = common_hal_board_create_i2c(0); + + uint8_t reg = OUTPUT_PORT0; + uint8_t output_state = 0x00; + common_hal_busio_i2c_write_read(i2c, tca9555_addresses[tca_index], ®, 1, &output_state, 1); + #if TCA9555_LOCAL_MEMORY + tca9555_output_state[LOW_BYTE(tca_index)] = output_state; + #endif + return output_state; +} + +uint8_t common_hal_tca_get_output_port_high(uint tca_index) { + invalid_params_if(TCA9555, tca_index >= TCA9555_CHIP_COUNT); + busio_i2c_obj_t *i2c = common_hal_board_create_i2c(0); + + uint8_t reg = OUTPUT_PORT1; + uint8_t output_state = 0x00; + common_hal_busio_i2c_write_read(i2c, tca9555_addresses[tca_index], ®, 1, &output_state, 1); + #if TCA9555_LOCAL_MEMORY + tca9555_output_state[HIGH_BYTE(tca_index)] = output_state; + #endif + return output_state; +} + +uint16_t common_hal_tca_get_config_port(uint tca_index) { + invalid_params_if(TCA9555, tca_index >= TCA9555_CHIP_COUNT); + busio_i2c_obj_t *i2c = common_hal_board_create_i2c(0); + + uint8_t reg = CONFIGURATION_PORT0; + uint16_t config_state = 0x0000; + common_hal_busio_i2c_write_read(i2c, tca9555_addresses[tca_index], ®, 1, (uint8_t *)&config_state, 2); + #if TCA9555_LOCAL_MEMORY + tca9555_config_state[HIGH_BYTE(tca_index)] = (config_state >> 8); + tca9555_config_state[LOW_BYTE(tca_index)] = (config_state & 0xFF); + #endif + return config_state; +} + +uint8_t common_hal_tca_get_config_port_low(uint tca_index) { + invalid_params_if(TCA9555, tca_index >= TCA9555_CHIP_COUNT); + busio_i2c_obj_t *i2c = common_hal_board_create_i2c(0); + + uint8_t reg = CONFIGURATION_PORT0; + uint8_t config_state = 0x00; + common_hal_busio_i2c_write_read(i2c, tca9555_addresses[tca_index], ®, 1, &config_state, 1); + #if TCA9555_LOCAL_MEMORY + tca9555_config_state[LOW_BYTE(tca_index)] = config_state; + #endif + return config_state; +} + +uint8_t common_hal_tca_get_config_port_high(uint tca_index) { + invalid_params_if(TCA9555, tca_index >= TCA9555_CHIP_COUNT); + busio_i2c_obj_t *i2c = common_hal_board_create_i2c(0); + + uint8_t reg = CONFIGURATION_PORT1; + uint8_t config_state = 0x00; + common_hal_busio_i2c_write_read(i2c, tca9555_addresses[tca_index], ®, 1, &config_state, 1); + #if TCA9555_LOCAL_MEMORY + tca9555_config_state[HIGH_BYTE(tca_index)] = config_state; + #endif + return config_state; +} + +uint16_t common_hal_tca_get_polarity_port(uint tca_index) { + invalid_params_if(TCA9555, tca_index >= TCA9555_CHIP_COUNT); + busio_i2c_obj_t *i2c = common_hal_board_create_i2c(0); + + uint8_t reg = POLARITY_PORT0; + uint16_t polarity_state = 0x0000; + common_hal_busio_i2c_write_read(i2c, tca9555_addresses[tca_index], ®, 1, (uint8_t *)&polarity_state, 2); + #if TCA9555_LOCAL_MEMORY + tca9555_polarity_state[HIGH_BYTE(tca_index)] = (polarity_state >> 8); + tca9555_polarity_state[LOW_BYTE(tca_index)] = (polarity_state & 0xFF); + #endif + return polarity_state; +} + +uint8_t common_hal_tca_get_polarity_port_low(uint tca_index) { + invalid_params_if(TCA9555, tca_index >= TCA9555_CHIP_COUNT); + busio_i2c_obj_t *i2c = common_hal_board_create_i2c(0); + + uint8_t reg = POLARITY_PORT0; + uint8_t polarity_state = 0x00; + common_hal_busio_i2c_write_read(i2c, tca9555_addresses[tca_index], ®, 1, &polarity_state, 1); + #if TCA9555_LOCAL_MEMORY + tca9555_polarity_state[LOW_BYTE(tca_index)] = polarity_state; + #endif + return polarity_state; +} + +uint8_t common_hal_tca_get_polarity_port_high(uint tca_index) { + invalid_params_if(TCA9555, tca_index >= TCA9555_CHIP_COUNT); + busio_i2c_obj_t *i2c = common_hal_board_create_i2c(0); + + uint8_t reg = POLARITY_PORT1; + uint8_t polarity_state = 0x00; + common_hal_busio_i2c_write_read(i2c, tca9555_addresses[tca_index], ®, 1, &polarity_state, 1); + #if TCA9555_LOCAL_MEMORY + tca9555_polarity_state[HIGH_BYTE(tca_index)] = polarity_state; + #endif + return polarity_state; +} + +void common_hal_tca_set_output_port(uint tca_index, uint16_t output_state) { + invalid_params_if(TCA9555, tca_index >= TCA9555_CHIP_COUNT); + busio_i2c_obj_t *i2c = common_hal_board_create_i2c(0); + uint8_t reg_and_data[3] = { OUTPUT_PORT0, output_state & 0xFF, (output_state >> 8) }; + common_hal_busio_i2c_write(i2c, tca9555_addresses[tca_index], reg_and_data, 3); + #if TCA9555_LOCAL_MEMORY + tca9555_output_state[HIGH_BYTE(tca_index)] = (output_state >> 8); + tca9555_output_state[LOW_BYTE(tca_index)] = (output_state & 0xFF); + #endif +} + +void common_hal_tca_set_output_port_low(uint tca_index, uint8_t output_state) { + invalid_params_if(TCA9555, tca_index >= TCA9555_CHIP_COUNT); + busio_i2c_obj_t *i2c = common_hal_board_create_i2c(0); + uint8_t reg_and_data[2] = { OUTPUT_PORT0, output_state }; + common_hal_busio_i2c_write(i2c, tca9555_addresses[tca_index], reg_and_data, 2); + #if TCA9555_LOCAL_MEMORY + tca9555_output_state[LOW_BYTE(tca_index)] = output_state; + #endif +} + +void common_hal_tca_set_output_port_high(uint tca_index, uint8_t output_state) { + invalid_params_if(TCA9555, tca_index >= TCA9555_CHIP_COUNT); + busio_i2c_obj_t *i2c = common_hal_board_create_i2c(0); + uint8_t reg_and_data[2] = { OUTPUT_PORT1, output_state }; + common_hal_busio_i2c_write(i2c, tca9555_addresses[tca_index], reg_and_data, 2); + #if TCA9555_LOCAL_MEMORY + tca9555_output_state[HIGH_BYTE(tca_index)] = output_state; + #endif +} + +void common_hal_tca_set_config_port(uint tca_index, uint16_t config_state) { + invalid_params_if(TCA9555, tca_index >= TCA9555_CHIP_COUNT); + busio_i2c_obj_t *i2c = common_hal_board_create_i2c(0); + uint8_t reg_and_data[3] = { CONFIGURATION_PORT0, config_state & 0xFF, (config_state >> 8) }; + common_hal_busio_i2c_write(i2c, tca9555_addresses[tca_index], reg_and_data, 3); + #if TCA9555_LOCAL_MEMORY + tca9555_config_state[HIGH_BYTE(tca_index)] = (config_state >> 8); + tca9555_config_state[LOW_BYTE(tca_index)] = (config_state & 0xFF); + #endif +} + +void common_hal_tca_set_config_port_low(uint tca_index, uint8_t config_state) { + invalid_params_if(TCA9555, tca_index >= TCA9555_CHIP_COUNT); + busio_i2c_obj_t *i2c = common_hal_board_create_i2c(0); + uint8_t reg_and_data[2] = { CONFIGURATION_PORT0, config_state }; + common_hal_busio_i2c_write(i2c, tca9555_addresses[tca_index], reg_and_data, 2); + #if TCA9555_LOCAL_MEMORY + tca9555_config_state[LOW_BYTE(tca_index)] = config_state; + #endif +} + +void common_hal_tca_set_config_port_high(uint tca_index, uint8_t config_state) { + invalid_params_if(TCA9555, tca_index >= TCA9555_CHIP_COUNT); + busio_i2c_obj_t *i2c = common_hal_board_create_i2c(0); + uint8_t reg_and_data[2] = { CONFIGURATION_PORT1, config_state }; + common_hal_busio_i2c_write(i2c, tca9555_addresses[tca_index], reg_and_data, 2); + #if TCA9555_LOCAL_MEMORY + tca9555_config_state[HIGH_BYTE(tca_index)] = config_state; + #endif +} + +void common_hal_tca_set_polarity_port(uint tca_index, uint16_t polarity_state) { + invalid_params_if(TCA9555, tca_index >= TCA9555_CHIP_COUNT); + busio_i2c_obj_t *i2c = common_hal_board_create_i2c(0); + uint8_t reg_and_data[3] = { POLARITY_PORT0, polarity_state & 0xFF, (polarity_state >> 8) }; + common_hal_busio_i2c_write(i2c, tca9555_addresses[tca_index], reg_and_data, 3); + #if TCA9555_LOCAL_MEMORY + tca9555_polarity_state[HIGH_BYTE(tca_index)] = (polarity_state >> 8); + tca9555_polarity_state[LOW_BYTE(tca_index)] = (polarity_state & 0xFF); + #endif +} + +void common_hal_tca_set_polarity_port_low(uint tca_index, uint8_t polarity_state) { + invalid_params_if(TCA9555, tca_index >= TCA9555_CHIP_COUNT); + busio_i2c_obj_t *i2c = common_hal_board_create_i2c(0); + uint8_t reg_and_data[2] = { POLARITY_PORT0, polarity_state }; + common_hal_busio_i2c_write(i2c, tca9555_addresses[tca_index], reg_and_data, 2); + #if TCA9555_LOCAL_MEMORY + tca9555_polarity_state[LOW_BYTE(tca_index)] = polarity_state; + #endif +} + +void common_hal_tca_set_polarity_port_high(uint tca_index, uint8_t polarity_state) { + invalid_params_if(TCA9555, tca_index >= TCA9555_CHIP_COUNT); + busio_i2c_obj_t *i2c = common_hal_board_create_i2c(0); + uint8_t reg_and_data[2] = { POLARITY_PORT1, polarity_state }; + common_hal_busio_i2c_write(i2c, tca9555_addresses[tca_index], reg_and_data, 2); + #if TCA9555_LOCAL_MEMORY + tca9555_polarity_state[HIGH_BYTE(tca_index)] = polarity_state; + #endif +} + +void common_hal_tca_change_output_mask(uint8_t chip, uint16_t mask, uint16_t state) { + uint8_t low_mask = (uint8_t)(mask & 0xFF); + uint8_t low_state = (uint8_t)(state & 0xFF); + uint8_t high_mask = (uint8_t)(mask >> 8); + uint8_t high_state = (uint8_t)(state >> 8); + bool low_changed = low_mask > 0; + bool high_changed = high_mask > 0; + if (low_changed && high_changed) { + #if TCA9555_LOCAL_MEMORY + uint16_t output_state = (tca9555_output_state[HIGH_BYTE(chip)] << 8) | tca9555_output_state[LOW_BYTE(chip)]; + #else + uint16_t output_state = common_hal_tca_get_output_port(chip); + #endif + uint16_t new_output_state = output_state; + new_output_state &= ~mask; // Clear the mask bits + new_output_state |= state; // Set the state bits + if (new_output_state != output_state) { + common_hal_tca_set_output_port(chip, new_output_state); + } + } else if (low_changed) { + #if TCA9555_LOCAL_MEMORY + uint8_t output_state = tca9555_output_state[LOW_BYTE(chip)]; + #else + uint8_t output_state = common_hal_tca_get_output_port_low(chip); + #endif + uint8_t new_output_state = (output_state & ~low_mask) | low_state; + if (new_output_state != output_state) { + common_hal_tca_set_output_port_low(chip, new_output_state); + } + } else if (high_changed) { + #if TCA9555_LOCAL_MEMORY + uint8_t output_state = tca9555_output_state[HIGH_BYTE(chip)]; + #else + uint8_t output_state = common_hal_tca_get_output_port_high(chip); + #endif + uint8_t new_output_state = (output_state & ~high_mask) | high_state; + if (new_output_state != output_state) { + common_hal_tca_set_output_port_high(chip, new_output_state); + } + } +} + +void common_hal_tca_change_config_mask(uint8_t chip, uint16_t mask, uint16_t state) { + uint8_t low_mask = (uint8_t)(mask & 0xFF); + uint8_t low_state = (uint8_t)(state & 0xFF); + uint8_t high_mask = (uint8_t)(mask >> 8); + uint8_t high_state = (uint8_t)(state >> 8); + bool low_changed = low_mask > 0; + bool high_changed = high_mask > 0; + if (low_changed && high_changed) { + #if TCA9555_LOCAL_MEMORY + uint16_t config_state = (tca9555_config_state[HIGH_BYTE(chip)] << 8) | tca9555_config_state[LOW_BYTE(chip)]; + #else + uint16_t config_state = common_hal_tca_get_config_port(chip); + #endif + uint16_t new_config_state = config_state; + new_config_state &= ~mask; // Clear the mask bits + new_config_state |= state; // Set the state bits + if (new_config_state != config_state) { + common_hal_tca_set_config_port(chip, new_config_state); + } + } else if (low_changed) { + #if TCA9555_LOCAL_MEMORY + uint8_t config_state = tca9555_config_state[LOW_BYTE(chip)]; + #else + uint8_t config_state = common_hal_tca_get_config_port_low(chip); + #endif + uint8_t new_config_state = (config_state & ~low_mask) | low_state; + if (new_config_state != config_state) { + common_hal_tca_set_config_port_low(chip, new_config_state); + } + } else if (high_changed) { + #if TCA9555_LOCAL_MEMORY + uint8_t config_state = tca9555_config_state[HIGH_BYTE(chip)]; + #else + uint8_t config_state = common_hal_tca_get_config_port_high(chip); + #endif + uint8_t new_config_state = (config_state & ~high_mask) | high_state; + if (new_config_state != config_state) { + common_hal_tca_set_config_port_high(chip, new_config_state); + } + } +} + +void common_hal_tca_change_polarity_mask(uint8_t chip, uint16_t mask, uint16_t state) { + uint8_t low_mask = (uint8_t)(mask & 0xFF); + uint8_t low_state = (uint8_t)(state & 0xFF); + uint8_t high_mask = (uint8_t)(mask >> 8); + uint8_t high_state = (uint8_t)(state >> 8); + bool low_changed = low_mask > 0; + bool high_changed = high_mask > 0; + if (low_changed && high_changed) { + #if TCA9555_LOCAL_MEMORY + uint16_t polarity_state = (tca9555_polarity_state[HIGH_BYTE(chip)] << 8) | tca9555_polarity_state[LOW_BYTE(chip)]; + #else + uint16_t polarity_state = common_hal_tca_get_polarity_port(chip); + #endif + uint16_t new_polarity_state = polarity_state; + new_polarity_state &= ~mask; // Clear the mask bits + new_polarity_state |= state; // Set the state bits + if (new_polarity_state != polarity_state) { + common_hal_tca_set_polarity_port(chip, new_polarity_state); + } + } else if (low_changed) { + #if TCA9555_LOCAL_MEMORY + uint8_t polarity_state = tca9555_polarity_state[LOW_BYTE(chip)]; + #else + uint8_t polarity_state = common_hal_tca_get_polarity_port_low(chip); + #endif + uint8_t new_polarity_state = (polarity_state & ~low_mask) | low_state; + if (new_polarity_state != polarity_state) { + common_hal_tca_set_polarity_port_low(chip, new_polarity_state); + } + } else if (high_changed) { + #if TCA9555_LOCAL_MEMORY + uint8_t polarity_state = tca9555_polarity_state[HIGH_BYTE(chip)]; + #else + uint8_t polarity_state = common_hal_tca_get_polarity_port_high(chip); + #endif + uint8_t new_polarity_state = (polarity_state & ~high_mask) | high_state; + if (new_polarity_state != polarity_state) { + common_hal_tca_set_polarity_port_high(chip, new_polarity_state); + } + } +} diff --git a/shared-module/tca9555/__init__.h b/shared-module/tca9555/__init__.h new file mode 100644 index 0000000000000..b16e499dd6f28 --- /dev/null +++ b/shared-module/tca9555/__init__.h @@ -0,0 +1,64 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2023 Christopher Parrott for Pimoroni Ltd + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef MICROPY_INCLUDED_SHARED_MODULE_TCA9555___INIT___H +#define MICROPY_INCLUDED_SHARED_MODULE_TCA9555___INIT___H + +#define INPUT_PORT0 0x00 +#define INPUT_PORT1 0x01 +#define OUTPUT_PORT0 0x02 +#define OUTPUT_PORT1 0x03 +#define POLARITY_PORT0 0x04 +#define POLARITY_PORT1 0x05 +#define CONFIGURATION_PORT0 0x06 +#define CONFIGURATION_PORT1 0x07 + +#define TCA9555_GPIO_COUNT 16 +#define TCA9555_VIRTUAL_GPIO_COUNT (TCA9555_GPIO_COUNT * TCA9555_CHIP_COUNT) + +#ifndef TCA9555_LOCAL_MEMORY +#define TCA9555_LOCAL_MEMORY (0) +#endif + +#ifndef TCA9555_READ_INTERNALS +#define TCA9555_READ_INTERNALS (0) +#endif + +#if TCA9555_LOCAL_MEMORY +extern uint8_t tca9555_output_state[TCA9555_CHIP_COUNT * 2]; +extern uint8_t tca9555_config_state[TCA9555_CHIP_COUNT * 2]; +extern uint8_t tca9555_polarity_state[TCA9555_CHIP_COUNT * 2]; +#endif + +#define HIGH_BYTE(index) (((index) * 2u) + 1u) +#define LOW_BYTE(index) (((index) * 2u)) +#define IS_PORT1(gpio) (((gpio) % TCA9555_GPIO_COUNT) >= 8u) +#define GPIO_BYTE(gpio) ((gpio) >> 3u) +#define GPIO_BIT_MASK(gpio) (1u << ((gpio) % 8u)) +#define CHIP_FROM_GPIO(gpio) ((gpio) / TCA9555_GPIO_COUNT) +#define ADDRESS_FROM_GPIO(gpio) (tca9555_addresses[CHIP_FROM_GPIO(gpio)]) + +#endif // MICROPY_INCLUDED_SHARED_MODULE_TCA9555___INIT___H pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy