Skip to content

Commit 71e3538

Browse files
committed
stm32/usb: Add USB_VCP.irq method, to set a callback on USB data RX.
Usage: usb = pyb.USB_VCP() usb.irq(lambda u:print(u, u.read()), usb.IRQ_RX) Signed-off-by: Damien George <[email protected]>
1 parent 8c02b94 commit 71e3538

File tree

5 files changed

+91
-1
lines changed

5 files changed

+91
-1
lines changed

docs/library/pyb.USB_VCP.rst

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,16 @@ Methods
109109

110110
Return value: number of bytes sent.
111111

112+
.. method:: USB_VCP.irq(handler=None, trigger=0, hard=False)
113+
114+
Register *handler* to be called whenever an event specified by *trigger*
115+
occurs. The *handler* function must take exactly one argument, which will
116+
be the USB VCP object. Pass in ``None`` to disable the callback.
117+
118+
Valid values for *trigger* are:
119+
120+
- ``USB_VCP.IRQ_RX``: new data is available for reading from the USB VCP object.
121+
112122

113123
Constants
114124
---------
@@ -117,3 +127,7 @@ Constants
117127
USB_VCP.CTS
118128

119129
to select the flow control type.
130+
131+
.. data:: USB_VCP.IRQ_RX
132+
133+
IRQ trigger values for :meth:`USB_VCP.irq`.

ports/stm32/mpconfigport.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -383,6 +383,9 @@ struct _mp_bluetooth_btstack_root_pointers_t;
383383
/* pointers to all CAN objects (if they have been created) */ \
384384
struct _pyb_can_obj_t *pyb_can_obj_all[MICROPY_HW_MAX_CAN]; \
385385
\
386+
/* USB_VCP IRQ callbacks (if they have been set) */ \
387+
mp_obj_t pyb_usb_vcp_irq[MICROPY_HW_USB_CDC_NUM]; \
388+
\
386389
/* list of registered NICs */ \
387390
mp_obj_list_t mod_network_nic_list; \
388391
\

ports/stm32/usb.c

Lines changed: 70 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
#include "py/stream.h"
4040
#include "py/mperrno.h"
4141
#include "py/mphal.h"
42+
#include "lib/utils/mpirq.h"
4243
#include "bufhelper.h"
4344
#include "storage.h"
4445
#include "sdcard.h"
@@ -70,6 +71,9 @@
7071
#define MAX_ENDPOINT(dev_id) (8)
7172
#endif
7273

74+
// Constants for USB_VCP.irq trigger.
75+
#define USBD_CDC_IRQ_RX (1)
76+
7377
STATIC void pyb_usb_vcp_init0(void);
7478

7579
// this will be persistent across a soft-reset
@@ -219,6 +223,7 @@ const mp_rom_obj_tuple_t pyb_usb_hid_keyboard_obj = {
219223

220224
void pyb_usb_init0(void) {
221225
for (int i = 0; i < MICROPY_HW_USB_CDC_NUM; ++i) {
226+
usb_device.usbd_cdc_itf[i].cdc_idx = i;
222227
usb_device.usbd_cdc_itf[i].attached_to_repl = false;
223228
}
224229
#if MICROPY_HW_USB_HID
@@ -644,14 +649,42 @@ const pyb_usb_vcp_obj_t pyb_usb_vcp_obj[MICROPY_HW_USB_CDC_NUM] = {
644649
#endif
645650
};
646651

652+
STATIC bool pyb_usb_vcp_irq_scheduled[MICROPY_HW_USB_CDC_NUM];
653+
647654
STATIC void pyb_usb_vcp_init0(void) {
655+
for (size_t i = 0; i < MICROPY_HW_USB_CDC_NUM; ++i) {
656+
MP_STATE_PORT(pyb_usb_vcp_irq)[i] = mp_const_none;
657+
pyb_usb_vcp_irq_scheduled[i] = false;
658+
}
659+
648660
// Activate USB_VCP(0) on dupterm slot 1 for the REPL
649661
MP_STATE_VM(dupterm_objs[1]) = MP_OBJ_FROM_PTR(&pyb_usb_vcp_obj[0]);
650662
usb_vcp_attach_to_repl(&pyb_usb_vcp_obj[0], true);
651663
}
652664

665+
STATIC mp_obj_t pyb_usb_vcp_irq_run(mp_obj_t self_in) {
666+
pyb_usb_vcp_obj_t *self = MP_OBJ_TO_PTR(self_in);
667+
uint8_t idx = self->cdc_itf->cdc_idx;
668+
mp_obj_t callback = MP_STATE_PORT(pyb_usb_vcp_irq)[idx];
669+
pyb_usb_vcp_irq_scheduled[idx] = false;
670+
if (callback != mp_const_none && usbd_cdc_rx_num(self->cdc_itf)) {
671+
mp_call_function_1(callback, self_in);
672+
}
673+
return mp_const_none;
674+
}
675+
STATIC MP_DEFINE_CONST_FUN_OBJ_1(pyb_usb_vcp_irq_run_obj, pyb_usb_vcp_irq_run);
676+
677+
void usbd_cdc_rx_event_callback(usbd_cdc_itf_t *cdc) {
678+
uint8_t idx = cdc->cdc_idx;
679+
mp_obj_t self = MP_OBJ_FROM_PTR(&pyb_usb_vcp_obj[idx]);
680+
mp_obj_t callback = MP_STATE_PORT(pyb_usb_vcp_irq)[idx];
681+
if (callback != mp_const_none && !pyb_usb_vcp_irq_scheduled[idx]) {
682+
pyb_usb_vcp_irq_scheduled[idx] = mp_sched_schedule(MP_OBJ_FROM_PTR(&pyb_usb_vcp_irq_run_obj), self);
683+
}
684+
}
685+
653686
STATIC void pyb_usb_vcp_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
654-
int id = ((pyb_usb_vcp_obj_t *)MP_OBJ_TO_PTR(self_in))->cdc_itf - &usb_device.usbd_cdc_itf[0];
687+
int id = ((pyb_usb_vcp_obj_t *)MP_OBJ_TO_PTR(self_in))->cdc_itf->cdc_idx;
655688
mp_printf(print, "USB_VCP(%u)", id);
656689
}
657690

@@ -796,6 +829,40 @@ STATIC mp_obj_t pyb_usb_vcp_recv(size_t n_args, const mp_obj_t *args, mp_map_t *
796829
}
797830
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_usb_vcp_recv_obj, 1, pyb_usb_vcp_recv);
798831

