Compare commits

..

22 Commits

Author SHA1 Message Date
Olivier 'reivilibre' 1acd5e568c WORKING. Will use for trip 2022-06-17 22:13:28 +01:00
Olivier 'reivilibre' 5911e3093a Add .gitignore 2022-06-15 21:09:39 +01:00
Olivier 'reivilibre' f2ce51c173 Update submodules for personal fork 2022-06-15 21:00:53 +01:00
Reinhold Gschweicher 04c923bd82 LittleVgl: implement screen transitions like on PineTime
Move lvgl display init from main.cpp into LittleVgl.cpp to be closer to
InfiniTime, where display initialization is also done in LitteVgl.cpp.

Enable the original FlushDisplay code to get the screen
transition animations like on the real PineTime.

Also slow down the rendering, to actually be able to see the screen
flushing.

For the Up and Down screen transitions implement the screen movement.
When moving Down, move the the whole screen content down, and then draw
the new part of the new screen at the top of the display. Repeat until
screen transition is finished.

Fixes: https://github.com/InfiniTimeOrg/InfiniSim/issues/13
2022-06-13 23:48:30 +02:00
Reinhold Gschweicher 53d765bbd8 Update LittleVgl with upstream changes 2022-06-13 23:48:30 +02:00
NeroBurner ce22ba29c9
Remove MotorController, use nrf_gpio Motor pin, apptimer repeat (#34)
Remove the custom MotorController class and use the upstream
MotorController instead.

To enable this move add custom code in nrf_gpio to handle the Motor pin
instead of the custom motor_is_running member variable.

Also implement the repeating app_timer type used in the upstream
MotorController.
2022-06-05 22:03:53 +02:00
NeroBurner f64e1aab80
Fix notification segfault (#36)
Need to loop the notification_idx after half the notification_messages
size since the change of splitting the subject and message body into two
strings.
2022-06-05 22:00:50 +02:00
NeroBurner 6313a7498b
CI: install lv_font_conv binary (#35)
We consider in CMake if we add the font subdir, but we didn't install
the `lv_font_conv` binary.
2022-06-05 21:58:12 +02:00
Reinhold Gschweicher 068c5a823e main: Fix 'N' clear new notification flag documentation
The documentation for the 'N' key wrongly stated it clears all
notifications. But in the code we see that just the new notification
flag is reset.
2022-05-16 23:45:34 +02:00
Reinhold Gschweicher 5f1dc192ac Update submodule with fonts target fix
Update InfiniTime to contain fix from
https://github.com/InfiniTimeOrg/InfiniTime/pull/1131
2022-05-16 19:27:37 +02:00
Reinhold Gschweicher 9427d4ddb1 SpiNorFlash: check Read/Write for out of bounds
Just to be extra pedantic and to get an error while developing on
InfiniSim. Because on PC we can easily add the debugger, on the device
we can, but it is a bit more involved.
2022-05-15 22:47:41 +02:00
JF 644431cbc4
Add support for SpiNorFlash and FS (#30)
The external SPI flash is implemented as a 4MB on the local filesystem.
This allows the FS (littleFS) and settings to work properly.

Remove the simulated `FS.h` and `FS.cpp`, because we can now use
the files from InfiniTime directly as the heavy lifting is done in the simulated
`SpiNorFlash.h` and cpp files.

`SpiNorFlash.h` provides read and write functions with `uint8_t` buffer, but
`fs::fstream` expects `char` buffer. Use `reinterpret_cast` and check if by
any chance the `char` type on a platform is implemented with more
than one byte. Then the `reinterpret_cast<char *>(buffer)` would change the
meaning of the `size` parameter, which could lead to garbage data.

Co-authored-by: Reinhold Gschweicher <pyro4hell@gmail.com>
2022-05-15 22:15:19 +02:00
Riku Isokoski b1fbae36f9
Build widgets in widgets folder (#31)
https://github.com/InfiniTimeOrg/InfiniTime/pull/1136 adds a new folder `widgets`
for source files.If it gets merged, this folder needs to be added to the build.

Add the source files to InfiniSim if the new folder exists
2022-05-15 21:13:24 +02:00
Reinhold Gschweicher f0c6ef9cd6 Use infinitime_fonts target if fonts/CMakeLists.txt exists
Since https://github.com/InfiniTimeOrg/InfiniTime/pull/1097 the fonts
are generated. Support this new way of using fonts.

When InfiniTime isn't a subdirectory of InfiniSim we need to provide
the binary dir when using `add_subdirectory()`. Use `fonts`.

Update README with instructions to install `lv_font_conv`.
2022-05-14 21:31:26 +02:00
Reinhold Gschweicher d9d729d40f FS: fix FS::Stat function by looking at local dir instead of `/`
Like in `FileOpen()` interpret filenames starting with `/` as paths
relative to the current working directory by removing the first `/` from
the path.

Fixes: https://github.com/InfiniTimeOrg/InfiniSim/issues/23
2022-04-24 23:23:45 +02:00
NeroBurner de8d0d6b6f SpiNorFlash: code style fix 2022-04-24 19:13:56 +02:00
Reinhold Gschweicher 7194e3e38e SpiNorFlash: use C++ style struct in C++ only header
`SpiNorFlash.h` is a C++ header, but the `Identification` struct is
created in a C style using `typedef struct`. Clang issues a warining
about this discrepancy:

```
In file included from /home/nero/repos/pinetime/InfiniSim/InfiniTime/src/systemtask/SystemTask.cpp:13:
/home/nero/repos/pinetime/InfiniSim/sim/drivers/SpiNorFlash.h:16:21: warning: anonymous non-C-compatible type given name for linkage purposes by typedef declaration; add a tag name here [-Wnon-c-typedef-for-linkage]
      typedef struct __attribute__((packed)) {
                    ^
                     Identification
/home/nero/repos/pinetime/InfiniSim/sim/drivers/SpiNorFlash.h:17:9: note: type is not C-compatible due to this default member initializer
        uint8_t manufacturer = 0;
        ^~~~~~~~~~~~~~~~~~~~
/home/nero/repos/pinetime/InfiniSim/sim/drivers/SpiNorFlash.h:20:9: note: type is given name 'Identification' for linkage purposes by this typedef declaration
      } Identification;
        ^
1 warning generated.
```

The easy fix is to use a C++ style struct.

Same fix as in: https://github.com/InfiniTimeOrg/InfiniTime/pull/1046
2022-04-24 19:13:56 +02:00
Reinhold Gschweicher 7110fa0034 cmake: create QCBOR target and link to InfiniSim 2022-04-08 11:26:30 +02:00
Reinhold Gschweicher 9f36b7886d FS: copy lfs_info and implement FS::Stat()
Implement the subset of the functionality to be usable to check if the
file exists or not and check the size of the file.
2022-04-04 21:22:57 +02:00
Reinhold Gschweicher d4e81ca177 Fix libpng submodule usage
The current usage used the system `png.h` header instead of the intended
submodule header. This may break if the system header isn't at
`libpng16/png.h`.

Fix the include header to get the genreated include files and the
`png.h` from the submodule as described in
https://github.com/glennrp/libpng/issues/342#issuecomment-864589614

Fixes: https://github.com/InfiniTimeOrg/InfiniSim/issues/19
2022-04-03 21:35:09 +02:00
Wivik 8507c601fb doc: add fedora required packages 2022-04-02 23:12:02 +02:00
Reinhold Gschweicher 80ef18cce0 Update InfiniTime to 1.9.0 development branch 2022-04-02 17:56:09 +02:00
23 changed files with 653 additions and 992 deletions

View File

@ -1,88 +0,0 @@
name: Emscripten build InfiniSim LVGL Simulator
# When to run this Workflow...
on:
# Run on all branches
push:
branches: []
# Also run this Workflow when a Pull Request is created or updated in the "main" and "develop" Branch
pull_request:
branches: [ main, develop ]
jobs:
emscripten:
runs-on: ubuntu-latest
steps:
#########################################################################################
# Checkout
- name: Checkout source files
uses: actions/checkout@v2
with:
submodules: recursive
#########################################################################################
# Download and Install Dependencies
- name: Setup emsdk
uses: mymindstorm/setup-emsdk@v11
with:
# Make sure to set a version number!
version: 3.1.8
# This is the name of the cache folder.
# The cache folder will be placed in the build directory,
# so make sure it doesn't conflict with anything!
actions-cache-folder: 'emsdk-cache'
- name: Tell emsdk to use SDL with pthread proxy fix
run: sed -i -e "s/^TAG =.*/TAG = 'ea7d5307acfb1daf9af6104b60b75114b15bcd27'/" -e "s/HASH =.*/HASH = 'b7d58124f0d1145f23338abfdb6aa07855ac74ed80b3a5b613d23b4930c84d04d1794a62aab2ca2680ba369128ee2028ea51236fab4aaf70556036172fa59e6a'/" ${EMSDK}/upstream/emscripten/tools/ports/sdl2.py
- name: Verify
run: emcc -v
- name: Install cmake
uses: lukka/get-cmake@v3.18.3
#########################################################################################
# CMake
- name: CMake
run: |
emcmake cmake -S . -B build_em -DCMAKE_BUILD_TYPE=Release
#########################################################################################
# Build and Upload simulator wasm files
- name: Build simulator executable
run: |
cmake --build build_em
- name: Upload simulator executable
uses: actions/upload-artifact@v3
with:
name: infinisim_emscripten
path: |
build_em/infinisim.html
build_em/infinisim.js
build_em/infinisim.wasm
# using https://github.com/gzuidhof/coi-serviceworker to get Cross-origin isolation on gh-pages
# otherwise the error 'Uncaught ReferenceError: SharedArrayBuffer is not defined' will be shown
- name: Prepare Github Page
shell: bash
run: |
mkdir emscripten
curl -o emscripten/coi-serviceworker.js https://raw.githubusercontent.com/gzuidhof/coi-serviceworker/ed2fbe98a222b8e0991a839f504137408d56d2e6/coi-serviceworker.js
sed -i -e 's#</title>#</title><script src="./coi-serviceworker.js"></script>#' build_em/infinisim.html
mv build_em/infinisim.html \
build_em/infinisim.js \
build_em/infinisim.wasm \
emscripten/.
- name: Deploy Github Page
uses: JamesIves/github-pages-deploy-action@v4.2.5
with:
branch: gh-pages
folder: emscripten

View File

@ -34,6 +34,10 @@ jobs:
sudo apt-get update sudo apt-get update
sudo apt-get -y install libsdl2-dev sudo apt-get -y install libsdl2-dev
- name: Install lv_font_conv
run:
npm i -g lv_font_conv@1.5.2
######################################################################################### #########################################################################################
# Checkout # Checkout

5
.gitignore vendored
View File

@ -49,3 +49,8 @@ Testing/Temporary/
src/nRF5_SDK_15.3.0_59ac345 src/nRF5_SDK_15.3.0_59ac345
src/arm-none-eabi src/arm-none-eabi
node_modules
package.json
package-lock.json
spiNorFlash.raw

6
.gitmodules vendored
View File

@ -1,9 +1,9 @@
[submodule "InfiniTime"] [submodule "InfiniTime"]
path = InfiniTime path = InfiniTime
url = ../../NeroBurner/InfiniTime.git url = gitea@bics.ga:rei-forks/InfiniTime.git
[submodule "lv_drivers"] [submodule "lv_drivers"]
path = lv_drivers path = lv_drivers
url = ../../lvgl/lv_drivers.git url = https://github.com/lvgl/lv_drivers.git
[submodule "libpng"] [submodule "libpng"]
path = libpng path = libpng
url = ../../glennrp/libpng.git url = https://github.com/glennrp/libpng.git

View File

@ -80,6 +80,8 @@ target_sources(infinisim PUBLIC
sim/components/ble/MusicService.cpp sim/components/ble/MusicService.cpp
sim/components/ble/NavigationService.h sim/components/ble/NavigationService.h
sim/components/ble/NavigationService.cpp sim/components/ble/NavigationService.cpp
sim/components/ble/AgendaService.h
sim/components/ble/AgendaService.cpp
sim/components/ble/NimbleController.h sim/components/ble/NimbleController.h
sim/components/ble/NimbleController.cpp sim/components/ble/NimbleController.cpp
sim/components/ble/weather/WeatherService.h sim/components/ble/weather/WeatherService.h
@ -88,14 +90,10 @@ target_sources(infinisim PUBLIC
sim/components/brightness/BrightnessController.cpp sim/components/brightness/BrightnessController.cpp
sim/components/firmwarevalidator/FirmwareValidator.h sim/components/firmwarevalidator/FirmwareValidator.h
sim/components/firmwarevalidator/FirmwareValidator.cpp sim/components/firmwarevalidator/FirmwareValidator.cpp
sim/components/fs/FS.h
sim/components/fs/FS.cpp
sim/components/heartrate/HeartRateController.h sim/components/heartrate/HeartRateController.h
sim/components/heartrate/HeartRateController.cpp sim/components/heartrate/HeartRateController.cpp
sim/components/motion/MotionController.h sim/components/motion/MotionController.h
sim/components/motion/MotionController.cpp sim/components/motion/MotionController.cpp
sim/components/motor/MotorController.h
sim/components/motor/MotorController.cpp
sim/drivers/Watchdog.h sim/drivers/Watchdog.h
sim/drivers/Watchdog.cpp sim/drivers/Watchdog.cpp
sim/drivers/Bma421.h sim/drivers/Bma421.h
@ -169,12 +167,25 @@ file(GLOB InfiniTime_FONTS
file(GLOB InfiniTime_ICONS file(GLOB InfiniTime_ICONS
"${InfiniTime_DIR}/src/displayapp/icons/*.c" "${InfiniTime_DIR}/src/displayapp/icons/*.c"
) )
file(GLOB InfiniTime_WIDGETS
"${InfiniTime_DIR}/src/displayapp/widgets/*.cpp"
"${InfiniTime_DIR}/src/displayapp/widgets/*.h"
)
set(LITTLEFS_SRC
${InfiniTime_DIR}/src/libs/littlefs/lfs_util.h
${InfiniTime_DIR}/src/libs/littlefs/lfs.h
${InfiniTime_DIR}/src/libs/littlefs/lfs_util.c
${InfiniTime_DIR}/src/libs/littlefs/lfs.c
)
target_sources(infinisim PUBLIC ${InfiniTime_SCREENS}) target_sources(infinisim PUBLIC ${InfiniTime_SCREENS})
target_sources(infinisim PUBLIC ${InfiniTime_FONTS}) target_sources(infinisim PUBLIC ${InfiniTime_FONTS})
target_sources(infinisim PUBLIC ${InfiniTime_ICONS}) target_sources(infinisim PUBLIC ${InfiniTime_ICONS})
target_sources(infinisim PUBLIC ${InfiniTime_WIDGETS})
target_sources(infinisim PUBLIC ${LITTLEFS_SRC})
# add files directly from InfiniTime sources # add files directly from InfiniTime sources
target_include_directories(infinisim PRIVATE "${InfiniTime_DIR}/src") target_include_directories(infinisim PRIVATE "${InfiniTime_DIR}/src")
target_include_directories(infinisim PRIVATE "${InfiniTime_DIR}/src/libs/littlefs")
target_sources(infinisim PUBLIC target_sources(infinisim PUBLIC
${InfiniTime_DIR}/src/BootloaderVersion.h ${InfiniTime_DIR}/src/BootloaderVersion.h
${InfiniTime_DIR}/src/BootloaderVersion.cpp ${InfiniTime_DIR}/src/BootloaderVersion.cpp
@ -197,6 +208,10 @@ target_sources(infinisim PUBLIC
${InfiniTime_DIR}/src/components/settings/Settings.cpp ${InfiniTime_DIR}/src/components/settings/Settings.cpp
${InfiniTime_DIR}/src/components/ble/NotificationManager.h ${InfiniTime_DIR}/src/components/ble/NotificationManager.h
${InfiniTime_DIR}/src/components/ble/NotificationManager.cpp ${InfiniTime_DIR}/src/components/ble/NotificationManager.cpp
${InfiniTime_DIR}/src/components/fs/FS.h
${InfiniTime_DIR}/src/components/fs/FS.cpp
${InfiniTime_DIR}/src/components/motor/MotorController.h
${InfiniTime_DIR}/src/components/motor/MotorController.cpp
${InfiniTime_DIR}/src/components/timer/TimerController.h ${InfiniTime_DIR}/src/components/timer/TimerController.h
${InfiniTime_DIR}/src/components/timer/TimerController.cpp ${InfiniTime_DIR}/src/components/timer/TimerController.cpp
${InfiniTime_DIR}/src/drivers/PinMap.h ${InfiniTime_DIR}/src/drivers/PinMap.h
@ -213,41 +228,47 @@ target_sources(infinisim PUBLIC
) )
# QCBOR
add_library(QCBOR STATIC
${InfiniTime_DIR}/src/libs/QCBOR/src/ieee754.c
${InfiniTime_DIR}/src/libs/QCBOR/src/qcbor_decode.c
${InfiniTime_DIR}/src/libs/QCBOR/src/qcbor_encode.c
${InfiniTime_DIR}/src/libs/QCBOR/src/qcbor_err_to_str.c
${InfiniTime_DIR}/src/libs/QCBOR/src/UsefulBuf.c)
target_include_directories(QCBOR SYSTEM PUBLIC ${InfiniTime_DIR}/src/libs/QCBOR/inc)
# This is required with the current configuration
target_compile_definitions(QCBOR PUBLIC QCBOR_DISABLE_FLOAT_HW_USE)
# These are for space-saving
target_compile_definitions(QCBOR PUBLIC QCBOR_DISABLE_PREFERRED_FLOAT)
target_compile_definitions(QCBOR PUBLIC QCBOR_DISABLE_EXP_AND_MANTISSA)
target_compile_definitions(QCBOR PUBLIC QCBOR_DISABLE_INDEFINITE_LENGTH_STRINGS)
#target_compile_definitions(QCBOR PUBLIC QCBOR_DISABLE_INDEFINITE_LENGTH_ARRAYS)
target_compile_definitions(QCBOR PUBLIC QCBOR_DISABLE_UNCOMMON_TAGS)
target_compile_definitions(QCBOR PUBLIC USEFULBUF_CONFIG_LITTLE_ENDIAN)
set_target_properties(QCBOR PROPERTIES LINKER_LANGUAGE C)
#target_compile_options(QCBOR PRIVATE
# $<$<AND:$<COMPILE_LANGUAGE:C>,$<CONFIG:DEBUG>>: ${COMMON_FLAGS} -O0 -g3>
# $<$<AND:$<COMPILE_LANGUAGE:C>,$<CONFIG:RELEASE>>: ${COMMON_FLAGS} -O3>
# $<$<COMPILE_LANGUAGE:ASM>: -MP -MD -x assembler-with-cpp>
# )
target_link_libraries(infinisim PRIVATE QCBOR)
if(EXISTS ${InfiniTime_DIR}/src/displayapp/fonts/CMakeLists.txt)
# available since https://github.com/InfiniTimeOrg/InfiniTime/pull/1097
message(STATUS "add subdirectory ${InfiniTime_DIR}/src/displayapp/fonts for 'infinitime_fonts' target")
add_subdirectory(${InfiniTime_DIR}/src/displayapp/fonts fonts)
target_link_libraries(infinisim PRIVATE infinitime_fonts)
endif()
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
# Special case for SDL2 dependency, goal is to find a config that exports SDL2::SDL2 target # Special case for SDL2 dependency, goal is to find a config that exports SDL2::SDL2 target
# libsdl2-dev has a `sdl2-config.cmake` that doesn't export this, but vcpkg does.. # libsdl2-dev has a `sdl2-config.cmake` that doesn't export this, but vcpkg does..
if(${CMAKE_SYSTEM_NAME} MATCHES "Emscripten")
# emcmake cmake -S . -B _build_em
set(USE_FLAGS "-s USE_PTHREADS=1 -pthread")
set(USE_FLAGS "${USE_FLAGS} -s USE_SDL=2")
set(USE_FLAGS "${USE_FLAGS} -fwasm-exceptions")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${USE_FLAGS}")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${USE_FLAGS}")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${USE_FLAGS}")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s PTHREAD_POOL_SIZE=4 -s PROXY_TO_PTHREAD=1")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s INITIAL_MEMORY=30736384") # 30MB memory
#set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s ALLOW_MEMORY_GROWTH=1")
#set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s ABORTING_MALLOC=0")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s EXPORTED_RUNTIME_METHODS=[\"printErr\"]")
set(CMAKE_EXECUTABLE_SUFFIX .html)
target_include_directories(infinisim PRIVATE ${SDL2_INCLUDE_DIRS})
target_link_libraries(infinisim PRIVATE ${SDL2_LIBRARIES})
#set_target_properties(infinisim PROPERTIES LINK_FLAGS "--shell-file ${PROJECT_SOURCE_DIR}/lvgl_shell.html")
else()
find_package(SDL2 CONFIG QUIET) find_package(SDL2 CONFIG QUIET)
if(NOT TARGET SDL2::SDL2) if(NOT TARGET SDL2::SDL2)
find_package(SDL2 MODULE REQUIRED) find_package(SDL2 MODULE REQUIRED)
endif() endif()
target_link_libraries(infinisim PRIVATE SDL2::SDL2) target_link_libraries(infinisim PRIVATE SDL2::SDL2)
option(WITH_PNG "Compile with libpng support to dump current screen as png" ON)
if(WITH_PNG)
target_compile_definitions(infinisim PRIVATE WITH_PNG)
add_subdirectory(libpng EXCLUDE_FROM_ALL)
target_link_libraries(infinisim PRIVATE png_static)
endif()
endif()
# Get the latest abbreviated commit hash of the working branch # Get the latest abbreviated commit hash of the working branch
execute_process( execute_process(
COMMAND ${GIT_EXECUTABLE} log -1 --format=%h COMMAND ${GIT_EXECUTABLE} log -1 --format=%h
@ -258,4 +279,13 @@ execute_process(
set(VERSION_EDIT_WARNING "// Do not edit this file, it is automatically generated by CMAKE!") set(VERSION_EDIT_WARNING "// Do not edit this file, it is automatically generated by CMAKE!")
configure_file("${InfiniTime_DIR}/src/Version.h.in" "${CMAKE_CURRENT_BINARY_DIR}/Version.h") configure_file("${InfiniTime_DIR}/src/Version.h.in" "${CMAKE_CURRENT_BINARY_DIR}/Version.h")
option(WITH_PNG "Compile with libpng support to dump current screen as png" ON)
if(WITH_PNG)
target_compile_definitions(infinisim PRIVATE WITH_PNG)
add_subdirectory(libpng EXCLUDE_FROM_ALL)
target_include_directories(infinisim PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}")
target_include_directories(infinisim PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/libpng")
target_link_libraries(infinisim PRIVATE png_static)
endif()
install(TARGETS infinisim DESTINATION bin) install(TARGETS infinisim DESTINATION bin)

@ -1 +1 @@
Subproject commit a1dac63672eee6f301d62cfa065f56ca8ea90266 Subproject commit e54d78a04b42264770cf25dc2bb2952c7618b764

View File

@ -14,17 +14,30 @@ For a history on how this simulator started and the challenges on its way visit
- CMake - CMake
- SDL2 (provides the simulator window, handles mouse and keyboard input) - SDL2 (provides the simulator window, handles mouse and keyboard input)
- Compiler (g++ or clang++) - Compiler (g++ or clang++)
- [lv_font_conv](https://github.com/lvgl/lv_font_conv#install-the-script) (for `font.c` generation since [InfiniTime#1097](https://github.com/InfiniTimeOrg/InfiniTime/pull/1097))
On Ubuntu/Debian install the following packages: On Ubuntu/Debian install the following packages:
```sh ```sh
sudo apt install -y cmake libsdl2-dev g++ sudo apt install -y cmake libsdl2-dev g++ npm
``` ```
On Arch Linux the following packages are needed: On Arch Linux the following packages are needed:
```sh ```sh
sudo pacman -S cmake sdl2 gcc sudo pacman -S cmake sdl2 gcc npm
```
On Fedora the following packages are needed:
```sh
sudo dnf install cmake SDL2-devel gcc
```
Then install the `lv_font_conv` executable to the source directory (will be installed at `node_modules/.bin/lv_font_conv`)
```sh
npm install lv_font_conv@1.5.2
``` ```
## Get the Sources ## Get the Sources
@ -83,7 +96,7 @@ Using the keyboard the following events can be triggered:
- `m` ... let motor run for 100 ms - `m` ... let motor run for 100 ms
- `M` ... let motor run for 255 ms - `M` ... let motor run for 255 ms
- `n` ... send notification - `n` ... send notification
- `N` ... clear all notifications - `N` ... clear new notification flag
- `b` ... connect Bluetooth - `b` ... connect Bluetooth
- `B` ... disconnect Bluetooth - `B` ... disconnect Bluetooth
- `v` ... increase battery voltage and percentage - `v` ... increase battery voltage and percentage

View File

@ -51,6 +51,7 @@
#include "displayapp/LittleVgl.h" #include "displayapp/LittleVgl.h"
#include <nrfx_gpiote.h> #include <nrfx_gpiote.h>
#include <hal/nrf_gpio.h>
#include <iostream> #include <iostream>
#include <typeinfo> #include <typeinfo>
@ -61,7 +62,7 @@
#include <date/date.h> #include <date/date.h>
#include <chrono> #include <chrono>
#if defined(WITH_PNG) #if defined(WITH_PNG)
#include <libpng16/png.h> #include <libpng/png.h>
#endif #endif
/********************* /*********************
@ -224,7 +225,7 @@ Pinetime::Drivers::Spi lcdSpi {spi, Pinetime::PinMap::SpiLcdCsn};
Pinetime::Drivers::St7789 lcd {lcdSpi, Pinetime::PinMap::LcdDataCommand}; Pinetime::Drivers::St7789 lcd {lcdSpi, Pinetime::PinMap::LcdDataCommand};
Pinetime::Drivers::Spi flashSpi {spi, Pinetime::PinMap::SpiFlashCsn}; Pinetime::Drivers::Spi flashSpi {spi, Pinetime::PinMap::SpiFlashCsn};
Pinetime::Drivers::SpiNorFlash spiNorFlash {flashSpi}; Pinetime::Drivers::SpiNorFlash spiNorFlash {"spiNorFlash.raw"};
// The TWI device should work @ up to 400Khz but there is a HW bug which prevent it from // The TWI device should work @ up to 400Khz but there is a HW bug which prevent it from
// respecting correct timings. According to erratas heet, this magic value makes it run // respecting correct timings. According to erratas heet, this magic value makes it run
@ -252,7 +253,7 @@ Pinetime::Controllers::Ble bleController;
Pinetime::Controllers::HeartRateController heartRateController; Pinetime::Controllers::HeartRateController heartRateController;
Pinetime::Applications::HeartRateTask heartRateApp(heartRateSensor, heartRateController); Pinetime::Applications::HeartRateTask heartRateApp(heartRateSensor, heartRateController);
Pinetime::Controllers::FS fs; // {spiNorFlash}; Pinetime::Controllers::FS fs {spiNorFlash};
Pinetime::Controllers::Settings settingsController {fs}; Pinetime::Controllers::Settings settingsController {fs};
Pinetime::Controllers::MotorController motorController {}; Pinetime::Controllers::MotorController motorController {};
@ -402,19 +403,11 @@ public:
} }
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0); SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0);
SDL_RenderClear(renderer); SDL_RenderClear(renderer);
{ // motorController.is_ringing
constexpr const int center_x = 15;
constexpr const int center_y = 15;
if (motorController.is_ringing) {
draw_circle_red(center_x, center_y, 15);
} else {
draw_circle_grey(center_x, center_y, 15);
}
}
{ // motorController.motor_is_running { // motorController.motor_is_running
constexpr const int center_x = 45; constexpr const int center_x = 45;
constexpr const int center_y = 15; constexpr const int center_y = 15;
if (motorController.motor_is_running) { bool motor_is_running = nrf_gpio_pin_read(Pinetime::PinMap::Motor);
if (motor_is_running) {
draw_circle_red(center_x, center_y, 15); draw_circle_red(center_x, center_y, 15);
} else { } else {
draw_circle_grey(center_x, center_y, 15); draw_circle_grey(center_x, center_y, 15);
@ -515,7 +508,7 @@ public:
notificationManager.Push(std::move(notif)); notificationManager.Push(std::move(notif));
// send next message the next time // send next message the next time
notification_idx++; notification_idx++;
if (notification_idx >= notification_messages.size()) { if (notification_idx >= notification_messages.size()/2) {
notification_idx = 0; notification_idx = 0;
} }
} }
@ -773,7 +766,7 @@ public:
private: private:
bool key_handled_r = false; // r ... enable ringing, R ... disable ringing bool key_handled_r = false; // r ... enable ringing, R ... disable ringing
bool key_handled_m = false; // m ... let motor run, M ... stop motor bool key_handled_m = false; // m ... let motor run, M ... stop motor
bool key_handled_n = false; // n ... send notification, N ... clear all notifications bool key_handled_n = false; // n ... send notification, N ... clear new notification flag
bool key_handled_b = false; // b ... connect Bluetooth, B ... disconnect Bluetooth bool key_handled_b = false; // b ... connect Bluetooth, B ... disconnect Bluetooth
bool key_handled_v = false; // battery Voltage and percentage, v ... increase, V ... decrease bool key_handled_v = false; // battery Voltage and percentage, v ... increase, V ... decrease
bool key_handled_c = false; // c ... charging, C ... not charging bool key_handled_c = false; // c ... charging, C ... not charging
@ -806,7 +799,7 @@ private:
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
// parse arguments // parse arguments
bool fw_status_window_visible = false; bool fw_status_window_visible = true;
bool arg_help = false; bool arg_help = false;
for (int i=1; i<argc; i++) for (int i=1; i<argc; i++)
{ {
@ -837,6 +830,8 @@ int main(int argc, char **argv)
/*Initialize the HAL (display, input devices, tick) for LVGL*/ /*Initialize the HAL (display, input devices, tick) for LVGL*/
hal_init(); hal_init();
fs.Init();
// initialize the core of our Simulator // initialize the core of our Simulator
Framework fw(fw_status_window_visible, 240,240); Framework fw(fw_status_window_visible, 240,240);
@ -868,21 +863,21 @@ static void hal_init(void)
SDL_CreateThread(tick_thread, "tick", NULL); SDL_CreateThread(tick_thread, "tick", NULL);
// use pinetime_theme // use pinetime_theme
lv_theme_t* th = lv_pinetime_theme_init( //lv_theme_t* th = lv_pinetime_theme_init(
LV_COLOR_WHITE, LV_COLOR_SILVER, 0, &jetbrains_mono_bold_20, &jetbrains_mono_bold_20, &jetbrains_mono_bold_20, &jetbrains_mono_bold_20); // LV_COLOR_WHITE, LV_COLOR_SILVER, 0, &jetbrains_mono_bold_20, &jetbrains_mono_bold_20, &jetbrains_mono_bold_20, &jetbrains_mono_bold_20);
lv_theme_set_act(th); //lv_theme_set_act(th);
/*Create a display buffer*/ ///*Create a display buffer*/
static lv_disp_buf_t disp_buf1; //static lv_disp_buf_t disp_buf1;
static lv_color_t buf1_1[LV_HOR_RES_MAX * 120]; //static lv_color_t buf1_1[LV_HOR_RES_MAX * 120];
lv_disp_buf_init(&disp_buf1, buf1_1, NULL, LV_HOR_RES_MAX * 120); //lv_disp_buf_init(&disp_buf1, buf1_1, NULL, LV_HOR_RES_MAX * 120);
/*Create a display*/ ///*Create a display*/
lv_disp_drv_t disp_drv; //lv_disp_drv_t disp_drv;
lv_disp_drv_init(&disp_drv); /*Basic initialization*/ //lv_disp_drv_init(&disp_drv); /*Basic initialization*/
disp_drv.buffer = &disp_buf1; //disp_drv.buffer = &disp_buf1;
disp_drv.flush_cb = monitor_flush; //disp_drv.flush_cb = monitor_flush;
lv_disp_drv_register(&disp_drv); //lv_disp_drv_register(&disp_drv);
/* Add the mouse as input device /* Add the mouse as input device
* Use the 'mouse' driver which reads the PC's mouse*/ * Use the 'mouse' driver which reads the PC's mouse*/

View File

@ -0,0 +1,138 @@
/* Copyright (C) 2021 Adam Pigg
This file is part of InfiniTime.
InfiniTime 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.
InfiniTime 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 <https://www.gnu.org/licenses/>.
*/
#include "components/ble/AgendaService.h"
#include "systemtask/SystemTask.h"
namespace {
/*
// 3fdaYYXX-e246-472e-b7e0-d2b0f3d9c17a
constexpr ble_uuid128_t CharUuid(uint8_t x, uint8_t y) {
return ble_uuid128_t {.u = {.type = BLE_UUID_TYPE_128},
.value = {0x7a, 0xc1, 0xd9, 0xf3, 0xb0, 0xd2, 0xe0, 0xb7, 0x2e, 0x47, 0x46, 0xe2, x, y, 0xda, 0x3f}};
}
// 00010000-78fc-48fe-8e23-433b3a1942d0
constexpr ble_uuid128_t BaseUuid() {
return CharUuid(0x00, 0x00);
}
constexpr ble_uuid128_t agendaUuid {BaseUuid()};
constexpr ble_uuid128_t agendaItemCharUuid {CharUuid(0x01, 0x00)};
int AgendaCallback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt, void* arg) {
auto agendaService = static_cast<Pinetime::Controllers::AgendaService*>(arg);
return agendaService->OnCommand(conn_handle, attr_handle, ctxt);
}
*/
} // namespace
Pinetime::Controllers::AgendaService::AgendaService(Pinetime::System::SystemTask& system) : m_system(system) {
/*
characteristicDefinition[0] = {.uuid = &agendaItemCharUuid.u,
.access_cb = AgendaCallback,
.arg = this,
.flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
characteristicDefinition[1] = {0};
serviceDefinition[0] = {.type = BLE_GATT_SVC_TYPE_PRIMARY, .uuid = &agendaUuid.u, .characteristics = characteristicDefinition};
serviceDefinition[1] = {0};
*/
m_agenda_times[0] = 1655327841;
m_agenda_pieces[0] = "Write AgendaService";
m_agenda_times[1] = 1655369999;
m_agenda_pieces[1] = "WTF C++ dates";
m_agenda_times[2] = 1655370699;
m_agenda_pieces[2] = "??? work ???";
m_agenda_times[3] = 1655380699;
m_agenda_pieces[3] = "??? stuff ???";
m_agenda_times[4] = 1655381699;
m_agenda_pieces[4] = "WTF C++ dates";
m_agenda_times[5] = 1655382699;
m_agenda_pieces[5] = "??? work ???";
m_agenda_times[6] = 1655493699;
m_agenda_pieces[6] = "??? stuff ???";
m_agenda_times[7] = 1655497999;
m_agenda_pieces[7] = "??? stuff ???";
}
void Pinetime::Controllers::AgendaService::Init() {
/*
int res = 0;
res = ble_gatts_count_cfg(serviceDefinition);
ASSERT(res == 0);
res = ble_gatts_add_svcs(serviceDefinition);
ASSERT(res == 0);
m_agenda_times[0] = 1655327841;
m_agenda_pieces[0] = "Write AgendaService";
*/
}
/*
int Pinetime::Controllers::AgendaService::OnCommand(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt) {
if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) {
size_t notifSize = OS_MBUF_PKTLEN(ctxt->om);
uint8_t data[notifSize + 1];
data[notifSize] = '\0';
os_mbuf_copydata(ctxt->om, 0, notifSize, data);
char* s = (char*) &data[0];
if (ble_uuid_cmp(ctxt->chr->uuid, &agendaItemCharUuid.u) == 0) {
if (notifSize >= 5) {
uint8_t itemIndex = (uint8_t) data[0];
uint32_t itemTime = ((uint32_t) data[1]) << 24 | ((uint32_t) data[2]) << 16 | ((uint32_t) data[3]) << 8 | ((uint32_t) data[4]);
char* sAgendaItem = (char*) &data[5];
if (itemIndex <= 15) { // don't allow a buffer overflow
m_agenda_times = itemTime;
// implicit std::string copy/conversion here:
m_agenda_pieces = sAgendaItem;
}
}
}
}
return 0;
}
*/
uint32_t Pinetime::Controllers::AgendaService::getAgendaTime(uint8_t index) {
if (index <= AGENDA_CAPACITY) {
return m_agenda_times[index];
} else {
return 0;
}
}
std::string Pinetime::Controllers::AgendaService::getAgendaPiece(uint8_t index) {
if (index <= AGENDA_CAPACITY) {
return m_agenda_pieces[index];
} else {
return 0;
}
}

View File

@ -0,0 +1,59 @@
/* Copyright (C) 2021 Adam Pigg
This file is part of InfiniTime.
InfiniTime 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.
InfiniTime 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 <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <cstdint>
#include <string>
// #define min // workaround: nimble's min/max macros conflict with libstdc++
// #define max
// #include <host/ble_gap.h>
// #include <host/ble_uuid.h>
// #undef max
// #undef min
namespace Pinetime {
namespace System {
class SystemTask;
}
namespace Controllers {
class AgendaService {
public:
static const uint8_t AGENDA_CAPACITY = 30;
explicit AgendaService(Pinetime::System::SystemTask& system);
void Init();
// int OnCommand(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt);
uint32_t getAgendaTime(uint8_t index);
std::string getAgendaPiece(uint8_t index);
private:
// struct ble_gatt_chr_def characteristicDefinition[2];
// struct ble_gatt_svc_def serviceDefinition[2];
uint32_t m_agenda_times[AGENDA_CAPACITY];
std::string m_agenda_pieces[AGENDA_CAPACITY];
Pinetime::System::SystemTask& m_system;
};
}
}

View File

@ -45,6 +45,7 @@ NimbleController::NimbleController(Pinetime::System::SystemTask& systemTask,
// currentTimeService {dateTimeController}, // currentTimeService {dateTimeController},
musicService {systemTask}, musicService {systemTask},
weatherService {systemTask, dateTimeController}, weatherService {systemTask, dateTimeController},
agendaService {systemTask},
navService {systemTask} { navService {systemTask} {
// batteryInformationService {batteryController}, // batteryInformationService {batteryController},
// immediateAlertService {systemTask, notificationManager}, // immediateAlertService {systemTask, notificationManager},
@ -92,6 +93,7 @@ void NimbleController::Init() {
musicService.Init(); musicService.Init();
weatherService.Init(); weatherService.Init();
navService.Init(); navService.Init();
agendaService.Init();
// anService.Init(); // anService.Init();
// dfuService.Init(); // dfuService.Init();
// batteryInformationService.Init(); // batteryInformationService.Init();

View File

@ -18,6 +18,7 @@
//#include "components/ble/ImmediateAlertService.h" //#include "components/ble/ImmediateAlertService.h"
#include "components/ble/MusicService.h" #include "components/ble/MusicService.h"
#include "components/ble/NavigationService.h" #include "components/ble/NavigationService.h"
#include "components/ble/AgendaService.h"
//#include "components/ble/ServiceDiscovery.h" //#include "components/ble/ServiceDiscovery.h"
//#include "components/ble/MotionService.h" //#include "components/ble/MotionService.h"
#include "components/ble/weather/WeatherService.h" #include "components/ble/weather/WeatherService.h"
@ -75,6 +76,9 @@ namespace Pinetime {
Pinetime::Controllers::NavigationService& navigation() { Pinetime::Controllers::NavigationService& navigation() {
return navService; return navService;
}; };
Pinetime::Controllers::AgendaService& agenda() {
return agendaService;
};
Pinetime::Controllers::AlertNotificationService& alertService() { Pinetime::Controllers::AlertNotificationService& alertService() {
return anService; return anService;
}; };
@ -113,6 +117,7 @@ namespace Pinetime {
MusicService musicService; MusicService musicService;
WeatherService weatherService; WeatherService weatherService;
NavigationService navService; NavigationService navService;
AgendaService agendaService;
// BatteryInformationService batteryInformationService; // BatteryInformationService batteryInformationService;
// ImmediateAlertService immediateAlertService; // ImmediateAlertService immediateAlertService;
// HeartRateService heartRateService; // HeartRateService heartRateService;

View File

@ -1,250 +0,0 @@
#include "FS.h"
#include <cassert>
#include <cstring>
#include <filesystem>
//#include <littlefs/lfs.h>
#include <lvgl/lvgl.h>
using namespace Pinetime::Controllers;
//FS::FS(Pinetime::Drivers::SpiNorFlash& driver)
// : flashDriver {driver},
// lfsConfig {
// .context = this,
// .read = SectorRead,
// .prog = SectorProg,
// .erase = SectorErase,
// .sync = SectorSync,
//
// .read_size = 16,
// .prog_size = 8,
// .block_size = blockSize,
// .block_count = size / blockSize,
// .block_cycles = 1000u,
//
// .cache_size = 16,
// .lookahead_size = 16,
//
// .name_max = 50,
// .attr_max = 50,
// } {
//}
void FS::Init() {
// // try mount
// int err = lfs_mount(&lfs, &lfsConfig);
//
// // reformat if we can't mount the filesystem
// // this should only happen on the first boot
// if (err != LFS_ERR_OK) {
// lfs_format(&lfs, &lfsConfig);
// err = lfs_mount(&lfs, &lfsConfig);
// if (err != LFS_ERR_OK) {
// return;
// }
// }
//
//#ifndef PINETIME_IS_RECOVERY
// VerifyResource();
// LVGLFileSystemInit();
//#endif
}
void FS::VerifyResource() {
// validate the resource metadata
resourcesValid = true;
}
int FS::FileOpen(lfs_file_t* file_p, const char* fileName, const int flags) {
// create the file in the current directory
const char *local_filename = fileName[0]=='/' ? &fileName[1] : fileName;
const char *mode;
bool flag_read = flags & LFS_O_RDONLY;
bool flag_write = flags & LFS_O_WRONLY;
bool flag_create = flags & LFS_O_CREAT;
if (flag_create) {
if (std::filesystem::exists(local_filename)) {
if (flag_read && flag_write) {
mode = "rb+";
} else if (flag_read) {
mode = "rb";
} else if (flag_write) {
mode = "wb";
} else {
assert(false); // not implemented
}
} else {
if (flag_read && flag_write) {
mode = "wb+";
} else if (flag_read) {
assert(false); // read only file not existing
mode = "rb";
} else if (flag_write) {
mode = "wb";
} else {
assert(false); // not implemented
}
}
} else {
if (std::filesystem::exists(local_filename)) {
if (flag_read && flag_write) {
mode = "rb+";
} else if (flag_read) {
mode = "rb";
} else if (flag_write) {
mode = "wb";
} else {
assert(false); // not implemented
}
} else {
return LFS_ERR_IO;
}
}
FILE *fptr = fopen(local_filename, mode);
if (fptr == nullptr) {
return LFS_ERR_BADF;
} else {
*file_p = fptr;
return LFS_ERR_OK;
}
//return lfs_file_open(&lfs, file_p, fileName, flags);
}
int FS::FileClose(lfs_file_t* file_p) {
return fclose(*file_p);
//return lfs_file_close(&lfs, file_p);
}
int FS::FileRead(lfs_file_t* file_p, uint8_t* buff, uint32_t size) {
return fread(buff, sizeof(uint8_t), size, *file_p);
//return lfs_file_read(&lfs, file_p, buff, size);
}
int FS::FileWrite(lfs_file_t* file_p, const uint8_t* buff, uint32_t size) {
return fwrite((void*)buff, sizeof(uint8_t), size, *file_p);
//return lfs_file_write(&lfs, file_p, buff, size);
}
int FS::FileSeek(lfs_file_t* file_p, uint32_t pos) {
return fseek(*file_p, pos, SEEK_SET);
//return lfs_file_seek(&lfs, file_p, pos, whence);
}
int FS::FileDelete(const char* fileName) {
return std::filesystem::remove(fileName);
//return lfs_remove(&lfs, fileName);
}
int FS::DirCreate(const char* path) {
return std::filesystem::create_directory(path);
//return lfs_mkdir(&lfs, path);
}
// Delete directory and all files inside
int FS::DirDelete(const char* path) {
return std::filesystem::remove_all(path);
//lfs_dir_t lfs_dir;
//lfs_info entryInfo;
//int err;
//err = lfs_dir_open(&lfs, &lfs_dir, path);
//if (err) {
// return err;
//}
//while (lfs_dir_read(&lfs, &lfs_dir, &entryInfo)) {
// lfs_remove(&lfs, entryInfo.name);
//}
//lfs_dir_close(&lfs, &lfs_dir);
//return LFS_ERR_OK;
}
/*
----------- Interface between littlefs and SpiNorFlash -----------
*/
//int FS::SectorSync(const struct lfs_config* c) {
// return 0;
//}
//
//int FS::SectorErase(const struct lfs_config* c, lfs_block_t block) {
// Pinetime::Controllers::FS& lfs = *(static_cast<Pinetime::Controllers::FS*>(c->context));
// const size_t address = startAddress + (block * blockSize);
// lfs.flashDriver.SectorErase(address);
// return lfs.flashDriver.EraseFailed() ? -1 : 0;
//}
//
//int FS::SectorProg(const struct lfs_config* c, lfs_block_t block, lfs_off_t off, const void* buffer, lfs_size_t size) {
// Pinetime::Controllers::FS& lfs = *(static_cast<Pinetime::Controllers::FS*>(c->context));
// const size_t address = startAddress + (block * blockSize) + off;
// lfs.flashDriver.Write(address, (uint8_t*) buffer, size);
// return lfs.flashDriver.ProgramFailed() ? -1 : 0;
//}
//
//int FS::SectorRead(const struct lfs_config* c, lfs_block_t block, lfs_off_t off, void* buffer, lfs_size_t size) {
// Pinetime::Controllers::FS& lfs = *(static_cast<Pinetime::Controllers::FS*>(c->context));
// const size_t address = startAddress + (block * blockSize) + off;
// lfs.flashDriver.Read(address, static_cast<uint8_t*>(buffer), size);
// return 0;
//}
/*
----------- LVGL filesystem integration -----------
*/
namespace {
lv_fs_res_t lvglOpen(lv_fs_drv_t* drv, void* file_p, const char* path, lv_fs_mode_t mode) {
lfs_file_t* file = static_cast<lfs_file_t*>(file_p);
FS* filesys = static_cast<FS*>(drv->user_data);
int ret = filesys->FileOpen(file, path, LFS_O_RDONLY);
if (ret != LFS_ERR_OK) {
return LV_FS_RES_FS_ERR;
}
return LV_FS_RES_OK;
}
lv_fs_res_t lvglClose(lv_fs_drv_t* drv, void* file_p) {
FS* filesys = static_cast<FS*>(drv->user_data);
lfs_file_t* file = static_cast<lfs_file_t*>(file_p);
filesys->FileClose(file);
return LV_FS_RES_OK;
}
lv_fs_res_t lvglRead(lv_fs_drv_t* drv, void* file_p, void* buf, uint32_t btr, uint32_t* br) {
FS* filesys = static_cast<FS*>(drv->user_data);
lfs_file_t* file = static_cast<lfs_file_t*>(file_p);
filesys->FileRead(file, static_cast<uint8_t*>(buf), btr);
*br = btr;
return LV_FS_RES_OK;
}
lv_fs_res_t lvglSeek(lv_fs_drv_t* drv, void* file_p, uint32_t pos) {
FS* filesys = static_cast<FS*>(drv->user_data);
lfs_file_t* file = static_cast<lfs_file_t*>(file_p);
filesys->FileSeek(file, pos);
return LV_FS_RES_OK;
}
}
void FS::LVGLFileSystemInit() {
lv_fs_drv_init(&fs_drv);
fs_drv.file_size = sizeof(lfs_file_t);
fs_drv.letter = 'F';
fs_drv.open_cb = lvglOpen;
fs_drv.close_cb = lvglClose;
fs_drv.read_cb = lvglRead;
fs_drv.seek_cb = lvglSeek;
fs_drv.user_data = this;
lv_fs_drv_register(&fs_drv);
}

View File

@ -1,134 +0,0 @@
#pragma once
#include <cstdio>
#include <cstdint>
//#include "drivers/SpiNorFlash.h"
//#include <littlefs/lfs.h>
#include <lvgl/lvgl.h>
using lfs_file_t = FILE*;
// copied from src/libs/littlefs/lfs.h
// Possible error codes, these are negative to allow
// valid positive return values
enum lfs_error {
LFS_ERR_OK = 0, // No error
LFS_ERR_IO = -5, // Error during device operation
LFS_ERR_CORRUPT = -84, // Corrupted
LFS_ERR_NOENT = -2, // No directory entry
LFS_ERR_EXIST = -17, // Entry already exists
LFS_ERR_NOTDIR = -20, // Entry is not a dir
LFS_ERR_ISDIR = -21, // Entry is a dir
LFS_ERR_NOTEMPTY = -39, // Dir is not empty
LFS_ERR_BADF = -9, // Bad file number
LFS_ERR_FBIG = -27, // File too large
LFS_ERR_INVAL = -22, // Invalid parameter
LFS_ERR_NOSPC = -28, // No space left on device
LFS_ERR_NOMEM = -12, // No more memory available
LFS_ERR_NOATTR = -61, // No data/attr available
LFS_ERR_NAMETOOLONG = -36, // File name too long
};
enum lfs_open_flags {
// open flags
LFS_O_RDONLY = 1, // Open a file as read only
#ifndef LFS_READONLY
LFS_O_WRONLY = 2, // Open a file as write only
LFS_O_RDWR = 3, // Open a file as read and write
LFS_O_CREAT = 0x0100, // Create a file if it does not exist
LFS_O_EXCL = 0x0200, // Fail if a file already exists
LFS_O_TRUNC = 0x0400, // Truncate the existing file to zero size
LFS_O_APPEND = 0x0800, // Move to end of file on every write
#endif
};
// File seek flags
enum lfs_whence_flags {
LFS_SEEK_SET = 0, // Seek relative to an absolute position
LFS_SEEK_CUR = 1, // Seek relative to the current file position
LFS_SEEK_END = 2, // Seek relative to the end of the file
};
typedef int32_t lfs_ssize_t;
namespace Pinetime {
namespace Controllers {
class FS {
public:
//FS(Pinetime::Drivers::SpiNorFlash&);
void Init();
void LVGLFileSystemInit();
int FileOpen(lfs_file_t* file_p, const char* fileName, const int flags);
int FileClose(lfs_file_t* file_p);
int FileRead(lfs_file_t* file_p, uint8_t* buff, uint32_t size);
int FileWrite(lfs_file_t* file_p, const uint8_t* buff, uint32_t size);
int FileSeek(lfs_file_t* file_p, uint32_t pos);
int FileDelete(const char* fileName);
//int DirOpen(const char* path, lfs_dir_t* lfs_dir);
//int DirClose(lfs_dir_t* lfs_dir);
//int DirRead(lfs_dir_t* dir, lfs_info* info);
//int DirRewind(lfs_dir_t* dir);
int DirCreate(const char* path);
int DirDelete(const char* path);
lfs_ssize_t GetFSSize();
int Rename(const char* oldPath, const char* newPath);
//int Stat(const char* path, lfs_info* info);
void VerifyResource();
static size_t getSize() {
return size;
}
static size_t getBlockSize() {
return blockSize;
}
private:
//Pinetime::Drivers::SpiNorFlash& flashDriver;
/*
* External Flash MAP (4 MBytes)
*
* 0x000000 +---------------------------------------+
* | Bootloader Assets |
* | 256 KBytes |
* | |
* 0x040000 +---------------------------------------+
* | OTA |
* | 464 KBytes |
* | |
* | |
* | |
* 0x0B4000 +---------------------------------------+
* | File System |
* | |
* | |
* | |
* | |
* 0x400000 +---------------------------------------+
*
*/
static constexpr size_t startAddress = 0x0B4000;
static constexpr size_t size = 0x34C000;
static constexpr size_t blockSize = 4096;
lv_fs_drv_t fs_drv;
bool resourcesValid = false;
//const struct lfs_config lfsConfig;
//lfs_t lfs;
//static int SectorSync(const struct lfs_config* c);
//static int SectorErase(const struct lfs_config* c, lfs_block_t block);
//static int SectorProg(const struct lfs_config* c, lfs_block_t block, lfs_off_t off, const void* buffer, lfs_size_t size);
//static int SectorRead(const struct lfs_config* c, lfs_block_t block, lfs_off_t off, void* buffer, lfs_size_t size);
};
}
}

View File

@ -1,58 +0,0 @@
#include "components/motor/MotorController.h"
#include <SDL2/SDL.h>
using namespace Pinetime::Controllers;
void MotorController::Init() {
//nrf_gpio_cfg_output(PinMap::Motor);
//nrf_gpio_pin_set(PinMap::Motor);
//app_timer_init();
//app_timer_create(&shortVibTimer, APP_TIMER_MODE_SINGLE_SHOT, StopMotor);
//app_timer_create(&longVibTimer, APP_TIMER_MODE_REPEATED, Ring);
}
void MotorController::Ring(void* p_context) {
auto* motorController = static_cast<MotorController*>(p_context);
motorController->RunForDuration(50);
}
Uint32 StopMotor_callback(Uint32 interval, void *param)
{
auto* motorController = static_cast<MotorController*>(param);
motorController->motor_is_running = false;
return 0; // cancel timer
}
Uint32 Ring_callback(Uint32 interval, void *param)
{
auto* motorController = static_cast<MotorController*>(param);
motorController->RunForDuration(50);
if (motorController->is_ringing) {
return interval;
}
return 0;
}
void MotorController::RunForDuration(uint8_t motorDuration) {
this->motor_is_running = true;
SDL_AddTimer(motorDuration, StopMotor_callback, this);
//nrf_gpio_pin_clear(PinMap::Motor);
//app_timer_start(shortVibTimer, APP_TIMER_TICKS(motorDuration), nullptr);
}
void MotorController::StartRinging() {
Ring(this);
is_ringing = true;
SDL_AddTimer(1000, Ring_callback, this);
//app_timer_start(longVibTimer, APP_TIMER_TICKS(1000), this);
}
void MotorController::StopRinging() {
is_ringing = false;
}
void MotorController::StopMotor(void* p_context) {
//nrf_gpio_pin_set(PinMap::Motor);
auto* motorController = static_cast<MotorController*>(p_context);
motorController->motor_is_running = false;
}

View File

@ -1,25 +0,0 @@
#pragma once
#include <cstdint>
namespace Pinetime {
namespace Controllers {
class MotorController {
public:
MotorController() = default;
void Init();
void RunForDuration(uint8_t motorDuration);
void StartRinging();
void StopRinging();
bool motor_is_running = false;
bool is_ringing = false;
private:
static void Ring(void* p_context);
static void StopMotor(void* p_context);
};
}
}

View File

@ -1,57 +1,73 @@
#include "displayapp/LittleVgl.h" #include "displayapp/LittleVgl.h"
#include "displayapp/lv_pinetime_theme.h" #include "displayapp/lv_pinetime_theme.h"
//#include <FreeRTOS.h> #include <FreeRTOS.h>
//#include <task.h> #include <task.h>
#include <timers.h>
////#include <projdefs.h> ////#include <projdefs.h>
#include "drivers/Cst816s.h" #include "drivers/Cst816s.h"
#include "drivers/St7789.h" #include "drivers/St7789.h"
// lv-sim monitor display driver for monitor_flush() function
#include "lv_drivers/display/monitor.h"
#include <array>
using namespace Pinetime::Components; using namespace Pinetime::Components;
//lv_style_t* LabelBigStyle = nullptr; lv_style_t* LabelBigStyle = nullptr;
//
//static void disp_flush(lv_disp_drv_t* disp_drv, const lv_area_t* area, lv_color_t* color_p) { static void disp_flush(lv_disp_drv_t* disp_drv, const lv_area_t* area, lv_color_t* color_p) {
// auto* lvgl = static_cast<LittleVgl*>(disp_drv->user_data); auto* lvgl = static_cast<LittleVgl*>(disp_drv->user_data);
// lvgl->FlushDisplay(area, color_p); lvgl->FlushDisplay(area, color_p);
//} }
//
static void rounder(lv_disp_drv_t* disp_drv, lv_area_t* area) {
auto* lvgl = static_cast<LittleVgl*>(disp_drv->user_data);
if (lvgl->GetFullRefresh()) {
area->x1 = 0;
area->x2 = LV_HOR_RES - 1;
area->y1 = 0;
area->y2 = LV_VER_RES - 1;
}
}
bool touchpad_read(lv_indev_drv_t* indev_drv, lv_indev_data_t* data) { bool touchpad_read(lv_indev_drv_t* indev_drv, lv_indev_data_t* data) {
auto* lvgl = static_cast<LittleVgl*>(indev_drv->user_data); auto* lvgl = static_cast<LittleVgl*>(indev_drv->user_data);
return lvgl->GetTouchPadInfo(data); return lvgl->GetTouchPadInfo(data);
} }
LittleVgl::LittleVgl(Pinetime::Drivers::St7789& lcd, Pinetime::Drivers::Cst816S& touchPanel) LittleVgl::LittleVgl(Pinetime::Drivers::St7789& lcd, Pinetime::Drivers::Cst816S& touchPanel)
: lcd {lcd}, touchPanel {touchPanel}, previousClick {0, 0} { : lcd {lcd}, touchPanel {touchPanel} {
} }
void LittleVgl::Init() { void LittleVgl::Init() {
// lv_init(); // lv_init();
// InitDisplay(); InitTheme();
// InitTheme(); InitDisplay();
InitTouchpad(); InitTouchpad();
} }
//void LittleVgl::InitDisplay() { void LittleVgl::InitDisplay() {
// lv_disp_draw_buf_init(&disp_buf_2, buf2_1, buf2_2, LV_HOR_RES_MAX * 4); /*Initialize the display buffer*/ lv_disp_buf_init(&disp_buf_2, buf2_1, buf2_2, LV_HOR_RES_MAX * 4); /*Initialize the display buffer*/
// lv_disp_drv_init(&disp_drv); /*Basic initialization*/ lv_disp_drv_init(&disp_drv); /*Basic initialization*/
//
// /*Set up the functions to access to your display*/ /*Set up the functions to access to your display*/
//
// /*Set the resolution of the display*/ /*Set the resolution of the display*/
// disp_drv.hor_res = 240; disp_drv.hor_res = 240;
// disp_drv.ver_res = 240; disp_drv.ver_res = 240;
//
// /*Used to copy the buffer's content to the display*/ /*Used to copy the buffer's content to the display*/
// disp_drv.flush_cb = disp_flush; disp_drv.flush_cb = disp_flush;
// /*Set a display buffer*/ /*Set a display buffer*/
// disp_drv.draw_buf = &disp_buf_2; disp_drv.buffer = &disp_buf_2;
// disp_drv.user_data = this; disp_drv.user_data = this;
// disp_drv.rounder_cb = rounder;
// /*Finally register the driver*/
// lv_disp_drv_register(&disp_drv); /*Finally register the driver*/
//} lv_disp_drv_register(&disp_drv);
}
void LittleVgl::InitTouchpad() { void LittleVgl::InitTouchpad() {
lv_indev_drv_t indev_drv; lv_indev_drv_t indev_drv;
@ -67,239 +83,185 @@ void LittleVgl::SetFullRefresh(FullRefreshDirections direction) {
if (scrollDirection == FullRefreshDirections::None) { if (scrollDirection == FullRefreshDirections::None) {
scrollDirection = direction; scrollDirection = direction;
if (scrollDirection == FullRefreshDirections::Down) { if (scrollDirection == FullRefreshDirections::Down) {
lv_disp_set_rotation(lv_disp_get_default(), LV_DISP_ROT_NONE); lv_disp_set_direction(lv_disp_get_default(), 1);
} else if (scrollDirection == FullRefreshDirections::Right) { } else if (scrollDirection == FullRefreshDirections::Right) {
lv_disp_set_rotation(lv_disp_get_default(), LV_DISP_ROT_NONE); lv_disp_set_direction(lv_disp_get_default(), 2);
} else if (scrollDirection == FullRefreshDirections::Left) { } else if (scrollDirection == FullRefreshDirections::Left) {
lv_disp_set_rotation(lv_disp_get_default(), LV_DISP_ROT_NONE); lv_disp_set_direction(lv_disp_get_default(), 3);
} else if (scrollDirection == FullRefreshDirections::RightAnim) { } else if (scrollDirection == FullRefreshDirections::RightAnim) {
lv_disp_set_rotation(lv_disp_get_default(), LV_DISP_ROT_NONE); lv_disp_set_direction(lv_disp_get_default(), 5);
} else if (scrollDirection == FullRefreshDirections::LeftAnim) { } else if (scrollDirection == FullRefreshDirections::LeftAnim) {
lv_disp_set_rotation(lv_disp_get_default(), LV_DISP_ROT_NONE); lv_disp_set_direction(lv_disp_get_default(), 4);
} }
} }
fullRefresh = true;
} }
//
//
//void LittleVgl::DisplayDownScroll(){
// // We are controlling the drawing process, disable lvgl timers
// lv_timer_enable(false);
//
// // For each segment, draw the full width, 4 lines at a time starting from the bottom
// // TODO: Should probably calculate this from the size of the draw buffer
// int16_t height = 4;
// int16_t width = 240;
// int16_t y2 = 240;
// int16_t y1 = 240 - height;
//
// lv_area_t area;
// area.x1 = 0;
// area.x2 = width;
//
// // Start from the bottom and create a 4 line high box
// for (y1 = 240 - height; y1 >= 0; y1 -= height) {
// y2 = y1 + height - 1;
//
// // If the box has reached the end of the visible line on the lcd controller...
// if (y2 == visibleNbLines - 1) {
// // move past the non visible lines
// writeOffset += (totalNbLines - visibleNbLines);
// // and wrap around to the start of address space
// writeOffset %= totalNbLines;
// }
// // Set new box
// area.y1 = y1;
// area.y2 = y2;
//
// // Scroll as we draw
// uint16_t toScroll = height;
// if (scrollOffset >= toScroll)
// scrollOffset -= toScroll;
// else { // now we need to wrap the scroll address
// toScroll -= scrollOffset;
// scrollOffset = totalNbLines - toScroll;
// }
// lcd.VerticalScrollStartAddress(scrollOffset);
//
// lv_disp_t* disp = lv_disp_get_default();
// // Clear invalid area list / tells lvgl that nothing on the screen needs to be updated
// _lv_inv_area(disp, nullptr);
// // invalidate only the segment we want to update in this portion of the animation
// _lv_inv_area(disp, &area);
// // cancel any current flushes in the display driver
// // Since we've stopped timers, it will be waiting forever if there is currently a flush
// lv_disp_flush_ready(disp->driver);
// lv_refr_now(disp);
// }
// // Done! clear flags and enable timers
// scrollDirection = FullRefreshDirections::None;
// animating = false;
// lv_timer_enable(true);
//}
//
//void LittleVgl::DisplayHorizAnim() {
// lv_timer_enable(false);
//
// int16_t height, width, x1, x2;
// lv_area_t area;
//
// height = 240;
// width = 4;
// int16_t (*NextStep)(int16_t, int16_t){};
// bool (*CheckEnd)(int16_t){};
//
// area.y1=0;
// area.y2=height;
//
// if (scrollDirection == FullRefreshDirections::RightAnim) {
// x1 = 0;
//
// CheckEnd = [](int16_t x) -> bool {
// return (x < LV_HOR_RES_MAX);
// };
// NextStep = [](int16_t x, int16_t width) -> int16_t {
// auto newx = x + width * 2;
// if (newx < 240) {return newx;};
// return (newx < 240 + width) ? (newx - 240 + width) : newx;
// };
//
// } else if (scrollDirection == FullRefreshDirections::LeftAnim) {
// x1 = 240 - width;
//
// CheckEnd = [](int16_t x) -> bool {
// return (x >= 0);
// };
// NextStep = [](int16_t x, int16_t width) -> int16_t {
// auto newx = x - width * 2;
// if (newx >= 0) {return newx;}
// return (newx >= 0 - width) ? (newx + 240 - width) : newx;
// };
//
// } else {
// // Not set for a horizontal animation!
// lv_timer_enable(true);
// return;
// }
//
// for (; CheckEnd(x1); x1 = NextStep(x1, width)) {
// x2 = x1 + width-1;
//
// if (area.y2 == visibleNbLines - 1) {
// writeOffset += (totalNbLines - visibleNbLines);
// writeOffset %= totalNbLines;
// }
// area.x1 = x1;
// area.x2 = x2;
//
// lv_disp_t* disp = lv_disp_get_default();
// _lv_inv_area(disp, nullptr);
// _lv_inv_area(disp, &area);
// lv_disp_flush_ready(disp->driver);
// lv_refr_now(disp);
// }
// scrollDirection = FullRefreshDirections::None;
// animating = false;
// lv_timer_enable(true);
//}
//
//void LittleVgl::FlushDisplayManually() {
// switch(scrollDirection){
// case FullRefreshDirections::Down:
// DisplayDownScroll();
// break;
// case FullRefreshDirections::RightAnim:
// case FullRefreshDirections::LeftAnim:
// DisplayHorizAnim();
// break;
// default:
// break;
// }
//}
//
void LittleVgl::FlushDisplay(const lv_area_t* area, lv_color_t* color_p) {
// uint16_t y1, y2, width, height = 0;
//
// ulTaskNotifyTake(pdTRUE, 200);
// // 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.
//
// if (!animating && (scrollDirection == FullRefreshDirections::Down ||
// scrollDirection == FullRefreshDirections::RightAnim ||
// scrollDirection == FullRefreshDirections::LeftAnim)){
// animating = true;
// FlushDisplayManually();
// return;
// }
//
// if ((scrollDirection == FullRefreshDirections::Up) && (area->y1 == 0)) {
// writeOffset = (writeOffset + visibleNbLines) % totalNbLines;
// }
//
// y1 = (area->y1 + writeOffset) % totalNbLines;
// y2 = (area->y2 + writeOffset) % totalNbLines;
//
// width = (area->x2 - area->x1) + 1;
// height = (area->y2 - area->y1) + 1;
//
// if (scrollDirection == FullRefreshDirections::Up) {
//
// if (area->y1 > 0) {
// if (area->y2 == visibleNbLines - 1) {
// scrollOffset += (height * 2);
// scrollDirection = FullRefreshDirections::None;
//// lv_disp_set_rotation(lv_disp_get_default(), LV_DISP_ROT_NONE);
// } else {
// scrollOffset += height;
// }
// scrollOffset = scrollOffset % totalNbLines;
// lcd.VerticalScrollStartAddress(scrollOffset);
// }
// } else if (scrollDirection == FullRefreshDirections::Left or scrollDirection == FullRefreshDirections::LeftAnim) {
// if (area->x2 == visibleNbLines - 1) {
// scrollDirection = FullRefreshDirections::None;
//// lv_disp_set_rotation(lv_disp_get_default(), LV_DISP_ROT_NONE);
// }
// } else if (scrollDirection == FullRefreshDirections::Right or scrollDirection == FullRefreshDirections::RightAnim) {
// if (area->x1 == 0) {
// scrollDirection = FullRefreshDirections::None;
//// lv_disp_set_rotation(lv_disp_get_default(), LV_DISP_ROT_NONE);
// }
// }
//
// if (y2 < y1) {
// height = totalNbLines - y1;
//
// if (height > 0) {
// lcd.DrawBuffer(area->x1, y1, width, height, reinterpret_cast<const uint8_t*>(color_p), width * height * 2);
// ulTaskNotifyTake(pdTRUE, 100);
// }
//
// uint16_t pixOffset = width * height;
// height = y2 + 1;
// lcd.DrawBuffer(area->x1, 0, width, height, reinterpret_cast<const uint8_t*>(color_p + pixOffset), width * height * 2);
//
// } else {
// lcd.DrawBuffer(area->x1, y1, width, height, reinterpret_cast<const uint8_t*>(color_p), width * height * 2);
// }
//
// // IMPORTANT!!!
// // Inform the graphics library that you are ready with the flushing
// lv_disp_flush_ready(&disp_drv);
// glue the lvgl code to the lv-sim monitor driver
void DrawBuffer(lv_disp_drv_t *disp_drv, uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint8_t* data, size_t size) {
lv_area_t area;
area.x1 = x;
area.x2 = x+width-1;
area.y1 = y;
area.y2 = y+height-1;
lv_color_t* color_p = reinterpret_cast<lv_color_t*>(data);
monitor_flush(disp_drv, &area, color_p);
}
// copied from lv_drivers/display/monitor.c to get the SDL_Window for the InfiniTime screen
extern "C"
{
typedef struct {
SDL_Window * window;
SDL_Renderer * renderer;
SDL_Texture * texture;
volatile bool sdl_refr_qry;
#if MONITOR_DOUBLE_BUFFERED
uint32_t * tft_fb_act;
#else
uint32_t tft_fb[LV_HOR_RES_MAX * LV_VER_RES_MAX];
#endif
}monitor_t;
extern monitor_t monitor;
}
// positive height moves screen down (draw y=0 to y=height)
// negative height moves screen up (draw y=height to y=0)
void MoveScreen(lv_disp_drv_t *disp_drv, int16_t height) {
if (height == 0)
return; // nothing to do
const int sdl_width = 240;
const int sdl_height = 240;
auto renderer = monitor.renderer;
const Uint32 format = SDL_PIXELFORMAT_RGBA8888;
SDL_Surface *surface = SDL_CreateRGBSurfaceWithFormat(0, sdl_width, sdl_height, 32, format);
SDL_RenderReadPixels(renderer, NULL, format, surface->pixels, surface->pitch);
uint8_t *pixels = (uint8_t*) surface->pixels;
std::array<lv_color16_t, 240*240> color_p;
for (int hi = 0; hi < sdl_height; hi++) {
for (int wi = 0; wi < sdl_width; wi++) {
auto red = pixels[hi*surface->pitch + wi*4 + 3]; // red
auto green = pixels[hi*surface->pitch + wi*4 + 2]; // greeen
auto blue = pixels[hi*surface->pitch + wi*4 + 1]; // blue
color_p.at(hi * sdl_width + wi) = LV_COLOR_MAKE(red, green, blue);
}
}
int16_t buffer_height = sdl_height - abs(height);
if (height >= 0) {
DrawBuffer(disp_drv, 0, height, sdl_width, sdl_height, (uint8_t*)color_p.data(), sdl_width*buffer_height *2);
} else {
DrawBuffer(disp_drv, 0, 0, sdl_width, sdl_height, (uint8_t*)(&color_p.at(sdl_width*abs(height))), sdl_width*buffer_height *2);
}
}
void LittleVgl::FlushDisplay(const lv_area_t* area, lv_color_t* color_p) {
uint16_t y1, y2, width, height = 0;
//ulTaskNotifyTake(pdTRUE, 200);
// Notification is still needed (even if there is a mutex on SPI) because of the DataCommand pin
// which cannot be set/clear during a transfer.
//if ((scrollDirection == LittleVgl::FullRefreshDirections::Down) && (area->y2 == visibleNbLines - 1)) {
// writeOffset = ((writeOffset + totalNbLines) - visibleNbLines) % totalNbLines;
//} else if ((scrollDirection == FullRefreshDirections::Up) && (area->y1 == 0)) {
// writeOffset = (writeOffset + visibleNbLines) % totalNbLines;
//}
y1 = (area->y1 + writeOffset) % totalNbLines;
y2 = (area->y2 + writeOffset) % totalNbLines;
width = (area->x2 - area->x1) + 1;
height = (area->y2 - area->y1) + 1;
if (scrollDirection == LittleVgl::FullRefreshDirections::Down) {
if (area->y2 < visibleNbLines - 1) {
uint16_t toScroll = 0;
if (area->y1 == 0) {
toScroll = height * 2;
scrollDirection = FullRefreshDirections::None;
lv_disp_set_direction(lv_disp_get_default(), 0);
} else {
toScroll = height;
}
if (scrollOffset >= toScroll)
scrollOffset -= toScroll;
else {
toScroll -= scrollOffset;
scrollOffset = (totalNbLines) -toScroll;
}
lcd.VerticalScrollStartAddress(scrollOffset);
}
// move the whole screen down and draw the new screen at the top of the display
MoveScreen(&disp_drv, static_cast<int16_t>(height));
y1 = 0;
y2 = height;
} else if (scrollDirection == FullRefreshDirections::Up) {
if (area->y1 > 0) {
if (area->y2 == visibleNbLines - 1) {
scrollOffset += (height * 2);
scrollDirection = FullRefreshDirections::None;
lv_disp_set_direction(lv_disp_get_default(), 0);
} else {
scrollOffset += height;
}
scrollOffset = scrollOffset % totalNbLines;
lcd.VerticalScrollStartAddress(scrollOffset);
}
// move the whole screen up and draw the new screen at the bottom the display
MoveScreen(&disp_drv, -static_cast<int16_t>(height));
y1 = LV_VER_RES - height;
y2 = LV_VER_RES;
} else if (scrollDirection == FullRefreshDirections::Left or scrollDirection == FullRefreshDirections::LeftAnim) {
if (area->x2 == visibleNbLines - 1) {
scrollDirection = FullRefreshDirections::None;
lv_disp_set_direction(lv_disp_get_default(), 0);
}
} else if (scrollDirection == FullRefreshDirections::Right or scrollDirection == FullRefreshDirections::RightAnim) {
if (area->x1 == 0) {
scrollDirection = FullRefreshDirections::None;
lv_disp_set_direction(lv_disp_get_default(), 0);
}
}
if (y2 < y1) {
height = totalNbLines - y1;
if (height > 0) {
//lcd.DrawBuffer(area->x1, y1, width, height, reinterpret_cast<const uint8_t*>(color_p), width * height * 2);
DrawBuffer(&disp_drv, area->x1, y1, width, height, reinterpret_cast<uint8_t*>(color_p), width * height * 2);
//ulTaskNotifyTake(pdTRUE, 100);
}
uint16_t pixOffset = width * height;
height = y2 + 1;
//lcd.DrawBuffer(area->x1, 0, width, height, reinterpret_cast<const uint8_t*>(color_p + pixOffset), width * height * 2);
DrawBuffer(&disp_drv, area->x1, 0, width, height, reinterpret_cast<uint8_t*>(color_p + pixOffset), width * height * 2);
} else {
//lcd.DrawBuffer(area->x1, y1, width, height, reinterpret_cast<const uint8_t*>(color_p), width * height * 2);
DrawBuffer(&disp_drv, area->x1, y1, width, height, reinterpret_cast<uint8_t*>(color_p), width * height * 2);
}
// IMPORTANT!!!
// Inform the graphics library that you are ready with the flushing
//lv_disp_flush_ready(&disp_drv);
// call flush with flushing_last set
// workaround because lv_disp_flush_ready() doesn't seem to trigger monitor_flush
lv_disp_t *disp = lv_disp_get_default(); lv_disp_t *disp = lv_disp_get_default();
lv_disp_drv_t *disp_drv = &disp->driver;
lv_area_t area_trimmed = *area;
if (area->x1 < 0)
area_trimmed.x1 = 0;
if (area->x2 >= LV_HOR_RES)
area_trimmed.x2 = LV_HOR_RES-1;
if (area->y1 < 0)
area_trimmed.y1 = 0;
if (area->y2 >= LV_VER_RES)
area_trimmed.y2 = LV_VER_RES-1;
// tell flush_cb this is the last thing to flush to get the monitor refreshed
lv_disp_get_buf(disp)->flushing_last = true; lv_disp_get_buf(disp)->flushing_last = true;
disp_drv->flush_cb(disp_drv, &area_trimmed, color_p); lv_area_t area_zero {0,0,0,0};
monitor_flush(&disp_drv, &area_zero, color_p);
// delay drawing to mimic PineTime display rendering speed
vTaskDelay(pdMS_TO_TICKS(3));
} }
void LittleVgl::SetNewTouchPoint(uint16_t x, uint16_t y, bool contact) { void LittleVgl::SetNewTouchPoint(uint16_t x, uint16_t y, bool contact) {
@ -319,9 +281,15 @@ bool LittleVgl::GetTouchPadInfo(lv_indev_data_t* ptr) {
return false; return false;
} }
//void LittleVgl::InitTheme() { void LittleVgl::InitTheme() {
// if (!lv_pinetime_theme_is_inited()) {
// lv_theme_t* th = lv_pinetime_theme_init(lv_disp_get_default(), lv_color_white(), lv_color_hex(0xC0C0C0), &jetbrains_mono_bold_20); lv_theme_t* th = lv_pinetime_theme_init(LV_COLOR_WHITE,
// lv_disp_set_theme(lv_disp_get_default(), th); LV_COLOR_SILVER,
// } 0,
//} &jetbrains_mono_bold_20,
&jetbrains_mono_bold_20,
&jetbrains_mono_bold_20,
&jetbrains_mono_bold_20);
lv_theme_set_act(th);
}

View File

@ -25,27 +25,30 @@ namespace Pinetime {
bool GetTouchPadInfo(lv_indev_data_t* ptr); bool GetTouchPadInfo(lv_indev_data_t* ptr);
void SetFullRefresh(FullRefreshDirections direction); void SetFullRefresh(FullRefreshDirections direction);
void SetNewTouchPoint(uint16_t x, uint16_t y, bool contact); void SetNewTouchPoint(uint16_t x, uint16_t y, bool contact);
//
// private: bool GetFullRefresh() {
// void InitDisplay(); bool returnValue = fullRefresh;
if (fullRefresh) {
fullRefresh = false;
}
return returnValue;
}
private:
void InitDisplay();
void InitTouchpad(); void InitTouchpad();
// void InitTheme(); void InitTheme();
//
// void FlushDisplayManually();
// void DisplayDownScroll();
// void DisplayHorizAnim();
Pinetime::Drivers::St7789& lcd; Pinetime::Drivers::St7789& lcd;
Pinetime::Drivers::Cst816S& touchPanel; Pinetime::Drivers::Cst816S& touchPanel;
// lv_disp_draw_buf_t disp_buf_2; lv_disp_buf_t disp_buf_2;
// lv_color_t buf2_1[LV_HOR_RES_MAX * 4]; lv_color_t buf2_1[LV_HOR_RES_MAX * 4];
// lv_color_t buf2_2[LV_HOR_RES_MAX * 4]; lv_color_t buf2_2[LV_HOR_RES_MAX * 4];
//
lv_disp_drv_t disp_drv;
lv_point_t previousClick;
bool firstTouch = true; lv_disp_drv_t disp_drv;
bool fullRefresh = false;
static constexpr uint8_t nbWriteLines = 4; static constexpr uint8_t nbWriteLines = 4;
static constexpr uint16_t totalNbLines = 320; static constexpr uint16_t totalNbLines = 320;
static constexpr uint16_t visibleNbLines = 240; static constexpr uint16_t visibleNbLines = 240;

View File

@ -1,12 +1,28 @@
#include "drivers/SpiNorFlash.h" #include "drivers/SpiNorFlash.h"
#include <hal/nrf_gpio.h> #include <hal/nrf_gpio.h>
#include <libraries/delay/nrf_delay.h>
#include <libraries/log/nrf_log.h> #include <libraries/log/nrf_log.h>
#include "drivers/Spi.h" #include "drivers/Spi.h"
#include <filesystem>
#include <iostream>
#include <stdexcept>
using namespace Pinetime::Drivers; using namespace Pinetime::Drivers;
SpiNorFlash::SpiNorFlash(Spi& spi) : spi {spi} { SpiNorFlash::SpiNorFlash(const std::string& memoryFilePath) : memoryFilePath{memoryFilePath} {
namespace fs = std::filesystem;
fs::path f{ memoryFilePath };
if (fs::exists(f)) {
memoryFile = std::fstream(memoryFilePath, std::ios::binary | std::fstream::in | std::fstream::out);
} else {
memoryFile = std::fstream(memoryFilePath, std::ios::trunc | std::ios::binary | std::fstream::in | std::fstream::out);
memoryFile.seekp(memorySize - 1);
memoryFile.write("", 1);
}
}
SpiNorFlash::~SpiNorFlash() {
if (memoryFile.is_open()) {
memoryFile.close();
}
} }
void SpiNorFlash::Init() { void SpiNorFlash::Init() {
@ -19,126 +35,67 @@ void SpiNorFlash::Uninit() {
} }
void SpiNorFlash::Sleep() { void SpiNorFlash::Sleep() {
auto cmd = static_cast<uint8_t>(Commands::DeepPowerDown);
spi.Write(&cmd, sizeof(uint8_t));
NRF_LOG_INFO("[SpiNorFlash] Sleep") NRF_LOG_INFO("[SpiNorFlash] Sleep")
} }
void SpiNorFlash::Wakeup() { void SpiNorFlash::Wakeup() {
// send Commands::ReleaseFromDeepPowerDown then 3 dummy bytes before reading Device ID
// static constexpr uint8_t cmdSize = 4;
// uint8_t cmd[cmdSize] = {static_cast<uint8_t>(Commands::ReleaseFromDeepPowerDown), 0x01, 0x02, 0x03};
// uint8_t id = 0;
// spi.Read(reinterpret_cast<uint8_t*>(&cmd), cmdSize, &id, 1);
// auto devId = device_id = ReadIdentificaion();
// if (devId.type != device_id.type) {
// NRF_LOG_INFO("[SpiNorFlash] ID on Wakeup: Failed");
// } else {
// NRF_LOG_INFO("[SpiNorFlash] ID on Wakeup: %d", id);
// }
NRF_LOG_INFO("[SpiNorFlash] Wakeup") NRF_LOG_INFO("[SpiNorFlash] Wakeup")
} }
SpiNorFlash::Identification SpiNorFlash::ReadIdentificaion() { SpiNorFlash::Identification SpiNorFlash::ReadIdentificaion() {
// auto cmd = static_cast<uint8_t>(Commands::ReadIdentification);
// Identification identification;
// spi.Read(&cmd, 1, reinterpret_cast<uint8_t*>(&identification), sizeof(Identification));
// return identification;
return {}; return {};
} }
uint8_t SpiNorFlash::ReadStatusRegister() { uint8_t SpiNorFlash::ReadStatusRegister() {
auto cmd = static_cast<uint8_t>(Commands::ReadStatusRegister); return 0;
uint8_t status;
spi.Read(&cmd, sizeof(cmd), &status, sizeof(uint8_t));
return status;
} }
bool SpiNorFlash::WriteInProgress() { bool SpiNorFlash::WriteInProgress() {
// return (ReadStatusRegister() & 0x01u) == 0x01u;
return false; return false;
} }
bool SpiNorFlash::WriteEnabled() { bool SpiNorFlash::WriteEnabled() {
// return (ReadStatusRegister() & 0x02u) == 0x02u;
return false; return false;
} }
uint8_t SpiNorFlash::ReadConfigurationRegister() { uint8_t SpiNorFlash::ReadConfigurationRegister() {
auto cmd = static_cast<uint8_t>(Commands::ReadConfigurationRegister); return 0;
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) { void SpiNorFlash::Read(uint32_t address, uint8_t* buffer, size_t size) {
static constexpr uint8_t cmdSize = 4; static_assert(sizeof(uint8_t) == sizeof(char));
uint8_t cmd[cmdSize] = {static_cast<uint8_t>(Commands::Read), (uint8_t) (address >> 16U), (uint8_t) (address >> 8U), (uint8_t) address}; if (address + size * sizeof(uint8_t) > memorySize) {
spi.Read(reinterpret_cast<uint8_t*>(&cmd), cmdSize, buffer, size); throw std::runtime_error("SpiNorFlash::Read out of bounds");
}
memoryFile.seekp(address);
memoryFile.read(reinterpret_cast<char *>(buffer), size);
} }
void SpiNorFlash::WriteEnable() { void SpiNorFlash::WriteEnable() {
auto cmd = static_cast<uint8_t>(Commands::WriteEnable);
spi.Read(&cmd, sizeof(cmd), nullptr, 0);
} }
void SpiNorFlash::SectorErase(uint32_t sectorAddress) { void SpiNorFlash::SectorErase(uint32_t sectorAddress) {
// static constexpr uint8_t cmdSize = 4;
// uint8_t cmd[cmdSize] = {static_cast<uint8_t>(Commands::SectorErase),
// (uint8_t) (sectorAddress >> 16U),
// (uint8_t) (sectorAddress >> 8U),
// (uint8_t) sectorAddress};
//
// WriteEnable();
// while (!WriteEnabled())
// vTaskDelay(1);
//
// spi.Read(reinterpret_cast<uint8_t*>(&cmd), cmdSize, nullptr, 0);
//
// while (WriteInProgress())
// vTaskDelay(1);
} }
uint8_t SpiNorFlash::ReadSecurityRegister() { uint8_t SpiNorFlash::ReadSecurityRegister() {
auto cmd = static_cast<uint8_t>(Commands::ReadSecurityRegister); return 0;
uint8_t status;
spi.Read(&cmd, sizeof(cmd), &status, sizeof(uint8_t));
return status;
} }
bool SpiNorFlash::ProgramFailed() { bool SpiNorFlash::ProgramFailed() {
// return (ReadSecurityRegister() & 0x20u) == 0x20u;
return false; return false;
} }
bool SpiNorFlash::EraseFailed() { bool SpiNorFlash::EraseFailed() {
// return (ReadSecurityRegister() & 0x40u) == 0x40u;
return false; return false;
} }
void SpiNorFlash::Write(uint32_t address, const uint8_t* buffer, size_t size) { void SpiNorFlash::Write(uint32_t address, const uint8_t* buffer, size_t size) {
// static constexpr uint8_t cmdSize = 4; if (address + size * sizeof(uint8_t) > memorySize) {
// throw std::runtime_error("SpiNorFlash::Write out of bounds");
// size_t len = size; }
// uint32_t addr = address; memoryFile.seekp(address);
// const uint8_t* b = buffer; memoryFile.write(reinterpret_cast<const char *>(buffer), size);
// while (len > 0) { memoryFile.flush();
// uint32_t pageLimit = (addr & ~(pageSize - 1u)) + pageSize;
// uint32_t toWrite = pageLimit - addr > len ? len : pageLimit - addr;
//
// uint8_t cmd[cmdSize] = {static_cast<uint8_t>(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;
// }
} }

View File

@ -1,23 +1,25 @@
#pragma once #pragma once
#include <cstddef> #include <cstddef>
#include <cstdint> #include <cstdint>
#include <fstream>
namespace Pinetime { namespace Pinetime {
namespace Drivers { namespace Drivers {
class Spi; class Spi;
class SpiNorFlash { class SpiNorFlash {
public: public:
explicit SpiNorFlash(Spi& spi); explicit SpiNorFlash(const std::string& memoryFilePath);
~SpiNorFlash();
SpiNorFlash(const SpiNorFlash&) = delete; SpiNorFlash(const SpiNorFlash&) = delete;
SpiNorFlash& operator=(const SpiNorFlash&) = delete; SpiNorFlash& operator=(const SpiNorFlash&) = delete;
SpiNorFlash(SpiNorFlash&&) = delete; SpiNorFlash(SpiNorFlash&&) = delete;
SpiNorFlash& operator=(SpiNorFlash&&) = delete; SpiNorFlash& operator=(SpiNorFlash&&) = delete;
typedef struct __attribute__((packed)) { struct __attribute__((packed)) Identification {
uint8_t manufacturer = 0; uint8_t manufacturer = 0;
uint8_t type = 0; uint8_t type = 0;
uint8_t density = 0; uint8_t density = 0;
} Identification; };
Identification ReadIdentificaion(); Identification ReadIdentificaion();
uint8_t ReadStatusRegister(); uint8_t ReadStatusRegister();
@ -53,8 +55,12 @@ namespace Pinetime {
}; };
static constexpr uint16_t pageSize = 256; static constexpr uint16_t pageSize = 256;
Spi& spi; static constexpr size_t memorySize {0x400000};
const std::string& memoryFilePath;
Identification device_id; Identification device_id;
std::fstream memoryFile;
}; };
} }
} }

View File

@ -57,8 +57,12 @@ ret_code_t app_timer_init(void) {
ret_code_t app_timer_create(app_timer_t *p_timer_id, ret_code_t app_timer_create(app_timer_t *p_timer_id,
app_timer_mode_t mode, app_timer_mode_t mode,
app_timer_timeout_handler_t timeout_handler) { app_timer_timeout_handler_t timeout_handler) {
if (mode != APP_TIMER_MODE_SINGLE_SHOT) { if (mode == APP_TIMER_MODE_SINGLE_SHOT) {
throw std::runtime_error("only mode 'APP_TIMER_MODE_SINGLE_SHOT' implemented"); p_timer_id->repeating = false;
} else if (mode == APP_TIMER_MODE_REPEATED) {
p_timer_id->repeating = true;
} else {
throw std::runtime_error("only mode 'APP_TIMER_MODE_SINGLE_SHOT' or 'APP_TIMER_MODE_REPEATED' implemented");
} }
p_timer_id->handler = timeout_handler; p_timer_id->handler = timeout_handler;
return 0; return 0;
@ -67,10 +71,15 @@ Uint32 timeout_callback_wrapper(Uint32 interval, void *param)
{ {
auto* timer_id = static_cast<app_timer_t*>(param); auto* timer_id = static_cast<app_timer_t*>(param);
timer_id->handler(timer_id->p_context); timer_id->handler(timer_id->p_context);
return 0; // cancel timer if (timer_id->repeating) {
return timer_id->repeat_period;
} else {
return 0; // cancel timer
}
} }
ret_code_t app_timer_start(app_timer_t &timer_id, uint32_t timeout_ticks, void * p_context) { ret_code_t app_timer_start(app_timer_t &timer_id, uint32_t timeout_ticks, void * p_context) {
timer_id.p_context = p_context; timer_id.p_context = p_context;
timer_id.repeat_period = timeout_ticks;
timer_id.sdl_timer_id = SDL_AddTimer(timeout_ticks, timeout_callback_wrapper, (void*)(&timer_id)); timer_id.sdl_timer_id = SDL_AddTimer(timeout_ticks, timeout_callback_wrapper, (void*)(&timer_id));
return 0; return 0;
} }

View File

@ -88,6 +88,13 @@ typedef uint32_t ret_code_t;
/**@brief Application time-out handler type. */ /**@brief Application time-out handler type. */
typedef void (*app_timer_timeout_handler_t)(void * p_context); typedef void (*app_timer_timeout_handler_t)(void * p_context);
/**@brief Timer modes. */
typedef enum
{
APP_TIMER_MODE_SINGLE_SHOT, /**< The timer will expire only once. */
APP_TIMER_MODE_REPEATED /**< The timer will restart each time it expires. */
} app_timer_mode_t;
struct app_timer_t struct app_timer_t
{ {
//nrf_sortlist_item_t list_item; /**< Token used by sortlist. */ //nrf_sortlist_item_t list_item; /**< Token used by sortlist. */
@ -96,6 +103,7 @@ struct app_timer_t
uint32_t repeat_period; /**< Repeat period (0 if single shot mode). */ uint32_t repeat_period; /**< Repeat period (0 if single shot mode). */
app_timer_timeout_handler_t handler; /**< User handler. */ app_timer_timeout_handler_t handler; /**< User handler. */
void * p_context; /**< User context. */ void * p_context; /**< User context. */
bool repeating;
//NRF_LOG_INSTANCE_PTR_DECLARE(p_log) /**< Pointer to instance of the logger object (Conditionally compiled). */ //NRF_LOG_INSTANCE_PTR_DECLARE(p_log) /**< Pointer to instance of the logger object (Conditionally compiled). */
//volatile bool active; /**< Flag indicating that timer is active. */ //volatile bool active; /**< Flag indicating that timer is active. */
}; };
@ -127,13 +135,6 @@ uint32_t constexpr APP_TIMER_TICKS(uint32_t ms) {
); );
} }
/**@brief Timer modes. */
typedef enum
{
APP_TIMER_MODE_SINGLE_SHOT, /**< The timer will expire only once. */
APP_TIMER_MODE_REPEATED /**< The timer will restart each time it expires. */
} app_timer_mode_t;
/**@brief Function for initializing the timer module. /**@brief Function for initializing the timer module.
* *
* @retval NRF_SUCCESS If the module was initialized successfully. * @retval NRF_SUCCESS If the module was initialized successfully.

View File

@ -6,8 +6,20 @@
#include <stdexcept> #include <stdexcept>
#include <string> // std::to_string #include <string> // std::to_string
void nrf_gpio_cfg_default(uint32_t pin_number) {} bool motor_running = false;
void nrf_gpio_pin_set(uint32_t pin_number) {}
void nrf_gpio_cfg_default(uint32_t pin_number) {
if (pin_number == Pinetime::PinMap::Motor)
{
motor_running = true;
}
}
void nrf_gpio_pin_set(uint32_t pin_number) {
if (pin_number == Pinetime::PinMap::Motor)
{
motor_running = false;
}
}
uint32_t nrf_gpio_pin_read(uint32_t pin_number) uint32_t nrf_gpio_pin_read(uint32_t pin_number)
{ {
if (pin_number == Pinetime::PinMap::Button) { if (pin_number == Pinetime::PinMap::Button) {
@ -16,12 +28,21 @@ uint32_t nrf_gpio_pin_read(uint32_t pin_number)
bool right_click = (buttons & SDL_BUTTON_RMASK) != 0; bool right_click = (buttons & SDL_BUTTON_RMASK) != 0;
return right_click; return right_click;
} }
else if (pin_number == Pinetime::PinMap::Motor)
{
return motor_running;
}
throw std::runtime_error("nrf_gpio_pin_read: unhandled pin_number: " + std::to_string(pin_number)); throw std::runtime_error("nrf_gpio_pin_read: unhandled pin_number: " + std::to_string(pin_number));
return 0; return 0;
} }
void nrf_gpio_cfg_output(uint32_t pin_number) {} void nrf_gpio_cfg_output(uint32_t pin_number) {}
void nrf_gpio_pin_clear(uint32_t pin_number) {} void nrf_gpio_pin_clear(uint32_t pin_number) {
if (pin_number == Pinetime::PinMap::Motor)
{
motor_running = true;
}
}
void nrf_gpio_range_cfg_input(uint32_t pin_range_start, void nrf_gpio_range_cfg_input(uint32_t pin_range_start,
uint32_t pin_range_end, uint32_t pin_range_end,
nrf_gpio_pin_pull_t pull_config) {} nrf_gpio_pin_pull_t pull_config) {}