From 2f2a56bd1245daaa902b9bc49b96e2833b615240 Mon Sep 17 00:00:00 2001 From: "Peter D. Gray" Date: Tue, 10 Jan 2023 14:33:39 -0500 Subject: [PATCH] pull basics from mk4 --- stm32/COLDCARD_Q1/c-modules | 1 + stm32/COLDCARD_Q1/ckcc-port.h | 46 + stm32/COLDCARD_Q1/clocks.c | 81 ++ stm32/COLDCARD_Q1/initfs.c | 13 + stm32/COLDCARD_Q1/layout.ld | 47 + stm32/COLDCARD_Q1/modckcc.c | 359 ++++++++ stm32/COLDCARD_Q1/modckcc.h | 9 + stm32/COLDCARD_Q1/mpconfigboard.h | 128 +++ stm32/COLDCARD_Q1/mpconfigboard.mk | 98 +++ stm32/COLDCARD_Q1/pins.csv | 91 ++ stm32/COLDCARD_Q1/psramdisk.c | 792 +++++++++++++++++ stm32/COLDCARD_Q1/rng.c | 152 ++++ stm32/COLDCARD_Q1/rng.h | 8 + stm32/COLDCARD_Q1/stm32l4s5_af.csv | 116 +++ stm32/COLDCARD_Q1/stm32l4xx_hal_conf.h | 20 + stm32/COLDCARD_Q1/stm32l4xx_hal_tsc. | 1 + stm32/COLDCARD_Q1/stm32l4xx_hal_tsc.c | 1127 ++++++++++++++++++++++++ stm32/COLDCARD_Q1/ulight.c | 58 ++ stm32/COLDCARD_Q1/ulight.h | 19 + stm32/COLDCARD_Q1/vcp_lockdown.c | 86 ++ 20 files changed, 3252 insertions(+) create mode 120000 stm32/COLDCARD_Q1/c-modules create mode 100644 stm32/COLDCARD_Q1/ckcc-port.h create mode 100644 stm32/COLDCARD_Q1/clocks.c create mode 100644 stm32/COLDCARD_Q1/initfs.c create mode 100644 stm32/COLDCARD_Q1/layout.ld create mode 100644 stm32/COLDCARD_Q1/modckcc.c create mode 100644 stm32/COLDCARD_Q1/modckcc.h create mode 100644 stm32/COLDCARD_Q1/mpconfigboard.h create mode 100644 stm32/COLDCARD_Q1/mpconfigboard.mk create mode 100644 stm32/COLDCARD_Q1/pins.csv create mode 100644 stm32/COLDCARD_Q1/psramdisk.c create mode 100644 stm32/COLDCARD_Q1/rng.c create mode 100644 stm32/COLDCARD_Q1/rng.h create mode 100644 stm32/COLDCARD_Q1/stm32l4s5_af.csv create mode 100644 stm32/COLDCARD_Q1/stm32l4xx_hal_conf.h create mode 120000 stm32/COLDCARD_Q1/stm32l4xx_hal_tsc. create mode 100644 stm32/COLDCARD_Q1/stm32l4xx_hal_tsc.c create mode 100644 stm32/COLDCARD_Q1/ulight.c create mode 100644 stm32/COLDCARD_Q1/ulight.h create mode 100644 stm32/COLDCARD_Q1/vcp_lockdown.c diff --git a/stm32/COLDCARD_Q1/c-modules b/stm32/COLDCARD_Q1/c-modules new file mode 120000 index 00000000..4a8546fc --- /dev/null +++ b/stm32/COLDCARD_Q1/c-modules @@ -0,0 +1 @@ +../../external/c-modules \ No newline at end of file diff --git a/stm32/COLDCARD_Q1/ckcc-port.h b/stm32/COLDCARD_Q1/ckcc-port.h new file mode 100644 index 00000000..d37081da --- /dev/null +++ b/stm32/COLDCARD_Q1/ckcc-port.h @@ -0,0 +1,46 @@ +/* + * (c) Copyright 2021 by Coinkite Inc. This file is covered by license found in COPYING-CC. + * + * based on ports/stm32/mpconfigport.h but overrides only + * + */ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013-2017 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. + */ + +// Options to control how MicroPython is built for this port, +// overriding defaults in py/mpconfig.h. + +// board specific definitions +#include "mpconfigboard.h" +#include "mpconfigboard_common.h" + +// default port config: see ports/stm32 +#include "mpconfigport.h" + +#undef MICROPY_PY_MACHINE_PULSE +#define MICROPY_PY_MACHINE_PULSE (0) + + diff --git a/stm32/COLDCARD_Q1/clocks.c b/stm32/COLDCARD_Q1/clocks.c new file mode 100644 index 00000000..5678bbff --- /dev/null +++ b/stm32/COLDCARD_Q1/clocks.c @@ -0,0 +1,81 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 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 +#include + +#include "py/mphal.h" +#include "irq.h" + +void __fatal_error(const char *msg); + +void SystemInit(void) +{ + // replace code from + // l-mpy/lib/stm32lib/CMSIS/STM32L4xx/Source/Templates/system_stm32l4xx.c + + // FPU settings +#if (__FPU_PRESENT == 1) && (__FPU_USED == 1) + SCB->CPACR |= ((3UL << 10*2)|(3UL << 11*2)); /* set CP10 and CP11 Full Access */ +#endif + + // Leave clock details unchanged! Do not reset RCC. + + // Disable all interrupts + RCC->CIER = 0x00000000; + + // Configure the Vector Table location add offset address +#ifdef VECT_TAB_SRAM + SCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM */ +#else + SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH */ +#endif + + // This global controls the return value of HAL_RCC_GetHCLKFreq() + // see clocks.h of bootrom ... SYSCLK in Hz = 120Mhz + SystemCoreClock = 120000000; +} + +void SystemClock_Config(void) +{ + // See code in clocks.c of bootloader for actual clock setup. + +#if 0 + __PWR_CLK_ENABLE(); + HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1); +#endif + + // redundant/override HAL_InitTick called from stm32_main just before this + HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/(1000*8)); + HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK_DIV8); + + NVIC_SetPriority(SysTick_IRQn, NVIC_EncodePriority(NVIC_PRIORITYGROUP_4, TICK_INT_PRIORITY, 0)); + + // bugfix? why is this needed? + enable_irq(IRQ_STATE_ENABLED); +} + +// EOF diff --git a/stm32/COLDCARD_Q1/initfs.c b/stm32/COLDCARD_Q1/initfs.c new file mode 100644 index 00000000..e47cb1ec --- /dev/null +++ b/stm32/COLDCARD_Q1/initfs.c @@ -0,0 +1,13 @@ +/* + * (c) Copyright 2021 by Coinkite Inc. This file is covered by license found in COPYING-CC. + * + */ + +int factory_reset_create_filesystem(void) +{ + // Replaces standard version of this function, so it's useful for this project. + // - but do not create a filesystem, instead do it in python + return 0; // success +} + +// EOF diff --git a/stm32/COLDCARD_Q1/layout.ld b/stm32/COLDCARD_Q1/layout.ld new file mode 100644 index 00000000..a2ce0fa0 --- /dev/null +++ b/stm32/COLDCARD_Q1/layout.ld @@ -0,0 +1,47 @@ +/* + GNU linker script for Coldcard Mk4 running on STM32L4s5 with 2MB of flash +*/ + +/* Specify the memory areas */ +MEMORY +{ + FLASH (rx) : ORIGIN = 0x08020000, LENGTH = 1984K + + /* Flash layout: + - first 64k of flash is the Coldcard Mk4 bootloader + - next 16k of flash reserved for interupt vectors and few other little things + - (bulk of firmware ~ 1.5meg) + - 512k of user settings in LFS2 filesystem + */ + FLASH_ISR (rx) : ORIGIN = 0x08020000, LENGTH = 16K + FLASH_TEXT (rx) : ORIGIN = 0x08024000, LENGTH = 1392K + FLASH_FS (r) : ORIGIN = 0x08180000, LENGTH = 512K + + /* SRAM1,2, and 3 are continuous, highest 0x2000 reserved for bootloader */ + RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 0x9e000 +} + +ENTRY(Reset_Handler) + +/* produce a link error if there is not this amount of RAM for these sections */ +_minimum_stack_size = 2K; +_minimum_heap_size = 16K; + +/* Define the stack. The stack is full descending so begins just above last byte of RAM, + or bottom of FS cache.. Note that EABI requires the stack to be 8-byte aligned for a call. */ + +/* RAM extents for the garbage collector */ +_ram_start = ORIGIN(RAM); +_ram_end = ORIGIN(RAM) + LENGTH(RAM); + +_ram_fs_cache_end = _ram_end; +_ram_fs_cache_start = _ram_fs_cache_end - 8K; /* fs cache = 8K .. one flash sector */ + +_estack = _ram_fs_cache_start - _estack_reserve; +_sstack = _estack - 16K; /* stack = 16K */ + +_heap_start = _ebss; /* heap starts just after statically allocated memory */ +_heap_end = _sstack; /* bss + heap, tunable by adjusting stack size */ + +_flash_fs_start = ORIGIN(FLASH_FS); +_flash_fs_end = ORIGIN(FLASH_FS) + LENGTH(FLASH_FS); diff --git a/stm32/COLDCARD_Q1/modckcc.c b/stm32/COLDCARD_Q1/modckcc.c new file mode 100644 index 00000000..7a02880f --- /dev/null +++ b/stm32/COLDCARD_Q1/modckcc.c @@ -0,0 +1,359 @@ +// +// (c) Copyright 2018 by Coinkite Inc. This file is covered by license found in COPYING-CC. +// +// modckcc.c - module for Coldcard hardware features and glue. +// +#include +#include + +#include "modckcc.h" +#include "rng.h" +#include "usb.h" +#include "flash.h" +#include "bufhelper.h" +#include "py/gc.h" +#include "py/runtime.h" +#include "py/mphal.h" +#include "py/mpstate.h" +#include "py/stackctrl.h" +#include "boardctrl.h" +#include "softtimer.h" +#include "ulight.h" +#include "uart.h" + +#include "storage.h" +#include "usb.h" +#include "extint.h" +#include "lib/utils/interrupt_char.h" // for mp_interrupt_char + +// this file needs -O0 for it to work well. +#pragma GCC push_options +#pragma GCC optimize ("O0") + +// Presume genuine light is on, at start +STATIC bool presumably_green_light = true; + +MP_DECLARE_CONST_FUN_OBJ_0(pyb_rng_get_obj); +MP_DECLARE_CONST_FUN_OBJ_1(pyb_rng_get_bytes_obj); + +// See startup.S where this table of entry points/version numbers is defined +typedef struct { + uint32_t callgate_entry; + uint32_t version_number; + uint32_t reserved[4]; +} bootloaderInfoTable_t; + +#define BOOTLOADER_TABLE (*((bootloaderInfoTable_t *)0x08000040)) + +STATIC int callgate_lower(uint32_t method_num, uint32_t arg2, mp_buffer_info_t *io_buf) +{ + uint32_t dest = BOOTLOADER_TABLE.callgate_entry; + + // +4 because that's required by call gate for firewall + // +1 to set LSB because we know a BLX instruction will be used to + // get there and we know its thumb code + // - also 0x100 aligned. + assert((dest & 0xff) == 0x05); + + ulight_off(); + mp_uint_t before = disable_irq(); + + // XXX this doesn't work with compiler optimizations enabled (ie. -O0 only!!) + + // Call the gate; it will trash the normal function-call registers (r0-4) + // but also r9 and r10. Does not use our stack. + // + // references: + // - + // - + // + int rv = -2; + register int r_method asm("r0") = method_num; + register const char *tx asm("r1") = io_buf ? io_buf->buf : NULL; + register uint32_t tx_len asm("r2") = io_buf ? io_buf->len: 0; + register uint32_t r_arg2 asm("r3") = arg2; + + asm volatile( "blx %[dest] \n" + "str r0, %[rv]" + : [rv] "=m" (rv) + : [dest] "r" (dest), "r" (tx), "r" (tx_len), "r" (r_arg2), "r" (r_method) + : "r9", "r10" + ); + + enable_irq(before); + + return rv; +} + +STATIC mp_obj_t sec_oneway_gate(mp_obj_t method_obj, mp_obj_t arg2_obj) +{ + // jump to the callgate, but don't expect to come back. Good for DFU entry. + + uint32_t dest = BOOTLOADER_TABLE.callgate_entry; + + uint32_t num = mp_obj_get_int(method_obj); + uint32_t arg2 = mp_obj_get_int(arg2_obj); + + pyb_usb_dev_deinit(); + storage_flush(); + ulight_off(); + + // NOTE: this may not work with compiler optimizations enabled (ie. -O0 only!!) + + mp_uint_t before = disable_irq(); + + int rv = -2; // not really used. + register int method_num asm("r0") = num; + register const char *tx asm("r1") = 0; + register uint32_t tx_len asm("r2") = 0; + register uint32_t r_arg2 asm("r3") = arg2; + + asm volatile( "blx %[dest] \n" + "str r0, %[rv]" + : [rv] "=m" (rv) + : [dest] "r" (dest), "r" (tx), "r" (tx_len), "r" (r_arg2), "r" (method_num) + : "r9", "r10" + ); + + enable_irq(before); + + // not reached, except maybe error cases + return mp_obj_new_int(rv); +} +MP_DEFINE_CONST_FUN_OBJ_2(sec_oneway_gate_obj, sec_oneway_gate); + +STATIC mp_obj_t sec_gate(mp_obj_t method_obj, mp_obj_t send_arg, mp_obj_t arg2_obj) +{ + + // first and last args are simple integers + uint32_t method_num = mp_obj_get_int(method_obj); + uint32_t arg2 = mp_obj_get_int(arg2_obj); + + // 2nd arg: buffer to send to function, or None for NULL + mp_buffer_info_t send_buf; + bool is_none = false; + + if(send_arg != mp_const_none) { + mp_get_buffer_raise(send_arg, &send_buf, MP_BUFFER_RW); + } else { + is_none = true; + } + + int rv = callgate_lower(method_num, arg2, is_none ? NULL : &send_buf); + +#if 0 + // bugfix: if bootloader (V1.0.1) calls systick_setup(), this will correct register + SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | + SysTick_CTRL_TICKINT_Msk | + SysTick_CTRL_ENABLE_Msk; +#endif + + + return mp_obj_new_int(rv); +} +MP_DEFINE_CONST_FUN_OBJ_3(sec_gate_obj, sec_gate); + + +// Since we cache state of green light, need a way for us +// to be reset, altho has no bearing on real operation of 508a. +STATIC mp_obj_t presume_green(void) +{ + presumably_green_light = true; + + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_0(presume_green_obj, presume_green); + + +STATIC mp_obj_t is_simulator(void) +{ + return MP_OBJ_NEW_SMALL_INT(0); +} +MP_DEFINE_CONST_FUN_OBJ_0(is_simulator_obj, is_simulator); + +STATIC mp_obj_t is_debug_build(void) +{ + return MP_OBJ_NEW_SMALL_INT(COLDCARD_DEBUG); +} +MP_DEFINE_CONST_FUN_OBJ_0(is_debug_build_obj, is_debug_build); + +STATIC mp_obj_t get_cpu_id(void) +{ + // Are we running on a STM32L496RG6? If so, expect 0x461 + return MP_OBJ_NEW_SMALL_INT(DBGMCU->IDCODE & 0xfff); +} +MP_DEFINE_CONST_FUN_OBJ_0(get_cpu_id_obj, get_cpu_id); + + +STATIC mp_obj_t vcp_enabled(mp_obj_t new_val) +{ + // see vcp_lockdown.c where this is used + extern bool ckcc_vcp_enabled; + + // Report/Control the VCP lockout. Call with None to readback. + if(mp_obj_is_integer(new_val)) { + ckcc_vcp_enabled = !!(mp_obj_get_int_truncated(new_val)); + + mp_interrupt_char = ckcc_vcp_enabled ? 3 : -1; + } + + return MP_OBJ_NEW_SMALL_INT(ckcc_vcp_enabled); +} +MP_DEFINE_CONST_FUN_OBJ_1(vcp_enabled_obj, vcp_enabled); + + +STATIC mp_obj_t stack_limit(mp_obj_t new_val) +{ + // Report or change the stack limit, in bytes. Probably less than 0x4000. + if(mp_obj_is_integer(new_val)) { + mp_int_t limit = mp_obj_get_int_truncated(new_val); + + // Small values will cause immediate crash due to stack-depth checking, so avoid. + if((limit < 1024) || (limit > 64*1024)) { + mp_raise_ValueError(NULL); + } + + mp_stack_set_limit(limit); + } + + return MP_OBJ_NEW_SMALL_INT(MP_STATE_THREAD(stack_limit)); +} +MP_DEFINE_CONST_FUN_OBJ_1(stack_limit_obj, stack_limit); + +STATIC mp_obj_t usb_active(void) +{ + // something happened at the class-driver level of USB. + // - keep the light flashing + ckcc_usb_active = true; + + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_0(usb_active_obj, usb_active); + +STATIC mp_obj_t breakpoint(void) +{ + // drop into the debugger, if connected. + asm("BKPT #0"); + + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_0(breakpoint_obj, breakpoint); + +STATIC mp_obj_t watchpoint(volatile mp_obj_t arg1) +{ + // just be an empty function that we can set as a breakpoint + // in the debugger... also gives some visiblilty into a single object + + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(watchpoint_obj, watchpoint); + +// See psram.c +extern const mp_obj_type_t psram_type; + +STATIC const mp_rom_map_elem_t ckcc_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_ckcc) }, + { MP_ROM_QSTR(MP_QSTR_rng), MP_ROM_PTR(&pyb_rng_get_obj) }, + { MP_ROM_QSTR(MP_QSTR_rng_bytes), MP_ROM_PTR(&pyb_rng_get_bytes_obj) }, + { MP_ROM_QSTR(MP_QSTR_gate), MP_ROM_PTR(&sec_gate_obj) }, + { MP_ROM_QSTR(MP_QSTR_oneway), MP_ROM_PTR(&sec_oneway_gate_obj) }, + { MP_ROM_QSTR(MP_QSTR_is_simulator), MP_ROM_PTR(&is_simulator_obj) }, + { MP_ROM_QSTR(MP_QSTR_is_debug_build), MP_ROM_PTR(&is_debug_build_obj) }, + { MP_ROM_QSTR(MP_QSTR_get_cpu_id), MP_ROM_PTR(&get_cpu_id_obj) }, + { MP_ROM_QSTR(MP_QSTR_vcp_enabled), MP_ROM_PTR(&vcp_enabled_obj) }, + { MP_ROM_QSTR(MP_QSTR_presume_green), MP_ROM_PTR(&presume_green_obj) }, + { MP_ROM_QSTR(MP_QSTR_breakpoint), MP_ROM_PTR(&breakpoint_obj) }, + { MP_ROM_QSTR(MP_QSTR_watchpoint), MP_ROM_PTR(&watchpoint_obj) }, + { MP_ROM_QSTR(MP_QSTR_stack_limit), MP_ROM_PTR(&stack_limit_obj) }, + { MP_ROM_QSTR(MP_QSTR_usb_active), MP_ROM_PTR(&usb_active_obj) }, + { MP_ROM_QSTR(MP_QSTR_PSRAM), MP_ROM_PTR(&psram_type) }, +}; + +STATIC MP_DEFINE_CONST_DICT(ckcc_module_globals, ckcc_module_globals_table); + +const mp_obj_module_t ckcc_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t*)&ckcc_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_ckcc, ckcc_module, 1); + +void ckcc_early_init(void) +{ + // Add system-wide init code here. + + // Disable ^C to interrupt code... but see mp_hal_set_interrupt_char() + // for best disable code. + mp_interrupt_char = -1; + + // Do the equivilent of "py.usb_mode(None)" in boot.py + extern mp_uint_t pyb_usb_flags; + pyb_usb_flags |= PYB_USB_FLAG_USB_MODE_CALLED; +} + + + +void ckcc_boardctrl_before_boot_py(boardctrl_state_t *state) +{ + // do not run /boot.py even if it exists + state->run_boot_py = false; + + // Clear PSRAM from previous cycles + extern void psram_init(void); + psram_init(); + + // setup USB activity light + ulight_setup(); +} +void ckcc_boardctrl_after_boot_py(boardctrl_state_t *state) +{ + // nothing to do, no way to report failures anyway +} + +// ckcc_heap_start() +// + void * +ckcc_heap_start(void) +{ + // see layout.ld (linker script) + extern uint32_t _heap_start; + + return &_heap_start; +} + +// ckcc_heap_start() +// + void * +ckcc_heap_end(void) +{ + extern uint32_t _ram_end; + uint8_t *rv = (uint8_t *)&_ram_end; + + if((DBGMCU->IDCODE & 0xfff) == 0x461) { + rv += 160*1024; + } + + // Mark 1 and 2 + return rv; +} + +// There is no classical C heap in bare-metal ports, only Python +// garbage-collected heap. For completeness, emulate C heap via +// GC heap. Note that MicroPython core never uses malloc() and friends, +// but I need these for the C-language extensions I'm using. +void *malloc(size_t size) +{ + return m_malloc(size); +} + +void free(void *ptr) +{ + m_free(ptr); +} + +void *realloc(void *ptr, size_t size) +{ + return m_realloc(ptr, size); +} + +// EOF diff --git a/stm32/COLDCARD_Q1/modckcc.h b/stm32/COLDCARD_Q1/modckcc.h new file mode 100644 index 00000000..e7069efb --- /dev/null +++ b/stm32/COLDCARD_Q1/modckcc.h @@ -0,0 +1,9 @@ +/* + * (c) Copyright 2018 by Coinkite Inc. This file is covered by license found in COPYING-CC. + */ +#ifndef MICROPY_INCLUDED_STMHAL_MODCKCC_H +#define MICROPY_INCLUDED_STMHAL_MODCKCC_H + +#include "py/obj.h" + +#endif // MICROPY_INCLUDED_STMHAL_MODCKCC_H diff --git a/stm32/COLDCARD_Q1/mpconfigboard.h b/stm32/COLDCARD_Q1/mpconfigboard.h new file mode 100644 index 00000000..4933d9cb --- /dev/null +++ b/stm32/COLDCARD_Q1/mpconfigboard.h @@ -0,0 +1,128 @@ +/* + * (c) Copyright 2018 by Coinkite Inc. This file is covered by license found in COPYING-CC. + */ + +#define MICROPY_HW_BOARD_NAME "Coldcard" +#define MICROPY_PY_SYS_PLATFORM "coldcard" + +#define MICROPY_HW_MCU_NAME "STM32L4SxVI" + +#define MICROPY_HW_HAS_SWITCH (0) +#define MICROPY_HW_HAS_FLASH (1) +#define MICROPY_HW_HAS_SDCARD (1) +#define MICROPY_HW_HAS_LCD (0) +#define MICROPY_HW_ENABLE_HW_I2C (1) + +// don't want, but lots of modules interdepend on this +#define MICROPY_HW_ENABLE_RTC (0) + +//#define HSE_VALUE ((uint32_t)8000000) /*!< Value of the External oscillator in Hz */ + +// USB config +#define MICROPY_HW_ENABLE_USB (1) +#define MICROPY_HW_USB_FS (1) + +// HSE is used and is 8MHz +// see hardcoding in clocks.c / bootrom / these values are not used / may be wrong +#define MICROPY_HW_CLK_PLLN (40) +#define MICROPY_HW_CLK_PLLM (2) +#define MICROPY_HW_CLK_PLLR (2) +#define MICROPY_HW_CLK_PLLP (7) +#define MICROPY_HW_CLK_PLLQ (4) + +#define MICROPY_HW_FLASH_LATENCY FLASH_LATENCY_4 + +// UART config +#define MICROPY_HW_UART4_TX (pin_A0) +#define MICROPY_HW_UART4_RX (pin_A0) +#define MICROPY_HW_UART2_TX (pin_A2) +#define MICROPY_HW_UART2_RX (pin_A2) +#define MICROPY_HW_UART1_TX (pin_A9) +#define MICROPY_HW_UART1_RX (pin_A10) + +// Mk4 has real debug serial port, wonderful. +#define MICROPY_HW_UART_REPL PYB_UART_1 +#define MICROPY_HW_UART_REPL_BAUD 115200 + +/* I2C busses: 2 */ +#define MICROPY_HW_I2C1_SCL (pin_B6) +#define MICROPY_HW_I2C1_SDA (pin_B7) +#define MICROPY_HW_I2C2_SCL (pin_B13) +#define MICROPY_HW_I2C2_SDA (pin_B14) +/* +#define MICROPY_HW_I2C3_SCL (pin_C0) +#define MICROPY_HW_I2C3_SDA (pin_C1) +*/ + +// SPI busses (one) +#define MICROPY_HW_SPI1_SCK (pin_A5) +#define MICROPY_HW_SPI1_MOSI (pin_A7) +//#define MICROPY_HW_SPI1_NSS (pin_A4) +//#define MICROPY_HW_SPI1_MISO (pin_A6) + +/* removed in Mk4 rev B +#define MICROPY_HW_SPI2_NSS (pin_B9) +#define MICROPY_HW_SPI2_SCK (pin_B10) +#define MICROPY_HW_SPI2_MISO (pin_C2) +#define MICROPY_HW_SPI2_MOSI (pin_C3) +*/ + +// SD card detect switch +// - open when card inserted, grounded when no card +#define MICROPY_HW_SDCARD_DETECT_PIN (pin_C13) +#define MICROPY_HW_SDCARD_DETECT_PULL (GPIO_PULLUP) +#define MICROPY_HW_SDCARD_DETECT_PRESENT (GPIO_PIN_SET) + + +// We have our own version of this code. +#define MICROPY_HW_ENABLE_RNG (0) + +extern void ckcc_early_init(void); +#define MICROPY_BOARD_EARLY_INIT ckcc_early_init + +// Need CRC32 for 7z support. +#define MICROPY_PY_UBINASCII_CRC32 (1) + +// Experiment, works. +//#define MICROPY_STACKLESS (1) + +#define USBD_MANUFACTURER_STRING "Coinkite" +#define USBD_PRODUCT_HS_STRING "Coldcard Wallet" +#define USBD_PRODUCT_FS_STRING "Coldcard Wallet" +#define USBD_CONFIGURATION_HS_STRING "HS Config" +#define USBD_INTERFACE_HS_STRING "HS Interface" +#define USBD_CONFIGURATION_FS_STRING "FS Config" +#define USBD_INTERFACE_FS_STRING "FS Interface" + +// Where the heap should end up. +extern void *ckcc_heap_start(void); +extern void *ckcc_heap_end(void); +#define MICROPY_HEAP_START ckcc_heap_start() +#define MICROPY_HEAP_END ckcc_heap_end() + +// Features/or not. +#define MICROPY_HW_ENABLE_DHT (0) +#define MICROPY_HW_ENABLE_ADC (0) + +// override some boot-up stuff +#define MICROPY_BOARD_BEFORE_BOOT_PY ckcc_boardctrl_before_boot_py +#define MICROPY_BOARD_AFTER_BOOT_PY ckcc_boardctrl_after_boot_py + +struct _boardctrl_state_t; +extern void ckcc_boardctrl_before_boot_py(struct _boardctrl_state_t *state); +extern void ckcc_boardctrl_after_boot_py(struct _boardctrl_state_t *state); + +#define MICROPY_HW_SDCARD_MOUNT_AT_BOOT (0) + +#define MICROPY_HW_ENABLE_SDCARD (1) +#define MICROPY_HW_ENABLE_CARD_IDENT (1) + +// called from usb.c to setup customized MSC storage +struct _usbd_cdc_msc_hid_state_t; +extern void psramdisk_USBD_MSC_RegisterStorage(int num_lun, struct _usbd_cdc_msc_hid_state_t *usbd); +#define MICROPY_HW_CUSTOM_USB_MSC psramdisk_USBD_MSC_RegisterStorage + +// enable some code inside oofatfs that we need +#define FF_USE_FASTSEEK (1) + +// EOF diff --git a/stm32/COLDCARD_Q1/mpconfigboard.mk b/stm32/COLDCARD_Q1/mpconfigboard.mk new file mode 100644 index 00000000..59f161bc --- /dev/null +++ b/stm32/COLDCARD_Q1/mpconfigboard.mk @@ -0,0 +1,98 @@ +# Config for our board. +# +MCU_SERIES = l4 +CMSIS_MCU = STM32L4S5xx +AF_FILE = boards/$(BOARD)/stm32l4s5_af.csv +LD_FILES = boards/$(BOARD)/layout.ld boards/common_ifs.ld +OPENOCD_CONFIG = boards/openocd_stm32l4.cfg + +# MicroPython settings +MICROPY_VFS_LFS2 = 1 +MICROPY_VFS_FAT = 1 + +# see py/mpconfig.h which uses this var if set +CFLAGS_EXTRA += -DMP_CONFIGFILE=\"boards/$(BOARD)/ckcc-port.h\" +CFLAGS_EXTRA += -DCOLDCARD_DEBUG=$(DEBUG_BUILD) + +# obsolete +# need the CDC inf file to be built before this file +#initfs.c: $(GEN_CDCINF_HEADER) + +# NgU and uQR libraries +NGU_NEEDS_CIFRA = 1 +USER_C_MODULES = boards/$(BOARD)/c-modules + +# the bulk of the COLDCARD-specific code +# - do not want contents of stm32/boards/manifest.py +FROZEN_MANIFEST = \ + boards/$(BOARD)/shared/manifest.py \ + boards/$(BOARD)/shared/manifest_mk4.py + +# This will relocate things up by 128k=0x2_0000 +# see also ./layout.ld +CFLAGS_MOD += -DVECT_TAB_OFFSET=0x20000 +TEXT0_ADDR = 0x08020000 +TEXT1_ADDR = 0x08024000 + +# don't want any of these: soft_spi, soft_qspi, dht +#DRIVERS_SRC_C -= drivers/bus/softspi.c \ +# drivers/bus/softqspi.c drivers/memory/spiflash.c \ +# drivers/dht/dht.c + +# Approximately all the source code files? +ALL_SRC = $(SRC_LIB) $(SRC_LIBM) $(EXTMOD_SRC_C) $(DRIVERS_SRC_C) \ + $(SRC_HAL) $(SRC_USBDEV) $(SRC_MOD) +ALL_SRC += $(addprefix ports/stm32/, $(SRC_C)) +ALL_SRC += py/*.[ch] + +# XXX: this barely works, ignore errors +tags: + echo $(ALL_SRC) + (cd $(TOP)/../..; pwd; ctags -R -f .tags $(addprefix external/micropython/, $(ALL_SRC)) \ + external/micropython/lib/stm32lib/STM32L4xx_HAL_Driver/*/*.[ch] \ + external/micropython/lib/stm32lib/CMSIS/STM32L4xx/Include/stm32l475xx.h \ + external/micropython/lib/cmsis/inc/core_cm4.h \ + external/micropython/ports/stm32/*.[ch] \ + external/micropython/ports/stm32/*/*.[ch] \ + external/micropython/ports/stm32/usbdev/{core,class}/{src,inc}/*.[ch] \ + external/libngu/ngu/*.[ch]) + sed -i .tmp '/dynruntime./d' $(TOP)/../../.tags + + +checksum: + $(OBJCOPY) -O binary -j .isr_vector -j .text -j .data \ + --pad-to 0x080e0000 --gap-fill 0xff \ + $(BUILD)/firmware.elf $(BUILD)/firmware-complete.bin + shasum -a 256 $(BUILD)/firmware-complete.bin + +# This costs about 241,136 of flash! Not clear on what I gain. +# It enables -O0 instead of -Os!! Very bad. +#DEBUG = 1 + +# we always want debug symbols, since they get stripped anyway +COPT += -g + +# bugfix IIRC +build-COLDCARD_MK4/boards/COLDCARD_MK4/modckcc.o: COPT = -O0 -DNDEBUG + +# pickiness +build-COLDCARD_MK4/dma.o: COPT=-Werror=unused-const-variable=0 +build-COLDCARD_MK4/boards/COLDCARD_MK4/psramdisk.o: COPT=-Werror=unused-const-variable=0 + +# bugfix: remove unwanted setup code called from ports/stm32/resethandler.s +build-COLDCARD_MK4/lib/stm32lib/CMSIS/STM32L4xx/Source/Templates/system_stm32l4xx.o: \ + CFLAGS += -DSystemInit=SystemInit_OMIT + +# bugfix: replace keyboard interrupt handling +build-COLDCARD_MK4/lib/utils/interrupt_char.o: \ + CFLAGS += -Dmp_hal_set_interrupt_char=mp_hal_set_interrupt_char_OMIT + + +files: + # SRC_C: $(SRC_C) + @echo + # SRC_HAL: $(SRC_HAL) + @echo + # CFLAGS: $(CFLAGS) + @echo + # FROZEN_MANIFEST: $(FROZEN_MANIFEST) diff --git a/stm32/COLDCARD_Q1/pins.csv b/stm32/COLDCARD_Q1/pins.csv new file mode 100644 index 00000000..e840247b --- /dev/null +++ b/stm32/COLDCARD_Q1/pins.csv @@ -0,0 +1,91 @@ +D0,PA3 +D1,PA2 +D2,PA10 +D3,PB3 +D4,PB5 +D5,PB4 +D6,PB10 +D7,PA8 +D8,PA9 +D9,PC7 +D10,PB6 +D11,PA7 +D12,PA6 +D13,PA5 +D14,PB9 +D15,PB8 +PA0,PA0 +PA1,PA1 +PA2,PA2 +PA3,PA3 +PA4,PA4 +PA5,PA5 +PA6,PA6 +PA7,PA7 +PA8,PA8 +PA9,PA9 +PA10,PA10 +PA15,PA15 +PB0,PB0 +PB1,PB1 +PB2,PB2 +PB3,PB3 +PB4,PB4 +PB5,PB5 +PB6,PB6 +PB7,PB7 +PB8,PB8 +PB9,PB9 +PB10,PB10 +PB11,PB11 +PB12,PB12 +PB13,PB13 +PB14,PB14 +PB15,PB15 +PC0,PC0 +PC1,PC1 +PC2,PC2 +PC3,PC3 +PC4,PC4 +PC5,PC5 +PC6,PC6 +PC7,PC7 +PC8,PC8 +PC9,PC9 +PC10,PC10 +PC11,PC11 +PC12,PC12 +PC13,PC13 +PC14,PC14 +PC15,PC15 +PD2,PD2 +PH0,PH0 +PH1,PH1 +USB_DM,PA11 +USB_DP,PA12 +SD_D0,PC8 +SD_D1,PC9 +SD_D2,PC10 +SD_D3,PC11 +SD_CMD,PD2 +SD_CK,PC12 +SD_SW,PC13 +SD,PA9 +M2_COL0,PB0 +M2_COL1,PB1 +M2_COL2,PB2 +M2_ROW0,PD8 +M2_ROW1,PD9 +M2_ROW2,PD10 +M2_ROW3,PD11 +F_CS,PB9 +SF_SCLK,PB10 +SF_MISO,PC2 +SF_MOSI,PC3 +SD_ACTIVE,PC7 +USB_ACTIVE,PC6 +SE2_SCL,PB13 +SE2_SDA,PB14 +NFC_SCL,PB6 +NFC_SDA,PB7 +NFC_ED,PC4 diff --git a/stm32/COLDCARD_Q1/psramdisk.c b/stm32/COLDCARD_Q1/psramdisk.c new file mode 100644 index 00000000..813f7efb --- /dev/null +++ b/stm32/COLDCARD_Q1/psramdisk.c @@ -0,0 +1,792 @@ +/* + * (c) Copyright 2021 by Coinkite Inc. This file is covered by license found in COPYING-CC. + * + * Implement a ram disk in PSRAM, accessible by host as MSC and mpy as block dev. + * + */ +#include + +#include "usbd_cdc_msc_hid.h" +#include "usbd_msc_interface.h" +#include "usbd_cdc_msc_hid0.h" +#include "usbd_msc_bot.h" +#include "usbd_msc_scsi.h" +#include "usbd_ioreq.h" +#include "py/gc.h" +#include "py/mphal.h" +#include "py/runtime.h" +#include "extmod/vfs.h" +#include "extmod/vfs_fat.h" +#include "lib/oofatfs/ff.h" +#include "py/runtime.h" +#include "py/mperrno.h" +#include "softtimer.h" +#include "ulight.h" + +// Our storage, in quad-serial SPI PSRAM chip +// - using top half of chip only +static uint8_t *PSRAM_TOP_BASE = (uint8_t *)0x90400000; // OCTOSPI mapping, top half +static uint8_t *PSRAM_BOT_BASE = (uint8_t *)0x90000000; // OCTOSPI mapping, bot half +static const uint32_t PSRAM_SIZE = 0x400000; // 4 megs (half) +static const uint32_t BLOCK_SIZE = 512; +static const uint32_t BLOCK_COUNT = PSRAM_SIZE / BLOCK_SIZE; // = 8192 + +extern __IO uint32_t uwTick; + +// this code will always be the first LUN +static const uint8_t MY_LUN = 0; + +STATIC mp_obj_t psram_wipe_and_setup(mp_obj_t unused_self); +STATIC const uint8_t psram_msc_lu_num = 1; + +typedef struct _psram_obj_t { + mp_obj_base_t base; + + uint32_t host_write_time; +} psram_obj_t; + +// singleton +const mp_obj_type_t psram_type; +psram_obj_t psram_obj = { + { &psram_type }, +}; + +#define HOST_WR_TIMEOUT 750 // (ms) +static soft_timer_entry_t host_wr_done; + +// we only have a single LUN, so flags can be simple +// - note that "started" is more like inserted vs. ejected +bool flag_STARTED = false; +bool flag_READONLY = false; + +// psram_init() +// + void +psram_init(void) +{ + // always clear and reset contents + psram_wipe_and_setup(NULL); + + //mp_pairheap_t pairheap; + host_wr_done.mode = SOFT_TIMER_MODE_ONE_SHOT; + host_wr_done.expiry_ms = HOST_WR_TIMEOUT; + host_wr_done.callback = MP_ROM_NONE; + host_wr_done.pairheap.base.type = &psram_type; // callback gets this object as only arg +} + +// reset_wr_timeout() +// + static void +reset_wr_timeout(void) +{ + // host has written something, reset/set a timeout to look at new change, + // assuming more is not written before the timeout expires. + + soft_timer_remove(&host_wr_done); + + psram_obj.host_write_time = uwTick; + + if(host_wr_done.callback != MP_ROM_NONE) { + host_wr_done.expiry_ms = uwTick + HOST_WR_TIMEOUT; + soft_timer_insert(&host_wr_done); + } +} + +// wr_timeout_now() +// + static void +wr_timeout_now(void) +{ + // host did something that indicates it won't be writing anymore to + // the disk, and therefore ok to immediately look at contents. + + soft_timer_remove(&host_wr_done); + + psram_obj.host_write_time = uwTick; + + if(host_wr_done.callback != MP_ROM_NONE) { + mp_sched_schedule(host_wr_done.callback, MP_OBJ_FROM_PTR(&psram_obj)); + } +} + +// block_to_ptr() +// + static uint8_t * +block_to_ptr(uint32_t blk, uint16_t num_blk) +{ + // Range checking on incoming requests also done in SCSI_CheckAddressRange() + // but this is an extra layer of safety, important since we might expose + // our address space otherwise! + // - note unsigned arguments + + if(blk >= BLOCK_COUNT) return NULL; + if((blk+num_blk) > BLOCK_COUNT) return NULL; + + return &PSRAM_TOP_BASE[blk * BLOCK_SIZE]; +} + +// Sent in response to MODE SENSE(6) command +const uint8_t PSRAM_MSC_Mode_Sense6_Data[4] = { + 0x03, // mode data length + 0x00, // medium type + 0x00, // bit 7: write protect + 0x00, // block descriptor length +}; + +// Sent in response to MODE SENSE(10) command +const uint8_t PSRAM_MSC_Mode_Sense10_Data[8] = { + 0x00, 0x06, // mode data length + 0x00, // medium type + 0x00, // bit 7: write protect + 0x00, + 0x00, + 0x00, 0x00, // block descriptor length +}; + +STATIC const uint8_t psram_msc_vpd00[6] = { + 0x00, // peripheral qualifier; peripheral device type + 0x00, // page code + 0x00, // reserved + 2, // page length (additional bytes beyond this entry) + 0x00, // page 0x00 supported + 0x83, // page 0x83 supported +}; + +STATIC const uint8_t psram_msc_vpd83[4] = { + 0x00, // peripheral qualifier; peripheral device type + 0x83, // page code + 0x00, 0x00, // page length (additional bytes beyond this entry) +}; + +STATIC const int8_t psram_msc_inquiry_data[36] = { + 0x00, // peripheral qualifier; peripheral device type + 0x80, // 0x00 for a fixed drive, 0x80 for a removable drive + 0x02, // version + 0x02, // response data format + (STANDARD_INQUIRY_DATA_LEN - 5), // additional length + 0x00, // various flags + 0x00, // various flags + 0x00, // various flags + 'C', 'o', 'i', 'n', 'k', 'i', 't', 'e', // Manufacturer : 8 bytes + 'C', 'O', 'L', 'D', 'C', 'A', 'R', 'D', // Product : 16 Bytes + ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', + '4', '.', '0','0', // Version : 4 Bytes +}; + +// Initialise all logical units (it's only ever called once, with lun_in=0) +STATIC int8_t psram_msc_Init(uint8_t lun_in) +{ + if(lun_in != MY_LUN) return -1; + + // don't change flag here, might have been set by python + //flags_STARTED = false; + //flags_READONLY = false; + + return 0; +} + +// Process SCSI INQUIRY command for the logical unit +STATIC int psram_msc_Inquiry(uint8_t lun, const uint8_t *params, uint8_t *data_out) +{ + if(lun != MY_LUN) return -1; + + ckcc_usb_active = true; + + if (params[1] & 1) { + // EVPD set - return vital product data parameters + uint8_t page_code = params[2]; + + switch (page_code) { + case 0x00: // Supported VPD pages + memcpy(data_out, psram_msc_vpd00, sizeof(psram_msc_vpd00)); + return sizeof(psram_msc_vpd00); + + case 0x83: // Device identification + memcpy(data_out, psram_msc_vpd83, sizeof(psram_msc_vpd83)); + return sizeof(psram_msc_vpd83); + + default: // Unsupported + return -1; + } + } + + // A standard inquiry + uint16_t alloc_len = params[3] << 8 | params[4]; + int len = MIN(sizeof(psram_msc_inquiry_data), alloc_len); + memcpy(data_out, psram_msc_inquiry_data, len); + + return len; +} + +// Get storage capacity of a logical unit +STATIC int8_t psram_msc_GetCapacity(uint8_t lun, uint32_t *block_num, uint16_t *block_size) +{ + // might be important not to write to pointers if unexpected LUN + if(lun != MY_LUN) return -1; + + ckcc_usb_active = true; + + *block_num = BLOCK_COUNT; + *block_size = BLOCK_SIZE; + + return 0; +} + +// Check if a logical unit is ready +STATIC int8_t psram_msc_IsReady(uint8_t lun) +{ + if(lun != MY_LUN) return -1; + + // NOTE: called frequently, and must be T for MacOS to recognize at all + // when F, macos keeps trying to work until it's ready again (freezing programs + // trying to work with the drive). + return flag_STARTED ? 0 : -1; +} + +// Check if a logical unit is write protected +STATIC int8_t psram_msc_IsWriteProtected(uint8_t lun) +{ + if(lun != MY_LUN) return -1; + + return flag_READONLY ? 1 : 0; +} + +// Start or stop a logical unit +STATIC int8_t psram_msc_StartStopUnit(uint8_t lun, uint8_t started) +{ + if(lun != MY_LUN) return -1; + + // host is not allowed to change our ready status: always fail + //printf("PSRAMdisk: started=%d tried\n", started); + ckcc_usb_active = true; + + if(!started) { + // (macos) is trying to "eject" the disk. Note this event. + wr_timeout_now(); + } + + return -1; +#if 0 + if (started) { + flag_STARTED = true; + ckcc_usb_active = true; + } else { + flag_STARTED = false; + } + return 0; +#endif +} + +// Prepare a logical unit for possible removal +STATIC int8_t psram_msc_PreventAllowMediumRemoval(uint8_t lun, uint8_t param) +{ + if(lun != MY_LUN) return -1; + + //printf("PSRAMdisk: prevallow=%d\n", param); + if(param == 0) { + // allow removal == host is done (like after umount in MacOS) + wr_timeout_now(); + } + + return 0; +} + +// Read data from a logical unit +STATIC int8_t psram_msc_Read(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len) +{ + if(lun != MY_LUN) return -1; + + ckcc_usb_active = true; + + uint8_t *ptr = block_to_ptr(blk_addr, blk_len); + if(!ptr) return -1; + + memcpy(buf, ptr, blk_len*BLOCK_SIZE); + + return 0; +} + +// Write data to a logical unit +STATIC int8_t psram_msc_Write(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len) +{ + if(lun != MY_LUN) return -1; + + ckcc_usb_active = true; + + uint8_t *ptr = block_to_ptr(blk_addr, blk_len); + if(!ptr) return -1; + + memcpy(ptr, buf, blk_len*BLOCK_SIZE); + + reset_wr_timeout(); + + return 0; +} + +// Get the number of attached logical units +STATIC int8_t psram_msc_GetMaxLun(void) { + ckcc_usb_active = true; + + return psram_msc_lu_num - 1; +} + + +// Table of operations for the SCSI layer to call +USBD_StorageTypeDef psramdisk_fops = { + psram_msc_Init, + psram_msc_Inquiry, + psram_msc_GetCapacity, + psram_msc_IsReady, + psram_msc_IsWriteProtected, + psram_msc_StartStopUnit, + psram_msc_PreventAllowMediumRemoval, + psram_msc_Read, + psram_msc_Write, + psram_msc_GetMaxLun, +}; + +void psramdisk_USBD_MSC_RegisterStorage(int num_lun, usbd_cdc_msc_hid_state_t *usbd) { + // equiv to usbdev/class/inc/usbd_cdc_msc_hid.h + usbd->MSC_BOT_ClassData.bdev_ops = &psramdisk_fops; + //mp_printf(&mp_plat_print, "PSRAMdisk: activated\n"); +} + +// +// mpy user interface: os.AbstractBlockDev interface +// +// see +// + + +STATIC void psram_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + mp_printf(print, "PSRAM()"); +} + +STATIC mp_obj_t psram_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + // Parse arguments: none allowed + mp_arg_parse_all_kw_array(n_args, n_kw, all_args, 0, NULL, NULL); + + // singleton, we take no args + return MP_OBJ_FROM_PTR(&psram_obj); +} + +STATIC mp_obj_t psram_readblocks(size_t n_args, const mp_obj_t *args) { + //psram_obj_t *self = MP_OBJ_TO_PTR(args[0]); + uint32_t block_num = mp_obj_get_int(args[1]); + + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(args[2], &bufinfo, MP_BUFFER_WRITE); + + // Full range check; not supporting partial blocks nor offsets + uint16_t blk_len = bufinfo.len / BLOCK_SIZE; + if(blk_len < 1) goto fail; + if((blk_len * BLOCK_SIZE) != bufinfo.len) goto fail; + + uint8_t *ptr = block_to_ptr(block_num, blk_len); + if(!ptr) goto fail; + + memcpy(bufinfo.buf, ptr, bufinfo.len); + + return MP_OBJ_NEW_SMALL_INT(0); +fail: + mp_raise_ValueError(NULL); + return mp_const_none; // not reached +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(psram_readblocks_obj, 3, 3, psram_readblocks); + +STATIC mp_obj_t psram_writeblocks(size_t n_args, const mp_obj_t *args) { + //psram_obj_t *self = MP_OBJ_TO_PTR(args[0]); + uint32_t block_num = mp_obj_get_int(args[1]); + + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(args[2], &bufinfo, MP_BUFFER_READ); + + // Full range check; not supporting partial blocks nor offsets + uint16_t blk_len = bufinfo.len / BLOCK_SIZE; + if(blk_len < 1) goto fail; + if((blk_len * BLOCK_SIZE) != bufinfo.len) goto fail; + + uint8_t *ptr = block_to_ptr(block_num, blk_len); + if(!ptr) goto fail; + + memcpy(ptr, bufinfo.buf, bufinfo.len); + + return MP_OBJ_NEW_SMALL_INT(0); +fail: + mp_raise_ValueError(NULL); + return mp_const_none; // not reached +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(psram_writeblocks_obj, 3, 3, psram_writeblocks); + +int direct_psram_read_blocks(uint8_t *dest, uint32_t block_num, uint32_t num_blocks) { + // Return zero or -MP_EIO + uint8_t *ptr = block_to_ptr(block_num, num_blocks); + if(!ptr) return -MP_EIO; + + memcpy(dest, ptr, num_blocks * BLOCK_SIZE); + + return 0; +} +int direct_psram_write_blocks(const uint8_t *src, uint32_t block_num, uint32_t num_blocks) { + // Return zero or -MP_EIO + uint8_t *ptr = block_to_ptr(block_num, num_blocks); + if(!ptr) return -MP_EIO; + + memcpy(ptr, src, num_blocks * BLOCK_SIZE); + + // Need some recovery time here for PSRAM or QUADSPI module. Otherwise, lockup! + asm("nop"); + asm("nop"); + asm("nop"); + asm("nop"); + + return 0; +} + +STATIC mp_obj_t psram_ioctl(mp_obj_t self_in, mp_obj_t cmd_in, mp_obj_t arg_in) { + //psram_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_int_t cmd = mp_obj_get_int(cmd_in); + + switch (cmd) { + case MP_BLOCKDEV_IOCTL_SYNC: + // umount() called; CC done w/ filesystem + return MP_OBJ_NEW_SMALL_INT(0); + + case MP_BLOCKDEV_IOCTL_INIT: // when mount() happens (even R/O) + case MP_BLOCKDEV_IOCTL_DEINIT: // not observed + // nothing to do + return MP_OBJ_NEW_SMALL_INT(0); + + case MP_BLOCKDEV_IOCTL_BLOCK_COUNT: + return MP_OBJ_NEW_SMALL_INT(BLOCK_COUNT); + + case MP_BLOCKDEV_IOCTL_BLOCK_SIZE: + return MP_OBJ_NEW_SMALL_INT(BLOCK_SIZE); + + case MP_BLOCKDEV_IOCTL_BLOCK_ERASE: { + mp_int_t block_num = mp_obj_get_int(arg_in); + + uint8_t *ptr = block_to_ptr(block_num, 1); + if(!ptr) return mp_const_none; + + memset(ptr, 0xff, BLOCK_SIZE); + + return MP_OBJ_NEW_SMALL_INT(0); + } + + default: + return mp_const_none; + } +} +STATIC MP_DEFINE_CONST_FUN_OBJ_3(psram_ioctl_obj, psram_ioctl); + +static void psram_init_vfs(fs_user_mount_t *vfs, bool readonly) { + // Simulates mounting the block device into VFS system. Assumes FAT format. + vfs->base.type = &mp_fat_vfs_type; + vfs->blockdev.flags |= MP_BLOCKDEV_FLAG_NATIVE | MP_BLOCKDEV_FLAG_HAVE_IOCTL; + + vfs->fatfs.drv = vfs; + vfs->fatfs.part = 0; // no partions; we have no MBR, like a floppy + vfs->blockdev.readblocks[0] = MP_OBJ_FROM_PTR(&psram_readblocks_obj); + vfs->blockdev.readblocks[1] = MP_OBJ_FROM_PTR(&psram_obj); + vfs->blockdev.readblocks[2] = MP_OBJ_FROM_PTR(direct_psram_read_blocks); + if(!readonly) { + vfs->blockdev.writeblocks[0] = MP_OBJ_FROM_PTR(&psram_writeblocks_obj); + vfs->blockdev.writeblocks[1] = MP_OBJ_FROM_PTR(&psram_obj); + vfs->blockdev.writeblocks[2] = MP_OBJ_FROM_PTR(direct_psram_write_blocks); + } + vfs->blockdev.u.ioctl[0] = MP_OBJ_FROM_PTR(&psram_ioctl_obj); + vfs->blockdev.u.ioctl[1] = MP_OBJ_FROM_PTR(&psram_obj); +} + +mp_obj_t psram_wipe_and_setup(mp_obj_t unused_self) +{ + // Erase and reformat filesystem + // - you probably should unmount it, before calling this + + // Wipe contents for security. + memset(PSRAM_TOP_BASE, 0x21, BLOCK_SIZE * BLOCK_COUNT); + + // Build obj to handle blockdev protocol + fs_user_mount_t vfs = {0}; + psram_init_vfs(&vfs, false); + + // newfs: + // - FAT16 (auto) + // - cluster=sector=512 to keep it simple + // - FM_SFD=>start sector=0, not 63 "single partition" no MBR wastage + uint8_t working_buf[FF_MAX_SS]; + FRESULT res = f_mkfs(&vfs.fatfs, FM_FAT|FM_SFD, BLOCK_SIZE, working_buf, sizeof(working_buf)); + if (res != FR_OK) { + //mp_printf(&mp_plat_print, "PSRAM: can't create filesystem\n"); + goto fail; + } + + // set volume label, which becomes mountpoint on MacOS + // .. can't do this from python AFAIK + f_setlabel(&vfs.fatfs, "COLDCARD"); + f_mkdir(&vfs.fatfs, "ident"); + + FIL fp; + UINT n; + + // create an ident file, or two + // - algo matches shared/version.py serial_number() function + { char fname[80]; + const uint8_t *id = (const uint8_t *)MP_HAL_UNIQUE_ID_ADDRESS; // 12 bytes, binary + snprintf(fname, sizeof(fname), + "ident/ckcc-%02X%02X%02X%02X%02X%02X.txt", + id[11], id[10] + id[2], id[9], id[8] + id[0], id[7], id[6]); + const char *serial = &fname[11]; + + f_open(&vfs.fatfs, &fp, fname, FA_WRITE | FA_CREATE_ALWAYS); + f_write(&fp, serial, 12, &n); + f_write(&fp, "\r\n", 2, &n); + f_close(&fp); + + f_open(&vfs.fatfs, &fp, "ident/serial.txt", FA_WRITE | FA_CREATE_ALWAYS); + f_write(&fp, serial, 12, &n); + f_write(&fp, "\r\n", 2, &n); + f_close(&fp); + } + + return mp_const_none; + +fail: + mp_raise_ValueError(NULL); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(psram_wipe_obj, psram_wipe_and_setup); + +// copy from lib/oofatfs/ff.c +static DWORD clst2sect ( /* !=0:Sector number, 0:Failed (invalid cluster#) */ + FATFS* fs, /* Filesystem object */ + DWORD clst /* Cluster# to be converted */ +) +{ + clst -= 2; /* Cluster number is origin from 2 */ + if (clst >= fs->n_fatent - 2) return 0; /* Is it invalid cluster number? */ + return fs->database + fs->csize * clst; /* Start sector number of the cluster */ +} + + +mp_obj_t psram_mmap_file(mp_obj_t unused_self, mp_obj_t fname_in) +{ + // Find a file inside a FATFS and return a list of tuples which + // provide the physical locations/lengths of the bytes of the + // file's contents. Effectively it's the mmap call. + // - file path must be striped of mountpt + const char *fname = mp_obj_str_get_str(fname_in); + + // Build obj to handle python protocol + fs_user_mount_t vfs = {0}; + psram_init_vfs(&vfs, true); + + FRESULT res = f_mount(&vfs.fatfs); + if (res != FR_OK) { + mp_raise_ValueError(MP_ERROR_TEXT("unmountable")); + } + + // open the file directly + FIL fp = {0}; + if(f_open(&vfs.fatfs, &fp, fname, FA_READ) != FR_OK) { + mp_raise_ValueError(MP_ERROR_TEXT("file no open")); + } + + // see to learn this magic + DWORD mapping[64]; + mapping[0] = MP_ARRAY_SIZE(mapping); + fp.cltbl = mapping; + + int rv = f_lseek(&fp, CREATE_LINKMAP); + if(rv != FR_OK) { + mp_raise_ValueError(MP_ERROR_TEXT("lseek")); + } + + // Convert and remap list of clusters + + // import ckcc; ckcc.PSRAM().mmap('serial.txt') + + int num_used = (mapping[0] - 1) / 2; + if((num_used < 1) || (num_used >= MP_ARRAY_SIZE(mapping))) { + mp_raise_ValueError(NULL); + } + DWORD *ptr = &mapping[1]; + + mp_obj_t tups[num_used]; + + uint32_t so_far = 0; + for(int i=0; i ", i, cluster, num_clusters); + //printf("0x%lx\n", clst2sect(&vfs.fatfs, cluster)); + mp_raise_ValueError(MP_ERROR_TEXT("clstfck")); + } + uint32_t len = num_clusters*BLOCK_SIZE; + + if(i == num_used-1) { + // final cluster might include some bytes past the EOF + len = fp.obj.objsize - so_far; + } else { + so_far += len; + } + + mp_obj_t here[2] = { + mp_obj_new_int_from_uint((uint32_t)spot), + MP_OBJ_NEW_SMALL_INT(len) + }; + + tups[i] = mp_obj_new_tuple(2, here); + + } + f_close(&fp); + + return mp_obj_new_list(num_used, tups); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(psram_mmap_file_obj, psram_mmap_file); + +mp_obj_t psram_copy_file(mp_obj_t unused_self, mp_obj_t offset_in, mp_obj_t fname_in) +{ + // Find a file inside a FATFS and copy it into another area of PSRAM. + // - file path must be striped of mountpt + uint32_t offset = mp_obj_get_int(offset_in); // checks below + const char *fname = mp_obj_str_get_str(fname_in); + + // Build obj to handle python protocol + fs_user_mount_t vfs = {0}; + psram_init_vfs(&vfs, true); + + FRESULT res = f_mount(&vfs.fatfs); + if (res != FR_OK) { + mp_raise_ValueError(MP_ERROR_TEXT("unmountable")); + } + + // open the file directly + FIL fp = {0}; + if(f_open(&vfs.fatfs, &fp, fname, FA_READ) != FR_OK) { + mp_raise_ValueError(MP_ERROR_TEXT("file no open")); + } + + // see to learn this magic + DWORD mapping[64]; + mapping[0] = MP_ARRAY_SIZE(mapping); + fp.cltbl = mapping; + + int rv = f_lseek(&fp, CREATE_LINKMAP); + if(rv != FR_OK) { + mp_raise_ValueError(MP_ERROR_TEXT("lseek")); + } + + // Convert and remap list of clusters + + int num_used = (mapping[0] - 1) / 2; + if((num_used < 1) || (num_used >= MP_ARRAY_SIZE(mapping))) { + mp_raise_ValueError(NULL); + } + + uint32_t actual_len = fp.obj.objsize; + + // where we will put copy + uint8_t *dest = PSRAM_BOT_BASE + offset; + if(offset % 4) mp_raise_ValueError(NULL); + if(((uint32_t)dest) % 4) mp_raise_ValueError(NULL); + if(dest < PSRAM_BOT_BASE) mp_raise_ValueError(NULL); + if(dest >= PSRAM_TOP_BASE) mp_raise_ValueError(NULL); + if(dest+actual_len+3 >= PSRAM_TOP_BASE) mp_raise_ValueError(NULL); + + uint32_t so_far = 0; + DWORD *ptr = &mapping[1]; + for(int i=0; i ", i, cluster, num_clusters); + //printf("0x%lx\n", clst2sect(&vfs.fatfs, cluster)); + mp_raise_ValueError(MP_ERROR_TEXT("clstfck")); + } + uint32_t len = num_clusters*BLOCK_SIZE; + + if(i == num_used-1) { + // final cluster might include some bytes past the EOF + len = actual_len - so_far; + // align4 + len = (len + 3) & ~0x3; + } else { + so_far += len; + } + + memcpy(dest, spot, len); + dest += len; + + if(dest >= PSRAM_TOP_BASE) mp_raise_ValueError(NULL); + } + f_close(&fp); + + return MP_OBJ_NEW_SMALL_INT(actual_len); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_3(psram_copy_file_obj, psram_copy_file); + +mp_obj_t psram_set_callback(mp_obj_t unused_self, mp_obj_t callback_in) +{ + // set or clear the callback, use None to disable + soft_timer_remove(&host_wr_done); + + host_wr_done.callback = callback_in; + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(psram_set_callback_obj, psram_set_callback); + +mp_obj_t psram_set_inserted(mp_obj_t unused_self, mp_obj_t enable_in) +{ + // set or clear insertion status (media started) + if(enable_in != MP_ROM_NONE) { + bool enable = !!mp_obj_get_int(enable_in); + + flag_STARTED = enable; + } + + return MP_OBJ_NEW_SMALL_INT(flag_STARTED); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(psram_set_inserted_obj, psram_set_inserted); + +mp_obj_t psram_get_time(mp_obj_t unused_self) +{ + // return time of last write from host + + return mp_obj_new_int_from_uint(psram_obj.host_write_time); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(psram_get_time_obj, psram_get_time); + + +STATIC const mp_rom_map_elem_t psram_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_readblocks), MP_ROM_PTR(&psram_readblocks_obj) }, + { MP_ROM_QSTR(MP_QSTR_writeblocks), MP_ROM_PTR(&psram_writeblocks_obj) }, + { MP_ROM_QSTR(MP_QSTR_ioctl), MP_ROM_PTR(&psram_ioctl_obj) }, + { MP_ROM_QSTR(MP_QSTR_wipe), MP_ROM_PTR(&psram_wipe_obj) }, + { MP_ROM_QSTR(MP_QSTR_mmap), MP_ROM_PTR(&psram_mmap_file_obj) }, + { MP_ROM_QSTR(MP_QSTR_copy_file), MP_ROM_PTR(&psram_copy_file_obj) }, + { MP_ROM_QSTR(MP_QSTR_callback), MP_ROM_PTR(&psram_set_callback_obj) }, + { MP_ROM_QSTR(MP_QSTR_set_inserted), MP_ROM_PTR(&psram_set_inserted_obj) }, + { MP_ROM_QSTR(MP_QSTR_get_time), MP_ROM_PTR(&psram_get_time_obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(psram_locals_dict, psram_locals_dict_table); + +// our block device object for Micropython +const mp_obj_type_t psram_type = { + { &mp_type_type }, + .name = MP_QSTR_PSRAM, + .print = psram_print, + .make_new = psram_make_new, + .locals_dict = (mp_obj_dict_t *)&psram_locals_dict, +}; + + +// EOF diff --git a/stm32/COLDCARD_Q1/rng.c b/stm32/COLDCARD_Q1/rng.c new file mode 100644 index 00000000..374cd32c --- /dev/null +++ b/stm32/COLDCARD_Q1/rng.c @@ -0,0 +1,152 @@ +/* + * (c) Copyright 2018 by Coinkite Inc. This file is covered by license found in COPYING-CC. + * + * based on ../../rng.c but more paranoid + * + */ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 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 + +#include "py/obj.h" +#include "py/runtime.h" +#include "py/mperrno.h" +#include "rng.h" + +#if MICROPY_HW_ENABLE_RNG +#error "this code replaces normal RNG module" +#endif + +void random_buffer(uint8_t *p, size_t count); + +static void rng_init(void) { + if (!(RNG->CR & RNG_CR_RNGEN)) { + __HAL_RCC_RNG_CLK_ENABLE(); + + RNG->CR |= RNG_CR_RNGEN; + + // TODO: throw out some samples? + } +} + + +#define RNG_TIMEOUT_MS (10) + +static uint32_t last_value; + +static uint32_t rng_get_or_fault(void) +{ + // Enable the RNG peripheral if it's not already enabled + rng_init(); + + // Wait for a new random number to be ready, takes on the order of 10us + uint32_t start = HAL_GetTick(); + + while (!(RNG->SR & RNG_SR_DRDY)) { + if (HAL_GetTick() - start >= RNG_TIMEOUT_MS) { + // hardware failure... do not return anything! + mp_raise_OSError(MP_EFAULT); + } + } + + // Get and return the new random number + last_value = RNG->DR; + + return last_value; +} + +/// \function pyb_rng_get() +// +/// Return a 30-bit hardware generated random number: or fail! +// +STATIC mp_obj_t pyb_rng_get(void) +{ + // Get and return the new random number + return mp_obj_new_int(rng_get_or_fault() >> 2); +} + +/// \function rng_get_bytes() +/// Fill a buffer with random bits; caller must provide sized buffer. +STATIC mp_obj_t pyb_rng_get_bytes(mp_obj_t buffer_io) { + + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(buffer_io, &bufinfo, MP_BUFFER_WRITE); + + mp_uint_t count = bufinfo.len; + if(count < 1) { + mp_raise_ValueError(NULL); + } + + // Read 32-bit words and unpack into provided buffer + random_buffer(bufinfo.buf, count); + + return mp_const_none; +} + +MP_DEFINE_CONST_FUN_OBJ_0(pyb_rng_get_obj, pyb_rng_get); +MP_DEFINE_CONST_FUN_OBJ_1(pyb_rng_get_bytes_obj, pyb_rng_get_bytes); + + +// compat/replacement for trezor-crypto/rand.[ch] + +// random32() +// + uint32_t +random32(void) +{ + uint32_t rv; + + random_buffer((uint8_t *)&rv, sizeof(uint32_t)); + + return rv; +} + +// random_buffer() +// + void +random_buffer(uint8_t *p, size_t count) +{ + uint32_t last = last_value; + + while(count) { + uint32_t next = rng_get_or_fault(); + + if(next == last) { + // if rng_init0 isn't called at boot time, then this fault will happen! Very Bad! + mp_raise_OSError(MP_EEXIST); + } + + int here = MIN(4, count); + + memcpy(p, &next, here); + p += here; + count -= here; + + last = next; + } +} + diff --git a/stm32/COLDCARD_Q1/rng.h b/stm32/COLDCARD_Q1/rng.h new file mode 100644 index 00000000..28775b33 --- /dev/null +++ b/stm32/COLDCARD_Q1/rng.h @@ -0,0 +1,8 @@ +/* + * (c) Copyright 2018 by Coinkite Inc. This file is covered by license found in COPYING-CC. + */ +#pragma once + +MP_DECLARE_CONST_FUN_OBJ_0(pyb_rng_get_obj); +MP_DECLARE_CONST_FUN_OBJ_1(pyb_rng_get_bytes_obj); + diff --git a/stm32/COLDCARD_Q1/stm32l4s5_af.csv b/stm32/COLDCARD_Q1/stm32l4s5_af.csv new file mode 100644 index 00000000..01db8957 --- /dev/null +++ b/stm32/COLDCARD_Q1/stm32l4s5_af.csv @@ -0,0 +1,116 @@ +Port,,AF0,AF1,AF2,AF3,AF4,AF5,AF6,AF7,AF8,AF9,AF10,AF11,AF12,AF13,AF14,AF15,, +,,SYS_AF,TIM1/TIM2/TIM5/TIM8/LPTIM1,TIM1/TIM2/TIM3/TIM4/TIM5,TIM8,I2C1/I2C2/I2C3,SPI1/SPI2,SPI3/DFSDM,USART1/USART2/USART3,UART4/UART5/LPUART1,CAN1/TSC,OTG_FS/QUADSPI,LCD,SDMMC1/COMP1/COMP2/FMC/SWPMI1,SAI1/SAI2,TIM2/TIM15/TIM16/TIM17/LPTIM2,EVENTOUT,ADC,COMP +PortA,PA0,,TIM2_CH1,TIM5_CH1,TIM8_ETR,,,,USART2_CTS,UART4_TX,,,,,SAI1_EXTCLK,TIM2_ETR,EVENTOUT,ADC12_IN5, +PortA,PA1,,TIM2_CH2,TIM5_CH2,,,,,USART2_RTS/USART2_DE,UART4_RX,,,LCD_SEG0,,,TIM15_CH1N,EVENTOUT,ADC12_IN6, +PortA,PA2,,TIM2_CH3,TIM5_CH3,,,,,USART2_TX,,,,LCD_SEG1,,SAI2_EXTCLK,TIM15_CH1,EVENTOUT,ADC12_IN7, +PortA,PA3,,TIM2_CH4,TIM5_CH4,,,,,USART2_RX,,,,LCD_SEG2,,,TIM15_CH2,EVENTOUT,ADC12_IN8, +PortA,PA4,,,,,,SPI1_NSS,SPI3_NSS,USART2_CK,,,,,,SAI1_FS_B,LPTIM2_OUT,EVENTOUT,ADC12_IN9, +PortA,PA5,,TIM2_CH1,TIM2_ETR,TIM8_CH1N,,SPI1_SCK,,,,,,,,,LPTIM2_ETR,EVENTOUT,ADC12_IN10, +PortA,PA6,,TIM1_BKIN,TIM3_CH1,TIM8_BKIN,,SPI1_MISO,,USART3_CTS,,,QUADSPI_BK1_IO3,LCD_SEG3,TIM1_BKIN_COMP2,TIM8_BKIN_COMP2,TIM16_CH1,EVENTOUT,ADC12_IN11, +PortA,PA7,,TIM1_CH1N,TIM3_CH2,TIM8_CH1N,,SPI1_MOSI,,,,,QUADSPI_BK1_IO2,LCD_SEG4,,,TIM17_CH1,EVENTOUT,ADC12_IN12, +PortA,PA8,MCO,TIM1_CH1,,,,,,USART1_CK,,,OTG_FS_SOF,LCD_COM0,,,LPTIM2_OUT,EVENTOUT,, +PortA,PA9,,TIM1_CH2,,,,,,USART1_TX,,,,LCD_COM1,,,TIM15_BKIN,EVENTOUT,, +PortA,PA10,,TIM1_CH3,,,,,,USART1_RX,,,OTG_FS_ID,LCD_COM2,,,TIM17_BKIN,EVENTOUT,, +PortA,PA11,,TIM1_CH4,TIM1_BKIN2,,,,,USART1_CTS,,CAN1_RX,OTG_FS_DM,,TIM1_BKIN2_COMP1,,,EVENTOUT,, +PortA,PA12,,TIM1_ETR,,,,,,USART1_RTS/USART1_DE,,CAN1_TX,OTG_FS_DP,,,,,EVENTOUT,, +PortA,PA13,JTMS/SWDIO,IR_OUT,,,,,,,,,OTG_FS_NOE,,,,,EVENTOUT,, +PortA,PA14,JTCK/SWCLK,,,,,,,,,,,,,,,EVENTOUT,, +PortA,PA15,JTDI,TIM2_CH1,TIM2_ETR,,,SPI1_NSS,SPI3_NSS,,UART4_RTS/UART4_DE,TSC_G3_IO1,,LCD_SEG17,,SAI2_FS_B,,EVENTOUT,, +PortB,PB0,,TIM1_CH2N,TIM3_CH3,TIM8_CH2N,,,,USART3_CK,,,QUADSPI_BK1_IO1,LCD_SEG5,COMP1_OUT,,,EVENTOUT,ADC12_IN15, +PortB,PB1,,TIM1_CH3N,TIM3_CH4,TIM8_CH3N,,,DFSDM_DATIN0,USART3_RTS/USART3_DE,,,QUADSPI_BK1_IO0,LCD_SEG6,,,LPTIM2_IN1,EVENTOUT,ADC12_IN16,COMP1_INN +PortB,PB2,RTC_OUT,LPTIM1_OUT,,,I2C3_SMBA,,DFSDM_CKIN0,,,,,,,,,EVENTOUT,,COMP1_INP +PortB,PB3,JTDO/TRACESWO,TIM2_CH2,,,,SPI1_SCK,SPI3_SCK,USART1_RTS/USART1_DE,,,,LCD_SEG7,,SAI1_SCK_B,,EVENTOUT,,COMP2_INM +PortB,PB4,NJTRST,,TIM3_CH1,,,SPI1_MISO,SPI3_MISO,USART1_CTS,UART5_RTS/UART5_DE,TSC_G2_IO1,,LCD_SEG8,,SAI1_MCLK_B,TIM17_BKIN,EVENTOUT,,COMP2_INP +PortB,PB5,,LPTIM1_IN1,TIM3_CH2,,I2C1_SMBA,SPI1_MOSI,SPI3_MOSI,USART1_CK,UART5_CTS,TSC_G2_IO2,,LCD_SEG9,COMP2_OUT,SAI1_SD_B,TIM16_BKIN,EVENTOUT,, +PortB,PB6,,LPTIM1_ETR,TIM4_CH1,TIM8_BKIN2,I2C1_SCL,,DFSDM_DATIN5,USART1_TX,,TSC_G2_IO3,,,TIM8_BKIN2_COMP2,SAI1_FS_B,TIM16_CH1N,EVENTOUT,,COMP2_INP +PortB,PB7,,LPTIM1_IN2,TIM4_CH2,TIM8_BKIN,I2C1_SDA,,DFSDM_CKIN5,USART1_RX,UART4_CTS,TSC_G2_IO4,,LCD_SEG21,FMC_NL,TIM8_BKIN_COMP1,TIM17_CH1N,EVENTOUT,,COMP2_INM +PortB,PB8,,,TIM4_CH3,,I2C1_SCL,,DFSDM_DATIN6,,,CAN1_RX,,LCD_SEG16,SDMMC1_D4,SAI1_MCLK_A,TIM16_CH1,EVENTOUT,, +PortB,PB9,,IR_OUT,TIM4_CH4,,I2C1_SDA,SPI2_NSS,DFSDM_CKIN6,,,CAN1_TX,,LCD_COM3,SDMMC1_D5,SAI1_FS_A,TIM17_CH1,EVENTOUT,, +PortB,PB10,,TIM2_CH3,,,I2C2_SCL,SPI2_SCK,DFSDM_DATIN7,USART3_TX,LPUART1_RX,,QUADSPI_CLK,LCD_SEG10,COMP1_OUT,SAI1_SCK_A,,EVENTOUT,, +PortB,PB11,,TIM2_CH4,,,I2C2_SDA,,DFSDM_CKIN7,USART3_RX,LPUART1_TX,,QUADSPI_NCS,LCD_SEG11,COMP2_OUT,,,EVENTOUT,, +PortB,PB12,,TIM1_BKIN,,TIM1_BKIN_COMP2,I2C2_SMBA,SPI2_NSS,DFSDM_DATIN1,USART3_CK,LPUART1_RTS/LPUART1_DE,TSC_G1_IO1,,LCD_SEG12,SWPMI1_IO,SAI2_FS_A,TIM15_BKIN,EVENTOUT,, +PortB,PB13,,TIM1_CH1N,,,I2C2_SCL,SPI2_SCK,DFSDM_CKIN1,USART3_CTS,LPUART1_CTS,TSC_G1_IO2,,LCD_SEG13,SWPMI1_TX,SAI2_SCK_A,TIM15_CH1N,EVENTOUT,, +PortB,PB14,,TIM1_CH2N,,TIM8_CH2N,I2C2_SDA,SPI2_MISO,DFSDM_DATIN2,USART3_RTS_DE,,TSC_G1_IO3,,LCD_SEG14,SWPMI1_RX,SAI2_MCLK_A,TIM15_CH1,EVENTOUT,, +PortB,PB15,RTC_REFIN,TIM1_CH3N,,TIM8_CH3N,,SPI2_MOSI,DFSDM_CKIN2,,,TSC_G1_IO4,,LCD_SEG15,SWPMI1_SUSPEND,SAI2_SD_A,TIM15_CH2,EVENTOUT,, +PortC,PC0,,LPTIM1_IN1,,,I2C3_SCL,,DFSDM_DATIN4,,LPUART1_RX,,,LCD_SEG18,,,LPTIM2_IN1,EVENTOUT,ADC123_IN1, +PortC,PC1,,LPTIM1_OUT,,,I2C3_SDA,,DFSDM_CKIN4,,LPUART1_TX,,,LCD_SEG19,,,,EVENTOUT,ADC123_IN2, +PortC,PC2,,LPTIM1_IN2,,,,SPI2_MISO,DFSDM_CKOUT,,,,,LCD_SEG20,,,,EVENTOUT,ADC123_IN3, +PortC,PC3,,LPTIM1_ETR,,,,SPI2_MOSI,,,,,,LCD_VLCD,,SAI1_SD_A,LPTIM2_ETR,EVENTOUT,ADC123_IN4, +PortC,PC4,,,,,,,,USART3_TX,,,,LCD_SEG22,,,,EVENTOUT,ADC12_IN13,COMP1_INM +PortC,PC5,,,,,,,,USART3_RX,,,,LCD_SEG23,,,,EVENTOUT,ADC12_IN14,COMP1_INP +PortC,PC6,,,TIM3_CH1,TIM8_CH1,,,DFSDM_CKIN3,,,TSC_G4_IO1,,LCD_SEG24,SDMMC1_D6,SAI2_MCLK_A,,EVENTOUT,, +PortC,PC7,,,TIM3_CH2,TIM8_CH2,,,DFSDM_DATIN3,,,TSC_G4_IO2,,LCD_SEG25,SDMMC1_D7,SAI2_MCLK_B,,EVENTOUT,, +PortC,PC8,,,TIM3_CH3,TIM8_CH3,,,,,,TSC_G4_IO3,,LCD_SEG26,SDMMC1_D0,,,EVENTOUT,, +PortC,PC9,,TIM8_BKIN2,TIM3_CH4,TIM8_CH4,,,,,,TSC_G4_IO4,OTG_FS_NOE,LCD_SEG27,SDMMC1_D1,SAI2_EXTCLK,TIM8_BKIN2_COMP1,EVENTOUT,, +PortC,PC10,,,,,,,SPI3_SCK,USART3_TX,UART4_TX,TSC_G3_IO2,,LCD_COM4/LCD_SEG28/LCD_SEG40,SDMMC1_D2,SAI2_SCK_B,,EVENTOUT,, +PortC,PC11,,,,,,,SPI3_MISO,USART3_RX,UART4_RX,TSC_G3_IO3,,LCD_COM5/LCD_SEG29/LCD_SEG41,SDMMC1_D3,SAI2_MCLK_B,,EVENTOUT,, +PortC,PC12,,,,,,,SPI3_MOSI,USART3_CK,UART5_TX,TSC_G3_IO4,,LCD_COM6/LCD_SEG30/LCD_SEG42,SDMMC1_CK,SAI2_SD_B,,EVENTOUT,, +PortC,PC13,,,,,,,,,,,,,,,,EVENTOUT,, +PortC,PC14,,,,,,,,,,,,,,,,EVENTOUT,, +PortC,PC15,,,,,,,,,,,,,,,,EVENTOUT,, +PortD,PD0,,,,,,SPI2_NSS,DFSDM_DATIN7,,,CAN1_RX,,,FMC_D2,,,EVENTOUT,, +PortD,PD1,,,,,,SPI2_SCK,DFSDM_CKIN7,,,CAN1_TX,,,FMC_D3,,,EVENTOUT,, +PortD,PD2,,,TIM3_ETR,,,,,USART3_RTS/USART3_DE,UART5_RX,TSC_SYNC,,LCD_COM7/LCD_SEG31/LCD_SEG43,SDMMC1_CMD,,,EVENTOUT,, +PortD,PD3,,,,,,SPI2_MISO,DFSDM_DATIN0,USART2_CTS,,,,,FMC_CLK,,,EVENTOUT,, +PortD,PD4,,,,,,SPI2_MOSI,DFSDM_CKIN0,USART2_RTS/USART2_DE,,,,,FMC_NOE,,,EVENTOUT,, +PortD,PD5,,,,,,,,USART2_TX,,,,,FMC_NWE,,,EVENTOUT,, +PortD,PD6,,,,,,,DFSDM_DATIN1,USART2_RX,,,,,FMC_NWAIT,SAI1_SD_A,,EVENTOUT,, +PortD,PD7,,,,,,,DFSDM_CKIN1,USART2_CK,,,,,FMC_NE1,,,EVENTOUT,, +PortD,PD8,,,,,,,,USART3_TX,,,,LCD_SEG28,FMC_D13,,,EVENTOUT,, +PortD,PD9,,,,,,,,USART3_RX,,,,LCD_SEG29,FMC_D14,SAI2_MCLK_A,,EVENTOUT,, +PortD,PD10,,,,,,,,USART3_CK,,TSC_G6_IO1,,LCD_SEG30,FMC_D15,SAI2_SCK_A,,EVENTOUT,, +PortD,PD11,,,,,,,,USART3_CTS,,TSC_G6_IO2,,LCD_SEG31,FMC_A16,SAI2_SD_A,LPTIM2_ETR,EVENTOUT,, +PortD,PD12,,,TIM4_CH1,,,,,USART3_RTS/USART3_DE,,TSC_G6_IO3,,LCD_SEG32,FMC_A17,SAI2_FS_A,LPTIM2_IN1,EVENTOUT,, +PortD,PD13,,,TIM4_CH2,,,,,,,TSC_G6_IO4,,LCD_SEG33,FMC_A18,,LPTIM2_OUT,EVENTOUT,, +PortD,PD14,,,TIM4_CH3,,,,,,,,,LCD_SEG34,FMC_D0,,,EVENTOUT,, +PortD,PD15,,,TIM4_CH4,,,,,,,,,LCD_SEG35,FMC_D1,,,EVENTOUT,, +PortE,PE0,,,TIM4_ETR,,,,,,,,,LCD_SEG36,FMC_NBL0,,TIM16_CH1,EVENTOUT,, +PortE,PE1,,,,,,,,,,,,LCD_SEG37,FMC_NBL1,,TIM17_CH1,EVENTOUT,, +PortE,PE2,TRACECLK,,TIM3_ETR,,,,,,,TSC_G7_IO1,,LCD_SEG38,FMC_A23,SAI1_MCLK_A,,EVENTOUT,, +PortE,PE3,TRACED0,,TIM3_CH1,,,,,,,TSC_G7_IO2,,LCD_SEG39,FMC_A19,SAI1_SD_B,,EVENTOUT,, +PortE,PE4,TRACED1,,TIM3_CH2,,,,DFSDM_DATIN3,,,TSC_G7_IO3,,,FMC_A20,SAI1_FS_A,,EVENTOUT,, +PortE,PE5,TRACED2,,TIM3_CH3,,,,DFSDM_CKIN3,,,TSC_G7_IO4,,,FMC_A21,SAI1_SCK_A,,EVENTOUT,, +PortE,PE6,TRACED3,,TIM3_CH4,,,,,,,,,,FMC_A22,SAI1_SD_A,,EVENTOUT,, +PortE,PE7,,TIM1_ETR,,,,,DFSDM_DATIN2,,,,,,FMC_D4,SAI1_SD_B,,EVENTOUT,, +PortE,PE8,,TIM1_CH1N,,,,,DFSDM_CKIN2,,,,,,FMC_D5,SAI1_SCK_B,,EVENTOUT,, +PortE,PE9,,TIM1_CH1,,,,,DFSDM_CKOUT,,,,,,FMC_D6,SAI1_FS_B,,EVENTOUT,, +PortE,PE10,,TIM1_CH2N,,,,,DFSDM_DATIN4,,,TSC_G5_IO1,QUADSPI_CLK,,FMC_D7,SAI1_MCLK_B,,EVENTOUT,, +PortE,PE11,,TIM1_CH2,,,,,DFSDM_CKIN4,,,TSC_G5_IO2,QUADSPI_NCS,,FMC_D8,,,EVENTOUT,, +PortE,PE12,,TIM1_CH3N,,,,SPI1_NSS,DFSDM_DATIN5,,,TSC_G5_IO3,QUADSPI_BK1_IO0,,FMC_D9,,,EVENTOUT,, +PortE,PE13,,TIM1_CH3,,,,SPI1_SCK,DFSDM_CKIN5,,,TSC_G5_IO4,QUADSPI_BK1_IO1,,FMC_D10,,,EVENTOUT,, +PortE,PE14,,TIM1_CH4,TIM1_BKIN2,TIM1_BKIN2_COMP2,,SPI1_MISO,,,,,QUADSPI_BK1_IO2,,FMC_D11,,,EVENTOUT,, +PortE,PE15,,TIM1_BKIN,,TIM1_BKIN_COMP1,,SPI1_MOSI,,,,,QUADSPI_BK1_IO3,,FMC_D12,,,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_IN6, +PortF,PF4,,,,,,,,,,,,,FMC_A4,,,EVENTOUT,ADC3_IN7, +PortF,PF5,,,,,,,,,,,,,FMC_A5,,,EVENTOUT,ADC3_IN8, +PortF,PF6,,TIM5_ETR,TIM5_CH1,,,,,,,,,,,SAI1_SD_B,,EVENTOUT,ADC3_IN9, +PortF,PF7,,,TIM5_CH2,,,,,,,,,,,SAI1_MCLK_B,,EVENTOUT,ADC3_IN10, +PortF,PF8,,,TIM5_CH3,,,,,,,,,,,SAI1_SCK_B,,EVENTOUT,ADC3_IN11, +PortF,PF9,,,TIM5_CH4,,,,,,,,,,,SAI1_FS_B,TIM15_CH1,EVENTOUT,ADC3_IN12, +PortF,PF10,,,,,,,,,,,,,,,TIM15_CH2,EVENTOUT,ADC3_IN13, +PortF,PF11,,,,,,,,,,,,,,,,EVENTOUT,, +PortF,PF12,,,,,,,,,,,,,FMC_A6,,,EVENTOUT,, +PortF,PF13,,,,,,,DFSDM_DATIN6,,,,,,FMC_A7,,,EVENTOUT,, +PortF,PF14,,,,,,,DFSDM_CKIN6,,,TSC_G8_IO1,,,FMC_A8,,,EVENTOUT,, +PortF,PF15,,,,,,,,,,TSC_G8_IO2,,,FMC_A9,,,EVENTOUT,, +PortG,PG0,,,,,,,,,,TSC_G8_IO3,,,FMC_A10,,,EVENTOUT,, +PortG,PG1,,,,,,,,,,TSC_G8_IO4,,,FMC_A11,,,EVENTOUT,, +PortG,PG2,,,,,,SPI1_SCK,,,,,,,FMC_A12,SAI2_SCK_B,,EVENTOUT,, +PortG,PG3,,,,,,SPI1_MISO,,,,,,,FMC_A13,SAI2_FS_B,,EVENTOUT,, +PortG,PG4,,,,,,SPI1_MOSI,,,,,,,FMC_A14,SAI2_MCLK_B,,EVENTOUT,, +PortG,PG5,,,,,,SPI1_NSS,,,LPUART1_CTS,,,,FMC_A15,SAI2_SD_B,,EVENTOUT,, +PortG,PG6,,,,,I2C3_SMBA,,,,LPUART1_RTS/LPUART1_DE,,,,,,,EVENTOUT,, +PortG,PG7,,,,,I2C3_SCL,,,,LPUART1_TX,,,,FMC_INT3,,,EVENTOUT,, +PortG,PG8,,,,,I2C3_SDA,,,,LPUART1_RX,,,,,,,EVENTOUT,, +PortG,PG9,,,,,,,SPI3_SCK,USART1_TX,,,,,FMC_NCE3/FMC_NE2,SAI2_SCK_A,TIM15_CH1N,EVENTOUT,, +PortG,PG10,,LPTIM1_IN1,,,,,SPI3_MISO,USART1_RX,,,,,FMC_NE3,SAI2_FS_A,TIM15_CH1,EVENTOUT,, +PortG,PG11,,LPTIM1_IN2,,,,,SPI3_MOSI,USART1_CTS,,,,,,SAI2_MCLK_A,TIM15_CH2,EVENTOUT,, +PortG,PG12,,LPTIM1_ETR,,,,,SPI3_NSS,USART1_RTS/USART1_DE,,,,,FMC_NE4,SAI2_SD_A,,EVENTOUT,, +PortG,PG13,,,,,I2C1_SDA,,,USART1_CK,,,,,FMC_A24,,,EVENTOUT,, +PortG,PG14,,,,,I2C1_SCL,,,,,,,,FMC_A25,,,EVENTOUT,, +PortG,PG15,,LPTIM1_OUT,,,I2C1_SMBA,,,,,,,,,,,EVENTOUT,, +PortH,PH0,,,,,,,,,,,,,,,,EVENTOUT,, +PortH,PH1,,,,,,,,,,,,,,,,EVENTOUT,, diff --git a/stm32/COLDCARD_Q1/stm32l4xx_hal_conf.h b/stm32/COLDCARD_Q1/stm32l4xx_hal_conf.h new file mode 100644 index 00000000..fd380ab7 --- /dev/null +++ b/stm32/COLDCARD_Q1/stm32l4xx_hal_conf.h @@ -0,0 +1,20 @@ +/* This file is part of the MicroPython project, http://micropython.org/ + * The MIT License (MIT) + * Copyright (c) 2019 Damien P. George + */ +#ifndef MICROPY_INCLUDED_STM32L4XX_HAL_CONF_H +#define MICROPY_INCLUDED_STM32L4XX_HAL_CONF_H + +#include "boards/stm32l4xx_hal_conf_base.h" + +// Oscillator values in Hz +#define HSE_VALUE (8000000) +#define LSE_VALUE (32768) +#define EXTERNAL_SAI1_CLOCK_VALUE (48000) +#define EXTERNAL_SAI2_CLOCK_VALUE (48000) + +// Oscillator timeouts in ms +#define HSE_STARTUP_TIMEOUT (100) +#define LSE_STARTUP_TIMEOUT (5000) + +#endif // MICROPY_INCLUDED_STM32L4XX_HAL_CONF_H diff --git a/stm32/COLDCARD_Q1/stm32l4xx_hal_tsc. b/stm32/COLDCARD_Q1/stm32l4xx_hal_tsc. new file mode 120000 index 00000000..23043ddb --- /dev/null +++ b/stm32/COLDCARD_Q1/stm32l4xx_hal_tsc. @@ -0,0 +1 @@ +../../external/micropython/lib/stm32lib/STM32L4xx_HAL_Driver/Src/stm32l4xx_hal_tsc. \ No newline at end of file diff --git a/stm32/COLDCARD_Q1/stm32l4xx_hal_tsc.c b/stm32/COLDCARD_Q1/stm32l4xx_hal_tsc.c new file mode 100644 index 00000000..2abff4a1 --- /dev/null +++ b/stm32/COLDCARD_Q1/stm32l4xx_hal_tsc.c @@ -0,0 +1,1127 @@ +/** + ****************************************************************************** + * @file stm32l4xx_hal_tsc.c + * @author MCD Application Team + * @brief This file provides firmware functions to manage the following + * functionalities of the Touch Sensing Controller (TSC) peripheral: + * + Initialization and De-initialization + * + Channel IOs, Shield IOs and Sampling IOs configuration + * + Start and Stop an acquisition + * + Read acquisition result + * + Interrupts and flags management + * + @verbatim +================================================================================ + ##### TSC specific features ##### +================================================================================ + [..] + (#) Proven and robust surface charge transfer acquisition principle + + (#) Supports up to 3 capacitive sensing channels per group + + (#) Capacitive sensing channels can be acquired in parallel offering a very good + response time + + (#) Spread spectrum feature to improve system robustness in noisy environments + + (#) Full hardware management of the charge transfer acquisition sequence + + (#) Programmable charge transfer frequency + + (#) Programmable sampling capacitor I/O pin + + (#) Programmable channel I/O pin + + (#) Programmable max count value to avoid long acquisition when a channel is faulty + + (#) Dedicated end of acquisition and max count error flags with interrupt capability + + (#) One sampling capacitor for up to 3 capacitive sensing channels to reduce the system + components + + (#) Compatible with proximity, touchkey, linear and rotary touch sensor implementation + + ##### How to use this driver ##### +================================================================================ + [..] + (#) Enable the TSC interface clock using __HAL_RCC_TSC_CLK_ENABLE() macro. + + (#) GPIO pins configuration + (++) Enable the clock for the TSC GPIOs using __HAL_RCC_GPIOx_CLK_ENABLE() macro. + (++) Configure the TSC pins used as sampling IOs in alternate function output Open-Drain mode, + and TSC pins used as channel/shield IOs in alternate function output Push-Pull mode + using HAL_GPIO_Init() function. + + (#) Interrupts configuration + (++) Configure the NVIC (if the interrupt model is used) using HAL_NVIC_SetPriority() + and HAL_NVIC_EnableIRQ() and function. + + (#) TSC configuration + (++) Configure all TSC parameters and used TSC IOs using HAL_TSC_Init() function. + + [..] TSC peripheral alternate functions are mapped on AF9. + + *** Acquisition sequence *** + =================================== + [..] + (+) Discharge all IOs using HAL_TSC_IODischarge() function. + (+) Wait a certain time allowing a good discharge of all capacitors. This delay depends + of the sampling capacitor and electrodes design. + (+) Select the channel IOs to be acquired using HAL_TSC_IOConfig() function. + (+) Launch the acquisition using either HAL_TSC_Start() or HAL_TSC_Start_IT() function. + If the synchronized mode is selected, the acquisition will start as soon as the signal + is received on the synchro pin. + (+) Wait the end of acquisition using either HAL_TSC_PollForAcquisition() or + HAL_TSC_GetState() function or using WFI instruction for example. + (+) Check the group acquisition status using HAL_TSC_GroupGetStatus() function. + (+) Read the acquisition value using HAL_TSC_GroupGetValue() function. + + *** Callback registration *** + ============================================= + + [..] + The compilation flag USE_HAL_TSC_REGISTER_CALLBACKS when set to 1 + allows the user to configure dynamically the driver callbacks. + Use Functions @ref HAL_TSC_RegisterCallback() to register an interrupt callback. + + [..] + Function @ref HAL_TSC_RegisterCallback() allows to register following callbacks: + (+) ConvCpltCallback : callback for conversion complete process. + (+) ErrorCallback : callback for error detection. + (+) MspInitCallback : callback for Msp Init. + (+) MspDeInitCallback : callback for Msp DeInit. + [..] + This function takes as parameters the HAL peripheral handle, the Callback ID + and a pointer to the user callback function. + + [..] + Use function @ref HAL_TSC_UnRegisterCallback to reset a callback to the default + weak function. + @ref HAL_TSC_UnRegisterCallback takes as parameters the HAL peripheral handle, + and the Callback ID. + [..] + This function allows to reset following callbacks: + (+) ConvCpltCallback : callback for conversion complete process. + (+) ErrorCallback : callback for error detection. + (+) MspInitCallback : callback for Msp Init. + (+) MspDeInitCallback : callback for Msp DeInit. + + [..] + By default, after the @ref HAL_TSC_Init() and when the state is @ref HAL_TSC_STATE_RESET + all callbacks are set to the corresponding weak functions: + examples @ref HAL_TSC_ConvCpltCallback(), @ref HAL_TSC_ErrorCallback(). + Exception done for MspInit and MspDeInit functions that are + reset to the legacy weak functions in the @ref HAL_TSC_Init()/ @ref HAL_TSC_DeInit() only when + these callbacks are null (not registered beforehand). + If MspInit or MspDeInit are not null, the @ref HAL_TSC_Init()/ @ref HAL_TSC_DeInit() + keep and use the user MspInit/MspDeInit callbacks (registered beforehand) whatever the state. + + [..] + Callbacks can be registered/unregistered in @ref HAL_TSC_STATE_READY state only. + Exception done MspInit/MspDeInit functions that can be registered/unregistered + in @ref HAL_TSC_STATE_READY or @ref HAL_TSC_STATE_RESET state, + thus registered (user) MspInit/DeInit callbacks can be used during the Init/DeInit. + Then, the user first registers the MspInit/MspDeInit user callbacks + using @ref HAL_TSC_RegisterCallback() before calling @ref HAL_TSC_DeInit() + or @ref HAL_TSC_Init() function. + + [..] + When the compilation flag USE_HAL_TSC_REGISTER_CALLBACKS is set to 0 or + not defined, the callback registration feature is not available and all callbacks + are set to the corresponding weak functions. + + @endverbatim + ****************************************************************************** + + Table 1. IOs for the STM32L4xx devices + +--------------------------------+ + | IOs | TSC functions | + |--------------|-----------------| + | PB12 (AF) | TSC_G1_IO1 | + | PB13 (AF) | TSC_G1_IO2 | + | PB14 (AF) | TSC_G1_IO3 | + | PB15 (AF) | TSC_G1_IO4 | + |--------------|-----------------| + | PB4 (AF) | TSC_G2_IO1 | + | PB5 (AF) | TSC_G2_IO2 | + | PB6 (AF) | TSC_G2_IO3 | + | PB7 (AF) | TSC_G2_IO4 | + |--------------|-----------------| + | PA15 (AF) | TSC_G3_IO1 | + | PC10 (AF) | TSC_G3_IO2 | + | PC11 (AF) | TSC_G3_IO3 | + | PC12 (AF) | TSC_G3_IO4 | + |--------------|-----------------| + | PC6 (AF) | TSC_G4_IO1 | + | PC7 (AF) | TSC_G4_IO2 | + | PC8 (AF) | TSC_G4_IO3 | + | PC9 (AF) | TSC_G4_IO4 | + |--------------|-----------------| + | PE10 (AF) | TSC_G5_IO1 | + | PE11 (AF) | TSC_G5_IO2 | + | PE12 (AF) | TSC_G5_IO3 | + | PE13 (AF) | TSC_G5_IO4 | + |--------------|-----------------| + | PD10 (AF) | TSC_G6_IO1 | + | PD11 (AF) | TSC_G6_IO2 | + | PD12 (AF) | TSC_G6_IO3 | + | PD13 (AF) | TSC_G6_IO4 | + |--------------|-----------------| + | PE2 (AF) | TSC_G7_IO1 | + | PE3 (AF) | TSC_G7_IO2 | + | PE4 (AF) | TSC_G7_IO3 | + | PE5 (AF) | TSC_G7_IO4 | + |--------------|-----------------| + | PF14 (AF) | TSC_G8_IO1 | + | PF15 (AF) | TSC_G8_IO2 | + | PG0 (AF) | TSC_G8_IO3 | + | PG1 (AF) | TSC_G8_IO4 | + |--------------|-----------------| + | PB10 (AF) | TSC_SYNC | + | PD2 (AF) | | + +--------------------------------+ + + ****************************************************************************** + * @attention + * + *

