summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/library/pyb.USB_VCP.rst14
-rw-r--r--ports/stm32/mpconfigport.h3
-rw-r--r--ports/stm32/usb.c71
-rw-r--r--ports/stm32/usbd_cdc_interface.c2
-rw-r--r--ports/stm32/usbd_cdc_interface.h2
5 files changed, 91 insertions, 1 deletions
diff --git a/docs/library/pyb.USB_VCP.rst b/docs/library/pyb.USB_VCP.rst
index bbcbc0701..1e44e53fd 100644
--- a/docs/library/pyb.USB_VCP.rst
+++ b/docs/library/pyb.USB_VCP.rst
@@ -109,6 +109,16 @@ Methods
Return value: number of bytes sent.
+.. method:: USB_VCP.irq(handler=None, trigger=0, hard=False)
+
+ Register *handler* to be called whenever an event specified by *trigger*
+ occurs. The *handler* function must take exactly one argument, which will
+ be the USB VCP object. Pass in ``None`` to disable the callback.
+
+ Valid values for *trigger* are:
+
+ - ``USB_VCP.IRQ_RX``: new data is available for reading from the USB VCP object.
+
Constants
---------
@@ -117,3 +127,7 @@ Constants
USB_VCP.CTS
to select the flow control type.
+
+.. data:: USB_VCP.IRQ_RX
+
+ IRQ trigger values for :meth:`USB_VCP.irq`.
diff --git a/ports/stm32/mpconfigport.h b/ports/stm32/mpconfigport.h
index 28a2d6a76..ff58c515e 100644
--- a/ports/stm32/mpconfigport.h
+++ b/ports/stm32/mpconfigport.h
@@ -383,6 +383,9 @@ struct _mp_bluetooth_btstack_root_pointers_t;
/* pointers to all CAN objects (if they have been created) */ \
struct _pyb_can_obj_t *pyb_can_obj_all[MICROPY_HW_MAX_CAN]; \
\
+ /* USB_VCP IRQ callbacks (if they have been set) */ \
+ mp_obj_t pyb_usb_vcp_irq[MICROPY_HW_USB_CDC_NUM]; \
+ \
/* list of registered NICs */ \
mp_obj_list_t mod_network_nic_list; \
\
diff --git a/ports/stm32/usb.c b/ports/stm32/usb.c
index 5003bb27c..eba95de49 100644
--- a/ports/stm32/usb.c
+++ b/ports/stm32/usb.c
@@ -39,6 +39,7 @@
#include "py/stream.h"
#include "py/mperrno.h"
#include "py/mphal.h"
+#include "lib/utils/mpirq.h"
#include "bufhelper.h"
#include "storage.h"
#include "sdcard.h"
@@ -70,6 +71,9 @@
#define MAX_ENDPOINT(dev_id) (8)
#endif
+// Constants for USB_VCP.irq trigger.
+#define USBD_CDC_IRQ_RX (1)
+
STATIC void pyb_usb_vcp_init0(void);
// this will be persistent across a soft-reset
@@ -219,6 +223,7 @@ const mp_rom_obj_tuple_t pyb_usb_hid_keyboard_obj = {
void pyb_usb_init0(void) {
for (int i = 0; i < MICROPY_HW_USB_CDC_NUM; ++i) {
+ usb_device.usbd_cdc_itf[i].cdc_idx = i;
usb_device.usbd_cdc_itf[i].attached_to_repl = false;
}
#if MICROPY_HW_USB_HID
@@ -644,14 +649,42 @@ const pyb_usb_vcp_obj_t pyb_usb_vcp_obj[MICROPY_HW_USB_CDC_NUM] = {
#endif
};
+STATIC bool pyb_usb_vcp_irq_scheduled[MICROPY_HW_USB_CDC_NUM];
+
STATIC void pyb_usb_vcp_init0(void) {
+ for (size_t i = 0; i < MICROPY_HW_USB_CDC_NUM; ++i) {
+ MP_STATE_PORT(pyb_usb_vcp_irq)[i] = mp_const_none;
+ pyb_usb_vcp_irq_scheduled[i] = false;
+ }
+
// Activate USB_VCP(0) on dupterm slot 1 for the REPL
MP_STATE_VM(dupterm_objs[1]) = MP_OBJ_FROM_PTR(&pyb_usb_vcp_obj[0]);
usb_vcp_attach_to_repl(&pyb_usb_vcp_obj[0], true);
}
+STATIC mp_obj_t pyb_usb_vcp_irq_run(mp_obj_t self_in) {
+ pyb_usb_vcp_obj_t *self = MP_OBJ_TO_PTR(self_in);
+ uint8_t idx = self->cdc_itf->cdc_idx;
+ mp_obj_t callback = MP_STATE_PORT(pyb_usb_vcp_irq)[idx];
+ pyb_usb_vcp_irq_scheduled[idx] = false;
+ if (callback != mp_const_none && usbd_cdc_rx_num(self->cdc_itf)) {
+ mp_call_function_1(callback, self_in);
+ }
+ return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(pyb_usb_vcp_irq_run_obj, pyb_usb_vcp_irq_run);
+
+void usbd_cdc_rx_event_callback(usbd_cdc_itf_t *cdc) {
+ uint8_t idx = cdc->cdc_idx;
+ mp_obj_t self = MP_OBJ_FROM_PTR(&pyb_usb_vcp_obj[idx]);
+ mp_obj_t callback = MP_STATE_PORT(pyb_usb_vcp_irq)[idx];
+ if (callback != mp_const_none && !pyb_usb_vcp_irq_scheduled[idx]) {
+ pyb_usb_vcp_irq_scheduled[idx] = mp_sched_schedule(MP_OBJ_FROM_PTR(&pyb_usb_vcp_irq_run_obj), self);
+ }
+}
+
STATIC void pyb_usb_vcp_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
- int id = ((pyb_usb_vcp_obj_t *)MP_OBJ_TO_PTR(self_in))->cdc_itf - &usb_device.usbd_cdc_itf[0];
+ int id = ((pyb_usb_vcp_obj_t *)MP_OBJ_TO_PTR(self_in))->cdc_itf->cdc_idx;
mp_printf(print, "USB_VCP(%u)", id);
}
@@ -796,6 +829,40 @@ STATIC mp_obj_t pyb_usb_vcp_recv(size_t n_args, const mp_obj_t *args, mp_map_t *
}
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_usb_vcp_recv_obj, 1, pyb_usb_vcp_recv);
+// irq(handler=None, trigger=0, hard=False)
+STATIC mp_obj_t pyb_usb_vcp_irq(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
+ mp_arg_val_t args[MP_IRQ_ARG_INIT_NUM_ARGS];
+ mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_IRQ_ARG_INIT_NUM_ARGS, mp_irq_init_args, args);
+ pyb_usb_vcp_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]);
+
+ if (n_args > 1 || kw_args->used != 0) {
+ // Check the handler.
+ mp_obj_t handler = args[MP_IRQ_ARG_INIT_handler].u_obj;
+ if (handler != mp_const_none && !mp_obj_is_callable(handler)) {
+ mp_raise_ValueError(MP_ERROR_TEXT("handler must be None or callable"));
+ }
+
+ // Check the trigger.
+ mp_uint_t trigger = args[MP_IRQ_ARG_INIT_trigger].u_int;
+ if (trigger == 0) {
+ handler = mp_const_none;
+ } else if (trigger != USBD_CDC_IRQ_RX) {
+ mp_raise_ValueError(MP_ERROR_TEXT("unsupported trigger"));
+ }
+
+ // Check hard/soft.
+ if (args[MP_IRQ_ARG_INIT_hard].u_bool) {
+ mp_raise_ValueError(MP_ERROR_TEXT("hard unsupported"));
+ }
+
+ // Reconfigure the IRQ.
+ MP_STATE_PORT(pyb_usb_vcp_irq)[self->cdc_itf->cdc_idx] = handler;
+ }
+
+ return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_usb_vcp_irq_obj, 1, pyb_usb_vcp_irq);
+
mp_obj_t pyb_usb_vcp___exit__(size_t n_args, const mp_obj_t *args) {
return mp_const_none;
}
@@ -814,6 +881,7 @@ STATIC const mp_rom_map_elem_t pyb_usb_vcp_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_readlines), MP_ROM_PTR(&mp_stream_unbuffered_readlines_obj)},
{ MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&mp_stream_write_obj) },
{ MP_ROM_QSTR(MP_QSTR_close), MP_ROM_PTR(&mp_identity_obj) },
+ { MP_ROM_QSTR(MP_QSTR_irq), MP_ROM_PTR(&pyb_usb_vcp_irq_obj) },
{ MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&mp_identity_obj) },
{ MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&mp_identity_obj) },
{ MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&pyb_usb_vcp___exit___obj) },
@@ -821,6 +889,7 @@ STATIC const mp_rom_map_elem_t pyb_usb_vcp_locals_dict_table[] = {
// class constants
{ MP_ROM_QSTR(MP_QSTR_RTS), MP_ROM_INT(USBD_CDC_FLOWCONTROL_RTS) },
{ MP_ROM_QSTR(MP_QSTR_CTS), MP_ROM_INT(USBD_CDC_FLOWCONTROL_CTS) },
+ { MP_ROM_QSTR(MP_QSTR_IRQ_RX), MP_ROM_INT(USBD_CDC_IRQ_RX) },
};
STATIC MP_DEFINE_CONST_DICT(pyb_usb_vcp_locals_dict, pyb_usb_vcp_locals_dict_table);
diff --git a/ports/stm32/usbd_cdc_interface.c b/ports/stm32/usbd_cdc_interface.c
index a465f608a..a1ac5fd22 100644
--- a/ports/stm32/usbd_cdc_interface.c
+++ b/ports/stm32/usbd_cdc_interface.c
@@ -308,6 +308,8 @@ int8_t usbd_cdc_receive(usbd_cdc_state_t *cdc_in, size_t len) {
}
}
+ usbd_cdc_rx_event_callback(cdc);
+
if ((cdc->flow & USBD_CDC_FLOWCONTROL_RTS) && (usbd_cdc_rx_buffer_full(cdc))) {
cdc->rx_buf_full = true;
return USBD_BUSY;
diff --git a/ports/stm32/usbd_cdc_interface.h b/ports/stm32/usbd_cdc_interface.h
index d0509b09f..6b510f239 100644
--- a/ports/stm32/usbd_cdc_interface.h
+++ b/ports/stm32/usbd_cdc_interface.h
@@ -63,6 +63,7 @@ typedef struct _usbd_cdc_itf_t {
uint16_t tx_buf_ptr_out_next; // next position of above once transmission finished
uint8_t tx_need_empty_packet; // used to flush the USB IN endpoint if the last packet was exactly the endpoint packet size
+ uint8_t cdc_idx; // between 0 and MICROPY_HW_USB_CDC_NUM-1
volatile uint8_t connect_state; // indicates if we are connected
uint8_t attached_to_repl; // indicates if interface is connected to REPL
uint8_t flow; // USBD_CDC_FLOWCONTROL_* setting flags
@@ -82,5 +83,6 @@ void usbd_cdc_tx_always(usbd_cdc_itf_t *cdc, const uint8_t *buf, uint32_t len);
int usbd_cdc_rx_num(usbd_cdc_itf_t *cdc);
int usbd_cdc_rx(usbd_cdc_itf_t *cdc, uint8_t *buf, uint32_t len, uint32_t timeout);
+void usbd_cdc_rx_event_callback(usbd_cdc_itf_t *cdc);
#endif // MICROPY_INCLUDED_STM32_USBD_CDC_INTERFACE_H