Skip to content

Commit f388b64

Browse files
committed
feature(esp_tinyusb): Added the vbus software monitoring for esp23p4
- added GPIO ISR, debounce timer, debounce logic - added GOTGCTL.BVALID driving on VBUS appear/disappear - added DCTL.SFTDISCON driving on VBUS appear/dissapear
1 parent fee7783 commit f388b64

File tree

3 files changed

+227
-8
lines changed

3 files changed

+227
-8
lines changed

device/esp_tinyusb/include_private/tinyusb_vbus_monitor.h

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,21 @@ typedef struct {
2929
* @param config VBUS monitoring configuration
3030
*
3131
* @return
32-
* - ESP_ERR_INVALID_ARG: if config is NULL
33-
* - ESP_ERR_NOT_SUPPORTED: VBUS monitoring is not supported yet
32+
* - ESP_ERR_INVALID_ARG if config is NULL
33+
* - ESP_ERR_INVALID_STATE if VBUS monitoring is already initialized
34+
* - ESP_ERR_NO_MEM if debounce timer creation failed
35+
* - ESP_OK on success
3436
*/
3537
esp_err_t tinyusb_vbus_monitor_init(tinyusb_vbus_monitor_config_t *config);
3638

3739
/**
3840
* @brief Deinitialize VBUS monitoring
41+
*
42+
* @return
43+
* - ESP_ERR_INVALID_STATE if VBUS monitoring is not initialized
44+
* - ESP_OK on success
3945
*/
40-
void tinyusb_vbus_monitor_deinit(void);
46+
esp_err_t tinyusb_vbus_monitor_deinit(void);
4147

4248
#ifdef __cplusplus
4349
}

