From c85db05244ef6185fbb3c218c508ddd179830942 Mon Sep 17 00:00:00 2001 From: Mathieu Serandour Date: Tue, 24 Oct 2023 15:54:09 +0200 Subject: [PATCH 01/37] py/lexer: Change token position for new lines. Set the position of new line tokens as the end of the preceding line instead of the beginning of the next line. This is done by first moving the pointer to the end of the current line to skip any whitespace, record the position for the token, then finaly skip any other line and whitespace. The previous behavior was to skip every new line and whitespace, including the indent of the next line, before recording the token position. (Note that both lex->emit_dent and lex->nested_bracket_level equal 0 if had_physical_newline == true, which allows simplifying the if-logic for MP_TOKEN_NEWLINE.) And update the cmd_parsetree.py test expected output, because the position of the new-line token has changed. Fixes issue #12792. Signed-off-by: Mathieu Serandour --- py/lexer.c | 31 +++++++++++++++++++----------- tests/cmdline/cmd_parsetree.py.exp | 2 +- 2 files changed, 21 insertions(+), 12 deletions(-) diff --git a/py/lexer.c b/py/lexer.c index 9587f6b164..5e911a1a23 100644 --- a/py/lexer.c +++ b/py/lexer.c @@ -527,14 +527,14 @@ STATIC void parse_string_literal(mp_lexer_t *lex, bool is_raw, bool is_fstring) vstr_cut_tail_bytes(&lex->vstr, n_closing); } +// This function returns whether it has crossed a newline or not. +// It therefore always return true if stop_at_newline is true STATIC bool skip_whitespace(mp_lexer_t *lex, bool stop_at_newline) { - bool had_physical_newline = false; while (!is_end(lex)) { if (is_physical_newline(lex)) { if (stop_at_newline && lex->nested_bracket_level == 0) { - break; + return true; } - had_physical_newline = true; next_char(lex); } else if (is_whitespace(lex)) { next_char(lex); @@ -543,16 +543,16 @@ STATIC bool skip_whitespace(mp_lexer_t *lex, bool stop_at_newline) { while (!is_end(lex) && !is_physical_newline(lex)) { next_char(lex); } - // had_physical_newline will be set on next loop + // will return true on next loop } else if (is_char_and(lex, '\\', '\n')) { - // line-continuation, so don't set had_physical_newline + // line-continuation, so don't return true next_char(lex); next_char(lex); } else { break; } } - return had_physical_newline; + return false; } void mp_lexer_to_next(mp_lexer_t *lex) { @@ -577,7 +577,10 @@ void mp_lexer_to_next(mp_lexer_t *lex) { vstr_reset(&lex->vstr); // skip white space and comments - bool had_physical_newline = skip_whitespace(lex, false); + // set the newline tokens at the line and column of the preceding line: + // only advance on the pointer until a new line is crossed, save the + // line and column, and then readvance it + bool had_physical_newline = skip_whitespace(lex, true); // set token source information lex->tok_line = lex->line; @@ -591,7 +594,12 @@ void mp_lexer_to_next(mp_lexer_t *lex) { lex->tok_kind = MP_TOKEN_INDENT; lex->emit_dent -= 1; - } else if (had_physical_newline && lex->nested_bracket_level == 0) { + } else if (had_physical_newline) { + // The cursor is at the end of the previous line, pointing to a + // physical newline. Skip any remaining whitespace, comments, and + // newlines. + skip_whitespace(lex, false); + lex->tok_kind = MP_TOKEN_NEWLINE; size_t num_spaces = lex->column - 1; @@ -862,9 +870,10 @@ mp_lexer_t *mp_lexer_new(qstr src_name, mp_reader_t reader) { // preload first token mp_lexer_to_next(lex); - // Check that the first token is in the first column. If it's not then we - // convert the token kind to INDENT so that the parser gives a syntax error. - if (lex->tok_column != 1) { + // Check that the first token is in the first column unless it is a + // newline. Otherwise we convert the token kind to INDENT so that + // the parser gives a syntax error. + if (lex->tok_column != 1 && lex->tok_kind != MP_TOKEN_NEWLINE) { lex->tok_kind = MP_TOKEN_INDENT; } diff --git a/tests/cmdline/cmd_parsetree.py.exp b/tests/cmdline/cmd_parsetree.py.exp index 3049267c0b..6ec553b8a9 100644 --- a/tests/cmdline/cmd_parsetree.py.exp +++ b/tests/cmdline/cmd_parsetree.py.exp @@ -1,5 +1,5 @@ ---------------- -[ 4] \(rule\|file_input_2\)(1) (n=10) +[ 1] file_input_2(1) (n=10) tok(6) [ 4] \(rule\|for_stmt\)(22) (n=4) id(i) From b6a977848407a4ced45d118cf926bd915cc89dfb Mon Sep 17 00:00:00 2001 From: Jim Mussared Date: Fri, 3 Nov 2023 14:19:55 +1100 Subject: [PATCH 02/37] py/misc: Change sizeof to offsetof for variable-length alloc. This fixes the case where e.g. struct foo_t { mp_obj_t x; uint16_t y; char buf[]; }; will have `sizeof(struct foo_t)==8`, but `offsetof(struct foo_t, buf)==6`. When computing the size to allocate for `m_new_obj_var` we need to use offsetof to avoid over-allocating. This is important especially when it might cause it to spill over into another GC block. This work was funded through GitHub Sponsors. Signed-off-by: Jim Mussared --- extmod/modre.c | 4 ++-- extmod/nimble/modbluetooth_nimble.c | 2 +- extmod/vfs_lfsx_file.c | 4 ++-- ports/unix/coverage.c | 2 +- py/misc.h | 14 +++++++------- py/modthread.c | 4 ++-- py/objexcept.c | 2 +- py/objfun.c | 10 +++++----- py/objnamedtuple.c | 2 +- py/objtuple.c | 2 +- py/objtype.c | 2 +- 11 files changed, 24 insertions(+), 24 deletions(-) diff --git a/extmod/modre.c b/extmod/modre.c index 7f00b1c23c..c24c07d095 100644 --- a/extmod/modre.c +++ b/extmod/modre.c @@ -207,12 +207,12 @@ STATIC mp_obj_t re_exec(bool is_anchored, uint n_args, const mp_obj_t *args) { subj.begin_line = subj.begin = mp_obj_str_get_data(args[1], &len); subj.end = subj.begin + len; int caps_num = (self->re.sub + 1) * 2; - mp_obj_match_t *match = m_new_obj_var(mp_obj_match_t, char *, caps_num); + mp_obj_match_t *match = m_new_obj_var(mp_obj_match_t, caps, char *, caps_num); // cast is a workaround for a bug in msvc: it treats const char** as a const pointer instead of a pointer to pointer to const char memset((char *)match->caps, 0, caps_num * sizeof(char *)); int res = re1_5_recursiveloopprog(&self->re, &subj, match->caps, caps_num, is_anchored); if (res == 0) { - m_del_var(mp_obj_match_t, char *, caps_num, match); + m_del_var(mp_obj_match_t, caps, char *, caps_num, match); return mp_const_none; } diff --git a/extmod/nimble/modbluetooth_nimble.c b/extmod/nimble/modbluetooth_nimble.c index 5e3e38ea67..3cdf4d1a78 100644 --- a/extmod/nimble/modbluetooth_nimble.c +++ b/extmod/nimble/modbluetooth_nimble.c @@ -1701,7 +1701,7 @@ STATIC int create_l2cap_channel(uint16_t mtu, mp_bluetooth_nimble_l2cap_channel_ // multiply that by the "MTUs per channel" (set to 3 above). const size_t buf_blocks = MP_CEIL_DIVIDE(mtu, L2CAP_BUF_BLOCK_SIZE) * L2CAP_BUF_SIZE_MTUS_PER_CHANNEL; - mp_bluetooth_nimble_l2cap_channel_t *chan = m_new_obj_var(mp_bluetooth_nimble_l2cap_channel_t, uint8_t, OS_MEMPOOL_SIZE(buf_blocks, L2CAP_BUF_BLOCK_SIZE) * sizeof(os_membuf_t)); + mp_bluetooth_nimble_l2cap_channel_t *chan = m_new_obj_var(mp_bluetooth_nimble_l2cap_channel_t, sdu_mem, uint8_t, OS_MEMPOOL_SIZE(buf_blocks, L2CAP_BUF_BLOCK_SIZE) * sizeof(os_membuf_t)); MP_STATE_PORT(bluetooth_nimble_root_pointers)->l2cap_chan = chan; // Will be set in BLE_L2CAP_EVENT_COC_CONNECTED or BLE_L2CAP_EVENT_COC_ACCEPT. diff --git a/extmod/vfs_lfsx_file.c b/extmod/vfs_lfsx_file.c index 879ba78973..b9f3323397 100644 --- a/extmod/vfs_lfsx_file.c +++ b/extmod/vfs_lfsx_file.c @@ -90,9 +90,9 @@ mp_obj_t MP_VFS_LFSx(file_open)(mp_obj_t self_in, mp_obj_t path_in, mp_obj_t mod } #if LFS_BUILD_VERSION == 1 - MP_OBJ_VFS_LFSx_FILE *o = m_new_obj_var_with_finaliser(MP_OBJ_VFS_LFSx_FILE, uint8_t, self->lfs.cfg->prog_size); + MP_OBJ_VFS_LFSx_FILE *o = m_new_obj_var_with_finaliser(MP_OBJ_VFS_LFSx_FILE, file_buffer, uint8_t, self->lfs.cfg->prog_size); #else - MP_OBJ_VFS_LFSx_FILE *o = m_new_obj_var_with_finaliser(MP_OBJ_VFS_LFSx_FILE, uint8_t, self->lfs.cfg->cache_size); + MP_OBJ_VFS_LFSx_FILE *o = m_new_obj_var_with_finaliser(MP_OBJ_VFS_LFSx_FILE, file_buffer, uint8_t, self->lfs.cfg->cache_size); #endif o->base.type = type; o->vfs = self; diff --git a/ports/unix/coverage.c b/ports/unix/coverage.c index f3bb3d450f..88dd48bfab 100644 --- a/ports/unix/coverage.c +++ b/ports/unix/coverage.c @@ -545,7 +545,7 @@ STATIC mp_obj_t extra_coverage(void) { fun_bc.context = &context; fun_bc.child_table = NULL; fun_bc.bytecode = (const byte *)"\x01"; // just needed for n_state - mp_code_state_t *code_state = m_new_obj_var(mp_code_state_t, mp_obj_t, 1); + mp_code_state_t *code_state = m_new_obj_var(mp_code_state_t, state, mp_obj_t, 1); code_state->fun_bc = &fun_bc; code_state->ip = (const byte *)"\x00"; // just needed for an invalid opcode code_state->sp = &code_state->state[0]; diff --git a/py/misc.h b/py/misc.h index 32061a009e..352a9f34c4 100644 --- a/py/misc.h +++ b/py/misc.h @@ -71,26 +71,26 @@ typedef unsigned int uint; #define m_new0(type, num) ((type *)(m_malloc0(sizeof(type) * (num)))) #define m_new_obj(type) (m_new(type, 1)) #define m_new_obj_maybe(type) (m_new_maybe(type, 1)) -#define m_new_obj_var(obj_type, var_type, var_num) ((obj_type *)m_malloc(sizeof(obj_type) + sizeof(var_type) * (var_num))) -#define m_new_obj_var0(obj_type, var_type, var_num) ((obj_type *)m_malloc0(sizeof(obj_type) + sizeof(var_type) * (var_num))) -#define m_new_obj_var_maybe(obj_type, var_type, var_num) ((obj_type *)m_malloc_maybe(sizeof(obj_type) + sizeof(var_type) * (var_num))) +#define m_new_obj_var(obj_type, var_field, var_type, var_num) ((obj_type *)m_malloc(offsetof(obj_type, var_field) + sizeof(var_type) * (var_num))) +#define m_new_obj_var0(obj_type, var_field, var_type, var_num) ((obj_type *)m_malloc0(offsetof(obj_type, var_field) + sizeof(var_type) * (var_num))) +#define m_new_obj_var_maybe(obj_type, var_field, var_type, var_num) ((obj_type *)m_malloc_maybe(offsetof(obj_type, var_field) + sizeof(var_type) * (var_num))) #if MICROPY_ENABLE_FINALISER #define m_new_obj_with_finaliser(type) ((type *)(m_malloc_with_finaliser(sizeof(type)))) -#define m_new_obj_var_with_finaliser(type, var_type, var_num) ((type *)m_malloc_with_finaliser(sizeof(type) + sizeof(var_type) * (var_num))) +#define m_new_obj_var_with_finaliser(type, var_field, var_type, var_num) ((type *)m_malloc_with_finaliser(offsetof(type, var_field) + sizeof(var_type) * (var_num))) #else #define m_new_obj_with_finaliser(type) m_new_obj(type) -#define m_new_obj_var_with_finaliser(type, var_type, var_num) m_new_obj_var(type, var_type, var_num) +#define m_new_obj_var_with_finaliser(type, var_field, var_type, var_num) m_new_obj_var(type, var_field, var_type, var_num) #endif #if MICROPY_MALLOC_USES_ALLOCATED_SIZE #define m_renew(type, ptr, old_num, new_num) ((type *)(m_realloc((ptr), sizeof(type) * (old_num), sizeof(type) * (new_num)))) #define m_renew_maybe(type, ptr, old_num, new_num, allow_move) ((type *)(m_realloc_maybe((ptr), sizeof(type) * (old_num), sizeof(type) * (new_num), (allow_move)))) #define m_del(type, ptr, num) m_free(ptr, sizeof(type) * (num)) -#define m_del_var(obj_type, var_type, var_num, ptr) (m_free(ptr, sizeof(obj_type) + sizeof(var_type) * (var_num))) +#define m_del_var(obj_type, var_field, var_type, var_num, ptr) (m_free(ptr, offsetof(obj_type, var_field) + sizeof(var_type) * (var_num))) #else #define m_renew(type, ptr, old_num, new_num) ((type *)(m_realloc((ptr), sizeof(type) * (new_num)))) #define m_renew_maybe(type, ptr, old_num, new_num, allow_move) ((type *)(m_realloc_maybe((ptr), sizeof(type) * (new_num), (allow_move)))) #define m_del(type, ptr, num) ((void)(num), m_free(ptr)) -#define m_del_var(obj_type, var_type, var_num, ptr) ((void)(var_num), m_free(ptr)) +#define m_del_var(obj_type, var_field, var_type, var_num, ptr) ((void)(var_num), m_free(ptr)) #endif #define m_del_obj(type, ptr) (m_del(type, ptr, 1)) diff --git a/py/modthread.c b/py/modthread.c index e4dcccd259..3a8a1e03ce 100644 --- a/py/modthread.c +++ b/py/modthread.c @@ -235,7 +235,7 @@ STATIC mp_obj_t mod_thread_start_new_thread(size_t n_args, const mp_obj_t *args) // check for keyword arguments if (n_args == 2) { // just position arguments - th_args = m_new_obj_var(thread_entry_args_t, mp_obj_t, pos_args_len); + th_args = m_new_obj_var(thread_entry_args_t, args, mp_obj_t, pos_args_len); th_args->n_kw = 0; } else { // positional and keyword arguments @@ -243,7 +243,7 @@ STATIC mp_obj_t mod_thread_start_new_thread(size_t n_args, const mp_obj_t *args) mp_raise_TypeError(MP_ERROR_TEXT("expecting a dict for keyword args")); } mp_map_t *map = &((mp_obj_dict_t *)MP_OBJ_TO_PTR(args[2]))->map; - th_args = m_new_obj_var(thread_entry_args_t, mp_obj_t, pos_args_len + 2 * map->used); + th_args = m_new_obj_var(thread_entry_args_t, args, mp_obj_t, pos_args_len + 2 * map->used); th_args->n_kw = map->used; // copy across the keyword arguments for (size_t i = 0, n = pos_args_len; i < map->alloc; ++i) { diff --git a/py/objexcept.c b/py/objexcept.c index a90405c525..a54b81370d 100644 --- a/py/objexcept.c +++ b/py/objexcept.c @@ -221,7 +221,7 @@ mp_obj_t mp_obj_exception_make_new(const mp_obj_type_t *type, size_t n_args, siz o_tuple = (mp_obj_tuple_t *)&mp_const_empty_tuple_obj; } else { // Try to allocate memory for the tuple containing the args - o_tuple = m_new_obj_var_maybe(mp_obj_tuple_t, mp_obj_t, n_args); + o_tuple = m_new_obj_var_maybe(mp_obj_tuple_t, items, mp_obj_t, n_args); #if MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF // If we are called by mp_obj_new_exception_msg_varg then it will have diff --git a/py/objfun.c b/py/objfun.c index 94b33cd5bd..e2136968b6 100644 --- a/py/objfun.c +++ b/py/objfun.c @@ -215,7 +215,7 @@ mp_code_state_t *mp_obj_fun_bc_prepare_codestate(mp_obj_t self_in, size_t n_args // RuntimeError should be raised instead. So, we use m_new_obj_var_maybe(), // return NULL, then vm.c takes the needed action (either raise // RuntimeError or fallback to stack allocation). - code_state = m_new_obj_var_maybe(mp_code_state_t, byte, state_size); + code_state = m_new_obj_var_maybe(mp_code_state_t, state, byte, state_size); if (!code_state) { return NULL; } @@ -247,10 +247,10 @@ STATIC mp_obj_t fun_bc_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const // allocate state for locals and stack mp_code_state_t *code_state = NULL; #if MICROPY_ENABLE_PYSTACK - code_state = mp_pystack_alloc(sizeof(mp_code_state_t) + state_size); + code_state = mp_pystack_alloc(offsetof(mp_code_state_t, state) + state_size); #else if (state_size > VM_MAX_STATE_ON_STACK) { - code_state = m_new_obj_var_maybe(mp_code_state_t, byte, state_size); + code_state = m_new_obj_var_maybe(mp_code_state_t, state, byte, state_size); #if MICROPY_DEBUG_VM_STACK_OVERFLOW if (code_state != NULL) { memset(code_state->state, 0, state_size); @@ -258,7 +258,7 @@ STATIC mp_obj_t fun_bc_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const #endif } if (code_state == NULL) { - code_state = alloca(sizeof(mp_code_state_t) + state_size); + code_state = alloca(offsetof(mp_code_state_t, state) + state_size); #if MICROPY_DEBUG_VM_STACK_OVERFLOW memset(code_state->state, 0, state_size); #endif @@ -320,7 +320,7 @@ STATIC mp_obj_t fun_bc_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const #else // free the state if it was allocated on the heap if (state_size != 0) { - m_del_var(mp_code_state_t, byte, state_size, code_state); + m_del_var(mp_code_state_t, state, byte, state_size, code_state); } #endif diff --git a/py/objnamedtuple.c b/py/objnamedtuple.c index 75e21494b4..e586f08945 100644 --- a/py/objnamedtuple.c +++ b/py/objnamedtuple.c @@ -143,7 +143,7 @@ STATIC mp_obj_t namedtuple_make_new(const mp_obj_type_t *type_in, size_t n_args, } mp_obj_namedtuple_type_t *mp_obj_new_namedtuple_base(size_t n_fields, mp_obj_t *fields) { - mp_obj_namedtuple_type_t *o = m_new_obj_var0(mp_obj_namedtuple_type_t, qstr, n_fields); + mp_obj_namedtuple_type_t *o = m_new_obj_var0(mp_obj_namedtuple_type_t, fields, qstr, n_fields); o->n_fields = n_fields; for (size_t i = 0; i < n_fields; i++) { o->fields[i] = mp_obj_str_get_qstr(fields[i]); diff --git a/py/objtuple.c b/py/objtuple.c index 0318a35db6..969c488ee2 100644 --- a/py/objtuple.c +++ b/py/objtuple.c @@ -264,7 +264,7 @@ void mp_obj_tuple_get(mp_obj_t self_in, size_t *len, mp_obj_t **items) { void mp_obj_tuple_del(mp_obj_t self_in) { assert(mp_obj_is_type(self_in, &mp_type_tuple)); mp_obj_tuple_t *self = MP_OBJ_TO_PTR(self_in); - m_del_var(mp_obj_tuple_t, mp_obj_t, self->len, self); + m_del_var(mp_obj_tuple_t, items, mp_obj_t, self->len, self); } /******************************************************************************/ diff --git a/py/objtype.c b/py/objtype.c index 772f25d744..d21c2a4b5b 100644 --- a/py/objtype.c +++ b/py/objtype.c @@ -1155,7 +1155,7 @@ mp_obj_t mp_obj_new_type(qstr name, mp_obj_t bases_tuple, mp_obj_t locals_dict) // (currently 10, plus 1 for base, plus 1 for base-protocol). // Note: mp_obj_type_t is (2 + 3 + #slots) words, so going from 11 to 12 slots // moves from 4 to 5 gc blocks. - mp_obj_type_t *o = m_new_obj_var0(mp_obj_type_t, void *, 10 + (bases_len ? 1 : 0) + (base_protocol ? 1 : 0)); + mp_obj_type_t *o = m_new_obj_var0(mp_obj_type_t, slots, void *, 10 + (bases_len ? 1 : 0) + (base_protocol ? 1 : 0)); o->base.type = &mp_type_type; o->flags = base_flags; o->name = name; From e5014a4d7953808355c1882a65d6d7824cdd0001 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Mon, 16 Oct 2023 17:20:40 +0200 Subject: [PATCH 03/37] stm32: Add configuration options for analog switches. Signed-off-by: iabdalkader --- ports/stm32/boards/ARDUINO_GIGA/mpconfigboard.h | 6 ++++++ .../stm32/boards/ARDUINO_NICLA_VISION/board_init.c | 5 ----- .../boards/ARDUINO_NICLA_VISION/mpconfigboard.h | 7 +++++++ .../boards/ARDUINO_PORTENTA_H7/mpconfigboard.h | 6 ++++++ ports/stm32/system_stm32.c | 14 ++++++++++++++ 5 files changed, 33 insertions(+), 5 deletions(-) diff --git a/ports/stm32/boards/ARDUINO_GIGA/mpconfigboard.h b/ports/stm32/boards/ARDUINO_GIGA/mpconfigboard.h index a9b8cbfc7b..ec9bbed5ea 100644 --- a/ports/stm32/boards/ARDUINO_GIGA/mpconfigboard.h +++ b/ports/stm32/boards/ARDUINO_GIGA/mpconfigboard.h @@ -104,6 +104,12 @@ void GIGA_board_low_power(int mode); // SMPS configuration #define MICROPY_HW_PWR_SMPS_CONFIG (PWR_LDO_SUPPLY) +// Configure the analog switches for dual-pad pins. +#define MICROPY_HW_ANALOG_SWITCH_PA0 (SYSCFG_SWITCH_PA0_OPEN) +#define MICROPY_HW_ANALOG_SWITCH_PA1 (SYSCFG_SWITCH_PA1_OPEN) +#define MICROPY_HW_ANALOG_SWITCH_PC2 (SYSCFG_SWITCH_PC2_OPEN) +#define MICROPY_HW_ANALOG_SWITCH_PC3 (SYSCFG_SWITCH_PC3_OPEN) + // There is an external 32kHz oscillator #define RTC_ASYNCH_PREDIV (0) #define RTC_SYNCH_PREDIV (0x7fff) diff --git a/ports/stm32/boards/ARDUINO_NICLA_VISION/board_init.c b/ports/stm32/boards/ARDUINO_NICLA_VISION/board_init.c index ed8c609275..ec78b09de3 100644 --- a/ports/stm32/boards/ARDUINO_NICLA_VISION/board_init.c +++ b/ports/stm32/boards/ARDUINO_NICLA_VISION/board_init.c @@ -65,11 +65,6 @@ void NICLAV_board_early_init(void) { } #endif - // Make sure PC2 and PC3 and PC2_C and PC3_C pads are connected - // through the analog switch for ULPI NXT and DIR pins. - HAL_SYSCFG_AnalogSwitchConfig(SYSCFG_SWITCH_PC2, SYSCFG_SWITCH_PC2_CLOSE); - HAL_SYSCFG_AnalogSwitchConfig(SYSCFG_SWITCH_PC3, SYSCFG_SWITCH_PC3_CLOSE); - #if MICROPY_HW_USB_HS_ULPI3320 // Make sure UPLI is Not in low-power mode. ulpi_leave_low_power(); diff --git a/ports/stm32/boards/ARDUINO_NICLA_VISION/mpconfigboard.h b/ports/stm32/boards/ARDUINO_NICLA_VISION/mpconfigboard.h index 7cb3d85d3d..3cae9b25fa 100644 --- a/ports/stm32/boards/ARDUINO_NICLA_VISION/mpconfigboard.h +++ b/ports/stm32/boards/ARDUINO_NICLA_VISION/mpconfigboard.h @@ -107,6 +107,13 @@ void NICLAV_board_osc_enable(int enable); // SMPS configuration #define MICROPY_HW_PWR_SMPS_CONFIG (PWR_LDO_SUPPLY) +// Configure the analog switches for dual-pad pins. +#define MICROPY_HW_ANALOG_SWITCH_PA0 (SYSCFG_SWITCH_PA0_OPEN) +#define MICROPY_HW_ANALOG_SWITCH_PA1 (SYSCFG_SWITCH_PA1_OPEN) +// PC2_C and PC3_C, which are connected to ULPI NXT and DIR pins. +#define MICROPY_HW_ANALOG_SWITCH_PC2 (SYSCFG_SWITCH_PC2_CLOSE) +#define MICROPY_HW_ANALOG_SWITCH_PC3 (SYSCFG_SWITCH_PC3_CLOSE) + // There is an external 32kHz oscillator #define RTC_ASYNCH_PREDIV (0) #define RTC_SYNCH_PREDIV (0x7fff) diff --git a/ports/stm32/boards/ARDUINO_PORTENTA_H7/mpconfigboard.h b/ports/stm32/boards/ARDUINO_PORTENTA_H7/mpconfigboard.h index 349ca9e001..133158c60a 100644 --- a/ports/stm32/boards/ARDUINO_PORTENTA_H7/mpconfigboard.h +++ b/ports/stm32/boards/ARDUINO_PORTENTA_H7/mpconfigboard.h @@ -109,6 +109,12 @@ void PORTENTA_board_osc_enable(int enable); // SMPS configuration #define MICROPY_HW_PWR_SMPS_CONFIG (PWR_SMPS_1V8_SUPPLIES_LDO) +// Configure the analog switches for dual-pad pins. +#define MICROPY_HW_ANALOG_SWITCH_PA0 (SYSCFG_SWITCH_PA0_OPEN) +#define MICROPY_HW_ANALOG_SWITCH_PA1 (SYSCFG_SWITCH_PA1_OPEN) +#define MICROPY_HW_ANALOG_SWITCH_PC2 (SYSCFG_SWITCH_PC2_OPEN) +#define MICROPY_HW_ANALOG_SWITCH_PC3 (SYSCFG_SWITCH_PC3_OPEN) + // There is an external 32kHz oscillator #define RTC_ASYNCH_PREDIV (0) #define RTC_SYNCH_PREDIV (0x7fff) diff --git a/ports/stm32/system_stm32.c b/ports/stm32/system_stm32.c index de1897b4a6..2cff36c3a6 100644 --- a/ports/stm32/system_stm32.c +++ b/ports/stm32/system_stm32.c @@ -587,6 +587,20 @@ MP_WEAK void SystemClock_Config(void) { // Enable the Debug Module in low-power modes. DBGMCU->CR |= (DBGMCU_CR_DBG_SLEEPD1 | DBGMCU_CR_DBG_STOPD1 | DBGMCU_CR_DBG_STANDBYD1); #endif + + // Configure the analog switches + #ifdef MICROPY_HW_ANALOG_SWITCH_PA0 + HAL_SYSCFG_AnalogSwitchConfig(SYSCFG_SWITCH_PA0, MICROPY_HW_ANALOG_SWITCH_PA0); + #endif + #ifdef MICROPY_HW_ANALOG_SWITCH_PA1 + HAL_SYSCFG_AnalogSwitchConfig(SYSCFG_SWITCH_PA1, MICROPY_HW_ANALOG_SWITCH_PA1); + #endif + #ifdef MICROPY_HW_ANALOG_SWITCH_PC2 + HAL_SYSCFG_AnalogSwitchConfig(SYSCFG_SWITCH_PC2, MICROPY_HW_ANALOG_SWITCH_PC2); + #endif + #ifdef MICROPY_HW_ANALOG_SWITCH_PC3 + HAL_SYSCFG_AnalogSwitchConfig(SYSCFG_SWITCH_PC3, MICROPY_HW_ANALOG_SWITCH_PC3); + #endif } #endif From 47ea831c0e741722a9e7a94bb453947cff5f66d8 Mon Sep 17 00:00:00 2001 From: Rene Straub Date: Sat, 21 Oct 2023 15:03:53 +0200 Subject: [PATCH 04/37] stm32: Add STM32H5 support for sleep mode. Update rtc, machine and powerctrl drivers to support STM32H5 sleep modes. This makes RTC alarm wakeup working from lightsleep() and deepsleep(). Changes: - Determine start reason for machine.reset_cause() in modmachine.c. - Add proper interrupt clear code in rtc.c. - Add wakeup functionality in powerctrl_enter_stop_mode(). Remember and restore voltage scaling level. Restart HSI48 if it was on before entering sleep mode. - Clear DBGMCU_CR in SystemClock_Config() as for other variants. Otherwise debug flags prevent entering sleep mode. Implementation Notes: - rtc.c: EXTI_RTSTR1 bits are not present for H5. Code sequence from G0/G4/L4/WB/WL would be invalid. RTSTR is only defined for external (GPIO) interrupts. Maybe this is also true for other STM32 variants. - powerctrl_enter_stop_mode() uses complicated, nested conditionals to select STM32 variants. To make code slightly better readable, comment have been added. A non-nested, #if/#elif sequence would make the code more readable. I leave this to the original authors. Signed-off-by: Rene Straub --- ports/stm32/modmachine.c | 6 ++++++ ports/stm32/powerctrl.c | 27 +++++++++++++++++++++++---- ports/stm32/powerctrlboot.c | 4 ++++ ports/stm32/rtc.c | 8 ++++++-- 4 files changed, 39 insertions(+), 6 deletions(-) diff --git a/ports/stm32/modmachine.c b/ports/stm32/modmachine.c index 83327bede0..67fcffb0f1 100644 --- a/ports/stm32/modmachine.c +++ b/ports/stm32/modmachine.c @@ -108,6 +108,12 @@ void machine_init(void) { reset_cause = PYB_RESET_DEEPSLEEP; PWR->CR1 |= PWR_CR1_CSBF; } else + #elif defined(STM32H5) + if (PWR->PMSR & PWR_PMSR_STOPF || PWR->PMSR & PWR_PMSR_SBF) { + // came out of standby or stop mode + reset_cause = PYB_RESET_DEEPSLEEP; + PWR->PMCR |= PWR_PMCR_CSSF; + } else #elif defined(STM32H7) if (PWR->CPUCR & PWR_CPUCR_SBF || PWR->CPUCR & PWR_CPUCR_STOPF) { // came out of standby or stop mode diff --git a/ports/stm32/powerctrl.c b/ports/stm32/powerctrl.c index 03224f4d8b..1bb66c9781 100644 --- a/ports/stm32/powerctrl.c +++ b/ports/stm32/powerctrl.c @@ -48,6 +48,8 @@ #define POWERCTRL_GET_VOLTAGE_SCALING() PWR_REGULATOR_VOLTAGE_SCALE0 #elif defined(STM32H723xx) #define POWERCTRL_GET_VOLTAGE_SCALING() LL_PWR_GetRegulVoltageScaling() +#elif defined(STM32H5) +#define POWERCTRL_GET_VOLTAGE_SCALING() LL_PWR_GetRegulVoltageScaling() #else #define POWERCTRL_GET_VOLTAGE_SCALING() \ (((PWR->CSR1 & PWR_CSR1_ACTVOS) && (SYSCFG->PWRCR & SYSCFG_PWRCR_ODEN)) ? \ @@ -797,6 +799,14 @@ void powerctrl_enter_stop_mode(void) { HAL_PWREx_EnableFlashPowerDown(); #endif + #if defined(STM32H5) + // Save RCC CR to re-enable OSCs and PLLs after wake up from low power mode. + uint32_t rcc_cr = RCC->CR; + + // Save the current voltage scaling level to restore after exiting low power mode. + uint32_t vscaling = POWERCTRL_GET_VOLTAGE_SCALING(); + #endif + #if defined(STM32H7) // Save RCC CR to re-enable OSCs and PLLs after wake up from low power mode. uint32_t rcc_cr = RCC->CR; @@ -837,9 +847,9 @@ void powerctrl_enter_stop_mode(void) { while (__HAL_RCC_GET_SYSCLK_SOURCE() != RCC_CFGR_SWS_HSI48) { } - #else + #else // defined(STM32F0) - #if defined(STM32H7) + #if defined(STM32H5) || defined(STM32H7) // When exiting from Stop or Standby modes, the Run mode voltage scaling is reset to // the default VOS3 value. Restore the voltage scaling to the previous voltage scale. if (vscaling != POWERCTRL_GET_VOLTAGE_SCALING()) { @@ -879,7 +889,7 @@ void powerctrl_enter_stop_mode(void) { while (LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_PLL1) { } - #else + #else // defined(STM32H5) // enable PLL __HAL_RCC_PLL_ENABLE(); @@ -899,7 +909,7 @@ void powerctrl_enter_stop_mode(void) { } #endif - #endif + #endif // defined(STM32H5) powerctrl_disable_hsi_if_unused(); @@ -912,6 +922,15 @@ void powerctrl_enter_stop_mode(void) { } #endif + #if defined(STM32H5) + if (rcc_cr & RCC_CR_HSI48ON) { + // Enable HSI48. + LL_RCC_HSI48_Enable(); + while (!LL_RCC_HSI48_IsReady()) { + } + } + #endif + #if defined(STM32H7) // Enable HSI if (rcc_cr & RCC_CR_HSION) { diff --git a/ports/stm32/powerctrlboot.c b/ports/stm32/powerctrlboot.c index c7802a5588..31dae527c1 100644 --- a/ports/stm32/powerctrlboot.c +++ b/ports/stm32/powerctrlboot.c @@ -298,6 +298,10 @@ void SystemClock_Config(void) { LL_RCC_SetUSBClockSource(LL_RCC_USB_CLKSOURCE_PLL3Q); #endif + + #ifdef NDEBUG + DBGMCU->CR = 0; + #endif } #elif defined(STM32L0) diff --git a/ports/stm32/rtc.c b/ports/stm32/rtc.c index 3b05fb40eb..b209ea8354 100644 --- a/ports/stm32/rtc.c +++ b/ports/stm32/rtc.c @@ -756,9 +756,11 @@ mp_obj_t pyb_rtc_wakeup(size_t n_args, const mp_obj_t *args) { RTC->WPR = 0xff; // enable external interrupts on line EXTI_RTC_WAKEUP - #if defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32L4) || defined(STM32WB) || defined(STM32WL) + #if defined(STM32G0) || defined(STM32G4) || defined(STM32L4) || defined(STM32WB) || defined(STM32WL) EXTI->IMR1 |= 1 << EXTI_RTC_WAKEUP; EXTI->RTSR1 |= 1 << EXTI_RTC_WAKEUP; + #elif defined(STM32H5) + EXTI->IMR1 |= 1 << EXTI_RTC_WAKEUP; #elif defined(STM32H7) EXTI_D1->IMR1 |= 1 << EXTI_RTC_WAKEUP; EXTI->RTSR1 |= 1 << EXTI_RTC_WAKEUP; @@ -768,8 +770,10 @@ mp_obj_t pyb_rtc_wakeup(size_t n_args, const mp_obj_t *args) { #endif // clear interrupt flags - #if defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32WL) + #if defined(STM32G0) || defined(STM32G4) || defined(STM32WL) RTC->ICSR &= ~RTC_ICSR_WUTWF; + #elif defined(STM32H5) + RTC->SCR = RTC_SCR_CWUTF; #elif defined(STM32H7A3xx) || defined(STM32H7A3xxQ) || defined(STM32H7B3xx) || defined(STM32H7B3xxQ) RTC->SR &= ~RTC_SR_WUTF; #else From d95f5aa01131f3aa669867ebdde391f9db277846 Mon Sep 17 00:00:00 2001 From: Maarten van der Schrieck Date: Tue, 24 Oct 2023 10:16:00 +0200 Subject: [PATCH 05/37] rp2/machine_uart: Fix handling of serial break condition. The FIFO reports not only the bytes read, but also 4 error bits. These were not checked, leading to NUL value read in case of break and possible garbage bytes being written on parity/framing error. This patch addresses the issue that NUL bytes are incorrectly read on break, and at least provides the boilerplate code and comments for error handling, that may be implemented in the future. Signed-off-by: Maarten van der Schrieck --- ports/rp2/machine_uart.c | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/ports/rp2/machine_uart.c b/ports/rp2/machine_uart.c index 044c905ff2..dae57012a8 100644 --- a/ports/rp2/machine_uart.c +++ b/ports/rp2/machine_uart.c @@ -140,8 +140,27 @@ static inline void read_mutex_unlock(machine_uart_obj_t *u) { STATIC void uart_drain_rx_fifo(machine_uart_obj_t *self) { if (read_mutex_try_lock(self)) { while (uart_is_readable(self->uart) && ringbuf_free(&self->read_buffer) > 0) { - // get a byte from uart and put into the buffer - ringbuf_put(&(self->read_buffer), uart_get_hw(self->uart)->dr); + // Get a byte from uart and put into the buffer. Every entry from + // the FIFO is accompanied by 4 error bits, that may be used for + // error handling. + uint16_t c = uart_get_hw(self->uart)->dr; + if (c & UART_UARTDR_OE_BITS) { + // Overrun Error: We missed at least one byte. Not much we can do here. + } + if (c & UART_UARTDR_BE_BITS) { + // Break Error: RX was held low for longer than one character + // (11 bits). We did *not* read the zero byte that we seemed to + // read from dr. + continue; + } + if (c & UART_UARTDR_PE_BITS) { + // Parity Error: The byte we read is invalid. + } + if (c & UART_UARTDR_FE_BITS) { + // Framing Error: We did not receive a valid stop bit. + } + + ringbuf_put(&(self->read_buffer), c); } read_mutex_unlock(self); } From b41055a5a3e1c51ee7ea274da4744dd47c211b3a Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Wed, 25 Oct 2023 09:05:54 +0200 Subject: [PATCH 06/37] rp2/machine_adc: Refactor channel/pin validation code. This patch ensures that integer channel numbers passed to the ADC constructor (including temperature sensor) are interpreted as raw channel numbers, and not cause any GPIO pins to be initialized. Signed-off-by: iabdalkader --- ports/rp2/machine_adc.c | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/ports/rp2/machine_adc.c b/ports/rp2/machine_adc.c index 80c4d0533e..3a7a71a741 100644 --- a/ports/rp2/machine_adc.c +++ b/ports/rp2/machine_adc.c @@ -69,21 +69,21 @@ STATIC mp_obj_t mp_machine_adc_make_new(const mp_obj_type_t *type, size_t n_args mp_obj_t source = all_args[0]; - uint32_t channel; + uint32_t channel = -1; bool is_ext = false; const machine_pin_obj_t *pin = NULL; if (mp_obj_is_int(source)) { // Get and validate channel number. channel = mp_obj_get_int(source); - if (!((channel >= 0 && channel <= ADC_CHANNEL_TEMPSENSOR) || ADC_IS_VALID_GPIO(channel))) { + if (ADC_IS_VALID_GPIO(channel)) { + channel = ADC_CHANNEL_FROM_GPIO(channel); + } else if (!(channel >= 0 && channel <= ADC_CHANNEL_TEMPSENSOR)) { mp_raise_ValueError(MP_ERROR_TEXT("invalid channel")); } - } else { // Get GPIO and check it has ADC capabilities. pin = machine_pin_find(source); - channel = pin->id; bool valid_adc_pin = false; #if MICROPY_HW_ADC_EXT_COUNT is_ext = pin->is_ext; @@ -92,7 +92,7 @@ STATIC mp_obj_t mp_machine_adc_make_new(const mp_obj_type_t *type, size_t n_args } else #endif { - valid_adc_pin = ADC_IS_VALID_GPIO(channel); + valid_adc_pin = ADC_IS_VALID_GPIO(pin->id); } if (!valid_adc_pin) { mp_raise_ValueError(MP_ERROR_TEXT("Pin doesn't have ADC capabilities")); @@ -104,16 +104,18 @@ STATIC mp_obj_t mp_machine_adc_make_new(const mp_obj_type_t *type, size_t n_args adc_init(); } - if (is_ext) { - #if MICROPY_HW_ADC_EXT_COUNT - // Note external pins are mutable. - machine_pin_ext_config((machine_pin_obj_t *)pin, MACHINE_PIN_MODE_ANALOG, 0); - channel = machine_pin_ext_to_adc_channel(pin); - #endif - } else if (ADC_IS_VALID_GPIO(channel)) { - // Configure the GPIO pin in ADC mode. - adc_gpio_init(channel); - channel = ADC_CHANNEL_FROM_GPIO(channel); + if (pin) { + if (is_ext) { + #if MICROPY_HW_ADC_EXT_COUNT + // Note external pins are mutable. + machine_pin_ext_config((machine_pin_obj_t *)pin, MACHINE_PIN_MODE_ANALOG, 0); + channel = machine_pin_ext_to_adc_channel(pin); + #endif + } else { + // Configure the GPIO pin in ADC mode. + adc_gpio_init(pin->id); + channel = ADC_CHANNEL_FROM_GPIO(pin->id); + } } else if (channel == ADC_CHANNEL_TEMPSENSOR) { // Enable temperature sensor. adc_set_temp_sensor_enabled(1); From 8c432ea2d437255eefb4aff18764936ad11b70c7 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Tue, 31 Oct 2023 16:42:10 +1100 Subject: [PATCH 07/37] rp2: Remove 1ms timeout to make idle waiting tickless. The main motivation for doing this was to reduce the latency when the system is woken by a USB interrupt. The best_effort_wfe_or_timeout() function calls into the pico-sdk dynamic timer framework which sets up a new dynamic timer instance each time, and then has to tear it down before continuing after a WFE. Testing Python interrupt latency, it seems to be improved by about 12us (from average of 46us to 34us running a Pin IRQ). C-based "scheduled nodes" should see even lower latency. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- ports/rp2/modmachine.c | 2 +- ports/rp2/mpconfigport.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ports/rp2/modmachine.c b/ports/rp2/modmachine.c index 29a8dbed2c..4c428cbddf 100644 --- a/ports/rp2/modmachine.c +++ b/ports/rp2/modmachine.c @@ -111,7 +111,7 @@ STATIC mp_obj_t machine_freq(size_t n_args, const mp_obj_t *args) { STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_freq_obj, 0, 1, machine_freq); STATIC mp_obj_t machine_idle(void) { - best_effort_wfe_or_timeout(make_timeout_time_ms(1)); + __wfe(); return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_0(machine_idle_obj, machine_idle); diff --git a/ports/rp2/mpconfigport.h b/ports/rp2/mpconfigport.h index 0a63312734..84a961db43 100644 --- a/ports/rp2/mpconfigport.h +++ b/ports/rp2/mpconfigport.h @@ -274,7 +274,7 @@ extern void mp_thread_end_atomic_section(uint32_t); #define MICROPY_EVENT_POLL_HOOK \ do { \ MICROPY_EVENT_POLL_HOOK_FAST; \ - best_effort_wfe_or_timeout(make_timeout_time_ms(1)); \ + __wfe(); \ } while (0); #define MICROPY_MAKE_POINTER_CALLABLE(p) ((void *)((mp_uint_t)(p) | 1)) From 841422817e069087df9d10d37bd8896a51f2f356 Mon Sep 17 00:00:00 2001 From: Jim Mussared Date: Fri, 3 Nov 2023 18:43:58 +1100 Subject: [PATCH 08/37] stm32/boards/make-pins.py: Fix H7 ADC generation. Fixes are: - Only emit ADC table entries for pins that aren't cpu-hidden (i.e. ignore `X,-Y` rows). - Only use the P channels on H7. Signed-off-by: Jim Mussared --- ports/stm32/boards/make-pins.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/ports/stm32/boards/make-pins.py b/ports/stm32/boards/make-pins.py index 31e7bdb0e2..9f9950ff17 100755 --- a/ports/stm32/boards/make-pins.py +++ b/ports/stm32/boards/make-pins.py @@ -135,7 +135,11 @@ class Stm32Pin(boardgen.Pin): "Invalid adc '{:s}' for pin '{:s}'".format(adc_name, self.name()) ) adc_units = [int(x) for x in m.group(1)] - _adc_mode = m.group(2) + adc_mode = m.group(2) + if adc_mode == "INN": + # On H7 we have INN/INP, all other parts use IN only. Only use + # IN or INP channels. + continue adc_channel = int(m.group(3)) # Pick the entry with the most ADC units, e.g. "ADC1_INP16/ADC12_INN1/ADC12_INP0" --> "ADC12_INN1". @@ -247,6 +251,8 @@ class Stm32PinGenerator(boardgen.PinGenerator): ) # Don't include pins that weren't in pins.csv. for pin in self.available_pins(): + if pin._hidden: + continue if adc_unit in pin._adc_units: print( " [{:d}] = {:s},".format(pin._adc_channel, self._cpu_pin_pointer(pin)), From c028f956fc0fc93f1ca00950203693d4c59254e4 Mon Sep 17 00:00:00 2001 From: Jim Mussared Date: Sat, 4 Nov 2023 09:00:33 +1100 Subject: [PATCH 09/37] stm32/boards/stm32f4x9_af.csv: Fix DCMI_VSYNC. This incorrectly had a `(1)` on the end. Signed-off-by: Jim Mussared --- ports/stm32/boards/stm32f429_af.csv | 340 ++++++++++++++-------------- ports/stm32/boards/stm32f439_af.csv | 340 ++++++++++++++-------------- 2 files changed, 340 insertions(+), 340 deletions(-) diff --git a/ports/stm32/boards/stm32f429_af.csv b/ports/stm32/boards/stm32f429_af.csv index e9cf2880b4..afa3250e11 100644 --- a/ports/stm32/boards/stm32f429_af.csv +++ b/ports/stm32/boards/stm32f429_af.csv @@ -1,170 +1,170 @@ -Port ,Pin ,AF0 ,AF1 ,AF2 ,AF3 ,AF4 ,AF5 ,AF6 ,AF7 ,AF8 ,AF9 ,AF10 ,AF11 ,AF12 ,AF13 ,AF14 ,AF15 ,ADC - , ,SYS ,TIM1/2 ,TIM3/4/5,TIM8/9/10/11,I2C1/2/3 ,SPI1/2/3/4/5/6 ,SPI2/3/SAI1 ,SPI3/USART1/2/3,USART6/UART4/5/7/8,CAN1/2/TIM12/13/14/LCD,OTG2_HS/OTG1_FS,ETH ,FMC/SDIO/OTG2_FS ,DCMI ,LCD ,SYS , -PortA,PA0 , ,TIM2_CH1/TIM2_ETR,TIM5_CH1,TIM8_ETR , , , ,USART2_CTS ,UART4_TX , , ,ETH_MII_CRS , , , ,EVENTOUT, -PortA,PA1 , ,TIM2_CH2 ,TIM5_CH2, , , , ,USART2_RTS ,UART4_RX , , ,ETH_MII_RX_CLK/ETH_RMII_REF_CLK, , , ,EVENTOUT,ADC123_IN1 -PortA,PA2 , ,TIM2_CH3 ,TIM5_CH3,TIM9_CH1 , , , ,USART2_TX , , , ,ETH_MDIO , , , ,EVENTOUT,ADC123_IN2 -PortA,PA3 , ,TIM2_CH4 ,TIM5_CH4,TIM9_CH2 , , , ,USART2_RX , , ,OTG_HS_ULPI_D0 ,ETH_MII_COL , , ,LCD_B5 ,EVENTOUT,ADC123_IN3 -PortA,PA4 , , , , , ,SPI1_NSS ,SPI3_NSS/I2S3_WS ,USART2_CK , , , , ,OTG_HS_SOF ,DCMI_HSYNC ,LCD_VSYNC,EVENTOUT,ADC12_IN4 -PortA,PA5 , ,TIM2_CH1/TIM2_ETR, ,TIM8_CH1N , ,SPI1_SCK , , , , ,OTG_HS_ULPI_CK , , , , ,EVENTOUT,ADC12_IN5 -PortA,PA6 , ,TIM1_BKIN ,TIM3_CH1,TIM8_BKIN , ,SPI1_MISO , , , ,TIM13_CH1 , , , ,DCMI_PIXCLK ,LCD_G2 ,EVENTOUT,ADC12_IN6 -PortA,PA7 , ,TIM1_CH1N ,TIM3_CH2,TIM8_CH1N , ,SPI1_MOSI , , , ,TIM14_CH1 , ,ETH_MII_RX_DV/ETH_RMII_CRS_DV , , , ,EVENTOUT,ADC12_IN7 -PortA,PA8 ,MCO1 ,TIM1_CH1 , , ,I2C3_SCL , , ,USART1_CK , , ,OTG_FS_SOF , , , ,LCD_R6 ,EVENTOUT, -PortA,PA9 , ,TIM1_CH2 , , ,I2C3_SMBA, , ,USART1_TX , , , , , ,DCMI_D0 , ,EVENTOUT, -PortA,PA10, ,TIM1_CH3 , , , , , ,USART1_RX , , ,OTG_FS_ID , , ,DCMI_D1 , ,EVENTOUT, -PortA,PA11, ,TIM1_CH4 , , , , , ,USART1_CTS , ,CAN1_RX ,OTG_FS_DM , , , ,LCD_R4 ,EVENTOUT, -PortA,PA12, ,TIM1_ETR , , , , , ,USART1_RTS , ,CAN1_TX ,OTG_FS_DP , , , ,LCD_R5 ,EVENTOUT, -PortA,PA13,JTMS/SWDIO , , , , , , , , , , , , , , ,EVENTOUT, -PortA,PA14,JTCK/SWCLK , , , , , , , , , , , , , , ,EVENTOUT, -PortA,PA15,JTDI ,TIM2_CH1/TIM2_ETR, , , ,SPI1_NSS ,SPI3_NSS/I2S3_WS , , , , , , , , ,EVENTOUT, -PortB,PB0 , ,TIM1_CH2N ,TIM3_CH3,TIM8_CH2N , , , , , ,LCD_R3 ,OTG_HS_ULPI_D1 ,ETH_MII_RXD2 , , , ,EVENTOUT,ADC12_IN8 -PortB,PB1 , ,TIM1_CH3N ,TIM3_CH4,TIM8_CH3N , , , , , ,LCD_R6 ,OTG_HS_ULPI_D2 ,ETH_MII_RXD3 , , , ,EVENTOUT,ADC12_IN9 -PortB,PB2 , , , , , , , , , , , , , , , ,EVENTOUT, -PortB,PB3 ,JTDO/TRACESWO,TIM2_CH2 , , , ,SPI1_SCK ,SPI3_SCK/I2S3_CK , , , , , , , , ,EVENTOUT, -PortB,PB4 ,NJTRST , ,TIM3_CH1, , ,SPI1_MISO ,SPI3_MISO ,I2S3ext_SD , , , , , , , ,EVENTOUT, -PortB,PB5 , , ,TIM3_CH2, ,I2C1_SMBA,SPI1_MOSI ,SPI3_MOSI/I2S3_SD, , ,CAN2_RX ,OTG_HS_ULPI_D7 ,ETH_PPS_OUT ,FMC_SDCKE1 ,DCMI_D10 , ,EVENTOUT, -PortB,PB6 , , ,TIM4_CH1, ,I2C1_SCL , , ,USART1_TX , ,CAN2_TX , , ,FMC_SDNE1 ,DCMI_D5 , ,EVENTOUT, -PortB,PB7 , , ,TIM4_CH2, ,I2C1_SDA , , ,USART1_RX , , , , ,FMC_NL ,DCMI_VSYNC , ,EVENTOUT, -PortB,PB8 , , ,TIM4_CH3,TIM10_CH1 ,I2C1_SCL , , , , ,CAN1_RX , ,ETH_MII_TXD3 ,SDIO_D4 ,DCMI_D6 ,LCD_B6 ,EVENTOUT, -PortB,PB9 , , ,TIM4_CH4,TIM11_CH1 ,I2C1_SDA ,SPI2_NSS/I2S2_WS , , , ,CAN1_TX , , ,SDIO_D5 ,DCMI_D7 ,LCD_B7 ,EVENTOUT, -PortB,PB10, ,TIM2_CH3 , , ,I2C2_SCL ,SPI2_SCK/I2S2_CK , ,USART3_TX , , ,OTG_HS_ULPI_D3 ,ETH_MII_RX_ER , , ,LCD_G4 ,EVENTOUT, -PortB,PB11, ,TIM2_CH4 , , ,I2C2_SDA , , ,USART3_RX , , ,OTG_HS_ULPI_D4 ,ETH_MII_TX_EN/ETH_RMII_TX_EN , , ,LCD_G5 ,EVENTOUT, -PortB,PB12, ,TIM1_BKIN , , ,I2C2_SMBA,SPI2_NSS/I2S2_WS , ,USART3_CK , ,CAN2_RX ,OTG_HS_ULPI_D5 ,ETH_MII_TXD0/ETH_RMII_TXD0 ,OTG_HS_ID , , ,EVENTOUT, -PortB,PB13, ,TIM1_CH1N , , , ,SPI2_SCK/I2S2_CK , ,USART3_CTS , ,CAN2_TX ,OTG_HS_ULPI_D6 ,ETH_MII_TXD1/ETH_RMII_TXD1 , , , ,EVENTOUT, -PortB,PB14, ,TIM1_CH2N , ,TIM8_CH2N , ,SPI2_MISO ,I2S2ext_SD ,USART3_RTS , ,TIM12_CH1 , , ,OTG_HS_DM , , ,EVENTOUT, -PortB,PB15,RTC_REFIN ,TIM1_CH3N , ,TIM8_CH3N , ,SPI2_MOSI/I2S2_SD, , , ,TIM12_CH2 , , ,OTG_HS_DP , , ,EVENTOUT, -PortC,PC0 , , , , , , , , , , ,OTG_HS_ULPI_STP, ,FMC_SDNWE , , ,EVENTOUT,ADC123_IN10 -PortC,PC1 , , , , , , , , , , , ,ETH_MDC , , , ,EVENTOUT,ADC123_IN11 -PortC,PC2 , , , , , ,SPI2_MISO ,I2S2ext_SD , , , ,OTG_HS_ULPI_DIR,ETH_MII_TXD2 ,FMC_SDNE0 , , ,EVENTOUT,ADC123_IN12 -PortC,PC3 , , , , , ,SPI2_MOSI/I2S2_SD, , , , ,OTG_HS_ULPI_NXT,ETH_MII_TX_CLK ,FMC_SDCKE0 , , ,EVENTOUT,ADC123_IN13 -PortC,PC4 , , , , , , , , , , , ,ETH_MII_RXD0/ETH_RMII_RXD0 , , , ,EVENTOUT,ADC12_IN14 -PortC,PC5 , , , , , , , , , , , ,ETH_MII_RXD1/ETH_RMII_RXD1 , , , ,EVENTOUT,ADC12_IN15 -PortC,PC6 , , ,TIM3_CH1,TIM8_CH1 , ,I2S2_MCK , , ,USART6_TX , , , ,SDIO_D6 ,DCMI_D0 ,LCD_HSYNC,EVENTOUT, -PortC,PC7 , , ,TIM3_CH2,TIM8_CH2 , , ,I2S3_MCK , ,USART6_RX , , , ,SDIO_D7 ,DCMI_D1 ,LCD_G6 ,EVENTOUT, -PortC,PC8 , , ,TIM3_CH3,TIM8_CH3 , , , , ,USART6_CK , , , ,SDIO_D0 ,DCMI_D2 , ,EVENTOUT, -PortC,PC9 ,MCO2 , ,TIM3_CH4,TIM8_CH4 ,I2C3_SDA ,I2S_CKIN , , , , , , ,SDIO_D1 ,DCMI_D3 , ,EVENTOUT, -PortC,PC10, , , , , , ,SPI3_SCK/I2S3_CK ,USART3_TX ,UART4_TX , , , ,SDIO_D2 ,DCMI_D8 ,LCD_R2 ,EVENTOUT, -PortC,PC11, , , , , ,I2S3ext_SD ,SPI3_MISO ,USART3_RX ,UART4_RX , , , ,SDIO_D3 ,DCMI_D4 , ,EVENTOUT, -PortC,PC12, , , , , , ,SPI3_MOSI/I2S3_SD,USART3_CK ,UART5_TX , , , ,SDIO_CK ,DCMI_D9 , ,EVENTOUT, -PortC,PC13, , , , , , , , , , , , , , , ,EVENTOUT, -PortC,PC14, , , , , , , , , , , , , , , ,EVENTOUT, -PortC,PC15, , , , , , , , , , , , , , , ,EVENTOUT, -PortD,PD0 , , , , , , , , , ,CAN1_RX , , ,FMC_D2 , , ,EVENTOUT, -PortD,PD1 , , , , , , , , , ,CAN1_TX , , ,FMC_D3 , , ,EVENTOUT, -PortD,PD2 , , ,TIM3_ETR, , , , , ,UART5_RX , , , ,SDIO_CMD ,DCMI_D11 , ,EVENTOUT, -PortD,PD3 , , , , , ,SPI2_SCK/I2S2_CK , ,USART2_CTS , , , , ,FMC_CLK ,DCMI_D5 ,LCD_G7 ,EVENTOUT, -PortD,PD4 , , , , , , , ,USART2_RTS , , , , ,FMC_NOE , , ,EVENTOUT, -PortD,PD5 , , , , , , , ,USART2_TX , , , , ,FMC_NWE , , ,EVENTOUT, -PortD,PD6 , , , , , ,SPI3_MOSI/I2S3_SD,SAI1_SD_A ,USART2_RX , , , , ,FMC_NWAIT ,DCMI_D10 ,LCD_B2 ,EVENTOUT, -PortD,PD7 , , , , , , , ,USART2_CK , , , , ,FMC_NE1/FMC_NCE2 , , ,EVENTOUT, -PortD,PD8 , , , , , , , ,USART3_TX , , , , ,FMC_D13 , , ,EVENTOUT, -PortD,PD9 , , , , , , , ,USART3_RX , , , , ,FMC_D14 , , ,EVENTOUT, -PortD,PD10, , , , , , , ,USART3_CK , , , , ,FMC_D15 , ,LCD_B3 ,EVENTOUT, -PortD,PD11, , , , , , , ,USART3_CTS , , , , ,FMC_A16 , , ,EVENTOUT, -PortD,PD12, , ,TIM4_CH1, , , , ,USART3_RTS , , , , ,FMC_A17 , , ,EVENTOUT, -PortD,PD13, , ,TIM4_CH2, , , , , , , , , ,FMC_A18 , , ,EVENTOUT, -PortD,PD14, , ,TIM4_CH3, , , , , , , , , ,FMC_D0 , , ,EVENTOUT, -PortD,PD15, , ,TIM4_CH4, , , , , , , , , ,FMC_D1 , , ,EVENTOUT, -PortE,PE0 , , ,TIM4_ETR, , , , , ,UART8_RX , , , ,FMC_NBL0 ,DCMI_D2 , ,EVENTOUT, -PortE,PE1 , , , , , , , , ,UART8_TX , , , ,FMC_NBL1 ,DCMI_D3 , ,EVENTOUT, -PortE,PE2 ,TRACECLK , , , , ,SPI4_SCK ,SAI1_MCLK_A , , , , ,ETH_MII_TXD3 ,FMC_A23 , , ,EVENTOUT, -PortE,PE3 ,TRACED0 , , , , , ,SAI1_SD_B , , , , , ,FMC_A19 , , ,EVENTOUT, -PortE,PE4 ,TRACED1 , , , , ,SPI4_NSS ,SAI1_FS_A , , , , , ,FMC_A20 ,DCMI_D4 ,LCD_B0 ,EVENTOUT, -PortE,PE5 ,TRACED2 , , ,TIM9_CH1 , ,SPI4_MISO ,SAI1_SCK_A , , , , , ,FMC_A21 ,DCMI_D6 ,LCD_G0 ,EVENTOUT, -PortE,PE6 ,TRACED3 , , ,TIM9_CH2 , ,SPI4_MOSI ,SAI1_SD_A , , , , , ,FMC_A22 ,DCMI_D7 ,LCD_G1 ,EVENTOUT, -PortE,PE7 , ,TIM1_ETR , , , , , , ,UART7_RX , , , ,FMC_D4 , , ,EVENTOUT, -PortE,PE8 , ,TIM1_CH1N , , , , , , ,UART7_TX , , , ,FMC_D5 , , ,EVENTOUT, -PortE,PE9 , ,TIM1_CH1 , , , , , , , , , , ,FMC_D6 , , ,EVENTOUT, -PortE,PE10, ,TIM1_CH2N , , , , , , , , , , ,FMC_D7 , , ,EVENTOUT, -PortE,PE11, ,TIM1_CH2 , , , ,SPI4_NSS , , , , , , ,FMC_D8 , ,LCD_G3 ,EVENTOUT, -PortE,PE12, ,TIM1_CH3N , , , ,SPI4_SCK , , , , , , ,FMC_D9 , ,LCD_B4 ,EVENTOUT, -PortE,PE13, ,TIM1_CH3 , , , ,SPI4_MISO , , , , , , ,FMC_D10 , ,LCD_DE ,EVENTOUT, -PortE,PE14, ,TIM1_CH4 , , , ,SPI4_MOSI , , , , , , ,FMC_D11 , ,LCD_CLK ,EVENTOUT, -PortE,PE15, ,TIM1_BKIN , , , , , , , , , , ,FMC_D12 , ,LCD_R7 ,EVENTOUT, -PortF,PF0 , , , , ,I2C2_SDA , , , , , , , ,FMC_A0 , , ,EVENTOUT, -PortF,PF1 , , , , ,I2C2_SCL , , , , , , , ,FMC_A1 , , ,EVENTOUT, -PortF,PF2 , , , , ,I2C2_SMBA, , , , , , , ,FMC_A2 , , ,EVENTOUT, -PortF,PF3 , , , , , , , , , , , , ,FMC_A3 , , ,EVENTOUT,ADC3_IN9 -PortF,PF4 , , , , , , , , , , , , ,FMC_A4 , , ,EVENTOUT,ADC3_IN14 -PortF,PF5 , , , , , , , , , , , , ,FMC_A5 , , ,EVENTOUT,ADC3_IN15 -PortF,PF6 , , , ,TIM10_CH1 , ,SPI5_NSS ,SAI1_SD_B , ,UART7_RX , , , ,FMC_NIORD , , ,EVENTOUT,ADC3_IN4 -PortF,PF7 , , , ,TIM11_CH1 , ,SPI5_SCK ,SAI1_MCLK_B , ,UART7_TX , , , ,FMC_NREG , , ,EVENTOUT,ADC3_IN5 -PortF,PF8 , , , , , ,SPI5_MISO ,SAI1_SCK_B , , ,TIM13_CH1 , , ,FMC_NIOWR , , ,EVENTOUT,ADC3_IN6 -PortF,PF9 , , , , , ,SPI5_MOSI ,SAI1_FS_B , , ,TIM14_CH1 , , ,FMC_CD , , ,EVENTOUT,ADC3_IN7 -PortF,PF10, , , , , , , , , , , , ,FMC_INTR ,DCMI_D11 ,LCD_DE ,EVENTOUT,ADC3_IN8 -PortF,PF11, , , , , ,SPI5_MOSI , , , , , , ,FMC_SDNRAS ,DCMI_D12 , ,EVENTOUT, -PortF,PF12, , , , , , , , , , , , ,FMC_A6 , , ,EVENTOUT, -PortF,PF13, , , , , , , , , , , , ,FMC_A7 , , ,EVENTOUT, -PortF,PF14, , , , , , , , , , , , ,FMC_A8 , , ,EVENTOUT, -PortF,PF15, , , , , , , , , , , , ,FMC_A9 , , ,EVENTOUT, -PortG,PG0 , , , , , , , , , , , , ,FMC_A10 , , ,EVENTOUT, -PortG,PG1 , , , , , , , , , , , , ,FMC_A11 , , ,EVENTOUT, -PortG,PG2 , , , , , , , , , , , , ,FMC_A12 , , ,EVENTOUT, -PortG,PG3 , , , , , , , , , , , , ,FMC_A13 , , ,EVENTOUT, -PortG,PG4 , , , , , , , , , , , , ,FMC_A14/FMC_BA0 , , ,EVENTOUT, -PortG,PG5 , , , , , , , , , , , , ,FMC_A15/FMC_BA1 , , ,EVENTOUT, -PortG,PG6 , , , , , , , , , , , , ,FMC_INT2 ,DCMI_D12 ,LCD_R7 ,EVENTOUT, -PortG,PG7 , , , , , , , , ,USART6_CK , , , ,FMC_INT3 ,DCMI_D13 ,LCD_CLK ,EVENTOUT, -PortG,PG8 , , , , , ,SPI6_NSS , , ,USART6_RTS , , ,ETH_PPS_OUT ,FMC_SDCLK , , ,EVENTOUT, -PortG,PG9 , , , , , , , , ,USART6_RX , , , ,FMC_NE2/FMC_NCE3 ,DCMI_VSYNC(1), ,EVENTOUT, -PortG,PG10, , , , , , , , , ,LCD_G3 , , ,FMC_NCE4_1/FMC_NE3,DCMI_D2 ,LCD_B2 ,EVENTOUT, -PortG,PG11, , , , , , , , , , , ,ETH_MII_TX_EN/ETH_RMII_TX_EN ,FMC_NCE4_2 ,DCMI_D3 ,LCD_B3 ,EVENTOUT, -PortG,PG12, , , , , ,SPI6_MISO , , ,USART6_RTS ,LCD_B4 , , ,FMC_NE4 , ,LCD_B1 ,EVENTOUT, -PortG,PG13, , , , , ,SPI6_SCK , , ,USART6_CTS , , ,ETH_MII_TXD0/ETH_RMII_TXD0 ,FMC_A24 , , ,EVENTOUT, -PortG,PG14, , , , , ,SPI6_MOSI , , ,USART6_TX , , ,ETH_MII_TXD1/ETH_RMII_TXD1 ,FMC_A25 , , ,EVENTOUT, -PortG,PG15, , , , , , , , ,USART6_CTS , , , ,FMC_SDNCAS ,DCMI_D13 , ,EVENTOUT, -PortH,PH0 , , , , , , , , , , , , , , , ,EVENTOUT, -PortH,PH1 , , , , , , , , , , , , , , , ,EVENTOUT, -PortH,PH2 , , , , , , , , , , , ,ETH_MII_CRS ,FMC_SDCKE0 , ,LCD_R0 ,EVENTOUT, -PortH,PH3 , , , , , , , , , , , ,ETH_MII_COL ,FMC_SDNE0 , ,LCD_R1 ,EVENTOUT, -PortH,PH4 , , , , ,I2C2_SCL , , , , , ,OTG_HS_ULPI_NXT, , , , ,EVENTOUT, -PortH,PH5 , , , , ,I2C2_SDA ,SPI5_NSS , , , , , , ,FMC_SDNWE , , ,EVENTOUT, -PortH,PH6 , , , , ,I2C2_SMBA,SPI5_SCK , , , ,TIM12_CH1 , , ,FMC_SDNE1 ,DCMI_D8 , , , -PortH,PH7 , , , , ,I2C3_SCL ,SPI5_MISO , , , , , ,ETH_MII_RXD3 ,FMC_SDCKE1 ,DCMI_D9 , , , -PortH,PH8 , , , , ,I2C3_SDA , , , , , , , ,FMC_D16 ,DCMI_HSYNC ,LCD_R2 ,EVENTOUT, -PortH,PH9 , , , , ,I2C3_SMBA, , , , ,TIM12_CH2 , , ,FMC_D17 ,DCMI_D0 ,LCD_R3 ,EVENTOUT, -PortH,PH10, , ,TIM5_CH1, , , , , , , , , ,FMC_D18 ,DCMI_D1 ,LCD_R4 ,EVENTOUT, -PortH,PH11, , ,TIM5_CH2, , , , , , , , , ,FMC_D19 ,DCMI_D2 ,LCD_R5 ,EVENTOUT, -PortH,PH12, , ,TIM5_CH3, , , , , , , , , ,FMC_D20 ,DCMI_D3 ,LCD_R6 ,EVENTOUT, -PortH,PH13, , , ,TIM8_CH1N , , , , , ,CAN1_TX , , ,FMC_D21 , ,LCD_G2 ,EVENTOUT, -PortH,PH14, , , ,TIM8_CH2N , , , , , , , , ,FMC_D22 ,DCMI_D4 ,LCD_G3 ,EVENTOUT, -PortH,PH15, , , ,TIM8_CH3N , , , , , , , , ,FMC_D23 ,DCMI_D11 ,LCD_G4 ,EVENTOUT, -PortI,PI0 , , ,TIM5_CH4, , ,SPI2_NSS/I2S2_WS , , , , , , ,FMC_D24 ,DCMI_D13 ,LCD_G5 ,EVENTOUT, -PortI,PI1 , , , , , ,SPI2_SCK/I2S2_CK , , , , , , ,FMC_D25 ,DCMI_D8 ,LCD_G6 ,EVENTOUT, -PortI,PI2 , , , ,TIM8_CH4 , ,SPI2_MISO ,I2S2ext_SD , , , , , ,FMC_D26 ,DCMI_D9 ,LCD_G7 ,EVENTOUT, -PortI,PI3 , , , ,TIM8_ETR , ,SPI2_MOSI/I2S2_SD, , , , , , ,FMC_D27 ,DCMI_D10 , ,EVENTOUT, -PortI,PI4 , , , ,TIM8_BKIN , , , , , , , , ,FMC_NBL2 ,DCMI_D5 ,LCD_B4 ,EVENTOUT, -PortI,PI5 , , , ,TIM8_CH1 , , , , , , , , ,FMC_NBL3 ,DCMI_VSYNC ,LCD_B5 ,EVENTOUT, -PortI,PI6 , , , ,TIM8_CH2 , , , , , , , , ,FMC_D28 ,DCMI_D6 ,LCD_B6 ,EVENTOUT, -PortI,PI7 , , , ,TIM8_CH3 , , , , , , , , ,FMC_D29 ,DCMI_D7 ,LCD_B7 ,EVENTOUT, -PortI,PI8 , , , , , , , , , , , , , , , ,EVENTOUT, -PortI,PI9 , , , , , , , , , ,CAN1_RX , , ,FMC_D30 , ,LCD_VSYNC,EVENTOUT, -PortI,PI10, , , , , , , , , , , ,ETH_MII_RX_ER ,FMC_D31 , ,LCD_HSYNC,EVENTOUT, -PortI,PI11, , , , , , , , , , ,OTG_HS_ULPI_DIR, , , , ,EVENTOUT, -PortI,PI12, , , , , , , , , , , , , , ,LCD_HSYNC,EVENTOUT, -PortI,PI13, , , , , , , , , , , , , , ,LCD_VSYNC,EVENTOUT, -PortI,PI14, , , , , , , , , , , , , , ,LCD_CLK ,EVENTOUT, -PortI,PI15, , , , , , , , , , , , , , ,LCD_R0 ,EVENTOUT, -PortJ,PJ0 , , , , , , , , , , , , , , ,LCD_R1 ,EVENTOUT, -PortJ,PJ1 , , , , , , , , , , , , , , ,LCD_R2 ,EVENTOUT, -PortJ,PJ2 , , , , , , , , , , , , , , ,LCD_R3 ,EVENTOUT, -PortJ,PJ3 , , , , , , , , , , , , , , ,LCD_R4 ,EVENTOUT, -PortJ,PJ4 , , , , , , , , , , , , , , ,LCD_R5 ,EVENTOUT, -PortJ,PJ5 , , , , , , , , , , , , , , ,LCD_R6 ,EVENTOUT, -PortJ,PJ6 , , , , , , , , , , , , , , ,LCD_R7 ,EVENTOUT, -PortJ,PJ7 , , , , , , , , , , , , , , ,LCD_G0 ,EVENTOUT, -PortJ,PJ8 , , , , , , , , , , , , , , ,LCD_G1 ,EVENTOUT, -PortJ,PJ9 , , , , , , , , , , , , , , ,LCD_G2 ,EVENTOUT, -PortJ,PJ10, , , , , , , , , , , , , , ,LCD_G3 ,EVENTOUT, -PortJ,PJ11, , , , , , , , , , , , , , ,LCD_G4 ,EVENTOUT, -PortJ,PJ12, , , , , , , , , , , , , , ,LCD_B0 ,EVENTOUT, -PortJ,PJ13, , , , , , , , , , , , , , ,LCD_B1 ,EVENTOUT, -PortJ,PJ14, , , , , , , , , , , , , , ,LCD_B2 ,EVENTOUT, -PortJ,PJ15, , , , , , , , , , , , , , ,LCD_B3 ,EVENTOUT, -PortK,PK0 , , , , , , , , , , , , , , ,LCD_G5 ,EVENTOUT, -PortK,PK1 , , , , , , , , , , , , , , ,LCD_G6 ,EVENTOUT, -PortK,PK2 , , , , , , , , , , , , , , ,LCD_G7 ,EVENTOUT, -PortK,PK3 , , , , , , , , , , , , , , ,LCD_B4 ,EVENTOUT, -PortK,PK4 , , , , , , , , , , , , , , ,LCD_B5 ,EVENTOUT, -PortK,PK5 , , , , , , , , , , , , , , ,LCD_B6 ,EVENTOUT, -PortK,PK6 , , , , , , , , , , , , , , ,LCD_B7 ,EVENTOUT, -PortK,PK7 , , , , , , , , , , , , , , ,LCD_DE ,EVENTOUT, +Port ,Pin ,AF0 ,AF1 ,AF2 ,AF3 ,AF4 ,AF5 ,AF6 ,AF7 ,AF8 ,AF9 ,AF10 ,AF11 ,AF12 ,AF13 ,AF14 ,AF15 ,ADC + , ,SYS ,TIM1/2 ,TIM3/4/5,TIM8/9/10/11,I2C1/2/3 ,SPI1/2/3/4/5/6 ,SPI2/3/SAI1 ,SPI3/USART1/2/3,USART6/UART4/5/7/8,CAN1/2/TIM12/13/14/LCD,OTG2_HS/OTG1_FS,ETH ,FMC/SDIO/OTG2_FS ,DCMI ,LCD ,SYS , +PortA,PA0 , ,TIM2_CH1/TIM2_ETR,TIM5_CH1,TIM8_ETR , , , ,USART2_CTS ,UART4_TX , , ,ETH_MII_CRS , , , ,EVENTOUT, +PortA,PA1 , ,TIM2_CH2 ,TIM5_CH2, , , , ,USART2_RTS ,UART4_RX , , ,ETH_MII_RX_CLK/ETH_RMII_REF_CLK, , , ,EVENTOUT,ADC123_IN1 +PortA,PA2 , ,TIM2_CH3 ,TIM5_CH3,TIM9_CH1 , , , ,USART2_TX , , , ,ETH_MDIO , , , ,EVENTOUT,ADC123_IN2 +PortA,PA3 , ,TIM2_CH4 ,TIM5_CH4,TIM9_CH2 , , , ,USART2_RX , , ,OTG_HS_ULPI_D0 ,ETH_MII_COL , , ,LCD_B5 ,EVENTOUT,ADC123_IN3 +PortA,PA4 , , , , , ,SPI1_NSS ,SPI3_NSS/I2S3_WS ,USART2_CK , , , , ,OTG_HS_SOF ,DCMI_HSYNC ,LCD_VSYNC,EVENTOUT,ADC12_IN4 +PortA,PA5 , ,TIM2_CH1/TIM2_ETR, ,TIM8_CH1N , ,SPI1_SCK , , , , ,OTG_HS_ULPI_CK , , , , ,EVENTOUT,ADC12_IN5 +PortA,PA6 , ,TIM1_BKIN ,TIM3_CH1,TIM8_BKIN , ,SPI1_MISO , , , ,TIM13_CH1 , , , ,DCMI_PIXCLK,LCD_G2 ,EVENTOUT,ADC12_IN6 +PortA,PA7 , ,TIM1_CH1N ,TIM3_CH2,TIM8_CH1N , ,SPI1_MOSI , , , ,TIM14_CH1 , ,ETH_MII_RX_DV/ETH_RMII_CRS_DV , , , ,EVENTOUT,ADC12_IN7 +PortA,PA8 ,MCO1 ,TIM1_CH1 , , ,I2C3_SCL , , ,USART1_CK , , ,OTG_FS_SOF , , , ,LCD_R6 ,EVENTOUT, +PortA,PA9 , ,TIM1_CH2 , , ,I2C3_SMBA, , ,USART1_TX , , , , , ,DCMI_D0 , ,EVENTOUT, +PortA,PA10, ,TIM1_CH3 , , , , , ,USART1_RX , , ,OTG_FS_ID , , ,DCMI_D1 , ,EVENTOUT, +PortA,PA11, ,TIM1_CH4 , , , , , ,USART1_CTS , ,CAN1_RX ,OTG_FS_DM , , , ,LCD_R4 ,EVENTOUT, +PortA,PA12, ,TIM1_ETR , , , , , ,USART1_RTS , ,CAN1_TX ,OTG_FS_DP , , , ,LCD_R5 ,EVENTOUT, +PortA,PA13,JTMS/SWDIO , , , , , , , , , , , , , , ,EVENTOUT, +PortA,PA14,JTCK/SWCLK , , , , , , , , , , , , , , ,EVENTOUT, +PortA,PA15,JTDI ,TIM2_CH1/TIM2_ETR, , , ,SPI1_NSS ,SPI3_NSS/I2S3_WS , , , , , , , , ,EVENTOUT, +PortB,PB0 , ,TIM1_CH2N ,TIM3_CH3,TIM8_CH2N , , , , , ,LCD_R3 ,OTG_HS_ULPI_D1 ,ETH_MII_RXD2 , , , ,EVENTOUT,ADC12_IN8 +PortB,PB1 , ,TIM1_CH3N ,TIM3_CH4,TIM8_CH3N , , , , , ,LCD_R6 ,OTG_HS_ULPI_D2 ,ETH_MII_RXD3 , , , ,EVENTOUT,ADC12_IN9 +PortB,PB2 , , , , , , , , , , , , , , , ,EVENTOUT, +PortB,PB3 ,JTDO/TRACESWO,TIM2_CH2 , , , ,SPI1_SCK ,SPI3_SCK/I2S3_CK , , , , , , , , ,EVENTOUT, +PortB,PB4 ,NJTRST , ,TIM3_CH1, , ,SPI1_MISO ,SPI3_MISO ,I2S3ext_SD , , , , , , , ,EVENTOUT, +PortB,PB5 , , ,TIM3_CH2, ,I2C1_SMBA,SPI1_MOSI ,SPI3_MOSI/I2S3_SD, , ,CAN2_RX ,OTG_HS_ULPI_D7 ,ETH_PPS_OUT ,FMC_SDCKE1 ,DCMI_D10 , ,EVENTOUT, +PortB,PB6 , , ,TIM4_CH1, ,I2C1_SCL , , ,USART1_TX , ,CAN2_TX , , ,FMC_SDNE1 ,DCMI_D5 , ,EVENTOUT, +PortB,PB7 , , ,TIM4_CH2, ,I2C1_SDA , , ,USART1_RX , , , , ,FMC_NL ,DCMI_VSYNC , ,EVENTOUT, +PortB,PB8 , , ,TIM4_CH3,TIM10_CH1 ,I2C1_SCL , , , , ,CAN1_RX , ,ETH_MII_TXD3 ,SDIO_D4 ,DCMI_D6 ,LCD_B6 ,EVENTOUT, +PortB,PB9 , , ,TIM4_CH4,TIM11_CH1 ,I2C1_SDA ,SPI2_NSS/I2S2_WS , , , ,CAN1_TX , , ,SDIO_D5 ,DCMI_D7 ,LCD_B7 ,EVENTOUT, +PortB,PB10, ,TIM2_CH3 , , ,I2C2_SCL ,SPI2_SCK/I2S2_CK , ,USART3_TX , , ,OTG_HS_ULPI_D3 ,ETH_MII_RX_ER , , ,LCD_G4 ,EVENTOUT, +PortB,PB11, ,TIM2_CH4 , , ,I2C2_SDA , , ,USART3_RX , , ,OTG_HS_ULPI_D4 ,ETH_MII_TX_EN/ETH_RMII_TX_EN , , ,LCD_G5 ,EVENTOUT, +PortB,PB12, ,TIM1_BKIN , , ,I2C2_SMBA,SPI2_NSS/I2S2_WS , ,USART3_CK , ,CAN2_RX ,OTG_HS_ULPI_D5 ,ETH_MII_TXD0/ETH_RMII_TXD0 ,OTG_HS_ID , , ,EVENTOUT, +PortB,PB13, ,TIM1_CH1N , , , ,SPI2_SCK/I2S2_CK , ,USART3_CTS , ,CAN2_TX ,OTG_HS_ULPI_D6 ,ETH_MII_TXD1/ETH_RMII_TXD1 , , , ,EVENTOUT, +PortB,PB14, ,TIM1_CH2N , ,TIM8_CH2N , ,SPI2_MISO ,I2S2ext_SD ,USART3_RTS , ,TIM12_CH1 , , ,OTG_HS_DM , , ,EVENTOUT, +PortB,PB15,RTC_REFIN ,TIM1_CH3N , ,TIM8_CH3N , ,SPI2_MOSI/I2S2_SD, , , ,TIM12_CH2 , , ,OTG_HS_DP , , ,EVENTOUT, +PortC,PC0 , , , , , , , , , , ,OTG_HS_ULPI_STP, ,FMC_SDNWE , , ,EVENTOUT,ADC123_IN10 +PortC,PC1 , , , , , , , , , , , ,ETH_MDC , , , ,EVENTOUT,ADC123_IN11 +PortC,PC2 , , , , , ,SPI2_MISO ,I2S2ext_SD , , , ,OTG_HS_ULPI_DIR,ETH_MII_TXD2 ,FMC_SDNE0 , , ,EVENTOUT,ADC123_IN12 +PortC,PC3 , , , , , ,SPI2_MOSI/I2S2_SD, , , , ,OTG_HS_ULPI_NXT,ETH_MII_TX_CLK ,FMC_SDCKE0 , , ,EVENTOUT,ADC123_IN13 +PortC,PC4 , , , , , , , , , , , ,ETH_MII_RXD0/ETH_RMII_RXD0 , , , ,EVENTOUT,ADC12_IN14 +PortC,PC5 , , , , , , , , , , , ,ETH_MII_RXD1/ETH_RMII_RXD1 , , , ,EVENTOUT,ADC12_IN15 +PortC,PC6 , , ,TIM3_CH1,TIM8_CH1 , ,I2S2_MCK , , ,USART6_TX , , , ,SDIO_D6 ,DCMI_D0 ,LCD_HSYNC,EVENTOUT, +PortC,PC7 , , ,TIM3_CH2,TIM8_CH2 , , ,I2S3_MCK , ,USART6_RX , , , ,SDIO_D7 ,DCMI_D1 ,LCD_G6 ,EVENTOUT, +PortC,PC8 , , ,TIM3_CH3,TIM8_CH3 , , , , ,USART6_CK , , , ,SDIO_D0 ,DCMI_D2 , ,EVENTOUT, +PortC,PC9 ,MCO2 , ,TIM3_CH4,TIM8_CH4 ,I2C3_SDA ,I2S_CKIN , , , , , , ,SDIO_D1 ,DCMI_D3 , ,EVENTOUT, +PortC,PC10, , , , , , ,SPI3_SCK/I2S3_CK ,USART3_TX ,UART4_TX , , , ,SDIO_D2 ,DCMI_D8 ,LCD_R2 ,EVENTOUT, +PortC,PC11, , , , , ,I2S3ext_SD ,SPI3_MISO ,USART3_RX ,UART4_RX , , , ,SDIO_D3 ,DCMI_D4 , ,EVENTOUT, +PortC,PC12, , , , , , ,SPI3_MOSI/I2S3_SD,USART3_CK ,UART5_TX , , , ,SDIO_CK ,DCMI_D9 , ,EVENTOUT, +PortC,PC13, , , , , , , , , , , , , , , ,EVENTOUT, +PortC,PC14, , , , , , , , , , , , , , , ,EVENTOUT, +PortC,PC15, , , , , , , , , , , , , , , ,EVENTOUT, +PortD,PD0 , , , , , , , , , ,CAN1_RX , , ,FMC_D2 , , ,EVENTOUT, +PortD,PD1 , , , , , , , , , ,CAN1_TX , , ,FMC_D3 , , ,EVENTOUT, +PortD,PD2 , , ,TIM3_ETR, , , , , ,UART5_RX , , , ,SDIO_CMD ,DCMI_D11 , ,EVENTOUT, +PortD,PD3 , , , , , ,SPI2_SCK/I2S2_CK , ,USART2_CTS , , , , ,FMC_CLK ,DCMI_D5 ,LCD_G7 ,EVENTOUT, +PortD,PD4 , , , , , , , ,USART2_RTS , , , , ,FMC_NOE , , ,EVENTOUT, +PortD,PD5 , , , , , , , ,USART2_TX , , , , ,FMC_NWE , , ,EVENTOUT, +PortD,PD6 , , , , , ,SPI3_MOSI/I2S3_SD,SAI1_SD_A ,USART2_RX , , , , ,FMC_NWAIT ,DCMI_D10 ,LCD_B2 ,EVENTOUT, +PortD,PD7 , , , , , , , ,USART2_CK , , , , ,FMC_NE1/FMC_NCE2 , , ,EVENTOUT, +PortD,PD8 , , , , , , , ,USART3_TX , , , , ,FMC_D13 , , ,EVENTOUT, +PortD,PD9 , , , , , , , ,USART3_RX , , , , ,FMC_D14 , , ,EVENTOUT, +PortD,PD10, , , , , , , ,USART3_CK , , , , ,FMC_D15 , ,LCD_B3 ,EVENTOUT, +PortD,PD11, , , , , , , ,USART3_CTS , , , , ,FMC_A16 , , ,EVENTOUT, +PortD,PD12, , ,TIM4_CH1, , , , ,USART3_RTS , , , , ,FMC_A17 , , ,EVENTOUT, +PortD,PD13, , ,TIM4_CH2, , , , , , , , , ,FMC_A18 , , ,EVENTOUT, +PortD,PD14, , ,TIM4_CH3, , , , , , , , , ,FMC_D0 , , ,EVENTOUT, +PortD,PD15, , ,TIM4_CH4, , , , , , , , , ,FMC_D1 , , ,EVENTOUT, +PortE,PE0 , , ,TIM4_ETR, , , , , ,UART8_RX , , , ,FMC_NBL0 ,DCMI_D2 , ,EVENTOUT, +PortE,PE1 , , , , , , , , ,UART8_TX , , , ,FMC_NBL1 ,DCMI_D3 , ,EVENTOUT, +PortE,PE2 ,TRACECLK , , , , ,SPI4_SCK ,SAI1_MCLK_A , , , , ,ETH_MII_TXD3 ,FMC_A23 , , ,EVENTOUT, +PortE,PE3 ,TRACED0 , , , , , ,SAI1_SD_B , , , , , ,FMC_A19 , , ,EVENTOUT, +PortE,PE4 ,TRACED1 , , , , ,SPI4_NSS ,SAI1_FS_A , , , , , ,FMC_A20 ,DCMI_D4 ,LCD_B0 ,EVENTOUT, +PortE,PE5 ,TRACED2 , , ,TIM9_CH1 , ,SPI4_MISO ,SAI1_SCK_A , , , , , ,FMC_A21 ,DCMI_D6 ,LCD_G0 ,EVENTOUT, +PortE,PE6 ,TRACED3 , , ,TIM9_CH2 , ,SPI4_MOSI ,SAI1_SD_A , , , , , ,FMC_A22 ,DCMI_D7 ,LCD_G1 ,EVENTOUT, +PortE,PE7 , ,TIM1_ETR , , , , , , ,UART7_RX , , , ,FMC_D4 , , ,EVENTOUT, +PortE,PE8 , ,TIM1_CH1N , , , , , , ,UART7_TX , , , ,FMC_D5 , , ,EVENTOUT, +PortE,PE9 , ,TIM1_CH1 , , , , , , , , , , ,FMC_D6 , , ,EVENTOUT, +PortE,PE10, ,TIM1_CH2N , , , , , , , , , , ,FMC_D7 , , ,EVENTOUT, +PortE,PE11, ,TIM1_CH2 , , , ,SPI4_NSS , , , , , , ,FMC_D8 , ,LCD_G3 ,EVENTOUT, +PortE,PE12, ,TIM1_CH3N , , , ,SPI4_SCK , , , , , , ,FMC_D9 , ,LCD_B4 ,EVENTOUT, +PortE,PE13, ,TIM1_CH3 , , , ,SPI4_MISO , , , , , , ,FMC_D10 , ,LCD_DE ,EVENTOUT, +PortE,PE14, ,TIM1_CH4 , , , ,SPI4_MOSI , , , , , , ,FMC_D11 , ,LCD_CLK ,EVENTOUT, +PortE,PE15, ,TIM1_BKIN , , , , , , , , , , ,FMC_D12 , ,LCD_R7 ,EVENTOUT, +PortF,PF0 , , , , ,I2C2_SDA , , , , , , , ,FMC_A0 , , ,EVENTOUT, +PortF,PF1 , , , , ,I2C2_SCL , , , , , , , ,FMC_A1 , , ,EVENTOUT, +PortF,PF2 , , , , ,I2C2_SMBA, , , , , , , ,FMC_A2 , , ,EVENTOUT, +PortF,PF3 , , , , , , , , , , , , ,FMC_A3 , , ,EVENTOUT,ADC3_IN9 +PortF,PF4 , , , , , , , , , , , , ,FMC_A4 , , ,EVENTOUT,ADC3_IN14 +PortF,PF5 , , , , , , , , , , , , ,FMC_A5 , , ,EVENTOUT,ADC3_IN15 +PortF,PF6 , , , ,TIM10_CH1 , ,SPI5_NSS ,SAI1_SD_B , ,UART7_RX , , , ,FMC_NIORD , , ,EVENTOUT,ADC3_IN4 +PortF,PF7 , , , ,TIM11_CH1 , ,SPI5_SCK ,SAI1_MCLK_B , ,UART7_TX , , , ,FMC_NREG , , ,EVENTOUT,ADC3_IN5 +PortF,PF8 , , , , , ,SPI5_MISO ,SAI1_SCK_B , , ,TIM13_CH1 , , ,FMC_NIOWR , , ,EVENTOUT,ADC3_IN6 +PortF,PF9 , , , , , ,SPI5_MOSI ,SAI1_FS_B , , ,TIM14_CH1 , , ,FMC_CD , , ,EVENTOUT,ADC3_IN7 +PortF,PF10, , , , , , , , , , , , ,FMC_INTR ,DCMI_D11 ,LCD_DE ,EVENTOUT,ADC3_IN8 +PortF,PF11, , , , , ,SPI5_MOSI , , , , , , ,FMC_SDNRAS ,DCMI_D12 , ,EVENTOUT, +PortF,PF12, , , , , , , , , , , , ,FMC_A6 , , ,EVENTOUT, +PortF,PF13, , , , , , , , , , , , ,FMC_A7 , , ,EVENTOUT, +PortF,PF14, , , , , , , , , , , , ,FMC_A8 , , ,EVENTOUT, +PortF,PF15, , , , , , , , , , , , ,FMC_A9 , , ,EVENTOUT, +PortG,PG0 , , , , , , , , , , , , ,FMC_A10 , , ,EVENTOUT, +PortG,PG1 , , , , , , , , , , , , ,FMC_A11 , , ,EVENTOUT, +PortG,PG2 , , , , , , , , , , , , ,FMC_A12 , , ,EVENTOUT, +PortG,PG3 , , , , , , , , , , , , ,FMC_A13 , , ,EVENTOUT, +PortG,PG4 , , , , , , , , , , , , ,FMC_A14/FMC_BA0 , , ,EVENTOUT, +PortG,PG5 , , , , , , , , , , , , ,FMC_A15/FMC_BA1 , , ,EVENTOUT, +PortG,PG6 , , , , , , , , , , , , ,FMC_INT2 ,DCMI_D12 ,LCD_R7 ,EVENTOUT, +PortG,PG7 , , , , , , , , ,USART6_CK , , , ,FMC_INT3 ,DCMI_D13 ,LCD_CLK ,EVENTOUT, +PortG,PG8 , , , , , ,SPI6_NSS , , ,USART6_RTS , , ,ETH_PPS_OUT ,FMC_SDCLK , , ,EVENTOUT, +PortG,PG9 , , , , , , , , ,USART6_RX , , , ,FMC_NE2/FMC_NCE3 ,DCMI_VSYNC , ,EVENTOUT, +PortG,PG10, , , , , , , , , ,LCD_G3 , , ,FMC_NCE4_1/FMC_NE3,DCMI_D2 ,LCD_B2 ,EVENTOUT, +PortG,PG11, , , , , , , , , , , ,ETH_MII_TX_EN/ETH_RMII_TX_EN ,FMC_NCE4_2 ,DCMI_D3 ,LCD_B3 ,EVENTOUT, +PortG,PG12, , , , , ,SPI6_MISO , , ,USART6_RTS ,LCD_B4 , , ,FMC_NE4 , ,LCD_B1 ,EVENTOUT, +PortG,PG13, , , , , ,SPI6_SCK , , ,USART6_CTS , , ,ETH_MII_TXD0/ETH_RMII_TXD0 ,FMC_A24 , , ,EVENTOUT, +PortG,PG14, , , , , ,SPI6_MOSI , , ,USART6_TX , , ,ETH_MII_TXD1/ETH_RMII_TXD1 ,FMC_A25 , , ,EVENTOUT, +PortG,PG15, , , , , , , , ,USART6_CTS , , , ,FMC_SDNCAS ,DCMI_D13 , ,EVENTOUT, +PortH,PH0 , , , , , , , , , , , , , , , ,EVENTOUT, +PortH,PH1 , , , , , , , , , , , , , , , ,EVENTOUT, +PortH,PH2 , , , , , , , , , , , ,ETH_MII_CRS ,FMC_SDCKE0 , ,LCD_R0 ,EVENTOUT, +PortH,PH3 , , , , , , , , , , , ,ETH_MII_COL ,FMC_SDNE0 , ,LCD_R1 ,EVENTOUT, +PortH,PH4 , , , , ,I2C2_SCL , , , , , ,OTG_HS_ULPI_NXT, , , , ,EVENTOUT, +PortH,PH5 , , , , ,I2C2_SDA ,SPI5_NSS , , , , , , ,FMC_SDNWE , , ,EVENTOUT, +PortH,PH6 , , , , ,I2C2_SMBA,SPI5_SCK , , , ,TIM12_CH1 , , ,FMC_SDNE1 ,DCMI_D8 , , , +PortH,PH7 , , , , ,I2C3_SCL ,SPI5_MISO , , , , , ,ETH_MII_RXD3 ,FMC_SDCKE1 ,DCMI_D9 , , , +PortH,PH8 , , , , ,I2C3_SDA , , , , , , , ,FMC_D16 ,DCMI_HSYNC ,LCD_R2 ,EVENTOUT, +PortH,PH9 , , , , ,I2C3_SMBA, , , , ,TIM12_CH2 , , ,FMC_D17 ,DCMI_D0 ,LCD_R3 ,EVENTOUT, +PortH,PH10, , ,TIM5_CH1, , , , , , , , , ,FMC_D18 ,DCMI_D1 ,LCD_R4 ,EVENTOUT, +PortH,PH11, , ,TIM5_CH2, , , , , , , , , ,FMC_D19 ,DCMI_D2 ,LCD_R5 ,EVENTOUT, +PortH,PH12, , ,TIM5_CH3, , , , , , , , , ,FMC_D20 ,DCMI_D3 ,LCD_R6 ,EVENTOUT, +PortH,PH13, , , ,TIM8_CH1N , , , , , ,CAN1_TX , , ,FMC_D21 , ,LCD_G2 ,EVENTOUT, +PortH,PH14, , , ,TIM8_CH2N , , , , , , , , ,FMC_D22 ,DCMI_D4 ,LCD_G3 ,EVENTOUT, +PortH,PH15, , , ,TIM8_CH3N , , , , , , , , ,FMC_D23 ,DCMI_D11 ,LCD_G4 ,EVENTOUT, +PortI,PI0 , , ,TIM5_CH4, , ,SPI2_NSS/I2S2_WS , , , , , , ,FMC_D24 ,DCMI_D13 ,LCD_G5 ,EVENTOUT, +PortI,PI1 , , , , , ,SPI2_SCK/I2S2_CK , , , , , , ,FMC_D25 ,DCMI_D8 ,LCD_G6 ,EVENTOUT, +PortI,PI2 , , , ,TIM8_CH4 , ,SPI2_MISO ,I2S2ext_SD , , , , , ,FMC_D26 ,DCMI_D9 ,LCD_G7 ,EVENTOUT, +PortI,PI3 , , , ,TIM8_ETR , ,SPI2_MOSI/I2S2_SD, , , , , , ,FMC_D27 ,DCMI_D10 , ,EVENTOUT, +PortI,PI4 , , , ,TIM8_BKIN , , , , , , , , ,FMC_NBL2 ,DCMI_D5 ,LCD_B4 ,EVENTOUT, +PortI,PI5 , , , ,TIM8_CH1 , , , , , , , , ,FMC_NBL3 ,DCMI_VSYNC ,LCD_B5 ,EVENTOUT, +PortI,PI6 , , , ,TIM8_CH2 , , , , , , , , ,FMC_D28 ,DCMI_D6 ,LCD_B6 ,EVENTOUT, +PortI,PI7 , , , ,TIM8_CH3 , , , , , , , , ,FMC_D29 ,DCMI_D7 ,LCD_B7 ,EVENTOUT, +PortI,PI8 , , , , , , , , , , , , , , , ,EVENTOUT, +PortI,PI9 , , , , , , , , , ,CAN1_RX , , ,FMC_D30 , ,LCD_VSYNC,EVENTOUT, +PortI,PI10, , , , , , , , , , , ,ETH_MII_RX_ER ,FMC_D31 , ,LCD_HSYNC,EVENTOUT, +PortI,PI11, , , , , , , , , , ,OTG_HS_ULPI_DIR, , , , ,EVENTOUT, +PortI,PI12, , , , , , , , , , , , , , ,LCD_HSYNC,EVENTOUT, +PortI,PI13, , , , , , , , , , , , , , ,LCD_VSYNC,EVENTOUT, +PortI,PI14, , , , , , , , , , , , , , ,LCD_CLK ,EVENTOUT, +PortI,PI15, , , , , , , , , , , , , , ,LCD_R0 ,EVENTOUT, +PortJ,PJ0 , , , , , , , , , , , , , , ,LCD_R1 ,EVENTOUT, +PortJ,PJ1 , , , , , , , , , , , , , , ,LCD_R2 ,EVENTOUT, +PortJ,PJ2 , , , , , , , , , , , , , , ,LCD_R3 ,EVENTOUT, +PortJ,PJ3 , , , , , , , , , , , , , , ,LCD_R4 ,EVENTOUT, +PortJ,PJ4 , , , , , , , , , , , , , , ,LCD_R5 ,EVENTOUT, +PortJ,PJ5 , , , , , , , , , , , , , , ,LCD_R6 ,EVENTOUT, +PortJ,PJ6 , , , , , , , , , , , , , , ,LCD_R7 ,EVENTOUT, +PortJ,PJ7 , , , , , , , , , , , , , , ,LCD_G0 ,EVENTOUT, +PortJ,PJ8 , , , , , , , , , , , , , , ,LCD_G1 ,EVENTOUT, +PortJ,PJ9 , , , , , , , , , , , , , , ,LCD_G2 ,EVENTOUT, +PortJ,PJ10, , , , , , , , , , , , , , ,LCD_G3 ,EVENTOUT, +PortJ,PJ11, , , , , , , , , , , , , , ,LCD_G4 ,EVENTOUT, +PortJ,PJ12, , , , , , , , , , , , , , ,LCD_B0 ,EVENTOUT, +PortJ,PJ13, , , , , , , , , , , , , , ,LCD_B1 ,EVENTOUT, +PortJ,PJ14, , , , , , , , , , , , , , ,LCD_B2 ,EVENTOUT, +PortJ,PJ15, , , , , , , , , , , , , , ,LCD_B3 ,EVENTOUT, +PortK,PK0 , , , , , , , , , , , , , , ,LCD_G5 ,EVENTOUT, +PortK,PK1 , , , , , , , , , , , , , , ,LCD_G6 ,EVENTOUT, +PortK,PK2 , , , , , , , , , , , , , , ,LCD_G7 ,EVENTOUT, +PortK,PK3 , , , , , , , , , , , , , , ,LCD_B4 ,EVENTOUT, +PortK,PK4 , , , , , , , , , , , , , , ,LCD_B5 ,EVENTOUT, +PortK,PK5 , , , , , , , , , , , , , , ,LCD_B6 ,EVENTOUT, +PortK,PK6 , , , , , , , , , , , , , , ,LCD_B7 ,EVENTOUT, +PortK,PK7 , , , , , , , , , , , , , , ,LCD_DE ,EVENTOUT, diff --git a/ports/stm32/boards/stm32f439_af.csv b/ports/stm32/boards/stm32f439_af.csv index 94466c6c1a..f7069fb579 100644 --- a/ports/stm32/boards/stm32f439_af.csv +++ b/ports/stm32/boards/stm32f439_af.csv @@ -1,170 +1,170 @@ -Port ,Pin ,AF0 ,AF1 ,AF2 ,AF3 ,AF4 ,AF5 ,AF6 ,AF7 ,AF8 ,AF9 ,AF10 ,AF11 ,AF12 ,AF13 ,AF14 ,AF15 ,ADC - , ,SYS ,TIM1/2 ,TIM3/4/5,TIM8/9/10/11,I2C1/2/3 ,SPI1/2/3/4/5/6 ,SPI2/3/SAI1 ,SPI3/USART1/2/3,USART6/UART4/5/7/8,CAN1/2/TIM12/13/14/LCD,OTG2_HS/OTG1_FS,ETH ,FMC/SDIO/OTG2_FS ,DCMI ,LCD ,SYS , -PortA,PA0 , ,TIM2_CH1/TIM2_ETR,TIM5_CH1,TIM8_ETR , , , ,USART2_CTS ,UART4_TX , , ,ETH_MII_CRS , , , ,EVENTOUT,ADC123_IN0 -PortA,PA1 , ,TIM2_CH2 ,TIM5_CH2, , , , ,USART2_RTS ,UART4_RX , , ,ETH_MII_RX_CLK/ETH_RMII_REF_CLK, , , ,EVENTOUT,ADC123_IN1 -PortA,PA2 , ,TIM2_CH3 ,TIM5_CH3,TIM9_CH1 , , , ,USART2_TX , , , ,ETH_MDIO , , , ,EVENTOUT,ADC123_IN2 -PortA,PA3 , ,TIM2_CH4 ,TIM5_CH4,TIM9_CH2 , , , ,USART2_RX , , ,OTG_HS_ULPI_D0 ,ETH_MII_COL , , ,LCD_B5 ,EVENTOUT,ADC123_IN3 -PortA,PA4 , , , , , ,SPI1_NSS ,SPI3_NSS/I2S3_WS ,USART2_CK , , , , ,OTG_HS_SOF ,DCMI_HSYNC ,LCD_VSYNC,EVENTOUT,ADC12_IN4 -PortA,PA5 , ,TIM2_CH1/TIM2_ETR, ,TIM8_CH1N , ,SPI1_SCK , , , , ,OTG_HS_ULPI_CK , , , , ,EVENTOUT,ADC12_IN5 -PortA,PA6 , ,TIM1_BKIN ,TIM3_CH1,TIM8_BKIN , ,SPI1_MISO , , , ,TIM13_CH1 , , , ,DCMI_PIXCLK ,LCD_G2 ,EVENTOUT,ADC12_IN6 -PortA,PA7 , ,TIM1_CH1N ,TIM3_CH2,TIM8_CH1N , ,SPI1_MOSI , , , ,TIM14_CH1 , ,ETH_MII_RX_DV/ETH_RMII_CRS_DV , , , ,EVENTOUT,ADC12_IN7 -PortA,PA8 ,MCO1 ,TIM1_CH1 , , ,I2C3_SCL , , ,USART1_CK , , ,OTG_FS_SOF , , , ,LCD_R6 ,EVENTOUT, -PortA,PA9 , ,TIM1_CH2 , , ,I2C3_SMBA, , ,USART1_TX , , , , , ,DCMI_D0 , ,EVENTOUT, -PortA,PA10, ,TIM1_CH3 , , , , , ,USART1_RX , , ,OTG_FS_ID , , ,DCMI_D1 , ,EVENTOUT, -PortA,PA11, ,TIM1_CH4 , , , , , ,USART1_CTS , ,CAN1_RX ,OTG_FS_DM , , , ,LCD_R4 ,EVENTOUT, -PortA,PA12, ,TIM1_ETR , , , , , ,USART1_RTS , ,CAN1_TX ,OTG_FS_DP , , , ,LCD_R5 ,EVENTOUT, -PortA,PA13,JTMS/SWDIO , , , , , , , , , , , , , , ,EVENTOUT, -PortA,PA14,JTCK/SWCLK , , , , , , , , , , , , , , ,EVENTOUT, -PortA,PA15,JTDI ,TIM2_CH1/TIM2_ETR, , , ,SPI1_NSS ,SPI3_NSS/I2S3_WS , , , , , , , , ,EVENTOUT, -PortB,PB0 , ,TIM1_CH2N ,TIM3_CH3,TIM8_CH2N , , , , , ,LCD_R3 ,OTG_HS_ULPI_D1 ,ETH_MII_RXD2 , , , ,EVENTOUT,ADC12_IN8 -PortB,PB1 , ,TIM1_CH3N ,TIM3_CH4,TIM8_CH3N , , , , , ,LCD_R6 ,OTG_HS_ULPI_D2 ,ETH_MII_RXD3 , , , ,EVENTOUT,ADC12_IN9 -PortB,PB2 , , , , , , , , , , , , , , , ,EVENTOUT, -PortB,PB3 ,JTDO/TRACESWO,TIM2_CH2 , , , ,SPI1_SCK ,SPI3_SCK/I2S3_CK , , , , , , , , ,EVENTOUT, -PortB,PB4 ,NJTRST , ,TIM3_CH1, , ,SPI1_MISO ,SPI3_MISO ,I2S3ext_SD , , , , , , , ,EVENTOUT, -PortB,PB5 , , ,TIM3_CH2, ,I2C1_SMBA,SPI1_MOSI ,SPI3_MOSI/I2S3_SD, , ,CAN2_RX ,OTG_HS_ULPI_D7 ,ETH_PPS_OUT ,FMC_SDCKE1 ,DCMI_D10 , ,EVENTOUT, -PortB,PB6 , , ,TIM4_CH1, ,I2C1_SCL , , ,USART1_TX , ,CAN2_TX , , ,FMC_SDNE1 ,DCMI_D5 , ,EVENTOUT, -PortB,PB7 , , ,TIM4_CH2, ,I2C1_SDA , , ,USART1_RX , , , , ,FMC_NL ,DCMI_VSYNC , ,EVENTOUT, -PortB,PB8 , , ,TIM4_CH3,TIM10_CH1 ,I2C1_SCL , , , , ,CAN1_RX , ,ETH_MII_TXD3 ,SDIO_D4 ,DCMI_D6 ,LCD_B6 ,EVENTOUT, -PortB,PB9 , , ,TIM4_CH4,TIM11_CH1 ,I2C1_SDA ,SPI2_NSS/I2S2_WS , , , ,CAN1_TX , , ,SDIO_D5 ,DCMI_D7 ,LCD_B7 ,EVENTOUT, -PortB,PB10, ,TIM2_CH3 , , ,I2C2_SCL ,SPI2_SCK/I2S2_CK , ,USART3_TX , , ,OTG_HS_ULPI_D3 ,ETH_MII_RX_ER , , ,LCD_G4 ,EVENTOUT, -PortB,PB11, ,TIM2_CH4 , , ,I2C2_SDA , , ,USART3_RX , , ,OTG_HS_ULPI_D4 ,ETH_MII_TX_EN/ETH_RMII_TX_EN , , ,LCD_G5 ,EVENTOUT, -PortB,PB12, ,TIM1_BKIN , , ,I2C2_SMBA,SPI2_NSS/I2S2_WS , ,USART3_CK , ,CAN2_RX ,OTG_HS_ULPI_D5 ,ETH_MII_TXD0/ETH_RMII_TXD0 ,OTG_HS_ID , , ,EVENTOUT, -PortB,PB13, ,TIM1_CH1N , , , ,SPI2_SCK/I2S2_CK , ,USART3_CTS , ,CAN2_TX ,OTG_HS_ULPI_D6 ,ETH_MII_TXD1/ETH_RMII_TXD1 , , , ,EVENTOUT, -PortB,PB14, ,TIM1_CH2N , ,TIM8_CH2N , ,SPI2_MISO ,I2S2ext_SD ,USART3_RTS , ,TIM12_CH1 , , ,OTG_HS_DM , , ,EVENTOUT, -PortB,PB15,RTC_REFIN ,TIM1_CH3N , ,TIM8_CH3N , ,SPI2_MOSI/I2S2_SD, , , ,TIM12_CH2 , , ,OTG_HS_DP , , ,EVENTOUT, -PortC,PC0 , , , , , , , , , , ,OTG_HS_ULPI_STP, ,FMC_SDNWE , , ,EVENTOUT,ADC123_IN10 -PortC,PC1 , , , , , , , , , , , ,ETH_MDC , , , ,EVENTOUT,ADC123_IN11 -PortC,PC2 , , , , , ,SPI2_MISO ,I2S2ext_SD , , , ,OTG_HS_ULPI_DIR,ETH_MII_TXD2 ,FMC_SDNE0 , , ,EVENTOUT,ADC123_IN12 -PortC,PC3 , , , , , ,SPI2_MOSI/I2S2_SD, , , , ,OTG_HS_ULPI_NXT,ETH_MII_TX_CLK ,FMC_SDCKE0 , , ,EVENTOUT,ADC123_IN13 -PortC,PC4 , , , , , , , , , , , ,ETH_MII_RXD0/ETH_RMII_RXD0 , , , ,EVENTOUT,ADC12_IN14 -PortC,PC5 , , , , , , , , , , , ,ETH_MII_RXD1/ETH_RMII_RXD1 , , , ,EVENTOUT,ADC12_IN15 -PortC,PC6 , , ,TIM3_CH1,TIM8_CH1 , ,I2S2_MCK , , ,USART6_TX , , , ,SDIO_D6 ,DCMI_D0 ,LCD_HSYNC,EVENTOUT, -PortC,PC7 , , ,TIM3_CH2,TIM8_CH2 , , ,I2S3_MCK , ,USART6_RX , , , ,SDIO_D7 ,DCMI_D1 ,LCD_G6 ,EVENTOUT, -PortC,PC8 , , ,TIM3_CH3,TIM8_CH3 , , , , ,USART6_CK , , , ,SDIO_D0 ,DCMI_D2 , ,EVENTOUT, -PortC,PC9 ,MCO2 , ,TIM3_CH4,TIM8_CH4 ,I2C3_SDA ,I2S_CKIN , , , , , , ,SDIO_D1 ,DCMI_D3 , ,EVENTOUT, -PortC,PC10, , , , , , ,SPI3_SCK/I2S3_CK ,USART3_TX ,UART4_TX , , , ,SDIO_D2 ,DCMI_D8 ,LCD_R2 ,EVENTOUT, -PortC,PC11, , , , , ,I2S3ext_SD ,SPI3_MISO ,USART3_RX ,UART4_RX , , , ,SDIO_D3 ,DCMI_D4 , ,EVENTOUT, -PortC,PC12, , , , , , ,SPI3_MOSI/I2S3_SD,USART3_CK ,UART5_TX , , , ,SDIO_CK ,DCMI_D9 , ,EVENTOUT, -PortC,PC13, , , , , , , , , , , , , , , ,EVENTOUT, -PortC,PC14, , , , , , , , , , , , , , , ,EVENTOUT, -PortC,PC15, , , , , , , , , , , , , , , ,EVENTOUT, -PortD,PD0 , , , , , , , , , ,CAN1_RX , , ,FMC_D2 , , ,EVENTOUT, -PortD,PD1 , , , , , , , , , ,CAN1_TX , , ,FMC_D3 , , ,EVENTOUT, -PortD,PD2 , , ,TIM3_ETR, , , , , ,UART5_RX , , , ,SDIO_CMD ,DCMI_D11 , ,EVENTOUT, -PortD,PD3 , , , , , ,SPI2_SCK/I2S2_CK , ,USART2_CTS , , , , ,FMC_CLK ,DCMI_D5 ,LCD_G7 ,EVENTOUT, -PortD,PD4 , , , , , , , ,USART2_RTS , , , , ,FMC_NOE , , ,EVENTOUT, -PortD,PD5 , , , , , , , ,USART2_TX , , , , ,FMC_NWE , , ,EVENTOUT, -PortD,PD6 , , , , , ,I2S3_SD ,SAI1_SD_A ,USART2_RX , , , , ,FMC_NWAIT ,DCMI_D10 ,LCD_B2 ,EVENTOUT, -PortD,PD7 , , , , , , , ,USART2_CK , , , , ,FMC_NE1/FMC_NCE2 , , ,EVENTOUT, -PortD,PD8 , , , , , , , ,USART3_TX , , , , ,FMC_D13 , , ,EVENTOUT, -PortD,PD9 , , , , , , , ,USART3_RX , , , , ,FMC_D14 , , ,EVENTOUT, -PortD,PD10, , , , , , , ,USART3_CK , , , , ,FMC_D15 , ,LCD_B3 ,EVENTOUT, -PortD,PD11, , , , , , , ,USART3_CTS , , , , ,FMC_A16 , , ,EVENTOUT, -PortD,PD12, , ,TIM4_CH1, , , , ,USART3_RTS , , , , ,FMC_A17 , , ,EVENTOUT, -PortD,PD13, , ,TIM4_CH2, , , , , , , , , ,FMC_A18 , , ,EVENTOUT, -PortD,PD14, , ,TIM4_CH3, , , , , , , , , ,FMC_D0 , , ,EVENTOUT, -PortD,PD15, , ,TIM4_CH4, , , , , , , , , ,FMC_D1 , , ,EVENTOUT, -PortE,PE0 , , ,TIM4_ETR, , , , , ,UART8_RX , , , ,FMC_NBL0 ,DCMI_D2 , ,EVENTOUT, -PortE,PE1 , , , , , , , , ,UART8_TX , , , ,FMC_NBL1 ,DCMI_D3 , ,EVENTOUT, -PortE,PE2 ,TRACECLK , , , , ,SPI4_SCK ,SAI1_MCLK_A , , , , ,ETH_MII_TXD3 ,FMC_A23 , , ,EVENTOUT, -PortE,PE3 ,TRACED0 , , , , , ,SAI1_SD_B , , , , , ,FMC_A19 , , ,EVENTOUT, -PortE,PE4 ,TRACED1 , , , , ,SPI4_NSS ,SAI1_FS_A , , , , , ,FMC_A20 ,DCMI_D4 ,LCD_B0 ,EVENTOUT, -PortE,PE5 ,TRACED2 , , ,TIM9_CH1 , ,SPI4_MISO ,SAI1_SCK_A , , , , , ,FMC_A21 ,DCMI_D6 ,LCD_G0 ,EVENTOUT, -PortE,PE6 ,TRACED3 , , ,TIM9_CH2 , ,SPI4_MOSI ,SAI1_SD_A , , , , , ,FMC_A22 ,DCMI_D7 ,LCD_G1 ,EVENTOUT, -PortE,PE7 , ,TIM1_ETR , , , , , , ,UART7_RX , , , ,FMC_D4 , , ,EVENTOUT, -PortE,PE8 , ,TIM1_CH1N , , , , , , ,UART7_TX , , , ,FMC_D5 , , ,EVENTOUT, -PortE,PE9 , ,TIM1_CH1 , , , , , , , , , , ,FMC_D6 , , ,EVENTOUT, -PortE,PE10, ,TIM1_CH2N , , , , , , , , , , ,FMC_D7 , , ,EVENTOUT, -PortE,PE11, ,TIM1_CH2 , , , ,SPI4_NSS ,SPI3_NSS , , , , , ,FMC_D8 , ,LCD_G3 ,EVENTOUT, -PortE,PE12, ,TIM1_CH3N , , , ,SPI4_SCK ,SPI3_SCK , , , , , ,FMC_D9 , ,LCD_B4 ,EVENTOUT, -PortE,PE13, ,TIM1_CH3 , , , ,SPI4_MISO ,SPI3_MISO , , , , , ,FMC_D10 , ,LCD_DE ,EVENTOUT, -PortE,PE14, ,TIM1_CH4 , , , ,SPI4_MOSI ,SP3_MOSI , , , , , ,FMC_D11 , ,LCD_CLK ,EVENTOUT, -PortE,PE15, ,TIM1_BKIN , , , , , , , , , , ,FMC_D12 , ,LCD_R7 ,EVENTOUT, -PortF,PF0 , , , , ,I2C2_SDA , , , , , , , ,FMC_A0 , , ,EVENTOUT, -PortF,PF1 , , , , ,I2C2_SCL , , , , , , , ,FMC_A1 , , ,EVENTOUT, -PortF,PF2 , , , , ,I2C2_SMBA, , , , , , , ,FMC_A2 , , ,EVENTOUT, -PortF,PF3 , , , , , , , , , , , , ,FMC_A3 , , ,EVENTOUT,ADC3_IN9 -PortF,PF4 , , , , , , , , , , , , ,FMC_A4 , , ,EVENTOUT,ADC3_IN14 -PortF,PF5 , , , , , , , , , , , , ,FMC_A5 , , ,EVENTOUT,ADC3_IN15 -PortF,PF6 , , , ,TIM10_CH1 , ,SPI5_NSS ,SAI1_SD_B , ,UART7_RX , , , ,FMC_NIORD , , ,EVENTOUT,ADC3_IN4 -PortF,PF7 , , , ,TIM11_CH1 , ,SPI5_SCK ,SAI1_MCLK_B , ,UART7_TX , , , ,FMC_NREG , , ,EVENTOUT,ADC3_IN5 -PortF,PF8 , , , , , ,SPI5_MISO ,SAI1_SCK_B , , ,TIM13_CH1 , , ,FMC_NIOWR , , ,EVENTOUT,ADC3_IN6 -PortF,PF9 , , , , , ,SPI5_MOSI ,SAI1_FS_B , , ,TIM14_CH1 , , ,FMC_CD , , ,EVENTOUT,ADC3_IN7 -PortF,PF10, , , , , , , , , , , , ,FMC_INTR ,DCMI_D11 ,LCD_DE ,EVENTOUT,ADC3_IN8 -PortF,PF11, , , , , ,SPI5_MOSI , , , , , , ,FMC_SDNRAS ,DCMI_D12 , ,EVENTOUT, -PortF,PF12, , , , , , , , , , , , ,FMC_A6 , , ,EVENTOUT, -PortF,PF13, , , , , , , , , , , , ,FMC_A7 , , ,EVENTOUT, -PortF,PF14, , , , , , , , , , , , ,FMC_A8 , , ,EVENTOUT, -PortF,PF15, , , , , , , , , , , , ,FMC_A9 , , ,EVENTOUT, -PortG,PG0 , , , , , , , , , , , , ,FMC_A10 , , ,EVENTOUT, -PortG,PG1 , , , , , , , , , , , , ,FMC_A11 , , ,EVENTOUT, -PortG,PG2 , , , , , , , , , , , , ,FMC_A12 , , ,EVENTOUT, -PortG,PG3 , , , , , , , , , , , , ,FMC_A13 , , ,EVENTOUT, -PortG,PG4 , , , , , , , , , , , , ,FMC_A14/FMC_BA0 , , ,EVENTOUT, -PortG,PG5 , , , , , , , , , , , , ,FMC_A15/FMC_BA1 , , ,EVENTOUT, -PortG,PG6 , , , , , , , , , , , , ,FMC_INT2 ,DCMI_D12 ,LCD_R7 ,EVENTOUT, -PortG,PG7 , , , , , , , , ,USART6_CK , , , ,FMC_INT3 ,DCMI_D13 ,LCD_CLK ,EVENTOUT, -PortG,PG8 , , , , , ,SPI6_NSS , , ,USART6_RTS , , ,ETH_PPS_OUT ,FMC_SDCLK , , ,EVENTOUT, -PortG,PG9 , , , , , , , , ,USART6_RX , , , ,FMC_NE2/FMC_NCE3 ,DCMI_VSYNC(1), ,EVENTOUT, -PortG,PG10, , , , , , , , , ,LCD_G3 , , ,FMC_NCE4_1/FMC_NE3,DCMI_D2 ,LCD_B2 ,EVENTOUT, -PortG,PG11, , , , , , , , , , , ,ETH_MII_TX_EN/ETH_RMII_TX_EN ,FMC_NCE4_2 ,DCMI_D3 ,LCD_B3 ,EVENTOUT, -PortG,PG12, , , , , ,SPI6_MISO , , ,USART6_RTS ,LCD_B4 , , ,FMC_NE4 , ,LCD_B1 ,EVENTOUT, -PortG,PG13, , , , , ,SPI6_SCK , , ,USART6_CTS , , ,ETH_MII_TXD0/ETH_RMII_TXD0 ,FMC_A24 , , ,EVENTOUT, -PortG,PG14, , , , , ,SPI6_MOSI , , ,USART6_TX , , ,ETH_MII_TXD1/ETH_RMII_TXD1 ,FMC_A25 , , ,EVENTOUT, -PortG,PG15, , , , , , , , ,USART6_CTS , , , ,FMC_SDNCAS ,DCMI_D13 , ,EVENTOUT, -PortH,PH0 , , , , , , , , , , , , , , , ,EVENTOUT, -PortH,PH1 , , , , , , , , , , , , , , , ,EVENTOUT, -PortH,PH2 , , , , , , , , , , , ,ETH_MII_CRS ,FMC_SDCKE0 , ,LCD_R0 ,EVENTOUT, -PortH,PH3 , , , , , , , , , , , ,ETH_MII_COL ,FMC_SDNE0 , ,LCD_R1 ,EVENTOUT, -PortH,PH4 , , , , ,I2C2_SCL , , , , , ,OTG_HS_ULPI_NXT, , , , ,EVENTOUT, -PortH,PH5 , , , , ,I2C2_SDA ,SPI5_NSS , , , , , , ,FMC_SDNWE , , ,EVENTOUT, -PortH,PH6 , , , , ,I2C2_SMBA,SPI5_SCK , , , ,TIM12_CH1 , , ,FMC_SDNE1 ,DCMI_D8 , , , -PortH,PH7 , , , , ,I2C3_SCL ,SPI5_MISO , , , , , ,ETH_MII_RXD3 ,FMC_SDCKE1 ,DCMI_D9 , , , -PortH,PH8 , , , , ,I2C3_SDA , , , , , , , ,FMC_D16 ,DCMI_HSYNC ,LCD_R2 ,EVENTOUT, -PortH,PH9 , , , , ,I2C3_SMBA, , , , ,TIM12_CH2 , , ,FMC_D17 ,DCMI_D0 ,LCD_R3 ,EVENTOUT, -PortH,PH10, , ,TIM5_CH1, , , , , , , , , ,FMC_D18 ,DCMI_D1 ,LCD_R4 ,EVENTOUT, -PortH,PH11, , ,TIM5_CH2, , , , , , , , , ,FMC_D19 ,DCMI_D2 ,LCD_R5 ,EVENTOUT, -PortH,PH12, , ,TIM5_CH3, , , , , , , , , ,FMC_D20 ,DCMI_D3 ,LCD_R6 ,EVENTOUT, -PortH,PH13, , , ,TIM8_CH1N , , , , , ,CAN1_TX , , ,FMC_D21 , ,LCD_G2 ,EVENTOUT, -PortH,PH14, , , ,TIM8_CH2N , , , , , , , , ,FMC_D22 ,DCMI_D4 ,LCD_G3 ,EVENTOUT, -PortH,PH15, , , ,TIM8_CH3N , , , , , , , , ,FMC_D23 ,DCMI_D11 ,LCD_G4 ,EVENTOUT, -PortI,PI0 , , ,TIM5_CH4, , ,SPI2_NSS/I2S2_WS , , , , , , ,FMC_D24 ,DCMI_D13 ,LCD_G5 ,EVENTOUT, -PortI,PI1 , , , , , ,SPI2_SCK/I2S2_CK , , , , , , ,FMC_D25 ,DCMI_D8 ,LCD_G6 ,EVENTOUT, -PortI,PI2 , , , ,TIM8_CH4 , ,SPI2_MISO ,I2S2ext_SD , , , , , ,FMC_D26 ,DCMI_D9 ,LCD_G7 ,EVENTOUT, -PortI,PI3 , , , ,TIM8_ETR , ,SPI2_MOSI/I2S2_SD, , , , , , ,FMC_D27 ,DCMI_D10 , ,EVENTOUT, -PortI,PI4 , , , ,TIM8_BKIN , , , , , , , , ,FMC_NBL2 ,DCMI_D5 ,LCD_B4 ,EVENTOUT, -PortI,PI5 , , , ,TIM8_CH1 , , , , , , , , ,FMC_NBL3 ,DCMI_VSYNC ,LCD_B5 ,EVENTOUT, -PortI,PI6 , , , ,TIM8_CH2 , , , , , , , , ,FMC_D28 ,DCMI_D6 ,LCD_B6 ,EVENTOUT, -PortI,PI7 , , , ,TIM8_CH3 , , , , , , , , ,FMC_D29 ,DCMI_D7 ,LCD_B7 ,EVENTOUT, -PortI,PI8 , , , , , , , , , , , , , , , ,EVENTOUT, -PortI,PI9 , , , , , , , , , ,CAN1_RX , , ,FMC_D30 , ,LCD_VSYNC,EVENTOUT, -PortI,PI10, , , , , , , , , , , ,ETH_MII_RX_ER ,FMC_D31 , ,LCD_HSYNC,EVENTOUT, -PortI,PI11, , , , , , , , , , ,OTG_HS_ULPI_DIR, , , , ,EVENTOUT, -PortI,PI12, , , , , , , , , , , , , , ,LCD_HSYNC,EVENTOUT, -PortI,PI13, , , , , , , , , , , , , , ,LCD_VSYNC,EVENTOUT, -PortI,PI14, , , , , , , , , , , , , , ,LCD_CLK ,EVENTOUT, -PortI,PI15, , , , , , , , , , , , , , ,LCD_R0 ,EVENTOUT, -PortJ,PJ0 , , , , , , , , , , , , , , ,LCD_R1 ,EVENTOUT, -PortJ,PJ1 , , , , , , , , , , , , , , ,LCD_R2 ,EVENTOUT, -PortJ,PJ2 , , , , , , , , , , , , , , ,LCD_R3 ,EVENTOUT, -PortJ,PJ3 , , , , , , , , , , , , , , ,LCD_R4 ,EVENTOUT, -PortJ,PJ4 , , , , , , , , , , , , , , ,LCD_R5 ,EVENTOUT, -PortJ,PJ5 , , , , , , , , , , , , , , ,LCD_R6 ,EVENTOUT, -PortJ,PJ6 , , , , , , , , , , , , , , ,LCD_R7 ,EVENTOUT, -PortJ,PJ7 , , , , , , , , , , , , , , ,LCD_G0 ,EVENTOUT, -PortJ,PJ8 , , , , , , , , , , , , , , ,LCD_G1 ,EVENTOUT, -PortJ,PJ9 , , , , , , , , , , , , , , ,LCD_G2 ,EVENTOUT, -PortJ,PJ10, , , , , , , , , , , , , , ,LCD_G3 ,EVENTOUT, -PortJ,PJ11, , , , , , , , , , , , , , ,LCD_G4 ,EVENTOUT, -PortJ,PJ12, , , , , , , , , , , , , , ,LCD_B0 ,EVENTOUT, -PortJ,PJ13, , , , , , , , , , , , , , ,LCD_B1 ,EVENTOUT, -PortJ,PJ14, , , , , , , , , , , , , , ,LCD_B2 ,EVENTOUT, -PortJ,PJ15, , , , , , , , , , , , , , ,LCD_B3 ,EVENTOUT, -PortK,PK0 , , , , , , , , , , , , , , ,LCD_G5 ,EVENTOUT, -PortK,PK1 , , , , , , , , , , , , , , ,LCD_G6 ,EVENTOUT, -PortK,PK2 , , , , , , , , , , , , , , ,LCD_G7 ,EVENTOUT, -PortK,PK3 , , , , , , , , , , , , , , ,LCD_B4 ,EVENTOUT, -PortK,PK4 , , , , , , , , , , , , , , ,LCD_B5 ,EVENTOUT, -PortK,PK5 , , , , , , , , , , , , , , ,LCD_B6 ,EVENTOUT, -PortK,PK6 , , , , , , , , , , , , , , ,LCD_B7 ,EVENTOUT, -PortK,PK7 , , , , , , , , , , , , , , ,LCD_DE ,EVENTOUT, +Port ,Pin ,AF0 ,AF1 ,AF2 ,AF3 ,AF4 ,AF5 ,AF6 ,AF7 ,AF8 ,AF9 ,AF10 ,AF11 ,AF12 ,AF13 ,AF14 ,AF15 ,ADC + , ,SYS ,TIM1/2 ,TIM3/4/5,TIM8/9/10/11,I2C1/2/3 ,SPI1/2/3/4/5/6 ,SPI2/3/SAI1 ,SPI3/USART1/2/3,USART6/UART4/5/7/8,CAN1/2/TIM12/13/14/LCD,OTG2_HS/OTG1_FS,ETH ,FMC/SDIO/OTG2_FS ,DCMI ,LCD ,SYS , +PortA,PA0 , ,TIM2_CH1/TIM2_ETR,TIM5_CH1,TIM8_ETR , , , ,USART2_CTS ,UART4_TX , , ,ETH_MII_CRS , , , ,EVENTOUT,ADC123_IN0 +PortA,PA1 , ,TIM2_CH2 ,TIM5_CH2, , , , ,USART2_RTS ,UART4_RX , , ,ETH_MII_RX_CLK/ETH_RMII_REF_CLK, , , ,EVENTOUT,ADC123_IN1 +PortA,PA2 , ,TIM2_CH3 ,TIM5_CH3,TIM9_CH1 , , , ,USART2_TX , , , ,ETH_MDIO , , , ,EVENTOUT,ADC123_IN2 +PortA,PA3 , ,TIM2_CH4 ,TIM5_CH4,TIM9_CH2 , , , ,USART2_RX , , ,OTG_HS_ULPI_D0 ,ETH_MII_COL , , ,LCD_B5 ,EVENTOUT,ADC123_IN3 +PortA,PA4 , , , , , ,SPI1_NSS ,SPI3_NSS/I2S3_WS ,USART2_CK , , , , ,OTG_HS_SOF ,DCMI_HSYNC ,LCD_VSYNC,EVENTOUT,ADC12_IN4 +PortA,PA5 , ,TIM2_CH1/TIM2_ETR, ,TIM8_CH1N , ,SPI1_SCK , , , , ,OTG_HS_ULPI_CK , , , , ,EVENTOUT,ADC12_IN5 +PortA,PA6 , ,TIM1_BKIN ,TIM3_CH1,TIM8_BKIN , ,SPI1_MISO , , , ,TIM13_CH1 , , , ,DCMI_PIXCLK,LCD_G2 ,EVENTOUT,ADC12_IN6 +PortA,PA7 , ,TIM1_CH1N ,TIM3_CH2,TIM8_CH1N , ,SPI1_MOSI , , , ,TIM14_CH1 , ,ETH_MII_RX_DV/ETH_RMII_CRS_DV , , , ,EVENTOUT,ADC12_IN7 +PortA,PA8 ,MCO1 ,TIM1_CH1 , , ,I2C3_SCL , , ,USART1_CK , , ,OTG_FS_SOF , , , ,LCD_R6 ,EVENTOUT, +PortA,PA9 , ,TIM1_CH2 , , ,I2C3_SMBA, , ,USART1_TX , , , , , ,DCMI_D0 , ,EVENTOUT, +PortA,PA10, ,TIM1_CH3 , , , , , ,USART1_RX , , ,OTG_FS_ID , , ,DCMI_D1 , ,EVENTOUT, +PortA,PA11, ,TIM1_CH4 , , , , , ,USART1_CTS , ,CAN1_RX ,OTG_FS_DM , , , ,LCD_R4 ,EVENTOUT, +PortA,PA12, ,TIM1_ETR , , , , , ,USART1_RTS , ,CAN1_TX ,OTG_FS_DP , , , ,LCD_R5 ,EVENTOUT, +PortA,PA13,JTMS/SWDIO , , , , , , , , , , , , , , ,EVENTOUT, +PortA,PA14,JTCK/SWCLK , , , , , , , , , , , , , , ,EVENTOUT, +PortA,PA15,JTDI ,TIM2_CH1/TIM2_ETR, , , ,SPI1_NSS ,SPI3_NSS/I2S3_WS , , , , , , , , ,EVENTOUT, +PortB,PB0 , ,TIM1_CH2N ,TIM3_CH3,TIM8_CH2N , , , , , ,LCD_R3 ,OTG_HS_ULPI_D1 ,ETH_MII_RXD2 , , , ,EVENTOUT,ADC12_IN8 +PortB,PB1 , ,TIM1_CH3N ,TIM3_CH4,TIM8_CH3N , , , , , ,LCD_R6 ,OTG_HS_ULPI_D2 ,ETH_MII_RXD3 , , , ,EVENTOUT,ADC12_IN9 +PortB,PB2 , , , , , , , , , , , , , , , ,EVENTOUT, +PortB,PB3 ,JTDO/TRACESWO,TIM2_CH2 , , , ,SPI1_SCK ,SPI3_SCK/I2S3_CK , , , , , , , , ,EVENTOUT, +PortB,PB4 ,NJTRST , ,TIM3_CH1, , ,SPI1_MISO ,SPI3_MISO ,I2S3ext_SD , , , , , , , ,EVENTOUT, +PortB,PB5 , , ,TIM3_CH2, ,I2C1_SMBA,SPI1_MOSI ,SPI3_MOSI/I2S3_SD, , ,CAN2_RX ,OTG_HS_ULPI_D7 ,ETH_PPS_OUT ,FMC_SDCKE1 ,DCMI_D10 , ,EVENTOUT, +PortB,PB6 , , ,TIM4_CH1, ,I2C1_SCL , , ,USART1_TX , ,CAN2_TX , , ,FMC_SDNE1 ,DCMI_D5 , ,EVENTOUT, +PortB,PB7 , , ,TIM4_CH2, ,I2C1_SDA , , ,USART1_RX , , , , ,FMC_NL ,DCMI_VSYNC , ,EVENTOUT, +PortB,PB8 , , ,TIM4_CH3,TIM10_CH1 ,I2C1_SCL , , , , ,CAN1_RX , ,ETH_MII_TXD3 ,SDIO_D4 ,DCMI_D6 ,LCD_B6 ,EVENTOUT, +PortB,PB9 , , ,TIM4_CH4,TIM11_CH1 ,I2C1_SDA ,SPI2_NSS/I2S2_WS , , , ,CAN1_TX , , ,SDIO_D5 ,DCMI_D7 ,LCD_B7 ,EVENTOUT, +PortB,PB10, ,TIM2_CH3 , , ,I2C2_SCL ,SPI2_SCK/I2S2_CK , ,USART3_TX , , ,OTG_HS_ULPI_D3 ,ETH_MII_RX_ER , , ,LCD_G4 ,EVENTOUT, +PortB,PB11, ,TIM2_CH4 , , ,I2C2_SDA , , ,USART3_RX , , ,OTG_HS_ULPI_D4 ,ETH_MII_TX_EN/ETH_RMII_TX_EN , , ,LCD_G5 ,EVENTOUT, +PortB,PB12, ,TIM1_BKIN , , ,I2C2_SMBA,SPI2_NSS/I2S2_WS , ,USART3_CK , ,CAN2_RX ,OTG_HS_ULPI_D5 ,ETH_MII_TXD0/ETH_RMII_TXD0 ,OTG_HS_ID , , ,EVENTOUT, +PortB,PB13, ,TIM1_CH1N , , , ,SPI2_SCK/I2S2_CK , ,USART3_CTS , ,CAN2_TX ,OTG_HS_ULPI_D6 ,ETH_MII_TXD1/ETH_RMII_TXD1 , , , ,EVENTOUT, +PortB,PB14, ,TIM1_CH2N , ,TIM8_CH2N , ,SPI2_MISO ,I2S2ext_SD ,USART3_RTS , ,TIM12_CH1 , , ,OTG_HS_DM , , ,EVENTOUT, +PortB,PB15,RTC_REFIN ,TIM1_CH3N , ,TIM8_CH3N , ,SPI2_MOSI/I2S2_SD, , , ,TIM12_CH2 , , ,OTG_HS_DP , , ,EVENTOUT, +PortC,PC0 , , , , , , , , , , ,OTG_HS_ULPI_STP, ,FMC_SDNWE , , ,EVENTOUT,ADC123_IN10 +PortC,PC1 , , , , , , , , , , , ,ETH_MDC , , , ,EVENTOUT,ADC123_IN11 +PortC,PC2 , , , , , ,SPI2_MISO ,I2S2ext_SD , , , ,OTG_HS_ULPI_DIR,ETH_MII_TXD2 ,FMC_SDNE0 , , ,EVENTOUT,ADC123_IN12 +PortC,PC3 , , , , , ,SPI2_MOSI/I2S2_SD, , , , ,OTG_HS_ULPI_NXT,ETH_MII_TX_CLK ,FMC_SDCKE0 , , ,EVENTOUT,ADC123_IN13 +PortC,PC4 , , , , , , , , , , , ,ETH_MII_RXD0/ETH_RMII_RXD0 , , , ,EVENTOUT,ADC12_IN14 +PortC,PC5 , , , , , , , , , , , ,ETH_MII_RXD1/ETH_RMII_RXD1 , , , ,EVENTOUT,ADC12_IN15 +PortC,PC6 , , ,TIM3_CH1,TIM8_CH1 , ,I2S2_MCK , , ,USART6_TX , , , ,SDIO_D6 ,DCMI_D0 ,LCD_HSYNC,EVENTOUT, +PortC,PC7 , , ,TIM3_CH2,TIM8_CH2 , , ,I2S3_MCK , ,USART6_RX , , , ,SDIO_D7 ,DCMI_D1 ,LCD_G6 ,EVENTOUT, +PortC,PC8 , , ,TIM3_CH3,TIM8_CH3 , , , , ,USART6_CK , , , ,SDIO_D0 ,DCMI_D2 , ,EVENTOUT, +PortC,PC9 ,MCO2 , ,TIM3_CH4,TIM8_CH4 ,I2C3_SDA ,I2S_CKIN , , , , , , ,SDIO_D1 ,DCMI_D3 , ,EVENTOUT, +PortC,PC10, , , , , , ,SPI3_SCK/I2S3_CK ,USART3_TX ,UART4_TX , , , ,SDIO_D2 ,DCMI_D8 ,LCD_R2 ,EVENTOUT, +PortC,PC11, , , , , ,I2S3ext_SD ,SPI3_MISO ,USART3_RX ,UART4_RX , , , ,SDIO_D3 ,DCMI_D4 , ,EVENTOUT, +PortC,PC12, , , , , , ,SPI3_MOSI/I2S3_SD,USART3_CK ,UART5_TX , , , ,SDIO_CK ,DCMI_D9 , ,EVENTOUT, +PortC,PC13, , , , , , , , , , , , , , , ,EVENTOUT, +PortC,PC14, , , , , , , , , , , , , , , ,EVENTOUT, +PortC,PC15, , , , , , , , , , , , , , , ,EVENTOUT, +PortD,PD0 , , , , , , , , , ,CAN1_RX , , ,FMC_D2 , , ,EVENTOUT, +PortD,PD1 , , , , , , , , , ,CAN1_TX , , ,FMC_D3 , , ,EVENTOUT, +PortD,PD2 , , ,TIM3_ETR, , , , , ,UART5_RX , , , ,SDIO_CMD ,DCMI_D11 , ,EVENTOUT, +PortD,PD3 , , , , , ,SPI2_SCK/I2S2_CK , ,USART2_CTS , , , , ,FMC_CLK ,DCMI_D5 ,LCD_G7 ,EVENTOUT, +PortD,PD4 , , , , , , , ,USART2_RTS , , , , ,FMC_NOE , , ,EVENTOUT, +PortD,PD5 , , , , , , , ,USART2_TX , , , , ,FMC_NWE , , ,EVENTOUT, +PortD,PD6 , , , , , ,I2S3_SD ,SAI1_SD_A ,USART2_RX , , , , ,FMC_NWAIT ,DCMI_D10 ,LCD_B2 ,EVENTOUT, +PortD,PD7 , , , , , , , ,USART2_CK , , , , ,FMC_NE1/FMC_NCE2 , , ,EVENTOUT, +PortD,PD8 , , , , , , , ,USART3_TX , , , , ,FMC_D13 , , ,EVENTOUT, +PortD,PD9 , , , , , , , ,USART3_RX , , , , ,FMC_D14 , , ,EVENTOUT, +PortD,PD10, , , , , , , ,USART3_CK , , , , ,FMC_D15 , ,LCD_B3 ,EVENTOUT, +PortD,PD11, , , , , , , ,USART3_CTS , , , , ,FMC_A16 , , ,EVENTOUT, +PortD,PD12, , ,TIM4_CH1, , , , ,USART3_RTS , , , , ,FMC_A17 , , ,EVENTOUT, +PortD,PD13, , ,TIM4_CH2, , , , , , , , , ,FMC_A18 , , ,EVENTOUT, +PortD,PD14, , ,TIM4_CH3, , , , , , , , , ,FMC_D0 , , ,EVENTOUT, +PortD,PD15, , ,TIM4_CH4, , , , , , , , , ,FMC_D1 , , ,EVENTOUT, +PortE,PE0 , , ,TIM4_ETR, , , , , ,UART8_RX , , , ,FMC_NBL0 ,DCMI_D2 , ,EVENTOUT, +PortE,PE1 , , , , , , , , ,UART8_TX , , , ,FMC_NBL1 ,DCMI_D3 , ,EVENTOUT, +PortE,PE2 ,TRACECLK , , , , ,SPI4_SCK ,SAI1_MCLK_A , , , , ,ETH_MII_TXD3 ,FMC_A23 , , ,EVENTOUT, +PortE,PE3 ,TRACED0 , , , , , ,SAI1_SD_B , , , , , ,FMC_A19 , , ,EVENTOUT, +PortE,PE4 ,TRACED1 , , , , ,SPI4_NSS ,SAI1_FS_A , , , , , ,FMC_A20 ,DCMI_D4 ,LCD_B0 ,EVENTOUT, +PortE,PE5 ,TRACED2 , , ,TIM9_CH1 , ,SPI4_MISO ,SAI1_SCK_A , , , , , ,FMC_A21 ,DCMI_D6 ,LCD_G0 ,EVENTOUT, +PortE,PE6 ,TRACED3 , , ,TIM9_CH2 , ,SPI4_MOSI ,SAI1_SD_A , , , , , ,FMC_A22 ,DCMI_D7 ,LCD_G1 ,EVENTOUT, +PortE,PE7 , ,TIM1_ETR , , , , , , ,UART7_RX , , , ,FMC_D4 , , ,EVENTOUT, +PortE,PE8 , ,TIM1_CH1N , , , , , , ,UART7_TX , , , ,FMC_D5 , , ,EVENTOUT, +PortE,PE9 , ,TIM1_CH1 , , , , , , , , , , ,FMC_D6 , , ,EVENTOUT, +PortE,PE10, ,TIM1_CH2N , , , , , , , , , , ,FMC_D7 , , ,EVENTOUT, +PortE,PE11, ,TIM1_CH2 , , , ,SPI4_NSS ,SPI3_NSS , , , , , ,FMC_D8 , ,LCD_G3 ,EVENTOUT, +PortE,PE12, ,TIM1_CH3N , , , ,SPI4_SCK ,SPI3_SCK , , , , , ,FMC_D9 , ,LCD_B4 ,EVENTOUT, +PortE,PE13, ,TIM1_CH3 , , , ,SPI4_MISO ,SPI3_MISO , , , , , ,FMC_D10 , ,LCD_DE ,EVENTOUT, +PortE,PE14, ,TIM1_CH4 , , , ,SPI4_MOSI ,SP3_MOSI , , , , , ,FMC_D11 , ,LCD_CLK ,EVENTOUT, +PortE,PE15, ,TIM1_BKIN , , , , , , , , , , ,FMC_D12 , ,LCD_R7 ,EVENTOUT, +PortF,PF0 , , , , ,I2C2_SDA , , , , , , , ,FMC_A0 , , ,EVENTOUT, +PortF,PF1 , , , , ,I2C2_SCL , , , , , , , ,FMC_A1 , , ,EVENTOUT, +PortF,PF2 , , , , ,I2C2_SMBA, , , , , , , ,FMC_A2 , , ,EVENTOUT, +PortF,PF3 , , , , , , , , , , , , ,FMC_A3 , , ,EVENTOUT,ADC3_IN9 +PortF,PF4 , , , , , , , , , , , , ,FMC_A4 , , ,EVENTOUT,ADC3_IN14 +PortF,PF5 , , , , , , , , , , , , ,FMC_A5 , , ,EVENTOUT,ADC3_IN15 +PortF,PF6 , , , ,TIM10_CH1 , ,SPI5_NSS ,SAI1_SD_B , ,UART7_RX , , , ,FMC_NIORD , , ,EVENTOUT,ADC3_IN4 +PortF,PF7 , , , ,TIM11_CH1 , ,SPI5_SCK ,SAI1_MCLK_B , ,UART7_TX , , , ,FMC_NREG , , ,EVENTOUT,ADC3_IN5 +PortF,PF8 , , , , , ,SPI5_MISO ,SAI1_SCK_B , , ,TIM13_CH1 , , ,FMC_NIOWR , , ,EVENTOUT,ADC3_IN6 +PortF,PF9 , , , , , ,SPI5_MOSI ,SAI1_FS_B , , ,TIM14_CH1 , , ,FMC_CD , , ,EVENTOUT,ADC3_IN7 +PortF,PF10, , , , , , , , , , , , ,FMC_INTR ,DCMI_D11 ,LCD_DE ,EVENTOUT,ADC3_IN8 +PortF,PF11, , , , , ,SPI5_MOSI , , , , , , ,FMC_SDNRAS ,DCMI_D12 , ,EVENTOUT, +PortF,PF12, , , , , , , , , , , , ,FMC_A6 , , ,EVENTOUT, +PortF,PF13, , , , , , , , , , , , ,FMC_A7 , , ,EVENTOUT, +PortF,PF14, , , , , , , , , , , , ,FMC_A8 , , ,EVENTOUT, +PortF,PF15, , , , , , , , , , , , ,FMC_A9 , , ,EVENTOUT, +PortG,PG0 , , , , , , , , , , , , ,FMC_A10 , , ,EVENTOUT, +PortG,PG1 , , , , , , , , , , , , ,FMC_A11 , , ,EVENTOUT, +PortG,PG2 , , , , , , , , , , , , ,FMC_A12 , , ,EVENTOUT, +PortG,PG3 , , , , , , , , , , , , ,FMC_A13 , , ,EVENTOUT, +PortG,PG4 , , , , , , , , , , , , ,FMC_A14/FMC_BA0 , , ,EVENTOUT, +PortG,PG5 , , , , , , , , , , , , ,FMC_A15/FMC_BA1 , , ,EVENTOUT, +PortG,PG6 , , , , , , , , , , , , ,FMC_INT2 ,DCMI_D12 ,LCD_R7 ,EVENTOUT, +PortG,PG7 , , , , , , , , ,USART6_CK , , , ,FMC_INT3 ,DCMI_D13 ,LCD_CLK ,EVENTOUT, +PortG,PG8 , , , , , ,SPI6_NSS , , ,USART6_RTS , , ,ETH_PPS_OUT ,FMC_SDCLK , , ,EVENTOUT, +PortG,PG9 , , , , , , , , ,USART6_RX , , , ,FMC_NE2/FMC_NCE3 ,DCMI_VSYNC , ,EVENTOUT, +PortG,PG10, , , , , , , , , ,LCD_G3 , , ,FMC_NCE4_1/FMC_NE3,DCMI_D2 ,LCD_B2 ,EVENTOUT, +PortG,PG11, , , , , , , , , , , ,ETH_MII_TX_EN/ETH_RMII_TX_EN ,FMC_NCE4_2 ,DCMI_D3 ,LCD_B3 ,EVENTOUT, +PortG,PG12, , , , , ,SPI6_MISO , , ,USART6_RTS ,LCD_B4 , , ,FMC_NE4 , ,LCD_B1 ,EVENTOUT, +PortG,PG13, , , , , ,SPI6_SCK , , ,USART6_CTS , , ,ETH_MII_TXD0/ETH_RMII_TXD0 ,FMC_A24 , , ,EVENTOUT, +PortG,PG14, , , , , ,SPI6_MOSI , , ,USART6_TX , , ,ETH_MII_TXD1/ETH_RMII_TXD1 ,FMC_A25 , , ,EVENTOUT, +PortG,PG15, , , , , , , , ,USART6_CTS , , , ,FMC_SDNCAS ,DCMI_D13 , ,EVENTOUT, +PortH,PH0 , , , , , , , , , , , , , , , ,EVENTOUT, +PortH,PH1 , , , , , , , , , , , , , , , ,EVENTOUT, +PortH,PH2 , , , , , , , , , , , ,ETH_MII_CRS ,FMC_SDCKE0 , ,LCD_R0 ,EVENTOUT, +PortH,PH3 , , , , , , , , , , , ,ETH_MII_COL ,FMC_SDNE0 , ,LCD_R1 ,EVENTOUT, +PortH,PH4 , , , , ,I2C2_SCL , , , , , ,OTG_HS_ULPI_NXT, , , , ,EVENTOUT, +PortH,PH5 , , , , ,I2C2_SDA ,SPI5_NSS , , , , , , ,FMC_SDNWE , , ,EVENTOUT, +PortH,PH6 , , , , ,I2C2_SMBA,SPI5_SCK , , , ,TIM12_CH1 , , ,FMC_SDNE1 ,DCMI_D8 , , , +PortH,PH7 , , , , ,I2C3_SCL ,SPI5_MISO , , , , , ,ETH_MII_RXD3 ,FMC_SDCKE1 ,DCMI_D9 , , , +PortH,PH8 , , , , ,I2C3_SDA , , , , , , , ,FMC_D16 ,DCMI_HSYNC ,LCD_R2 ,EVENTOUT, +PortH,PH9 , , , , ,I2C3_SMBA, , , , ,TIM12_CH2 , , ,FMC_D17 ,DCMI_D0 ,LCD_R3 ,EVENTOUT, +PortH,PH10, , ,TIM5_CH1, , , , , , , , , ,FMC_D18 ,DCMI_D1 ,LCD_R4 ,EVENTOUT, +PortH,PH11, , ,TIM5_CH2, , , , , , , , , ,FMC_D19 ,DCMI_D2 ,LCD_R5 ,EVENTOUT, +PortH,PH12, , ,TIM5_CH3, , , , , , , , , ,FMC_D20 ,DCMI_D3 ,LCD_R6 ,EVENTOUT, +PortH,PH13, , , ,TIM8_CH1N , , , , , ,CAN1_TX , , ,FMC_D21 , ,LCD_G2 ,EVENTOUT, +PortH,PH14, , , ,TIM8_CH2N , , , , , , , , ,FMC_D22 ,DCMI_D4 ,LCD_G3 ,EVENTOUT, +PortH,PH15, , , ,TIM8_CH3N , , , , , , , , ,FMC_D23 ,DCMI_D11 ,LCD_G4 ,EVENTOUT, +PortI,PI0 , , ,TIM5_CH4, , ,SPI2_NSS/I2S2_WS , , , , , , ,FMC_D24 ,DCMI_D13 ,LCD_G5 ,EVENTOUT, +PortI,PI1 , , , , , ,SPI2_SCK/I2S2_CK , , , , , , ,FMC_D25 ,DCMI_D8 ,LCD_G6 ,EVENTOUT, +PortI,PI2 , , , ,TIM8_CH4 , ,SPI2_MISO ,I2S2ext_SD , , , , , ,FMC_D26 ,DCMI_D9 ,LCD_G7 ,EVENTOUT, +PortI,PI3 , , , ,TIM8_ETR , ,SPI2_MOSI/I2S2_SD, , , , , , ,FMC_D27 ,DCMI_D10 , ,EVENTOUT, +PortI,PI4 , , , ,TIM8_BKIN , , , , , , , , ,FMC_NBL2 ,DCMI_D5 ,LCD_B4 ,EVENTOUT, +PortI,PI5 , , , ,TIM8_CH1 , , , , , , , , ,FMC_NBL3 ,DCMI_VSYNC ,LCD_B5 ,EVENTOUT, +PortI,PI6 , , , ,TIM8_CH2 , , , , , , , , ,FMC_D28 ,DCMI_D6 ,LCD_B6 ,EVENTOUT, +PortI,PI7 , , , ,TIM8_CH3 , , , , , , , , ,FMC_D29 ,DCMI_D7 ,LCD_B7 ,EVENTOUT, +PortI,PI8 , , , , , , , , , , , , , , , ,EVENTOUT, +PortI,PI9 , , , , , , , , , ,CAN1_RX , , ,FMC_D30 , ,LCD_VSYNC,EVENTOUT, +PortI,PI10, , , , , , , , , , , ,ETH_MII_RX_ER ,FMC_D31 , ,LCD_HSYNC,EVENTOUT, +PortI,PI11, , , , , , , , , , ,OTG_HS_ULPI_DIR, , , , ,EVENTOUT, +PortI,PI12, , , , , , , , , , , , , , ,LCD_HSYNC,EVENTOUT, +PortI,PI13, , , , , , , , , , , , , , ,LCD_VSYNC,EVENTOUT, +PortI,PI14, , , , , , , , , , , , , , ,LCD_CLK ,EVENTOUT, +PortI,PI15, , , , , , , , , , , , , , ,LCD_R0 ,EVENTOUT, +PortJ,PJ0 , , , , , , , , , , , , , , ,LCD_R1 ,EVENTOUT, +PortJ,PJ1 , , , , , , , , , , , , , , ,LCD_R2 ,EVENTOUT, +PortJ,PJ2 , , , , , , , , , , , , , , ,LCD_R3 ,EVENTOUT, +PortJ,PJ3 , , , , , , , , , , , , , , ,LCD_R4 ,EVENTOUT, +PortJ,PJ4 , , , , , , , , , , , , , , ,LCD_R5 ,EVENTOUT, +PortJ,PJ5 , , , , , , , , , , , , , , ,LCD_R6 ,EVENTOUT, +PortJ,PJ6 , , , , , , , , , , , , , , ,LCD_R7 ,EVENTOUT, +PortJ,PJ7 , , , , , , , , , , , , , , ,LCD_G0 ,EVENTOUT, +PortJ,PJ8 , , , , , , , , , , , , , , ,LCD_G1 ,EVENTOUT, +PortJ,PJ9 , , , , , , , , , , , , , , ,LCD_G2 ,EVENTOUT, +PortJ,PJ10, , , , , , , , , , , , , , ,LCD_G3 ,EVENTOUT, +PortJ,PJ11, , , , , , , , , , , , , , ,LCD_G4 ,EVENTOUT, +PortJ,PJ12, , , , , , , , , , , , , , ,LCD_B0 ,EVENTOUT, +PortJ,PJ13, , , , , , , , , , , , , , ,LCD_B1 ,EVENTOUT, +PortJ,PJ14, , , , , , , , , , , , , , ,LCD_B2 ,EVENTOUT, +PortJ,PJ15, , , , , , , , , , , , , , ,LCD_B3 ,EVENTOUT, +PortK,PK0 , , , , , , , , , , , , , , ,LCD_G5 ,EVENTOUT, +PortK,PK1 , , , , , , , , , , , , , , ,LCD_G6 ,EVENTOUT, +PortK,PK2 , , , , , , , , , , , , , , ,LCD_G7 ,EVENTOUT, +PortK,PK3 , , , , , , , , , , , , , , ,LCD_B4 ,EVENTOUT, +PortK,PK4 , , , , , , , , , , , , , , ,LCD_B5 ,EVENTOUT, +PortK,PK5 , , , , , , , , , , , , , , ,LCD_B6 ,EVENTOUT, +PortK,PK6 , , , , , , , , , , , , , , ,LCD_B7 ,EVENTOUT, +PortK,PK7 , , , , , , , , , , , , , , ,LCD_DE ,EVENTOUT, From 09c9c8ac3038bba46fa7b57634eff3ed646bacb4 Mon Sep 17 00:00:00 2001 From: Jim Mussared Date: Sat, 4 Nov 2023 11:00:06 +1100 Subject: [PATCH 10/37] stm32/boards/stm32g474_af.csv: Fix final row ADC column. The original file was missing a trailing , on the final row. Signed-off-by: Jim Mussared --- ports/stm32/boards/stm32g474_af.csv | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/stm32/boards/stm32g474_af.csv b/ports/stm32/boards/stm32g474_af.csv index 34ab285837..5853f5f900 100644 --- a/ports/stm32/boards/stm32g474_af.csv +++ b/ports/stm32/boards/stm32g474_af.csv @@ -106,4 +106,4 @@ PortG,PG6 , , ,TIM20_BKIN PortG,PG7 , , , ,SAI1_CK1 ,I2C3_SCL , , , ,LPUART1_TX , , , ,FMC_INT ,SAI1_MCLK_A , ,EVENTOUT, PortG,PG8 , , , , ,I2C3_SDA , , , ,LPUART1_RX , , , ,FMC_NE3 , , ,EVENTOUT, PortG,PG9 , , , , , , ,SPI3_SCK ,USART1_TX , , , , ,FMC_NCE/FMC_NE2 , ,TIM15_CH1N ,EVENTOUT, -PortG,PG10,MCO , , , , , , , , , , , , , , , ,EVENTOUT +PortG,PG10,MCO , , , , , , , , , , , , , , ,EVENTOUT, From ae3b1cfab1387c4d875ec67bdeda885f87e52967 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Fri, 3 Nov 2023 22:20:06 +0100 Subject: [PATCH 11/37] mimxrt/modmachine: Fix settings for the MIMXRT1170 board. These were not changed with commit c0b64a3f2 for using tools/boardgen.py. Signed-off-by: robert-hh --- ports/mimxrt/modmachine.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ports/mimxrt/modmachine.c b/ports/mimxrt/modmachine.c index aa12f44d47..57090bda27 100644 --- a/ports/mimxrt/modmachine.c +++ b/ports/mimxrt/modmachine.c @@ -121,10 +121,10 @@ STATIC mp_obj_t machine_deepsleep(size_t n_args, const mp_obj_t *args) { } #ifdef MIMXRT117x_SERIES - machine_pin_config(&pin_WAKEUP_DIG, PIN_MODE_IT_RISING, PIN_PULL_DISABLED, PIN_DRIVE_OFF, 0, PIN_AF_MODE_ALT5); + machine_pin_config(pin_WAKEUP_DIG, PIN_MODE_IT_RISING, PIN_PULL_DISABLED, PIN_DRIVE_OFF, 0, PIN_AF_MODE_ALT5); GPC_CM_EnableIrqWakeup(GPC_CPU_MODE_CTRL_0, GPIO13_Combined_0_31_IRQn, true); #elif defined IOMUXC_SNVS_WAKEUP_GPIO5_IO00 - machine_pin_config(&pin_WAKEUP, PIN_MODE_IT_RISING, PIN_PULL_DISABLED, PIN_DRIVE_OFF, 0, PIN_AF_MODE_ALT5); + machine_pin_config(pin_WAKEUP, PIN_MODE_IT_RISING, PIN_PULL_DISABLED, PIN_DRIVE_OFF, 0, PIN_AF_MODE_ALT5); GPC_EnableIRQ(GPC, GPIO5_Combined_0_15_IRQn); #endif From e63d7189bc2b6dfa1e8f5a799d9f17e17f792155 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Sat, 4 Nov 2023 15:20:31 +0100 Subject: [PATCH 12/37] docs/mimxrt: Change the examples which denote a Pin with a number. This option was removed in PR #12211. Signed-off-by: robert-hh --- docs/mimxrt/quickref.rst | 44 +++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 23 deletions(-) diff --git a/docs/mimxrt/quickref.rst b/docs/mimxrt/quickref.rst index ab8bf8831b..578364c55a 100644 --- a/docs/mimxrt/quickref.rst +++ b/docs/mimxrt/quickref.rst @@ -92,9 +92,7 @@ Use the :ref:`machine.Pin ` class:: Available Pins follow the ranges and labelling of the respective board, like: -- 0-33 for Teensy 4.0, -- 0-21 for the MIMXRT10xx-EVK board, or 'D0-Dxx', or 'A0-Ann', -- 0-14 for the Olimex RT1010Py board, or 'D0'-'Dxx' and 'A0'-'Ann' +- 'D0-Dxx', or 'A0-Ann' for Teensy 4.0, MIMXRT10xx-EVK ns Olimex board, - 'J3_xx', 'J4_xx', 'J5_xx' for the Seeed ARCH MIX board, or the pin names of the Pin.board or Pin.cpu classes. @@ -106,9 +104,9 @@ Notes: * At the MIMXRT1010_EVK, pins D4, D5 and D9 of the Arduino connector are by default not connected to the MCU. For details refer to the schematics. * At the MIMXRT1170_EVK board, the inner rows of the Arduino connectors are assigned as follows: - - D16 - D23: J9, odd pin numbers; D17 is by default not connected. - - D24 - D27: J26, odd pin numbers; J63-J66 have to be closed to enable these pins. - - D29 - D36: J25, odd pin numbers; D29 and D30 are by default not connected. + - 'D16' - 'D23': J9, odd pin numbers; 'D17' is by default not connected. + - 'D24' - 'D27': J26, odd pin numbers; J63-J66 have to be closed to enable these pins. + - 'D29' - 'D36': J25, odd pin numbers; 'D29' and 'D30' are by default not connected. There's a higher-level abstraction :ref:`machine.Signal ` which can be used to invert a pin. Useful for illuminating active-low LEDs @@ -146,22 +144,22 @@ handling signal groups. :: from machine import Pin, PWM # create PWM object from a pin and set the frequency and duty cycle - pwm2 = PWM(Pin(2), freq=2000, duty_u16=32768) + pwm2 = PWM(Pin('D2'), freq=2000, duty_u16=32768) pwm2.freq() # get the current frequency pwm2.freq(1000) # set/change the frequency pwm2.duty_u16() # get the current duty cycle, range 0-65535 pwm2.duty_u16(200) # set the duty cycle, range 0-65535 pwm2.deinit() # turn off PWM on the pin # create a complementary signal pair on Pin 2 and 3 - pwm2 = PWM((2, 3), freq=2000, duty_ns=20000) + pwm2 = PWM(('D2', 'D3'), freq=2000, duty_ns=20000) # Create a group of four synchronized signals. - # Start with Pin(4) at submodule 0, which creates the sync pulse. - pwm4 = PWM(Pin(4), freq=1000, align=PWM.HEAD) - # Pins 5, 6, and 9 are pins at the same module - pwm5 = PWM(Pin(5), freq=1000, duty_u16=10000, align=PWM.HEAD, sync=True) - pwm6 = PWM(Pin(6), freq=1000, duty_u16=20000, align=PWM.HEAD, sync=True) - pwm9 = PWM(Pin(9), freq=1000, duty_u16=30000, align=PWM.HEAD, sync=True) + # Start with Pin('D4') at submodule 0, which creates the sync pulse. + pwm4 = PWM(Pin('D4'), freq=1000, align=PWM.HEAD) + # Pins D5, D6, and D9 are pins at the same module + pwm5 = PWM(Pin('D5'), freq=1000, duty_u16=10000, align=PWM.HEAD, sync=True) + pwm6 = PWM(Pin('D6', freq=1000, duty_u16=20000, align=PWM.HEAD, sync=True) + pwm9 = PWM(Pin('D9'), freq=1000, duty_u16=30000, align=PWM.HEAD, sync=True) pwm3 # show the PWM objects properties @@ -256,7 +254,7 @@ Use the :ref:`machine.ADC ` class:: from machine import ADC - adc = ADC(Pin(32)) # create ADC object on ADC pin + adc = ADC(Pin('A2')) # create ADC object on ADC pin adc.read_u16() # read value, 0-65536 across voltage range 0.0v - 3.3v The resolution of the ADC is 12 bit with 10 to 11 bit accuracy, irrespective of the @@ -274,7 +272,7 @@ Software SPI (using bit-banging) works on all pins, and is accessed via the # construct a SoftSPI bus on the given pins # polarity is the idle state of SCK # phase=0 means sample on the first edge of SCK, phase=1 means the second - spi = SoftSPI(baudrate=100000, polarity=1, phase=0, sck=Pin(0), mosi=Pin(2), miso=Pin(4)) + spi = SoftSPI(baudrate=100000, polarity=1, phase=0, sck=Pin('D0'), mosi=Pin('D2'), miso=Pin('D4')) spi.init(baudrate=200000) # set the baudrate @@ -303,7 +301,7 @@ rates (up to 30Mhz). Hardware SPI is accessed via the from machine import SPI, Pin spi = SPI(0, 10000000) - cs_pin = Pin(6, Pin.OUT, value=1) + cs_pin = Pin('D6', Pin.OUT, value=1) cs_pin(0) spi.write('Hello World') cs_pin(1) @@ -331,7 +329,7 @@ accessed via the :ref:`machine.SoftI2C ` class:: from machine import Pin, SoftI2C - i2c = SoftI2C(scl=Pin(5), sda=Pin(4), freq=100000) + i2c = SoftI2C(scl=Pin('D5'), sda=Pin('D4'), freq=100000) i2c.scan() # scan for devices @@ -365,7 +363,7 @@ See :ref:`machine.I2S `. Example using a Teensy 4.1 board with a si external Codec like UDA1334.:: from machine import I2S, Pin - i2s = I2S(2, sck=Pin(26), ws=Pin(27), sd=Pin(7), + i2s = I2S(2, sck=Pin('D26'), ws=Pin('D27'), sd=Pin('D7'), mode=I2S.TX, bts=16,format=I2S.STEREO, rate=44100,ibuf=40000) i2s.write(buf) # write buffer of audio samples to I2S device @@ -397,7 +395,7 @@ Example using the Teensy audio shield:: from machine import I2C, I2S, Pin from sgtl5000 import CODEC - i2s = I2S(1, sck=Pin(21), ws=Pin(20), sd=Pin(7), mck=Pin(23), + i2s = I2S(1, sck=Pin('D21'), ws=Pin('D20'), sd=Pin('D7'), mck=Pin('D23'), mode=I2S.TX, bits=16,rate=44100,format=I2S.STEREO, ibuf=40000, ) @@ -475,7 +473,7 @@ The OneWire driver is implemented in software and works on all pins:: from machine import Pin import onewire - ow = onewire.OneWire(Pin(12)) # create a OneWire bus on GPIO12 + ow = onewire.OneWire(Pin('D12')) # create a OneWire bus on GPIO12 ow.scan() # return a list of devices on the bus ow.reset() # reset the bus ow.readbyte() # read a byte @@ -505,12 +503,12 @@ The DHT driver is implemented in software and works on all pins:: import dht import machine - d = dht.DHT11(machine.Pin(4)) + d = dht.DHT11(machine.Pin('D4')) d.measure() d.temperature() # eg. 23 (Ā°C) d.humidity() # eg. 41 (% RH) - d = dht.DHT22(machine.Pin(4)) + d = dht.DHT22(machine.Pin('D4')) d.measure() d.temperature() # eg. 23.6 (Ā°C) d.humidity() # eg. 41.3 (% RH) From 47ed06bda21799b3378091c2a41a47b64187dfc0 Mon Sep 17 00:00:00 2001 From: Jim Mussared Date: Sat, 4 Nov 2023 11:08:37 +1100 Subject: [PATCH 13/37] stm32/boards/make-pins.py: Only support ADC1-3. e.g. The STM32G4 includes ADC4 & ADC5 which is not currently supported by the stm32 driver. Signed-off-by: Jim Mussared --- ports/stm32/boards/make-pins.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/ports/stm32/boards/make-pins.py b/ports/stm32/boards/make-pins.py index 9f9950ff17..bfa22d7974 100755 --- a/ports/stm32/boards/make-pins.py +++ b/ports/stm32/boards/make-pins.py @@ -49,6 +49,11 @@ PinAf = namedtuple( ], ) +# Only support ADC1, ADC2, ADC3 for now (e.g. cannot support ADC4 & ADC5 on +# STM32G4). +MIN_ADC_UNIT = 1 +MAX_ADC_UNIT = 3 + class Stm32Pin(boardgen.Pin): def __init__(self, cpu_pin_name): @@ -134,7 +139,7 @@ class Stm32Pin(boardgen.Pin): raise boardgen.PinGeneratorError( "Invalid adc '{:s}' for pin '{:s}'".format(adc_name, self.name()) ) - adc_units = [int(x) for x in m.group(1)] + adc_units = [int(x) for x in m.group(1) if MIN_ADC_UNIT <= int(x) <= MAX_ADC_UNIT] adc_mode = m.group(2) if adc_mode == "INN": # On H7 we have INN/INP, all other parts use IN only. Only use From 6866d17d8f414140fd79be526c46ef236a29be06 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Mon, 28 Aug 2023 19:11:28 +0200 Subject: [PATCH 14/37] docs/samd: Fix the pinout for SAMD21 Itsy Bitsy Express M0. And the "Pin", "GPIO" and "Name" key explanations. Signed-off-by: robert-hh --- docs/samd/pinout.rst | 80 ++++++++++++++++++++++++-------------------- 1 file changed, 44 insertions(+), 36 deletions(-) diff --git a/docs/samd/pinout.rst b/docs/samd/pinout.rst index 5fe1e14ecb..272f7c58df 100644 --- a/docs/samd/pinout.rst +++ b/docs/samd/pinout.rst @@ -17,44 +17,53 @@ Adafruit ItsyBitsy M0 Express pin assignment table === ==== ============ ==== ==== ====== ====== ====== ====== Pin GPIO Pin name IRQ ADC Serial Serial TCC/TC TCC/TC === ==== ============ ==== ==== ====== ====== ====== ====== - 0 PA11 D0 11 19 0/3 2/3 1/1 0/3 - 1 PA10 D1 10 18 0/2 2/2 1/0 0/2 - 2 PA14 D2 14 - 2/2 4/2 3/0 0/4 - 3 PA09 D3 9 17 0/1 2/1 0/1 1/3 - 4 PA08 D4 - 16 0/0 2/0 0/0 1/2 - 5 PA15 D5 15 - 2/3 4/3 3/1 0/5 - 7 PA21 D7 5 - 5/3 3/3 7/1 0/7 - 9 PA07 D9 7 7 - 0/3 1/1 - - 10 PA18 D10 2 - 1/2 3/2 3/0 0/2 - 11 PA16 D11 0 - 1/0 3/0 2/0 0/6 - 12 PA19 D12 3 - 1/3 3/3 3/1 0/3 - 13 PA17 D13 1 - 1/1 3/1 2/1 0/7 - 14 PA02 A0 2 0 - - - - - 15 PB08 A1 8 2 - 4/0 4/0 - - 16 PB09 A2 9 3 - 4/1 4/1 - - 17 PA04 A3 4 4 - 0/0 0/0 - - 18 PA05 A4 5 5 - 0/1 0/1 - - 19 PB02 A5 2 - - 5/0 6/0 - - 20 PA22 SDA 6 - 3/0 5/0 4/0 0/4 - 21 PA23 SCL 7 - 3/1 5/1 4/1 0/5 - 22 PB10 MOSI 10 - - 4/2 5/0 0/4 - 23 PA12 MISO 12 - 2/0 4/0 2/0 0/6 - 24 PB11 SCK 11 - - 4/3 5/1 0/5 - 25 PA00 DOTSTAR_CLK 0 - - 1/0 2/0 - - 26 PA01 DOTSTAR_DATA 1 - - 1/1 2/1 - - 27 PB22 FLASH_MOSI 6 - - 5/2 7/0 - - 28 PB03 FLASH_MISO 3 - - 5/1 6/1 - - 29 PB23 FLASH_SCK 7 - - 5/3 7/1 - + 2 PA02 A0 2 0 - - - - + 40 PB08 A1 8 2 - 4/0 4/0 - + 41 PB09 A2 9 3 - 4/1 4/1 - + 4 PA04 A3 4 4 - 0/0 0/0 - + 5 PA05 A4 5 5 - 0/1 0/1 - + 34 PB02 A5 2 10 - 5/0 6/0 - + 11 PA11 D0 11 19 0/3 2/3 1/1 0/3 + 10 PA10 D1 10 18 0/2 2/2 1/0 0/2 + 14 PA14 D2 14 - 2/2 4/2 3/0 0/4 + 9 PA09 D3 9 17 0/1 2/1 0/1 1/3 + 8 PA08 D4 - 16 0/0 2/0 0/0 1/2 + 15 PA15 D5 15 - 2/3 4/3 3/1 0/5 + 21 PA21 D7 5 - 5/3 3/3 7/1 0/7 + 7 PA07 D9 7 7 - 0/3 1/1 - + 18 PA18 D10 2 - 1/2 3/2 3/0 0/2 + 16 PA16 D11 0 - 1/0 3/0 2/0 0/6 + 19 PA19 D12 3 - 1/3 3/3 3/1 0/3 + 17 PA17 D13 1 - 1/1 3/1 2/1 0/7 + 0 PA00 DOTSTAR_CLK 0 - - 1/0 2/0 - + 1 PA01 DOTSTAR_DATA 1 - - 1/1 2/1 - + 27 PA27 FLASH_CS 15 - - - - - + 35 PB03 FLASH_MISO 3 11 - 5/1 6/1 - + 54 PB22 FLASH_MOSI 6 - - 5/2 7/0 - + 55 PB23 FLASH_SCK 7 - - 5/3 7/1 - + 12 PA12 MISO 12 - 2/0 4/0 2/0 0/6 + 42 PB10 MOSI 10 - - 4/2 5/0 0/4 + 43 PB11 SCK 11 - - 4/3 5/1 0/5 + 23 PA23 SCL 7 - 3/1 5/1 4/1 0/5 + 22 PA22 SDA 6 - 3/0 5/0 4/0 0/4 + 30 PA30 SWCLK 10 - - 1/2 1/0 - + 31 PA31 SWDIO 11 - - 1/3 1/1 - + 24 PA24 USB_DM 12 - 3/2 5/2 5/0 1/2 + 25 PA25 USB_DP 13 - 3/3 5/3 5/1 1/3 + 3 PA03 3 1 - - - - + 6 PA06 6 6 - 0/2 1/0 - + 13 PA13 13 - 2/1 4/1 2/0 0/7 + 20 PA20 4 - 5/2 3/2 7/0 0/4 + 28 PA28 8 - - - - - === ==== ============ ==== ==== ====== ====== ====== ====== Description of the columns: - *Pin* - The number that is expected at ``machine.Pin(n)``, if the pin is given - as a number. This is NOT the GPIO number, but the board pin number, as - given in the board specific definition file. -- *GPIO* - The GPIO number. -- *Pin Name* - The name of a Pin which is expected argument to ``machine.Pin("name")``. + as a number. +- *GPIO* - The GPIO name, which can be used as argument to ``machine.Pin("name")``. +- *Pin Name* - The boards name, which can be used as argument to ``machine.Pin("name")``. - *IRQ* - The IRQ number assigned to that GPIO, used internally by ``Pin.irq()``. When using ``Pin.irq()``, different pins must use different IRQs - *ADC* - The ADC channel assigned to the pin. When using ADC, different pins must @@ -183,10 +192,9 @@ Pin GPIO Pin name IRQ ADC ADC Serial Serial TC PWM PWM Description of the columns: - *Pin* - The number that is expected at ``machine.Pin(n)``, if the pin is given - as a number. This is NOT the GPIO number, but the board pin number, as - given in the board specific definition file. -- *GPIO* - The GPIO number. -- *Pin Name* The name of a Pin which is expected argument to ``machine.Pin("name")``. + as a number. +- *GPIO* - The GPIO name, which can be used as argument to ``machine.Pin("name")``. +- *Pin Name* - The boards name, which can be used as argument to ``machine.Pin("name")``. - *IRQ* - The IRQ number assigned to that GPIO, used internally by ``Pin.irq()``. When using ``Pin.irq()``, different pins must use different IRQs - *ADC* - The ADC0/1 channel assigned to the pin. When using ADC, different pins must From 2c1f23820572e36a1d12597dff3261242fd6573c Mon Sep 17 00:00:00 2001 From: robert-hh Date: Thu, 7 Sep 2023 15:59:34 +0200 Subject: [PATCH 15/37] samd/mpconfigport: Set MICROPY_USE_INTERNAL_ERRNO to 1. Without this, error codes can be misleading. Signed-off-by: robert-hh --- ports/samd/mpconfigport.h | 1 + 1 file changed, 1 insertion(+) diff --git a/ports/samd/mpconfigport.h b/ports/samd/mpconfigport.h index 801408ed47..91a7c1a10d 100644 --- a/ports/samd/mpconfigport.h +++ b/ports/samd/mpconfigport.h @@ -56,6 +56,7 @@ #define MICROPY_PY_BUILTINS_HELP (1) #define MICROPY_PY_BUILTINS_HELP_TEXT samd_help_text #define MICROPY_PY_BUILTINS_HELP_MODULES (1) +#define MICROPY_USE_INTERNAL_ERRNO (1) #define MICROPY_ENABLE_SCHEDULER (1) #define MICROPY_SCHEDULER_STATIC_NODES (1) #define MICROPY_HW_ENABLE_USBDEV (1) From 59afeb056ac3657eebfffc16136d7eedbbc378e2 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Mon, 18 Sep 2023 09:08:24 +0200 Subject: [PATCH 16/37] samd/machine_uart: Add machine_uart_set_baudrate() function. Changing the baudrate requires a complete re-configuration of the Sercom device, which is put into a separate rather large function. This new machine_uart_set_baudrate() function will be useful for future drivers such as Bluetooth. Signed-off-by: robert-hh --- ports/samd/machine_uart.c | 159 +++++++++++++++++++++----------------- 1 file changed, 86 insertions(+), 73 deletions(-) diff --git a/ports/samd/machine_uart.c b/ports/samd/machine_uart.c index ada193020b..de6c92c5f5 100644 --- a/ports/samd/machine_uart.c +++ b/ports/samd/machine_uart.c @@ -49,6 +49,7 @@ typedef struct _machine_uart_obj_t { uint8_t bits; uint8_t parity; uint8_t stop; + uint8_t flow_control; uint8_t tx; uint8_t rx; sercom_pad_config_t tx_pad_config; @@ -112,6 +113,85 @@ void common_uart_irq_handler(int uart_id) { } } +// Configure the Sercom device +STATIC void machine_sercom_configure(machine_uart_obj_t *self) { + Sercom *uart = sercom_instance[self->id]; + + // Reset (clear) the peripheral registers. + while (uart->USART.SYNCBUSY.bit.SWRST) { + } + uart->USART.CTRLA.bit.SWRST = 1; // Reset all Registers, disable peripheral + while (uart->USART.SYNCBUSY.bit.SWRST) { + } + + uint8_t txpo = self->tx_pad_config.pad_nr; + #if defined(MCU_SAMD21) + if (self->tx_pad_config.pad_nr == 2) { // Map pad 2 to TXPO = 1 + txpo = 1; + } else + #endif + if (self->tx_pad_config.pad_nr != 0) { + mp_raise_ValueError(MP_ERROR_TEXT("invalid UART pin")); + } + #if MICROPY_HW_UART_RTSCTS + if ((self->flow_control & FLOW_CONTROL_RTS) && self->rts_pad_config.pad_nr == 2) { + txpo = 2; + mp_hal_set_pin_mux(self->rts, self->rts_pad_config.alt_fct); + } + if ((self->flow_control & FLOW_CONTROL_CTS) && self->cts_pad_config.pad_nr == 3) { + txpo = 2; + mp_hal_set_pin_mux(self->cts, self->cts_pad_config.alt_fct); + } + #endif + + uart->USART.CTRLA.reg = + SERCOM_USART_CTRLA_DORD // Data order + | SERCOM_USART_CTRLA_FORM(self->parity != 0 ? 1 : 0) // Enable parity or not + | SERCOM_USART_CTRLA_RXPO(self->rx_pad_config.pad_nr) // Set Pad# + | SERCOM_USART_CTRLA_TXPO(txpo) // Set Pad# + | SERCOM_USART_CTRLA_MODE(1) // USART with internal clock + ; + uart->USART.CTRLB.reg = + SERCOM_USART_CTRLB_RXEN // Enable Rx & Tx + | SERCOM_USART_CTRLB_TXEN + | ((self->parity & 1) << SERCOM_USART_CTRLB_PMODE_Pos) + | (self->stop << SERCOM_USART_CTRLB_SBMODE_Pos) + | SERCOM_USART_CTRLB_CHSIZE((self->bits & 7) | (self->bits & 1)) + ; + while (uart->USART.SYNCBUSY.bit.CTRLB) { + } + + // USART is driven by the clock of GCLK Generator 2, freq by get_peripheral_freq() + // baud rate; 65536 * (1 - 16 * 115200/bus_freq) + uint32_t baud = 65536 - ((uint64_t)(65536 * 16) * self->baudrate + get_peripheral_freq() / 2) / get_peripheral_freq(); + uart->USART.BAUD.bit.BAUD = baud; // Set Baud + + sercom_register_irq(self->id, &common_uart_irq_handler); + + // Enable RXC interrupt + uart->USART.INTENSET.reg = SERCOM_USART_INTENSET_RXC; + #if defined(MCU_SAMD21) + NVIC_EnableIRQ(SERCOM0_IRQn + self->id); + #elif defined(MCU_SAMD51) + NVIC_EnableIRQ(SERCOM0_0_IRQn + 4 * self->id + 2); + #endif + #if MICROPY_HW_UART_TXBUF + // Enable DRE interrupt + // SAMD21 has just 1 IRQ for all USART events, so no need for an additional NVIC enable + #if defined(MCU_SAMD51) + NVIC_EnableIRQ(SERCOM0_0_IRQn + 4 * self->id + 0); + #endif + #endif + + sercom_enable(uart, 1); +} + +void machine_uart_set_baudrate(mp_obj_t self_in, uint32_t baudrate) { + machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in); + self->baudrate = baudrate; + machine_sercom_configure(self); +} + STATIC void mp_machine_uart_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in); mp_printf(print, "UART(%u, baudrate=%u, bits=%u, parity=%s, stop=%u, " @@ -194,22 +274,22 @@ STATIC void mp_machine_uart_init_helper(machine_uart_obj_t *self, size_t n_args, if (args[ARG_rx].u_obj != mp_const_none) { self->rx = mp_hal_get_pin_obj(args[ARG_rx].u_obj); } + self->flow_control = 0; #if MICROPY_HW_UART_RTSCTS - uint8_t flow_control = 0; // Set RTS/CTS pins if configured. if (args[ARG_rts].u_obj != mp_const_none) { self->rts = mp_hal_get_pin_obj(args[ARG_rts].u_obj); self->rts_pad_config = get_sercom_config(self->rts, self->id); - flow_control = FLOW_CONTROL_RTS; + self->flow_control = FLOW_CONTROL_RTS; } if (args[ARG_cts].u_obj != mp_const_none) { self->cts = mp_hal_get_pin_obj(args[ARG_cts].u_obj); self->cts_pad_config = get_sercom_config(self->cts, self->id); - flow_control |= FLOW_CONTROL_CTS; + self->flow_control |= FLOW_CONTROL_CTS; } // rts only flow control is not allowed. Otherwise the state of the // cts pin is undefined. - if (flow_control == FLOW_CONTROL_RTS) { + if (self->flow_control == FLOW_CONTROL_RTS) { mp_raise_ValueError(MP_ERROR_TEXT("cts missing for flow control")); } #endif @@ -278,75 +358,8 @@ STATIC void mp_machine_uart_init_helper(machine_uart_obj_t *self, size_t n_args, // Next: Set up the clocks enable_sercom_clock(self->id); - // Next: Configure the USART - Sercom *uart = sercom_instance[self->id]; - // Reset (clear) the peripheral registers. - while (uart->USART.SYNCBUSY.bit.SWRST) { - } - uart->USART.CTRLA.bit.SWRST = 1; // Reset all Registers, disable peripheral - while (uart->USART.SYNCBUSY.bit.SWRST) { - } - - uint8_t txpo = self->tx_pad_config.pad_nr; - #if defined(MCU_SAMD21) - if (self->tx_pad_config.pad_nr == 2) { // Map pad 2 to TXPO = 1 - txpo = 1; - } else - #endif - if (self->tx_pad_config.pad_nr != 0) { - mp_raise_ValueError(MP_ERROR_TEXT("invalid UART pin")); - } - #if MICROPY_HW_UART_RTSCTS - if ((flow_control & FLOW_CONTROL_RTS) && self->rts_pad_config.pad_nr == 2) { - txpo = 2; - mp_hal_set_pin_mux(self->rts, self->rts_pad_config.alt_fct); - } - if ((flow_control & FLOW_CONTROL_CTS) && self->cts_pad_config.pad_nr == 3) { - txpo = 2; - mp_hal_set_pin_mux(self->cts, self->cts_pad_config.alt_fct); - } - #endif - - uart->USART.CTRLA.reg = - SERCOM_USART_CTRLA_DORD // Data order - | SERCOM_USART_CTRLA_FORM(self->parity != 0 ? 1 : 0) // Enable parity or not - | SERCOM_USART_CTRLA_RXPO(self->rx_pad_config.pad_nr) // Set Pad# - | SERCOM_USART_CTRLA_TXPO(txpo) // Set Pad# - | SERCOM_USART_CTRLA_MODE(1) // USART with internal clock - ; - uart->USART.CTRLB.reg = - SERCOM_USART_CTRLB_RXEN // Enable Rx & Tx - | SERCOM_USART_CTRLB_TXEN - | ((self->parity & 1) << SERCOM_USART_CTRLB_PMODE_Pos) - | (self->stop << SERCOM_USART_CTRLB_SBMODE_Pos) - | SERCOM_USART_CTRLB_CHSIZE((self->bits & 7) | (self->bits & 1)) - ; - while (uart->USART.SYNCBUSY.bit.CTRLB) { - } - - // USART is driven by the clock of GCLK Generator 2, freq by get_peripheral_freq() - // baud rate; 65536 * (1 - 16 * 115200/bus_freq) - uint32_t baud = 65536 - ((uint64_t)(65536 * 16) * self->baudrate + get_peripheral_freq() / 2) / get_peripheral_freq(); - uart->USART.BAUD.bit.BAUD = baud; // Set Baud - - sercom_register_irq(self->id, &common_uart_irq_handler); - - // Enable RXC interrupt - uart->USART.INTENSET.reg = SERCOM_USART_INTENSET_RXC; - #if defined(MCU_SAMD21) - NVIC_EnableIRQ(SERCOM0_IRQn + self->id); - #elif defined(MCU_SAMD51) - NVIC_EnableIRQ(SERCOM0_0_IRQn + 4 * self->id + 2); - #endif - #if MICROPY_HW_UART_TXBUF - // Enable DRE interrupt - // SAMD21 has just 1 IRQ for all USART events, so no need for an additional NVIC enable - #if defined(MCU_SAMD51) - NVIC_EnableIRQ(SERCOM0_0_IRQn + 4 * self->id + 0); - #endif - #endif - - sercom_enable(uart, 1); + // Configure the sercom module + machine_sercom_configure(self); } } From c35cc63366a924b818eaaf4f0bd1c11eeb236d79 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Sat, 4 Nov 2023 19:00:56 +0100 Subject: [PATCH 17/37] samd/pin_af: Fix a typo in a conditional compile. Thanks to @ricksorensen for finding it. Signed-off-by: robert-hh --- ports/samd/pin_af.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/samd/pin_af.c b/ports/samd/pin_af.c index ca11e3b7a8..a73c6591dd 100644 --- a/ports/samd/pin_af.c +++ b/ports/samd/pin_af.c @@ -112,7 +112,7 @@ adc_config_t get_adc_config(int pin_id, int32_t flag) { const machine_pin_obj_t *pct_ptr = pin_find_by_id(pin_id); if (pct_ptr->adc0 != 0xff && (flag & (1 << pct_ptr->adc0)) == 0) { return (adc_config_t) {0, pct_ptr->adc0}; - #if defined(MUC_SAMD51) + #if defined(MCU_SAMD51) } else if (pct_ptr->adc1 != 0xff && (flag & (1 << (pct_ptr->adc1 + 16))) == 0) { return (adc_config_t) {1, pct_ptr->adc1}; #endif From bea6ff82fadf37555a3a282105f861a914475bb6 Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Thu, 2 Nov 2023 13:19:09 +0100 Subject: [PATCH 18/37] tools/tinytest-codegen.py: Externalise tests list. Remove port-specific test directories and excluded tests from tinytest-codegen, and let it read said information from an external file. This way tinytest-codegen is not limited to always generate tests for the `qemu-arm` target. This allows having port-specific test directory and excluded tests for more than one QEMU bare-metal target. The `qemu-arm` port Makefile was modified to work with the generator changes and a tests profile file was added to said port. Signed-off-by: Alessandro Gatti --- ports/qemu-arm/Makefile.test | 4 +- ports/qemu-arm/tests_profile.txt | 16 +++++ tools/tinytest-codegen.py | 116 +++++++++++++++++-------------- 3 files changed, 82 insertions(+), 54 deletions(-) create mode 100644 ports/qemu-arm/tests_profile.txt diff --git a/ports/qemu-arm/Makefile.test b/ports/qemu-arm/Makefile.test index e0f3a54169..cb5b0927c8 100644 --- a/ports/qemu-arm/Makefile.test +++ b/ports/qemu-arm/Makefile.test @@ -13,10 +13,12 @@ CFLAGS += -DTEST .PHONY: $(BUILD)/genhdr/tests.h +TESTS_PROFILE = $(dir $(abspath $(firstword $(MAKEFILE_LIST))))/tests_profile.txt + $(BUILD)/test_main.o: $(BUILD)/genhdr/tests.h $(BUILD)/genhdr/tests.h: (cd $(TOP)/tests; ./run-tests.py --target=qemu-arm --write-exp) - $(Q)echo "Generating $@";(cd $(TOP)/tests; ../tools/tinytest-codegen.py $(addprefix --exclude ,$(TESTS_EXCLUDE))) > $@ + $(Q)echo "Generating $@";(cd $(TOP)/tests; ../tools/tinytest-codegen.py --profile $(TESTS_PROFILE) $(addprefix --exclude ,$(TESTS_EXCLUDE))) > $@ $(BUILD)/lib/tinytest/tinytest.o: CFLAGS += -DNO_FORKING diff --git a/ports/qemu-arm/tests_profile.txt b/ports/qemu-arm/tests_profile.txt new file mode 100644 index 0000000000..c55185a0eb --- /dev/null +++ b/ports/qemu-arm/tests_profile.txt @@ -0,0 +1,16 @@ +# Port-specific test directories. + +test_dirs.add(("inlineasm", "qemu-arm")) + +# Port-specific tests exclusion list. + +exclude_tests.add( + ( + # inline asm FP tests (require Cortex-M4) + "inlineasm/asmfpaddsub.py", + "inlineasm/asmfpcmp.py", + "inlineasm/asmfpldrstr.py", + "inlineasm/asmfpmuldiv.py", + "inlineasm/asmfpsqrt.py", + ) +) diff --git a/tools/tinytest-codegen.py b/tools/tinytest-codegen.py index 7f6c86243b..8178b56020 100755 --- a/tools/tinytest-codegen.py +++ b/tools/tinytest-codegen.py @@ -32,6 +32,12 @@ def script_to_map(test_file): return r +def load_profile(profile_file, test_dirs, exclude_tests): + profile_globals = {"test_dirs": test_dirs, "exclude_tests": exclude_tests} + exec(profile_file.read(), profile_globals) + return profile_globals["test_dirs"], profile_globals["exclude_tests"] + + test_function = ( "void {name}(void* data) {{\n" " static const char pystr[] = {script};\n" @@ -50,58 +56,55 @@ testgroup_struct = "struct testgroup_t groups[] = {{\n{body}\n END_OF_GROUPS\n} testgroup_member = ' {{ "{name}", {name}_tests }},' ## XXX: may be we could have `--without ` argument... -# currently these tests are selected because they pass on qemu-arm -test_dirs = ( - "basics", - "micropython", - "misc", - "extmod", - "float", - "inlineasm", - "qemu-arm", -) # 'import', 'io',) -exclude_tests = ( - # pattern matching in .exp - "basics/bytes_compare3.py", - "extmod/ticks_diff.py", - "extmod/time_ms_us.py", - # unicode char issue - "extmod/json_loads.py", - # doesn't output to python stdout - "extmod/re_debug.py", - "extmod/vfs_basic.py", - "extmod/vfs_fat_ramdisk.py", - "extmod/vfs_fat_fileio.py", - "extmod/vfs_fat_fsusermount.py", - "extmod/vfs_fat_oldproto.py", - # rounding issues - "float/float_divmod.py", - # requires double precision floating point to work - "float/float2int_doubleprec_intbig.py", - "float/float_format_ints_doubleprec.py", - "float/float_parse_doubleprec.py", - # inline asm FP tests (require Cortex-M4) - "inlineasm/asmfpaddsub.py", - "inlineasm/asmfpcmp.py", - "inlineasm/asmfpldrstr.py", - "inlineasm/asmfpmuldiv.py", - "inlineasm/asmfpsqrt.py", - # different filename in output - "micropython/emg_exc.py", - "micropython/heapalloc_traceback.py", - # don't have emergency exception buffer - "micropython/heapalloc_exc_compressed_emg_exc.py", - # pattern matching in .exp - "micropython/meminfo.py", - # needs sys stdfiles - "misc/print_exception.py", - # settrace .exp files are too large - "misc/sys_settrace_loop.py", - "misc/sys_settrace_generator.py", - "misc/sys_settrace_features.py", - # don't have f-string - "basics/string_fstring.py", - "basics/string_fstring_debug.py", + +test_dirs = set( + ( + "basics", + "extmod", + "float", + "micropython", + "misc", + ) +) + +exclude_tests = set( + ( + # pattern matching in .exp + "basics/bytes_compare3.py", + "extmod/ticks_diff.py", + "extmod/time_ms_us.py", + # unicode char issue + "extmod/json_loads.py", + # doesn't output to python stdout + "extmod/re_debug.py", + "extmod/vfs_basic.py", + "extmod/vfs_fat_ramdisk.py", + "extmod/vfs_fat_fileio.py", + "extmod/vfs_fat_fsusermount.py", + "extmod/vfs_fat_oldproto.py", + # rounding issues + "float/float_divmod.py", + # requires double precision floating point to work + "float/float2int_doubleprec_intbig.py", + "float/float_format_ints_doubleprec.py", + "float/float_parse_doubleprec.py", + # different filename in output + "micropython/emg_exc.py", + "micropython/heapalloc_traceback.py", + # don't have emergency exception buffer + "micropython/heapalloc_exc_compressed_emg_exc.py", + # pattern matching in .exp + "micropython/meminfo.py", + # needs sys stdfiles + "misc/print_exception.py", + # settrace .exp files are too large + "misc/sys_settrace_loop.py", + "misc/sys_settrace_generator.py", + "misc/sys_settrace_features.py", + # don't have f-string + "basics/string_fstring.py", + "basics/string_fstring_debug.py", + ) ) output = [] @@ -112,11 +115,18 @@ argparser = argparse.ArgumentParser( ) argparser.add_argument("--stdin", action="store_true", help="read list of tests from stdin") argparser.add_argument("--exclude", action="append", help="exclude test by name") +argparser.add_argument( + "--profile", + type=argparse.FileType("rt", encoding="utf-8"), + help="optional profile file providing test directories and exclusion list", +) args = argparser.parse_args() if not args.stdin: + if args.profile: + test_dirs, exclude_tests = load_profile(args.profile, test_dirs, exclude_tests) if args.exclude: - exclude_tests += tuple(args.exclude) + exclude_tests = exclude_tests.union(args.exclude) for group in test_dirs: tests += [test for test in glob("{}/*.py".format(group)) if test not in exclude_tests] else: From 9be0623d4ce518eead8ea477a4615cb8c3e357d8 Mon Sep 17 00:00:00 2001 From: Jim Mussared Date: Mon, 6 Nov 2023 15:06:28 +1100 Subject: [PATCH 19/37] shared/libc/string0: Don't deref args for n==0 case. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit C99 says that strncmp has UB for either string being NULL, so the current behavior is technically correct, but it's an easy fix to handle this case correctly. 7.1.4: "unless explicitly stated otherwise in the detailed description... if an argument to a function has ...null pointer.. the behavior is undeļ¬ned". 7.21.1: "Unless explicitly stated otherwise in the description of a particular function in this subclause, pointer arguments on such a call shall still have valid values, as described in 7.1.4". Also make the same change for the minimal version in bare-arm/lib.c. This work was funded through GitHub Sponsors. Signed-off-by: Jim Mussared --- ports/bare-arm/lib.c | 3 ++- shared/libc/string0.c | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/ports/bare-arm/lib.c b/ports/bare-arm/lib.c index 61d3be64e3..1574d5c902 100644 --- a/ports/bare-arm/lib.c +++ b/ports/bare-arm/lib.c @@ -110,8 +110,9 @@ char *strchr(const char *s, int c) { } int strncmp(const char *s1, const char *s2, size_t n) { - while (*s1 && *s2 && n-- > 0) { + while (n > 0 && *s1 && *s2) { int c = *s1++ - *s2++; + --n; if (c) { return c; } diff --git a/shared/libc/string0.c b/shared/libc/string0.c index a3b268e441..3909f70ed8 100644 --- a/shared/libc/string0.c +++ b/shared/libc/string0.c @@ -154,7 +154,7 @@ int strcmp(const char *s1, const char *s2) { } int strncmp(const char *s1, const char *s2, size_t n) { - while (*s1 && *s2 && n > 0) { + while (n > 0 && *s1 && *s2) { char c1 = *s1++; // XXX UTF8 get char, next char char c2 = *s2++; // XXX UTF8 get char, next char n--; From 4212799fd8bded69e5cf47faf2ebd675906ac151 Mon Sep 17 00:00:00 2001 From: Jim Mussared Date: Mon, 6 Nov 2023 15:08:11 +1100 Subject: [PATCH 20/37] py/qstr: Special case qstr_find_strn for empty string. This handles the case where an empty bytes/bytearray/str could pass in NULL as the str argument (with length zero). This would result in UB in strncmp. Even though our bare-metal implementation of strncmp handles this, best to avoid it for when we're using system strncmp. This work was funded through GitHub Sponsors. Signed-off-by: Jim Mussared --- py/qstr.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/py/qstr.c b/py/qstr.c index 0ae08354a5..dc89013f86 100644 --- a/py/qstr.c +++ b/py/qstr.c @@ -233,6 +233,11 @@ STATIC qstr qstr_add(mp_uint_t hash, mp_uint_t len, const char *q_ptr) { } qstr qstr_find_strn(const char *str, size_t str_len) { + if (str_len == 0) { + // strncmp behaviour is undefined for str==NULL. + return MP_QSTR_; + } + // work out hash of str size_t str_hash = qstr_compute_hash((const byte *)str, str_len); From 03eae48847c61985afece342df971f149c8ffa42 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 25 Oct 2023 19:13:11 +1100 Subject: [PATCH 21/37] extmod/machine_adc_block: Factor esp32 ADCBlock bindings to common code. This is a code factoring to have the Python bindings in one location, and all the ports use those same bindings. At this stage only esp32 implements this class, so the code for the bindings comes from that port. The documentation is also updated to reflect the esp32's behaviour of ADCBlock.connect(). Signed-off-by: Damien George --- docs/library/machine.ADCBlock.rst | 9 +- extmod/extmod.cmake | 1 + extmod/extmod.mk | 1 + extmod/machine_adc_block.c | 128 +++++++++++++++++++ extmod/modmachine.h | 2 + ports/esp32/adc.c | 92 ++++++++++++++ ports/esp32/adc.h | 60 +++++++++ ports/esp32/esp32_common.cmake | 2 +- ports/esp32/machine_adc.c | 10 +- ports/esp32/machine_adc.h | 16 --- ports/esp32/machine_adc_block.c | 72 +++++++++++ ports/esp32/machine_adcblock.c | 204 ------------------------------ ports/esp32/machine_adcblock.h | 22 ---- ports/esp32/modmachine.c | 4 +- ports/esp32/modmachine.h | 1 - ports/esp32/mpconfigport.h | 3 +- ports/renesas-ra/modmachine.h | 1 - 17 files changed, 371 insertions(+), 257 deletions(-) create mode 100644 extmod/machine_adc_block.c create mode 100644 ports/esp32/adc.c create mode 100644 ports/esp32/adc.h delete mode 100644 ports/esp32/machine_adc.h create mode 100644 ports/esp32/machine_adc_block.c delete mode 100644 ports/esp32/machine_adcblock.c delete mode 100644 ports/esp32/machine_adcblock.h diff --git a/docs/library/machine.ADCBlock.rst b/docs/library/machine.ADCBlock.rst index 56a468dd62..eb94362d55 100644 --- a/docs/library/machine.ADCBlock.rst +++ b/docs/library/machine.ADCBlock.rst @@ -39,9 +39,9 @@ Methods Configure the ADC peripheral. *bits* will set the resolution of the conversion process. -.. method:: ADCBlock.connect(channel) - ADCBlock.connect(source) - ADCBlock.connect(channel, source) +.. method:: ADCBlock.connect(channel, *, ...) + ADCBlock.connect(source, *, ...) + ADCBlock.connect(channel, source, *, ...) Connect up a channel on the ADC peripheral so it is ready for sampling, and return an :ref:`ADC ` object that represents that connection. @@ -56,3 +56,6 @@ Methods If both *channel* and *source* are given then they are connected together and made ready for sampling. + + Any additional keyword arguments are used to configure the returned ADC object, + via its :meth:`init ` method. diff --git a/extmod/extmod.cmake b/extmod/extmod.cmake index a1a0a9f736..5bfdf65513 100644 --- a/extmod/extmod.cmake +++ b/extmod/extmod.cmake @@ -8,6 +8,7 @@ set(MICROPY_SOURCE_EXTMOD ${MICROPY_DIR}/shared/libc/printf.c ${MICROPY_EXTMOD_DIR}/btstack/modbluetooth_btstack.c ${MICROPY_EXTMOD_DIR}/machine_adc.c + ${MICROPY_EXTMOD_DIR}/machine_adc_block.c ${MICROPY_EXTMOD_DIR}/machine_bitstream.c ${MICROPY_EXTMOD_DIR}/machine_i2c.c ${MICROPY_EXTMOD_DIR}/machine_i2s.c diff --git a/extmod/extmod.mk b/extmod/extmod.mk index c4b2c3d9c5..a04856bce2 100644 --- a/extmod/extmod.mk +++ b/extmod/extmod.mk @@ -3,6 +3,7 @@ SRC_EXTMOD_C += \ extmod/machine_adc.c \ + extmod/machine_adc_block.c \ extmod/machine_bitstream.c \ extmod/machine_i2c.c \ extmod/machine_i2s.c \ diff --git a/extmod/machine_adc_block.c b/extmod/machine_adc_block.c new file mode 100644 index 0000000000..f80e2dae4c --- /dev/null +++ b/extmod/machine_adc_block.c @@ -0,0 +1,128 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Jonathan Hogg + * Copyright (c) 2023 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "py/runtime.h" + +#if MICROPY_PY_MACHINE_ADC_BLOCK + +#include "py/mphal.h" +#include "extmod/modmachine.h" + +// The port must provide implementations of these low-level ADCBlock functions. +STATIC void mp_machine_adc_block_print(const mp_print_t *print, machine_adc_block_obj_t *self); +STATIC machine_adc_block_obj_t *mp_machine_adc_block_get(mp_int_t unit); +STATIC void mp_machine_adc_block_bits_set(machine_adc_block_obj_t *self, mp_int_t bits); +STATIC machine_adc_obj_t *mp_machine_adc_block_connect(machine_adc_block_obj_t *self, mp_int_t channel_id, mp_hal_pin_obj_t pin, mp_map_t *kw_args); + +// The port provides implementations of the above in this file. +#include MICROPY_PY_MACHINE_ADC_BLOCK_INCLUDEFILE + +STATIC void machine_adc_block_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + machine_adc_block_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_machine_adc_block_print(print, self); +} + +STATIC void machine_adc_block_init_helper(machine_adc_block_obj_t *self, size_t n_pos_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { + ARG_bits, + }; + + static const mp_arg_t allowed_args[] = { + { MP_QSTR_bits, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_pos_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + mp_int_t bits = args[ARG_bits].u_int; + mp_machine_adc_block_bits_set(self, bits); +} + +STATIC mp_obj_t machine_adc_block_make_new(const mp_obj_type_t *type, size_t n_pos_args, size_t n_kw_args, const mp_obj_t *args) { + mp_arg_check_num(n_pos_args, n_kw_args, 1, MP_OBJ_FUN_ARGS_MAX, true); + mp_int_t unit = mp_obj_get_int(args[0]); + machine_adc_block_obj_t *self = mp_machine_adc_block_get(unit); + if (self == NULL) { + mp_raise_ValueError(MP_ERROR_TEXT("invalid block id")); + } + + mp_map_t kw_args; + mp_map_init_fixed_table(&kw_args, n_kw_args, args + n_pos_args); + machine_adc_block_init_helper(self, n_pos_args - 1, args + 1, &kw_args); + + return MP_OBJ_FROM_PTR(self); +} + +STATIC mp_obj_t machine_adc_block_init(size_t n_pos_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + machine_adc_block_obj_t *self = pos_args[0]; + machine_adc_block_init_helper(self, n_pos_args - 1, pos_args + 1, kw_args); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(machine_adc_block_init_obj, 1, machine_adc_block_init); + +STATIC mp_obj_t machine_adc_block_connect(size_t n_pos_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + machine_adc_block_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + mp_int_t channel_id = -1; + mp_hal_pin_obj_t pin = -1; + if (n_pos_args == 2) { + if (mp_obj_is_int(pos_args[1])) { + channel_id = mp_obj_get_int(pos_args[1]); + } else { + pin = mp_hal_get_pin_obj(pos_args[1]); + } + } else if (n_pos_args == 3) { + channel_id = mp_obj_get_int(pos_args[1]); + pin = mp_hal_get_pin_obj(pos_args[2]); + } else { + mp_raise_TypeError(MP_ERROR_TEXT("too many positional args")); + } + + machine_adc_obj_t *adc = mp_machine_adc_block_connect(self, channel_id, pin, kw_args); + if (adc == NULL) { + mp_raise_ValueError(MP_ERROR_TEXT("no matching ADC")); + } + + return MP_OBJ_FROM_PTR(adc); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(machine_adc_block_connect_obj, 2, machine_adc_block_connect); + +STATIC const mp_rom_map_elem_t machine_adc_block_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&machine_adc_block_init_obj) }, + { MP_ROM_QSTR(MP_QSTR_connect), MP_ROM_PTR(&machine_adc_block_connect_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(machine_adc_block_locals_dict, machine_adc_block_locals_dict_table); + +MP_DEFINE_CONST_OBJ_TYPE( + machine_adc_block_type, + MP_QSTR_ADCBlock, + MP_TYPE_FLAG_NONE, + make_new, machine_adc_block_make_new, + print, machine_adc_block_print, + locals_dict, &machine_adc_block_locals_dict + ); + +#endif // MICROPY_PY_MACHINE_ADC_BLOCK diff --git a/extmod/modmachine.h b/extmod/modmachine.h index 6cace4f851..0744ef7889 100644 --- a/extmod/modmachine.h +++ b/extmod/modmachine.h @@ -128,6 +128,7 @@ // A port must provide these types, but they are otherwise opaque. typedef struct _machine_adc_obj_t machine_adc_obj_t; +typedef struct _machine_adc_block_obj_t machine_adc_block_obj_t; typedef struct _machine_i2s_obj_t machine_i2s_obj_t; typedef struct _machine_pwm_obj_t machine_pwm_obj_t; typedef struct _machine_uart_obj_t machine_uart_obj_t; @@ -200,6 +201,7 @@ extern const machine_mem_obj_t machine_mem32_obj; // Their Python bindings are implemented in extmod, and their implementation // is provided by a port. extern const mp_obj_type_t machine_adc_type; +extern const mp_obj_type_t machine_adc_block_type; extern const mp_obj_type_t machine_i2c_type; extern const mp_obj_type_t machine_i2s_type; extern const mp_obj_type_t machine_mem_type; diff --git a/ports/esp32/adc.c b/ports/esp32/adc.c new file mode 100644 index 0000000000..c5886624ec --- /dev/null +++ b/ports/esp32/adc.c @@ -0,0 +1,92 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 Nick Moore + * Copyright (c) 2021 Jonathan Hogg + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "py/mphal.h" +#include "adc.h" +#include "driver/adc.h" + +#define DEFAULT_VREF 1100 + +void madcblock_bits_helper(machine_adc_block_obj_t *self, mp_int_t bits) { + switch (bits) { + #if CONFIG_IDF_TARGET_ESP32 + case 9: + self->width = ADC_WIDTH_BIT_9; + break; + case 10: + self->width = ADC_WIDTH_BIT_10; + break; + case 11: + self->width = ADC_WIDTH_BIT_11; + break; + #endif + #if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S3 + case 12: + self->width = ADC_WIDTH_BIT_12; + break; + #endif + #if CONFIG_IDF_TARGET_ESP32S2 + case 13: + self->width = ADC_WIDTH_BIT_13; + break; + #endif + default: + mp_raise_ValueError(MP_ERROR_TEXT("invalid bits")); + } + self->bits = bits; + + if (self->unit_id == ADC_UNIT_1) { + adc1_config_width(self->width); + } + for (adc_atten_t atten = ADC_ATTEN_DB_0; atten < ADC_ATTEN_MAX; atten++) { + if (self->characteristics[atten] != NULL) { + esp_adc_cal_characterize(self->unit_id, atten, self->width, DEFAULT_VREF, self->characteristics[atten]); + } + } +} + +mp_int_t madcblock_read_helper(machine_adc_block_obj_t *self, adc_channel_t channel_id) { + int raw; + if (self->unit_id == ADC_UNIT_1) { + raw = adc1_get_raw(channel_id); + } else { + check_esp_err(adc2_get_raw(channel_id, self->width, &raw)); + } + return raw; +} + +mp_int_t madcblock_read_uv_helper(machine_adc_block_obj_t *self, adc_channel_t channel_id, adc_atten_t atten) { + int raw = madcblock_read_helper(self, channel_id); + esp_adc_cal_characteristics_t *adc_chars = self->characteristics[atten]; + if (adc_chars == NULL) { + adc_chars = malloc(sizeof(esp_adc_cal_characteristics_t)); + esp_adc_cal_characterize(self->unit_id, atten, self->width, DEFAULT_VREF, adc_chars); + self->characteristics[atten] = adc_chars; + } + mp_int_t uv = esp_adc_cal_raw_to_voltage(raw, adc_chars) * 1000; + return uv; +} diff --git a/ports/esp32/adc.h b/ports/esp32/adc.h new file mode 100644 index 0000000000..ae5c0d3c37 --- /dev/null +++ b/ports/esp32/adc.h @@ -0,0 +1,60 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * Development of the code in this file was sponsored by Microbric Pty Ltd + * + * The MIT License (MIT) + * + * Copyright (c) 2023 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef MICROPY_INCLUDED_ESP32_ADC_H +#define MICROPY_INCLUDED_ESP32_ADC_H + +#include "py/runtime.h" +#include "esp_adc_cal.h" + +#define ADC_ATTEN_MAX SOC_ADC_ATTEN_NUM + +typedef struct _machine_adc_block_obj_t { + mp_obj_base_t base; + adc_unit_t unit_id; + mp_int_t bits; + adc_bits_width_t width; + esp_adc_cal_characteristics_t *characteristics[ADC_ATTEN_MAX]; +} machine_adc_block_obj_t; + +typedef struct _machine_adc_obj_t { + mp_obj_base_t base; + machine_adc_block_obj_t *block; + adc_channel_t channel_id; + gpio_num_t gpio_id; +} machine_adc_obj_t; + +extern machine_adc_block_obj_t madcblock_obj[]; + +void madcblock_bits_helper(machine_adc_block_obj_t *self, mp_int_t bits); +mp_int_t madcblock_read_helper(machine_adc_block_obj_t *self, adc_channel_t channel_id); +mp_int_t madcblock_read_uv_helper(machine_adc_block_obj_t *self, adc_channel_t channel_id, adc_atten_t atten); + +const machine_adc_obj_t *madc_search_helper(machine_adc_block_obj_t *block, adc_channel_t channel_id, gpio_num_t gpio_id); +void madc_init_helper(const machine_adc_obj_t *self, size_t n_pos_args, const mp_obj_t *pos_args, mp_map_t *kw_args); + +#endif // MICROPY_INCLUDED_ESP32_ADC_H diff --git a/ports/esp32/esp32_common.cmake b/ports/esp32/esp32_common.cmake index f66c59e341..40ae98822d 100644 --- a/ports/esp32/esp32_common.cmake +++ b/ports/esp32/esp32_common.cmake @@ -53,6 +53,7 @@ list(APPEND MICROPY_SOURCE_DRIVERS ) list(APPEND MICROPY_SOURCE_PORT + adc.c main.c ppp_set_auth.c uart.c @@ -66,7 +67,6 @@ list(APPEND MICROPY_SOURCE_PORT machine_timer.c machine_pin.c machine_touchpad.c - machine_adcblock.c machine_dac.c machine_i2c.c modmachine.c diff --git a/ports/esp32/machine_adc.c b/ports/esp32/machine_adc.c index 42746cb4bd..b5233e4dda 100644 --- a/ports/esp32/machine_adc.c +++ b/ports/esp32/machine_adc.c @@ -28,13 +28,9 @@ // This file is never compiled standalone, it's included directly from // extmod/machine_adc.c via MICROPY_PY_MACHINE_ADC_INCLUDEFILE. -#include "esp_log.h" - -#include "driver/gpio.h" -#include "driver/adc.h" - #include "py/mphal.h" -#include "machine_adc.h" +#include "adc.h" +#include "driver/adc.h" #define ADCBLOCK1 (&madcblock_obj[0]) #define ADCBLOCK2 (&madcblock_obj[1]) @@ -136,7 +132,7 @@ static inline void madc_atten_set(const machine_adc_obj_t *self, adc_atten_t att madc_obj_atten[self - &madc_obj[0]] = atten + 1; } -const machine_adc_obj_t *madc_search_helper(madcblock_obj_t *block, adc_channel_t channel_id, gpio_num_t gpio_id) { +const machine_adc_obj_t *madc_search_helper(machine_adc_block_obj_t *block, adc_channel_t channel_id, gpio_num_t gpio_id) { for (int i = 0; i < MP_ARRAY_SIZE(madc_obj); i++) { const machine_adc_obj_t *adc = &madc_obj[i]; if ((block == NULL || block == adc->block) && (channel_id == -1 || channel_id == adc->channel_id) && (gpio_id == -1 || gpio_id == adc->gpio_id)) { diff --git a/ports/esp32/machine_adc.h b/ports/esp32/machine_adc.h deleted file mode 100644 index 6d06486c30..0000000000 --- a/ports/esp32/machine_adc.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef MICROPY_INCLUDED_MACHINE_ADC_H -#define MICROPY_INCLUDED_MACHINE_ADC_H - -#include "machine_adcblock.h" - -typedef struct _machine_adc_obj_t { - mp_obj_base_t base; - madcblock_obj_t *block; - adc_channel_t channel_id; - gpio_num_t gpio_id; -} machine_adc_obj_t; - -const machine_adc_obj_t *madc_search_helper(madcblock_obj_t *block, adc_channel_t channel_id, gpio_num_t gpio_id); -void madc_init_helper(const machine_adc_obj_t *self, size_t n_pos_args, const mp_obj_t *pos_args, mp_map_t *kw_args); - -#endif // MICROPY_INCLUDED_MACHINE_ADC_H diff --git a/ports/esp32/machine_adc_block.c b/ports/esp32/machine_adc_block.c new file mode 100644 index 0000000000..f2975ff2f2 --- /dev/null +++ b/ports/esp32/machine_adc_block.c @@ -0,0 +1,72 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Jonathan Hogg + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +// This file is never compiled standalone, it's included directly from +// extmod/machine_adc_block.c via MICROPY_PY_MACHINE_ADC_BLOCK_INCLUDEFILE. + +#include "py/mphal.h" +#include "adc.h" +#include "driver/adc.h" + +machine_adc_block_obj_t madcblock_obj[] = { + #if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S3 + {{&machine_adc_block_type}, ADC_UNIT_1, 12, -1, {0}}, + {{&machine_adc_block_type}, ADC_UNIT_2, 12, -1, {0}}, + #elif CONFIG_IDF_TARGET_ESP32S2 + {{&machine_adc_block_type}, ADC_UNIT_1, 13, -1, {0}}, + {{&machine_adc_block_type}, ADC_UNIT_2, 13, -1, {0}}, + #endif +}; + +STATIC void mp_machine_adc_block_print(const mp_print_t *print, machine_adc_block_obj_t *self) { + mp_printf(print, "ADCBlock(%u, bits=%u)", self->unit_id, self->bits); +} + +STATIC void mp_machine_adc_block_bits_set(machine_adc_block_obj_t *self, mp_int_t bits) { + if (bits != -1) { + madcblock_bits_helper(self, bits); + } else if (self->width == -1) { + madcblock_bits_helper(self, self->bits); + } +} + +STATIC machine_adc_block_obj_t *mp_machine_adc_block_get(mp_int_t unit) { + for (int i = 0; i < MP_ARRAY_SIZE(madcblock_obj); i++) { + if (unit == madcblock_obj[i].unit_id) { + return &madcblock_obj[i]; + } + } + return NULL; +} + +STATIC machine_adc_obj_t *mp_machine_adc_block_connect(machine_adc_block_obj_t *self, mp_int_t channel_id, mp_hal_pin_obj_t gpio_id, mp_map_t *kw_args) { + const machine_adc_obj_t *adc = madc_search_helper(self, channel_id, gpio_id); + if (adc == NULL) { + return NULL; + } + madc_init_helper(adc, 0, NULL, kw_args); + return (machine_adc_obj_t *)adc; +} diff --git a/ports/esp32/machine_adcblock.c b/ports/esp32/machine_adcblock.c deleted file mode 100644 index 8d5296e2e5..0000000000 --- a/ports/esp32/machine_adcblock.c +++ /dev/null @@ -1,204 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2021 Jonathan Hogg - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include - -#include "esp_log.h" - -#include "driver/gpio.h" -#include "driver/adc.h" - -#include "py/runtime.h" -#include "py/mphal.h" -#include "modmachine.h" -#include "machine_adc.h" -#include "machine_adcblock.h" - -#define DEFAULT_VREF 1100 - -madcblock_obj_t madcblock_obj[] = { - #if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S3 - {{&machine_adcblock_type}, ADC_UNIT_1, 12, -1, {0}}, - {{&machine_adcblock_type}, ADC_UNIT_2, 12, -1, {0}}, - #elif CONFIG_IDF_TARGET_ESP32S2 - {{&machine_adcblock_type}, ADC_UNIT_1, 13, -1, {0}}, - {{&machine_adcblock_type}, ADC_UNIT_2, 13, -1, {0}}, - #endif -}; - -STATIC void madcblock_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { - madcblock_obj_t *self = MP_OBJ_TO_PTR(self_in); - mp_printf(print, "ADCBlock(%u, bits=%u)", self->unit_id, self->bits); -} - -void madcblock_bits_helper(madcblock_obj_t *self, mp_int_t bits) { - switch (bits) { - #if CONFIG_IDF_TARGET_ESP32 - case 9: - self->width = ADC_WIDTH_BIT_9; - break; - case 10: - self->width = ADC_WIDTH_BIT_10; - break; - case 11: - self->width = ADC_WIDTH_BIT_11; - break; - #endif - #if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S3 - case 12: - self->width = ADC_WIDTH_BIT_12; - break; - #endif - #if CONFIG_IDF_TARGET_ESP32S2 - case 13: - self->width = ADC_WIDTH_BIT_13; - break; - #endif - default: - mp_raise_ValueError(MP_ERROR_TEXT("invalid bits")); - } - self->bits = bits; - - if (self->unit_id == ADC_UNIT_1) { - adc1_config_width(self->width); - } - for (adc_atten_t atten = ADC_ATTEN_DB_0; atten < ADC_ATTEN_MAX; atten++) { - if (self->characteristics[atten] != NULL) { - esp_adc_cal_characterize(self->unit_id, atten, self->width, DEFAULT_VREF, self->characteristics[atten]); - } - } -} - -STATIC void madcblock_init_helper(madcblock_obj_t *self, size_t n_pos_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { - ARG_bits, - }; - - static const mp_arg_t allowed_args[] = { - { MP_QSTR_bits, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, - }; - - mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; - mp_arg_parse_all(n_pos_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - - mp_int_t bits = args[ARG_bits].u_int; - if (bits != -1) { - madcblock_bits_helper(self, bits); - } else if (self->width == -1) { - madcblock_bits_helper(self, self->bits); - } -} - -STATIC mp_obj_t madcblock_make_new(const mp_obj_type_t *type, size_t n_pos_args, size_t n_kw_args, const mp_obj_t *args) { - mp_arg_check_num(n_pos_args, n_kw_args, 1, MP_OBJ_FUN_ARGS_MAX, true); - adc_unit_t unit = mp_obj_get_int(args[0]); - madcblock_obj_t *self = NULL; - for (int i = 0; i < MP_ARRAY_SIZE(madcblock_obj); i++) { - if (unit == madcblock_obj[i].unit_id) { - self = &madcblock_obj[i]; - break; - } - } - if (!self) { - mp_raise_ValueError(MP_ERROR_TEXT("invalid block id")); - } - - mp_map_t kw_args; - mp_map_init_fixed_table(&kw_args, n_kw_args, args + n_pos_args); - madcblock_init_helper(self, n_pos_args - 1, args + 1, &kw_args); - - return MP_OBJ_FROM_PTR(self); -} - -STATIC mp_obj_t madcblock_init(size_t n_pos_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - madcblock_obj_t *self = pos_args[0]; - madcblock_init_helper(self, n_pos_args - 1, pos_args + 1, kw_args); - return mp_const_none; -} -STATIC MP_DEFINE_CONST_FUN_OBJ_KW(madcblock_init_obj, 1, madcblock_init); - -STATIC mp_obj_t madcblock_connect(size_t n_pos_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - madcblock_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); - adc_channel_t channel_id = -1; - gpio_num_t gpio_id = -1; - if (n_pos_args == 2) { - if (mp_obj_is_int(pos_args[1])) { - channel_id = mp_obj_get_int(pos_args[1]); - } else { - gpio_id = machine_pin_get_id(pos_args[1]); - } - } else if (n_pos_args == 3) { - channel_id = mp_obj_get_int(pos_args[1]); - gpio_id = machine_pin_get_id(pos_args[2]); - } else { - mp_raise_TypeError(MP_ERROR_TEXT("too many positional args")); - } - - const machine_adc_obj_t *adc = madc_search_helper(self, channel_id, gpio_id); - if (adc != NULL) { - madc_init_helper(adc, 0, pos_args + n_pos_args, kw_args); - return MP_OBJ_FROM_PTR(adc); - } - mp_raise_ValueError(MP_ERROR_TEXT("no matching ADC")); -} -STATIC MP_DEFINE_CONST_FUN_OBJ_KW(madcblock_connect_obj, 2, madcblock_connect); - -mp_int_t madcblock_read_helper(madcblock_obj_t *self, adc_channel_t channel_id) { - int raw; - if (self->unit_id == ADC_UNIT_1) { - raw = adc1_get_raw(channel_id); - } else { - check_esp_err(adc2_get_raw(channel_id, self->width, &raw)); - } - return raw; -} - -mp_int_t madcblock_read_uv_helper(madcblock_obj_t *self, adc_channel_t channel_id, adc_atten_t atten) { - int raw = madcblock_read_helper(self, channel_id); - esp_adc_cal_characteristics_t *adc_chars = self->characteristics[atten]; - if (adc_chars == NULL) { - adc_chars = malloc(sizeof(esp_adc_cal_characteristics_t)); - esp_adc_cal_characterize(self->unit_id, atten, self->width, DEFAULT_VREF, adc_chars); - self->characteristics[atten] = adc_chars; - } - mp_int_t uv = esp_adc_cal_raw_to_voltage(raw, adc_chars) * 1000; - return uv; -} - -STATIC const mp_rom_map_elem_t madcblock_locals_dict_table[] = { - { MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&madcblock_init_obj) }, - { MP_ROM_QSTR(MP_QSTR_connect), MP_ROM_PTR(&madcblock_connect_obj) }, -}; -STATIC MP_DEFINE_CONST_DICT(madcblock_locals_dict, madcblock_locals_dict_table); - -MP_DEFINE_CONST_OBJ_TYPE( - machine_adcblock_type, - MP_QSTR_ADCBlock, - MP_TYPE_FLAG_NONE, - make_new, madcblock_make_new, - print, madcblock_print, - locals_dict, &madcblock_locals_dict - ); diff --git a/ports/esp32/machine_adcblock.h b/ports/esp32/machine_adcblock.h deleted file mode 100644 index 7c9249b072..0000000000 --- a/ports/esp32/machine_adcblock.h +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef MICROPY_INCLUDED_MACHINE_ADCBLOCK_H -#define MICROPY_INCLUDED_MACHINE_ADCBLOCK_H - -#include "esp_adc_cal.h" - -#define ADC_ATTEN_MAX SOC_ADC_ATTEN_NUM - -typedef struct _madcblock_obj_t { - mp_obj_base_t base; - adc_unit_t unit_id; - mp_int_t bits; - adc_bits_width_t width; - esp_adc_cal_characteristics_t *characteristics[ADC_ATTEN_MAX]; -} madcblock_obj_t; - -extern madcblock_obj_t madcblock_obj[]; - -extern void madcblock_bits_helper(madcblock_obj_t *self, mp_int_t bits); -extern mp_int_t madcblock_read_helper(madcblock_obj_t *self, adc_channel_t channel_id); -extern mp_int_t madcblock_read_uv_helper(madcblock_obj_t *self, adc_channel_t channel_id, adc_atten_t atten); - -#endif // MICROPY_INCLUDED_MACHINE_ADCBLOCK_H diff --git a/ports/esp32/modmachine.c b/ports/esp32/modmachine.c index 1e83935a8b..461ddacdad 100644 --- a/ports/esp32/modmachine.c +++ b/ports/esp32/modmachine.c @@ -312,7 +312,9 @@ STATIC const mp_rom_map_elem_t machine_module_globals_table[] = { #if MICROPY_PY_MACHINE_ADC { MP_ROM_QSTR(MP_QSTR_ADC), MP_ROM_PTR(&machine_adc_type) }, #endif - { MP_ROM_QSTR(MP_QSTR_ADCBlock), MP_ROM_PTR(&machine_adcblock_type) }, + #if MICROPY_PY_MACHINE_ADC_BLOCK + { MP_ROM_QSTR(MP_QSTR_ADCBlock), MP_ROM_PTR(&machine_adc_block_type) }, + #endif #if MICROPY_PY_MACHINE_DAC { MP_ROM_QSTR(MP_QSTR_DAC), MP_ROM_PTR(&machine_dac_type) }, #endif diff --git a/ports/esp32/modmachine.h b/ports/esp32/modmachine.h index 7b26df609d..06a1d7b0e2 100644 --- a/ports/esp32/modmachine.h +++ b/ports/esp32/modmachine.h @@ -10,7 +10,6 @@ typedef enum { } wake_type_t; extern const mp_obj_type_t machine_touchpad_type; -extern const mp_obj_type_t machine_adcblock_type; extern const mp_obj_type_t machine_dac_type; extern const mp_obj_type_t machine_sdcard_type; diff --git a/ports/esp32/mpconfigport.h b/ports/esp32/mpconfigport.h index b4ec082092..706baae5b2 100644 --- a/ports/esp32/mpconfigport.h +++ b/ports/esp32/mpconfigport.h @@ -99,10 +99,11 @@ #define MICROPY_PY_MACHINE_ADC (1) #define MICROPY_PY_MACHINE_ADC_INCLUDEFILE "ports/esp32/machine_adc.c" #define MICROPY_PY_MACHINE_ADC_ATTEN_WIDTH (1) -#define MICROPY_PY_MACHINE_ADC_BLOCK (1) #define MICROPY_PY_MACHINE_ADC_INIT (1) #define MICROPY_PY_MACHINE_ADC_READ (1) #define MICROPY_PY_MACHINE_ADC_READ_UV (1) +#define MICROPY_PY_MACHINE_ADC_BLOCK (1) +#define MICROPY_PY_MACHINE_ADC_BLOCK_INCLUDEFILE "ports/esp32/machine_adc_block.c" #define MICROPY_PY_MACHINE_PIN_MAKE_NEW mp_pin_make_new #define MICROPY_PY_MACHINE_BITSTREAM (1) #define MICROPY_PY_MACHINE_PULSE (1) diff --git a/ports/renesas-ra/modmachine.h b/ports/renesas-ra/modmachine.h index 9bb3196b37..22d1fc475e 100644 --- a/ports/renesas-ra/modmachine.h +++ b/ports/renesas-ra/modmachine.h @@ -30,7 +30,6 @@ #include "py/obj.h" extern const mp_obj_type_t machine_touchpad_type; -extern const mp_obj_type_t machine_adcblock_type; extern const mp_obj_type_t machine_dac_type; extern const mp_obj_type_t machine_sdcard_type; From 958c6d917dac86e844458974e6faa8f9552c04f7 Mon Sep 17 00:00:00 2001 From: stijn Date: Tue, 15 Nov 2022 15:13:29 +0100 Subject: [PATCH 22/37] windows: Use the MicroPython logo as application icon. Add a .ico file with common icon image size, created from vector-logo-2.png, and embed it into the resulting executable. Signed-off-by: stijn --- .gitattributes | 1 + logo/vector-logo-2.ico | Bin 0 -> 170356 bytes ports/windows/Makefile | 6 ++++++ ports/windows/README.md | 10 ++++++++++ ports/windows/micropython.rc | 1 + ports/windows/micropython.vcxproj | 3 +++ py/mkenv.mk | 1 + 7 files changed, 22 insertions(+) create mode 100644 logo/vector-logo-2.ico create mode 100644 ports/windows/micropython.rc diff --git a/.gitattributes b/.gitattributes index e6d31d6aa3..fe0c4b47e0 100644 --- a/.gitattributes +++ b/.gitattributes @@ -8,6 +8,7 @@ # These are binary so should never be modified by git. *.a binary +*.ico binary *.png binary *.jpg binary *.dxf binary diff --git a/logo/vector-logo-2.ico b/logo/vector-logo-2.ico new file mode 100644 index 0000000000000000000000000000000000000000..24b12812cf49e2dd31050869020c2a359152d2e2 GIT binary patch literal 170356 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 Lj~fPCw?>|*)OnyZzh5MN?rUd>3gNjr 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 literal 0 HcmV?d00001 diff --git a/ports/windows/Makefile b/ports/windows/Makefile index 47ce664d7b..2215ce38d1 100644 --- a/ports/windows/Makefile +++ b/ports/windows/Makefile @@ -102,3 +102,9 @@ test: $(BUILD)/$(PROG) $(TOP)/tests/run-tests.py test_full: test $(eval DIRNAME=ports/$(notdir $(CURDIR))) cd $(TOP)/tests && MICROPY_MICROPYTHON=../$(DIRNAME)/$(BUILD)/$(PROG) $(PYTHON) ./run-tests.py --via-mpy $(RUN_TESTS_MPY_CROSS_FLAGS) $(RUN_TESTS_SKIP) -d basics float micropython + +$(BUILD)/$(PROG): $(BUILD)/micropython.res + +$(BUILD)/%.res: %.rc + $(ECHO) "WINDRES $<" + $(Q)$(WINDRES) $< -O coff -o $@ diff --git a/ports/windows/README.md b/ports/windows/README.md index 7b5f92f10a..2b3ed44599 100644 --- a/ports/windows/README.md +++ b/ports/windows/README.md @@ -135,3 +135,13 @@ For more info, see https://www.winehq.org/docs/wineusr-guide/cui-programs . If built without line editing and history capabilities (MICROPY_USE_READLINE=0), the resulting binary can be run using the standard `wine` tool. + + +Generating the icon file +------------------------ +The windows builds use a .ico file for the executable logo. +To generate such file from a .png file use ImageMagick, as was done for the icons in the logo/ directory: + + magick convert vector-logo-2.png -define icon:auto-resize="256,128,96,64,48,32,16" vector-logo-2.ico + +Note that for versions prior to 7.0 the command is `convert` instead of `magick convert`. diff --git a/ports/windows/micropython.rc b/ports/windows/micropython.rc new file mode 100644 index 0000000000..8d92bb0d81 --- /dev/null +++ b/ports/windows/micropython.rc @@ -0,0 +1 @@ +app ICON "../../logo/vector-logo-2.ico" diff --git a/ports/windows/micropython.vcxproj b/ports/windows/micropython.vcxproj index de04975cd4..d5affd9e23 100644 --- a/ports/windows/micropython.vcxproj +++ b/ports/windows/micropython.vcxproj @@ -103,6 +103,9 @@ + + + diff --git a/py/mkenv.mk b/py/mkenv.mk index d3dddcc32d..b52dafbc9d 100644 --- a/py/mkenv.mk +++ b/py/mkenv.mk @@ -54,6 +54,7 @@ OBJCOPY = $(CROSS_COMPILE)objcopy SIZE = $(CROSS_COMPILE)size STRIP = $(CROSS_COMPILE)strip AR = $(CROSS_COMPILE)ar +WINDRES = $(CROSS_COMPILE)windres MAKE_MANIFEST = $(PYTHON) $(TOP)/tools/makemanifest.py MAKE_FROZEN = $(PYTHON) $(TOP)/tools/make-frozen.py From dff293840e381444bcd843a325ab3dd7da685733 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 8 Nov 2023 23:34:08 +1100 Subject: [PATCH 23/37] extmod/machine_i2c: Do a fast poll during I2C.scan(). Fixes issue #12912. Signed-off-by: Damien George --- extmod/machine_i2c.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/extmod/machine_i2c.c b/extmod/machine_i2c.c index 44e6921309..6cc2aa5bbf 100644 --- a/extmod/machine_i2c.c +++ b/extmod/machine_i2c.c @@ -328,7 +328,12 @@ STATIC mp_obj_t machine_i2c_scan(mp_obj_t self_in) { if (ret == 0) { mp_obj_list_append(list, MP_OBJ_NEW_SMALL_INT(addr)); } - #ifdef MICROPY_EVENT_POLL_HOOK + // This scan loop may run for some time, so process any pending events/exceptions, + // or allow the port to run any necessary background tasks. But do it as fast as + // possible, in particular we are not waiting on any events. + #if defined(MICROPY_EVENT_POLL_HOOK_FAST) + MICROPY_EVENT_POLL_HOOK_FAST; + #elif defined(MICROPY_EVENT_POLL_HOOK) MICROPY_EVENT_POLL_HOOK #endif } From 4cf741062b20fa34062d236d56b2168515e990b4 Mon Sep 17 00:00:00 2001 From: Andrew Leech Date: Tue, 31 Oct 2023 15:14:05 +1100 Subject: [PATCH 24/37] extmod/vfs_reader: Add file ioctl to set read buffer size. Can be used to speed up importing a file from a vfs based filesystem. Signed-off-by: Andrew Leech --- extmod/vfs_reader.c | 50 ++++++++++++++++++++++++---------- py/stream.h | 1 + tests/extmod/vfs_userfs.py | 18 +++++++++++- tests/extmod/vfs_userfs.py.exp | 16 +++++++++++ 4 files changed, 69 insertions(+), 16 deletions(-) diff --git a/extmod/vfs_reader.c b/extmod/vfs_reader.c index 3c6ff52a99..13fc31bee5 100644 --- a/extmod/vfs_reader.c +++ b/extmod/vfs_reader.c @@ -26,6 +26,7 @@ #include #include +#include #include "py/runtime.h" #include "py/stream.h" @@ -34,33 +35,39 @@ #if MICROPY_READER_VFS +#ifndef MICROPY_READER_VFS_DEFAULT_BUFFER_SIZE +#define MICROPY_READER_VFS_DEFAULT_BUFFER_SIZE (2 * MICROPY_BYTES_PER_GC_BLOCK - offsetof(mp_reader_vfs_t, buf)) +#endif +#define MICROPY_READER_VFS_MIN_BUFFER_SIZE (MICROPY_BYTES_PER_GC_BLOCK - offsetof(mp_reader_vfs_t, buf)) +#define MICROPY_READER_VFS_MAX_BUFFER_SIZE (255) + typedef struct _mp_reader_vfs_t { mp_obj_t file; - uint16_t len; - uint16_t pos; - byte buf[24]; + uint8_t bufpos; + uint8_t buflen; + uint8_t bufsize; + byte buf[]; } mp_reader_vfs_t; STATIC mp_uint_t mp_reader_vfs_readbyte(void *data) { mp_reader_vfs_t *reader = (mp_reader_vfs_t *)data; - if (reader->pos >= reader->len) { - if (reader->len < sizeof(reader->buf)) { + if (reader->bufpos >= reader->buflen) { + if (reader->buflen < reader->bufsize) { return MP_READER_EOF; } else { int errcode; - reader->len = mp_stream_rw(reader->file, reader->buf, sizeof(reader->buf), - &errcode, MP_STREAM_RW_READ | MP_STREAM_RW_ONCE); + reader->buflen = mp_stream_rw(reader->file, reader->buf, reader->bufsize, &errcode, MP_STREAM_RW_READ | MP_STREAM_RW_ONCE); if (errcode != 0) { // TODO handle errors properly return MP_READER_EOF; } - if (reader->len == 0) { + if (reader->buflen == 0) { return MP_READER_EOF; } - reader->pos = 0; + reader->bufpos = 0; } } - return reader->buf[reader->pos++]; + return reader->buf[reader->bufpos++]; } STATIC void mp_reader_vfs_close(void *data) { @@ -70,18 +77,31 @@ STATIC void mp_reader_vfs_close(void *data) { } void mp_reader_new_file(mp_reader_t *reader, qstr filename) { - mp_reader_vfs_t *rf = m_new_obj(mp_reader_vfs_t); mp_obj_t args[2] = { MP_OBJ_NEW_QSTR(filename), MP_OBJ_NEW_QSTR(MP_QSTR_rb), }; - rf->file = mp_vfs_open(MP_ARRAY_SIZE(args), &args[0], (mp_map_t *)&mp_const_empty_map); - int errcode; - rf->len = mp_stream_rw(rf->file, rf->buf, sizeof(rf->buf), &errcode, MP_STREAM_RW_READ | MP_STREAM_RW_ONCE); + mp_obj_t file = mp_vfs_open(MP_ARRAY_SIZE(args), &args[0], (mp_map_t *)&mp_const_empty_map); + + const mp_stream_p_t *stream_p = mp_get_stream(file); + int errcode = 0; + mp_uint_t bufsize = stream_p->ioctl(file, MP_STREAM_GET_BUFFER_SIZE, 0, &errcode); + if (bufsize == MP_STREAM_ERROR || bufsize == 0) { + // bufsize == 0 is included here to support mpremote v1.21 and older where mount file ioctl + // returned 0 by default. + bufsize = MICROPY_READER_VFS_DEFAULT_BUFFER_SIZE; + } else { + bufsize = MIN(MICROPY_READER_VFS_MAX_BUFFER_SIZE, MAX(MICROPY_READER_VFS_MIN_BUFFER_SIZE, bufsize)); + } + + mp_reader_vfs_t *rf = m_new_obj_var(mp_reader_vfs_t, buf, byte, bufsize); + rf->file = file; + rf->bufsize = bufsize; + rf->buflen = mp_stream_rw(rf->file, rf->buf, rf->bufsize, &errcode, MP_STREAM_RW_READ | MP_STREAM_RW_ONCE); if (errcode != 0) { mp_raise_OSError(errcode); } - rf->pos = 0; + rf->bufpos = 0; reader->data = rf; reader->readbyte = mp_reader_vfs_readbyte; reader->close = mp_reader_vfs_close; diff --git a/py/stream.h b/py/stream.h index 6b5c85ab15..e6e6f283df 100644 --- a/py/stream.h +++ b/py/stream.h @@ -43,6 +43,7 @@ #define MP_STREAM_GET_DATA_OPTS (8) // Get data/message options #define MP_STREAM_SET_DATA_OPTS (9) // Set data/message options #define MP_STREAM_GET_FILENO (10) // Get fileno of underlying file +#define MP_STREAM_GET_BUFFER_SIZE (11) // Get preferred buffer size for file // These poll ioctl values are compatible with Linux #define MP_STREAM_POLL_RD (0x0001) diff --git a/tests/extmod/vfs_userfs.py b/tests/extmod/vfs_userfs.py index 36f1088870..5c487315eb 100644 --- a/tests/extmod/vfs_userfs.py +++ b/tests/extmod/vfs_userfs.py @@ -16,6 +16,8 @@ except (ImportError, AttributeError): class UserFile(io.IOBase): + buffer_size = 16 + def __init__(self, mode, data): assert isinstance(data, bytes) self.is_text = mode.find("b") == -1 @@ -39,7 +41,11 @@ class UserFile(io.IOBase): def ioctl(self, req, arg): print("ioctl", req, arg) - return 0 + if req == 4: # MP_STREAM_CLOSE + return 0 + if req == 11: # MP_STREAM_GET_BUFFER_SIZE + return UserFile.buffer_size + return -1 class UserFS: @@ -70,6 +76,8 @@ user_files = { "/usermod2.py": b"print('in usermod2')", "/usermod3.py": b"syntax error", "/usermod4.mpy": b"syntax error", + "/usermod5.py": b"print('in usermod5')", + "/usermod6.py": b"print('in usermod6')", } os.mount(UserFS(user_files), "/userfs") @@ -93,6 +101,14 @@ try: except ValueError: print("ValueError in usermod4") +# Test an import with largest buffer size +UserFile.buffer_size = 255 +import usermod5 + +# Test an import with over-size buffer size (should be safely limited internally) +UserFile.buffer_size = 1024 +import usermod6 + # unmount and undo path addition os.umount("/userfs") sys.path.pop() diff --git a/tests/extmod/vfs_userfs.py.exp b/tests/extmod/vfs_userfs.py.exp index 05cff0452c..be8011d567 100644 --- a/tests/extmod/vfs_userfs.py.exp +++ b/tests/extmod/vfs_userfs.py.exp @@ -3,21 +3,37 @@ some data in a text file stat /usermod1 stat /usermod1.py open /usermod1.py rb +ioctl 11 0 ioctl 4 0 in usermod1 stat /usermod2 stat /usermod2.py open /usermod2.py rb +ioctl 11 0 ioctl 4 0 in usermod2 stat /usermod3 stat /usermod3.py open /usermod3.py rb +ioctl 11 0 ioctl 4 0 SyntaxError in usermod3 stat /usermod4 stat /usermod4.py stat /usermod4.mpy open /usermod4.mpy rb +ioctl 11 0 ioctl 4 0 ValueError in usermod4 +stat /usermod5 +stat /usermod5.py +open /usermod5.py rb +ioctl 11 0 +ioctl 4 0 +in usermod5 +stat /usermod6 +stat /usermod6.py +open /usermod6.py rb +ioctl 11 0 +ioctl 4 0 +in usermod6 From bbc5a18d092425bee802d88a5c9ed3516056bdd5 Mon Sep 17 00:00:00 2001 From: Andrew Leech Date: Wed, 1 Nov 2023 15:14:37 +1100 Subject: [PATCH 25/37] tools/mpremote: Add ioctl to specify large read buffer size. Speeds up importing files from mounted filesystem. Also fix the return code for invalid / unsupported ioctl requests. Signed-off-by: Andrew Leech --- tools/mpremote/mpremote/transport_serial.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tools/mpremote/mpremote/transport_serial.py b/tools/mpremote/mpremote/transport_serial.py index 23a379d16b..3b4cd00078 100644 --- a/tools/mpremote/mpremote/transport_serial.py +++ b/tools/mpremote/mpremote/transport_serial.py @@ -753,6 +753,13 @@ class RemoteFile(io.IOBase): machine.mem32[arg] = self.seek(machine.mem32[arg], machine.mem32[arg + 4]) elif request == 4: # CLOSE self.close() + elif request == 11: # BUFFER_SIZE + # This is used as the vfs_reader buffer. n + 4 should be less than 255 to + # fit in stdin ringbuffer on supported ports. n + 7 should be multiple of 16 + # to efficiently use gc blocks in mp_reader_vfs_t. + return 249 + else: + return -1 return 0 def flush(self): From 2d363a23cb9e825200e772f973eab921a69e0646 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 25 Oct 2023 11:04:54 +1100 Subject: [PATCH 26/37] shared/tinyusb: Schedule TinyUSB task function from dcd_event_handler. dcd_event_handler() is called from the IRQ when a new DCD event is queued for processing by the TinyUSB thread mode task. This lets us queue the handler to run immediately when MicroPython resumes. Currently this relies on a linker --wrap hack to work, but a PR has been submitted to TinyUSB to allow the function to be called inline from dcd_event_handler() itself. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- shared/tinyusb/mp_usbd.c | 23 +++++++++++++++++++++++ shared/tinyusb/mp_usbd.h | 3 --- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/shared/tinyusb/mp_usbd.c b/shared/tinyusb/mp_usbd.c index ea1de674bc..87c10310f7 100644 --- a/shared/tinyusb/mp_usbd.c +++ b/shared/tinyusb/mp_usbd.c @@ -27,17 +27,40 @@ #include #include "py/mpconfig.h" +#include "py/runtime.h" #if MICROPY_HW_ENABLE_USBDEV #ifndef NO_QSTR #include "tusb.h" // TinyUSB is not available when running the string preprocessor +#include "device/dcd.h" #include "device/usbd.h" #include "device/usbd_pvt.h" #endif +// Legacy TinyUSB task function wrapper, called by some ports from the interpreter hook void usbd_task(void) { tud_task_ext(0, false); } +// TinyUSB task function wrapper, as scheduled from the USB IRQ +static void mp_usbd_task(mp_sched_node_t *node); + +extern void __real_dcd_event_handler(dcd_event_t const *event, bool in_isr); + +// If -Wl,--wrap=dcd_event_handler is passed to the linker, then this wrapper +// will be called and allows MicroPython to schedule the TinyUSB task when +// dcd_event_handler() is called from an ISR. +TU_ATTR_FAST_FUNC void __wrap_dcd_event_handler(dcd_event_t const *event, bool in_isr) { + static mp_sched_node_t usbd_task_node; + + __real_dcd_event_handler(event, in_isr); + mp_sched_schedule_node(&usbd_task_node, mp_usbd_task); +} + +static void mp_usbd_task(mp_sched_node_t *node) { + (void)node; + tud_task_ext(0, false); +} + #endif diff --git a/shared/tinyusb/mp_usbd.h b/shared/tinyusb/mp_usbd.h index 3a93b929c5..2e4feaca9f 100644 --- a/shared/tinyusb/mp_usbd.h +++ b/shared/tinyusb/mp_usbd.h @@ -29,9 +29,6 @@ #include "py/obj.h" -// Call instead of tud_task() -void mp_usbd_task(void); - // Function to be implemented in port code. // Can write a string up to MICROPY_HW_USB_DESC_STR_MAX characters long, plus terminating byte. extern void mp_usbd_port_get_serial_number(char *buf); From bcbdee235719d459a4cd60d51021454fba54cd0f Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 25 Oct 2023 13:50:53 +1100 Subject: [PATCH 27/37] rp2: Change to use TinyUSB dcd_event_handler hook. This change: - Has a small code size reduction. - Should slightly improve overall performance. The old hook code seemed to use between 0.1% and 1.6% of the total CPU time doing no-op calls even when no USB work was required. - USB performance is mostly the same, there is a small increase in latency for some workloads that seems to because sometimes the hook usbd_task() is called at the right time to line up with the next USB host request. This only happened semi-randomly due to the timing of the hook. Improving the wakeup latency by switching rp2 to tickless WFE allows the usbd_task() to run in time for the next USB host request almost always, improving performance and more than offsetting this impact. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- ports/rp2/CMakeLists.txt | 1 + ports/rp2/mpconfigport.h | 15 --------------- 2 files changed, 1 insertion(+), 15 deletions(-) diff --git a/ports/rp2/CMakeLists.txt b/ports/rp2/CMakeLists.txt index 4f76c5864f..15f6f6ca1c 100644 --- a/ports/rp2/CMakeLists.txt +++ b/ports/rp2/CMakeLists.txt @@ -406,6 +406,7 @@ target_compile_options(${MICROPY_TARGET} PRIVATE target_link_options(${MICROPY_TARGET} PRIVATE -Wl,--defsym=__micropy_c_heap_size__=${MICROPY_C_HEAP_SIZE} + -Wl,--wrap=dcd_event_handler ) set_source_files_properties( diff --git a/ports/rp2/mpconfigport.h b/ports/rp2/mpconfigport.h index 84a961db43..61749f108a 100644 --- a/ports/rp2/mpconfigport.h +++ b/ports/rp2/mpconfigport.h @@ -250,23 +250,8 @@ extern void mp_thread_end_atomic_section(uint32_t); #define MICROPY_PY_LWIP_REENTER lwip_lock_acquire(); #define MICROPY_PY_LWIP_EXIT lwip_lock_release(); -#if MICROPY_HW_ENABLE_USBDEV -#define MICROPY_HW_USBDEV_TASK_HOOK extern void usbd_task(void); usbd_task(); -#define MICROPY_VM_HOOK_COUNT (10) -#define MICROPY_VM_HOOK_INIT static uint vm_hook_divisor = MICROPY_VM_HOOK_COUNT; -#define MICROPY_VM_HOOK_POLL if (get_core_num() == 0 && --vm_hook_divisor == 0) { \ - vm_hook_divisor = MICROPY_VM_HOOK_COUNT; \ - MICROPY_HW_USBDEV_TASK_HOOK \ -} -#define MICROPY_VM_HOOK_LOOP MICROPY_VM_HOOK_POLL -#define MICROPY_VM_HOOK_RETURN MICROPY_VM_HOOK_POLL -#else -#define MICROPY_HW_USBDEV_TASK_HOOK -#endif - #define MICROPY_EVENT_POLL_HOOK_FAST \ do { \ - if (get_core_num() == 0) { MICROPY_HW_USBDEV_TASK_HOOK } \ extern void mp_handle_pending(bool); \ mp_handle_pending(true); \ } while (0) From 26d5032980335ccdfe81234ad7a24030a580f6d3 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 1 Nov 2023 15:30:05 +1100 Subject: [PATCH 28/37] samd: Switch TinyUSB to run via a scheduled task. Previously the TinyUSB task was run in the ISR immediately after the interrupt handler. This approach gives very similar performance (no change in CDC throughput tests) but reduces the amount of time spent in the ISR, and allows TinyUSB callbacks to run in thread mode. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- ports/samd/Makefile | 3 +++ ports/samd/samd_isr.c | 8 ++++---- ports/samd/samd_soc.h | 4 ---- ports/samd/tusb_port.c | 27 --------------------------- 4 files changed, 7 insertions(+), 35 deletions(-) diff --git a/ports/samd/Makefile b/ports/samd/Makefile index 291a894ab7..12223e6ded 100644 --- a/ports/samd/Makefile +++ b/ports/samd/Makefile @@ -93,6 +93,8 @@ LIBSTDCPP_FILE_NAME = "$(shell $(CXX) $(CXXFLAGS) -print-file-name=libstdc++.a)" LDFLAGS += -L"$(shell dirname $(LIBSTDCPP_FILE_NAME))" endif +LDFLAGS += --wrap=dcd_event_handler + MPY_CROSS_FLAGS += -march=$(MPY_CROSS_MCU_ARCH) SRC_C += \ @@ -131,6 +133,7 @@ SHARED_SRC_C += \ shared/runtime/sys_stdio_mphal.c \ shared/timeutils/timeutils.c \ shared/tinyusb/mp_cdc_common.c \ + shared/tinyusb/mp_usbd.c ASF4_SRC_C += $(addprefix lib/asf4/$(MCU_SERIES_LOWER)/,\ hal/src/hal_atomic.c \ diff --git a/ports/samd/samd_isr.c b/ports/samd/samd_isr.c index a12140313b..7c4c1d060f 100644 --- a/ports/samd/samd_isr.c +++ b/ports/samd/samd_isr.c @@ -310,10 +310,10 @@ const ISR isr_vector[] __attribute__((section(".isr_vector"))) = { &Sercom7_Handler, // 77 Serial Communication Interface 7 (SERCOM7): SERCOM7_3 - 6 0, // 78 Control Area Network 0 (CAN0) 0, // 79 Control Area Network 1 (CAN1) - &USB_0_Handler_wrapper, // 80 Universal Serial Bus (USB): USB_EORSM_DNRS, ... - &USB_1_Handler_wrapper, // 81 Universal Serial Bus (USB): USB_SOF_HSOF - &USB_2_Handler_wrapper, // 82 Universal Serial Bus (USB): USB_TRCPT0_0 - _7 - &USB_3_Handler_wrapper, // 83 Universal Serial Bus (USB): USB_TRCPT1_0 - _7 + &USB_Handler_wrapper, // 80 Universal Serial Bus (USB): USB_EORSM_DNRS, ... + &USB_Handler_wrapper, // 81 Universal Serial Bus (USB): USB_SOF_HSOF + &USB_Handler_wrapper, // 82 Universal Serial Bus (USB): USB_TRCPT0_0 - _7 + &USB_Handler_wrapper, // 83 Universal Serial Bus (USB): USB_TRCPT1_0 - _7 0, // 84 Ethernet MAC (GMAC) 0, // 85 Timer Counter Control 0 (TCC0): TCC0_CNT_A ... 0, // 86 Timer Counter Control 0 (TCC0): TCC0_MC_0 diff --git a/ports/samd/samd_soc.h b/ports/samd/samd_soc.h index 1848cf7db1..90a5a57ffa 100644 --- a/ports/samd/samd_soc.h +++ b/ports/samd/samd_soc.h @@ -36,10 +36,6 @@ void samd_init(void); void samd_main(void); void USB_Handler_wrapper(void); -void USB_0_Handler_wrapper(void); -void USB_1_Handler_wrapper(void); -void USB_2_Handler_wrapper(void); -void USB_3_Handler_wrapper(void); void sercom_enable(Sercom *spi, int state); void sercom_register_irq(int sercom_id, void (*sercom_irq_handler)); diff --git a/ports/samd/tusb_port.c b/ports/samd/tusb_port.c index 2098334fba..e56ef0fd6a 100644 --- a/ports/samd/tusb_port.c +++ b/ports/samd/tusb_port.c @@ -117,33 +117,6 @@ const uint16_t *tud_descriptor_string_cb(uint8_t index, uint16_t langid) { return desc_str; } -#if defined(MCU_SAMD21) - void USB_Handler_wrapper(void) { tud_int_handler(0); - tud_task(); } - -#elif defined(MCU_SAMD51) - -void USB_0_Handler_wrapper(void) { - tud_int_handler(0); - tud_task(); -} - -void USB_1_Handler_wrapper(void) { - tud_int_handler(0); - tud_task(); -} - -void USB_2_Handler_wrapper(void) { - tud_int_handler(0); - tud_task(); -} - -void USB_3_Handler_wrapper(void) { - tud_int_handler(0); - tud_task(); -} - -#endif From 4679a90097cc881c9d53d6e0dd0dde1c4c2de99c Mon Sep 17 00:00:00 2001 From: Andrew Leech Date: Wed, 8 Nov 2023 15:50:47 +1100 Subject: [PATCH 29/37] CODECONVENTIONS: Update for change from black to ruff format. Also add notes on running pre-commit manually. Signed-off-by: Andrew Leech --- CODECONVENTIONS.md | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/CODECONVENTIONS.md b/CODECONVENTIONS.md index d44b382b25..d6b0a5926b 100644 --- a/CODECONVENTIONS.md +++ b/CODECONVENTIONS.md @@ -53,13 +53,16 @@ are then certifying and signing off against the following: Code auto-formatting ==================== -Both C and Python code are auto-formatted using the `tools/codeformat.py` -script. This uses [uncrustify](https://github.com/uncrustify/uncrustify) to -format C code and [black](https://github.com/psf/black) to format Python code. -After making changes, and before committing, run this tool to reformat your -changes to the correct style. Without arguments this tool will reformat all -source code (and may take some time to run). Otherwise pass as arguments to -the tool the files that changed and it will only reformat those. +Both C and Python code formatting are controlled for consistency across the +MicroPython codebase. C code is formatted using the `tools/codeformat.py` +script which uses [uncrustify](https://github.com/uncrustify/uncrustify). +Python code is linted and formatted using +[ruff & ruff format](https://github.com/astral-sh/ruff). +After making changes, and before committing, run `tools/codeformat.py` to +reformat your C code and `ruff format` for any Python code. Without +arguments this tool will reformat all source code (and may take some time +to run). Otherwise pass as arguments to the tool the files that changed, +and it will only reformat those. uncrustify ========== @@ -151,12 +154,22 @@ Tips: * To ignore the pre-commit message format check temporarily, start the commit message subject line with "WIP" (for "Work In Progress"). +Running pre-commit manually +=========================== + +Once pre-commit is installed as per the previous section it can be manually +run against the MicroPython python codebase to update file formatting on +demand, with either: +* `pre-commit run --all-files` to fix all files in the MicroPython codebase +* `pre-commit run --file ./path/to/my/file` to fix just one file +* `pre-commit run --file ./path/to/my/folder/*` to fix just one folder + Python code conventions ======================= Python code follows [PEP 8](https://legacy.python.org/dev/peps/pep-0008/) and -is auto-formatted using [black](https://github.com/psf/black) with a line-length -of 99 characters. +is auto-formatted using [ruff format](https://docs.astral.sh/ruff/formatter) +with a line-length of 99 characters. Naming conventions: - Module names are short and all lowercase; eg pyb, stm. From f07f90f1abfd6784db9c47a5602fae319991f8e7 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Thu, 19 Oct 2023 12:43:17 +0200 Subject: [PATCH 30/37] mimxrt/boards/OLIMEX_RT1010: Adjust the UART pin assignment. Olimex asked for that, getting a UART at the UEXT1 connector as well. Signed-off-by: robert-hh --- docs/mimxrt/pinout.rst | 2 +- ports/mimxrt/boards/OLIMEX_RT1010/mpconfigboard.h | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/mimxrt/pinout.rst b/docs/mimxrt/pinout.rst index b5a7fbbfe0..16bb472273 100644 --- a/docs/mimxrt/pinout.rst +++ b/docs/mimxrt/pinout.rst @@ -28,7 +28,7 @@ MIMXRT1060-EVK Debug USB D0/D1 D7/D6 D8/D9 MIMXRT1064-EVK Debug USB D0/D1 D7/D6 D8/D9 MIMXRT1170-EVK Debug USB D0/D1 D12/D11 D10/D13 Adafruit Metro M7 - D0/D1 D7/D3 A1/A0 -Olimex RT1010Py - RxD/TxD D5/D6 - +Olimex RT1010Py - RxD/TxD D7/D8 D5/D6 Seeed ARCH MIX - J3_19/J3_20 J4_16/J4_17 J4_06/J4_07 ================= =========== =========== =========== =========== diff --git a/ports/mimxrt/boards/OLIMEX_RT1010/mpconfigboard.h b/ports/mimxrt/boards/OLIMEX_RT1010/mpconfigboard.h index 324cee9b14..1ad2e9df5e 100644 --- a/ports/mimxrt/boards/OLIMEX_RT1010/mpconfigboard.h +++ b/ports/mimxrt/boards/OLIMEX_RT1010/mpconfigboard.h @@ -18,18 +18,18 @@ // LPUART4 on D5/D6 -> 2 #define MICROPY_HW_UART_NUM (sizeof(uart_index_table) / sizeof(uart_index_table)[0]) -#define MICROPY_HW_UART_INDEX { 0, 1, 4 } +#define MICROPY_HW_UART_INDEX { 0, 1, 3, 4 } #define IOMUX_TABLE_UART \ { IOMUXC_GPIO_10_LPUART1_TXD }, { IOMUXC_GPIO_09_LPUART1_RXD }, \ { 0 }, { 0 }, \ - { 0 }, { 0 }, \ + { IOMUXC_GPIO_08_LPUART3_TXD }, { IOMUXC_GPIO_07_LPUART3_RXD }, \ { IOMUXC_GPIO_06_LPUART4_TXD }, { IOMUXC_GPIO_05_LPUART4_RXD }, #define IOMUX_TABLE_UART_CTS_RTS \ { IOMUXC_GPIO_08_LPUART1_CTS_B }, { IOMUXC_GPIO_07_LPUART1_RTS_B }, \ { 0 }, { 0 }, \ - { 0 }, { 0 }, \ + { IOMUXC_GPIO_AD_14_LPUART3_CTS_B }, { IOMUXC_GPIO_AD_13_LPUART3_RTS_B }, \ { IOMUXC_GPIO_AD_14_LPUART4_CTS_B }, { IOMUXC_GPIO_AD_13_LPUART4_RTS_B }, #define MICROPY_HW_SPI_INDEX { 0, 1, 2 } From fbb7c32040c407a60126a879dc2afe07a0ef6e5d Mon Sep 17 00:00:00 2001 From: Mark Blakeney Date: Tue, 24 Oct 2023 10:24:26 +1000 Subject: [PATCH 31/37] esp32/esp32_rmt: Change RMT.source_freq() to class method. To create an esp32.RMT() instance with an optimum (i.e. highest resolution) clock_div is currently awkward because you need to know the source clock frequency to calculate the best clock_div, but unfortunately that is only currently available as an source_freq() method on the instance after you have already created it. So RMT.source_freq() should really be a class method, not an instance method. This change is backwards compatible for existing code because you can still reference that function from an instance, or now also, from the class. Signed-off-by: Mark Blakeney --- docs/library/esp32.rst | 2 +- ports/esp32/esp32_rmt.c | 9 ++++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/docs/library/esp32.rst b/docs/library/esp32.rst index 7701f43539..796dbbbc54 100644 --- a/docs/library/esp32.rst +++ b/docs/library/esp32.rst @@ -226,7 +226,7 @@ For more details see Espressif's `ESP-IDF RMT documentation. ``100``) and the output level to apply the carrier to (a boolean as per *idle_level*). -.. method:: RMT.source_freq() +.. classmethod:: RMT.source_freq() Returns the source clock frequency. Currently the source clock is not configurable so this will always return 80MHz. diff --git a/ports/esp32/esp32_rmt.c b/ports/esp32/esp32_rmt.c index f92af636f5..14caffddad 100644 --- a/ports/esp32/esp32_rmt.c +++ b/ports/esp32/esp32_rmt.c @@ -206,10 +206,11 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_1(esp32_rmt_deinit_obj, esp32_rmt_deinit); // Return the source frequency. // Currently only the APB clock (80MHz) can be used but it is possible other // clock sources will added in the future. -STATIC mp_obj_t esp32_rmt_source_freq(mp_obj_t self_in) { +STATIC mp_obj_t esp32_rmt_source_freq() { return mp_obj_new_int(APB_CLK_FREQ); } -STATIC MP_DEFINE_CONST_FUN_OBJ_1(esp32_rmt_source_freq_obj, esp32_rmt_source_freq); +STATIC MP_DEFINE_CONST_FUN_OBJ_0(esp32_rmt_source_freq_obj, esp32_rmt_source_freq); +STATIC MP_DEFINE_CONST_STATICMETHOD_OBJ(esp32_rmt_source_obj, MP_ROM_PTR(&esp32_rmt_source_freq_obj)); // Return the clock divider. STATIC mp_obj_t esp32_rmt_clock_div(mp_obj_t self_in) { @@ -357,7 +358,6 @@ STATIC MP_DEFINE_CONST_STATICMETHOD_OBJ(esp32_rmt_bitstream_channel_obj, MP_ROM_ STATIC const mp_rom_map_elem_t esp32_rmt_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&esp32_rmt_deinit_obj) }, { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&esp32_rmt_deinit_obj) }, - { MP_ROM_QSTR(MP_QSTR_source_freq), MP_ROM_PTR(&esp32_rmt_source_freq_obj) }, { MP_ROM_QSTR(MP_QSTR_clock_div), MP_ROM_PTR(&esp32_rmt_clock_div_obj) }, { MP_ROM_QSTR(MP_QSTR_wait_done), MP_ROM_PTR(&esp32_rmt_wait_done_obj) }, { MP_ROM_QSTR(MP_QSTR_loop), MP_ROM_PTR(&esp32_rmt_loop_obj) }, @@ -365,6 +365,9 @@ STATIC const mp_rom_map_elem_t esp32_rmt_locals_dict_table[] = { // Static methods { MP_ROM_QSTR(MP_QSTR_bitstream_channel), MP_ROM_PTR(&esp32_rmt_bitstream_channel_obj) }, + + // Class methods + { MP_ROM_QSTR(MP_QSTR_source_freq), MP_ROM_PTR(&esp32_rmt_source_obj) }, }; STATIC MP_DEFINE_CONST_DICT(esp32_rmt_locals_dict, esp32_rmt_locals_dict_table); From 2888c5b23057b056bdeed6fcd2af414ddf2ef0c4 Mon Sep 17 00:00:00 2001 From: Mark Blakeney Date: Tue, 31 Oct 2023 14:24:43 +1000 Subject: [PATCH 32/37] esp32/esp32_rmt: Add RMT.PULSE_MAX constant. If you have a variable frequency and pulse width, and you want to optimize pulse resolution, then you must do a calculation beforehand to ensure you normalize the array to keep all list values within bound. That calculation requires RMT.source_freq(), RMT.clock_div(), and this 32767 constant. Signed-off-by: Mark Blakeney --- docs/library/esp32.rst | 17 ++++++++++++----- ports/esp32/esp32_rmt.c | 3 +++ 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/docs/library/esp32.rst b/docs/library/esp32.rst index 796dbbbc54..422329bf1e 100644 --- a/docs/library/esp32.rst +++ b/docs/library/esp32.rst @@ -193,7 +193,7 @@ numbers specified in ``write_pulses`` are multiplied by the resolution to define the pulses. ``clock_div`` is an 8-bit divider (0-255) and each pulse can be defined by -multiplying the resolution by a 15-bit (0-32,768) number. There are eight +multiplying the resolution by a 15-bit (1-``PULSE_MAX``) number. There are eight channels (0-7) and each can have a different clock divider. So, in the example above, the 80MHz clock is divided by 8. Thus the @@ -264,10 +264,10 @@ For more details see Espressif's `ESP-IDF RMT documentation. **Mode 3:** *duration* and *data* are lists or tuples of equal length, specifying individual durations and the output level for each. - Durations are in integer units of the channel resolution (as described - above), between 1 and 32767 units. Output levels are any value that can - be converted to a boolean, with ``True`` representing high voltage and - ``False`` representing low. + Durations are in integer units of the channel resolution (as + described above), between 1 and ``PULSE_MAX`` units. Output levels + are any value that can be converted to a boolean, with ``True`` + representing high voltage and ``False`` representing low. If transmission of an earlier sequence is in progress then this method will block until that transmission is complete before beginning the new sequence. @@ -290,6 +290,13 @@ For more details see Espressif's `ESP-IDF RMT documentation. Passing in no argument will not change the channel. This function returns the current channel number. +Constants +--------- + +.. data:: RMT.PULSE_MAX + + Maximum integer that can be set for a pulse duration. + Ultra-Low-Power co-processor ---------------------------- diff --git a/ports/esp32/esp32_rmt.c b/ports/esp32/esp32_rmt.c index 14caffddad..5d53018f4b 100644 --- a/ports/esp32/esp32_rmt.c +++ b/ports/esp32/esp32_rmt.c @@ -368,6 +368,9 @@ STATIC const mp_rom_map_elem_t esp32_rmt_locals_dict_table[] = { // Class methods { MP_ROM_QSTR(MP_QSTR_source_freq), MP_ROM_PTR(&esp32_rmt_source_obj) }, + + // Constants + { MP_ROM_QSTR(MP_QSTR_PULSE_MAX), MP_ROM_INT(32767) }, }; STATIC MP_DEFINE_CONST_DICT(esp32_rmt_locals_dict, esp32_rmt_locals_dict_table); From af52e1ff24b0b543f6fdb3bc937713a6ab94d506 Mon Sep 17 00:00:00 2001 From: Jim Mussared Date: Wed, 8 Nov 2023 13:01:08 +1100 Subject: [PATCH 33/37] stm32/boards/NUCLEO_WL55: Freeze LoRa driver. This adds the sync version of the LoRa driver (and the base WL55 driver). Adds +13.6kiB (212.6 -> 226.2). Limit for this board is 232kiB. This work was funded through GitHub Sponsors. Signed-off-by: Jim Mussared --- ports/stm32/boards/NUCLEO_WL55/manifest.py | 6 ++++++ ports/stm32/boards/NUCLEO_WL55/mpconfigboard.mk | 4 ++-- 2 files changed, 8 insertions(+), 2 deletions(-) create mode 100644 ports/stm32/boards/NUCLEO_WL55/manifest.py diff --git a/ports/stm32/boards/NUCLEO_WL55/manifest.py b/ports/stm32/boards/NUCLEO_WL55/manifest.py new file mode 100644 index 0000000000..8998166a3e --- /dev/null +++ b/ports/stm32/boards/NUCLEO_WL55/manifest.py @@ -0,0 +1,6 @@ +# Don't include default frozen modules because MCU is tight on flash space. + +# Only install the sync version of the LoRa driver because this board doesn't +# have asyncio by default. +require("lora-sync") +require("lora-stm32wl5") diff --git a/ports/stm32/boards/NUCLEO_WL55/mpconfigboard.mk b/ports/stm32/boards/NUCLEO_WL55/mpconfigboard.mk index 210f3058c1..ced2e7619b 100644 --- a/ports/stm32/boards/NUCLEO_WL55/mpconfigboard.mk +++ b/ports/stm32/boards/NUCLEO_WL55/mpconfigboard.mk @@ -9,5 +9,5 @@ TEXT0_ADDR = 0x08000000 MICROPY_VFS_FAT = 0 MICROPY_VFS_LFS2 = 1 -# Don't include default frozen modules because MCU is tight on flash space -FROZEN_MANIFEST ?= +# Board-specific manifest (doesn't include default modules, adds LoRa driver). +FROZEN_MANIFEST ?= $(BOARD_DIR)/manifest.py From 3b954698fa036ab34fe578ce46ff9fa5cf6ca4ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dani=C3=ABl=20van=20de=20Giessen?= Date: Mon, 30 Oct 2023 15:25:38 +0100 Subject: [PATCH 34/37] extmod/modbluetooth: Initialise nlr_jump_callback_top for IRQ handlers. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Similar to 3883f29485fad21105f6f965d56bfec842b8cfbb where this change was implemented for threads: when the Bluetooth IRQ handler is called the thread state is not not zero-initialized and thus we need to manually set this to NULL. Fixes issue #12239. Signed-off-by: DaniĆ«l van de Giessen --- extmod/modbluetooth.c | 1 + 1 file changed, 1 insertion(+) diff --git a/extmod/modbluetooth.c b/extmod/modbluetooth.c index 3417df7a27..cef5151cc9 100644 --- a/extmod/modbluetooth.c +++ b/extmod/modbluetooth.c @@ -1276,6 +1276,7 @@ STATIC mp_obj_t invoke_irq_handler(uint16_t event, mp_stack_set_top(&ts + 1); // need to include ts in root-pointer scan mp_stack_set_limit(MICROPY_PY_BLUETOOTH_SYNC_EVENT_STACK_SIZE - 1024); ts.gc_lock_depth = 0; + ts.nlr_jump_callback_top = NULL; ts.mp_pending_exception = MP_OBJ_NULL; mp_locals_set(mp_state_ctx.thread.dict_locals); // set from the outer context mp_globals_set(mp_state_ctx.thread.dict_globals); // set from the outer context From 365913953a4e050eab45c00e637e92f612400b11 Mon Sep 17 00:00:00 2001 From: stijn Date: Mon, 6 Nov 2023 15:48:13 +0100 Subject: [PATCH 35/37] extmod/vfs_posix_file: Make standard file objects non-const. Fixes undefined behavior when calling vfs_posix_file_ioctl with MP_STREAM_CLOSE as request because that casts away the constness and assigns -1 to the object's fd member. Fixes issue #12670. Signed-off-by: stijn --- extmod/vfs_posix_file.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/extmod/vfs_posix_file.c b/extmod/vfs_posix_file.c index 81a608d2b6..2f70789543 100644 --- a/extmod/vfs_posix_file.c +++ b/extmod/vfs_posix_file.c @@ -279,14 +279,14 @@ STATIC const mp_stream_p_t vfs_posix_textio_stream_p = { #if MICROPY_PY_SYS_STDIO_BUFFER -const mp_obj_vfs_posix_file_t mp_sys_stdin_buffer_obj = {{&mp_type_vfs_posix_fileio}, STDIN_FILENO}; -const mp_obj_vfs_posix_file_t mp_sys_stdout_buffer_obj = {{&mp_type_vfs_posix_fileio}, STDOUT_FILENO}; -const mp_obj_vfs_posix_file_t mp_sys_stderr_buffer_obj = {{&mp_type_vfs_posix_fileio}, STDERR_FILENO}; +mp_obj_vfs_posix_file_t mp_sys_stdin_buffer_obj = {{&mp_type_vfs_posix_fileio}, STDIN_FILENO}; +mp_obj_vfs_posix_file_t mp_sys_stdout_buffer_obj = {{&mp_type_vfs_posix_fileio}, STDOUT_FILENO}; +mp_obj_vfs_posix_file_t mp_sys_stderr_buffer_obj = {{&mp_type_vfs_posix_fileio}, STDERR_FILENO}; // Forward declarations. -const mp_obj_vfs_posix_file_t mp_sys_stdin_obj; -const mp_obj_vfs_posix_file_t mp_sys_stdout_obj; -const mp_obj_vfs_posix_file_t mp_sys_stderr_obj; +mp_obj_vfs_posix_file_t mp_sys_stdin_obj; +mp_obj_vfs_posix_file_t mp_sys_stdout_obj; +mp_obj_vfs_posix_file_t mp_sys_stderr_obj; STATIC void vfs_posix_textio_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { if (dest[0] != MP_OBJ_NULL) { @@ -332,8 +332,8 @@ MP_DEFINE_CONST_OBJ_TYPE( locals_dict, &vfs_posix_rawfile_locals_dict ); -const mp_obj_vfs_posix_file_t mp_sys_stdin_obj = {{&mp_type_vfs_posix_textio}, STDIN_FILENO}; -const mp_obj_vfs_posix_file_t mp_sys_stdout_obj = {{&mp_type_vfs_posix_textio}, STDOUT_FILENO}; -const mp_obj_vfs_posix_file_t mp_sys_stderr_obj = {{&mp_type_vfs_posix_textio}, STDERR_FILENO}; +mp_obj_vfs_posix_file_t mp_sys_stdin_obj = {{&mp_type_vfs_posix_textio}, STDIN_FILENO}; +mp_obj_vfs_posix_file_t mp_sys_stdout_obj = {{&mp_type_vfs_posix_textio}, STDOUT_FILENO}; +mp_obj_vfs_posix_file_t mp_sys_stderr_obj = {{&mp_type_vfs_posix_textio}, STDERR_FILENO}; #endif // MICROPY_VFS_POSIX From d46dc5e1738d843b83f1668798669ff177119d03 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 9 Nov 2023 16:56:06 +1100 Subject: [PATCH 36/37] shared/tinyusb: Expose mp_usbd_task as a public function. Signed-off-by: Damien George --- ports/renesas-ra/mpconfigport.h | 2 +- shared/tinyusb/mp_usbd.c | 17 ++++++++--------- shared/tinyusb/mp_usbd.h | 3 +++ 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/ports/renesas-ra/mpconfigport.h b/ports/renesas-ra/mpconfigport.h index d3bc3235fd..222d5b919d 100644 --- a/ports/renesas-ra/mpconfigport.h +++ b/ports/renesas-ra/mpconfigport.h @@ -272,7 +272,7 @@ static inline mp_uint_t disable_irq(void) { #define MICROPY_END_ATOMIC_SECTION(state) enable_irq(state) #if MICROPY_HW_ENABLE_USBDEV -#define MICROPY_HW_USBDEV_TASK_HOOK extern void usbd_task(void); usbd_task(); +#define MICROPY_HW_USBDEV_TASK_HOOK extern void mp_usbd_task(void); mp_usbd_task(); #define MICROPY_VM_HOOK_COUNT (10) #define MICROPY_VM_HOOK_INIT static uint vm_hook_divisor = MICROPY_VM_HOOK_COUNT; #define MICROPY_VM_HOOK_POLL if (--vm_hook_divisor == 0) { \ diff --git a/shared/tinyusb/mp_usbd.c b/shared/tinyusb/mp_usbd.c index 87c10310f7..55af3d4fb4 100644 --- a/shared/tinyusb/mp_usbd.c +++ b/shared/tinyusb/mp_usbd.c @@ -38,16 +38,15 @@ #include "device/usbd_pvt.h" #endif -// Legacy TinyUSB task function wrapper, called by some ports from the interpreter hook -void usbd_task(void) { - tud_task_ext(0, false); -} - // TinyUSB task function wrapper, as scheduled from the USB IRQ -static void mp_usbd_task(mp_sched_node_t *node); +static void mp_usbd_task_callback(mp_sched_node_t *node); extern void __real_dcd_event_handler(dcd_event_t const *event, bool in_isr); +void mp_usbd_task(void) { + tud_task_ext(0, false); +} + // If -Wl,--wrap=dcd_event_handler is passed to the linker, then this wrapper // will be called and allows MicroPython to schedule the TinyUSB task when // dcd_event_handler() is called from an ISR. @@ -55,12 +54,12 @@ TU_ATTR_FAST_FUNC void __wrap_dcd_event_handler(dcd_event_t const *event, bool i static mp_sched_node_t usbd_task_node; __real_dcd_event_handler(event, in_isr); - mp_sched_schedule_node(&usbd_task_node, mp_usbd_task); + mp_sched_schedule_node(&usbd_task_node, mp_usbd_task_callback); } -static void mp_usbd_task(mp_sched_node_t *node) { +static void mp_usbd_task_callback(mp_sched_node_t *node) { (void)node; - tud_task_ext(0, false); + mp_usbd_task(); } #endif diff --git a/shared/tinyusb/mp_usbd.h b/shared/tinyusb/mp_usbd.h index 2e4feaca9f..340637c95f 100644 --- a/shared/tinyusb/mp_usbd.h +++ b/shared/tinyusb/mp_usbd.h @@ -29,6 +29,9 @@ #include "py/obj.h" +// Call this to explicitly run the TinyUSB device task. +void mp_usbd_task(void); + // Function to be implemented in port code. // Can write a string up to MICROPY_HW_USB_DESC_STR_MAX characters long, plus terminating byte. extern void mp_usbd_port_get_serial_number(char *buf); From a00c9d56db775ee5fc14c2db60eb07bab8e872dd Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 9 Nov 2023 16:56:21 +1100 Subject: [PATCH 37/37] rp2/mphalport: Run TinyUSB stack while waiting for CDC input/output. The recent change in bcbdee235719d459a4cd60d51021454fba54cd0f means that TinyUSB can no longer be run from within a soft (or hard) IRQ handler, ie when the scheduler is locked. That means that Python code that calls `print(...)` from within a scheduled function may block indefinitely if the USB CDC buffers are full. This commit fixes that problem by explicitly running the TinyUSB stack when waiting within stdio tx/rx functions. Signed-off-by: Damien George --- ports/rp2/mphalport.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/ports/rp2/mphalport.c b/ports/rp2/mphalport.c index d1bba43642..4b1c1fa1f5 100644 --- a/ports/rp2/mphalport.c +++ b/ports/rp2/mphalport.c @@ -30,6 +30,7 @@ #include "extmod/misc.h" #include "shared/runtime/interrupt_char.h" #include "shared/timeutils/timeutils.h" +#include "shared/tinyusb/mp_usbd.h" #include "tusb.h" #include "uart.h" #include "hardware/rtc.h" @@ -54,6 +55,19 @@ ringbuf_t stdin_ringbuf = { stdin_ringbuf_array, sizeof(stdin_ringbuf_array) }; #endif +#if MICROPY_HW_USB_CDC +// Explicitly run the USB stack in case the scheduler is locked (eg we are in an +// interrupt handler) and there is in/out data pending on the USB CDC interface. +#define MICROPY_EVENT_POLL_HOOK_WITH_USB \ + do { \ + MICROPY_EVENT_POLL_HOOK; \ + mp_usbd_task(); \ + } while (0) + +#else +#define MICROPY_EVENT_POLL_HOOK_WITH_USB MICROPY_EVENT_POLL_HOOK +#endif + #if MICROPY_HW_USB_CDC uint8_t cdc_itf_pending; // keep track of cdc interfaces which need attention to poll @@ -135,7 +149,7 @@ int mp_hal_stdin_rx_chr(void) { return dupterm_c; } #endif - MICROPY_EVENT_POLL_HOOK + MICROPY_EVENT_POLL_HOOK_WITH_USB; } } @@ -155,7 +169,7 @@ void mp_hal_stdout_tx_strn(const char *str, mp_uint_t len) { int timeout = 0; // Wait with a max of USC_CDC_TIMEOUT ms while (n > tud_cdc_write_available() && timeout++ < MICROPY_HW_USB_CDC_TX_TIMEOUT) { - MICROPY_EVENT_POLL_HOOK + MICROPY_EVENT_POLL_HOOK_WITH_USB; } if (timeout >= MICROPY_HW_USB_CDC_TX_TIMEOUT) { break;