diff --git a/ports/nrf/boards/pca10056/mpconfigboard.h b/ports/nrf/boards/pca10056/mpconfigboard.h index 4856a957001e4..71c7cc7e4c071 100644 --- a/ports/nrf/boards/pca10056/mpconfigboard.h +++ b/ports/nrf/boards/pca10056/mpconfigboard.h @@ -42,6 +42,8 @@ #define DEFAULT_UART_BUS_RX (&pin_P1_01) #define DEFAULT_UART_BUS_TX (&pin_P1_02) +#define DEFAULT_JACDAC_BUS (&pin_P1_02) + // Flash operation mode is determined by MICROPY_QSPI_DATAn pin configuration. // A pin config is valid if it is defined and its value is not 0xFF. // Quad mode: If all DATA0 --> DATA3 are valid diff --git a/ports/nrf/boards/pca10056/pins.c b/ports/nrf/boards/pca10056/pins.c index e00bc8a11e62d..9f05116d21deb 100644 --- a/ports/nrf/boards/pca10056/pins.c +++ b/ports/nrf/boards/pca10056/pins.c @@ -80,6 +80,7 @@ STATIC const mp_rom_map_elem_t board_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_P1_02), MP_ROM_PTR(&pin_P1_02) }, { MP_ROM_QSTR(MP_QSTR_D1), MP_ROM_PTR(&pin_P1_02) }, { MP_ROM_QSTR(MP_QSTR_TX), MP_ROM_PTR(&pin_P1_02) }, + { MP_ROM_QSTR(MP_QSTR_JACDAC), MP_ROM_PTR(&pin_P1_02) }, { MP_ROM_QSTR(MP_QSTR_P1_03), MP_ROM_PTR(&pin_P1_03) }, { MP_ROM_QSTR(MP_QSTR_D2), MP_ROM_PTR(&pin_P1_03) }, diff --git a/ports/nrf/common-hal/busio/JACDAC.c b/ports/nrf/common-hal/busio/JACDAC.c new file mode 100644 index 0000000000000..cbf82c153baaf --- /dev/null +++ b/ports/nrf/common-hal/busio/JACDAC.c @@ -0,0 +1,939 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 J Devine, M Lambrichts + * + * 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/microcontroller/__init__.h" +#include "shared-bindings/busio/JACDAC.h" + +#include "lib/utils/interrupt_char.h" +#include "py/mpconfig.h" +#include "py/gc.h" +#include "py/mperrno.h" +#include "py/runtime.h" +#include "supervisor/shared/translate.h" + +#include "nrfx_config.h" +#include "nrfx_gpiote.h" +#include "nrf_gpio.h" + + +#include +#include +#include + +#define JD_LOG_SIZE 512 +volatile uint32_t jd_log[JD_LOG_SIZE] = {0}; +uint32_t logidx = 0; + +static inline void log_char(char c) { + jd_log[logidx] = c; + logidx = (logidx + 1) % JD_LOG_SIZE; +} + + +#define JD_FRAME_SIZE(pkt) ((pkt)->size + 12) +#define JD_PERIPHERALS 2 + + +// states +#define ACTIVE 0x03 +#define RX_ACTIVE 0x01 +#define TX_ACTIVE 0x02 + +#define TX_CONFIGURED 0x04 +#define RX_CONFIGURED 0x08 + +#define TX_PENDING 0x10 + +#define JD_INST_ARRAY_SIZE 4 +busio_jacdac_obj_t* jd_instances[JD_INST_ARRAY_SIZE] = { NULL }; + +static void tim_set_timer(busio_jacdac_obj_t* self, int delta, cb_t cb); +static void initial_rx_timeout(busio_jacdac_obj_t* self); +void tx_start(busio_jacdac_obj_t *self); + +static inline void cfg_dbg_pins(void){ + nrf_gpio_cfg_output(4); // P0 + nrf_gpio_cfg_output(5); // P1 + nrf_gpio_cfg_output(3); // P2 +} + +static inline void set_P0(int val) { + nrf_gpio_pin_write(4, val); +} + +static inline void set_P1(int val) { + nrf_gpio_pin_write(5, val); +} + +static inline void set_P2(int val) { + nrf_gpio_pin_write(3, val); +} + +static uint32_t seed; + +static void seed_random(uint32_t s) { + seed = (seed * 0x1000193) ^ s; +} + +static uint32_t get_random(void) { + if (seed == 0) + seed_random(13 + *((uint32_t*)0x20032e20)); + + // xorshift algorithm + uint32_t x = seed; + x ^= x << 13; + x ^= x >> 17; + x ^= x << 5; + seed = x; + return x; +} + +// return v +/- 25% or so +static uint32_t random_around(uint32_t v) { + uint32_t mask = 0xfffffff; + while (mask > v) + mask >>= 1; + return (v - (mask >> 1)) + (get_random() & mask); +} + +/************************************************************************************* +* Configuration +*/ + + +// static busio_jacdac_obj_t* peripherals[JD_PERIPHERALS]; +// static void allocate_peripheral(busio_jacdac_obj_t* peripheral) { +// for (uint8_t i = 0; i < JD_PERIPHERALS; i++) { +// if (peripherals[i] == NULL) { +// peripherals[i] = peripheral; +// return; +// } +// } + +// mp_raise_RuntimeError(translate("All JACDAC peripherals in use")); +// } + +// static void free_peripheral(busio_jacdac_obj_t* peripheral) { +// for (uint8_t i = 0; i < JD_PERIPHERALS; i++) { +// if (peripherals[i] != NULL && peripherals[i]->pin == peripheral->pin) { +// peripherals[i] = NULL; +// return; +// } +// } +// } + +static nrfx_uarte_t nrfx_uartes[] = { +#if NRFX_CHECK(NRFX_UARTE0_ENABLED) + NRFX_UARTE_INSTANCE(0), +#endif +#if NRFX_CHECK(NRFX_UARTE1_ENABLED) + NRFX_UARTE_INSTANCE(1), +#endif +}; + + +/************************************************************************************* +* Helper methods +*/ + + +// static int8_t irq_disabled; + +// static void target_enable_irq(void) { +// irq_disabled--; +// if (irq_disabled <= 0) { +// irq_disabled = 0; +// __enable_irq(); +// } +// // } + +// static void target_disable_irq(void) { +// __disable_irq(); +// irq_disabled++; +// } + +static void target_panic(void) { + log_char('P'); + while (1) {} +} + +static uint16_t jd_crc16(const void *data, uint32_t size) { + const uint8_t *ptr = (const uint8_t *)data; + uint16_t crc = 0xffff; + while (size--) { + uint8_t d = *ptr++; + uint8_t x = (crc >> 8) ^ d; + x ^= x >> 4; + crc = (crc << 8) ^ (x << 12) ^ (x << 5) ^ x; + } + return crc; +} + +//static jd_diagnostics_t jd_diagnostics; +//jd_diagnostics_t *jd_get_diagnostics(void) { +// jd_diagnostics.bus_state = 0; +// return &jd_diagnostics; +//} + + +/************************************************************************************* +* GPIO helper +*/ + + +static void gpio_set(busio_jacdac_obj_t* self, uint32_t value) { + nrf_gpio_cfg_output(self->pin); + nrf_gpio_pin_write(self->pin, value); +} + +static uint32_t gpio_get(busio_jacdac_obj_t* self) { + nrf_gpio_cfg_input(self->pin, NRF_GPIO_PIN_PULLUP); + return nrf_gpio_pin_read(self->pin); +} + +static inline bool gpio_is_output(busio_jacdac_obj_t* self) { + return nrf_gpio_pin_dir_get(self->pin) == NRF_GPIO_PIN_DIR_OUTPUT; +} + +static inline void enable_gpio_interrupts(busio_jacdac_obj_t* self) { + nrf_gpio_cfg_input(self->pin, NRF_GPIO_PIN_PULLUP); + nrfx_gpiote_in_event_enable(self->pin, true); +} + +static inline void disable_gpio_interrupts(busio_jacdac_obj_t* self) { + nrfx_gpiote_in_event_disable(self->pin); +} + + +jd_frame_t* buffer_from_pool(busio_jacdac_obj_t* self) { + jd_frame_t* ret = NULL; + + __disable_irq(); + for (int i = 0; i < JD_POOL_SIZE; i++) + if (self->buffer_pool[i]) + { + ret = self->buffer_pool[i]; + self->buffer_pool[i] = NULL; + break; + } + __enable_irq(); + + return ret; +} + +int move_to_rx_queue(busio_jacdac_obj_t* self, jd_frame_t* f) { + int i; + + for (i = 0; i < JD_RX_SIZE; i++) + if (self->rx_queue[i] == NULL) + { + self->rx_queue[i] = f; + break; + } + + if (i == JD_RX_SIZE) + return -1; + + return 0; +} + +int move_to_tx_queue(busio_jacdac_obj_t* self, jd_frame_t* f) { + int i; + + for (i = 0; i < JD_TX_SIZE; i++) + if (self->tx_queue[i] == NULL) + { + self->tx_queue[i] = f; + break; + } + + if (i == JD_TX_SIZE) + return -1; + + return 1; +} + +void return_buffer_to_pool(busio_jacdac_obj_t* self, jd_frame_t* buf) { + __disable_irq(); + for (int i = 0; i < JD_POOL_SIZE; i++) + if (self->buffer_pool[i] == NULL) + { + self->buffer_pool[i] = buf; + break; + } + __enable_irq(); +} + +/************************************************************************************* +* Status helper +*/ + + +// set the given status +static inline void set_status(busio_jacdac_obj_t* self, uint16_t status) { + self->status |= status; +} + +// clear the given status +static inline void clr_status(busio_jacdac_obj_t* self, uint16_t status) { + self->status &= ~status; +} + +// check if the given status is set or not +static inline bool is_status(busio_jacdac_obj_t* self, uint16_t status) { + return self->status & status; +} + + +/************************************************************************************* +* UART configuration +*/ + + +static void uart_configure_tx(busio_jacdac_obj_t* self, int enable) { + if (enable && !is_status(self, TX_CONFIGURED)) { + NRF_P0->DIR |= (1 << self->pin); + NRF_P0->PIN_CNF[self->pin] = 3 << 2; // this overrides DIR setting above + self->uarte->p_reg->PSEL.TXD = self->pin; + self->uarte->p_reg->EVENTS_ENDTX = 0; + self->uarte->p_reg->INTENSET = (UARTE_INTENSET_ENDTX_Msk); + self->uarte->p_reg->ENABLE = 8; + while(!(self->uarte->p_reg->ENABLE)); + set_status(self, TX_CONFIGURED); + } else if (!enable && is_status(self, TX_CONFIGURED)) { + self->uarte->p_reg->TASKS_STOPTX = 1; + while(self->uarte->p_reg->TASKS_STOPTX); + self->uarte->p_reg->ENABLE = 0; + while((self->uarte->p_reg->ENABLE)); + self->uarte->p_reg->INTENCLR = (UARTE_INTENCLR_ENDTX_Msk); + self->uarte->p_reg->PSEL.TXD = 0xFFFFFFFF; + clr_status(self, TX_CONFIGURED); + } +} + +static void uart_configure_rx(busio_jacdac_obj_t* self, int enable) { + if (enable && !is_status(self, RX_CONFIGURED)) { + NRF_P0->DIR &= ~(1 << self->pin); + NRF_P0->PIN_CNF[self->pin] = 3 << 2; // this overrides DIR setting above + self->uarte->p_reg->PSEL.RXD = self->pin; + self->uarte->p_reg->EVENTS_ENDRX = 0; + self->uarte->p_reg->EVENTS_ERROR = 0; + self->uarte->p_reg->ERRORSRC = self->uarte->p_reg->ERRORSRC; + self->uarte->p_reg->INTENSET = (UARTE_INTENSET_ENDRX_Msk | UARTE_INTENSET_ERROR_Msk); + self->uarte->p_reg->ENABLE = 8; + while(!(self->uarte->p_reg->ENABLE)); + set_status(self, RX_CONFIGURED); + } else if (!enable && is_status(self, RX_CONFIGURED)) { + self->uarte->p_reg->TASKS_STOPRX = 1; + while(self->uarte->p_reg->TASKS_STOPRX); + self->uarte->p_reg->ENABLE = 0; + while((self->uarte->p_reg->ENABLE)); + self->uarte->p_reg->INTENCLR = (UARTE_INTENCLR_ENDRX_Msk | UARTE_INTENCLR_ERROR_Msk); + self->uarte->p_reg->PSEL.RXD = 0xFFFFFFFF; + clr_status(self, RX_CONFIGURED); + } +} + + +/************************************************************************************* +* Pin configuration +*/ + + +// set the JACDAC pin to act as the UART tx pin +static inline void set_pin_tx(busio_jacdac_obj_t* self) { + if (is_status(self, TX_CONFIGURED)) + return; + + uart_configure_rx(self, 0); + uart_configure_tx(self, 1); +} + +// set the JACDAC pin to act as the UART rx pin +static inline void set_pin_rx(busio_jacdac_obj_t* self) { + if (is_status(self, RX_CONFIGURED)) + return; + + uart_configure_tx(self, 0); + uart_configure_rx(self, 1); +} + +// set the JACDAC pin to act as a gpio pin +static inline void set_pin_gpio(busio_jacdac_obj_t* self) { + uart_configure_tx(self, 0); + uart_configure_rx(self, 0); +} + + +/************************************************************************************* +* Receiving +*/ +static void stop_uart_dma(busio_jacdac_obj_t* self) { + nrfx_uarte_tx_abort(self->uarte); + nrfx_uarte_rx_abort(self->uarte); +} + +static void rx_timeout(busio_jacdac_obj_t* self) { + log_char('?'); + set_P1(0); + + // if (!is_status(self, RX_ACTIVE)) + // target_panic(); + + // disable uart + stop_uart_dma(self); + + set_pin_gpio(self); + clr_status(self, RX_ACTIVE); + // restart normal operation + enable_gpio_interrupts(self); +} + +static void rx_start(busio_jacdac_obj_t* self) { + log_char('r'); + if (is_status(self, RX_ACTIVE)) + target_panic(); + + set_P1(1); + + disable_gpio_interrupts(self); + set_status(self, RX_ACTIVE); + + // TODO set timer for ticks. + gpio_get(self); + tim_set_timer(self, 1000, rx_timeout); + while(nrf_gpio_pin_read(self->pin) == 0); + + set_pin_rx(self); + + // if we don't receive these bytes (CRC) after 200 us assume an error + uint8_t* b = (uint8_t *)self->rx_buffer; + b[0] = 0; + b[1] = 0; + b[2] = 0; + b[3] = 0; + + nrfx_uarte_rx(self->uarte, (uint8_t*) self->rx_buffer, sizeof(jd_frame_t)); + tim_set_timer(self, 200, initial_rx_timeout); +} + +static void rx_done(busio_jacdac_obj_t *self) { + set_P1(0); + + log_char('R'); + // clear any upcoming timer interrupts + nrfx_timer_capture(self->timer, NRF_TIMER_CC_CHANNEL0); + + if (!is_status(self, RX_ACTIVE)) + target_panic(); + + set_pin_gpio(self); + clr_status(self, RX_ACTIVE); + + gpio_get(self); + while(nrf_gpio_pin_read(self->pin) == 0); + + + // check size + uint32_t txSize = sizeof(*self->rx_buffer); + uint32_t declaredSize = JD_FRAME_SIZE(self->rx_buffer); + if (txSize < declaredSize || declaredSize == 0) { + //jd_diagnostics.bus_uart_error++; + log_char('V'); + enable_gpio_interrupts(self); + return; + } + + // check crc + uint16_t crc = jd_crc16((uint8_t *)self->rx_buffer + 2, declaredSize - 2); + if (crc != self->rx_buffer->crc) { + //.bus_uart_error++; + log_char('M'); + // target_panic(); + enable_gpio_interrupts(self); + return; + } + + jd_frame_t* rx = self->rx_buffer; + self->rx_buffer = buffer_from_pool(self); + + int ret = move_to_rx_queue(self, rx); + + // drop but ensure memory is not left floating around... + if (ret == -1) + return_buffer_to_pool(self, rx); + + stop_uart_dma(self); + // restart normal operation + enable_gpio_interrupts(self); + + if (is_status(self, TX_PENDING)) + tim_set_timer(self, random_around(150), tx_start); +} + + +/************************************************************************************* +* JACDAC - transmitting +*/ +void tx_start(busio_jacdac_obj_t *self) { + if ((self->status & TX_ACTIVE) || (self->status & RX_ACTIVE)) + target_panic(); + + log_char('T'); + + if (self->tx_buffer == NULL) + { + __disable_irq(); + for (int i = 0; i < JD_RX_SIZE; i++) + if (self->tx_queue[i]) + { + self->tx_buffer = self->tx_queue[i]; + self->tx_queue[i] = NULL; + break; + } + __enable_irq(); + + if (self->tx_buffer == NULL) + { + clr_status(self, TX_PENDING); + return; + } + } + + // try to pull the line low, provided it currently reads as high + if (gpio_get(self) == 0) { + set_P0(0); + set_status(self, TX_PENDING); + return; + } + + disable_gpio_interrupts(self); + + set_P0(1); + log_char(';'); + gpio_set(self, 0); + // start pulse (11-15µs) + common_hal_mcu_delay_us(10); + clr_status(self, TX_PENDING); + set_status(self, TX_ACTIVE); + + // start-data gap (40-89µs) + gpio_set(self, 1); + uint16_t* data = (uint16_t*)self->tx_buffer; + *data = jd_crc16((uint8_t *)self->tx_buffer + 2, JD_FRAME_SIZE(self->tx_buffer) - 2); + common_hal_mcu_delay_us(19); + + // setup UART tx + set_pin_tx(self); + nrfx_uarte_tx(self->uarte, (uint8_t*) self->tx_buffer, JD_FRAME_SIZE(self->tx_buffer)); +} + +static void tx_done(busio_jacdac_obj_t *self) { + set_P0(0); + log_char('t'); + + if (!is_status(self, TX_ACTIVE)) + target_panic(); + + set_pin_gpio(self); + + // end pulse (11-15µs) + gpio_set(self, 0); + common_hal_mcu_delay_us(10); + return_buffer_to_pool(self, self->tx_buffer); + self->tx_buffer = NULL; + gpio_set(self, 1); + + uart_configure_tx(self, 0); + + // restart idle operation + clr_status(self, TX_ACTIVE); + + __disable_irq(); + bool more = false; + for (int i = 0; i < JD_RX_SIZE; i++) + if (self->tx_queue[i]) + { + more = true; + break; + } + __enable_irq(); + + if (more) + { + set_status(self, TX_PENDING); + tim_set_timer(self, random_around(150), tx_start); + } + + enable_gpio_interrupts(self); +} + +static void tim_set_timer(busio_jacdac_obj_t* self, int delta, cb_t cb) { + self->tim_cb = cb; + uint32_t now = nrfx_timer_capture(self->timer, NRF_TIMER_CC_CHANNEL1); + nrfx_timer_compare(self->timer, NRF_TIMER_CC_CHANNEL0, now + delta, true); +} + +/************************************************************************************* +* Interrupt handlers +*/ + + +// interrupt handler for UART +static void uart_irq(const nrfx_uarte_event_t* event, void* context) { + busio_jacdac_obj_t* self = (busio_jacdac_obj_t*) context; + + switch ( event->type ) { + case NRFX_UARTE_EVT_RX_DONE: + rx_done(self); + break; + + case NRFX_UARTE_EVT_TX_DONE: + tx_done(self); + break; + + case NRFX_UARTE_EVT_ERROR: + log_char('E'); + log_char((char)event->data.error.error_mask); + // Possible Error source is Overrun, Parity, Framing, Break + if (is_status(self, RX_ACTIVE)) + { + log_char('&'); + if ((event->data.error.error_mask & NRF_UARTE_ERROR_BREAK_MASK)) + rx_done(self); + } + else if (is_status(self, TX_ACTIVE)) + { + log_char('*'); + } + + break; + } +} + +static void initial_rx_timeout(busio_jacdac_obj_t* self) +{ + log_char('N'); + if (is_status(self, RX_ACTIVE)) { + uint8_t* b = (uint8_t *)self->rx_buffer; + + if (b[0] == 0 && b[1] == 0) + { + log_char('Y'); + rx_timeout(self); + return; + } + + log_char('I'); + tim_set_timer(self, JD_FRAME_SIZE(self->rx_buffer) * 12 + 60, rx_timeout); + } +} + +// interrupt handler for timers +static void timer_irq(nrf_timer_event_t event_type, void* context) { + log_char('i'); + set_P2(1); + busio_jacdac_obj_t* self = (busio_jacdac_obj_t*) context; + + if (event_type == NRF_TIMER_EVENT_COMPARE0) { + if (self->tim_cb) + self->tim_cb(self); + } + set_P2(0); +} + +// interrupt handler for GPIO +static void gpiote_callback(nrfx_gpiote_pin_t pin, nrf_gpiote_polarity_t action) { + + busio_jacdac_obj_t* self = NULL; + + for (int i = 0; i < JD_INST_ARRAY_SIZE; i++) + if (jd_instances[i]->pin == pin) + { + self = jd_instances[i]; + break; + } + + if (self && !is_status(self, TX_ACTIVE)) + rx_start(self); +} + + +/************************************************************************************* +* Initialization +*/ + +static void initialize_gpio(busio_jacdac_obj_t *self) { + + nrfx_gpiote_in_config_t cfg = { + .sense = NRF_GPIOTE_POLARITY_HITOLO, + .pull = NRF_GPIO_PIN_PULLUP, // idle_state ? NRF_GPIO_PIN_PULLDOWN : NRF_GPIO_PIN_PULLUP, + .is_watcher = false, // nrf_gpio_cfg_watcher vs nrf_gpio_cfg_input + .hi_accuracy = true, + .skip_gpio_setup = false + }; + + nrfx_gpiote_init(0); + nrfx_gpiote_in_init(self->pin, &cfg, gpiote_callback); + nrfx_gpiote_in_event_enable(self->pin, true); + + enable_gpio_interrupts(self); +} + +static void initialize_timer(busio_jacdac_obj_t *self) { + self->timer = nrf_peripherals_allocate_timer(); + + if (self->timer == NULL) { + target_panic(); + mp_raise_RuntimeError(translate("All timers in use")); + } + + nrfx_timer_config_t timer_config = { + .frequency = NRF_TIMER_FREQ_1MHz, + .mode = NRF_TIMER_MODE_TIMER, + .bit_width = NRF_TIMER_BIT_WIDTH_32, + .interrupt_priority = 2, + .p_context = self + }; + + nrfx_timer_init(self->timer, &timer_config, &timer_irq); + nrfx_timer_enable(self->timer); +} + +static void initialize_uart(busio_jacdac_obj_t *self) { + self->uarte = NULL; + + for (size_t i = 0; i < MP_ARRAY_SIZE(nrfx_uartes); i++) { + if ((nrfx_uartes[i].p_reg->ENABLE & UARTE_ENABLE_ENABLE_Msk) == 0) { + self->uarte = &nrfx_uartes[i]; + break; + } + } + + if (self->uarte == NULL) + mp_raise_ValueError(translate("All UART peripherals are in use")); + + nrfx_uarte_config_t uart_config = { + .pseltxd = NRF_UARTE_PSEL_DISCONNECTED, + .pselrxd = NRF_UARTE_PSEL_DISCONNECTED, + .pselcts = NRF_UARTE_PSEL_DISCONNECTED, + .pselrts = NRF_UARTE_PSEL_DISCONNECTED, + .p_context = self, + .baudrate = NRF_UARTE_BAUDRATE_1000000, + .interrupt_priority = 1, + .hal_cfg = { + .hwfc = NRF_UARTE_HWFC_DISABLED, + .parity = NRF_UARTE_PARITY_EXCLUDED, + } + }; + + nrfx_uarte_init(self->uarte, &uart_config, uart_irq); +} + + +/************************************************************************************* +* Public JACDAC methods +*/ + +void common_hal_busio_jacdac_construct(busio_jacdac_obj_t* self, const mcu_pin_obj_t* pin) { + log_char('A'); + + bool found = false; + for (int i = 0; i < JD_INST_ARRAY_SIZE; i++) + if (jd_instances[i] == self) + { + found = true; + break; + } + + if (!found) + { + int i; + for (i = 0; i < JD_INST_ARRAY_SIZE; i++) + if (jd_instances[i] == NULL) + { + jd_instances[i] = self; + break; + } + + if (i == JD_INST_ARRAY_SIZE) + target_panic(); + } + + self->pin = pin->number; + self->status = 0; + self->tim_cb = NULL; + + for (int i = 0; i < JD_POOL_SIZE; i++) + self->buffer_pool[i] = m_malloc(sizeof(jd_frame_t), true); + + for (int i = 0; i < JD_RX_SIZE; i++) + self->rx_queue[i] = NULL; + + for (int i = 0; i < JD_TX_SIZE; i++) + self->tx_queue[i] = NULL; + + self->rx_buffer = buffer_from_pool(self); + self->tx_buffer = NULL; + + cfg_dbg_pins(); + + claim_pin(pin); + initialize_timer(self); + initialize_gpio(self); + initialize_uart(self); + + log_char('B'); +} + + +void common_hal_busio_jacdac_deinit(busio_jacdac_obj_t* self) { + if (common_hal_busio_jacdac_deinited(self)) + return; + + nrfx_gpiote_in_event_disable(self->pin); + nrfx_gpiote_in_uninit(self->pin); + + nrf_peripherals_free_timer(self->timer); + + // uart + if (self->uarte) + { + nrfx_uarte_tx_abort(self->uarte); + nrfx_uarte_rx_abort(self->uarte); + nrfx_uarte_uninit(self->uarte); + } + + // pin + reset_pin_number(self->pin); + + for (int i = 0; i < JD_POOL_SIZE; i++) + { + if (self->buffer_pool[i]) + { + m_free(self->buffer_pool[i]); + self->buffer_pool[i] = NULL; + } + } + + for (int i = 0; i < JD_RX_SIZE; i++) + { + if (self->rx_queue[i]) + { + m_free(self->rx_queue[i]); + self->rx_queue[i] = NULL; + } + } + + for (int i = 0; i < JD_TX_SIZE; i++) + { + if (self->tx_queue[i]) + { + m_free(self->tx_queue[i]); + self->tx_queue[i] = NULL; + } + } + + if (self->rx_buffer) + { + m_free(self->rx_buffer); + self->rx_buffer = NULL; + } + + if (self->tx_buffer) + { + m_free(self->tx_buffer); + self->tx_buffer = NULL; + } + + self->pin = NO_PIN; +} + +bool common_hal_busio_jacdac_deinited(busio_jacdac_obj_t *self) { + return self->pin == NO_PIN; +} + +int common_hal_busio_jacdac_send(busio_jacdac_obj_t *self, const uint8_t *data, size_t len) { + jd_frame_t* f = buffer_from_pool(self); + + if (f == NULL) + return -1; + + memcpy(f, data, MIN(len, JD_MAX_FRAME_SIZE)); + + int ret = move_to_tx_queue(self, f); + + if (ret == -1) + { + return_buffer_to_pool(self, f); + return -1; + } + + if (!is_status(self, TX_PENDING | TX_ACTIVE | RX_ACTIVE)) + { + set_status(self, TX_PENDING); + tim_set_timer(self, 100, tx_start); + } + + return 1; +} + +int common_hal_busio_jacdac_receive(busio_jacdac_obj_t *self, uint8_t *data, size_t len) { + + jd_frame_t *f = NULL; + + __disable_irq(); + for (int i = 0; i < JD_RX_SIZE; i++) + if (self->rx_queue[i]) + { + f = self->rx_queue[i]; + self->rx_queue[i] = NULL; + break; + } + __enable_irq(); + + if (f) + { + memcpy(data, f, sizeof(jd_frame_t)); + return_buffer_to_pool(self, f); + return 1; + } + + return 0; +} + +void jacdac_reset(void) { + log_char('}'); + + for (int i = 0; i < JD_INST_ARRAY_SIZE; i++) + { + if (jd_instances[i]) + { + common_hal_busio_jacdac_deinit(jd_instances[i]); + jd_instances[i] = NULL; + } + } +} \ No newline at end of file diff --git a/ports/nrf/common-hal/busio/JACDAC.h b/ports/nrf/common-hal/busio/JACDAC.h new file mode 100644 index 0000000000000..e6ef7931338f4 --- /dev/null +++ b/ports/nrf/common-hal/busio/JACDAC.h @@ -0,0 +1,95 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 J Devine, M Lambrichts + * + * 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_NRF_COMMON_HAL_BUSIO_JACDAC_H +#define MICROPY_INCLUDED_NRF_COMMON_HAL_BUSIO_JACDAC_H + +#include "common-hal/microcontroller/Pin.h" +#include "py/obj.h" + +#include "nrfx_uarte.h" +#include "nrf/timers.h" + +#define JD_POOL_SIZE 20 +#define JD_RX_SIZE 10 +#define JD_TX_SIZE 10 + + +// 255 minus size of the serial header, rounded down to 4 +#define JD_SERIAL_PAYLOAD_SIZE 236 +#define JD_MAX_FRAME_SIZE 252 + + +// structure for a jacdac frame +struct _jd_frame_t { + uint16_t crc; + uint8_t size; + uint8_t flags; + + uint64_t device_identifier; + + uint8_t data[JD_SERIAL_PAYLOAD_SIZE + 4]; +} __attribute__((__packed__, aligned(4))); +typedef struct _jd_frame_t jd_frame_t; + + +typedef struct busio_jacdac_obj { + mp_obj_base_t base; + uint8_t pin; + uint16_t status; + + nrfx_uarte_t* uarte; + nrfx_timer_t* timer; + + void (*tim_cb)(struct busio_jacdac_obj*); + + jd_frame_t* buffer_pool[JD_POOL_SIZE]; + jd_frame_t* rx_queue[JD_RX_SIZE]; + jd_frame_t* tx_queue[JD_TX_SIZE]; + + jd_frame_t* rx_buffer; + jd_frame_t* tx_buffer; +} busio_jacdac_obj_t; + +typedef void (*cb_t)(busio_jacdac_obj_t*); + +/* +typedef struct { + uint32_t bus_state; + uint32_t bus_lo_error; + uint32_t bus_uart_error; + uint32_t bus_timeout_error; + uint32_t packets_sent; + uint32_t packets_received; + uint32_t packets_dropped; +} jd_diagnostics_t; +jd_diagnostics_t *jd_get_diagnostics(void); +*/ + +void jacdac_reset(void); + + +#endif // MICROPY_INCLUDED_NRF_COMMON_HAL_BUSIO_JACDAC_H diff --git a/ports/nrf/common-hal/busio/__init__.c b/ports/nrf/common-hal/busio/__init__.c index 41761b6743aea..e9f75c8ec230f 100644 --- a/ports/nrf/common-hal/busio/__init__.c +++ b/ports/nrf/common-hal/busio/__init__.c @@ -1 +1 @@ -// No busio module functions. +// No busio module functions. \ No newline at end of file diff --git a/ports/nrf/supervisor/port.c b/ports/nrf/supervisor/port.c index bc8b47be77137..e70cf28560d9b 100644 --- a/ports/nrf/supervisor/port.c +++ b/ports/nrf/supervisor/port.c @@ -45,6 +45,7 @@ #include "common-hal/busio/I2C.h" #include "common-hal/busio/SPI.h" #include "common-hal/busio/UART.h" +#include "common-hal/busio/JACDAC.h" #include "common-hal/pulseio/PulseOut.h" #include "common-hal/pulseio/PulseIn.h" #include "common-hal/pwmio/PWMOut.h" @@ -179,6 +180,7 @@ void reset_port(void) { #if CIRCUITPY_BUSIO i2c_reset(); spi_reset(); + jacdac_reset(); uart_reset(); #endif diff --git a/py/circuitpy_defns.mk b/py/circuitpy_defns.mk index 76c0a4db6752b..76d5381c7ab3a 100644 --- a/py/circuitpy_defns.mk +++ b/py/circuitpy_defns.mk @@ -315,6 +315,7 @@ SRC_COMMON_HAL_ALL = \ busio/I2C.c \ busio/SPI.c \ busio/UART.c \ + busio/JACDAC.c \ busio/__init__.c \ camera/__init__.c \ camera/Camera.c \ @@ -402,7 +403,7 @@ $(filter $(SRC_PATTERNS), \ ) SRC_BINDINGS_ENUMS += \ - util.c + util.c \ SRC_SHARED_MODULE_ALL = \ _bleio/Address.c \ diff --git a/py/circuitpy_mpconfig.h b/py/circuitpy_mpconfig.h index 0583ae1c97a43..f4dd4880dcae8 100644 --- a/py/circuitpy_mpconfig.h +++ b/py/circuitpy_mpconfig.h @@ -308,6 +308,7 @@ extern const struct _mp_obj_module_t board_module; #define BOARD_I2C (defined(DEFAULT_I2C_BUS_SDA) && defined(DEFAULT_I2C_BUS_SCL)) #define BOARD_SPI (defined(DEFAULT_SPI_BUS_SCK) && defined(DEFAULT_SPI_BUS_MISO) && defined(DEFAULT_SPI_BUS_MOSI)) #define BOARD_UART (defined(DEFAULT_UART_BUS_RX) && defined(DEFAULT_UART_BUS_TX)) +#define BOARD_JACDAC (defined(DEFAULT_JACDAC_BUS)) // I2C and SPI are always allocated off the heap. @@ -317,6 +318,12 @@ extern const struct _mp_obj_module_t board_module; #define BOARD_UART_ROOT_POINTER #endif +#if BOARD_JACDAC +#define BOARD_JACDAC_ROOT_POINTER mp_obj_t shared_jacdac_bus; +#else +#define BOARD_JACDAC_ROOT_POINTER +#endif + #else #define BOARD_MODULE #define BOARD_UART_ROOT_POINTER @@ -854,6 +861,7 @@ extern const struct _mp_obj_module_t wifi_module; mp_obj_t pew_singleton; \ mp_obj_t terminal_tilegrid_tiles; \ BOARD_UART_ROOT_POINTER \ + BOARD_JACDAC_ROOT_POINTER \ FLASH_ROOT_POINTERS \ MEMORYMONITOR_ROOT_POINTERS \ NETWORK_ROOT_POINTERS \ diff --git a/shared-bindings/board/__init__.c b/shared-bindings/board/__init__.c index 6837fc41c2dc9..045cedbd4e9ec 100644 --- a/shared-bindings/board/__init__.c +++ b/shared-bindings/board/__init__.c @@ -114,6 +114,31 @@ mp_obj_t board_uart(void) { #endif MP_DEFINE_CONST_FUN_OBJ_0(board_uart_obj, board_uart); + +//| def JACDAC() -> busio.JACDAC: +#if BOARD_JACDAC +mp_obj_t board_jacdac(void) { + mp_obj_t singleton = common_hal_board_get_jacdac(); + if (singleton != NULL) { + return singleton; + } + + assert_pin_free(DEFAULT_JACDAC_BUS); + + return common_hal_board_create_jacdac(); +} +#else +mp_obj_t board_jacdac(void) { + mp_raise_NotImplementedError_varg(translate("No default %q bus"), MP_QSTR_JACDAC); + return NULL; +} + +#endif +MP_DEFINE_CONST_FUN_OBJ_0(board_jacdac_obj, board_jacdac); + + + + const mp_obj_module_t board_module = { .base = { &mp_type_module }, .globals = (mp_obj_dict_t*)&board_module_globals, diff --git a/shared-bindings/board/__init__.h b/shared-bindings/board/__init__.h index a9b652ba8dfaf..9704e2d94bb11 100644 --- a/shared-bindings/board/__init__.h +++ b/shared-bindings/board/__init__.h @@ -45,4 +45,8 @@ mp_obj_t common_hal_board_get_uart(void); mp_obj_t common_hal_board_create_uart(void); MP_DECLARE_CONST_FUN_OBJ_0(board_uart_obj); +mp_obj_t common_hal_board_get_jacdac(void); +mp_obj_t common_hal_board_create_jacdac(void); +MP_DECLARE_CONST_FUN_OBJ_0(board_jacdac_obj); + #endif // MICROPY_INCLUDED_SHARED_BINDINGS_BOARD___INIT___H diff --git a/shared-bindings/busio/JACDAC.c b/shared-bindings/busio/JACDAC.c new file mode 100644 index 0000000000000..865dc697ae44d --- /dev/null +++ b/shared-bindings/busio/JACDAC.c @@ -0,0 +1,255 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 J Devine, M Lambrichts + * + * 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 + +#include "shared-bindings/busio/JACDAC.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-bindings/util.h" + +#include "lib/utils/buffer_helper.h" +#include "lib/utils/context_manager_helpers.h" +#include "lib/utils/interrupt_char.h" + +#include "py/ioctl.h" +#include "py/objproperty.h" +#include "py/runtime.h" +#include "supervisor/shared/translate.h" + + +//| class JACDAC: +//| """A bidirectional single wire serial protocol""" +//| def __init__(self, pin: microcontroller.Pin) -> None: +//| """A common bidirectional serial protocol that uses an an agreed upon speed +//| rather than a shared clock line. +//| +//| :param ~microcontroller.Pin pin: the pin to transmit with. +//| ... +//| +STATIC mp_obj_t busio_jacdac_make_new(const mp_obj_type_t *type, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + // Always initially allocate the UART (so also JACDAC) object within the long-lived heap. + // This is needed to avoid crashes with certain UART implementations which + // cannot accomodate being moved after creation. (See + // https://github.com/adafruit/circuitpython/issues/1056) + busio_jacdac_obj_t *self = m_new_ll_obj(busio_jacdac_obj_t); + self->base.type = &busio_jacdac_type; + + enum { ARG_pin }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_pin, MP_ARG_OBJ, {.u_obj = mp_const_none} }, + }; + + 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); + + const mcu_pin_obj_t* pin = validate_obj_is_free_pin_or_none(args[ARG_pin].u_obj); + + if ( (pin == NULL)) { + mp_raise_ValueError(translate("pin can not be None")); + } + + common_hal_busio_jacdac_construct(self, pin); + return (mp_obj_t)self; +} + +//| def deinit(self) -> None: +//| """Deinitialises JACDAC and releases any hardware resources for reuse.""" +//| ... +//| +STATIC mp_obj_t busio_jacdac_obj_deinit(mp_obj_t self_in) { + busio_jacdac_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_busio_jacdac_deinit(self); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(busio_jacdac_deinit_obj, busio_jacdac_obj_deinit); + +STATIC void check_for_deinit(busio_jacdac_obj_t *self) { + if (common_hal_busio_jacdac_deinited(self)) { + raise_deinited_error(); + } +} + +//| def __enter__(self) -> JACDAC: +//| """No-op used by Context Managers.""" +//| ... +//| +// Provided by context manager helper. + +//| def __exit__(self) -> None: +//| """Automatically deinitializes the hardware when exiting a context. See +//| :ref:`lifetime-and-contextmanagers` for more info.""" +//| ... +//| +STATIC mp_obj_t busio_jacdac_obj___exit__(size_t n_args, const mp_obj_t *args) { + (void)n_args; + common_hal_busio_jacdac_deinit(args[0]); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(busio_jacdac___exit___obj, 4, 4, busio_jacdac_obj___exit__); + + + + + +STATIC mp_obj_t busio_jacdac_send(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_buffer, ARG_start, ARG_end }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_buffer, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_start, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_end, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = INT_MAX} }, + }; + busio_jacdac_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + check_for_deinit(self); + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + // setup buffer + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(args[ARG_buffer].u_obj, &bufinfo, MP_BUFFER_READ); + int32_t start = args[ARG_start].u_int; + size_t length = bufinfo.len; + normalize_buffer_bounds(&start, args[ARG_end].u_int, &length); + + // empty buffer + if (length == 0) { + return mp_obj_new_bool(0); + } + + return mp_obj_new_bool(common_hal_busio_jacdac_send(self, ((uint8_t*)bufinfo.buf) + start, length)); +} +MP_DEFINE_CONST_FUN_OBJ_KW(busio_jacdac_send_obj, 2, busio_jacdac_send); + + + + +STATIC mp_obj_t busio_jacdac_receive(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_buffer, ARG_start, ARG_end }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_buffer, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_start, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_end, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = INT_MAX} }, + }; + busio_jacdac_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + check_for_deinit(self); + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + // setup buffer + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(args[ARG_buffer].u_obj, &bufinfo, MP_BUFFER_WRITE); + int32_t start = args[ARG_start].u_int; + size_t length = bufinfo.len; + normalize_buffer_bounds(&start, args[ARG_end].u_int, &length); + + // empty buffer + if (length == 0) { + return mp_obj_new_bool(0); + } + + return mp_obj_new_bool(common_hal_busio_jacdac_receive(self, ((uint8_t*)bufinfo.buf) + start, length)); +} +MP_DEFINE_CONST_FUN_OBJ_KW(busio_jacdac_receive_obj, 2, busio_jacdac_receive); + +// https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function +static uint32_t hash_fnv1(const void *data, unsigned len) { + const uint8_t *d = (const uint8_t *)data; + uint32_t h = 0x811c9dc5; + while (len--) + h = (h * 0x1000193) ^ *d++; + return h; +} + +static uint32_t jd_hash(uint8_t* buf, size_t length, int bits) { + if (bits < 1) + return 0; + + uint32_t h = hash_fnv1(buf, length); + + if (bits >= 32) + return h; + else + return ((h ^ (h >> bits)) & ((1 << bits) - 1)); +} + + +STATIC mp_obj_t busio_jacdac_hash(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_buffer }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_buffer, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + }; + + busio_jacdac_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + check_for_deinit(self); + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + // setup buffer + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(args[ARG_buffer].u_obj, &bufinfo, MP_BUFFER_READ); + int32_t start = 0; + size_t length = bufinfo.len; + normalize_buffer_bounds(&start, INT_MAX, &length); + + // empty buffer + if (length == 0) { + return 0; + } + + uint32_t h = jd_hash(((uint8_t*)bufinfo.buf), length, 30); + + vstr_t vstr; + vstr_init_len(&vstr, 4); + + vstr.buf[0] = 0x41 + h % 26; + vstr.buf[1] = 0x41 + (h / 26) % 26; + vstr.buf[2] = 0x30 + (h / (26 * 26)) % 10; + vstr.buf[3] = 0x30 + (h / (26 * 26 * 10)) % 10; + + return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr); +} +MP_DEFINE_CONST_FUN_OBJ_KW(busio_jacdac_hash_obj, 2, busio_jacdac_hash); + + +STATIC const mp_rom_map_elem_t busio_jacdac_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&busio_jacdac_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&default___enter___obj) }, + { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&busio_jacdac___exit___obj) }, + + { MP_ROM_QSTR(MP_QSTR_send), MP_ROM_PTR(&busio_jacdac_send_obj) }, + { MP_ROM_QSTR(MP_QSTR_receive), MP_ROM_PTR(&busio_jacdac_receive_obj) }, + { MP_ROM_QSTR(MP_QSTR_hash), MP_ROM_PTR(&busio_jacdac_hash_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(busio_jacdac_locals_dict, busio_jacdac_locals_dict_table); + +const mp_obj_type_t busio_jacdac_type = { + { &mp_type_type }, + .name = MP_QSTR_JACDAC, + .make_new = busio_jacdac_make_new, + .locals_dict = (mp_obj_dict_t*)&busio_jacdac_locals_dict, +}; diff --git a/shared-bindings/busio/JACDAC.h b/shared-bindings/busio/JACDAC.h new file mode 100644 index 0000000000000..5efafbf299a4b --- /dev/null +++ b/shared-bindings/busio/JACDAC.h @@ -0,0 +1,46 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 J Devine, M Lambrichts + * + * 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_BUSIO_JACDAC_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_BUSIO_JACDAC_H + +#include "common-hal/microcontroller/Pin.h" +#include "common-hal/busio/JACDAC.h" +#include "py/ringbuf.h" + +extern const mp_obj_type_t busio_jacdac_type; + + +// Construct an underlying UART object. +extern void common_hal_busio_jacdac_construct(busio_jacdac_obj_t *context, const mcu_pin_obj_t * pin); + +extern void common_hal_busio_jacdac_deinit(busio_jacdac_obj_t *context); +extern bool common_hal_busio_jacdac_deinited(busio_jacdac_obj_t *context); + +extern int common_hal_busio_jacdac_send(busio_jacdac_obj_t *context, const uint8_t *data, size_t len); +extern int common_hal_busio_jacdac_receive(busio_jacdac_obj_t *context, uint8_t *data, size_t len); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_BUSIO_JACDAC_H diff --git a/shared-bindings/busio/__init__.c b/shared-bindings/busio/__init__.c index 04632c2f4a540..5ffc713f1e63c 100644 --- a/shared-bindings/busio/__init__.c +++ b/shared-bindings/busio/__init__.c @@ -35,6 +35,7 @@ #include "shared-bindings/busio/OneWire.h" #include "shared-bindings/busio/SPI.h" #include "shared-bindings/busio/UART.h" +#include "shared-bindings/busio/JACDAC.h" #include "py/runtime.h" @@ -75,6 +76,7 @@ STATIC const mp_rom_map_elem_t busio_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_SPI), MP_ROM_PTR(&busio_spi_type) }, { MP_ROM_QSTR(MP_QSTR_OneWire), MP_ROM_PTR(&busio_onewire_type) }, { MP_ROM_QSTR(MP_QSTR_UART), MP_ROM_PTR(&busio_uart_type) }, + { MP_ROM_QSTR(MP_QSTR_JACDAC), MP_ROM_PTR(&busio_jacdac_type) }, }; STATIC MP_DEFINE_CONST_DICT(busio_module_globals, busio_module_globals_table); diff --git a/shared-module/board/__init__.c b/shared-module/board/__init__.c index 39b68a0f11548..3cdda772f2822 100644 --- a/shared-module/board/__init__.c +++ b/shared-module/board/__init__.c @@ -33,6 +33,7 @@ #include "shared-bindings/busio/I2C.h" #include "shared-bindings/busio/SPI.h" #include "shared-bindings/busio/UART.h" +#include "shared-bindings/busio/JACDAC.h" #endif #if CIRCUITPY_DISPLAYIO @@ -134,6 +135,24 @@ mp_obj_t common_hal_board_create_uart(void) { } #endif +/* +#if BOARD_JACDAC +mp_obj_t common_hal_board_get_jacdac(void) { + return MP_STATE_VM(shared_jacdac_bus); +} + +mp_obj_t common_hal_board_create_jacdac(void) { + busio_jacdac_obj_t *self = m_new_ll_obj(busio_jacdac_obj_t); + self->base.type = &busio_jacdac_type; + + const mcu_pin_obj_t* bus = MP_OBJ_TO_PTR(DEFAULT_JACDAC_BUS); + + common_hal_busio_jacdac_construct(self, bus); + MP_STATE_VM(shared_jacdac_bus) = MP_OBJ_FROM_PTR(self); + return MP_STATE_VM(shared_jacdac_bus); +} +#endif +*/ void reset_board_busses(void) { #if BOARD_I2C bool display_using_i2c = false; @@ -177,4 +196,9 @@ void reset_board_busses(void) { #if BOARD_UART MP_STATE_VM(shared_uart_bus) = NULL; #endif +/* +#if BOARD_JACDAC + MP_STATE_VM(shared_jacdac_bus) = NULL; +#endif +*/ }