diff --git a/CMakeLists.txt b/CMakeLists.txt index f803cecc..67a9af60 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -66,5 +66,4 @@ endif() set(VERSION_EDIT_WARNING "// Do not edit this file, it is automatically generated by CMAKE!") configure_file(${CMAKE_CURRENT_SOURCE_DIR}/src/Version.h.in ${CMAKE_CURRENT_SOURCE_DIR}/src/Version.h) -include("cmake-nRF5x/CMake_nRF5x.cmake") add_subdirectory(src) diff --git a/README.md b/README.md index 4c970f8e..30a92dc8 100644 --- a/README.md +++ b/README.md @@ -37,10 +37,12 @@ I've tested this project on the actual PineTime hardware. * Watchdog (automatic reset in case of firmware crash) and reset support (push and hold the button for 7 - 10s); * BLE Notification support (still Work-In-Progress, [companion app](https://github.com/JF002/gobbledegook) needed); * Supported by companion app [Amazfish](https://openrepos.net/content/piggz/amazfish) (time synchronization and notifications are integrated). + * **[EXPERIMENTAL]** Firmware update (OTA) via BLE. ## Documentation * [BLE implementation and API](./doc/ble.md) + * [Bootloader and DFU](./bootloader/README.md) ## Stub using NRF52-DK ![Pinetime stub](./images/pinetimestub1.jpg "PinetimeStub") @@ -114,6 +116,11 @@ $ make -j pinetime-app $ make FLASH_ERASE ``` +* Flash application + +``` +$ make FLASH_pinetime-app +``` * For your information : list make targets : diff --git a/bootloader/README.md b/bootloader/README.md new file mode 100644 index 00000000..facc0500 --- /dev/null +++ b/bootloader/README.md @@ -0,0 +1,50 @@ +# Bootloader + +## Bootloader binary +The binary comes from https://github.com/lupyuen/pinetime-rust-mynewt/releases/tag/v4.1.7 + +It must be flash at address **0x00** in the internal flash memory. + +Using OpenOCD: + +` +program mynewt_nosemi.elf_4.1.7.bin 0 +` + +## Application firmware image +Build the binary compatible with the booloader: + +` +make pinetime-mcuboot-app +` + +The binary is located in */src/pinetime-mcuboot-app.bin*. + +It must me converted into a MCUBoot image using *imgtool.py* from [MCUBoot](https://github.com/JuulLabs-OSS/mcuboot/tree/master/scripts). + +` +imgtool.py create --align 4 --version 1.0.0 --header-size 32 --slot-size 475136 --pad-header /src/pinetime-mcuboot-app.bin image.bin +` + +The image must be then flashed at address **0x8000** in the internal flash memory. + +Using OpenOCD: + +` +program image.bin 0x8000 +` + +## OTA and DFU +Pack the image into a .zip file for the NRF DFU protocol: + +` +adafruit-nrfutil dfu genpkg --dev-type 0x0052 --application image.bin dfu.zip +` + +Use NRFConnect or dfu.py to upload the zip file to the device: + +` +sudo dfu.py -z /home/jf/nrf52/bootloader/dfu.zip -a --legacy +` + +**TODO** : dfu.py \ No newline at end of file diff --git a/bootloader/mynewt_nosemi.elf_4.1.7.bin b/bootloader/mynewt_nosemi.elf_4.1.7.bin new file mode 100644 index 00000000..bd00ec9c Binary files /dev/null and b/bootloader/mynewt_nosemi.elf_4.1.7.bin differ diff --git a/bootloader/mynewt_nosemi_4.1.7.elf b/bootloader/mynewt_nosemi_4.1.7.elf new file mode 100644 index 00000000..3d7c231f Binary files /dev/null and b/bootloader/mynewt_nosemi_4.1.7.elf differ diff --git a/gcc_nrf52-mcuboot.ld b/gcc_nrf52-mcuboot.ld new file mode 100644 index 00000000..0746f491 --- /dev/null +++ b/gcc_nrf52-mcuboot.ld @@ -0,0 +1,136 @@ +/* Linker script to configure memory regions. */ + +SEARCH_DIR(.) +GROUP(-lgcc -lc -lnosys) + +MEMORY +{ + FLASH (rx) : ORIGIN = 0x08020, LENGTH = 0x78000 + RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 0x10000 +} + +SECTIONS +{ +} + +SECTIONS +{ + . = ALIGN(4); + .mem_section_dummy_ram : + { + } + .cli_sorted_cmd_ptrs : + { + PROVIDE(__start_cli_sorted_cmd_ptrs = .); + KEEP(*(.cli_sorted_cmd_ptrs)) + PROVIDE(__stop_cli_sorted_cmd_ptrs = .); + } > RAM + .fs_data : + { + PROVIDE(__start_fs_data = .); + KEEP(*(.fs_data)) + PROVIDE(__stop_fs_data = .); + } > RAM + .log_dynamic_data : + { + PROVIDE(__start_log_dynamic_data = .); + KEEP(*(SORT(.log_dynamic_data*))) + PROVIDE(__stop_log_dynamic_data = .); + } > RAM + .log_filter_data : + { + PROVIDE(__start_log_filter_data = .); + KEEP(*(SORT(.log_filter_data*))) + PROVIDE(__stop_log_filter_data = .); + } > RAM + +} INSERT AFTER .data; + +SECTIONS +{ + .mem_section_dummy_rom : + { + } + .sdh_soc_observers : + { + PROVIDE(__start_sdh_soc_observers = .); + KEEP(*(SORT(.sdh_soc_observers*))) + PROVIDE(__stop_sdh_soc_observers = .); + } > FLASH + .sdh_ble_observers : + { + PROVIDE(__start_sdh_ble_observers = .); + KEEP(*(SORT(.sdh_ble_observers*))) + PROVIDE(__stop_sdh_ble_observers = .); + } > FLASH + .sdh_req_observers : + { + PROVIDE(__start_sdh_req_observers = .); + KEEP(*(SORT(.sdh_req_observers*))) + PROVIDE(__stop_sdh_req_observers = .); + } > FLASH + .sdh_state_observers : + { + PROVIDE(__start_sdh_state_observers = .); + KEEP(*(SORT(.sdh_state_observers*))) + PROVIDE(__stop_sdh_state_observers = .); + } > FLASH + .sdh_stack_observers : + { + PROVIDE(__start_sdh_stack_observers = .); + KEEP(*(SORT(.sdh_stack_observers*))) + PROVIDE(__stop_sdh_stack_observers = .); + } > FLASH + .nrf_queue : + { + PROVIDE(__start_nrf_queue = .); + KEEP(*(.nrf_queue)) + PROVIDE(__stop_nrf_queue = .); + } > FLASH + .nrf_balloc : + { + PROVIDE(__start_nrf_balloc = .); + KEEP(*(.nrf_balloc)) + PROVIDE(__stop_nrf_balloc = .); + } > FLASH + .cli_command : + { + PROVIDE(__start_cli_command = .); + KEEP(*(.cli_command)) + PROVIDE(__stop_cli_command = .); + } > FLASH + .crypto_data : + { + PROVIDE(__start_crypto_data = .); + KEEP(*(SORT(.crypto_data*))) + PROVIDE(__stop_crypto_data = .); + } > FLASH + .pwr_mgmt_data : + { + PROVIDE(__start_pwr_mgmt_data = .); + KEEP(*(SORT(.pwr_mgmt_data*))) + PROVIDE(__stop_pwr_mgmt_data = .); + } > FLASH + .log_const_data : + { + PROVIDE(__start_log_const_data = .); + KEEP(*(SORT(.log_const_data*))) + PROVIDE(__stop_log_const_data = .); + } > FLASH + .log_backends : + { + PROVIDE(__start_log_backends = .); + KEEP(*(SORT(.log_backends*))) + PROVIDE(__stop_log_backends = .); + } > FLASH + .nrf_balloc : + { + PROVIDE(__start_nrf_balloc = .); + KEEP(*(.nrf_balloc)) + PROVIDE(__stop_nrf_balloc = .); + } > FLASH + +} INSERT AFTER .text + + +INCLUDE "./nrf_common.ld" diff --git a/gcc_nrf52.ld b/gcc_nrf52.ld index b849ea2c..98e133aa 100644 --- a/gcc_nrf52.ld +++ b/gcc_nrf52.ld @@ -5,7 +5,7 @@ GROUP(-lgcc -lc -lnosys) MEMORY { - FLASH (rx) : ORIGIN = 0x00000, LENGTH = 0x80000 + FLASH (rx) : ORIGIN = 0x00000, LENGTH = 0x78000 RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 0x10000 } diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 4b1f7c16..023c4ee6 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -5,29 +5,86 @@ project(pinetime-app C CXX ASM) # define some variables just for this example to determine file locations set(NRF_PROJECT_NAME pinetime-app) set(NRF_BOARD pca10040) -#set(NRF_SOFTDEVICE s132) -nRF5x_toolchainSetup() -nRF5x_setup() +# check if all the necessary tools paths have been provided. +if (NOT NRF5_SDK_PATH) + message(FATAL_ERROR "The path to the nRF5 SDK (NRF5_SDK_PATH) must be set.") +endif () +if(DEFINED ARM_NONE_EABI_TOOLCHAIN_PATH) + set(ARM_NONE_EABI_TOOLCHAIN_BIN_PATH ${ARM_NONE_EABI_TOOLCHAIN_PATH}/bin) +endif() -#nRF5x_addAppScheduler() -#nRF5x_addAppFIFO() -#nRF5x_addAppTimer() -#nRF5x_addAppUART() -nRF5x_addAppButton() -nRF5x_addBSP(FALSE FALSE FALSE) -nRF5x_addAppGpiote() -#nRF5x_addBLEGATT() -# -#nRF5x_addBLEService(ble_lbs) +if (NOT NRF_TARGET MATCHES "nrf52") + message(FATAL_ERROR "Only rRF52 boards are supported right now") +endif() -add_definitions(-DCONFIG_GPIO_AS_PINRESET) -add_definitions(-DDEBUG) -add_definitions(-DNIMBLE_CFG_CONTROLLER) -add_definitions(-DOS_CPUTIME_FREQ) +# Setup toolchain +include(${CMAKE_SOURCE_DIR}/cmake-nRF5x/arm-gcc-toolchain.cmake) -include_directories(.) -include_directories(libs/) +if(NOT DEFINED ARM_GCC_TOOLCHAIN) + message(FATAL_ERROR "The toolchain must be set up before calling this macro") +endif() +set(CMAKE_OSX_SYSROOT "/") +set(CMAKE_OSX_DEPLOYMENT_TARGET "") + + +set(SDK_SOURCE_FILES + # Startup + "${NRF5_SDK_PATH}/modules/nrfx/mdk/system_nrf52.c" + "${NRF5_SDK_PATH}/modules/nrfx/mdk/gcc_startup_nrf52.S" + + # Base SDK + "${NRF5_SDK_PATH}/components/boards/boards.c" + "${NRF5_SDK_PATH}/integration/nrfx/legacy/nrf_drv_clock.c" + "${NRF5_SDK_PATH}/modules/nrfx/drivers/src/nrfx_clock.c" + "${NRF5_SDK_PATH}/modules/nrfx/drivers/src/nrfx_gpiote.c" + "${NRF5_SDK_PATH}/modules/nrfx/soc/nrfx_atomic.c" + "${NRF5_SDK_PATH}/modules/nrfx/drivers/src/nrfx_saadc.c" + + # FreeRTOS + ${NRF5_SDK_PATH}/external/freertos/source/croutine.c + ${NRF5_SDK_PATH}/external/freertos/source/event_groups.c + ${NRF5_SDK_PATH}/external/freertos/source/portable/MemMang/heap_1.c + ${NRF5_SDK_PATH}/external/freertos/source/list.c + ${NRF5_SDK_PATH}/external/freertos/source/queue.c + ${NRF5_SDK_PATH}/external/freertos/source/stream_buffer.c + ${NRF5_SDK_PATH}/external/freertos/source/tasks.c + ${NRF5_SDK_PATH}/external/freertos/source/timers.c + ${NRF5_SDK_PATH}/components/libraries/timer/app_timer_freertos.c + + # Libs + "${NRF5_SDK_PATH}/components/libraries/atomic/nrf_atomic.c" + "${NRF5_SDK_PATH}/components/libraries/balloc/nrf_balloc.c" + "${NRF5_SDK_PATH}/components/libraries/util/nrf_assert.c" + "${NRF5_SDK_PATH}/components/libraries/util/app_error.c" + "${NRF5_SDK_PATH}/components/libraries/util/app_error_weak.c" + "${NRF5_SDK_PATH}/components/libraries/util/app_error_handler_gcc.c" + "${NRF5_SDK_PATH}/components/libraries/util/app_util_platform.c" + "${NRF5_SDK_PATH}/components/libraries/log/src/nrf_log_backend_rtt.c" + "${NRF5_SDK_PATH}/components/libraries/log/src/nrf_log_backend_serial.c" + "${NRF5_SDK_PATH}/components/libraries/log/src/nrf_log_default_backends.c" + "${NRF5_SDK_PATH}/components/libraries/log/src/nrf_log_frontend.c" + "${NRF5_SDK_PATH}/components/libraries/log/src/nrf_log_str_formatter.c" + "${NRF5_SDK_PATH}/components/libraries/memobj/nrf_memobj.c" + "${NRF5_SDK_PATH}/components/libraries/ringbuf/nrf_ringbuf.c" + "${NRF5_SDK_PATH}/components/libraries/strerror/nrf_strerror.c" + + # Segger RTT + "${NRF5_SDK_PATH}/external/segger_rtt/SEGGER_RTT_Syscalls_GCC.c" + "${NRF5_SDK_PATH}/external/segger_rtt/SEGGER_RTT.c" + "${NRF5_SDK_PATH}/external/segger_rtt/SEGGER_RTT_printf.c" + + # Other + "${NRF5_SDK_PATH}/external/utf_converter/utf.c" + "${NRF5_SDK_PATH}/external/fprintf/nrf_fprintf.c" + "${NRF5_SDK_PATH}/external/fprintf/nrf_fprintf_format.c" + + # TWI + "${NRF5_SDK_PATH}/modules/nrfx/drivers/src/nrfx_twi.c" + + # GPIOTE + "${NRF5_SDK_PATH}/components/libraries/gpiote/app_gpiote.c" +) set(TINYCRYPT_SRC libs/mynewt-nimble/ext/tinycrypt/src/aes_encrypt.c @@ -37,9 +94,6 @@ set(TINYCRYPT_SRC set(NIMBLE_SRC libs/mynewt-nimble/porting/npl/freertos/src/nimble_port_freertos.c libs/mynewt-nimble/porting/npl/freertos/src/npl_os_freertos.c - - - libs/mynewt-nimble/nimble/host/src/ble_hs.c libs/mynewt-nimble/nimble/host/src/ble_hs_hci_evt.c libs/mynewt-nimble/nimble/host/src/ble_l2cap_sig_cmd.c @@ -78,11 +132,7 @@ set(NIMBLE_SRC libs/mynewt-nimble/nimble/host/src/ble_hs_stop.c libs/mynewt-nimble/nimble/host/src/ble_hs_startup.c libs/mynewt-nimble/nimble/host/store/ram/src/ble_store_ram.c - libs/mynewt-nimble/nimble/transport/ram/src/ble_hci_ram.c - - - libs/mynewt-nimble/nimble/controller/src/ble_ll.c libs/mynewt-nimble/nimble/controller/src/ble_ll_rand.c libs/mynewt-nimble/nimble/controller/src/ble_ll_conn.c @@ -97,9 +147,6 @@ set(NIMBLE_SRC libs/mynewt-nimble/nimble/controller/src/ble_ll_supp_cmd.c libs/mynewt-nimble/nimble/controller/src/ble_ll_hci_ev.c libs/mynewt-nimble/nimble/controller/src/ble_ll_rfmgmt.c - - - libs/mynewt-nimble/porting/nimble/src/os_cputime.c libs/mynewt-nimble/porting/nimble/src/os_cputime_pwr2.c libs/mynewt-nimble/porting/nimble/src/os_mbuf.c @@ -108,13 +155,10 @@ set(NIMBLE_SRC libs/mynewt-nimble/porting/nimble/src/mem.c libs/mynewt-nimble/porting/nimble/src/endian.c libs/mynewt-nimble/porting/nimble/src/os_msys_init.c - libs/mynewt-nimble/nimble/drivers/nrf52/src/ble_hw.c libs/mynewt-nimble/nimble/drivers/nrf52/src/ble_phy.c - libs/mynewt-nimble/nimble/host/services/gap/src/ble_svc_gap.c libs/mynewt-nimble/nimble/host/services/gatt/src/ble_svc_gatt.c - libs/mynewt-nimble/nimble/host/util/src/addr.c ) @@ -135,7 +179,6 @@ set(LVGL_SRC libs/lvgl/src/lv_core/lv_refr.h libs/lvgl/src/lv_core/lv_style.c libs/lvgl/src/lv_core/lv_style.h - libs/lvgl/src/lv_misc/lv_anim.c libs/lvgl/src/lv_misc/lv_anim.h libs/lvgl/src/lv_misc/lv_async.h @@ -175,7 +218,6 @@ set(LVGL_SRC libs/lvgl/src/lv_misc/lv_types.h libs/lvgl/src/lv_misc/lv_utils.c libs/lvgl/src/lv_misc/lv_utils.h - libs/lvgl/src/lv_draw/lv_draw.c libs/lvgl/src/lv_draw/lv_draw.h libs/lvgl/src/lv_draw/lv_draw_arc.c @@ -196,7 +238,6 @@ set(LVGL_SRC libs/lvgl/src/lv_draw/lv_img_cache.h libs/lvgl/src/lv_draw/lv_img_decoder.c libs/lvgl/src/lv_draw/lv_img_decoder.h - libs/lvgl/src/lv_hal/lv_hal.h libs/lvgl/src/lv_hal/lv_hal_disp.c libs/lvgl/src/lv_hal/lv_hal_disp.h @@ -204,31 +245,23 @@ set(LVGL_SRC libs/lvgl/src/lv_hal/lv_hal_indev.h libs/lvgl/src/lv_hal/lv_hal_tick.c libs/lvgl/src/lv_hal/lv_hal_tick.h - libs/lvgl/src/lv_font/lv_font.c libs/lvgl/src/lv_font/lv_font.h libs/lvgl/src/lv_font/lv_font_fmt_txt.c libs/lvgl/src/lv_font/lv_font_fmt_txt.h -# libs/lvgl/src/lv_font/lv_font_roboto_16.c libs/lvgl/src/lv_font/lv_symbol_def.h - libs/lvgl/src/lv_themes/lv_theme.c libs/lvgl/src/lv_themes/lv_theme.h - libs/lvgl/src/lv_objx/lv_btn.h libs/lvgl/src/lv_objx/lv_btn.c - libs/lvgl/src/lv_objx/lv_cont.h libs/lvgl/src/lv_objx/lv_cont.c - libs/lvgl/src/lv_objx/lv_label.h libs/lvgl/src/lv_objx/lv_label.c - libs/lvgl/src/lv_themes/lv_theme.c libs/lvgl/src/lv_themes/lv_theme.h libs/lvgl/src/lv_themes/lv_theme_night.h libs/lvgl/src/lv_themes/lv_theme_night.c - libs/lvgl/src/lv_objx/lv_list.c libs/lvgl/src/lv_objx/lv_list.h libs/lvgl/src/lv_objx/lv_tileview.c @@ -247,20 +280,16 @@ set(LVGL_SRC libs/lvgl/src/lv_objx/lv_arc.h libs/lvgl/src/lv_objx/lv_gauge.c libs/lvgl/src/lv_objx/lv_gauge.h - libs/lvgl/src/lv_objx/lv_mbox.c libs/lvgl/src/lv_objx/lv_mbox.h - libs/lvgl/src/lv_objx/lv_bar.c libs/lvgl/src/lv_objx/lv_bar.h libs/lvgl/src/lv_objx/lv_slider.h libs/lvgl/src/lv_objx/lv_slider.c - ) list(APPEND IMAGE_FILES DisplayApp/Icons/battery/os_battery_error.c - DisplayApp/Icons/battery/os_battery_100.c DisplayApp/Icons/battery/os_battery_090.c DisplayApp/Icons/battery/os_battery_080.c @@ -305,9 +334,12 @@ list(APPEND SOURCE_FILES DisplayApp/Screens/Brightness.cpp DisplayApp/Screens/ScreenList.cpp DisplayApp/Screens/Label.cpp + DisplayApp/Screens/FirmwareUpdate.cpp main.cpp drivers/St7789.cpp + drivers/SpiNorFlash.cpp drivers/SpiMaster.cpp + drivers/Spi.cpp drivers/Watchdog.cpp drivers/DebugPins.cpp Components/Battery/BatteryController.cpp @@ -319,6 +351,7 @@ list(APPEND SOURCE_FILES Components/Ble/DeviceInformationService.cpp Components/Ble/CurrentTimeClient.cpp Components/Ble/AlertNotificationClient.cpp + Components/Ble/DfuService.cpp Components/Ble/CurrentTimeService.cpp Components/Ble/AlertNotificationService.cpp drivers/Cst816s.cpp @@ -329,6 +362,7 @@ list(APPEND SOURCE_FILES ${NIMBLE_SRC} ${LVGL_SRC} ${IMAGE_FILES} + ${SDK_SOURCE_FILES} DisplayApp/LittleVgl.cpp DisplayApp/Fonts/jetbrains_mono_extrabold_compressed.c @@ -355,8 +389,11 @@ set(INCLUDE_FILES DisplayApp/Screens/Brightness.h DisplayApp/Screens/ScreenList.h DisplayApp/Screens/Label.h + DisplayApp/Screens/FirmwareUpdate.h drivers/St7789.h + drivers/SpiNorFlash.h drivers/SpiMaster.h + drivers/Spi.h drivers/Watchdog.h drivers/DebugPins.h Components/Battery/BatteryController.h @@ -368,6 +405,7 @@ set(INCLUDE_FILES Components/Ble/DeviceInformationService.h Components/Ble/CurrentTimeClient.h Components/Ble/AlertNotificationClient.h + Components/Ble/DfuService.h drivers/Cst816s.h FreeRTOS/portmacro.h FreeRTOS/portmacro_cmsis.h @@ -379,13 +417,14 @@ set(INCLUDE_FILES libs/date/includes/date/julian.h libs/date/includes/date/ptz.h libs/date/includes/date/tz_private.h - DisplayApp/LittleVgl.h - SystemTask/SystemTask.h ) include_directories( + . + ../ + libs/ FreeRTOS/ libs/date/includes libs/mynewt-nimble/porting/npl/freertos/include @@ -400,10 +439,179 @@ include_directories( libs/mynewt-nimble/nimble/host/services/gatt/include libs/mynewt-nimble/nimble/host/util/include libs/mynewt-nimble/nimble/host/store/ram/include + + "${NRF5_SDK_PATH}/components/drivers_nrf/nrf_soc_nosd" + "${NRF5_SDK_PATH}/components" + "${NRF5_SDK_PATH}/components/boards" + "${NRF5_SDK_PATH}/components/softdevice/common" + "${NRF5_SDK_PATH}/integration/nrfx" + "${NRF5_SDK_PATH}/integration/nrfx/legacy" + "${NRF5_SDK_PATH}/modules/nrfx" + "${NRF5_SDK_PATH}/modules/nrfx/drivers/include" + "${NRF5_SDK_PATH}/modules/nrfx/hal" + "${NRF5_SDK_PATH}/modules/nrfx/mdk" + ${NRF5_SDK_PATH}/external/freertos/source/include + "${NRF5_SDK_PATH}/components/toolchain/cmsis/include" + "${NRF5_SDK_PATH}/components/libraries/atomic" + "${NRF5_SDK_PATH}/components/libraries/atomic_fifo" + "${NRF5_SDK_PATH}/components/libraries/atomic_flags" + "${NRF5_SDK_PATH}/components/libraries/balloc" + "${NRF5_SDK_PATH}/components/libraries/bootloader/ble_dfu" + "${NRF5_SDK_PATH}/components/libraries/cli" + "${NRF5_SDK_PATH}/components/libraries/crc16" + "${NRF5_SDK_PATH}/components/libraries/crc32" + "${NRF5_SDK_PATH}/components/libraries/crypto" + "${NRF5_SDK_PATH}/components/libraries/csense" + "${NRF5_SDK_PATH}/components/libraries/csense_drv" + "${NRF5_SDK_PATH}/components/libraries/delay" + "${NRF5_SDK_PATH}/components/libraries/ecc" + "${NRF5_SDK_PATH}/components/libraries/experimental_section_vars" + "${NRF5_SDK_PATH}/components/libraries/experimental_task_manager" + "${NRF5_SDK_PATH}/components/libraries/fds" + "${NRF5_SDK_PATH}/components/libraries/fstorage" + "${NRF5_SDK_PATH}/components/libraries/gfx" + "${NRF5_SDK_PATH}/components/libraries/gpiote" + "${NRF5_SDK_PATH}/components/libraries/hardfault" + "${NRF5_SDK_PATH}/components/libraries/hci" + "${NRF5_SDK_PATH}/components/libraries/led_softblink" + "${NRF5_SDK_PATH}/components/libraries/log" + "${NRF5_SDK_PATH}/components/libraries/log/src" + "${NRF5_SDK_PATH}/components/libraries/low_power_pwm" + "${NRF5_SDK_PATH}/components/libraries/mem_manager" + "${NRF5_SDK_PATH}/components/libraries/memobj" + "${NRF5_SDK_PATH}/components/libraries/mpu" + "${NRF5_SDK_PATH}/components/libraries/mutex" + "${NRF5_SDK_PATH}/components/libraries/pwm" + "${NRF5_SDK_PATH}/components/libraries/pwr_mgmt" + "${NRF5_SDK_PATH}/components/libraries/queue" + "${NRF5_SDK_PATH}/components/libraries/ringbuf" + "${NRF5_SDK_PATH}/components/libraries/scheduler" + "${NRF5_SDK_PATH}/components/libraries/sdcard" + "${NRF5_SDK_PATH}/components/libraries/slip" + "${NRF5_SDK_PATH}/components/libraries/sortlist" + "${NRF5_SDK_PATH}/components/libraries/spi_mngr" + "${NRF5_SDK_PATH}/components/libraries/stack_guard" + "${NRF5_SDK_PATH}/components/libraries/strerror" + "${NRF5_SDK_PATH}/components/libraries/svc" + "${NRF5_SDK_PATH}/components/libraries/timer" + "${NRF5_SDK_PATH}/components/libraries/usbd" + "${NRF5_SDK_PATH}/components/libraries/usbd/class/audio" + "${NRF5_SDK_PATH}/components/libraries/usbd/class/cdc" + "${NRF5_SDK_PATH}/components/libraries/usbd/class/cdc/acm" + "${NRF5_SDK_PATH}/components/libraries/usbd/class/hid" + "${NRF5_SDK_PATH}/components/libraries/usbd/class/hid/generic" + "${NRF5_SDK_PATH}/components/libraries/usbd/class/hid/kbd" + "${NRF5_SDK_PATH}/components/libraries/usbd/class/hid/mouse" + "${NRF5_SDK_PATH}/components/libraries/usbd/class/msc" + "${NRF5_SDK_PATH}/components/libraries/util" + "${NRF5_SDK_PATH}/external/segger_rtt/" + "${NRF5_SDK_PATH}/external/fprintf/" + "${NRF5_SDK_PATH}/external/thedotfactory_fonts" + "${NRF5_SDK_PATH}/components/libraries/gpiote" + ) link_directories( - ../ ) -nRF5x_addExecutable(pinetime-app "${SOURCE_FILES}" ${INCLUDE_FILES}) + +set(COMMON_FLAGS -MP -MD -mthumb -mabi=aapcs -Wall -g3 -ffunction-sections -fdata-sections -fno-strict-aliasing -fno-builtin --short-enums -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=fpv4-sp-d16 -Wreturn-type -Werror=return-type) +add_definitions(-DCONFIG_GPIO_AS_PINRESET) +add_definitions(-DDEBUG) +add_definitions(-DNIMBLE_CFG_CONTROLLER) +add_definitions(-DOS_CPUTIME_FREQ) +add_definitions(-DNRF52 -DNRF52832 -DNRF52832_XXAA -DNRF52_PAN_74 -DNRF52_PAN_64 -DNRF52_PAN_12 -DNRF52_PAN_58 -DNRF52_PAN_54 -DNRF52_PAN_31 -DNRF52_PAN_51 -DNRF52_PAN_36 -DNRF52_PAN_15 -DNRF52_PAN_20 -DNRF52_PAN_55 -DBOARD_PCA10040) +add_definitions(-DFREERTOS) +add_definitions(-DDEBUG_NRF_USER) + +# Build autonomous binary (without support for bootloader) +set(EXECUTABLE_NAME "pinetime-app") +set(NRF5_LINKER_SCRIPT "${CMAKE_SOURCE_DIR}/gcc_nrf52.ld") +add_executable(${EXECUTABLE_NAME} ${SOURCE_FILES}) +target_compile_options(${EXECUTABLE_NAME} PUBLIC + $<$,$>: ${COMMON_FLAGS} -O0 -g3> + $<$,$>: ${COMMON_FLAGS} -O3> + $<$,$>: ${COMMON_FLAGS} -O0 -g3> + $<$,$>: ${COMMON_FLAGS} -O3> + $<$: -MP -MD -std=c99 -x assembler-with-cpp> +) + +set_target_properties(${EXECUTABLE_NAME} PROPERTIES + SUFFIX ".out" + LINK_FLAGS "-mthumb -mabi=aapcs -std=gnu++98 -std=c99 -L ${NRF5_SDK_PATH}/modules/nrfx/mdk -T${NRF5_LINKER_SCRIPT} -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=fpv4-sp-d16 -Wl,--gc-sections --specs=nano.specs -lc -lnosys -lm -Wl,-Map=${EXECUTABLE_NAME}.map" + CXX_STANDARD 11 + C_STANDARD 99 + ) + +add_custom_command(TARGET ${EXECUTABLE_NAME} + POST_BUILD + COMMAND ${CMAKE_SIZE_UTIL} ${EXECUTABLE_NAME}.out + COMMAND ${CMAKE_OBJCOPY} -O binary ${EXECUTABLE_NAME}.out "${EXECUTABLE_NAME}.bin" + COMMAND ${CMAKE_OBJCOPY} -O ihex ${EXECUTABLE_NAME}.out "${EXECUTABLE_NAME}.hex" + COMMENT "post build steps for ${EXECUTABLE_NAME}") + + +# Build binary intended to be used by bootloader +set(EXECUTABLE_MCUBOOT_NAME "pinetime-mcuboot-app") +set(EXECUTABLE_MCUBOOT_WITH_BOOTLOADER_NAME "pinetime-mcuboot-app-wth-bootloader") +set(NRF5_LINKER_SCRIPT_MCUBOOT "${CMAKE_SOURCE_DIR}/gcc_nrf52-mcuboot.ld") +add_executable(${EXECUTABLE_MCUBOOT_NAME} ${SOURCE_FILES}) +target_compile_options(${EXECUTABLE_MCUBOOT_NAME} PUBLIC + $<$,$>: ${COMMON_FLAGS} -O0 -g3> + $<$,$>: ${COMMON_FLAGS} -O3> + $<$,$>: ${COMMON_FLAGS} -O0 -g3> + $<$,$>: ${COMMON_FLAGS} -O3> + $<$: -MP -MD -std=c99 -x assembler-with-cpp> + ) + +set_target_properties(${EXECUTABLE_MCUBOOT_NAME} PROPERTIES + SUFFIX ".out" + LINK_FLAGS "-mthumb -mabi=aapcs -std=gnu++98 -std=c99 -L ${NRF5_SDK_PATH}/modules/nrfx/mdk -T${NRF5_LINKER_SCRIPT_MCUBOOT} -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=fpv4-sp-d16 -Wl,--gc-sections --specs=nano.specs -lc -lnosys -lm -Wl,-Map=${EXECUTABLE_MCUBOOT_NAME}.map" + CXX_STANDARD 11 + C_STANDARD 99 + ) + +add_custom_command(TARGET ${EXECUTABLE_MCUBOOT_NAME} + POST_BUILD + COMMAND ${CMAKE_SIZE_UTIL} ${EXECUTABLE_MCUBOOT_NAME}.out + COMMAND ${CMAKE_OBJCOPY} -O binary ${EXECUTABLE_MCUBOOT_NAME}.out "${EXECUTABLE_MCUBOOT_NAME}.bin" + COMMAND ${CMAKE_OBJCOPY} -O ihex ${EXECUTABLE_MCUBOOT_NAME}.out "${EXECUTABLE_MCUBOOT_NAME}.hex" + COMMENT "post build steps for ${EXECUTABLE_MCUBOOT_NAME}" +) + +# FLASH +if(USE_JLINK) + add_custom_target(FLASH_ERASE + COMMAND ${NRFJPROG} --eraseall -f ${NRF_TARGET} + COMMENT "erasing flashing" + ) + add_custom_target("FLASH_${EXECUTABLE_NAME}" + DEPENDS ${EXECUTABLE_NAME} + COMMAND ${NRFJPROG} --program ${EXECUTABLE_NAME}.hex -f ${NRF_TARGET} --sectorerase + COMMAND sleep 0.5s + COMMAND ${NRFJPROG} --reset -f ${NRF_TARGET} + COMMENT "flashing ${EXECUTABLE_NAME}.hex" + ) + +elseif(USE_GDB_CLIENT) + add_custom_target(FLASH_ERASE + COMMAND ${GDB_CLIENT_BIN_PATH} -nx --batch -ex 'target extended-remote ${GDB_CLIENT_TARGET_REMOTE}' -ex 'monitor swdp_scan' -ex 'attach 1' -ex 'mon erase_mass' + COMMENT "erasing flashing" + ) + add_custom_target("FLASH_${EXECUTABLE_NAME}" + DEPENDS ${EXECUTABLE_NAME} + COMMAND ${GDB_CLIENT_BIN_PATH} -nx --batch -ex 'target extended-remote ${GDB_CLIENT_TARGET_REMOTE}' -ex 'monitor swdp_scan' -ex 'attach 1' -ex 'load' -ex 'kill' ${EXECUTABLE_NAME}.hex + COMMENT "flashing ${EXECUTABLE_NAME}.hex" + ) +elseif(USE_OPENOCD) + add_custom_target(FLASH_ERASE + COMMAND ${OPENOCD_BIN_PATH} -f interface/stlink.cfg -c 'transport select hla_swd' -f target/nrf52.cfg -c init -c halt -c 'nrf5 mass_erase' -c reset -c shutdown + COMMENT "erasing flashing" + ) + add_custom_target("FLASH_${EXECUTABLE_NAME}" + DEPENDS ${EXECUTABLE_NAME} + COMMAND ${OPENOCD_BIN_PATH} -c "tcl_port disabled" -c "gdb_port 3333" -c "telnet_port 4444" -f interface/stlink.cfg -c 'transport select hla_swd' -f target/nrf52.cfg -c "program \"${EXECUTABLE_NAME}.hex\"" -c reset -c shutdown + COMMENT "flashing ${EXECUTABLE_NAME}.hex" + ) + +endif() \ No newline at end of file diff --git a/src/Components/Ble/AlertNotificationClient.cpp b/src/Components/Ble/AlertNotificationClient.cpp index 6e096353..bf4d851c 100644 --- a/src/Components/Ble/AlertNotificationClient.cpp +++ b/src/Components/Ble/AlertNotificationClient.cpp @@ -42,10 +42,6 @@ bool AlertNotificationClient::OnDiscoveryEvent(uint16_t connectionHandle, const return false; } -void AlertNotificationClient::Init() { - -} - int AlertNotificationClient::OnCharacteristicsDiscoveryEvent(uint16_t connectionHandle, const ble_gatt_error *error, const ble_gatt_chr *characteristic) { if(error->status != 0 && error->status != BLE_HS_EDONE) { diff --git a/src/Components/Ble/AlertNotificationClient.h b/src/Components/Ble/AlertNotificationClient.h index 7a085b7e..ca4f4e94 100644 --- a/src/Components/Ble/AlertNotificationClient.h +++ b/src/Components/Ble/AlertNotificationClient.h @@ -16,7 +16,6 @@ namespace Pinetime { public: explicit AlertNotificationClient(Pinetime::System::SystemTask &systemTask, Pinetime::Controllers::NotificationManager ¬ificationManager); - void Init(); bool OnDiscoveryEvent(uint16_t connectionHandle, const ble_gatt_error *error, const ble_gatt_svc *service); int OnCharacteristicsDiscoveryEvent(uint16_t connectionHandle, const ble_gatt_error *error, diff --git a/src/Components/Ble/BleController.cpp b/src/Components/Ble/BleController.cpp index 5fa51688..2b396e12 100644 --- a/src/Components/Ble/BleController.cpp +++ b/src/Components/Ble/BleController.cpp @@ -12,4 +12,20 @@ void Ble::Disconnect() { isConnected = false; } +void Ble::StartFirmwareUpdate() { + isFirmwareUpdating = true; +} + +void Ble::StopFirmwareUpdate() { + isFirmwareUpdating = false; +} + +void Ble::FirmwareUpdateTotalBytes(uint32_t totalBytes) { + firmwareUpdateTotalBytes = totalBytes; +} + +void Ble::FirmwareUpdateCurrentBytes(uint32_t currentBytes) { + firmwareUpdateCurrentBytes = currentBytes; +} + diff --git a/src/Components/Ble/BleController.h b/src/Components/Ble/BleController.h index f2bd77e0..65a5ef8f 100644 --- a/src/Components/Ble/BleController.h +++ b/src/Components/Ble/BleController.h @@ -12,8 +12,20 @@ namespace Pinetime { bool IsConnected() const {return isConnected;} void Connect(); void Disconnect(); + + void StartFirmwareUpdate(); + void StopFirmwareUpdate(); + void FirmwareUpdateTotalBytes(uint32_t totalBytes); + void FirmwareUpdateCurrentBytes(uint32_t currentBytes); + + bool IsFirmwareUpdating() const { return isFirmwareUpdating; } + uint32_t FirmwareUpdateTotalBytes() const { return firmwareUpdateTotalBytes; } + uint32_t FirmwareUpdateCurrentBytes() const { return firmwareUpdateCurrentBytes; } private: bool isConnected = false; + bool isFirmwareUpdating = false; + uint32_t firmwareUpdateTotalBytes = 0; + uint32_t firmwareUpdateCurrentBytes = 0; }; } diff --git a/src/Components/Ble/CurrentTimeClient.cpp b/src/Components/Ble/CurrentTimeClient.cpp index fdebc084..7a225f4b 100644 --- a/src/Components/Ble/CurrentTimeClient.cpp +++ b/src/Components/Ble/CurrentTimeClient.cpp @@ -15,63 +15,63 @@ void CurrentTimeClient::Init() { } bool CurrentTimeClient::OnDiscoveryEvent(uint16_t connectionHandle, const ble_gatt_error *error, const ble_gatt_svc *service) { - if(service == nullptr && error->status == BLE_HS_EDONE) { - NRF_LOG_INFO("CTS Discovery complete"); - return true; - } + if(service == nullptr && error->status == BLE_HS_EDONE) { + NRF_LOG_INFO("CTS Discovery complete"); + return true; + } - if(service != nullptr && ble_uuid_cmp(((ble_uuid_t*)&ctsServiceUuid), &service->uuid.u) == 0) { - NRF_LOG_INFO("CTS discovered : 0x%x", service->start_handle); - isDiscovered = true; - ctsStartHandle = service->start_handle; - ctsEndHandle = service->end_handle; + if(service != nullptr && ble_uuid_cmp(((ble_uuid_t*)&ctsServiceUuid), &service->uuid.u) == 0) { + NRF_LOG_INFO("CTS discovered : 0x%x", service->start_handle); + isDiscovered = true; + ctsStartHandle = service->start_handle; + ctsEndHandle = service->end_handle; + return false; + } return false; - } - return false; } int CurrentTimeClient::OnCharacteristicDiscoveryEvent(uint16_t conn_handle, const ble_gatt_error *error, - const ble_gatt_chr *characteristic) { - if(characteristic == nullptr && error->status == BLE_HS_EDONE) { - NRF_LOG_INFO("CTS Characteristic discovery complete"); - return 0; - } + const ble_gatt_chr *characteristic) { + if(characteristic == nullptr && error->status == BLE_HS_EDONE) { + NRF_LOG_INFO("CTS Characteristic discovery complete"); + return 0; + } - if(characteristic != nullptr && ble_uuid_cmp(((ble_uuid_t*)¤tTimeCharacteristicUuid), &characteristic->uuid.u) == 0) { - NRF_LOG_INFO("CTS Characteristic discovered : 0x%x", characteristic->val_handle); - currentTimeHandle = characteristic->val_handle; - } - return 0; + if(characteristic != nullptr && ble_uuid_cmp(((ble_uuid_t*)¤tTimeCharacteristicUuid), &characteristic->uuid.u) == 0) { + NRF_LOG_INFO("CTS Characteristic discovered : 0x%x", characteristic->val_handle); + currentTimeHandle = characteristic->val_handle; + } + return 0; } int CurrentTimeClient::OnCurrentTimeReadResult(uint16_t conn_handle, const ble_gatt_error *error, const ble_gatt_attr *attribute) { - if(error->status == 0) { - // TODO check that attribute->handle equals the handle discovered in OnCharacteristicDiscoveryEvent - CtsData result; - os_mbuf_copydata(attribute->om, 0, sizeof(CtsData), &result); - NRF_LOG_INFO("Received data: %d-%d-%d %d:%d:%d", result.year, - result.month, result.dayofmonth, - result.hour, result.minute, result.second); - dateTimeController.SetTime(result.year, result.month, result.dayofmonth, - 0, result.hour, result.minute, result.second, nrf_rtc_counter_get(portNRF_RTC_REG)); - } else { - NRF_LOG_INFO("Error retrieving current time: %d", error->status); - } - return 0; + if(error->status == 0) { + // TODO check that attribute->handle equals the handle discovered in OnCharacteristicDiscoveryEvent + CtsData result; + os_mbuf_copydata(attribute->om, 0, sizeof(CtsData), &result); + NRF_LOG_INFO("Received data: %d-%d-%d %d:%d:%d", result.year, + result.month, result.dayofmonth, + result.hour, result.minute, result.second); + dateTimeController.SetTime(result.year, result.month, result.dayofmonth, + 0, result.hour, result.minute, result.second, nrf_rtc_counter_get(portNRF_RTC_REG)); + } else { + NRF_LOG_INFO("Error retrieving current time: %d", error->status); + } + return 0; } bool CurrentTimeClient::IsDiscovered() const { - return isDiscovered; + return isDiscovered; } uint16_t CurrentTimeClient::StartHandle() const { - return ctsStartHandle; + return ctsStartHandle; } uint16_t CurrentTimeClient::EndHandle() const { - return ctsEndHandle; + return ctsEndHandle; } uint16_t CurrentTimeClient::CurrentTimeHandle() const { - return currentTimeHandle; -} + return currentTimeHandle; +} \ No newline at end of file diff --git a/src/Components/Ble/CurrentTimeClient.h b/src/Components/Ble/CurrentTimeClient.h index 2278ef15..fabcdaca 100644 --- a/src/Components/Ble/CurrentTimeClient.h +++ b/src/Components/Ble/CurrentTimeClient.h @@ -5,51 +5,51 @@ #include namespace Pinetime { - namespace Controllers { + namespace Controllers { - class CurrentTimeClient { - public: - explicit CurrentTimeClient(DateTime& dateTimeController); - void Init(); - bool OnDiscoveryEvent(uint16_t connectionHandle, const ble_gatt_error *error, const ble_gatt_svc *service); - int OnCharacteristicDiscoveryEvent(uint16_t conn_handle, const ble_gatt_error *error, - const ble_gatt_chr *characteristic); - int OnCurrentTimeReadResult(uint16_t conn_handle, const ble_gatt_error *error, const ble_gatt_attr *attribute); - bool IsDiscovered() const; - uint16_t StartHandle() const; - uint16_t EndHandle() const; - uint16_t CurrentTimeHandle() const; - static constexpr const ble_uuid16_t* Uuid() { return &CurrentTimeClient::ctsServiceUuid; } - static constexpr const ble_uuid16_t* CurrentTimeCharacteristicUuid() { return &CurrentTimeClient::currentTimeCharacteristicUuid; } - private: - typedef struct __attribute__((packed)) { - uint16_t year; - uint8_t month; - uint8_t dayofmonth; - uint8_t hour; - uint8_t minute; - uint8_t second; - uint8_t millis; - uint8_t reason; - } CtsData; + class CurrentTimeClient { + public: + explicit CurrentTimeClient(DateTime& dateTimeController); + void Init(); + bool OnDiscoveryEvent(uint16_t connectionHandle, const ble_gatt_error *error, const ble_gatt_svc *service); + int OnCharacteristicDiscoveryEvent(uint16_t conn_handle, const ble_gatt_error *error, + const ble_gatt_chr *characteristic); + int OnCurrentTimeReadResult(uint16_t conn_handle, const ble_gatt_error *error, const ble_gatt_attr *attribute); + bool IsDiscovered() const; + uint16_t StartHandle() const; + uint16_t EndHandle() const; + uint16_t CurrentTimeHandle() const; + static constexpr const ble_uuid16_t* Uuid() { return &CurrentTimeClient::ctsServiceUuid; } + static constexpr const ble_uuid16_t* CurrentTimeCharacteristicUuid() { return &CurrentTimeClient::currentTimeCharacteristicUuid; } + private: + typedef struct __attribute__((packed)) { + uint16_t year; + uint8_t month; + uint8_t dayofmonth; + uint8_t hour; + uint8_t minute; + uint8_t second; + uint8_t millis; + uint8_t reason; + } CtsData; - static constexpr uint16_t ctsServiceId {0x1805}; - static constexpr uint16_t currentTimeCharacteristicId {0x2a2b}; + static constexpr uint16_t ctsServiceId {0x1805}; + static constexpr uint16_t currentTimeCharacteristicId {0x2a2b}; - static constexpr ble_uuid16_t ctsServiceUuid { - .u { .type = BLE_UUID_TYPE_16 }, - .value = ctsServiceId + static constexpr ble_uuid16_t ctsServiceUuid { + .u { .type = BLE_UUID_TYPE_16 }, + .value = ctsServiceId + }; + static constexpr ble_uuid16_t currentTimeCharacteristicUuid { + .u { .type = BLE_UUID_TYPE_16 }, + .value = currentTimeCharacteristicId + }; + + uint16_t currentTimeHandle; + DateTime& dateTimeController; + bool isDiscovered = false; + uint16_t ctsStartHandle; + uint16_t ctsEndHandle; }; - static constexpr ble_uuid16_t currentTimeCharacteristicUuid { - .u { .type = BLE_UUID_TYPE_16 }, - .value = currentTimeCharacteristicId - }; - - uint16_t currentTimeHandle; - DateTime& dateTimeController; - bool isDiscovered = false; - uint16_t ctsStartHandle; - uint16_t ctsEndHandle; - }; - } + } } \ No newline at end of file diff --git a/src/Components/Ble/DfuService.cpp b/src/Components/Ble/DfuService.cpp index 3099b1bf..de525321 100644 --- a/src/Components/Ble/DfuService.cpp +++ b/src/Components/Ble/DfuService.cpp @@ -1,101 +1,340 @@ -#include "DeviceInformationService.h" +#include +#include +#include +#include "DfuService.h" using namespace Pinetime::Controllers; -constexpr ble_uuid16_t DeviceInformationService::manufacturerNameUuid; -constexpr ble_uuid16_t DeviceInformationService::modelNumberUuid; -constexpr ble_uuid16_t DeviceInformationService::serialNumberUuid; -constexpr ble_uuid16_t DeviceInformationService::fwRevisionUuid; -constexpr ble_uuid16_t DeviceInformationService::deviceInfoUuid; -constexpr ble_uuid16_t DeviceInformationService::hwRevisionUuid; +constexpr ble_uuid128_t DfuService::serviceUuid; +constexpr ble_uuid128_t DfuService::controlPointCharacteristicUuid; +constexpr ble_uuid128_t DfuService::revisionCharacteristicUuid; +constexpr ble_uuid128_t DfuService::packetCharacteristicUuid; -int DeviceInformationCallback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg) { - auto deviceInformationService = static_cast(arg); - return deviceInformationService->OnDeviceInfoRequested(conn_handle, attr_handle, ctxt); +int DfuServiceCallback(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, void *arg) { + auto dfuService = static_cast(arg); + return dfuService->OnServiceData(conn_handle, attr_handle, ctxt); } -void DeviceInformationService::Init() { - ble_gatts_count_cfg(serviceDefinition); - ble_gatts_add_svcs(serviceDefinition); -} - - -int DeviceInformationService::OnDeviceInfoRequested(uint16_t conn_handle, uint16_t attr_handle, - struct ble_gatt_access_ctxt *ctxt) { - const char *str; - - switch (ble_uuid_u16(ctxt->chr->uuid)) { - case manufacturerNameId: - str = manufacturerName; - break; - case modelNumberId: - str = modelNumber; - break; - case serialNumberId: - str = serialNumber; - break; - case fwRevisionId: - str = fwRevision; - break; - case hwRevisionId: - str = hwRevision; - break; - default: - return BLE_ATT_ERR_UNLIKELY; - } - - int res = os_mbuf_append(ctxt->om, str, strlen(str)); - return (res == 0) ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; -} - -DeviceInformationService::DeviceInformationService() : +DfuService::DfuService(Pinetime::System::SystemTask& systemTask, Pinetime::Controllers::Ble& bleController, Pinetime::Drivers::SpiNorFlash& spiNorFlash) : + systemTask{systemTask}, + bleController{bleController}, + spiNorFlash{spiNorFlash}, characteristicDefinition{ { - .uuid = (ble_uuid_t *) &manufacturerNameUuid, - .access_cb = DeviceInformationCallback, + .uuid = (ble_uuid_t *) &packetCharacteristicUuid, + .access_cb = DfuServiceCallback, .arg = this, - .flags = BLE_GATT_CHR_F_READ, + .flags = BLE_GATT_CHR_F_WRITE_NO_RSP, + .val_handle = nullptr, }, { - .uuid = (ble_uuid_t *) &modelNumberUuid, - .access_cb = DeviceInformationCallback, + .uuid = (ble_uuid_t *) &controlPointCharacteristicUuid, + .access_cb = DfuServiceCallback, .arg = this, - .flags = BLE_GATT_CHR_F_READ, + .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_NOTIFY, + .val_handle = nullptr, }, { - .uuid = (ble_uuid_t *) &serialNumberUuid, - .access_cb = DeviceInformationCallback, - .arg = this, - .flags = BLE_GATT_CHR_F_READ, - }, - { - .uuid = (ble_uuid_t *) &fwRevisionUuid, - .access_cb = DeviceInformationCallback, - .arg = this, - .flags = BLE_GATT_CHR_F_READ, - }, - { - .uuid = (ble_uuid_t *) &hwRevisionUuid, - .access_cb = DeviceInformationCallback, + .uuid = (ble_uuid_t *) &revisionCharacteristicUuid, + .access_cb = DfuServiceCallback, .arg = this, .flags = BLE_GATT_CHR_F_READ, + .val_handle = &revision, + }, { 0 } + }, - serviceDefinition{ + serviceDefinition { { /* Device Information Service */ .type = BLE_GATT_SVC_TYPE_PRIMARY, - .uuid = (ble_uuid_t *) &deviceInfoUuid, + .uuid = (ble_uuid_t *) &serviceUuid, .characteristics = characteristicDefinition }, { 0 }, } - { + + { } +void DfuService::Init() { + ble_gatts_count_cfg(serviceDefinition); + ble_gatts_add_svcs(serviceDefinition); +} + +int DfuService::OnServiceData(uint16_t connectionHandle, uint16_t attributeHandle, ble_gatt_access_ctxt *context) { + + ble_gatts_find_chr((ble_uuid_t*)&serviceUuid, (ble_uuid_t*)&packetCharacteristicUuid, nullptr, &packetCharacteristicHandle); + ble_gatts_find_chr((ble_uuid_t*)&serviceUuid, (ble_uuid_t*)&controlPointCharacteristicUuid, nullptr, &controlPointCharacteristicHandle); + ble_gatts_find_chr((ble_uuid_t*)&serviceUuid, (ble_uuid_t*)&revisionCharacteristicUuid, nullptr, &revisionCharacteristicHandle); + + if(attributeHandle == packetCharacteristicHandle) { + if(context->op == BLE_GATT_ACCESS_OP_WRITE_CHR) + return WritePacketHandler(connectionHandle, context->om); + else return 0; + } else if(attributeHandle == controlPointCharacteristicHandle) { + if(context->op == BLE_GATT_ACCESS_OP_WRITE_CHR) + return ControlPointHandler(connectionHandle, context->om); + else return 0; + } else if(attributeHandle == revisionCharacteristicHandle) { + if(context->op == BLE_GATT_ACCESS_OP_READ_CHR) + return SendDfuRevision(context->om); + else return 0; + } else { + NRF_LOG_INFO("[DFU] Unknown Characteristic : %d", attributeHandle); + return 0; + } +} + +int DfuService::SendDfuRevision(os_mbuf *om) const { + int res = os_mbuf_append(om, &revision, sizeof(revision)); + return (res == 0) ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; +} + +int DfuService::WritePacketHandler(uint16_t connectionHandle, os_mbuf *om) { + switch(state) { + case States::Start: { + softdeviceSize = om->om_data[0] + (om->om_data[1] << 8) + (om->om_data[2] << 16) + (om->om_data[3] << 24); + bootloaderSize = om->om_data[4] + (om->om_data[5] << 8) + (om->om_data[6] << 16) + (om->om_data[7] << 24); + applicationSize = om->om_data[8] + (om->om_data[9] << 8) + (om->om_data[10] << 16) + (om->om_data[11] << 24); + bleController.FirmwareUpdateTotalBytes(applicationSize); + NRF_LOG_INFO("[DFU] -> Start data received : SD size : %d, BT size : %d, app size : %d", softdeviceSize, bootloaderSize, applicationSize); + + for(int erased = 0; erased < maxImageSize; erased += 0x1000) { +#if 1 + spiNorFlash.SectorErase(writeOffset + erased); + + auto p = spiNorFlash.ProgramFailed(); + auto e = spiNorFlash.EraseFailed(); + NRF_LOG_INFO("[DFU] Erasing sector %d - %d-%d", erased, p, e); +#endif + } + + uint8_t data[] {16, 1, 1}; + SendNotification(connectionHandle, data, 3); + state = States::Init; + } + return 0; + case States::Init: { + uint16_t deviceType = om->om_data[0] + (om->om_data[1] << 8); + uint16_t deviceRevision = om->om_data[2] + (om->om_data[3] << 8); + uint32_t applicationVersion = om->om_data[4] + (om->om_data[5] << 8) + (om->om_data[6] << 16) + (om->om_data[7] << 24); + uint16_t softdeviceArrayLength = om->om_data[8] + (om->om_data[9] << 8); + uint16_t sd[softdeviceArrayLength]; + for(int i = 0; i < softdeviceArrayLength; i++) { + sd[i] = om->om_data[10 + (i*2)] + (om->om_data[(i*2)+1] << 8); + } + uint16_t crc = om->om_data[10 + (softdeviceArrayLength*2)] + (om->om_data[10 + (softdeviceArrayLength*2)] << 8); + + NRF_LOG_INFO("[DFU] -> Init data received : deviceType = %d, deviceRevision = %d, applicationVersion = %d, nb SD = %d, First SD = %d, CRC = %u", + deviceType, deviceRevision, applicationVersion, softdeviceArrayLength, sd[0], crc); + + return 0; + } + + case States::Data: { + nbPacketReceived++; + auto offset = ((nbPacketReceived-1) % nbPacketsToNotify)*20; + std::memcpy(tempBuffer + offset, om->om_data, om->om_len); + if(firstCrc) { + tempCrc = ComputeCrc(om->om_data, om->om_len, NULL); + firstCrc = false; + } + else + tempCrc = ComputeCrc(om->om_data, om->om_len, &tempCrc); + + if(nbPacketReceived > 0 && (nbPacketReceived % nbPacketsToNotify) == 0) { +#if 1 + spiNorFlash.Write(writeOffset + ((nbPacketReceived-nbPacketsToNotify)*20), tempBuffer, 200); +#endif + } + + + bytesReceived += om->om_len; + bleController.FirmwareUpdateCurrentBytes(bytesReceived); + //NRF_LOG_INFO("[DFU] -> Bytes received : %d in %d packets", bytesReceived, nbPacketReceived); + + + + if((nbPacketReceived % nbPacketsToNotify) == 0) { + uint8_t data[5]{static_cast(Opcodes::PacketReceiptNotification), + (uint8_t)(bytesReceived&0x000000FFu),(uint8_t)(bytesReceived>>8u), (uint8_t)(bytesReceived>>16u),(uint8_t)(bytesReceived>>24u) }; + NRF_LOG_INFO("[DFU] -> Send packet notification: %d bytes received",bytesReceived); + SendNotification(connectionHandle, data, 5); + } + if(bytesReceived == applicationSize) { + if((nbPacketReceived % nbPacketsToNotify) != 0) { + auto remaningPacket = nbPacketReceived % nbPacketsToNotify; + uint32_t spiOffset = writeOffset + ((nbPacketReceived-remaningPacket)*20); + + spiNorFlash.Write(writeOffset + ((nbPacketReceived-remaningPacket)*20), tempBuffer, remaningPacket * 20); + } + if(applicationSize < maxImageSize) { + WriteMagicNumber(); + } + + uint8_t data[3]{static_cast(Opcodes::Response), + static_cast(Opcodes::ReceiveFirmwareImage), + static_cast(ErrorCodes::NoError)}; + NRF_LOG_INFO("[DFU] -> Send packet notification : all bytes received! CRC = %u", tempCrc); + SendNotification(connectionHandle, data, 3); + state = States::Validate; + + Validate(); + } + } + return 0; + default: + // Invalid state + return 0; + } + return 0; +} + +int DfuService::ControlPointHandler(uint16_t connectionHandle, os_mbuf *om) { + auto opcode = static_cast(om->om_data[0]); + NRF_LOG_INFO("[DFU] -> ControlPointHandler"); + + switch(opcode) { + case Opcodes::StartDFU: { + if(state != States::Idle && state != States::Start) { + NRF_LOG_INFO("[DFU] -> Start DFU requested, but we are not in Idle state"); + return 0; + } + if(state == States::Start) { + NRF_LOG_INFO("[DFU] -> Start DFU requested, but we are already in Start state"); + return 0; + } + auto imageType = static_cast(om->om_data[1]); + if(imageType == ImageTypes::Application) { + NRF_LOG_INFO("[DFU] -> Start DFU, mode = Application"); + state = States::Start; + bleController.StartFirmwareUpdate(); + bleController.FirmwareUpdateTotalBytes(0xffffffffu); + bleController.FirmwareUpdateCurrentBytes(0); + systemTask.PushMessage(Pinetime::System::SystemTask::Messages::BleFirmwareUpdateStarted); + return 0; + } else { + NRF_LOG_INFO("[DFU] -> Start DFU, mode %d not supported!", imageType); + return 0; + } + } + break; + case Opcodes::InitDFUParameters: { + if (state != States::Init) { + NRF_LOG_INFO("[DFU] -> Init DFU requested, but we are not in Init state"); + return 0; + } + bool isInitComplete = (om->om_data[1] != 0); + NRF_LOG_INFO("[DFU] -> Init DFU parameters %s", isInitComplete ? " complete" : " not complete"); + + if (isInitComplete) { + uint8_t data[3]{static_cast(Opcodes::Response), + static_cast(Opcodes::InitDFUParameters), + (isInitComplete ? uint8_t{1} : uint8_t{0})}; + NRF_LOG_INFO("SEND NOTIF : %d %d %d", data[0], data[1], data[2]); + SendNotification(connectionHandle, data, 3); + return 0; + } + } + return 0; + case Opcodes::PacketReceiptNotificationRequest: + nbPacketsToNotify = om->om_data[1]; + NRF_LOG_INFO("[DFU] -> Receive Packet Notification Request, nb packet = %d", nbPacketsToNotify); + return 0; + case Opcodes::ReceiveFirmwareImage: + if(state != States::Init) { + NRF_LOG_INFO("[DFU] -> Receive firmware image requested, but we are not in Start Init"); + return 0; + } + NRF_LOG_INFO("[DFU] -> Starting receive firmware"); + state = States::Data; + return 0; + case Opcodes::ValidateFirmware: { + if(state != States::Validate) { + NRF_LOG_INFO("[DFU] -> Validate firmware image requested, but we are not in Data state"); + return 0; + } + NRF_LOG_INFO("[DFU] -> Validate firmware"); + state = States::Validated; + uint8_t data[3]{static_cast(Opcodes::Response), + static_cast(Opcodes::ValidateFirmware), + static_cast(ErrorCodes::NoError)}; + SendNotification(connectionHandle, data, 3); + return 0; + } + case Opcodes::ActivateImageAndReset: + if(state != States::Validated) { + NRF_LOG_INFO("[DFU] -> Activate image and reset requested, but we are not in Validated state"); + return 0; + } + NRF_LOG_INFO("[DFU] -> Activate image and reset!"); + bleController.StopFirmwareUpdate(); + systemTask.PushMessage(Pinetime::System::SystemTask::Messages::BleFirmwareUpdateFinished); + return 0; + default: return 0; + } +} + +void DfuService::SendNotification(uint16_t connectionHandle, const uint8_t *data, const size_t size) { + auto *om = ble_hs_mbuf_from_flat(data, size); + auto ret = ble_gattc_notify_custom(connectionHandle, controlPointCharacteristicHandle, om); + ASSERT(ret == 0); +} + +uint16_t DfuService::ComputeCrc(uint8_t const * p_data, uint32_t size, uint16_t const * p_crc) +{ + uint16_t crc = (p_crc == NULL) ? 0xFFFF : *p_crc; + + for (uint32_t i = 0; i < size; i++) + { + crc = (uint8_t)(crc >> 8) | (crc << 8); + crc ^= p_data[i]; + crc ^= (uint8_t)(crc & 0xFF) >> 4; + crc ^= (crc << 8) << 4; + crc ^= ((crc & 0xFF) << 4) << 1; + } + + return crc; +} + +void DfuService::Validate() { + uint32_t chunkSize = 200; + int currentOffset = 0; + uint16_t crc = 0; + + bool first = true; + while(currentOffset < applicationSize) { + uint32_t readSize = (applicationSize - currentOffset) > chunkSize ? chunkSize : (applicationSize - currentOffset); + + spiNorFlash.Read(writeOffset + currentOffset, tempBuffer, readSize); + if(first) { + crc = ComputeCrc(tempBuffer, readSize, NULL); + first = false; + } + else + crc = ComputeCrc(tempBuffer, readSize, &crc); + currentOffset += readSize; + } + + NRF_LOG_INFO("CRC : %u", crc); +} + +void DfuService::WriteMagicNumber() { + uint32_t magic[4] = { + 0xf395c277, + 0x7fefd260, + 0x0f505235, + 0x8079b62c, + }; + + uint32_t offset = writeOffset + (maxImageSize - (4 * sizeof(uint32_t))); + spiNorFlash.Write(offset, reinterpret_cast(magic), 4 * sizeof(uint32_t)); +} diff --git a/src/Components/Ble/DfuService.h b/src/Components/Ble/DfuService.h index 6249893d..c8351ede 100644 --- a/src/Components/Ble/DfuService.h +++ b/src/Components/Ble/DfuService.h @@ -5,63 +5,110 @@ #include namespace Pinetime { + namespace System { + class SystemTask; + } + namespace Drivers { + class SpiNorFlash; + } namespace Controllers { - class DeviceInformationService { + class Ble; + class DfuService { public: - DeviceInformationService(); + DfuService(Pinetime::System::SystemTask& systemTask, Pinetime::Controllers::Ble& bleController, + Pinetime::Drivers::SpiNorFlash& spiNorFlash); void Init(); + void Validate(); - int OnDeviceInfoRequested(uint16_t conn_handle, uint16_t attr_handle, - struct ble_gatt_access_ctxt *ctxt); - + int OnServiceData(uint16_t connectionHandle, uint16_t attributeHandle, ble_gatt_access_ctxt *context); private: - static constexpr uint16_t deviceInfoId {0x180a}; - static constexpr uint16_t manufacturerNameId {0x2a29}; - static constexpr uint16_t modelNumberId {0x2a24}; - static constexpr uint16_t serialNumberId {0x2a25}; - static constexpr uint16_t fwRevisionId {0x2a26}; - static constexpr uint16_t hwRevisionId {0x2a27}; + Pinetime::System::SystemTask& systemTask; + Pinetime::Controllers::Ble& bleController; + Pinetime::Drivers::SpiNorFlash& spiNorFlash; - static constexpr char* manufacturerName = "Codingfield"; - static constexpr char* modelNumber = "1"; - static constexpr char* serialNumber = "9.8.7.6.5.4"; - static constexpr char* fwRevision = "0.5.0"; - static constexpr char* hwRevision = "1.0.0"; + static constexpr uint16_t dfuServiceId {0x1530}; + static constexpr uint16_t packetCharacteristicId {0x1532}; + static constexpr uint16_t controlPointCharacteristicId {0x1531}; + static constexpr uint16_t revisionCharacteristicId {0x1534}; - static constexpr ble_uuid16_t deviceInfoUuid { - .u { .type = BLE_UUID_TYPE_16 }, - .value = deviceInfoId + uint16_t revision {0x0008}; + + static constexpr ble_uuid128_t serviceUuid { + .u { .type = BLE_UUID_TYPE_128}, + .value = {0x23, 0xD1, 0xBC, 0xEA, 0x5F, 0x78, 0x23, 0x15, + 0xDE, 0xEF, 0x12, 0x12, 0x30, 0x15, 0x00, 0x00} }; - static constexpr ble_uuid16_t manufacturerNameUuid { - .u { .type = BLE_UUID_TYPE_16 }, - .value = manufacturerNameId + static constexpr ble_uuid128_t packetCharacteristicUuid { + .u { .type = BLE_UUID_TYPE_128}, + .value = {0x23, 0xD1, 0xBC, 0xEA, 0x5F, 0x78, 0x23, 0x15, + 0xDE, 0xEF, 0x12, 0x12, 0x32, 0x15, 0x00, 0x00} }; - static constexpr ble_uuid16_t modelNumberUuid { - .u { .type = BLE_UUID_TYPE_16 }, - .value = modelNumberId + static constexpr ble_uuid128_t controlPointCharacteristicUuid { + .u { .type = BLE_UUID_TYPE_128}, + .value = {0x23, 0xD1, 0xBC, 0xEA, 0x5F, 0x78, 0x23, 0x15, + 0xDE, 0xEF, 0x12, 0x12, 0x31, 0x15, 0x00, 0x00} }; - static constexpr ble_uuid16_t serialNumberUuid { - .u { .type = BLE_UUID_TYPE_16 }, - .value = serialNumberId + static constexpr ble_uuid128_t revisionCharacteristicUuid { + .u { .type = BLE_UUID_TYPE_128}, + .value = {0x23, 0xD1, 0xBC, 0xEA, 0x5F, 0x78, 0x23, 0x15, + 0xDE, 0xEF, 0x12, 0x12, 0x34, 0x15, 0x00, 0x00} }; - static constexpr ble_uuid16_t fwRevisionUuid { - .u { .type = BLE_UUID_TYPE_16 }, - .value = fwRevisionId - }; - - static constexpr ble_uuid16_t hwRevisionUuid { - .u {.type = BLE_UUID_TYPE_16}, - .value = hwRevisionId - }; - - struct ble_gatt_chr_def characteristicDefinition[6]; + struct ble_gatt_chr_def characteristicDefinition[4]; struct ble_gatt_svc_def serviceDefinition[2]; + uint16_t packetCharacteristicHandle; + uint16_t controlPointCharacteristicHandle; + uint16_t revisionCharacteristicHandle; + enum class States : uint8_t {Idle, Init, Start, Data, Validate, Validated}; + States state = States::Idle; + enum class ImageTypes : uint8_t { + NoImage = 0x00, + SoftDevice = 0x01, + Bootloader = 0x02, + SoftDeviceAndBootloader = 0x03, + Application = 0x04 + }; + + enum class Opcodes : uint8_t { + StartDFU = 0x01, + InitDFUParameters = 0x02, + ReceiveFirmwareImage = 0x03, + ValidateFirmware = 0x04, + ActivateImageAndReset = 0x05, + PacketReceiptNotificationRequest = 0x08, + Response = 0x10, + PacketReceiptNotification = 0x11 + }; + + enum class ErrorCodes { NoError = 0x01}; + + uint8_t nbPacketsToNotify = 0; + uint32_t nbPacketReceived = 0; + uint32_t bytesReceived = 0; + uint32_t writeOffset = 0x40000; + + uint32_t softdeviceSize = 0; + uint32_t bootloaderSize = 0; + uint32_t applicationSize = 0; + static constexpr uint32_t maxImageSize = 475136; + + int SendDfuRevision(os_mbuf *om) const; + void SendNotification(uint16_t connectionHandle, const uint8_t *data, const size_t size); + int WritePacketHandler(uint16_t connectionHandle, os_mbuf *om); + int ControlPointHandler(uint16_t connectionHandle, os_mbuf *om); + + uint8_t tempBuffer[200]; + uint16_t ComputeCrc(uint8_t const * p_data, uint32_t size, uint16_t const * p_crc); + + bool firstCrc = true; + uint16_t tempCrc = 0; + + void WriteMagicNumber(); }; } } \ No newline at end of file diff --git a/src/Components/Ble/NimbleController.cpp b/src/Components/Ble/NimbleController.cpp index 2fe03571..7a7753b1 100644 --- a/src/Components/Ble/NimbleController.cpp +++ b/src/Components/Ble/NimbleController.cpp @@ -24,11 +24,14 @@ using namespace Pinetime::Controllers; NimbleController::NimbleController(Pinetime::System::SystemTask& systemTask, Pinetime::Controllers::Ble& bleController, DateTime& dateTimeController, - Pinetime::Controllers::NotificationManager& notificationManager) : + Pinetime::Controllers::NotificationManager& notificationManager, + Pinetime::Drivers::SpiNorFlash& spiNorFlash) : systemTask{systemTask}, bleController{bleController}, dateTimeController{dateTimeController}, notificationManager{notificationManager}, + spiNorFlash{spiNorFlash}, + dfuService{systemTask, bleController, spiNorFlash}, currentTimeClient{dateTimeController}, alertNotificationClient{systemTask, notificationManager}, anService{systemTask, notificationManager}, @@ -80,6 +83,7 @@ void NimbleController::Init() { anService.Init(); + dfuService.Init(); int res; res = ble_hs_util_ensure_addr(0); ASSERT(res == 0); @@ -116,8 +120,9 @@ void NimbleController::StartAdvertising() { // fields.uuids128 = BLE_UUID128(BLE_UUID128_DECLARE( // 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, // 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff)); - fields.num_uuids128 = 0; - fields.uuids128_is_complete = 0;; + fields.uuids128 = &dfuServiceUuid; + fields.num_uuids128 = 1; + fields.uuids128_is_complete = 1; fields.tx_pwr_lvl = BLE_HS_ADV_TX_PWR_LVL_AUTO; rsp_fields.name = (uint8_t *)"Pinetime-JF"; @@ -126,16 +131,14 @@ void NimbleController::StartAdvertising() { int res; res = ble_gap_adv_set_fields(&fields); - //ASSERT(res == 0); +// ASSERT(res == 0); // TODO this one sometimes fails with error 22 (notsync) res = ble_gap_adv_rsp_set_fields(&rsp_fields); - //ASSERT(res == 0); +// ASSERT(res == 0); res = ble_gap_adv_start(addrType, NULL, 10000, &adv_params, GAPEventCallback, this); - //ASSERT(res == 0); - - // TODO I've disabled these ASSERT as they sometime asserts and reset the mcu. +// ASSERT(res == 0);// TODO I've disabled these ASSERT as they sometime asserts and reset the mcu. // For now, the advertising is restarted as soon as it ends. There may be a race condition // that prevent the advertising from restarting reliably. // I remove the assert to prevent this uncesseray crash, but in the long term, the management of @@ -172,8 +175,9 @@ int NimbleController::OnGAPEvent(ble_gap_event *event) { bleController.Disconnect(); } else { bleController.Connect(); + systemTask.PushMessage(Pinetime::System::SystemTask::Messages::BleConnected); connectionHandle = event->connect.conn_handle; - ble_gattc_disc_all_svcs(connectionHandle, OnAllSvrDisco, this); + // Service discovery is deffered via systemtask } } break; @@ -182,6 +186,7 @@ int NimbleController::OnGAPEvent(ble_gap_event *event) { NRF_LOG_INFO("disconnect; reason=%d", event->disconnect.reason); /* Connection terminated; resume advertising. */ + connectionHandle = BLE_HS_CONN_HANDLE_NONE; bleController.Disconnect(); StartAdvertising(); break; @@ -247,7 +252,7 @@ int NimbleController::OnGAPEvent(ble_gap_event *event) { /* Attribute data is contained in event->notify_rx.attr_data. */ default: - NRF_LOG_INFO("Advertising event : %d", event->type); +// NRF_LOG_INFO("Advertising event : %d", event->type); break; } return 0; @@ -264,7 +269,6 @@ int NimbleController::OnDiscoveryEvent(uint16_t i, const ble_gatt_error *error, ble_gattc_disc_all_chrs(connectionHandle, alertNotificationClient.StartHandle(), alertNotificationClient.EndHandle(), AlertNotificationCharacteristicDiscoveredCallback, this); } - return 0; } alertNotificationClient.OnDiscoveryEvent(i, error, service); @@ -311,6 +315,10 @@ int NimbleController::OnANSDescriptorDiscoveryEventCallback(uint16_t connectionH return alertNotificationClient.OnDescriptorDiscoveryEventCallback(connectionHandle, error, characteristicValueHandle, descriptor); } +void NimbleController::StartDiscovery() { + ble_gattc_disc_all_svcs(connectionHandle, OnAllSvrDisco, this); +} + diff --git a/src/Components/Ble/NimbleController.h b/src/Components/Ble/NimbleController.h index 44fbbe2c..7e680607 100644 --- a/src/Components/Ble/NimbleController.h +++ b/src/Components/Ble/NimbleController.h @@ -5,16 +5,22 @@ #include "AlertNotificationClient.h" #include "DeviceInformationService.h" #include "CurrentTimeClient.h" +#include "DfuService.h" #include "CurrentTimeService.h" #include namespace Pinetime { + namespace Drivers { + class SpiNorFlash; + } namespace Controllers { class DateTime; class NimbleController { public: - NimbleController(Pinetime::System::SystemTask& systemTask, Pinetime::Controllers::Ble& bleController, DateTime& dateTimeController, Pinetime::Controllers::NotificationManager& notificationManager); + NimbleController(Pinetime::System::SystemTask& systemTask, Pinetime::Controllers::Ble& bleController, + DateTime& dateTimeController, Pinetime::Controllers::NotificationManager& notificationManager, + Pinetime::Drivers::SpiNorFlash& spiNorFlash); void Init(); void StartAdvertising(); int OnGAPEvent(ble_gap_event *event); @@ -27,12 +33,16 @@ namespace Pinetime { int OnCurrentTimeReadResult(uint16_t connectionHandle, const ble_gatt_error *error, ble_gatt_attr *attribute); int OnANSDescriptorDiscoveryEventCallback(uint16_t connectionHandle, const ble_gatt_error *error, uint16_t characteristicValueHandle, const ble_gatt_dsc *descriptor); + + void StartDiscovery(); private: static constexpr char* deviceName = "Pinetime-JF"; Pinetime::System::SystemTask& systemTask; Pinetime::Controllers::Ble& bleController; DateTime& dateTimeController; Pinetime::Controllers::NotificationManager& notificationManager; + Pinetime::Drivers::SpiNorFlash& spiNorFlash; + Pinetime::Controllers::DfuService dfuService; DeviceInformationService deviceInformationService; CurrentTimeClient currentTimeClient; @@ -42,6 +52,12 @@ namespace Pinetime { uint8_t addrType; uint16_t connectionHandle; + + ble_uuid128_t dfuServiceUuid { + .u { .type = BLE_UUID_TYPE_128}, + .value = {0x23, 0xD1, 0xBC, 0xEA, 0x5F, 0x78, 0x23, 0x15, + 0xDE, 0xEF, 0x12, 0x12, 0x30, 0x15, 0x00, 0x00} + }; }; } } diff --git a/src/DisplayApp/DisplayApp.cpp b/src/DisplayApp/DisplayApp.cpp index 1b4515e0..c42639c7 100644 --- a/src/DisplayApp/DisplayApp.cpp +++ b/src/DisplayApp/DisplayApp.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include "../SystemTask/SystemTask.h" using namespace Pinetime::Applications; @@ -157,6 +158,17 @@ void DisplayApp::Refresh() { // toggle = true; // } + break; + case Messages::BleFirmwareUpdateStarted: + lvgl.SetFullRefresh(Components::LittleVgl::FullRefreshDirections::Down); + currentScreen.reset(nullptr); + currentScreen.reset(new Screens::FirmwareUpdate(this, bleController)); + + break; + case Messages::BleFirmwareUpdateFinished: + lvgl.SetFullRefresh(Components::LittleVgl::FullRefreshDirections::Down); + currentScreen.reset(nullptr); + currentScreen.reset(new Screens::Clock(this, dateTimeController, batteryController, bleController)); break; } } diff --git a/src/DisplayApp/DisplayApp.h b/src/DisplayApp/DisplayApp.h index 09f0d1cd..b45a0bee 100644 --- a/src/DisplayApp/DisplayApp.h +++ b/src/DisplayApp/DisplayApp.h @@ -30,7 +30,7 @@ namespace Pinetime { public: enum class States {Idle, Running}; enum class Messages : uint8_t {GoToSleep, GoToRunning, UpdateDateTime, UpdateBleConnection, UpdateBatteryLevel, TouchEvent, SwitchScreen,ButtonPushed, - NewNotification + NewNotification, BleFirmwareUpdateStarted, BleFirmwareUpdateFinished }; enum class FullRefreshDirections { None, Up, Down }; diff --git a/src/DisplayApp/LittleVgl.cpp b/src/DisplayApp/LittleVgl.cpp index b1b894f7..7b6cda94 100644 --- a/src/DisplayApp/LittleVgl.cpp +++ b/src/DisplayApp/LittleVgl.cpp @@ -74,6 +74,9 @@ void LittleVgl::SetFullRefresh(FullRefreshDirections direction) { void LittleVgl::FlushDisplay(const lv_area_t *area, lv_color_t *color_p) { ulTaskNotifyTake(pdTRUE, 500); + // NOtification is still needed (even if there is a mutex on SPI) because of the DataCommand pin + // which cannot be set/clear during a transfert. + // TODO refactore and remove duplicated code diff --git a/src/DisplayApp/Screens/FirmwareUpdate.cpp b/src/DisplayApp/Screens/FirmwareUpdate.cpp new file mode 100644 index 00000000..f3cf42f9 --- /dev/null +++ b/src/DisplayApp/Screens/FirmwareUpdate.cpp @@ -0,0 +1,49 @@ +#include +#include "FirmwareUpdate.h" +#include "../DisplayApp.h" + +using namespace Pinetime::Applications::Screens; +extern lv_font_t jetbrains_mono_extrabold_compressed; +extern lv_font_t jetbrains_mono_bold_20; + + +FirmwareUpdate::FirmwareUpdate(Pinetime::Applications::DisplayApp *app, Pinetime::Controllers::Ble& bleController) : + Screen(app), bleController{bleController} { + + titleLabel = lv_label_create(lv_scr_act(), NULL); + lv_label_set_text(titleLabel, "Firmware update"); + lv_obj_set_auto_realign(titleLabel, true); + lv_obj_align(titleLabel, NULL, LV_ALIGN_IN_TOP_MID, 0, 50); + + bar1 = lv_bar_create(lv_scr_act(), NULL); + lv_obj_set_size(bar1, 200, 30); + lv_obj_align(bar1, NULL, LV_ALIGN_CENTER, 0, 0); + lv_bar_set_anim_time(bar1, 10); + lv_bar_set_range(bar1, 0, 100); + lv_bar_set_value(bar1, 0, LV_ANIM_OFF); + + percentLabel = lv_label_create(lv_scr_act(), NULL); + lv_label_set_text(percentLabel, ""); + lv_obj_set_auto_realign(percentLabel, true); + lv_obj_align(percentLabel, bar1, LV_ALIGN_OUT_TOP_MID, 0, 60); +} + +FirmwareUpdate::~FirmwareUpdate() { + lv_obj_clean(lv_scr_act()); +} + +bool FirmwareUpdate::Refresh() { + float current = bleController.FirmwareUpdateCurrentBytes() / 1024.0f; + float total = bleController.FirmwareUpdateTotalBytes() / 1024.0f; + int16_t pc = (current / total) * 100.0f; + sprintf(percentStr, "%d %%", pc); + lv_label_set_text(percentLabel, percentStr); + + lv_bar_set_value(bar1, pc, LV_ANIM_OFF); + return running; +} + +bool FirmwareUpdate::OnButtonPushed() { + running = false; + return true; +} diff --git a/src/DisplayApp/Screens/FirmwareUpdate.h b/src/DisplayApp/Screens/FirmwareUpdate.h new file mode 100644 index 00000000..a4cbec62 --- /dev/null +++ b/src/DisplayApp/Screens/FirmwareUpdate.h @@ -0,0 +1,39 @@ +#pragma once + +#include +#include +#include +#include "Screen.h" +#include +#include +#include +#include +#include +#include "../Fonts/lcdfont14.h" +#include "../Fonts/lcdfont70.h" +#include "../../Version.h" + +namespace Pinetime { + namespace Applications { + namespace Screens { + + class FirmwareUpdate : public Screen{ + public: + FirmwareUpdate(DisplayApp* app, Pinetime::Controllers::Ble& bleController); + ~FirmwareUpdate() override; + + bool Refresh() override; + bool OnButtonPushed() override; + + private: + Pinetime::Controllers::Ble& bleController; + lv_obj_t * bar1; + lv_obj_t * percentLabel; + lv_obj_t * titleLabel; + char percentStr[10]; + bool running = true; + + }; + } + } +} diff --git a/src/SystemTask/SystemTask.cpp b/src/SystemTask/SystemTask.cpp index fc37ecb2..84392c7d 100644 --- a/src/SystemTask/SystemTask.cpp +++ b/src/SystemTask/SystemTask.cpp @@ -14,15 +14,16 @@ using namespace Pinetime::System; -SystemTask::SystemTask(Drivers::SpiMaster &spi, Drivers::St7789 &lcd, Drivers::Cst816S &touchPanel, +SystemTask::SystemTask(Drivers::SpiMaster &spi, Drivers::St7789 &lcd, + Pinetime::Drivers::SpiNorFlash& spiNorFlash, Drivers::Cst816S &touchPanel, Components::LittleVgl &lvgl, Controllers::Battery &batteryController, Controllers::Ble &bleController, Controllers::DateTime &dateTimeController, Pinetime::Controllers::NotificationManager& notificationManager) : - spi{spi}, lcd{lcd}, touchPanel{touchPanel}, lvgl{lvgl}, batteryController{batteryController}, + spi{spi}, lcd{lcd}, spiNorFlash{spiNorFlash}, touchPanel{touchPanel}, lvgl{lvgl}, batteryController{batteryController}, bleController{bleController}, dateTimeController{dateTimeController}, watchdog{}, watchdogView{watchdog}, notificationManager{notificationManager}, - nimbleController(*this, bleController,dateTimeController, notificationManager) { + nimbleController(*this, bleController,dateTimeController, notificationManager, spiNorFlash) { systemTaksMsgQueue = xQueueCreate(10, 1); } @@ -37,19 +38,47 @@ void SystemTask::Process(void *instance) { app->Work(); } +static inline void nrf52_wait_for_flash_ready(void) +{ + while (NRF_NVMC->READY == NVMC_READY_READY_Busy) {;} +} + +void nrf52_nvmc_write_word(uint32_t address, uint32_t value) { + // Enable write. + NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Wen; + __ISB(); + __DSB(); + + // Write word + *(uint32_t*)address = value; + nrf52_wait_for_flash_ready(); + + // Disable write + NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Ren; + __ISB(); + __DSB(); +} + void SystemTask::Work() { watchdog.Setup(7); watchdog.Start(); NRF_LOG_INFO("Last reset reason : %s", Pinetime::Drivers::Watchdog::ResetReasonToString(watchdog.ResetReason())); APP_GPIOTE_INIT(2); -/* BLE */ + spi.Init(); + spiNorFlash.Init(); + + uint32_t* magicptr = reinterpret_cast(0x7BFE8); + uint32_t magic = *magicptr; + if(magic != 1) + nrf52_nvmc_write_word(0x7BFE8, 1); + + NRF_LOG_INFO("MAGIC : %d", magic); + nimbleController.Init(); nimbleController.StartAdvertising(); -/* /BLE*/ - - spi.Init(); lcd.Init(); + touchPanel.Init(); batteryController.Init(); @@ -100,9 +129,31 @@ void SystemTask::Work() { case Messages::OnNewNotification: displayApp->PushMessage(Pinetime::Applications::DisplayApp::Messages::NewNotification); break; + case Messages::BleConnected: + isBleDiscoveryTimerRunning = true; + bleDiscoveryTimer = 5; + break; + case Messages::BleFirmwareUpdateStarted: + displayApp->PushMessage(Pinetime::Applications::DisplayApp::Messages::BleFirmwareUpdateStarted); + break; + case Messages::BleFirmwareUpdateFinished: + displayApp->PushMessage(Pinetime::Applications::DisplayApp::Messages::BleFirmwareUpdateFinished); + break; default: break; } } + + if(isBleDiscoveryTimerRunning) { + if(bleDiscoveryTimer == 0) { + isBleDiscoveryTimerRunning = false; + // Services discovery is deffered from 3 seconds to avoid the conflicts between the host communicating with the + // tharget and vice-versa. I'm not sure if this is the right way to handle this... + nimbleController.StartDiscovery(); + } else { + bleDiscoveryTimer--; + } + } + uint32_t systick_counter = nrf_rtc_counter_get(portNRF_RTC_REG); dateTimeController.UpdateTime(systick_counter); batteryController.Update(); diff --git a/src/SystemTask/SystemTask.h b/src/SystemTask/SystemTask.h index 5eba391b..53e69fa7 100644 --- a/src/SystemTask/SystemTask.h +++ b/src/SystemTask/SystemTask.h @@ -9,15 +9,18 @@ #include #include #include +#include namespace Pinetime { namespace System { class SystemTask { public: - enum class Messages {GoToSleep, GoToRunning, OnNewTime, OnNewNotification + enum class Messages {GoToSleep, GoToRunning, OnNewTime, OnNewNotification, BleConnected, + BleFirmwareUpdateStarted, BleFirmwareUpdateFinished }; - SystemTask(Drivers::SpiMaster &spi, Drivers::St7789 &lcd, Drivers::Cst816S &touchPanel, + SystemTask(Drivers::SpiMaster &spi, Drivers::St7789 &lcd, + Pinetime::Drivers::SpiNorFlash& spiNorFlash, Drivers::Cst816S &touchPanel, Components::LittleVgl &lvgl, Controllers::Battery &batteryController, Controllers::Ble &bleController, Controllers::DateTime &dateTimeController, @@ -34,6 +37,7 @@ namespace Pinetime { Pinetime::Drivers::SpiMaster& spi; Pinetime::Drivers::St7789& lcd; + Pinetime::Drivers::SpiNorFlash& spiNorFlash; Pinetime::Drivers::Cst816S& touchPanel; Pinetime::Components::LittleVgl& lvgl; Pinetime::Controllers::Battery& batteryController; @@ -58,7 +62,8 @@ namespace Pinetime { static void Process(void* instance); void Work(); - + bool isBleDiscoveryTimerRunning = false; + uint8_t bleDiscoveryTimer = 0; }; } diff --git a/src/drivers/Spi.cpp b/src/drivers/Spi.cpp new file mode 100644 index 00000000..ec3a5e94 --- /dev/null +++ b/src/drivers/Spi.cpp @@ -0,0 +1,34 @@ +#include +#include "Spi.h" + +using namespace Pinetime::Drivers; + +Spi::Spi(SpiMaster& spiMaster, uint8_t pinCsn) : + spiMaster{spiMaster}, pinCsn{pinCsn} { + nrf_gpio_cfg_output(pinCsn); + nrf_gpio_pin_set(pinCsn); +} + +bool Spi::Write(const uint8_t *data, size_t size) { + return spiMaster.Write(pinCsn, data, size); +} + +bool Spi::Read(uint8_t* cmd, size_t cmdSize, uint8_t *data, size_t dataSize) { + return spiMaster.Read(pinCsn, cmd, cmdSize, data, dataSize); +} + +void Spi::Sleep() { + // TODO sleep spi + nrf_gpio_cfg_default(pinCsn); +} + +bool Spi::Init() { + nrf_gpio_pin_set(pinCsn); /* disable Set slave select (inactive high) */ + return true; +} + +bool Spi::WriteCmdAndBuffer(uint8_t *cmd, size_t cmdSize, uint8_t *data, size_t dataSize) { + return spiMaster.WriteCmdAndBuffer(pinCsn, cmd, cmdSize, data, dataSize); +} + + diff --git a/src/drivers/Spi.h b/src/drivers/Spi.h new file mode 100644 index 00000000..bee39af2 --- /dev/null +++ b/src/drivers/Spi.h @@ -0,0 +1,34 @@ +#pragma once +#include +#include +#include +#include +#include +#include + +#include "BufferProvider.h" +#include "SpiMaster.h" + +namespace Pinetime { + namespace Drivers { + class Spi { + public: + Spi(SpiMaster& spiMaster, uint8_t pinCsn); + Spi(const Spi&) = delete; + Spi& operator=(const Spi&) = delete; + Spi(Spi&&) = delete; + Spi& operator=(Spi&&) = delete; + + bool Init(); + bool Write(const uint8_t* data, size_t size); + bool Read(uint8_t* cmd, size_t cmdSize, uint8_t *data, size_t dataSize); + bool WriteCmdAndBuffer(uint8_t* cmd, size_t cmdSize, uint8_t *data, size_t dataSize); + void Sleep(); + void Wakeup(); + + private: + SpiMaster& spiMaster; + uint8_t pinCsn; + }; + } +} diff --git a/src/drivers/SpiMaster.cpp b/src/drivers/SpiMaster.cpp index 4b3dd677..18bf336f 100644 --- a/src/drivers/SpiMaster.cpp +++ b/src/drivers/SpiMaster.cpp @@ -9,8 +9,8 @@ using namespace Pinetime::Drivers; SpiMaster::SpiMaster(const SpiMaster::SpiModule spi, const SpiMaster::Parameters ¶ms) : spi{spi}, params{params} { - mutex = xSemaphoreCreateBinary(); - ASSERT(mutex != NULL); + mutex = xSemaphoreCreateBinary(); + ASSERT(mutex != NULL); } bool SpiMaster::Init() { @@ -20,8 +20,8 @@ bool SpiMaster::Init() { nrf_gpio_pin_clear(params.pinMOSI); nrf_gpio_cfg_output(params.pinMOSI); nrf_gpio_cfg_input(params.pinMISO, NRF_GPIO_PIN_NOPULL); - nrf_gpio_cfg_output(params.pinCSN); - pinCsn = params.pinCSN; +// nrf_gpio_cfg_output(params.pinCSN); +// pinCsn = params.pinCSN; switch(spi) { case SpiModule::SPI0: spiBaseAddress = NRF_SPIM0; break; @@ -33,7 +33,6 @@ bool SpiMaster::Init() { spiBaseAddress->PSELSCK = params.pinSCK; spiBaseAddress->PSELMOSI = params.pinMOSI; spiBaseAddress->PSELMISO = params.pinMISO; - nrf_gpio_pin_set(pinCsn); /* disable Set slave select (inactive high) */ uint32_t frequency; switch(params.Frequency) { @@ -147,19 +146,33 @@ void SpiMaster::PrepareTx(const volatile uint32_t bufferAddress, const volatile spiBaseAddress->EVENTS_END = 0; } -bool SpiMaster::Write(const uint8_t *data, size_t size) { +void SpiMaster::PrepareRx(const volatile uint32_t cmdAddress, const volatile size_t cmdSize, const volatile uint32_t bufferAddress, const volatile size_t size) { + spiBaseAddress->TXD.PTR = 0; + spiBaseAddress->TXD.MAXCNT = 0; + spiBaseAddress->TXD.LIST = 0; + spiBaseAddress->RXD.PTR = bufferAddress; + spiBaseAddress->RXD.MAXCNT = size; + spiBaseAddress->RXD.LIST = 0; + spiBaseAddress->EVENTS_END = 0; +} + + +bool SpiMaster::Write(uint8_t pinCsn, const uint8_t *data, size_t size) { if(data == nullptr) return false; auto ok = xSemaphoreTake(mutex, portMAX_DELAY); ASSERT(ok == true); taskToNotify = xTaskGetCurrentTaskHandle(); + + this->pinCsn = pinCsn; + if(size == 1) { SetupWorkaroundForFtpan58(spiBaseAddress, 0,0); } else { DisableWorkaroundForFtpan58(spiBaseAddress, 0, 0); } - nrf_gpio_pin_clear(pinCsn); + nrf_gpio_pin_clear(this->pinCsn); currentBufferAddr = (uint32_t)data; currentBufferSize = size; @@ -178,6 +191,39 @@ bool SpiMaster::Write(const uint8_t *data, size_t size) { return true; } +bool SpiMaster::Read(uint8_t pinCsn, uint8_t* cmd, size_t cmdSize, uint8_t *data, size_t dataSize) { + xSemaphoreTake(mutex, portMAX_DELAY); + + taskToNotify = nullptr; + + this->pinCsn = pinCsn; + DisableWorkaroundForFtpan58(spiBaseAddress, 0,0); + spiBaseAddress->INTENCLR = (1<<6); + spiBaseAddress->INTENCLR = (1<<1); + spiBaseAddress->INTENCLR = (1<<19); + + nrf_gpio_pin_clear(this->pinCsn); + + + currentBufferAddr = 0; + currentBufferSize = 0; + + PrepareTx((uint32_t)cmd, cmdSize); + spiBaseAddress->TASKS_START = 1; + while (spiBaseAddress->EVENTS_END == 0); + + PrepareRx((uint32_t)cmd, cmdSize, (uint32_t)data, dataSize); + spiBaseAddress->TASKS_START = 1; + + while (spiBaseAddress->EVENTS_END == 0); + nrf_gpio_pin_set(this->pinCsn); + + xSemaphoreGive(mutex); + + return true; +} + + void SpiMaster::Sleep() { while(spiBaseAddress->ENABLE != 0) { spiBaseAddress->ENABLE = (SPIM_ENABLE_ENABLE_Disabled << SPIM_ENABLE_ENABLE_Pos); @@ -185,11 +231,43 @@ void SpiMaster::Sleep() { nrf_gpio_cfg_default(params.pinSCK); nrf_gpio_cfg_default(params.pinMOSI); nrf_gpio_cfg_default(params.pinMISO); - nrf_gpio_cfg_default(params.pinCSN); } void SpiMaster::Wakeup() { Init(); } +bool SpiMaster::WriteCmdAndBuffer(uint8_t pinCsn, uint8_t *cmd, size_t cmdSize, uint8_t *data, size_t dataSize) { + xSemaphoreTake(mutex, portMAX_DELAY); + + taskToNotify = nullptr; + + this->pinCsn = pinCsn; + DisableWorkaroundForFtpan58(spiBaseAddress, 0,0); + spiBaseAddress->INTENCLR = (1<<6); + spiBaseAddress->INTENCLR = (1<<1); + spiBaseAddress->INTENCLR = (1<<19); + + nrf_gpio_pin_clear(this->pinCsn); + + + currentBufferAddr = 0; + currentBufferSize = 0; + + PrepareTx((uint32_t)cmd, cmdSize); + spiBaseAddress->TASKS_START = 1; + while (spiBaseAddress->EVENTS_END == 0); + + PrepareTx((uint32_t)data, dataSize); + spiBaseAddress->TASKS_START = 1; + + while (spiBaseAddress->EVENTS_END == 0); + nrf_gpio_pin_set(this->pinCsn); + + xSemaphoreGive(mutex); + + return true; +} + + diff --git a/src/drivers/SpiMaster.h b/src/drivers/SpiMaster.h index 8a633b7f..88b37a35 100644 --- a/src/drivers/SpiMaster.h +++ b/src/drivers/SpiMaster.h @@ -5,6 +5,7 @@ #include #include #include +#include #include "BufferProvider.h" #include @@ -24,7 +25,6 @@ namespace Pinetime { uint8_t pinSCK; uint8_t pinMOSI; uint8_t pinMISO; - uint8_t pinCSN; }; SpiMaster(const SpiModule spi, const Parameters& params); @@ -34,7 +34,10 @@ namespace Pinetime { SpiMaster& operator=(SpiMaster&&) = delete; bool Init(); - bool Write(const uint8_t* data, size_t size); + bool Write(uint8_t pinCsn, const uint8_t* data, size_t size); + bool Read(uint8_t pinCsn, uint8_t* cmd, size_t cmdSize, uint8_t *data, size_t dataSize); + + bool WriteCmdAndBuffer(uint8_t pinCsn, uint8_t* cmd, size_t cmdSize, uint8_t *data, size_t dataSize); void OnStartedEvent(); void OnEndEvent(); @@ -46,6 +49,7 @@ namespace Pinetime { void SetupWorkaroundForFtpan58(NRF_SPIM_Type *spim, uint32_t ppi_channel, uint32_t gpiote_channel); void DisableWorkaroundForFtpan58(NRF_SPIM_Type *spim, uint32_t ppi_channel, uint32_t gpiote_channel); void PrepareTx(const volatile uint32_t bufferAddress, const volatile size_t size); + void PrepareRx(const volatile uint32_t cmdAddress, const volatile size_t cmdSize, const volatile uint32_t bufferAddress, const volatile size_t size); NRF_SPIM_Type * spiBaseAddress; uint8_t pinCsn; diff --git a/src/drivers/SpiNorFlash.cpp b/src/drivers/SpiNorFlash.cpp new file mode 100644 index 00000000..8fbb53a1 --- /dev/null +++ b/src/drivers/SpiNorFlash.cpp @@ -0,0 +1,124 @@ +#include +#include +#include +#include "SpiNorFlash.h" +#include "Spi.h" + +using namespace Pinetime::Drivers; + +SpiNorFlash::SpiNorFlash(Spi& spi) : spi{spi} { + +} + +void SpiNorFlash::Init() { + auto id = ReadIdentificaion(); + NRF_LOG_INFO("[SPI FLASH] Manufacturer : %d, Memory type : %d, memory density : %d", id.manufacturer, id.type, id.density); +} + +void SpiNorFlash::Uninit() { + +} + +void SpiNorFlash::Sleep() { + +} + +void SpiNorFlash::Wakeup() { + +} + +SpiNorFlash::Identification SpiNorFlash::ReadIdentificaion() { + auto cmd = static_cast(Commands::ReadIdentification); + Identification identification; + spi.Read(&cmd, 1, reinterpret_cast(&identification), sizeof(Identification)); + return identification; +} + +uint8_t SpiNorFlash::ReadStatusRegister() { + auto cmd = static_cast(Commands::ReadStatusRegister); + uint8_t status; + spi.Read(&cmd, sizeof(cmd), &status, sizeof(uint8_t)); + return status; +} + +bool SpiNorFlash::WriteInProgress() { + return (ReadStatusRegister() & 0x01u) == 0x01u; +} + +bool SpiNorFlash::WriteEnabled() { + return (ReadStatusRegister() & 0x02u) == 0x02u; +} + +uint8_t SpiNorFlash::ReadConfigurationRegister() { + auto cmd = static_cast(Commands::ReadConfigurationRegister); + uint8_t status; + spi.Read(&cmd, sizeof(cmd), &status, sizeof(uint8_t)); + return status; +} + +void SpiNorFlash::Read(uint32_t address, uint8_t *buffer, size_t size) { + static constexpr uint8_t cmdSize = 4; + uint8_t cmd[cmdSize] = { static_cast(Commands::Read), (uint8_t)(address >> 16U), (uint8_t)(address >> 8U), + (uint8_t)address }; + spi.Read(reinterpret_cast(&cmd), cmdSize, buffer, size); +} + +void SpiNorFlash::WriteEnable() { + auto cmd = static_cast(Commands::WriteEnable); + spi.Read(&cmd, sizeof(cmd), nullptr, 0); +} + +void SpiNorFlash::SectorErase(uint32_t sectorAddress) { + static constexpr uint8_t cmdSize = 4; + uint8_t cmd[cmdSize] = { static_cast(Commands::SectorErase), (uint8_t)(sectorAddress >> 16U), (uint8_t)(sectorAddress >> 8U), + (uint8_t)sectorAddress }; + + WriteEnable(); + while(!WriteEnabled()) vTaskDelay(1); + + spi.Read(reinterpret_cast(&cmd), cmdSize, nullptr, 0); + + while(WriteInProgress()) vTaskDelay(1); +} + +uint8_t SpiNorFlash::ReadSecurityRegister() { + auto cmd = static_cast(Commands::ReadSecurityRegister); + uint8_t status; + spi.Read(&cmd, sizeof(cmd), &status, sizeof(uint8_t)); + return status; +} + +bool SpiNorFlash::ProgramFailed() { + return (ReadSecurityRegister() & 0x20u) == 0x20u; +} + +bool SpiNorFlash::EraseFailed() { + return (ReadSecurityRegister() & 0x40u) == 0x40u; +} + +void SpiNorFlash::Write(uint32_t address, uint8_t *buffer, size_t size) { + static constexpr uint8_t cmdSize = 4; + + size_t len = size; + uint32_t addr = address; + uint8_t* b = buffer; + while(len > 0) { + uint32_t pageLimit = (addr & ~(pageSize - 1u)) + pageSize; + uint32_t toWrite = pageLimit - addr > len ? len : pageLimit - addr; + + uint8_t cmd[cmdSize] = { static_cast(Commands::PageProgram), (uint8_t)(addr >> 16U), (uint8_t)(addr >> 8U), + (uint8_t)addr }; + + WriteEnable(); + while(!WriteEnabled()) vTaskDelay(1); + + spi.WriteCmdAndBuffer(cmd, cmdSize, b, toWrite); + + while(WriteInProgress()) vTaskDelay(1); + + addr += toWrite; + b += toWrite; + len -= toWrite; + } + +} diff --git a/src/drivers/SpiNorFlash.h b/src/drivers/SpiNorFlash.h new file mode 100644 index 00000000..b5f19202 --- /dev/null +++ b/src/drivers/SpiNorFlash.h @@ -0,0 +1,60 @@ +#pragma once +#include + +namespace Pinetime { + namespace Drivers { + class Spi; + class SpiNorFlash { + public: + explicit SpiNorFlash(Spi& spi); + SpiNorFlash(const SpiNorFlash&) = delete; + SpiNorFlash& operator=(const SpiNorFlash&) = delete; + SpiNorFlash(SpiNorFlash&&) = delete; + SpiNorFlash& operator=(SpiNorFlash&&) = delete; + + typedef struct __attribute__((packed)) { + uint8_t manufacturer = 0; + uint8_t type = 0; + uint8_t density = 0; + } Identification; + + Identification ReadIdentificaion(); + uint8_t ReadStatusRegister(); + bool WriteInProgress(); + bool WriteEnabled(); + uint8_t ReadConfigurationRegister(); + void Read(uint32_t address, uint8_t* buffer, size_t size); + void Write(uint32_t address, uint8_t *buffer, size_t size); + void WriteEnable(); + void SectorErase(uint32_t sectorAddress); + uint8_t ReadSecurityRegister(); + bool ProgramFailed(); + bool EraseFailed(); + + + void Init(); + void Uninit(); + + + void Sleep(); + void Wakeup(); + private: + enum class Commands : uint8_t { + PageProgram = 0x02, + Read = 0x03, + ReadStatusRegister = 0x05, + WriteEnable = 0x06, + ReadConfigurationRegister = 0x15, + SectorErase = 0x20, + ReadSecurityRegister = 0x2B, + ReadIdentification = 0x9F, + }; + static constexpr uint16_t pageSize = 256; + + Spi& spi; + + }; + } +} + + diff --git a/src/drivers/St7789.cpp b/src/drivers/St7789.cpp index db7c27e2..09269afd 100644 --- a/src/drivers/St7789.cpp +++ b/src/drivers/St7789.cpp @@ -1,16 +1,17 @@ #include #include #include "St7789.h" -#include "SpiMaster.h" +#include "Spi.h" using namespace Pinetime::Drivers; -St7789::St7789(SpiMaster &spiMaster, uint8_t pinDataCommand) : spi{spiMaster}, pinDataCommand{pinDataCommand} { +St7789::St7789(Spi &spi, uint8_t pinDataCommand) : spi{spi}, pinDataCommand{pinDataCommand} { } void St7789::Init() { + spi.Init(); nrf_gpio_cfg_output(pinDataCommand); nrf_gpio_cfg_output(26); nrf_gpio_pin_set(26); @@ -173,11 +174,11 @@ void St7789::HardwareReset() { void St7789::Sleep() { SleepIn(); nrf_gpio_cfg_default(pinDataCommand); - spi.Sleep(); +// spi.Sleep(); // TODO sleep SPI } void St7789::Wakeup() { - spi.Wakeup(); +// spi.Wakeup(); // TODO wake up SPI nrf_gpio_cfg_output(pinDataCommand); // TODO why do we need to reset the controller? diff --git a/src/drivers/St7789.h b/src/drivers/St7789.h index 3721b184..0b94cf24 100644 --- a/src/drivers/St7789.h +++ b/src/drivers/St7789.h @@ -3,10 +3,10 @@ namespace Pinetime { namespace Drivers { - class SpiMaster; + class Spi; class St7789 { public: - explicit St7789(SpiMaster& spiMaster, uint8_t pinDataCommand); + explicit St7789(Spi& spi, uint8_t pinDataCommand); St7789(const St7789&) = delete; St7789& operator=(const St7789&) = delete; St7789(St7789&&) = delete; @@ -29,7 +29,7 @@ namespace Pinetime { void Sleep(); void Wakeup(); private: - SpiMaster& spi; + Spi& spi; uint8_t pinDataCommand; uint8_t verticalScrollingStartAddress = 0; diff --git a/src/libs/mynewt-nimble/porting/nimble/include/logcfg/logcfg.h b/src/libs/mynewt-nimble/porting/nimble/include/logcfg/logcfg.h index 6119ecfa..983f419f 100644 --- a/src/libs/mynewt-nimble/porting/nimble/include/logcfg/logcfg.h +++ b/src/libs/mynewt-nimble/porting/nimble/include/logcfg/logcfg.h @@ -14,7 +14,7 @@ #define BLE_HS_LOG_INFO(...) NRF_LOG_INFO(__VA_ARGS__) #define BLE_HS_LOG_WARN(...) NRF_LOG_WARNING( __VA_ARGS__) #define BLE_HS_LOG_ERROR(...) NRF_LOG_ERROR(__VA_ARGS__) -#define BLE_HS_LOG_CRITICAL(...) MODLOG_CRITICAL(4, __VA_ARGS__) +#define BLE_HS_LOG_CRITICAL(...) NRF_LOG_ERROR(__VA_ARGS__) #define BLE_HS_LOG_DISABLED(...) MODLOG_DISABLED(4, __VA_ARGS__) #endif #if 0 diff --git a/src/libs/mynewt-nimble/porting/nimble/include/syscfg/syscfg.h b/src/libs/mynewt-nimble/porting/nimble/include/syscfg/syscfg.h index d4913caf..d06056eb 100644 --- a/src/libs/mynewt-nimble/porting/nimble/include/syscfg/syscfg.h +++ b/src/libs/mynewt-nimble/porting/nimble/include/syscfg/syscfg.h @@ -460,7 +460,7 @@ /* Overridden by @apache-mynewt-nimble/targets/riot (defined by @apache-mynewt-core/kernel/os) */ #ifndef MYNEWT_VAL_MSYS_1_BLOCK_COUNT -#define MYNEWT_VAL_MSYS_1_BLOCK_COUNT (5) +#define MYNEWT_VAL_MSYS_1_BLOCK_COUNT (12) #endif /* Overridden by @apache-mynewt-nimble/targets/riot (defined by @apache-mynewt-core/kernel/os) */ diff --git a/src/main.cpp b/src/main.cpp index 797495bb..869e4246 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -12,6 +12,7 @@ #include "Components/Ble/BleController.h" #include #include +#include #include #include #include @@ -38,7 +39,8 @@ Pinetime::Logging::DummyLogger logger; static constexpr uint8_t pinSpiSck = 2; static constexpr uint8_t pinSpiMosi = 3; static constexpr uint8_t pinSpiMiso = 4; -static constexpr uint8_t pinSpiCsn = 25; +static constexpr uint8_t pinSpiFlashCsn = 5; +static constexpr uint8_t pinLcdCsn = 25; static constexpr uint8_t pinLcdDataCommand = 18; Pinetime::Drivers::SpiMaster spi{Pinetime::Drivers::SpiMaster::SpiModule::SPI0, { @@ -47,11 +49,15 @@ Pinetime::Drivers::SpiMaster spi{Pinetime::Drivers::SpiMaster::SpiModule::SPI0, Pinetime::Drivers::SpiMaster::Frequencies::Freq8Mhz, pinSpiSck, pinSpiMosi, - pinSpiMiso, - pinSpiCsn + pinSpiMiso } }; -Pinetime::Drivers::St7789 lcd {spi, pinLcdDataCommand}; + +Pinetime::Drivers::Spi lcdSpi {spi, pinLcdCsn}; +Pinetime::Drivers::St7789 lcd {lcdSpi, pinLcdDataCommand}; + +Pinetime::Drivers::Spi flashSpi {spi, pinSpiFlashCsn}; +Pinetime::Drivers::SpiNorFlash spiNorFlash {flashSpi}; Pinetime::Drivers::Cst816S touchPanel {}; Pinetime::Components::LittleVgl lvgl {lcd, touchPanel}; @@ -206,7 +212,7 @@ int main(void) { debounceTimer = xTimerCreate ("debounceTimer", 200, pdFALSE, (void *) 0, DebounceTimerCallback); - systemTask.reset(new Pinetime::System::SystemTask(spi, lcd, touchPanel, lvgl, batteryController, bleController, + systemTask.reset(new Pinetime::System::SystemTask(spi, lcd, spiNorFlash, touchPanel, lvgl, batteryController, bleController, dateTimeController, notificationManager)); systemTask->Start(); nimble_port_init();