© Copyright (c) 2017 STMicroelectronics. + * All rights reserved.

+ * + * This software component is licensed by ST under BSD 3-Clause license, + * the "License"; You may not use this file except in compliance with the + * License. You may obtain a copy of the License at: + * opensource.org/licenses/BSD-3-Clause + * + ****************************************************************************** + */ + +/* Includes ------------------------------------------------------------------*/ +#include "stm32l4xx_hal.h" + +/** @addtogroup STM32L4xx_HAL_Driver + * @{ + */ + +/** @defgroup TSC TSC + * @brief HAL TSC module driver + * @{ + */ + +#ifdef HAL_TSC_MODULE_ENABLED + +/* Private typedef -----------------------------------------------------------*/ +/* Private define ------------------------------------------------------------*/ +/* Private macro -------------------------------------------------------------*/ +/* Private variables ---------------------------------------------------------*/ +/* Private function prototypes -----------------------------------------------*/ +static uint32_t TSC_extract_groups(uint32_t iomask); + +/* Exported functions --------------------------------------------------------*/ + +/** @defgroup TSC_Exported_Functions TSC Exported Functions + * @{ + */ + +/** @defgroup TSC_Exported_Functions_Group1 Initialization and de-initialization functions + * @brief Initialization and Configuration functions + * +@verbatim + =============================================================================== + ##### Initialization and de-initialization functions ##### + =============================================================================== + [..] This section provides functions allowing to: + (+) Initialize and configure the TSC. + (+) De-initialize the TSC. +@endverbatim + * @{ + */ + +/** + * @brief Initialize the TSC peripheral according to the specified parameters + * in the TSC_InitTypeDef structure and initialize the associated handle. + * @param htsc TSC handle + * @retval HAL status + */ +HAL_StatusTypeDef HAL_TSC_Init(TSC_HandleTypeDef *htsc) +{ + /* Check TSC handle allocation */ + if (htsc == NULL) + { + return HAL_ERROR; + } + + /* Check the parameters */ + assert_param(IS_TSC_ALL_INSTANCE(htsc->Instance)); + assert_param(IS_TSC_CTPH(htsc->Init.CTPulseHighLength)); + assert_param(IS_TSC_CTPL(htsc->Init.CTPulseLowLength)); + assert_param(IS_TSC_SS(htsc->Init.SpreadSpectrum)); + assert_param(IS_TSC_SSD(htsc->Init.SpreadSpectrumDeviation)); + assert_param(IS_TSC_SS_PRESC(htsc->Init.SpreadSpectrumPrescaler)); + assert_param(IS_TSC_PG_PRESC(htsc->Init.PulseGeneratorPrescaler)); + assert_param(IS_TSC_PG_PRESC_VS_CTPL(htsc->Init.PulseGeneratorPrescaler, htsc->Init.CTPulseLowLength)); + assert_param(IS_TSC_MCV(htsc->Init.MaxCountValue)); + assert_param(IS_TSC_IODEF(htsc->Init.IODefaultMode)); + assert_param(IS_TSC_SYNC_POL(htsc->Init.SynchroPinPolarity)); + assert_param(IS_TSC_ACQ_MODE(htsc->Init.AcquisitionMode)); + assert_param(IS_TSC_MCE_IT(htsc->Init.MaxCountInterrupt)); + assert_param(IS_TSC_GROUP(htsc->Init.ChannelIOs)); + assert_param(IS_TSC_GROUP(htsc->Init.ShieldIOs)); + assert_param(IS_TSC_GROUP(htsc->Init.SamplingIOs)); + + if (htsc->State == HAL_TSC_STATE_RESET) + { + /* Allocate lock resource and initialize it */ + htsc->Lock = HAL_UNLOCKED; + +#if (USE_HAL_TSC_REGISTER_CALLBACKS == 1) + /* Init the TSC Callback settings */ + htsc->ConvCpltCallback = HAL_TSC_ConvCpltCallback; /* Legacy weak ConvCpltCallback */ + htsc->ErrorCallback = HAL_TSC_ErrorCallback; /* Legacy weak ErrorCallback */ + + if (htsc->MspInitCallback == NULL) + { + htsc->MspInitCallback = HAL_TSC_MspInit; /* Legacy weak MspInit */ + } + + /* Init the low level hardware : GPIO, CLOCK, CORTEX...etc */ + htsc->MspInitCallback(htsc); +#else + /* Init the low level hardware : GPIO, CLOCK, CORTEX */ + HAL_TSC_MspInit(htsc); +#endif /* USE_HAL_TSC_REGISTER_CALLBACKS */ + } + + /* Initialize the TSC state */ + htsc->State = HAL_TSC_STATE_BUSY; + + /*--------------------------------------------------------------------------*/ + /* Set TSC parameters */ + + /* Enable TSC */ + htsc->Instance->CR = TSC_CR_TSCE; + + /* Set all functions */ + htsc->Instance->CR |= (htsc->Init.CTPulseHighLength | + htsc->Init.CTPulseLowLength | + (htsc->Init.SpreadSpectrumDeviation << TSC_CR_SSD_Pos) | + htsc->Init.SpreadSpectrumPrescaler | + htsc->Init.PulseGeneratorPrescaler | + htsc->Init.MaxCountValue | + htsc->Init.SynchroPinPolarity | + htsc->Init.AcquisitionMode); + + /* Spread spectrum */ + if (htsc->Init.SpreadSpectrum == ENABLE) + { + htsc->Instance->CR |= TSC_CR_SSE; + } + + /* Disable Schmitt trigger hysteresis on all used TSC IOs */ + htsc->Instance->IOHCR = (~(htsc->Init.ChannelIOs | htsc->Init.ShieldIOs | htsc->Init.SamplingIOs)); + + /* Set channel and shield IOs */ + htsc->Instance->IOCCR = (htsc->Init.ChannelIOs | htsc->Init.ShieldIOs); + + /* Set sampling IOs */ + htsc->Instance->IOSCR = htsc->Init.SamplingIOs; + + /* Set the groups to be acquired */ + htsc->Instance->IOGCSR = TSC_extract_groups(htsc->Init.ChannelIOs); + + /* Disable interrupts */ + htsc->Instance->IER &= (~(TSC_IT_EOA | TSC_IT_MCE)); + + /* Clear flags */ + htsc->Instance->ICR = (TSC_FLAG_EOA | TSC_FLAG_MCE); + + /*--------------------------------------------------------------------------*/ + + /* Initialize the TSC state */ + htsc->State = HAL_TSC_STATE_READY; + + /* Return function status */ + return HAL_OK; +} + +/** + * @brief Deinitialize the TSC peripheral registers to their default reset values. + * @param htsc TSC handle + * @retval HAL status + */ +HAL_StatusTypeDef HAL_TSC_DeInit(TSC_HandleTypeDef *htsc) +{ + /* Check TSC handle allocation */ + if (htsc == NULL) + { + return HAL_ERROR; + } + + /* Check the parameters */ + assert_param(IS_TSC_ALL_INSTANCE(htsc->Instance)); + + /* Change TSC state */ + htsc->State = HAL_TSC_STATE_BUSY; + +#if (USE_HAL_TSC_REGISTER_CALLBACKS == 1) + if (htsc->MspDeInitCallback == NULL) + { + htsc->MspDeInitCallback = HAL_TSC_MspDeInit; /* Legacy weak MspDeInit */ + } + + /* DeInit the low level hardware: GPIO, CLOCK, NVIC */ + htsc->MspDeInitCallback(htsc); +#else + /* DeInit the low level hardware */ + HAL_TSC_MspDeInit(htsc); +#endif /* USE_HAL_TSC_REGISTER_CALLBACKS */ + + /* Change TSC state */ + htsc->State = HAL_TSC_STATE_RESET; + + /* Process unlocked */ + __HAL_UNLOCK(htsc); + + /* Return function status */ + return HAL_OK; +} + +/** + * @brief Initialize the TSC MSP. + * @param htsc Pointer to a TSC_HandleTypeDef structure that contains + * the configuration information for the specified TSC. + * @retval None + */ +__weak void HAL_TSC_MspInit(TSC_HandleTypeDef *htsc) +{ + /* Prevent unused argument(s) compilation warning */ + UNUSED(htsc); + + /* NOTE : This function should not be modified, when the callback is needed, + the HAL_TSC_MspInit could be implemented in the user file. + */ +} + +/** + * @brief DeInitialize the TSC MSP. + * @param htsc Pointer to a TSC_HandleTypeDef structure that contains + * the configuration information for the specified TSC. + * @retval None + */ +__weak void HAL_TSC_MspDeInit(TSC_HandleTypeDef *htsc) +{ + /* Prevent unused argument(s) compilation warning */ + UNUSED(htsc); + + /* NOTE : This function should not be modified, when the callback is needed, + the HAL_TSC_MspDeInit could be implemented in the user file. + */ +} + +#if (USE_HAL_TSC_REGISTER_CALLBACKS == 1) +/** + * @brief Register a User TSC Callback + * To be used instead of the weak predefined callback + * @param htsc Pointer to a TSC_HandleTypeDef structure that contains + * the configuration information for the specified TSC. + * @param CallbackID ID of the callback to be registered + * This parameter can be one of the following values: + * @arg @ref HAL_TSC_CONV_COMPLETE_CB_ID Conversion completed callback ID + * @arg @ref HAL_TSC_ERROR_CB_ID Error callback ID + * @arg @ref HAL_TSC_MSPINIT_CB_ID MspInit callback ID + * @arg @ref HAL_TSC_MSPDEINIT_CB_ID MspDeInit callback ID + * @param pCallback pointer to the Callback function + * @retval HAL status + */ +HAL_StatusTypeDef HAL_TSC_RegisterCallback(TSC_HandleTypeDef *htsc, HAL_TSC_CallbackIDTypeDef CallbackID, + pTSC_CallbackTypeDef pCallback) +{ + HAL_StatusTypeDef status = HAL_OK; + + if (pCallback == NULL) + { + /* Update the error code */ + htsc->ErrorCode |= HAL_TSC_ERROR_INVALID_CALLBACK; + + return HAL_ERROR; + } + /* Process locked */ + __HAL_LOCK(htsc); + + if (HAL_TSC_STATE_READY == htsc->State) + { + switch (CallbackID) + { + case HAL_TSC_CONV_COMPLETE_CB_ID : + htsc->ConvCpltCallback = pCallback; + break; + + case HAL_TSC_ERROR_CB_ID : + htsc->ErrorCallback = pCallback; + break; + + case HAL_TSC_MSPINIT_CB_ID : + htsc->MspInitCallback = pCallback; + break; + + case HAL_TSC_MSPDEINIT_CB_ID : + htsc->MspDeInitCallback = pCallback; + break; + + default : + /* Update the error code */ + htsc->ErrorCode |= HAL_TSC_ERROR_INVALID_CALLBACK; + + /* Return error status */ + status = HAL_ERROR; + break; + } + } + else if (HAL_TSC_STATE_RESET == htsc->State) + { + switch (CallbackID) + { + case HAL_TSC_MSPINIT_CB_ID : + htsc->MspInitCallback = pCallback; + break; + + case HAL_TSC_MSPDEINIT_CB_ID : + htsc->MspDeInitCallback = pCallback; + break; + + default : + /* Update the error code */ + htsc->ErrorCode |= HAL_TSC_ERROR_INVALID_CALLBACK; + + /* Return error status */ + status = HAL_ERROR; + break; + } + } + else + { + /* Update the error code */ + htsc->ErrorCode |= HAL_TSC_ERROR_INVALID_CALLBACK; + + /* Return error status */ + status = HAL_ERROR; + } + + /* Release Lock */ + __HAL_UNLOCK(htsc); + return status; +} + +/** + * @brief Unregister an TSC Callback + * TSC callback is redirected to the weak predefined callback + * @param htsc Pointer to a TSC_HandleTypeDef structure that contains + * the configuration information for the specified TSC. + * @param CallbackID ID of the callback to be unregistered + * This parameter can be one of the following values: + * This parameter can be one of the following values: + * @arg @ref HAL_TSC_CONV_COMPLETE_CB_ID Conversion completed callback ID + * @arg @ref HAL_TSC_ERROR_CB_ID Error callback ID + * @arg @ref HAL_TSC_MSPINIT_CB_ID MspInit callback ID + * @arg @ref HAL_TSC_MSPDEINIT_CB_ID MspDeInit callback ID + * @retval HAL status + */ +HAL_StatusTypeDef HAL_TSC_UnRegisterCallback(TSC_HandleTypeDef *htsc, HAL_TSC_CallbackIDTypeDef CallbackID) +{ + HAL_StatusTypeDef status = HAL_OK; + + /* Process locked */ + __HAL_LOCK(htsc); + + if (HAL_TSC_STATE_READY == htsc->State) + { + switch (CallbackID) + { + case HAL_TSC_CONV_COMPLETE_CB_ID : + htsc->ConvCpltCallback = HAL_TSC_ConvCpltCallback; /* Legacy weak ConvCpltCallback */ + break; + + case HAL_TSC_ERROR_CB_ID : + htsc->ErrorCallback = HAL_TSC_ErrorCallback; /* Legacy weak ErrorCallback */ + break; + + case HAL_TSC_MSPINIT_CB_ID : + htsc->MspInitCallback = HAL_TSC_MspInit; /* Legacy weak MspInit */ + break; + + case HAL_TSC_MSPDEINIT_CB_ID : + htsc->MspDeInitCallback = HAL_TSC_MspDeInit; /* Legacy weak MspDeInit */ + break; + + default : + /* Update the error code */ + htsc->ErrorCode |= HAL_TSC_ERROR_INVALID_CALLBACK; + + /* Return error status */ + status = HAL_ERROR; + break; + } + } + else if (HAL_TSC_STATE_RESET == htsc->State) + { + switch (CallbackID) + { + case HAL_TSC_MSPINIT_CB_ID : + htsc->MspInitCallback = HAL_TSC_MspInit; /* Legacy weak MspInit */ + break; + + case HAL_TSC_MSPDEINIT_CB_ID : + htsc->MspDeInitCallback = HAL_TSC_MspDeInit; /* Legacy weak MspDeInit */ + break; + + default : + /* Update the error code */ + htsc->ErrorCode |= HAL_TSC_ERROR_INVALID_CALLBACK; + + /* Return error status */ + status = HAL_ERROR; + break; + } + } + else + { + /* Update the error code */ + htsc->ErrorCode |= HAL_TSC_ERROR_INVALID_CALLBACK; + + /* Return error status */ + status = HAL_ERROR; + } + + /* Release Lock */ + __HAL_UNLOCK(htsc); + return status; +} + +#endif /* USE_HAL_TSC_REGISTER_CALLBACKS */ + +/** + * @} + */ + +/** @defgroup TSC_Exported_Functions_Group2 Input and Output operation functions + * @brief Input and Output operation functions + * +@verbatim + =============================================================================== + ##### IO Operation functions ##### + =============================================================================== + [..] This section provides functions allowing to: + (+) Start acquisition in polling mode. + (+) Start acquisition in interrupt mode. + (+) Stop conversion in polling mode. + (+) Stop conversion in interrupt mode. + (+) Poll for acquisition completed. + (+) Get group acquisition status. + (+) Get group acquisition value. +@endverbatim + * @{ + */ + +/** + * @brief Start the acquisition. + * @param htsc Pointer to a TSC_HandleTypeDef structure that contains + * the configuration information for the specified TSC. + * @retval HAL status + */ +HAL_StatusTypeDef HAL_TSC_Start(TSC_HandleTypeDef *htsc) +{ + /* Check the parameters */ + assert_param(IS_TSC_ALL_INSTANCE(htsc->Instance)); + + /* Process locked */ + __HAL_LOCK(htsc); + + /* Change TSC state */ + htsc->State = HAL_TSC_STATE_BUSY; + + /* Clear interrupts */ + __HAL_TSC_DISABLE_IT(htsc, (TSC_IT_EOA | TSC_IT_MCE)); + + /* Clear flags */ + __HAL_TSC_CLEAR_FLAG(htsc, (TSC_FLAG_EOA | TSC_FLAG_MCE)); + + /* Set touch sensing IOs not acquired to the specified IODefaultMode */ + if (htsc->Init.IODefaultMode == TSC_IODEF_OUT_PP_LOW) + { + __HAL_TSC_SET_IODEF_OUTPPLOW(htsc); + } + else + { + __HAL_TSC_SET_IODEF_INFLOAT(htsc); + } + + /* Launch the acquisition */ + __HAL_TSC_START_ACQ(htsc); + + /* Process unlocked */ + __HAL_UNLOCK(htsc); + + /* Return function status */ + return HAL_OK; +} + +/** + * @brief Start the acquisition in interrupt mode. + * @param htsc Pointer to a TSC_HandleTypeDef structure that contains + * the configuration information for the specified TSC. + * @retval HAL status. + */ +HAL_StatusTypeDef HAL_TSC_Start_IT(TSC_HandleTypeDef *htsc) +{ + /* Check the parameters */ + assert_param(IS_TSC_ALL_INSTANCE(htsc->Instance)); + assert_param(IS_TSC_MCE_IT(htsc->Init.MaxCountInterrupt)); + + /* Process locked */ + __HAL_LOCK(htsc); + + /* Change TSC state */ + htsc->State = HAL_TSC_STATE_BUSY; + + /* Enable end of acquisition interrupt */ + __HAL_TSC_ENABLE_IT(htsc, TSC_IT_EOA); + + /* Enable max count error interrupt (optional) */ + if (htsc->Init.MaxCountInterrupt == ENABLE) + { + __HAL_TSC_ENABLE_IT(htsc, TSC_IT_MCE); + } + else + { + __HAL_TSC_DISABLE_IT(htsc, TSC_IT_MCE); + } + + /* Clear flags */ + __HAL_TSC_CLEAR_FLAG(htsc, (TSC_FLAG_EOA | TSC_FLAG_MCE)); + + /* Set touch sensing IOs not acquired to the specified IODefaultMode */ + if (htsc->Init.IODefaultMode == TSC_IODEF_OUT_PP_LOW) + { + __HAL_TSC_SET_IODEF_OUTPPLOW(htsc); + } + else + { + __HAL_TSC_SET_IODEF_INFLOAT(htsc); + } + + /* Launch the acquisition */ + __HAL_TSC_START_ACQ(htsc); + + /* Process unlocked */ + __HAL_UNLOCK(htsc); + + /* Return function status */ + return HAL_OK; +} + +/** + * @brief Stop the acquisition previously launched in polling mode. + * @param htsc Pointer to a TSC_HandleTypeDef structure that contains + * the configuration information for the specified TSC. + * @retval HAL status + */ +HAL_StatusTypeDef HAL_TSC_Stop(TSC_HandleTypeDef *htsc) +{ + /* Check the parameters */ + assert_param(IS_TSC_ALL_INSTANCE(htsc->Instance)); + + /* Process locked */ + __HAL_LOCK(htsc); + + /* Stop the acquisition */ + __HAL_TSC_STOP_ACQ(htsc); + + /* Set touch sensing IOs in low power mode (output push-pull) */ + __HAL_TSC_SET_IODEF_OUTPPLOW(htsc); + + /* Clear flags */ + __HAL_TSC_CLEAR_FLAG(htsc, (TSC_FLAG_EOA | TSC_FLAG_MCE)); + + /* Change TSC state */ + htsc->State = HAL_TSC_STATE_READY; + + /* Process unlocked */ + __HAL_UNLOCK(htsc); + + /* Return function status */ + return HAL_OK; +} + +/** + * @brief Stop the acquisition previously launched in interrupt mode. + * @param htsc Pointer to a TSC_HandleTypeDef structure that contains + * the configuration information for the specified TSC. + * @retval HAL status + */ +HAL_StatusTypeDef HAL_TSC_Stop_IT(TSC_HandleTypeDef *htsc) +{ + /* Check the parameters */ + assert_param(IS_TSC_ALL_INSTANCE(htsc->Instance)); + + /* Process locked */ + __HAL_LOCK(htsc); + + /* Stop the acquisition */ + __HAL_TSC_STOP_ACQ(htsc); + + /* Set touch sensing IOs in low power mode (output push-pull) */ + __HAL_TSC_SET_IODEF_OUTPPLOW(htsc); + + /* Disable interrupts */ + __HAL_TSC_DISABLE_IT(htsc, (TSC_IT_EOA | TSC_IT_MCE)); + + /* Clear flags */ + __HAL_TSC_CLEAR_FLAG(htsc, (TSC_FLAG_EOA | TSC_FLAG_MCE)); + + /* Change TSC state */ + htsc->State = HAL_TSC_STATE_READY; + + /* Process unlocked */ + __HAL_UNLOCK(htsc); + + /* Return function status */ + return HAL_OK; +} + +/** + * @brief Start acquisition and wait until completion. + * @note There is no need of a timeout parameter as the max count error is already + * managed by the TSC peripheral. + * @param htsc Pointer to a TSC_HandleTypeDef structure that contains + * the configuration information for the specified TSC. + * @retval HAL state + */ +HAL_StatusTypeDef HAL_TSC_PollForAcquisition(TSC_HandleTypeDef *htsc) +{ + /* Check the parameters */ + assert_param(IS_TSC_ALL_INSTANCE(htsc->Instance)); + + /* Process locked */ + __HAL_LOCK(htsc); + + /* Check end of acquisition */ + while (HAL_TSC_GetState(htsc) == HAL_TSC_STATE_BUSY) + { + /* The timeout (max count error) is managed by the TSC peripheral itself. */ + } + + /* Process unlocked */ + __HAL_UNLOCK(htsc); + + return HAL_OK; +} + +/** + * @brief Get the acquisition status for a group. + * @param htsc Pointer to a TSC_HandleTypeDef structure that contains + * the configuration information for the specified TSC. + * @param gx_index Index of the group + * @retval Group status + */ +TSC_GroupStatusTypeDef HAL_TSC_GroupGetStatus(TSC_HandleTypeDef *htsc, uint32_t gx_index) +{ + /* Check the parameters */ + assert_param(IS_TSC_ALL_INSTANCE(htsc->Instance)); + assert_param(IS_TSC_GROUP_INDEX(gx_index)); + + /* Return the group status */ + return (__HAL_TSC_GET_GROUP_STATUS(htsc, gx_index)); +} + +/** + * @brief Get the acquisition measure for a group. + * @param htsc Pointer to a TSC_HandleTypeDef structure that contains + * the configuration information for the specified TSC. + * @param gx_index Index of the group + * @retval Acquisition measure + */ +uint32_t HAL_TSC_GroupGetValue(TSC_HandleTypeDef *htsc, uint32_t gx_index) +{ + /* Check the parameters */ + assert_param(IS_TSC_ALL_INSTANCE(htsc->Instance)); + assert_param(IS_TSC_GROUP_INDEX(gx_index)); + + /* Return the group acquisition counter */ + return htsc->Instance->IOGXCR[gx_index]; +} + +/** + * @} + */ + +/** @defgroup TSC_Exported_Functions_Group3 Peripheral Control functions + * @brief Peripheral Control functions + * +@verbatim + =============================================================================== + ##### Peripheral Control functions ##### + =============================================================================== + [..] This section provides functions allowing to: + (+) Configure TSC IOs + (+) Discharge TSC IOs +@endverbatim + * @{ + */ + +/** + * @brief Configure TSC IOs. + * @param htsc Pointer to a TSC_HandleTypeDef structure that contains + * the configuration information for the specified TSC. + * @param config Pointer to the configuration structure. + * @retval HAL status + */ +HAL_StatusTypeDef HAL_TSC_IOConfig(TSC_HandleTypeDef *htsc, TSC_IOConfigTypeDef *config) +{ + /* Check the parameters */ + assert_param(IS_TSC_ALL_INSTANCE(htsc->Instance)); + assert_param(IS_TSC_GROUP(config->ChannelIOs)); + assert_param(IS_TSC_GROUP(config->ShieldIOs)); + assert_param(IS_TSC_GROUP(config->SamplingIOs)); + + /* Process locked */ + __HAL_LOCK(htsc); + + /* Stop acquisition */ + __HAL_TSC_STOP_ACQ(htsc); + + /* Disable Schmitt trigger hysteresis on all used TSC IOs */ + htsc->Instance->IOHCR = (~(config->ChannelIOs | config->ShieldIOs | config->SamplingIOs)); + + /* Set channel and shield IOs */ + htsc->Instance->IOCCR = (config->ChannelIOs | config->ShieldIOs); + + /* Set sampling IOs */ + htsc->Instance->IOSCR = config->SamplingIOs; + + /* Set groups to be acquired */ + htsc->Instance->IOGCSR = TSC_extract_groups(config->ChannelIOs); + + /* Process unlocked */ + __HAL_UNLOCK(htsc); + + /* Return function status */ + return HAL_OK; +} + +/** + * @brief Discharge TSC IOs. + * @param htsc Pointer to a TSC_HandleTypeDef structure that contains + * the configuration information for the specified TSC. + * @param choice This parameter can be set to ENABLE or DISABLE. + * @retval HAL status + */ +HAL_StatusTypeDef HAL_TSC_IODischarge(TSC_HandleTypeDef *htsc, FunctionalState choice) +{ + /* Check the parameters */ + assert_param(IS_TSC_ALL_INSTANCE(htsc->Instance)); + + /* Process locked */ + __HAL_LOCK(htsc); + + if (choice == ENABLE) + { + __HAL_TSC_SET_IODEF_OUTPPLOW(htsc); + } + else + { + __HAL_TSC_SET_IODEF_INFLOAT(htsc); + } + + /* Process unlocked */ + __HAL_UNLOCK(htsc); + + /* Return the group acquisition counter */ + return HAL_OK; +} + +/** + * @} + */ + +/** @defgroup TSC_Exported_Functions_Group4 Peripheral State and Errors functions + * @brief Peripheral State and Errors functions + * +@verbatim + =============================================================================== + ##### State and Errors functions ##### + =============================================================================== + [..] + This subsection provides functions allowing to + (+) Get TSC state. + +@endverbatim + * @{ + */ + +/** + * @brief Return the TSC handle state. + * @param htsc Pointer to a TSC_HandleTypeDef structure that contains + * the configuration information for the specified TSC. + * @retval HAL state + */ +HAL_TSC_StateTypeDef HAL_TSC_GetState(TSC_HandleTypeDef *htsc) +{ + /* Check the parameters */ + assert_param(IS_TSC_ALL_INSTANCE(htsc->Instance)); + + if (htsc->State == HAL_TSC_STATE_BUSY) + { + /* Check end of acquisition flag */ + if (__HAL_TSC_GET_FLAG(htsc, TSC_FLAG_EOA) != RESET) + { + /* Check max count error flag */ + if (__HAL_TSC_GET_FLAG(htsc, TSC_FLAG_MCE) != RESET) + { + /* Change TSC state */ + htsc->State = HAL_TSC_STATE_ERROR; + } + else + { + /* Change TSC state */ + htsc->State = HAL_TSC_STATE_READY; + } + } + } + + /* Return TSC state */ + return htsc->State; +} + +/** + * @} + */ + +/** @defgroup TSC_IRQ_Handler_and_Callbacks IRQ Handler and Callbacks + * @{ + */ + +/** + * @brief Handle TSC interrupt request. + * @param htsc Pointer to a TSC_HandleTypeDef structure that contains + * the configuration information for the specified TSC. + * @retval None + */ +void HAL_TSC_IRQHandler(TSC_HandleTypeDef *htsc) +{ + /* Check the parameters */ + assert_param(IS_TSC_ALL_INSTANCE(htsc->Instance)); + + /* Check if the end of acquisition occurred */ + if (__HAL_TSC_GET_FLAG(htsc, TSC_FLAG_EOA) != RESET) + { + /* Clear EOA flag */ + __HAL_TSC_CLEAR_FLAG(htsc, TSC_FLAG_EOA); + } + + /* Check if max count error occurred */ + if (__HAL_TSC_GET_FLAG(htsc, TSC_FLAG_MCE) != RESET) + { + /* Clear MCE flag */ + __HAL_TSC_CLEAR_FLAG(htsc, TSC_FLAG_MCE); + /* Change TSC state */ + htsc->State = HAL_TSC_STATE_ERROR; +#if (USE_HAL_TSC_REGISTER_CALLBACKS == 1) + htsc->ErrorCallback(htsc); +#else + /* Conversion completed callback */ + HAL_TSC_ErrorCallback(htsc); +#endif /* USE_HAL_TSC_REGISTER_CALLBACKS */ + } + else + { + /* Change TSC state */ + htsc->State = HAL_TSC_STATE_READY; +#if (USE_HAL_TSC_REGISTER_CALLBACKS == 1) + htsc->ConvCpltCallback(htsc); +#else + /* Conversion completed callback */ + HAL_TSC_ConvCpltCallback(htsc); +#endif /* USE_HAL_TSC_REGISTER_CALLBACKS */ + } +} + +/** + * @brief Acquisition completed callback in non-blocking mode. + * @param htsc Pointer to a TSC_HandleTypeDef structure that contains + * the configuration information for the specified TSC. + * @retval None + */ +__weak void HAL_TSC_ConvCpltCallback(TSC_HandleTypeDef *htsc) +{ + /* Prevent unused argument(s) compilation warning */ + UNUSED(htsc); + + /* NOTE : This function should not be modified, when the callback is needed, + the HAL_TSC_ConvCpltCallback could be implemented in the user file. + */ +} + +/** + * @brief Error callback in non-blocking mode. + * @param htsc Pointer to a TSC_HandleTypeDef structure that contains + * the configuration information for the specified TSC. + * @retval None + */ +__weak void HAL_TSC_ErrorCallback(TSC_HandleTypeDef *htsc) +{ + /* Prevent unused argument(s) compilation warning */ + UNUSED(htsc); + + /* NOTE : This function should not be modified, when the callback is needed, + the HAL_TSC_ErrorCallback could be implemented in the user file. + */ +} + +/** + * @} + */ + +/** + * @} + */ + +/* Private functions ---------------------------------------------------------*/ +/** @defgroup TSC_Private_Functions TSC Private Functions + * @{ + */ + +/** + * @brief Utility function used to set the acquired groups mask. + * @param iomask Channels IOs mask + * @retval Acquired groups mask + */ +static uint32_t TSC_extract_groups(uint32_t iomask) +{ + uint32_t groups = 0UL; + uint32_t idx; + + for (idx = 0UL; idx < (uint32_t)TSC_NB_OF_GROUPS; idx++) + { + if ((iomask & (0x0FUL << (idx * 4UL))) != 0UL ) + { + groups |= (1UL << idx); + } + } + + return groups; +} + +/** + * @} + */ + +#endif /* HAL_TSC_MODULE_ENABLED */ + +/** + * @} + */ + +/** + * @} + */ + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/stm32/COLDCARD_Q1/ulight.c b/stm32/COLDCARD_Q1/ulight.c new file mode 100644 index 00000000..7c8195d3 --- /dev/null +++ b/stm32/COLDCARD_Q1/ulight.c @@ -0,0 +1,58 @@ +// +// (c) Copyright 2021 by Coinkite Inc. This file is covered by license found in COPYING-CC. +// +// ulight.c - handle a flashing USB activity light on Mk4 rev B and later. +// +#include "py/mphal.h" +#include "softtimer.h" +#include "ulight.h" + +// set this whenever something happens over USB; makes the light flash +bool ckcc_usb_active; + +// period for blinking (ms) +#define BLINK_RATE 150 + +static soft_timer_entry_t led_blinktimer; + +STATIC mp_obj_t led_blinker(mp_obj_t unused_obj) +{ + // Called at 150 ms rate. + + // if something happened in previous time period, keep flashing. + bool active = false; + if(ckcc_usb_active) { + ckcc_usb_active = false; + active = true; + } + + static bool led_on; + if(!active) { + if(led_on) { + mp_hal_pin_low(USB_LED_PIN); + led_on = false; + } + } else { + led_on = !led_on; + mp_hal_pin_write(USB_LED_PIN, led_on); + } + + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(led_blinker_obj, led_blinker); + +// ulight_setup() +// + void +ulight_setup(void) +{ + // setup USB activity light + led_blinktimer.mode = SOFT_TIMER_MODE_PERIODIC; + led_blinktimer.delta_ms = BLINK_RATE; + led_blinktimer.callback = (void *)&led_blinker_obj; + led_blinktimer.pairheap.base.type = MP_ROM_NONE; // not needed + + soft_timer_insert(&led_blinktimer); +} + +// EOF diff --git a/stm32/COLDCARD_Q1/ulight.h b/stm32/COLDCARD_Q1/ulight.h new file mode 100644 index 00000000..b847345e --- /dev/null +++ b/stm32/COLDCARD_Q1/ulight.h @@ -0,0 +1,19 @@ +/* + * (c) Copyright 2021 by Coinkite Inc. This file is covered by license found in COPYING-CC. + */ +#pragma once + +// active-high LED on this pin +#define USB_LED_PIN pin_C6 + +// set this whenever something happens over USB; makes the light flash +extern bool ckcc_usb_active; + +// call once +void ulight_setup(void); + +static inline void ulight_off(void) { + mp_hal_pin_low(USB_LED_PIN); +} + +// EOF diff --git a/stm32/COLDCARD_Q1/vcp_lockdown.c b/stm32/COLDCARD_Q1/vcp_lockdown.c new file mode 100644 index 00000000..75e825ad --- /dev/null +++ b/stm32/COLDCARD_Q1/vcp_lockdown.c @@ -0,0 +1,86 @@ +/* + * (c) Copyright 2018 by Coinkite Inc. This file is covered by license found in COPYING-CC. + */ +#include + +#include "py/runtime.h" +#include "py/mperrno.h" +#include "py/mphal.h" +#include "extmod/misc.h" +#include "usb.h" +#include "uart.h" +#include "lib/utils/interrupt_char.h" // for mp_interrupt_char + +// is the REPL enabled by user (default: no) +bool ckcc_vcp_enabled; + +// mp_hal_stdin_rx_chr() +// +// - replaces code in stm32/mphalport.c +// - ignore all keys unless in REPL mode +// + int +mp_hal_stdin_rx_chr(void) { + for (;;) { + if (MP_STATE_PORT(pyb_stdio_uart) != NULL && uart_rx_any(MP_STATE_PORT(pyb_stdio_uart))) { + // consume it, but forward only if enabled. + int ch = uart_rx_char(MP_STATE_PORT(pyb_stdio_uart)); + +#if COLDCARD_DEBUG + return ch; +#else + if(ckcc_vcp_enabled) return ch; +#endif + } + + // USB virtual comm port Rx support -- always disabled on Mk4 by design +#if 0 + int dupterm_c = mp_uos_dupterm_rx_chr(); + if (dupterm_c >= 0) { + if(ckcc_vcp_enabled) { + return dupterm_c; + } + } +#endif + + MICROPY_EVENT_POLL_HOOK + } +} + +// mp_hal_set_interrupt_char() +// + void +mp_hal_set_interrupt_char(int c) +{ + // Replaces content of l-mpy/lib/utils/interrupt_char.c + // - many things call this at many times. + // - instead, use our ckcc.vcp_enable() call +#if COLDCARD_DEBUG + mp_interrupt_char = 3; +#else + mp_interrupt_char = -1; +#endif +} + +// mp_hal_stdout_tx_strn() +// + void +mp_hal_stdout_tx_strn(const char *str, size_t len) +{ +#if !COLDCARD_DEBUG + if(!ckcc_vcp_enabled) { + // allow the copyright notice and version string, then no more output + static int so_far = 0; + + if(so_far > 84) return; + so_far += len; + } +#endif + + if (MP_STATE_PORT(pyb_stdio_uart) != NULL) { + uart_tx_strn(MP_STATE_PORT(pyb_stdio_uart), str, len); + } + mp_uos_dupterm_tx_strn(str, len); +} + +// EOF