From c33f92bd72c5bca152012edf6d27ff1c064f10b6 Mon Sep 17 00:00:00 2001 From: tychovrahe Date: Tue, 31 May 2022 09:31:32 +0200 Subject: [PATCH] feat(core): add RGB LED driver for Model R --- core/.changelog.d/2300.added | 1 + core/SConscript.boardloader | 1 + core/SConscript.bootloader | 3 + core/SConscript.firmware | 8 +- core/embed/bootloader/.changelog.d/2300.added | 1 + core/embed/bootloader/main.c | 2 + core/embed/bootloader/memory.ld | 5 + core/embed/bootloader/messages.c | 18 +- core/embed/firmware/main.c | 9 +- core/embed/rust/build.rs | 2 + core/embed/rust/src/trezorhal/mod.rs | 2 + core/embed/rust/src/trezorhal/rgb_led.rs | 7 + core/embed/rust/trezorhal.h | 1 + core/embed/trezorhal/rgb_led.c | 168 ++++++++++++++++++ core/embed/trezorhal/rgb_led.h | 4 + core/embed/unix/rgb_led.h | 4 + 16 files changed, 226 insertions(+), 10 deletions(-) create mode 100644 core/.changelog.d/2300.added create mode 100644 core/embed/bootloader/.changelog.d/2300.added create mode 100644 core/embed/rust/src/trezorhal/rgb_led.rs create mode 100644 core/embed/trezorhal/rgb_led.c create mode 100644 core/embed/trezorhal/rgb_led.h create mode 100644 core/embed/unix/rgb_led.h diff --git a/core/.changelog.d/2300.added b/core/.changelog.d/2300.added new file mode 100644 index 000000000..63c33ae2b --- /dev/null +++ b/core/.changelog.d/2300.added @@ -0,0 +1 @@ +Add RGB LED for Model R diff --git a/core/SConscript.boardloader b/core/SConscript.boardloader index 2b16ea2c5..ff8b5d2bc 100644 --- a/core/SConscript.boardloader +++ b/core/SConscript.boardloader @@ -139,6 +139,7 @@ env.Replace( 'vendor/micropython/lib/cmsis/inc', ] + CPPPATH_MOD, CPPDEFINES=[ + 'BOARDLOADER', 'TREZOR_MODEL_'+TREZOR_MODEL, CPU_MODEL, 'USE_HAL_DRIVER', diff --git a/core/SConscript.bootloader b/core/SConscript.bootloader index 86f8936f2..edf4da926 100644 --- a/core/SConscript.bootloader +++ b/core/SConscript.bootloader @@ -71,6 +71,7 @@ SOURCE_MOD += [ SOURCE_STMHAL = [ 'vendor/micropython/lib/stm32lib/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal.c', 'vendor/micropython/lib/stm32lib/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_cortex.c', + 'vendor/micropython/lib/stm32lib/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_dma.c', 'vendor/micropython/lib/stm32lib/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_flash.c', 'vendor/micropython/lib/stm32lib/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_flash_ex.c', 'vendor/micropython/lib/stm32lib/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_gpio.c', @@ -125,6 +126,7 @@ SOURCE_TREZORHAL = [ if TREZOR_MODEL in ('R'): SOURCE_TREZORHAL.append('embed/trezorhal/button.c') + SOURCE_TREZORHAL.append('embed/trezorhal/rgb_led.c') if TREZOR_MODEL in ('T',): SOURCE_TREZORHAL.append('embed/trezorhal/touch.c') @@ -174,6 +176,7 @@ env.Replace( 'vendor/nanopb', ] + CPPPATH_MOD, CPPDEFINES=[ + 'BOOTLOADER', 'TREZOR_MODEL_'+TREZOR_MODEL, CPU_MODEL, 'USE_HAL_DRIVER', diff --git a/core/SConscript.firmware b/core/SConscript.firmware index b0bbcbb31..d685afd26 100644 --- a/core/SConscript.firmware +++ b/core/SConscript.firmware @@ -384,7 +384,12 @@ if TREZOR_MODEL in ('T',): 'embed/trezorhal/sdcard.c', 'embed/trezorhal/touch.c', ] -elif TREZOR_MODEL in ('1', 'R'): +elif TREZOR_MODEL in ('R',): + SOURCE_TREZORHAL += [ + 'embed/trezorhal/button.c', + 'embed/trezorhal/rgb_led.c', + ] +elif TREZOR_MODEL in ('1',): SOURCE_TREZORHAL += [ 'embed/trezorhal/button.c', ] @@ -470,6 +475,7 @@ env.Replace( 'vendor/micropython/lib/cmsis/inc', ] + CPPPATH_MOD, CPPDEFINES=[ + 'FIRMWARE', 'TREZOR_MODEL_'+TREZOR_MODEL, CPU_MODEL, 'USE_HAL_DRIVER', diff --git a/core/embed/bootloader/.changelog.d/2300.added b/core/embed/bootloader/.changelog.d/2300.added new file mode 100644 index 000000000..63c33ae2b --- /dev/null +++ b/core/embed/bootloader/.changelog.d/2300.added @@ -0,0 +1 @@ +Add RGB LED for Model R diff --git a/core/embed/bootloader/main.c b/core/embed/bootloader/main.c index 1f9f7500b..3d1c5a61d 100644 --- a/core/embed/bootloader/main.c +++ b/core/embed/bootloader/main.c @@ -34,6 +34,7 @@ #endif #if defined TREZOR_MODEL_R #include "button.h" +#include "rgb_led.h" #endif #include "usb.h" #include "version.h" @@ -255,6 +256,7 @@ int main(void) { #if defined TREZOR_MODEL_R button_init(); + rgb_led_init(); #endif mpu_config_bootloader(); diff --git a/core/embed/bootloader/memory.ld b/core/embed/bootloader/memory.ld index 2015d0f9b..bc3dc77c8 100644 --- a/core/embed/bootloader/memory.ld +++ b/core/embed/bootloader/memory.ld @@ -49,6 +49,11 @@ SECTIONS { . = ALIGN(4); } >CCMRAM + .buf : ALIGN(4) { + *(.buf*); + . = ALIGN(4); + } >SRAM + .stack : ALIGN(8) { . = 4K; /* this acts as a build time assertion that at least this much memory is available for stack use */ } >CCMRAM diff --git a/core/embed/bootloader/messages.c b/core/embed/bootloader/messages.c index 2188814cc..14474f1c1 100644 --- a/core/embed/bootloader/messages.c +++ b/core/embed/bootloader/messages.c @@ -358,8 +358,10 @@ void process_msg_FirmwareErase(uint8_t iface_num, uint32_t msg_size, } static uint32_t chunk_size = 0; -// SRAM is unused, so we can use it for chunk buffer -uint8_t *const chunk_buffer = (uint8_t *const)0x20000000; + +__attribute__((section(".buf"))) uint32_t chunk_buffer[IMAGE_CHUNK_SIZE / 4]; + +#define CHUNK_BUFFER_PTR ((const uint8_t *const)&chunk_buffer) /* we don't use secbool/sectrue/secfalse here as it is a nanopb api */ static bool _read_payload(pb_istream_t *stream, const pb_field_t *field, @@ -375,7 +377,7 @@ static bool _read_payload(pb_istream_t *stream, const pb_field_t *field, if (offset == 0) { // clear chunk buffer - memset(chunk_buffer, 0xFF, IMAGE_CHUNK_SIZE); + memset((uint8_t *)&chunk_buffer, 0xFF, IMAGE_CHUNK_SIZE); } uint32_t chunk_written = offset; @@ -390,7 +392,7 @@ static bool _read_payload(pb_istream_t *stream, const pb_field_t *field, } // read data if (!pb_read( - stream, (pb_byte_t *)(chunk_buffer + chunk_written), + stream, (pb_byte_t *)(CHUNK_BUFFER_PTR + chunk_written), (stream->bytes_left > BUFSIZE) ? BUFSIZE : stream->bytes_left)) { chunk_size = 0; return false; @@ -481,14 +483,14 @@ int process_msg_FirmwareUpload(uint8_t iface_num, uint32_t msg_size, if (headers_offset == 0) { // first block and headers are not yet parsed vendor_header vhdr; - if (sectrue != load_vendor_header_keys(chunk_buffer, &vhdr)) { + if (sectrue != load_vendor_header_keys(CHUNK_BUFFER_PTR, &vhdr)) { MSG_SEND_INIT(Failure); MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_ProcessError); MSG_SEND_ASSIGN_STRING(message, "Invalid vendor header"); MSG_SEND(Failure); return -2; } - if (sectrue != load_image_header(chunk_buffer + vhdr.hdrlen, + if (sectrue != load_image_header(CHUNK_BUFFER_PTR + vhdr.hdrlen, FIRMWARE_IMAGE_MAGIC, FIRMWARE_IMAGE_MAXSIZE, vhdr.vsig_m, vhdr.vsig_n, vhdr.vpub, &hdr)) { @@ -574,7 +576,7 @@ int process_msg_FirmwareUpload(uint8_t iface_num, uint32_t msg_size, } if (sectrue != check_single_hash(hdr.hashes + firmware_block * 32, - chunk_buffer + headers_offset, + CHUNK_BUFFER_PTR + headers_offset, chunk_size - headers_offset)) { if (firmware_upload_chunk_retry > 0) { --firmware_upload_chunk_retry; @@ -594,7 +596,7 @@ int process_msg_FirmwareUpload(uint8_t iface_num, uint32_t msg_size, ensure(flash_unlock_write(), NULL); - const uint32_t *const src = (const uint32_t *const)chunk_buffer; + const uint32_t *const src = (const uint32_t *const)CHUNK_BUFFER_PTR; for (int i = 0; i < chunk_size / sizeof(uint32_t); i++) { ensure(flash_write_word(FIRMWARE_SECTORS[firmware_block], i * sizeof(uint32_t), src[i]), diff --git a/core/embed/firmware/main.c b/core/embed/firmware/main.c index c1ee7c2c4..210e90fb1 100644 --- a/core/embed/firmware/main.c +++ b/core/embed/firmware/main.c @@ -37,7 +37,6 @@ #include "bl_check.h" #include "board_capabilities.h" -#include "button.h" #include "common.h" #include "compiler_traits.h" #include "display.h" @@ -45,6 +44,13 @@ #include "image.h" #include "mpu.h" #include "random_delays.h" +#ifdef TREZOR_MODEL_R +#include "rgb_led.h" +#endif +#if defined TREZOR_MODEL_R || defined TREZOR_MODEL_1 +#include "button.h" +#endif + #ifdef SYSTEM_VIEW #include "systemview.h" #endif @@ -100,6 +106,7 @@ int main(void) { #if defined TREZOR_MODEL_R button_init(); display_clear(); + rgb_led_init(); #endif #if defined TREZOR_MODEL_T diff --git a/core/embed/rust/build.rs b/core/embed/rust/build.rs index 7d5d59eef..e18b672c8 100644 --- a/core/embed/rust/build.rs +++ b/core/embed/rust/build.rs @@ -274,6 +274,8 @@ fn generate_trezorhal_bindings() { .allowlist_function("button_sequence_to_word") // random .allowlist_function("random_uniform") + // rgb led + .allowlist_function("rgb_led_set_color") // time .allowlist_function("hal_delay") .allowlist_function("hal_ticks_ms"); diff --git a/core/embed/rust/src/trezorhal/mod.rs b/core/embed/rust/src/trezorhal/mod.rs index b5571e8c4..d1430cb1c 100644 --- a/core/embed/rust/src/trezorhal/mod.rs +++ b/core/embed/rust/src/trezorhal/mod.rs @@ -4,6 +4,8 @@ pub mod common; pub mod display; mod ffi; pub mod random; +#[cfg(feature = "model_tr")] +pub mod rgb_led; pub mod slip39; #[cfg(not(feature = "micropython"))] diff --git a/core/embed/rust/src/trezorhal/rgb_led.rs b/core/embed/rust/src/trezorhal/rgb_led.rs new file mode 100644 index 000000000..f1a46b9f5 --- /dev/null +++ b/core/embed/rust/src/trezorhal/rgb_led.rs @@ -0,0 +1,7 @@ +use super::ffi; + +pub fn set_color(color: u32) { + unsafe { + ffi::rgb_led_set_color(color); + } +} diff --git a/core/embed/rust/trezorhal.h b/core/embed/rust/trezorhal.h index a9983404f..09dc838f0 100644 --- a/core/embed/rust/trezorhal.h +++ b/core/embed/rust/trezorhal.h @@ -1,5 +1,6 @@ #include "common.h" #include "display.h" +#include "rgb_led.h" #include "secbool.h" #include "storage.h" diff --git a/core/embed/trezorhal/rgb_led.c b/core/embed/trezorhal/rgb_led.c new file mode 100644 index 000000000..a0c692e34 --- /dev/null +++ b/core/embed/trezorhal/rgb_led.c @@ -0,0 +1,168 @@ +/* + * This file is part of the Trezor project, https://trezor.io/ + * + * Copyright (c) SatoshiLabs + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * Implements driver for IN-PI15TAT5R5G5B 1515 RGB LED 4-Pin with Integrated IC + * + * The communication protocol prescribes encoding of 0 as a short pulse + * (200-400ns) and 1 as a long pulse (580ns-1us). Before sending the data reset + * period is required: at least 80us without pulses. + * + * The data send to LED is 24 bit RGB. These data are encoded as the long and + * short pulses. + * + * After data is sent, the PWM compare level is set to 0 to stop pulsing. + * + * For pulse generation with precise timing PWM implemented via TIM8 is used. + * To avoid any glitches when setting the CCR register, preloading is used, + * therefore sync with TIM4 is needed (to generate COM event on TIM update) + * + * DMA is used to set the CCR register to avoid need for interrupt after every + * bit is sent. + */ + +#include "common.h" + +#include STM32_HAL_H + +#define RESET_DATA_LEN 18 // >80us no pulse before sending data +#define DATA_LEN 25 // 24 RGB bits and a final zero +#define TIMER_PERIOD 832 // cca 200 KHz @ 180MHz +#define BIT_0_LEN 52 // 312ns +#define BIT_1_LEN 125 // 750ns + +#if defined BOARDLOADER +#error Not implemented for boardloader! +#endif + +#if defined BOOTLOADER +__attribute__((section(".buf"))) +#endif +uint32_t rgb_led_data[RESET_DATA_LEN + DATA_LEN] = {0}; + +static void rgb_led_set(uint32_t* start, uint8_t color) { + for (int i = 0; i < 8; i++) { + uint32_t bit_mask = (1 << (7 - i)); + if (color & bit_mask) { + start[i] = BIT_1_LEN; + } else { + start[i] = BIT_0_LEN; + } + } +} + +void rgb_led_set_color(uint32_t color) { + uint16_t red = (0xFF0000 & color) >> 16; + uint16_t green = (0xFF00 & color) >> 8; + uint16_t blue = (0xFF & color); + + // wait for previous command to finish + while (DMA2_Stream1->CR & DMA_SxCR_EN) + ; + + rgb_led_set(&rgb_led_data[RESET_DATA_LEN + 0], green); + rgb_led_set(&rgb_led_data[RESET_DATA_LEN + 8], red); + rgb_led_set(&rgb_led_data[RESET_DATA_LEN + 16], blue); + rgb_led_data[RESET_DATA_LEN + DATA_LEN - 1] = 0; + + DMA2->LIFCR |= 0xFC0; + DMA2_Stream1->M0AR = (uint32_t)rgb_led_data; + DMA2_Stream1->PAR = (uint32_t)&TIM8->CCR1; + DMA2_Stream1->NDTR = RESET_DATA_LEN + DATA_LEN; + DMA2_Stream1->CR |= DMA_SxCR_EN; +} + +void rgb_led_init(void) { + __HAL_RCC_GPIOC_CLK_ENABLE(); + GPIO_InitTypeDef GPIO_InitStructure; + GPIO_InitStructure.Mode = GPIO_MODE_AF_PP; + GPIO_InitStructure.Pull = GPIO_PULLUP; + GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_HIGH; + GPIO_InitStructure.Alternate = GPIO_AF3_TIM8; + GPIO_InitStructure.Pin = GPIO_PIN_6; + HAL_GPIO_Init(GPIOC, &GPIO_InitStructure); + + __HAL_RCC_TIM4_CLK_ENABLE(); + TIM_HandleTypeDef TIM4_Handle; + TIM4_Handle.State = HAL_TIM_STATE_RESET; + TIM4_Handle.Instance = TIM4; + TIM4_Handle.Init.Period = TIMER_PERIOD; + TIM4_Handle.Init.Prescaler = 0; + TIM4_Handle.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; + TIM4_Handle.Init.CounterMode = TIM_COUNTERMODE_UP; + TIM4_Handle.Init.RepetitionCounter = 0; + HAL_TIM_PWM_Init(&TIM4_Handle); + + __HAL_RCC_TIM8_CLK_ENABLE(); + TIM_HandleTypeDef TIM8_Handle; + TIM8_Handle.State = HAL_TIM_STATE_RESET; + TIM8_Handle.Instance = TIM8; + TIM8_Handle.Init.Period = TIMER_PERIOD; + TIM8_Handle.Init.Prescaler = 0; + TIM8_Handle.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; + TIM8_Handle.Init.CounterMode = TIM_COUNTERMODE_UP; + TIM8_Handle.Init.RepetitionCounter = 0; + HAL_TIM_PWM_Init(&TIM8_Handle); + + TIM_OC_InitTypeDef TIM_OC_InitStructure; + TIM_OC_InitStructure.Pulse = 0; + TIM_OC_InitStructure.OCMode = TIM_OCMODE_PWM1; + TIM_OC_InitStructure.OCPolarity = TIM_OCPOLARITY_LOW; + TIM_OC_InitStructure.OCFastMode = TIM_OCFAST_DISABLE; + TIM_OC_InitStructure.OCNPolarity = TIM_OCNPOLARITY_HIGH; + TIM_OC_InitStructure.OCIdleState = TIM_OCIDLESTATE_SET; + TIM_OC_InitStructure.OCNIdleState = TIM_OCNIDLESTATE_SET; + HAL_TIM_PWM_ConfigChannel(&TIM8_Handle, &TIM_OC_InitStructure, TIM_CHANNEL_1); + + __HAL_RCC_DMA2_CLK_ENABLE(); + DMA_HandleTypeDef DMA_InitStructure = {0}; + DMA_InitStructure.Instance = DMA2_Stream1; + DMA_InitStructure.State = HAL_DMA_STATE_RESET; + DMA_InitStructure.Init.Channel = DMA_CHANNEL_7; + DMA_InitStructure.Init.Direction = DMA_MEMORY_TO_PERIPH; + DMA_InitStructure.Init.FIFOMode = DMA_FIFOMODE_DISABLE; + DMA_InitStructure.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_1QUARTERFULL; + DMA_InitStructure.Init.MemBurst = DMA_MBURST_SINGLE; + DMA_InitStructure.Init.MemDataAlignment = DMA_MDATAALIGN_WORD; + DMA_InitStructure.Init.MemInc = DMA_MINC_ENABLE; + DMA_InitStructure.Init.Mode = DMA_NORMAL; + DMA_InitStructure.Init.PeriphBurst = DMA_PBURST_SINGLE; + DMA_InitStructure.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD; + DMA_InitStructure.Init.PeriphInc = DMA_PINC_DISABLE; + DMA_InitStructure.Init.Priority = DMA_PRIORITY_HIGH; + HAL_DMA_Init(&DMA_InitStructure); + + TIM4->CR2 |= TIM_CR2_MMS_1; // update event as TRGO + + TIM8->CR2 |= TIM_CR2_CCPC; // preloading CCR register + TIM8->CR2 |= TIM_CR2_CCUS; // preload when TRGI + TIM8->SMCR |= TIM_SMCR_SMS_2; // reset mode - sync timers + TIM8->SMCR |= TIM_SMCR_TS_1; // sync with TIM 4 + + TIM8->DIER |= TIM_DMA_UPDATE; // allow DMA request from update event + TIM8->CCR1 = 0; + + // NVIC configuration for SDIO interrupts + HAL_TIM_Base_Start(&TIM4_Handle); + HAL_TIM_Base_Start(&TIM8_Handle); + HAL_TIM_PWM_Start(&TIM8_Handle, TIM_CHANNEL_1); + + // turns off the LED + rgb_led_set_color(0x000000); +} diff --git a/core/embed/trezorhal/rgb_led.h b/core/embed/trezorhal/rgb_led.h new file mode 100644 index 000000000..a342b9a85 --- /dev/null +++ b/core/embed/trezorhal/rgb_led.h @@ -0,0 +1,4 @@ + +void rgb_led_init(void); + +void rgb_led_set_color(uint32_t color); diff --git a/core/embed/unix/rgb_led.h b/core/embed/unix/rgb_led.h new file mode 100644 index 000000000..a342b9a85 --- /dev/null +++ b/core/embed/unix/rgb_led.h @@ -0,0 +1,4 @@ + +void rgb_led_init(void); + +void rgb_led_set_color(uint32_t color);