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 <damien@micropython.org>
This commit is contained in:
parent
8c02b94946
commit
71e3538a32
@ -109,6 +109,16 @@ Methods
|
|||||||
|
|
||||||
Return value: number of bytes sent.
|
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
|
Constants
|
||||||
---------
|
---------
|
||||||
@ -117,3 +127,7 @@ Constants
|
|||||||
USB_VCP.CTS
|
USB_VCP.CTS
|
||||||
|
|
||||||
to select the flow control type.
|
to select the flow control type.
|
||||||
|
|
||||||
|
.. data:: USB_VCP.IRQ_RX
|
||||||
|
|
||||||
|
IRQ trigger values for :meth:`USB_VCP.irq`.
|
||||||
|
@ -383,6 +383,9 @@ struct _mp_bluetooth_btstack_root_pointers_t;
|
|||||||
/* pointers to all CAN objects (if they have been created) */ \
|
/* pointers to all CAN objects (if they have been created) */ \
|
||||||
struct _pyb_can_obj_t *pyb_can_obj_all[MICROPY_HW_MAX_CAN]; \
|
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 */ \
|
/* list of registered NICs */ \
|
||||||
mp_obj_list_t mod_network_nic_list; \
|
mp_obj_list_t mod_network_nic_list; \
|
||||||
\
|
\
|
||||||
|
@ -39,6 +39,7 @@
|
|||||||
#include "py/stream.h"
|
#include "py/stream.h"
|
||||||
#include "py/mperrno.h"
|
#include "py/mperrno.h"
|
||||||
#include "py/mphal.h"
|
#include "py/mphal.h"
|
||||||
|
#include "lib/utils/mpirq.h"
|
||||||
#include "bufhelper.h"
|
#include "bufhelper.h"
|
||||||
#include "storage.h"
|
#include "storage.h"
|
||||||
#include "sdcard.h"
|
#include "sdcard.h"
|
||||||
@ -70,6 +71,9 @@
|
|||||||
#define MAX_ENDPOINT(dev_id) (8)
|
#define MAX_ENDPOINT(dev_id) (8)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// Constants for USB_VCP.irq trigger.
|
||||||
|
#define USBD_CDC_IRQ_RX (1)
|
||||||
|
|
||||||
STATIC void pyb_usb_vcp_init0(void);
|
STATIC void pyb_usb_vcp_init0(void);
|
||||||
|
|
||||||
// this will be persistent across a soft-reset
|
// 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) {
|
void pyb_usb_init0(void) {
|
||||||
for (int i = 0; i < MICROPY_HW_USB_CDC_NUM; ++i) {
|
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;
|
usb_device.usbd_cdc_itf[i].attached_to_repl = false;
|
||||||
}
|
}
|
||||||
#if MICROPY_HW_USB_HID
|
#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
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
STATIC bool pyb_usb_vcp_irq_scheduled[MICROPY_HW_USB_CDC_NUM];
|
||||||
|
|
||||||
STATIC void pyb_usb_vcp_init0(void) {
|
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
|
// 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]);
|
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);
|
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) {
|
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);
|
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);
|
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) {
|
mp_obj_t pyb_usb_vcp___exit__(size_t n_args, const mp_obj_t *args) {
|
||||||
return mp_const_none;
|
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_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_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_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___del__), MP_ROM_PTR(&mp_identity_obj) },
|
||||||
{ MP_ROM_QSTR(MP_QSTR___enter__), 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) },
|
{ 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
|
// class constants
|
||||||
{ MP_ROM_QSTR(MP_QSTR_RTS), MP_ROM_INT(USBD_CDC_FLOWCONTROL_RTS) },
|
{ 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_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);
|
STATIC MP_DEFINE_CONST_DICT(pyb_usb_vcp_locals_dict, pyb_usb_vcp_locals_dict_table);
|
||||||
|
@ -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))) {
|
if ((cdc->flow & USBD_CDC_FLOWCONTROL_RTS) && (usbd_cdc_rx_buffer_full(cdc))) {
|
||||||
cdc->rx_buf_full = true;
|
cdc->rx_buf_full = true;
|
||||||
return USBD_BUSY;
|
return USBD_BUSY;
|
||||||
|
@ -63,6 +63,7 @@ typedef struct _usbd_cdc_itf_t {
|
|||||||
uint16_t tx_buf_ptr_out_next; // next position of above once transmission finished
|
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 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
|
volatile uint8_t connect_state; // indicates if we are connected
|
||||||
uint8_t attached_to_repl; // indicates if interface is connected to REPL
|
uint8_t attached_to_repl; // indicates if interface is connected to REPL
|
||||||
uint8_t flow; // USBD_CDC_FLOWCONTROL_* setting flags
|
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_num(usbd_cdc_itf_t *cdc);
|
||||||
int usbd_cdc_rx(usbd_cdc_itf_t *cdc, uint8_t *buf, uint32_t len, uint32_t timeout);
|
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
|
#endif // MICROPY_INCLUDED_STM32_USBD_CDC_INTERFACE_H
|
||||||
|
Loading…
Reference in New Issue
Block a user