From c8cdf948e0cad1719f648a74b5005eaae06b9cea Mon Sep 17 00:00:00 2001 From: moon2 <moon2protonmail@protonmail.com> Date: Fri, 9 Jun 2023 21:39:41 +0200 Subject: [PATCH] badge link: some backend --- components/badge23/include/badge23/spio.h | 63 +++++- components/badge23/spio.c | 240 ++++++++++++++++++---- usermodule/micropython.cmake | 1 + usermodule/mp_badge_link.c | 64 ++++++ 4 files changed, 326 insertions(+), 42 deletions(-) create mode 100644 usermodule/mp_badge_link.c diff --git a/components/badge23/include/badge23/spio.h b/components/badge23/include/badge23/spio.h index ee8f5f3cab..085dc306e8 100644 --- a/components/badge23/include/badge23/spio.h +++ b/components/badge23/include/badge23/spio.h @@ -6,13 +6,68 @@ #define BUTTON_PRESSED_RIGHT 1 #define BUTTON_NOT_PRESSED 0 -int8_t get_button_state(bool leftbutton); -void update_button_state(); +#define BADGE_LINK_LINE_IN_TIP 0b0001 +#define BADGE_LINK_LINE_IN_RING 0b0010 +#define BADGE_LINK_LINE_OUT_TIP 0b0100 +#define BADGE_LINK_LINE_OUT_RING 0b1000 +#define BADGE_LINK_LINE_IN ((BADGE_LINK_LINE_IN_TIP) | (BADGE_LINK_LINE_IN_RING)) +#define BADGE_LINK_LINE_OUT ((BADGE_LINK_LINE_OUT_TIP) | (BADGE_LINK_LINE_OUT_RING)) +#define BADGE_LINK_ALL ((BADGE_LINK_LINE_IN) | (BADGE_LINK_LINE_OUT)) + +#define BADGE_LINK_LINE_IN_TIP_PIN 4 +#define BADGE_LINK_LINE_IN_RING_PIN 5 +#define BADGE_LINK_LINE_OUT_TIP_PIN 6 +#define BADGE_LINK_LINE_OUT_RING_PIN 7 + +/* Initializes GPIO modes, prefills structs, etc. Call before using library. + */ void init_buttons(); +/* Gets data from I2C portexpanders and GPIOs. Requires I2C lock. + */ +void update_button_state(); + +/* UI sugar: People might prefer using one button for in-application stuff and the other + * for entering main menu/volume control, depending on their handedness and how they hold + * the badge. This function allows them configure this and is meant to be only be used by + * the OS user config handler. + * + * Set to 1 to use the left shoulder button as the menu button, 0 for the right + */ +void spio_menu_button_set_left(bool left); + +/* Gets user menu button user preference as set with spio_menu_button_set_left. + */ +int8_t spio_menu_button_get_left(); + +/* Read the state of the menu/application button at the last call of update_button_state. + * Compare with BUTTON_(NOT)PRESSED* constants for -h. + */ int8_t spio_menu_button_get(); int8_t spio_application_button_get(); + +/* Read the state of the left/right button at the last call of update_button_state. + * Compare with BUTTON_(NOT)PRESSED* constants for -h. + * This ignores user preference and should be used only with good reason. + */ int8_t spio_left_button_get(); int8_t spio_right_button_get(); -int8_t spio_menu_button_get_left(); -void spio_menu_button_set_left(bool left); + +/* Gets active badge links ports. Mask with BADGE_LINK_LINE_{IN/OUT}_{TIP/RING}. The corresponding + * GPIO indices are listed in BADGE_LINK_LINE_{OUT/IN}_{TIP/RING}_PIN. + */ +uint8_t spio_badge_link_get_active(uint8_t pin_mask); + +/* Disables badge link ports. Mask with BADGE_LINK_LINE_{IN/OUT}_{TIP/RING}. The corresponding + * GPIO indices are listed in BADGE_LINK_LINE_{OUT/IN}_{TIP/RING}_PIN. + * Returns the output of spio_badge_link_get_active after execution. + */ +uint8_t spio_badge_link_disable(uint8_t pin_mask); + +/* Enables badge link ports. Mask with BADGE_LINK_LINE_{IN/OUT}_{TIP/RING}. The corresponding + * GPIO indices are listed in BADGE_LINK_LINE_{OUT/IN}_{TIP/RING}_PIN. + * Returns the output of spio_badge_link_get_active after execution. + * + * Do NOT connect headphones to a badge link port. You might hear a ringing for a while. Warn user. + */ +uint8_t spio_badge_link_enable(uint8_t pin_mask); diff --git a/components/badge23/spio.c b/components/badge23/spio.c index e93bc90456..22946bee31 100644 --- a/components/badge23/spio.c +++ b/components/badge23/spio.c @@ -4,10 +4,123 @@ #include "stdint.h" #include "badge23/spio.h" #include "badge23/lock.h" +#include "badge23/audio.h" + +#include "driver/i2c.h" +#define I2C_MASTER_NUM 0 +#define TIMEOUT_MS 1000 + + +#if defined(CONFIG_BADGE23_HW_GEN_P4) || defined(CONFIG_BADGE23_HW_GEN_P3) + +#define BADGE_LINK_LINE_OUT_TIP_ENABLE_PIN 6 +#define BADGE_LINK_LINE_OUT_RING_ENABLE_PIN 7 +#define BADGE_LINK_LINE_IN_TIP_ENABLE_PIN 5 +#define BADGE_LINK_LINE_IN_RING_ENABLE_PIN 4 + +#define LEFT_BUTTON_RIGHT (0+8) +#define RIGHT_BUTTON_LEFT (6+8) +#define RIGHT_BUTTON_MID (5+8) +#define RIGHT_BUTTON_RIGHT (7+8) + +#elif defined(CONFIG_BADGE23_HW_GEN_P6) + +#define BADGE_LINK_LINE_OUT_TIP_ENABLE_PIN 5 +#define BADGE_LINK_LINE_OUT_RING_ENABLE_PIN 6 +#define BADGE_LINK_LINE_IN_TIP_ENABLE_PIN 3 + +#define BADGE_LINK_LINE_IN_RING_ENABLE_PIN 4 +#define LEFT_BUTTON_RIGHT (0+8) +#define RIGHT_BUTTON_LEFT (4+8) +#define RIGHT_BUTTON_MID (7+8) +#define RIGHT_BUTTON_RIGHT (5+8) +#endif static int8_t leftbutton = 0; static int8_t rightbutton = 0; +static bool menu_button_left = 0; + +static uint8_t badge_link_enabled = 0; + +/* The MAX7321 doesn't have any input/output pin configuration methods. Instead, + * it has a ~40k pullup resistor to VCC and a programmable shunt transistor to VEE. + * Writing a byte to the IC closes each shunt with a LO at its index. + * Reading a byte returns the state of each pin. + * + * This means that changing a single output bit cannot be changed without some + * information about the other pins. Also a output pin set to HI can read as LO + * if there's an outside shunt. + */ +typedef struct{ + uint8_t address; + uint8_t is_output_pin; // mask for pins we wish to use as outputs + uint8_t read_state; // + uint8_t output_state; // goal output state +} max7321_t; + +max7321_t port_expanders[2]; + +void _max7321_update(max7321_t *max){ + uint8_t rx = 0; + uint8_t tx = (~(max->is_output_pin)) | max->output_state; + + xSemaphoreTake(mutex_i2c, portMAX_DELAY); + esp_err_t tx_error = i2c_master_write_to_device(I2C_MASTER_NUM, max->address, &tx, sizeof(tx), TIMEOUT_MS / portTICK_PERIOD_MS); + esp_err_t rx_error = i2c_master_read_from_device(I2C_MASTER_NUM, max->address, &rx, sizeof(rx), TIMEOUT_MS / portTICK_PERIOD_MS); + xSemaphoreGive(mutex_i2c); + max->read_state = rx; +} + +void max7321s_update(){ + _max7321_update(&port_expanders[0]); + _max7321_update(&port_expanders[1]); +} + +bool max7321s_get_pin(uint8_t pin){ + if(pin>15) return 0; + max7321_t * pe = &port_expanders[0]; + if(pin>7){ + pe = &port_expanders[1]; + pin -= 8; + } + return ((pe->read_state) >> pin) & 1; +} + +bool max7321s_set_pin(uint8_t pin, bool on){ + if(pin>15) return 0; + max7321_t * pe = &port_expanders[0]; + if(pin>7){ + pe = &port_expanders[1]; + pin -= 8; + } + + if(((pe->is_output_pin) >> pin) & 1){ + if(on){ + pe->output_state |= 1<<pin; + } else { + pe->output_state &= ~(1<<pin); + } + return 1; + } else { + return 0; + } +} + +void max7321s_set_pinmode_output(uint8_t pin, bool output){ + if(pin>15) return 0; + max7321_t * pe = &port_expanders[0]; + if(pin>7){ + pe = &port_expanders[1]; + pin -= 8; + } + if(output){ + pe->is_output_pin |= (1<<pin); + } else { + pe->is_output_pin &= ~(1<<pin); + } +} + #if defined(CONFIG_BADGE23_HW_GEN_P1) #define RIGHT_BUTTON_LEFT 37 @@ -61,21 +174,16 @@ void update_button_state(){ } } -#elif defined(CONFIG_BADGE23_HW_GEN_P3) || defined(CONFIG_BADGE23_HW_GEN_P4) - -#include "driver/i2c.h" -#define I2C_MASTER_NUM 0 -#define TIMEOUT_MS 1000 +#elif defined(CONFIG_BADGE23_HW_GEN_P3) || defined(CONFIG_BADGE23_HW_GEN_P4) || defined(CONFIG_BADGE23_HW_GEN_P6) //on ESP32 #define LEFT_BUTTON_LEFT 3 #define LEFT_BUTTON_MID 0 //on PORTEXPANDER -#define LEFT_BUTTON_RIGHT 0 -#define RIGHT_BUTTON_LEFT 6 -#define RIGHT_BUTTON_MID 5 -#define RIGHT_BUTTON_RIGHT 7 + +max7321_t port_expanders[] = { {0b01101110, 0, 255, 255}, + {0b01101101, 0, 255, 255} }; static void _init_buttons(){ //configure all buttons as pullup @@ -90,42 +198,45 @@ static void _init_buttons(){ cfg.pin_bit_mask = 1; cfg.pull_up_en = GPIO_PULLUP_DISABLE; ESP_ERROR_CHECK(gpio_config(&cfg)); - printf("nya\n"); + + max7321s_set_pinmode_output(RIGHT_BUTTON_RIGHT, 0); + max7321s_set_pinmode_output(RIGHT_BUTTON_MID, 0); + max7321s_set_pinmode_output(RIGHT_BUTTON_LEFT, 0); + max7321s_set_pinmode_output(LEFT_BUTTON_RIGHT, 0); } -void update_button_state(){ - uint8_t port; - - xSemaphoreTake(mutex_i2c, portMAX_DELAY); - esp_err_t ret = i2c_master_read_from_device(I2C_MASTER_NUM, 0b1101101, &port, sizeof(port), TIMEOUT_MS / portTICK_PERIOD_MS); - xSemaphoreGive(mutex_i2c); +int8_t process_button_state(bool r, bool m, bool l){ + if(!l){ + return BUTTON_PRESSED_LEFT; + } else if(!m){ + return BUTTON_PRESSED_DOWN; + } else if(!r){ + return BUTTON_PRESSED_RIGHT; + } else { + return BUTTON_NOT_PRESSED; + } +} - uint8_t rr = port & (1ULL << RIGHT_BUTTON_RIGHT); - uint8_t rm = port & (1ULL << RIGHT_BUTTON_MID); - uint8_t rl = port & (1ULL << RIGHT_BUTTON_LEFT); - uint8_t lr = port & (1ULL << LEFT_BUTTON_RIGHT); +void update_button_state(){ + max7321s_update(); + uint8_t rr = max7321s_get_pin(RIGHT_BUTTON_RIGHT); + uint8_t rm = max7321s_get_pin(RIGHT_BUTTON_MID); + uint8_t rl = max7321s_get_pin(RIGHT_BUTTON_LEFT); + uint8_t lr = max7321s_get_pin(LEFT_BUTTON_RIGHT); uint8_t ll = gpio_get_level(LEFT_BUTTON_LEFT); uint8_t lm = gpio_get_level(LEFT_BUTTON_MID); - if(!rl){ - rightbutton = BUTTON_PRESSED_LEFT; - } else if(!rm){ - rightbutton = BUTTON_PRESSED_DOWN; - } else if(!rr){ - rightbutton = BUTTON_PRESSED_RIGHT; - } else { - rightbutton = BUTTON_NOT_PRESSED; + int8_t new_rightbutton = process_button_state(rr, rm, rl); + int8_t new_leftbutton = process_button_state(lr, lm, ll); + if(new_rightbutton != rightbutton){ + //TODO: CALLBACK button_state_has_changed_to(new_rightbutton) + //note: consider menubutton/application button config option } - - if(!ll){ - leftbutton = BUTTON_PRESSED_LEFT; - } else if(!lm){ - leftbutton = BUTTON_PRESSED_DOWN; - } else if(!lr){ - leftbutton = BUTTON_PRESSED_RIGHT; - } else { - leftbutton = BUTTON_NOT_PRESSED; + if(new_leftbutton != leftbutton){ + //TODO: CALLBACK button_state_has_changed_to(new_leftbutton) } + rightbutton = new_rightbutton; + leftbutton = new_leftbutton; } #else @@ -145,7 +256,6 @@ int8_t get_button_state(bool left){ return rightbutton; } -static bool menu_button_left = 0; void spio_menu_button_set_left(bool left){ menu_button_left = 1; @@ -170,3 +280,57 @@ int8_t spio_right_button_get(){ int8_t spio_menu_button_get_left(){ return menu_button_left; } + +uint8_t spio_badge_link_get_active(uint8_t pin_mask){ + return badge_link_enabled & pin_mask; +} + +#if defined(CONFIG_BADGE23_HW_GEN_P1) + +static uint8_t spio_badge_link_set(uint8_t pin_mask, uint8_t state){ + return 0; // no badge link here (yet) +} + +#elif defined(CONFIG_BADGE23_HW_GEN_P3) || defined(CONFIG_BADGE23_HW_GEN_P4) || defined(CONFIG_BADGE23_HW_GEN_P6) + +#define USER_WARNINGS_ENABLED + +static int8_t spio_badge_link_set(uint8_t pin_mask, bool state){ + if(state) { + if((pin_mask & BADGE_LINK_LINE_OUT_RING) || (pin_mask & BADGE_LINK_LINE_OUT_TIP)){ + if(!audio_headphones_are_connected()) { + pin_mask &= ~BADGE_LINK_LINE_OUT_RING; + pin_mask &= ~BADGE_LINK_LINE_OUT_TIP; +#ifdef USER_WARNINGS_ENABLED + printf("cannot enable line out badge link without cable plugged in for safety reasons\n"); + } else { + printf("badge link enabled on line out. please make sure not to have headphones or sound sources connected before transmitting data.\n"); +#endif + } + } + } + + if(pin_mask & BADGE_LINK_LINE_IN_RING) max7321s_set_pinmode_output(BADGE_LINK_LINE_IN_RING_ENABLE_PIN, 1); + if(pin_mask & BADGE_LINK_LINE_IN_TIP) max7321s_set_pinmode_output(BADGE_LINK_LINE_IN_TIP_ENABLE_PIN, 1); + if(pin_mask & BADGE_LINK_LINE_OUT_RING) max7321s_set_pinmode_output(BADGE_LINK_LINE_OUT_RING_ENABLE_PIN, 1); + if(pin_mask & BADGE_LINK_LINE_OUT_TIP) max7321s_set_pinmode_output(BADGE_LINK_LINE_OUT_TIP_ENABLE_PIN, 1); + + if(pin_mask & BADGE_LINK_LINE_IN_RING) max7321s_set_pin(BADGE_LINK_LINE_IN_RING_ENABLE_PIN, !state); + if(pin_mask & BADGE_LINK_LINE_IN_TIP) max7321s_set_pin(BADGE_LINK_LINE_IN_TIP_ENABLE_PIN, !state); + if(pin_mask & BADGE_LINK_LINE_OUT_RING) max7321s_set_pin(BADGE_LINK_LINE_OUT_RING_ENABLE_PIN, !state); + if(pin_mask & BADGE_LINK_LINE_OUT_TIP) max7321s_set_pin(BADGE_LINK_LINE_OUT_TIP_ENABLE_PIN, !state); + + max7321s_update(); + + badge_link_enabled = (badge_link_enabled & (~pin_mask)) | (pin_mask & (state ? 255: 0)); + return spio_badge_link_get_active(pin_mask); +} +#endif + +uint8_t spio_badge_link_disable(uint8_t pin_mask){ + return spio_badge_link_set(pin_mask, 0); +} + +uint8_t spio_badge_link_enable(uint8_t pin_mask){ + return spio_badge_link_set(pin_mask, 1); +} diff --git a/usermodule/micropython.cmake b/usermodule/micropython.cmake index 94c79a75f7..2912379039 100644 --- a/usermodule/micropython.cmake +++ b/usermodule/micropython.cmake @@ -7,6 +7,7 @@ add_library(usermod_badge23 INTERFACE) target_sources(usermod_badge23 INTERFACE ${CMAKE_CURRENT_LIST_DIR}/mp_hardware.c ${CMAKE_CURRENT_LIST_DIR}/mp_audio.c + ${CMAKE_CURRENT_LIST_DIR}/mp_badge_link.c ${CMAKE_CURRENT_LIST_DIR}/mp_synth.c ${CMAKE_CURRENT_LIST_DIR}/mp_kernel.c ) diff --git a/usermodule/mp_badge_link.c b/usermodule/mp_badge_link.c new file mode 100644 index 0000000000..cfb8fb4104 --- /dev/null +++ b/usermodule/mp_badge_link.c @@ -0,0 +1,64 @@ +// probably doesn't need all of these idk +#include <stdio.h> +#include <string.h> + +#include "py/runtime.h" +#include "py/mphal.h" +#include "mphalport.h" +#include "modmachine.h" +#include "extmod/virtpin.h" +#include "machine_rtc.h" +#include "py/builtin.h" +#include "py/runtime.h" + +#include "badge23/audio.h" +#include "badge23/leds.h" +#include "badge23/captouch.h" +#include "badge23/display.h" +#include "badge23/spio.h" +#include "badge23/espan.h" +#include "badge23_hwconfig.h" + +mp_obj_t mp_ctx_from_ctx(Ctx *ctx); + +STATIC mp_obj_t mp_get_active(mp_obj_t pin_mask) { + return mp_obj_new_int(spio_badge_link_get_active(mp_obj_get_int(pin_mask))); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_get_active_obj, mp_get_active); + +STATIC mp_obj_t mp_enable(mp_obj_t pin_mask) { + return mp_obj_new_int(spio_badge_link_enable(mp_obj_get_int(pin_mask))); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_enable_obj, mp_enable); + +STATIC mp_obj_t mp_disable(mp_obj_t pin_mask) { + return mp_obj_new_int(spio_badge_link_disable(mp_obj_get_int(pin_mask))); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_disable_obj, mp_disable); + +STATIC const mp_rom_map_elem_t mp_module_badge_link_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_badge_link) }, + { MP_ROM_QSTR(MP_QSTR_get_active), MP_ROM_PTR(&mp_get_active_obj) }, + { MP_ROM_QSTR(MP_QSTR_enable), MP_ROM_PTR(&mp_enable_obj) }, + { MP_ROM_QSTR(MP_QSTR_disable), MP_ROM_PTR(&mp_disable_obj) }, + + { MP_ROM_QSTR(MP_QSTR_BADGE_LINK_LINE_IN_TIP), MP_ROM_INT(BADGE_LINK_LINE_IN_TIP) }, + { MP_ROM_QSTR(MP_QSTR_BADGE_LINK_LINE_IN_RING), MP_ROM_INT(BADGE_LINK_LINE_IN_RING) }, + { MP_ROM_QSTR(MP_QSTR_BADGE_LINK_LINE_OUT_TIP), MP_ROM_INT(BADGE_LINK_LINE_OUT_TIP) }, + { MP_ROM_QSTR(MP_QSTR_BADGE_LINK_LINE_OUT_RING), MP_ROM_INT(BADGE_LINK_LINE_OUT_RING) }, + { MP_ROM_QSTR(MP_QSTR_BADGE_LINK_ALL), MP_ROM_INT(BADGE_LINK_ALL) }, + { MP_ROM_QSTR(MP_QSTR_BADGE_LINK_LINE_IN_TIP_PIN), MP_ROM_INT(BADGE_LINK_LINE_IN_TIP_PIN) }, + { MP_ROM_QSTR(MP_QSTR_BADGE_LINK_LINE_IN_RING_PIN), MP_ROM_INT(BADGE_LINK_LINE_IN_RING_PIN) }, + { MP_ROM_QSTR(MP_QSTR_BADGE_LINK_LINE_OUT_TIP_PIN), MP_ROM_INT(BADGE_LINK_LINE_OUT_TIP_PIN) }, + { MP_ROM_QSTR(MP_QSTR_BADGE_LINK_LINE_OUT_RING_PIN), MP_ROM_INT(BADGE_LINK_LINE_OUT_RING_PIN) }, +}; + +STATIC MP_DEFINE_CONST_DICT(mp_module_badge_link_globals, mp_module_badge_link_globals_table); + +const mp_obj_module_t mp_module_badge_link = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&mp_module_badge_link_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_badge_link, mp_module_badge_link); + -- GitLab