832+
// irq(handler=None, trigger=0, hard=False)
833+
STATIC mp_obj_t pyb_usb_vcp_irq(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
834+
mp_arg_val_t args[MP_IRQ_ARG_INIT_NUM_ARGS];
835+
mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_IRQ_ARG_INIT_NUM_ARGS, mp_irq_init_args, args);
836+
pyb_usb_vcp_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]);
837+
838+
if (n_args > 1 || kw_args->used != 0) {
839+
// Check the handler.
840+
mp_obj_t handler = args[MP_IRQ_ARG_INIT_handler].u_obj;
841+
if (handler != mp_const_none && !mp_obj_is_callable(handler)) {
842+
mp_raise_ValueError(MP_ERROR_TEXT("handler must be None or callable"));
843+
}
844+
845+
// Check the trigger.
846+
mp_uint_t trigger = args[MP_IRQ_ARG_INIT_trigger].u_int;
847+
if (trigger == 0) {
848+
handler = mp_const_none;
849+
} else if (trigger != USBD_CDC_IRQ_RX) {
850+
mp_raise_ValueError(MP_ERROR_TEXT("unsupported trigger"));
851+
}
852+
853+
// Check hard/soft.
854+
if (args[MP_IRQ_ARG_INIT_hard].u_bool) {
855+
mp_raise_ValueError(MP_ERROR_TEXT("hard unsupported"));
856+
}
857+
858+
// Reconfigure the IRQ.
859+
MP_STATE_PORT(pyb_usb_vcp_irq)[self->cdc_itf->cdc_idx] = handler;
860+
}
861+
862+
return mp_const_none;
863+
}
864+
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_usb_vcp_irq_obj, 1, pyb_usb_vcp_irq);
865+
799866
mp_obj_t pyb_usb_vcp___exit__(size_t n_args, const mp_obj_t *args) {
800867
return mp_const_none;
801868
}
@@ -814,13 +881,15 @@ STATIC const mp_rom_map_elem_t pyb_usb_vcp_locals_dict_table[] = {
814881
{ MP_ROM_QSTR(MP_QSTR_readlines), MP_ROM_PTR(&mp_stream_unbuffered_readlines_obj)},
815882
{ MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&mp_stream_write_obj) },
816883
{ MP_ROM_QSTR(MP_QSTR_close), MP_ROM_PTR(&mp_identity_obj) },
884+
{ MP_ROM_QSTR(MP_QSTR_irq), MP_ROM_PTR(&pyb_usb_vcp_irq_obj) },
817885
{ MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&mp_identity_obj) },
818886
{ MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&mp_identity_obj) },
819887
{ MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&pyb_usb_vcp___exit___obj) },
820888

821889
// class constants
822890
{ MP_ROM_QSTR(MP_QSTR_RTS), MP_ROM_INT(USBD_CDC_FLOWCONTROL_RTS) },
823891
{ MP_ROM_QSTR(MP_QSTR_CTS), MP_ROM_INT(USBD_CDC_FLOWCONTROL_CTS) },
892+
{ MP_ROM_QSTR(MP_QSTR_IRQ_RX), MP_ROM_INT(USBD_CDC_IRQ_RX) },
824893
};
825894

826895
STATIC MP_DEFINE_CONST_DICT(pyb_usb_vcp_locals_dict, pyb_usb_vcp_locals_dict_table);

ports/stm32/usbd_cdc_interface.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,8 @@ int8_t usbd_cdc_receive(usbd_cdc_state_t *cdc_in, size_t len) {
308308
}
309309
}
310310

311+
usbd_cdc_rx_event_callback(cdc);
312+
311313
if ((cdc->flow & USBD_CDC_FLOWCONTROL_RTS) && (usbd_cdc_rx_buffer_full(cdc))) {
312314
cdc->rx_buf_full = true;
313315
return USBD_BUSY;

ports/stm32/usbd_cdc_interface.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ typedef struct _usbd_cdc_itf_t {
6363
uint16_t tx_buf_ptr_out_next; // next position of above once transmission finished
6464
uint8_t tx_need_empty_packet; // used to flush the USB IN endpoint if the last packet was exactly the endpoint packet size
6565

66+
uint8_t cdc_idx; // between 0 and MICROPY_HW_USB_CDC_NUM-1
6667
volatile uint8_t connect_state; // indicates if we are connected
6768
uint8_t attached_to_repl; // indicates if interface is connected to REPL
6869
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);
8283

8384
int usbd_cdc_rx_num(usbd_cdc_itf_t *cdc);
8485
int usbd_cdc_rx(usbd_cdc_itf_t *cdc, uint8_t *buf, uint32_t len, uint32_t timeout);
86+
void usbd_cdc_rx_event_callback(usbd_cdc_itf_t *cdc);
8587

8688
#endif // MICROPY_INCLUDED_STM32_USBD_CDC_INTERFACE_H

0 commit comments

Comments
 (0)