device/esp_tinyusb/test_apps/vbus_monitor/main/test_vbus_monitor.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -510,7 +510,7 @@ TEST_CASE("Emulated VBUS USB OTG 2.0, verify attach/detach events callback (via
510510

511511
test_device_wait_event(TINYUSB_EVENT_ATTACHED);
512512

513-
uint8_t dev_mounted = 0; /* TODO: Expect to fail on run. Enable in VBUS monitor part2 */
513+
uint8_t dev_mounted = test_vbus_emulated_via_gotgctl_bvalid(&USB_DWC_HS);
514514

515515
// Cleanup
516516
TEST_ASSERT_EQUAL_MESSAGE(ESP_OK, tinyusb_driver_uninstall(), "Failed to uninstall TinyUSB driver");
@@ -558,7 +558,7 @@ TEST_CASE("Controlled VBUS USB OTG 2.0, verify attach/detach events callback", "
558558
TEST_ASSERT_EQUAL_MESSAGE(ESP_OK, tinyusb_driver_install(&tusb_cfg), "Failed to install TinyUSB driver");
559559
test_device_wait_event(TINYUSB_EVENT_ATTACHED);
560560

561-
uint8_t dev_mounted = 0; /* TODO: Expect to fail on run. Enable in VBUS monitor part2 */
561+
uint8_t dev_mounted = test_vbus_controlled_by_gpio();
562562

563563
// Cleanup
564564
TEST_ASSERT_EQUAL_MESSAGE(ESP_OK, tinyusb_driver_uninstall(), "Failed to uninstall TinyUSB driver");
@@ -606,7 +606,7 @@ TEST_CASE("Real VBUS USB OTG 2.0, verify attach/detach events callback (requires
606606
TEST_ASSERT_EQUAL_MESSAGE(ESP_OK, tinyusb_driver_install(&tusb_cfg), "Failed to install TinyUSB driver");
607607
test_device_wait_event(TINYUSB_EVENT_ATTACHED);
608608

609-
uint8_t dev_mounted = 0; /* TODO: Expect to fail on run. Enable in VBUS monitor part2 */
609+
uint8_t dev_mounted = test_vbus_manual_attach_detach();
610610

611611
// Cleanup
612612
TEST_ASSERT_EQUAL_MESSAGE(ESP_OK, tinyusb_driver_uninstall(), "Failed to uninstall TinyUSB driver");

device/esp_tinyusb/tinyusb_vbus_monitor.c

Lines changed: 215 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,20 @@
88
#include "esp_log.h"
99
#include "esp_err.h"
1010
#include "esp_check.h"
11+
#include "freertos/FreeRTOS.h"
12+
#include "freertos/timers.h"
13+
#include "driver/gpio.h"
1114
#include "tinyusb_vbus_monitor.h"
1215

1316
const static char *TAG = "VBUS mon";
1417

18+
#if (CONFIG_IDF_TARGET_ESP32P4)
19+
#include "soc/usb_dwc_struct.h"
20+
// On ESP32-P4 USB OTG 2.0 signals are not wired to GPIO matrix
21+
// So we need to override the Bvalid signal from PHY
22+
#define USB_DWC_REG USB_DWC_HS
23+
#endif // CONFIG_IDF_TARGET_ESP32P4
24+
1525
/**
1626
* @brief VBUS monitoring context
1727
*
@@ -23,16 +33,219 @@ typedef struct {
2333
TimerHandle_t debounce_timer; /*!< Debounce timer handle */
2434
} vbus_monitor_context_t;
2535

36+
static vbus_monitor_context_t _vbus_ctx = {
37+
.gpio_num = GPIO_NUM_NC,
38+
.prev_state = false,
39+
.debounce_timer = NULL,
40+
};
41+
42+
//
43+
// Additional low-level USB DWC functions, which are not present in the IDF USB DWC HAL
44+
//
45+
46+
// --------------- GOTGCTL register ------------------
47+
48+
static void usb_dwc_ll_gotgctl_set_bvalid_override_value(usb_dwc_dev_t *hw, uint8_t value)
49+
{
50+
hw->gotgctl_reg.bvalidovval = value;
51+
}
52+
53+
static void usb_dwc_ll_gotgctl_enable_bvalid_override(usb_dwc_dev_t *hw, bool enable)
54+
{
55+
hw->gotgctl_reg.bvalidoven = enable ? 1 : 0;
56+
}
57+
58+
// ------------------ DCTL register --------------------
59+
60+
static void usb_dwc_ll_dctl_set_soft_disconnect(usb_dwc_dev_t *hw, bool enable)
61+
{
62+
hw->dctl_reg.sftdiscon = enable ? 1 : 0;
63+
}
64+
65+
// -------------- VBUS Internal Logic ------------------
66+
67+
/**
68+
* @brief Handle VBUS appeared event
69+
*/
70+
static void vbus_appeared(void)
71+
{
72+
ESP_LOGD(TAG, "Appeared");
73+
usb_dwc_ll_gotgctl_set_bvalid_override_value(&USB_DWC_REG, 1);
74+
usb_dwc_ll_dctl_set_soft_disconnect(&USB_DWC_REG, false);
75+
}
76+
77+
/**
78+
* @brief Handle VBUS disappeared event
79+
*/
80+
static void vbus_disappeared(void)
81+
{
82+
ESP_LOGD(TAG, "Disappeared");
83+
usb_dwc_ll_gotgctl_set_bvalid_override_value(&USB_DWC_REG, 0);
84+
usb_dwc_ll_dctl_set_soft_disconnect(&USB_DWC_REG, true);
85+
}
86+
87+
/**
88+
* @brief GPIO interrupt handler for VBUS monitoring io
89+
*
90+
* @param arg GPIO number
91+
*/
92+
static void vbus_io_cb(void *arg)
93+
{
94+
(void) arg;
95+
// disable interrupts for a while to debounce
96+
gpio_intr_disable(_vbus_ctx.gpio_num);
97+
98+
bool vbus_curr_state = gpio_get_level(_vbus_ctx.gpio_num);
99+
100+
if (_vbus_ctx.prev_state != vbus_curr_state) {
101+
_vbus_ctx.prev_state = vbus_curr_state;
102+
// VBUS pin state has changed, start the debounce timer
103+
BaseType_t yield = pdFALSE;
104+
if (xTimerStartFromISR(_vbus_ctx.debounce_timer, &yield) != pdPASS) {
105+
ESP_EARLY_LOGE(TAG, "Failed to start VBUS debounce timer");
106+
}
107+
if (yield == pdTRUE) {
108+
portYIELD_FROM_ISR();
109+
}
110+
} else {
111+
// VBUS gpio glitch, ignore and re-enable interrupt
112+
gpio_intr_enable(_vbus_ctx.gpio_num);
113+
}
114+
}
115+
116+
/**
117+
* @brief VBUS debounce timer callback
118+
*
119+
* @param xTimer Timer handle
120+
*/
121+
static void vbus_debounce_timer_cb(TimerHandle_t xTimer)
122+
{
123+
bool vbus_prev_state = _vbus_ctx.prev_state;
124+
bool vbus_curr_state = gpio_get_level(_vbus_ctx.gpio_num);
125+
126+
if (vbus_curr_state && vbus_prev_state) {
127+
vbus_appeared();
128+
} else if (!vbus_curr_state && !vbus_prev_state) {
129+
vbus_disappeared();
130+
} else {
131+
// State changed again during debounce period, ignore
132+
}
133+
// Update the state
134+
_vbus_ctx.prev_state = vbus_curr_state;
135+
// Re-enable GPIO interrupt
136+
gpio_intr_enable(_vbus_ctx.gpio_num);
137+
}
138+
26139
// -------------- Public API ------------------
27140

28141
esp_err_t tinyusb_vbus_monitor_init(tinyusb_vbus_monitor_config_t *config)
29142
{
30143
ESP_RETURN_ON_FALSE(config != NULL, ESP_ERR_INVALID_ARG, TAG, "Invalid argument: config is NULL");
144+
145+
esp_err_t ret;
146+
// There could be only one instance of VBUS monitoring
147+
if (_vbus_ctx.debounce_timer != NULL) {
148+
ESP_LOGE(TAG, "Already initialized");
149+
return ESP_ERR_INVALID_STATE;
150+
}
151+
152+
_vbus_ctx.gpio_num = config->gpio_num;
153+
_vbus_ctx.prev_state = false;
154+
155+
// VBUS Debounce timer
156+
_vbus_ctx.debounce_timer = xTimerCreate("vbus_debounce_timer",
157+
pdMS_TO_TICKS(config->debounce_delay_ms),
158+
pdFALSE,
159+
NULL,
160+
vbus_debounce_timer_cb);
161+
162+
if (_vbus_ctx.debounce_timer == NULL) {
163+
ESP_LOGE(TAG, "Create VBUS debounce timer failed");
164+
return ESP_ERR_NO_MEM;
165+
}
166+
167+
// Init gpio IRQ for VBUS monitoring
168+
const gpio_config_t vbus_io_cfg = {
169+
.pin_bit_mask = BIT64(_vbus_ctx.gpio_num),
170+
.mode = GPIO_MODE_INPUT,
171+
.pull_down_en = GPIO_PULLDOWN_ENABLE,
172+
.intr_type = GPIO_INTR_ANYEDGE,
173+
};
174+
175+
ret = gpio_config(&vbus_io_cfg);
176+
if (ret != ESP_OK) {
177+
ESP_LOGE(TAG, "Config VBUS GPIO failed");
178+
goto gpio_fail;
179+
}
180+
181+
ret = gpio_install_isr_service(ESP_INTR_FLAG_LOWMED);
182+
// Service could be already installed, it is not an error
183+
if (ret != ESP_OK && ret != ESP_ERR_INVALID_STATE) {
184+
ESP_LOGE(TAG, "Install GPIO ISR service failed");
185+
goto isr_fail;
186+
}
187+
188+
ret = gpio_isr_handler_add(_vbus_ctx.gpio_num, vbus_io_cb, (void *) NULL);
189+
if (ret != ESP_OK) {
190+
ESP_LOGE(TAG, "Add GPIO ISR handler failed");
191+
goto isr_fail;
192+
}
193+
// Disable GPIO interrupt
194+
gpio_intr_disable(_vbus_ctx.gpio_num);
195+
// Set initial Bvalid override value and enable override
196+
usb_dwc_ll_gotgctl_set_bvalid_override_value(&USB_DWC_REG, 0);
197+
// Wait 1 microsecond (sufficient for >5 PHY clocks)
198+
esp_rom_delay_us(1);
199+
// Enable to override the signal from PHY
200+
usb_dwc_ll_gotgctl_enable_bvalid_override(&USB_DWC_REG, true);
201+
202+
// Device could be already connected, check the status and start the timer if needed
203+
if (gpio_get_level(_vbus_ctx.gpio_num)) {
204+
_vbus_ctx.prev_state = true;
205+
// Start debounce timer
206+
if (xTimerStart(_vbus_ctx.debounce_timer, 0) != pdPASS) {
207+
ESP_LOGE(TAG, "Failed to start VBUS debounce timer");
208+
}
209+
} else {
210+
// Enable GPIO interrupt
211+
gpio_intr_enable(_vbus_ctx.gpio_num);
212+
}
213+
31214
ESP_LOGD(TAG, "Init GPIO%d, debounce delay: %"PRIu32" ms", config->gpio_num, config->debounce_delay_ms);
32-
return ESP_ERR_NOT_SUPPORTED;
215+
return ESP_OK;
216+
217+
isr_fail:
218+
gpio_reset_pin(_vbus_ctx.gpio_num);
219+
gpio_fail:
220+
if (_vbus_ctx.debounce_timer) {
221+
xTimerDelete(_vbus_ctx.debounce_timer, 0);
222+
_vbus_ctx.debounce_timer = NULL;
223+
}
224+
return ret;
33225
}
34226

35-
void tinyusb_vbus_monitor_deinit(void)
227+
esp_err_t tinyusb_vbus_monitor_deinit(void)
36228
{
229+
if (_vbus_ctx.debounce_timer == NULL) {
230+
return ESP_ERR_INVALID_STATE;
231+
}
232+
// Deinit gpio IRQ for VBUS monitoring
233+
if (xTimerIsTimerActive(_vbus_ctx.debounce_timer) == pdTRUE) {
234+
xTimerStop(_vbus_ctx.debounce_timer, 0);
235+
}
236+
xTimerDelete(_vbus_ctx.debounce_timer, 0);
237+
_vbus_ctx.debounce_timer = NULL;
238+
239+
// Disable to override the signal from PHY
240+
usb_dwc_ll_gotgctl_enable_bvalid_override(&USB_DWC_REG, false);
241+
// Deinit gpio IRQ for VBUS monitoring
242+
esp_err_t ret = gpio_isr_handler_remove(_vbus_ctx.gpio_num);
243+
if (ret != ESP_OK) {
244+
ESP_LOGE(TAG, "Remove GPIO ISR handler failed");
245+
return ret;
246+
}
247+
gpio_intr_disable(_vbus_ctx.gpio_num);
248+
37249
ESP_LOGD(TAG, "Deinit");
250+
return ESP_OK;
38251
}

0 commit comments

Comments
 (0)