From 3740a3287ad5965df34639e90bfa890b36c0fd60 Mon Sep 17 00:00:00 2001 From: hathach Date: Tue, 29 Aug 2023 18:22:21 +0700 Subject: [PATCH] - correct tuh_max3421e_int_api() for nrf52 - hcd_int_disable/enable is software only to reduce interrupt lag --- hw/bsp/nrf/boards/pca10056/board.h | 9 +++ hw/bsp/nrf/family.c | 20 +++++- src/portable/analog/max3421/hcd_max3421.c | 86 ++++++++++++++++------- 3 files changed, 87 insertions(+), 28 deletions(-) diff --git a/hw/bsp/nrf/boards/pca10056/board.h b/hw/bsp/nrf/boards/pca10056/board.h index f4368f830..16d31addb 100644 --- a/hw/bsp/nrf/boards/pca10056/board.h +++ b/hw/bsp/nrf/boards/pca10056/board.h @@ -31,6 +31,8 @@ extern "C" { #endif +#define _PINNUM(port, pin) ((port)*32 + (pin)) + // LED #define LED_PIN 13 #define LED_STATE_ON 0 @@ -43,6 +45,13 @@ #define UART_RX_PIN 8 #define UART_TX_PIN 6 +// SPI for USB host shield +#define MAX3421E_SCK_PIN _PINNUM(1, 15) +#define MAX3421E_MOSI_PIN _PINNUM(1, 13) +#define MAX3421E_MISO_PIN _PINNUM(1, 14) +#define MAX3421E_CS_PIN _PINNUM(1, 12) +#define MAX3241E_INTR_PIN _PINNUM(1, 11) + #ifdef __cplusplus } #endif diff --git a/hw/bsp/nrf/family.c b/hw/bsp/nrf/family.c index 368763b84..083efc235 100644 --- a/hw/bsp/nrf/family.c +++ b/hw/bsp/nrf/family.c @@ -107,7 +107,12 @@ void max3421e_int_handler(nrfx_gpiote_pin_t pin, nrf_gpiote_polarity_t action) { //--------------------------------------------------------------------+ void tuh_max3421e_int_api(uint8_t rhport, bool enabled) { (void) rhport; - nrfx_gpiote_trigger_enable(MAX3241E_INTR_PIN, enabled); + + if (enabled) { + nrfx_gpiote_trigger_enable(MAX3241E_INTR_PIN, true); + }else { + nrfx_gpiote_trigger_disable(MAX3241E_INTR_PIN); + } } void tuh_max3421_spi_cs_api(uint8_t rhport, bool active) { @@ -217,6 +222,19 @@ void board_init(void) { #endif #if CFG_TUH_ENABLED && defined(CFG_TUH_MAX3421) && CFG_TUH_MAX3421 + // MAX3421 need 3.3v signal + if ((NRF_UICR->REGOUT0 & UICR_REGOUT0_VOUT_Msk) != UICR_REGOUT0_VOUT_3V3) { + NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Wen << NVMC_CONFIG_WEN_Pos; + while (NRF_NVMC->READY == NVMC_READY_READY_Busy){} + + NRF_UICR->REGOUT0 = (NRF_UICR->REGOUT0 & ~UICR_REGOUT0_VOUT_Msk) | UICR_REGOUT0_VOUT_3V3; + + NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Ren << NVMC_CONFIG_WEN_Pos; + while (NRF_NVMC->READY == NVMC_READY_READY_Busy){} + + NVIC_SystemReset(); + } + // manually manage CS nrf_gpio_cfg_output(MAX3421E_CS_PIN); tuh_max3421_spi_cs_api(0, false); diff --git a/src/portable/analog/max3421/hcd_max3421.c b/src/portable/analog/max3421/hcd_max3421.c index 262dbca0f..cb7911992 100644 --- a/src/portable/analog/max3421/hcd_max3421.c +++ b/src/portable/analog/max3421/hcd_max3421.c @@ -184,8 +184,6 @@ typedef struct { } max3421_ep_t; typedef struct { - atomic_flag busy; // busy transferring - // cached register uint8_t sndbc; uint8_t hirq; @@ -194,7 +192,10 @@ typedef struct { uint8_t peraddr; uint8_t hxfr; + atomic_flag busy; // busy transferring + volatile uint8_t intr_disable_count; volatile uint16_t frame_count; + max3421_ep_t ep[CFG_TUH_MAX3421_ENDPOINT_TOTAL]; // [0] is reserved for addr0 OSAL_MUTEX_DEF(spi_mutexdef); @@ -220,8 +221,8 @@ void tuh_max3421e_int_api(uint8_t rhport, bool enabled); static void max3421_spi_lock(uint8_t rhport, bool in_isr) { // disable interrupt and mutex lock (for pre-emptive RTOS) if not in_isr if (!in_isr) { - tuh_max3421e_int_api(rhport, false); (void) osal_mutex_lock(_hcd_data.spi_mutex, OSAL_TIMEOUT_WAIT_FOREVER); + tuh_max3421e_int_api(rhport, false); } // assert CS @@ -294,6 +295,12 @@ static uint8_t reg_read(uint8_t rhport, uint8_t reg, bool in_isr) { return ret ? rx_buf[1] : 0; } +static inline void hirq_write(uint8_t rhport, uint8_t data, bool in_isr) { + reg_write(rhport, HIRQ_ADDR, data, in_isr); + // HIRQ write 1 is clear + _hcd_data.hirq &= ~data; +} + static inline void hien_write(uint8_t rhport, uint8_t data, bool in_isr) { _hcd_data.hien = data; reg_write(rhport, HIEN_ADDR, data, in_isr); @@ -440,13 +447,14 @@ tusb_speed_t handle_connect_irq(uint8_t rhport) { bool hcd_init(uint8_t rhport) { (void) rhport; - hcd_int_disable(rhport); + tuh_max3421e_int_api(rhport, false); tuh_max3421_spi_cs_api(rhport, false); TU_LOG2_INT(sizeof(max3421_ep_t)); TU_LOG2_INT(sizeof(max3421_data_t)); tu_memclr(&_hcd_data, sizeof(_hcd_data)); + _hcd_data.peraddr = 0xff; // invalid #if OSAL_MUTEX_REQUIRED _hcd_data.spi_mutex = osal_mutex_create(&_hcd_data.spi_mutexdef); @@ -469,11 +477,13 @@ bool hcd_init(uint8_t rhport) { reg_write(rhport, HCTL_ADDR, HCTL_BUSRST | HCTL_FRMRST, false); // clear all previously pending IRQ - reg_write(rhport, HIRQ_ADDR, 0xff, false); + hirq_write(rhport, 0xff, false); // Enable IRQ hien_write(rhport, DEFAULT_HIEN, false); + tuh_max3421e_int_api(rhport, true); + // Enable Interrupt pin reg_write(rhport, CPUCTL_ADDR, CPUCTL_IE, false); @@ -481,13 +491,21 @@ bool hcd_init(uint8_t rhport) { } // Enable USB interrupt +// Not actually enable GPIO interrupt, just set variable to prevent handler to process void hcd_int_enable (uint8_t rhport) { - tuh_max3421e_int_api(rhport, true); + (void) rhport; + // tuh_max3421e_int_api(rhport, true); + if (_hcd_data.intr_disable_count) { + _hcd_data.intr_disable_count--; + } } // Disable USB interrupt +// Not actually disable GPIO interrupt, just set variable to prevent handler to process void hcd_int_disable(uint8_t rhport) { - tuh_max3421e_int_api(rhport, false); + (void) rhport; + //tuh_max3421e_int_api(rhport, false); + _hcd_data.intr_disable_count++; } // Get frame number (1ms) @@ -521,7 +539,7 @@ void hcd_port_reset_end(uint8_t rhport) { reg_write(rhport, HCTL_ADDR, 0, false); // Bus reset will also trigger CONDET IRQ, clear and re-enable it after reset - reg_write(rhport, HIRQ_ADDR, HIRQ_CONDET_IRQ, false); + hirq_write(rhport, HIRQ_CONDET_IRQ, false); hien_write(rhport, DEFAULT_HIEN, false); } @@ -572,19 +590,23 @@ bool hcd_edpt_open(uint8_t rhport, uint8_t daddr, tusb_desc_endpoint_t const * e void xact_out(uint8_t rhport, max3421_ep_t *ep, bool switch_ep, bool in_isr) { // Page 12: Programming BULK-OUT Transfers // TODO double buffered - if (switch_ep) { - peraddr_write(rhport, ep->daddr, in_isr); - - uint8_t const hctl = (ep->data_toggle ? HCTL_SNDTOG1 : HCTL_SNDTOG0); - reg_write(rhport, HCTL_ADDR, hctl, in_isr); - } uint8_t hxfr = ep->ep_num | HXFR_OUT_NIN | (ep->is_iso ? HXFR_ISO : 0); - if (ep->ep_num == 0 && (ep->buf == NULL || ep->total_len == 0)) { // Control ZLP status use HS hxfr |= HXFR_HS; - } else { + } + + if (switch_ep) { + peraddr_write(rhport, ep->daddr, in_isr); + + if ( 0 == (hxfr & HXFR_HS) ) { + uint8_t const hctl = (ep->data_toggle ? HCTL_SNDTOG1 : HCTL_SNDTOG0); + reg_write(rhport, HCTL_ADDR, hctl, in_isr); + } + } + + if ( 0 == (hxfr & HXFR_HS) ) { uint8_t const xact_len = (uint8_t) tu_min16(ep->total_len - ep->xferred_len, ep->packet_size); TU_ASSERT(_hcd_data.hirq & HIRQ_SNDBAV_IRQ,); if (xact_len) { @@ -598,12 +620,6 @@ void xact_out(uint8_t rhport, max3421_ep_t *ep, bool switch_ep, bool in_isr) { void xact_in(uint8_t rhport, max3421_ep_t *ep, bool switch_ep, bool in_isr) { // Page 13: Programming BULK-IN Transfers - if (switch_ep) { - peraddr_write(rhport, ep->daddr, in_isr); - - uint8_t const hctl = (ep->data_toggle ? HCTL_RCVTOG1 : HCTL_RCVTOG0); - reg_write(rhport, HCTL_ADDR, hctl, in_isr); - } uint8_t hxfr = ep->ep_num | (ep->is_iso ? HXFR_ISO : 0); if (ep->ep_num == 0 && (ep->buf == NULL || ep->total_len == 0)) { @@ -611,6 +627,15 @@ void xact_in(uint8_t rhport, max3421_ep_t *ep, bool switch_ep, bool in_isr) { hxfr |= HXFR_HS; } + if (switch_ep) { + peraddr_write(rhport, ep->daddr, in_isr); + + if ( 0 == (hxfr & HXFR_HS) ) { + uint8_t const hctl = (ep->data_toggle ? HCTL_RCVTOG1 : HCTL_RCVTOG0); + reg_write(rhport, HCTL_ADDR, hctl, in_isr); + } + } + hxfr_write(rhport, hxfr, in_isr); } @@ -721,7 +746,7 @@ static void handle_xfer_done(uint8_t rhport) { xfer_result_t xfer_result; - //TU_LOG3("HRSL: %02X\r\n", hrsl); +// TU_LOG3("HRSL: %02X\r\n", hrsl); switch(hresult) { case HRSL_SUCCESS: xfer_result = XFER_RESULT_SUCCESS; @@ -821,13 +846,20 @@ void print_hirq(uint8_t hirq) { void hcd_int_handler(uint8_t rhport) { uint8_t hirq = reg_read(rhport, HIRQ_ADDR, true) & _hcd_data.hien; if (!hirq) return; - // print_hirq(hirq); if (hirq & HIRQ_FRAME_IRQ) { _hcd_data.frame_count++; } + // interrupt is disabled, only ack FRAME IRQ and skip the rest + if (_hcd_data.intr_disable_count) { + if (hirq & HIRQ_FRAME_IRQ) { + hirq_write(rhport, HIRQ_FRAME_IRQ, true); + } + return; + } + if (hirq & HIRQ_CONDET_IRQ) { tusb_speed_t speed = handle_connect_irq(rhport); @@ -861,7 +893,7 @@ void hcd_int_handler(uint8_t rhport) { } // ack RCVDVAV IRQ - reg_write(rhport, HIRQ_ADDR, HIRQ_RCVDAV_IRQ, true); + hirq_write(rhport, HIRQ_RCVDAV_IRQ, true); hirq = reg_read(rhport, HIRQ_ADDR, true); } @@ -871,7 +903,7 @@ void hcd_int_handler(uint8_t rhport) { } if ( hirq & HIRQ_HXFRDN_IRQ ) { - reg_write(rhport, HIRQ_ADDR, HIRQ_HXFRDN_IRQ, true); + hirq_write(rhport, HIRQ_HXFRDN_IRQ, true); handle_xfer_done(rhport); } @@ -881,7 +913,7 @@ void hcd_int_handler(uint8_t rhport) { // clear all interrupt except SNDBAV_IRQ (never clear by us). Note RCVDAV_IRQ, HXFRDN_IRQ already clear while processing hirq &= ~HIRQ_SNDBAV_IRQ; if ( hirq ) { - reg_write(rhport, HIRQ_ADDR, hirq, true); + hirq_write(rhport, hirq, true); } }