mirror of
https://github.com/InfiniTimeOrg/InfiniTime.git
synced 2024-10-22 15:11:51 +02:00
Merge branch 'main' into button-unlock
This commit is contained in:
commit
35cc022db4
32
.devcontainer.json
Normal file
32
.devcontainer.json
Normal file
|
@ -0,0 +1,32 @@
|
|||
// For format details, see https://aka.ms/devcontainer.json. For config options, see the README at:
|
||||
// https://github.com/microsoft/vscode-dev-containers/tree/v0.154.2/containers/cpp
|
||||
{
|
||||
"build": {
|
||||
"dockerfile": "docker/Dockerfile"
|
||||
},
|
||||
"customizations": {
|
||||
"vscode": {
|
||||
"settings": {
|
||||
// Set *default* container specific settings.json values on container create.
|
||||
"terminal.integrated.profiles.linux": {
|
||||
"bash": {
|
||||
"path": "/bin/bash"
|
||||
}
|
||||
},
|
||||
"terminal.integrated.defaultProfile.linux": "bash",
|
||||
"editor.formatOnSave": true,
|
||||
// FIXME: This and the Dockerfile might get out of sync
|
||||
"clang-format.executable": "clang-format-14"
|
||||
},
|
||||
// Add the IDs of extensions you want installed when the container is created.
|
||||
"extensions": [
|
||||
"ms-vscode.cpptools",
|
||||
"ms-vscode.cmake-tools",
|
||||
"marus25.cortex-debug",
|
||||
"notskm.clang-tidy",
|
||||
"mjohns.clang-format"
|
||||
]
|
||||
}
|
||||
},
|
||||
"remoteUser": "infinitime"
|
||||
}
|
|
@ -1,66 +0,0 @@
|
|||
FROM ubuntu:latest
|
||||
|
||||
ARG DEBIAN_FRONTEND=noninteractive
|
||||
RUN apt-get update -qq \
|
||||
&& apt-get install -y \
|
||||
# x86_64 / generic packages
|
||||
bash \
|
||||
build-essential \
|
||||
cmake \
|
||||
git \
|
||||
make \
|
||||
python3 \
|
||||
python3-pip \
|
||||
python3-pil \
|
||||
tar \
|
||||
unzip \
|
||||
wget \
|
||||
curl \
|
||||
dos2unix \
|
||||
clang-format-12 \
|
||||
clang-tidy \
|
||||
locales \
|
||||
libncurses5 \
|
||||
# aarch64 packages
|
||||
libffi-dev \
|
||||
libssl-dev \
|
||||
python3-dev \
|
||||
rustc \
|
||||
&& rm -rf /var/cache/apt/* /var/lib/apt/lists/*;
|
||||
|
||||
#SET LOCALE
|
||||
RUN locale-gen en_US.UTF-8
|
||||
ENV LANG en_US.UTF-8
|
||||
ENV LANGUAGE en_US:en
|
||||
ENV LC_ALL en_US.UTF-8
|
||||
|
||||
RUN pip3 install adafruit-nrfutil
|
||||
# required for McuBoot
|
||||
RUN pip3 install setuptools_rust
|
||||
|
||||
WORKDIR /opt/
|
||||
# build.sh knows how to compile but it problimatic on Win10
|
||||
COPY build.sh .
|
||||
RUN chmod +x build.sh
|
||||
# create_build_openocd.sh uses cmake to crate to build directory
|
||||
COPY create_build_openocd.sh .
|
||||
RUN chmod +x create_build_openocd.sh
|
||||
# Lets get each in a separate docker layer for better downloads
|
||||
# GCC
|
||||
# RUN bash -c "source /opt/build.sh; GetGcc;"
|
||||
RUN wget https://developer.arm.com/-/media/Files/downloads/gnu-rm/9-2020q2/gcc-arm-none-eabi-9-2020-q2-update-x86_64-linux.tar.bz2 -O - | tar -xj -C /opt
|
||||
# NrfSdk
|
||||
# RUN bash -c "source /opt/build.sh; GetNrfSdk;"
|
||||
RUN wget -q "https://developer.nordicsemi.com/nRF5_SDK/nRF5_SDK_v15.x.x/nRF5_SDK_15.3.0_59ac345.zip" -O /tmp/nRF5_SDK_15.3.0_59ac345
|
||||
RUN unzip -q /tmp/nRF5_SDK_15.3.0_59ac345 -d /opt
|
||||
RUN rm /tmp/nRF5_SDK_15.3.0_59ac345
|
||||
# McuBoot
|
||||
# RUN bash -c "source /opt/build.sh; GetMcuBoot;"
|
||||
RUN git clone https://github.com/mcu-tools/mcuboot.git
|
||||
RUN pip3 install -r ./mcuboot/scripts/requirements.txt
|
||||
|
||||
RUN adduser infinitime
|
||||
|
||||
ENV NRF5_SDK_PATH /opt/nRF5_SDK_15.3.0_59ac345
|
||||
ENV ARM_NONE_EABI_TOOLCHAIN_PATH /opt/gcc-arm-none-eabi-9-2020-q2-update
|
||||
ENV SOURCES_DIR /workspaces/InfiniTime
|
|
@ -1,87 +0,0 @@
|
|||
#!/bin/bash
|
||||
(return 0 2>/dev/null) && SOURCED="true" || SOURCED="false"
|
||||
export LC_ALL=C.UTF-8
|
||||
export LANG=C.UTF-8
|
||||
set -x
|
||||
set -e
|
||||
|
||||
# Default locations if the var isn't already set
|
||||
export TOOLS_DIR="${TOOLS_DIR:=/opt}"
|
||||
export SOURCES_DIR="${SOURCES_DIR:=/sources}"
|
||||
export BUILD_DIR="${BUILD_DIR:=$SOURCES_DIR/build}"
|
||||
export OUTPUT_DIR="${OUTPUT_DIR:=$BUILD_DIR/output}"
|
||||
|
||||
export BUILD_TYPE=${BUILD_TYPE:=Release}
|
||||
export GCC_ARM_VER=${GCC_ARM_VER:="gcc-arm-none-eabi-9-2020-q2-update"}
|
||||
export NRF_SDK_VER=${NRF_SDK_VER:="nRF5_SDK_15.3.0_59ac345"}
|
||||
|
||||
MACHINE="$(uname -m)"
|
||||
[[ "$MACHINE" == "arm64" ]] && MACHINE="aarch64"
|
||||
|
||||
main() {
|
||||
local target="$1"
|
||||
|
||||
mkdir -p "$TOOLS_DIR"
|
||||
|
||||
[[ ! -d "$TOOLS_DIR/$GCC_ARM_VER" ]] && GetGcc
|
||||
[[ ! -d "$TOOLS_DIR/$NRF_SDK_VER" ]] && GetNrfSdk
|
||||
[[ ! -d "$TOOLS_DIR/mcuboot" ]] && GetMcuBoot
|
||||
|
||||
mkdir -p "$BUILD_DIR"
|
||||
|
||||
CmakeGenerate
|
||||
CmakeBuild $target
|
||||
BUILD_RESULT=$?
|
||||
if [ "$DISABLE_POSTBUILD" != "true" -a "$BUILD_RESULT" == 0 ]; then
|
||||
source "$BUILD_DIR/post_build.sh"
|
||||
fi
|
||||
# assuming post_build.sh will never fail on a successful build
|
||||
return $BUILD_RESULT
|
||||
}
|
||||
|
||||
GetGcc() {
|
||||
GCC_SRC="$GCC_ARM_VER-$MACHINE-linux.tar.bz"
|
||||
wget -q https://developer.arm.com/-/media/Files/downloads/gnu-rm/9-2020q2/$GCC_SRC -O - | tar -xj -C $TOOLS_DIR/
|
||||
}
|
||||
|
||||
GetMcuBoot() {
|
||||
git clone https://github.com/mcu-tools/mcuboot.git "$TOOLS_DIR/mcuboot"
|
||||
pip3 install -r "$TOOLS_DIR/mcuboot/scripts/requirements.txt"
|
||||
}
|
||||
|
||||
GetNrfSdk() {
|
||||
wget -q "https://developer.nordicsemi.com/nRF5_SDK/nRF5_SDK_v15.x.x/$NRF_SDK_VER.zip" -O /tmp/$NRF_SDK_VER
|
||||
unzip -q /tmp/$NRF_SDK_VER -d "$TOOLS_DIR/"
|
||||
rm /tmp/$NRF_SDK_VER
|
||||
}
|
||||
|
||||
CmakeGenerate() {
|
||||
# We can swap the CD and trailing SOURCES_DIR for -B and -S respectively
|
||||
# once we go to newer CMake (Ubuntu 18.10 gives us CMake 3.10)
|
||||
cd "$BUILD_DIR"
|
||||
|
||||
cmake -G "Unix Makefiles" \
|
||||
-DCMAKE_BUILD_TYPE=$BUILD_TYPE \
|
||||
-DARM_NONE_EABI_TOOLCHAIN_PATH="$TOOLS_DIR/$GCC_ARM_VER" \
|
||||
-DNRF5_SDK_PATH="$TOOLS_DIR/$NRF_SDK_VER" \
|
||||
"$SOURCES_DIR"
|
||||
cmake -L -N .
|
||||
}
|
||||
|
||||
CmakeBuild() {
|
||||
local target="$1"
|
||||
[[ -n "$target" ]] && target="--target $target"
|
||||
if cmake --build "$BUILD_DIR" --config $BUILD_TYPE $target -- -j$(nproc)
|
||||
then return 0; else return 1;
|
||||
fi
|
||||
}
|
||||
|
||||
if [[ $SOURCED == "false" ]]; then
|
||||
# It is important to return exit code of main
|
||||
# To be future-proof, this is handled explicitely
|
||||
main "$@"
|
||||
BUILD_RESULT=$?
|
||||
exit $BUILD_RESULT
|
||||
else
|
||||
echo "Sourced!"
|
||||
fi
|
|
@ -1,2 +0,0 @@
|
|||
#!/bin/bash
|
||||
cmake --build /workspaces/Pinetime/build --config Release -- -j6 pinetime-app
|
|
@ -1,3 +0,0 @@
|
|||
#!/bin/bash
|
||||
rm -rf build/
|
||||
cmake -G 'Unix Makefiles' -DCMAKE_BUILD_TYPE=Release -DUSE_OPENOCD=1 -DARM_NONE_EABI_TOOLCHAIN_PATH=/opt/gcc-arm-none-eabi-9-2020-q2-update -DNRF5_SDK_PATH=/opt/nRF5_SDK_15.3.0_59ac345 -S . -Bbuild
|
|
@ -1,38 +0,0 @@
|
|||
// For format details, see https://aka.ms/devcontainer.json. For config options, see the README at:
|
||||
// https://github.com/microsoft/vscode-dev-containers/tree/v0.154.2/containers/cpp
|
||||
{
|
||||
// "name": "Pinetime",
|
||||
// "image": "feabhas/pinetime-dev"
|
||||
"build": {
|
||||
"dockerfile": "Dockerfile",
|
||||
// Update 'VARIANT' to pick an Debian / Ubuntu OS version: debian-10, debian-9, ubuntu-20.04, ubuntu-18.04
|
||||
// "args": { "VARIANT": "ubuntu-20.04" }
|
||||
},
|
||||
"runArgs": [ "--cap-add=SYS_PTRACE", "--security-opt", "seccomp=unconfined"],
|
||||
|
||||
// Set *default* container specific settings.json values on container create.
|
||||
"settings": {
|
||||
"terminal.integrated.shell.linux": "/bin/bash",
|
||||
"editor.formatOnSave": true,
|
||||
"clang-format.executable": "clang-format-12"
|
||||
},
|
||||
|
||||
// Add the IDs of extensions you want installed when the container is created.
|
||||
"extensions": [
|
||||
"ms-vscode.cpptools",
|
||||
"ms-vscode.cmake-tools",
|
||||
"marus25.cortex-debug",
|
||||
"notskm.clang-tidy",
|
||||
"mjohns.clang-format"
|
||||
],
|
||||
|
||||
// Use 'forwardPorts' to make a list of ports inside the container available locally.
|
||||
// "forwardPorts": [],
|
||||
|
||||
// Use 'postCreateCommand' to run commands after the container is created.
|
||||
// "postCreateCommand": "bash /opt/create_build_openocd.sh",
|
||||
|
||||
// Comment out connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root.
|
||||
// "remoteUser": "vscode"
|
||||
"remoteUser": "infinitime"
|
||||
}
|
|
@ -1,2 +0,0 @@
|
|||
#!/bin/bash
|
||||
cmake -G 'Unix Makefiles' -DCMAKE_BUILD_TYPE=Release -DUSE_OPENOCD=1 -DARM_NONE_EABI_TOOLCHAIN_PATH=/opt/gcc-arm-none-eabi-9-2020-q2-update -DNRF5_SDK_PATH=/opt/nRF5_SDK_15.3.0_59ac345 ${SOURCES_DIR}
|
22
.vscode/c_cpp_properties.json
vendored
22
.vscode/c_cpp_properties.json
vendored
|
@ -1,4 +1,9 @@
|
|||
{
|
||||
"env": {
|
||||
// TODO: This is a duplication of the configuration set in /docker/build.sh!
|
||||
"TOOLS_DIR": "/opt",
|
||||
"GCC_ARM_PATH": "gcc-arm-none-eabi-10.3-2021.10"
|
||||
},
|
||||
"configurations": [
|
||||
{
|
||||
"name": "nrfCC",
|
||||
|
@ -14,7 +19,22 @@
|
|||
"intelliSenseMode": "linux-gcc-arm",
|
||||
"configurationProvider": "ms-vscode.cpp-tools",
|
||||
"compileCommands": "${workspaceFolder}/build/compile_commands.json"
|
||||
},
|
||||
{
|
||||
"name": "nrfCC Devcontainer",
|
||||
"includePath": [
|
||||
"${workspaceFolder}/**",
|
||||
"${workspaceFolder}/src/**",
|
||||
"${workspaceFolder}/src"
|
||||
],
|
||||
"defines": [],
|
||||
"compilerPath": "${TOOLS_DIR}/${GCC_ARM_PATH}/bin/arm-none-eabi-gcc",
|
||||
"cStandard": "c99",
|
||||
"cppStandard": "c++20",
|
||||
"intelliSenseMode": "linux-gcc-arm",
|
||||
"configurationProvider": "ms-vscode.cpp-tools",
|
||||
"compileCommands": "${workspaceFolder}/build/compile_commands.json"
|
||||
}
|
||||
],
|
||||
"version": 4
|
||||
}
|
||||
}
|
||||
|
|
6
.vscode/cmake-kits.json
vendored
Normal file
6
.vscode/cmake-kits.json
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
[
|
||||
{
|
||||
"name": "InfiniTime Compiler",
|
||||
"environmentSetupScript": "${workspaceFolder}/docker/build.sh"
|
||||
}
|
||||
]
|
45
.vscode/launch.json
vendored
45
.vscode/launch.json
vendored
|
@ -1,20 +1,18 @@
|
|||
{
|
||||
{
|
||||
"version": "0.1.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Debug - Openocd docker Remote",
|
||||
"type":"cortex-debug",
|
||||
"cortex-debug.armToolchainPath":"${env:ARM_NONE_EABI_TOOLCHAIN_PATH}/bin",
|
||||
"type": "cortex-debug",
|
||||
"cwd": "${workspaceRoot}",
|
||||
"executable": "${command:cmake.launchTargetPath}",
|
||||
"request": "launch",
|
||||
"servertype": "external",
|
||||
// This may need to be arm-none-eabi-gdb depending on your system
|
||||
"gdbPath" : "${env:ARM_NONE_EABI_TOOLCHAIN_PATH}/bin/arm-none-eabi-gdb",
|
||||
"gdbPath": "${env:ARM_NONE_EABI_TOOLCHAIN_PATH}/bin/arm-none-eabi-gdb",
|
||||
// Connect to an already running OpenOCD instance
|
||||
"gdbTarget": "host.docker.internal:3333",
|
||||
"svdFile": "${workspaceRoot}/nrf52.svd",
|
||||
"runToMain": true,
|
||||
"runToEntryPoint": "main",
|
||||
// Work around for stopping at main on restart
|
||||
"postRestartCommands": [
|
||||
"break main",
|
||||
|
@ -23,18 +21,16 @@
|
|||
},
|
||||
{
|
||||
"name": "Debug - Openocd Local",
|
||||
"type":"cortex-debug",
|
||||
"cortex-debug.armToolchainPath":"${env:ARM_NONE_EABI_TOOLCHAIN_PATH}/bin",
|
||||
"type": "cortex-debug",
|
||||
"cwd": "${workspaceRoot}",
|
||||
"executable": "${command:cmake.launchTargetPath}",
|
||||
"request": "launch",
|
||||
"servertype": "openocd",
|
||||
// This may need to be arm-none-eabi-gdb depending on your system
|
||||
"gdbPath" : "${env:ARM_NONE_EABI_TOOLCHAIN_PATH}/bin/arm-none-eabi-gdb",
|
||||
"gdbPath": "${env:ARM_NONE_EABI_TOOLCHAIN_PATH}/bin/arm-none-eabi-gdb",
|
||||
// Connect to an already running OpenOCD instance
|
||||
"gdbTarget": "localhost:3333",
|
||||
"svdFile": "${workspaceRoot}/nrf52.svd",
|
||||
"runToMain": true,
|
||||
"runToEntryPoint": "main",
|
||||
// Work around for stopping at main on restart
|
||||
"postRestartCommands": [
|
||||
"break main",
|
||||
|
@ -51,6 +47,11 @@
|
|||
"showDevDebugOutput": false,
|
||||
"servertype": "openocd",
|
||||
"runToMain": true,
|
||||
// Work around for stopping at main on restart
|
||||
"postRestartCommands": [
|
||||
"break main",
|
||||
"continue"
|
||||
],
|
||||
// Only use armToolchainPath if your arm-none-eabi-gdb is not in your path (some GCC packages does not contain arm-none-eabi-gdb)
|
||||
"armToolchainPath": "${workspaceRoot}/../gcc-arm-none-eabi-10.3-2021.10/bin",
|
||||
"svdFile": "${workspaceRoot}/nrf52.svd",
|
||||
|
@ -58,7 +59,25 @@
|
|||
"interface/stlink.cfg",
|
||||
"target/nrf52.cfg"
|
||||
],
|
||||
}
|
||||
|
||||
},
|
||||
{
|
||||
"name": "Debug - Openocd Devcontainer",
|
||||
"type": "cortex-debug",
|
||||
"cwd": "${workspaceRoot}",
|
||||
"executable": "${command:cmake.launchTargetPath}",
|
||||
"request": "launch",
|
||||
"servertype": "external",
|
||||
// FIXME: This is hardcoded. I have no idea how to use the values set in build.sh here
|
||||
"gdbPath": "/opt/gcc-arm-none-eabi-10.3-2021.10/bin/arm-none-eabi-gdb",
|
||||
// Connect to an already running OpenOCD instance
|
||||
"gdbTarget": "host.docker.internal:3333",
|
||||
"svdFile": "${workspaceRoot}/nrf52.svd",
|
||||
"runToEntryPoint": "main",
|
||||
// Work around for stopping at main on restart
|
||||
"postRestartCommands": [
|
||||
"break main",
|
||||
"continue"
|
||||
]
|
||||
},
|
||||
]
|
||||
}
|
||||
|
|
15
.vscode/settings.json
vendored
15
.vscode/settings.json
vendored
|
@ -1,9 +1,20 @@
|
|||
{
|
||||
"C_Cpp.default.configurationProvider": "ms-vscode.cmake-tools",
|
||||
"cmake.configureArgs": [
|
||||
"-DARM_NONE_EABI_TOOLCHAIN_PATH=${env:ARM_NONE_EABI_TOOLCHAIN_PATH}",
|
||||
"-DNRF5_SDK_PATH=${env:NRF5_SDK_PATH}",
|
||||
"-DARM_NONE_EABI_TOOLCHAIN_PATH=${env:TOOLS_DIR}/${env:GCC_ARM_PATH}",
|
||||
"-DNRF5_SDK_PATH=${env:TOOLS_DIR}/${env:NRF_SDK_VER}",
|
||||
],
|
||||
"cmake.statusbar.advanced": {
|
||||
"launch": {
|
||||
"visibility": "hidden"
|
||||
},
|
||||
"launchTarget": {
|
||||
"visibility": "hidden"
|
||||
},
|
||||
"debug": {
|
||||
"visibility": "hidden"
|
||||
}
|
||||
},
|
||||
"cmake.generator": "Unix Makefiles",
|
||||
"clang-tidy.buildPath": "build/compile_commands.json",
|
||||
"files.associations": {
|
||||
|
|
22
.vscode/tasks.json
vendored
22
.vscode/tasks.json
vendored
|
@ -1,20 +1,6 @@
|
|||
{
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "create openocd build",
|
||||
"type": "shell",
|
||||
"command": "/opt/create_build_openocd.sh",
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
},
|
||||
"presentation": {
|
||||
"reveal": "always",
|
||||
"panel": "shared"
|
||||
},
|
||||
"problemMatcher": []
|
||||
},
|
||||
{
|
||||
"label": "update submodules",
|
||||
"type": "shell",
|
||||
|
@ -31,14 +17,6 @@
|
|||
"panel": "shared"
|
||||
},
|
||||
"problemMatcher": []
|
||||
},
|
||||
{
|
||||
"label": "BuildInit",
|
||||
"dependsOn": [
|
||||
"update submodules",
|
||||
"create openocd build"
|
||||
],
|
||||
"problemMatcher": []
|
||||
}
|
||||
]
|
||||
}
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
![InfiniTime logo](doc/logo/infinitime-logo-small.jpg "InfiniTime Logo")
|
||||
|
||||
Fast open-source firmware for the [PineTime smartwatch](https://www.pine64.org/pinetime/) with many features, written in modern C++.
|
||||
Fast open-source firmware for the [PineTime smartwatch](https://pine64.org/devices/pinetime/) with many features, written in modern C++.
|
||||
|
||||
## New to InfiniTime?
|
||||
|
||||
|
|
|
@ -7,9 +7,9 @@ My own contribution is little more than a brute force conversion to
|
|||
python3. It is sparsely tested so there are likely to be a few
|
||||
remaining bytes versus string bugs remaining in the places I didn't test
|
||||
. I used it primarily as part of
|
||||
[wasp-os](https://github.com/daniel-thompson/wasp-os) as a way to
|
||||
[wasp-os](https://github.com/wasp-os/wasp-os) as a way to
|
||||
deliver OTA updates to nRF52-based smart watches, especially the
|
||||
[Pine64 PineTime](https://www.pine64.org/pinetime/).
|
||||
[Pine64 PineTime](https://pine64.org/devices/pinetime/).
|
||||
|
||||
## What does it do?
|
||||
|
||||
|
|
|
@ -21,3 +21,5 @@ The current raw motion values. This is a 3 `int16_t` array:
|
|||
- [0] : X
|
||||
- [1] : Y
|
||||
- [2] : Z
|
||||
|
||||
The three motion values are in units of "binary milli-g", where 1g is represented by a value of 1024.
|
||||
|
|
|
@ -32,7 +32,7 @@ The .devcontainer folder contains the configuration and scripts for using a Dock
|
|||
|
||||
Using the [Remote-Containers](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers) extension is recommended. It will handle configuring the Docker virtual machine and setting everything up.
|
||||
|
||||
More documentation is available in the [readme in .devcontainer](../.devcontainer/README.md)
|
||||
More documentation is available in the [readme in .devcontainer](usingDevcontainers.md)
|
||||
|
||||
### DevContainer on Ubuntu
|
||||
|
||||
|
|
|
@ -140,10 +140,10 @@ namespace Pinetime {
|
|||
}
|
||||
|
||||
template <>
|
||||
struct AppTraits<Apps:MyApp> {
|
||||
struct AppTraits<Apps::MyApp> {
|
||||
static constexpr Apps app = Apps::MyApp;
|
||||
static constexpr const char* icon = Screens::Symbol::myApp;
|
||||
static Screens::Screens* Create(AppController& controllers) {
|
||||
static constexpr const char* icon = Screens::Symbols::myApp;
|
||||
static Screens::Screen* Create(AppControllers& controllers) {
|
||||
return new Screens::MyApp();
|
||||
}
|
||||
};
|
||||
|
@ -176,7 +176,7 @@ Now we have our very own app, but InfiniTime does not know about it yet.
|
|||
The first step is to include your `MyApp.cpp` (or any new cpp files for that matter)
|
||||
in the compilation by adding it to [CMakeLists.txt](/CMakeLists.txt).
|
||||
The next step to making it launch-able is to give your app an id.
|
||||
To do this, add an entry in the enum class `Pinetime::Applications::Apps` ([displayapp/Apps.h](/src/displayapp/Apps.h)).
|
||||
To do this, add an entry in the enum class `Pinetime::Applications::Apps` ([displayapp/apps/Apps.h](/src/displayapp/apps/Apps.h.in)).
|
||||
Name this entry after your app. Add `#include "displayapp/screens/MyApp.h"`
|
||||
to the file [displayapp/DisplayApp.cpp](/src/displayapp/DisplayApp.cpp).
|
||||
|
||||
|
|
|
@ -37,6 +37,13 @@ RUN apt-get update -qq \
|
|||
libpangocairo-1.0-0 \
|
||||
&& rm -rf /var/cache/apt/* /var/lib/apt/lists/*;
|
||||
|
||||
# Add the necessary apt-gets for the devcontainer
|
||||
RUN apt-get update -qq \
|
||||
&& apt-get install -y \
|
||||
clang-format-14 \
|
||||
clang-tidy \
|
||||
libncurses5
|
||||
|
||||
# Git needed for PROJECT_GIT_COMMIT_HASH variable setting
|
||||
|
||||
RUN pip3 install adafruit-nrfutil
|
||||
|
@ -55,5 +62,8 @@ RUN bash -c "source /opt/build.sh; GetNrfSdk;"
|
|||
# McuBoot
|
||||
RUN bash -c "source /opt/build.sh; GetMcuBoot;"
|
||||
|
||||
# Add the infinitime user for connecting devcontainer
|
||||
RUN adduser infinitime
|
||||
|
||||
ENV SOURCES_DIR /sources
|
||||
CMD ["/opt/build.sh"]
|
||||
|
|
|
@ -379,6 +379,7 @@ list(APPEND SOURCE_FILES
|
|||
displayapp/screens/Navigation.cpp
|
||||
displayapp/screens/Metronome.cpp
|
||||
displayapp/screens/Motion.cpp
|
||||
displayapp/screens/Weather.cpp
|
||||
displayapp/screens/FirmwareValidation.cpp
|
||||
displayapp/screens/ApplicationList.cpp
|
||||
displayapp/screens/Notifications.cpp
|
||||
|
@ -543,7 +544,6 @@ list(APPEND RECOVERY_SOURCE_FILES
|
|||
systemtask/SystemTask.cpp
|
||||
systemtask/SystemMonitor.cpp
|
||||
drivers/TwiMaster.cpp
|
||||
components/gfx/Gfx.cpp
|
||||
components/rle/RleDecoder.cpp
|
||||
components/heartrate/HeartRateController.cpp
|
||||
heartratetask/HeartRateTask.cpp
|
||||
|
@ -573,7 +573,6 @@ list(APPEND RECOVERYLOADER_SOURCE_FILES
|
|||
|
||||
components/rle/RleDecoder.cpp
|
||||
|
||||
components/gfx/Gfx.cpp
|
||||
drivers/St7789.cpp
|
||||
components/brightness/BrightnessController.cpp
|
||||
|
||||
|
|
|
@ -75,6 +75,7 @@
|
|||
#define configUSE_TIME_SLICING 0
|
||||
#define configUSE_NEWLIB_REENTRANT 0
|
||||
#define configENABLE_BACKWARD_COMPATIBILITY 1
|
||||
#define configUSE_TASK_NOTIFICATIONS 0
|
||||
|
||||
/* Hook function related definitions. */
|
||||
#define configUSE_IDLE_HOOK 0
|
||||
|
|
|
@ -357,6 +357,8 @@ void DfuService::DfuImage::Init(size_t chunkSize, size_t totalSize, uint16_t exp
|
|||
this->totalSize = totalSize;
|
||||
this->expectedCrc = expectedCrc;
|
||||
this->ready = true;
|
||||
totalWriteIndex = 0;
|
||||
bufferWriteIndex = 0;
|
||||
}
|
||||
|
||||
void DfuService::DfuImage::Append(uint8_t* data, size_t size) {
|
||||
|
|
|
@ -2,9 +2,9 @@
|
|||
#define min // workaround: nimble's min/max macros conflict with libstdc++
|
||||
#define max
|
||||
#include <host/ble_gap.h>
|
||||
#include <atomic>
|
||||
#undef max
|
||||
#undef min
|
||||
#include <atomic>
|
||||
|
||||
namespace Pinetime {
|
||||
namespace Controllers {
|
||||
|
|
|
@ -158,3 +158,16 @@ bool SimpleWeatherService::CurrentWeather::operator==(const SimpleWeatherService
|
|||
this->maxTemperature == other.maxTemperature && this->minTemperature == other.maxTemperature &&
|
||||
std::strcmp(this->location.data(), other.location.data()) == 0;
|
||||
}
|
||||
|
||||
bool SimpleWeatherService::Forecast::Day::operator==(const SimpleWeatherService::Forecast::Day& other) const {
|
||||
return this->iconId == other.iconId && this->maxTemperature == other.maxTemperature && this->minTemperature == other.maxTemperature;
|
||||
}
|
||||
|
||||
bool SimpleWeatherService::Forecast::operator==(const SimpleWeatherService::Forecast& other) const {
|
||||
for (int i = 0; i < this->nbDays; i++) {
|
||||
if (this->days[i] != other.days[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return this->timestamp == other.timestamp && this->nbDays == other.nbDays;
|
||||
}
|
||||
|
|
|
@ -96,9 +96,13 @@ namespace Pinetime {
|
|||
int16_t minTemperature;
|
||||
int16_t maxTemperature;
|
||||
Icons iconId;
|
||||
|
||||
bool operator==(const Day& other) const;
|
||||
};
|
||||
|
||||
std::array<Day, MaxNbForecastDays> days;
|
||||
|
||||
bool operator==(const Forecast& other) const;
|
||||
};
|
||||
|
||||
std::optional<CurrentWeather> Current() const;
|
||||
|
|
|
@ -115,8 +115,8 @@ const char* DateTime::MonthShortToStringLow(Months month) {
|
|||
return MonthsStringLow[static_cast<uint8_t>(month)];
|
||||
}
|
||||
|
||||
const char* DateTime::DayOfWeekShortToStringLow() const {
|
||||
return DaysStringShortLow[static_cast<uint8_t>(DayOfWeek())];
|
||||
const char* DateTime::DayOfWeekShortToStringLow(Days day) {
|
||||
return DaysStringShortLow[static_cast<uint8_t>(day)];
|
||||
}
|
||||
|
||||
void DateTime::Register(Pinetime::System::SystemTask* systemTask) {
|
||||
|
|
|
@ -122,7 +122,7 @@ namespace Pinetime {
|
|||
const char* MonthShortToString() const;
|
||||
const char* DayOfWeekShortToString() const;
|
||||
static const char* MonthShortToStringLow(Months month);
|
||||
const char* DayOfWeekShortToStringLow() const;
|
||||
static const char* DayOfWeekShortToStringLow(Days day);
|
||||
|
||||
std::chrono::time_point<std::chrono::system_clock, std::chrono::nanoseconds> CurrentDateTime() const {
|
||||
return currentDateTime;
|
||||
|
|
|
@ -1,196 +0,0 @@
|
|||
#include "components/gfx/Gfx.h"
|
||||
#include "drivers/St7789.h"
|
||||
using namespace Pinetime::Components;
|
||||
|
||||
Gfx::Gfx(Pinetime::Drivers::St7789& lcd) : lcd {lcd} {
|
||||
}
|
||||
|
||||
void Gfx::Init() {
|
||||
}
|
||||
|
||||
void Gfx::ClearScreen() {
|
||||
SetBackgroundColor(0x0000);
|
||||
|
||||
state.remainingIterations = 240 + 1;
|
||||
state.currentIteration = 0;
|
||||
state.busy = true;
|
||||
state.action = Action::FillRectangle;
|
||||
state.taskToNotify = xTaskGetCurrentTaskHandle();
|
||||
|
||||
lcd.DrawBuffer(0, 0, width, height, reinterpret_cast<const uint8_t*>(buffer), width * 2);
|
||||
WaitTransferFinished();
|
||||
}
|
||||
|
||||
void Gfx::FillRectangle(uint8_t x, uint8_t y, uint8_t w, uint8_t h, uint16_t color) {
|
||||
SetBackgroundColor(color);
|
||||
|
||||
state.remainingIterations = h;
|
||||
state.currentIteration = 0;
|
||||
state.busy = true;
|
||||
state.action = Action::FillRectangle;
|
||||
state.color = color;
|
||||
state.taskToNotify = xTaskGetCurrentTaskHandle();
|
||||
|
||||
lcd.DrawBuffer(x, y, w, h, reinterpret_cast<const uint8_t*>(buffer), width * 2);
|
||||
|
||||
WaitTransferFinished();
|
||||
}
|
||||
|
||||
void Gfx::FillRectangle(uint8_t x, uint8_t y, uint8_t w, uint8_t h, uint8_t* b) {
|
||||
state.remainingIterations = h;
|
||||
state.currentIteration = 0;
|
||||
state.busy = true;
|
||||
state.action = Action::FillRectangle;
|
||||
state.color = 0x00;
|
||||
state.taskToNotify = xTaskGetCurrentTaskHandle();
|
||||
|
||||
lcd.DrawBuffer(x, y, w, h, reinterpret_cast<const uint8_t*>(b), width * 2);
|
||||
|
||||
WaitTransferFinished();
|
||||
}
|
||||
|
||||
void Gfx::DrawString(uint8_t x, uint8_t y, uint16_t color, const char* text, const FONT_INFO* p_font, bool wrap) {
|
||||
if (y > (height - p_font->height)) {
|
||||
// Not enough space to write even single char.
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t current_x = x;
|
||||
uint8_t current_y = y;
|
||||
|
||||
for (size_t i = 0; text[i] != '\0'; i++) {
|
||||
if (text[i] == '\n') {
|
||||
current_x = x;
|
||||
current_y += p_font->height + p_font->height / 10;
|
||||
} else {
|
||||
DrawChar(p_font, (uint8_t) text[i], ¤t_x, current_y, color);
|
||||
}
|
||||
|
||||
uint8_t char_idx = text[i] - p_font->startChar;
|
||||
uint16_t char_width = text[i] == ' ' ? (p_font->height / 2) : p_font->charInfo[char_idx].widthBits;
|
||||
|
||||
if (current_x > (width - char_width)) {
|
||||
if (wrap) {
|
||||
current_x = x;
|
||||
current_y += p_font->height + p_font->height / 10;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
||||
if (y > (height - p_font->height)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Gfx::DrawChar(const FONT_INFO* font, uint8_t c, uint8_t* x, uint8_t y, uint16_t color) {
|
||||
uint8_t char_idx = c - font->startChar;
|
||||
uint16_t bytes_in_line = CEIL_DIV(font->charInfo[char_idx].widthBits, 8);
|
||||
uint16_t bg = 0x0000;
|
||||
|
||||
if (c == ' ') {
|
||||
*x += font->height / 2;
|
||||
return;
|
||||
}
|
||||
|
||||
// Build first line
|
||||
for (uint16_t j = 0; j < bytes_in_line; j++) {
|
||||
for (uint8_t k = 0; k < 8; k++) {
|
||||
if ((1 << (7 - k)) & font->data[font->charInfo[char_idx].offset + j]) {
|
||||
buffer[(j * 8) + k] = color;
|
||||
} else {
|
||||
buffer[(j * 8) + k] = bg;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
state.remainingIterations = font->height + 0;
|
||||
state.currentIteration = 0;
|
||||
state.busy = true;
|
||||
state.action = Action::DrawChar;
|
||||
state.font = const_cast<FONT_INFO*>(font);
|
||||
state.character = c;
|
||||
state.color = color;
|
||||
state.taskToNotify = xTaskGetCurrentTaskHandle();
|
||||
|
||||
lcd.DrawBuffer(*x, y, bytes_in_line * 8, font->height, reinterpret_cast<const uint8_t*>(&buffer), bytes_in_line * 8 * 2);
|
||||
WaitTransferFinished();
|
||||
|
||||
*x += font->charInfo[char_idx].widthBits + font->spacePixels;
|
||||
}
|
||||
|
||||
void Gfx::pixel_draw(uint8_t x, uint8_t y, uint16_t color) {
|
||||
lcd.DrawPixel(x, y, color);
|
||||
}
|
||||
|
||||
void Gfx::Sleep() {
|
||||
lcd.Sleep();
|
||||
}
|
||||
|
||||
void Gfx::Wakeup() {
|
||||
lcd.Wakeup();
|
||||
}
|
||||
|
||||
void Gfx::SetBackgroundColor(uint16_t color) {
|
||||
for (int i = 0; i < width; i++) {
|
||||
buffer[i] = color;
|
||||
}
|
||||
}
|
||||
|
||||
bool Gfx::GetNextBuffer(uint8_t** data, size_t& size) {
|
||||
if (!state.busy)
|
||||
return false;
|
||||
state.remainingIterations = state.remainingIterations - 1;
|
||||
if (state.remainingIterations == 0) {
|
||||
state.busy = false;
|
||||
NotifyEndOfTransfer(state.taskToNotify);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (state.action == Action::FillRectangle) {
|
||||
*data = reinterpret_cast<uint8_t*>(buffer);
|
||||
size = width * 2;
|
||||
} else if (state.action == Action::DrawChar) {
|
||||
uint16_t bg = 0x0000;
|
||||
uint8_t char_idx = state.character - state.font->startChar;
|
||||
uint16_t bytes_in_line = CEIL_DIV(state.font->charInfo[char_idx].widthBits, 8);
|
||||
|
||||
for (uint16_t j = 0; j < bytes_in_line; j++) {
|
||||
for (uint8_t k = 0; k < 8; k++) {
|
||||
if ((1 << (7 - k)) & state.font->data[state.font->charInfo[char_idx].offset + ((state.currentIteration + 1) * bytes_in_line) + j]) {
|
||||
buffer[(j * 8) + k] = state.color;
|
||||
} else {
|
||||
buffer[(j * 8) + k] = bg;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*data = reinterpret_cast<uint8_t*>(buffer);
|
||||
size = bytes_in_line * 8 * 2;
|
||||
}
|
||||
|
||||
state.currentIteration = state.currentIteration + 1;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Gfx::NotifyEndOfTransfer(TaskHandle_t task) {
|
||||
if (task != nullptr) {
|
||||
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
|
||||
vTaskNotifyGiveFromISR(task, &xHigherPriorityTaskWoken);
|
||||
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
|
||||
}
|
||||
}
|
||||
|
||||
void Gfx::WaitTransferFinished() const {
|
||||
ulTaskNotifyTake(pdTRUE, 500);
|
||||
}
|
||||
|
||||
void Gfx::SetScrollArea(uint16_t topFixedLines, uint16_t scrollLines, uint16_t bottomFixedLines) {
|
||||
lcd.VerticalScrollDefinition(topFixedLines, scrollLines, bottomFixedLines);
|
||||
}
|
||||
|
||||
void Gfx::SetScrollStartLine(uint16_t line) {
|
||||
lcd.VerticalScrollStartAddress(line);
|
||||
}
|
|
@ -1,62 +0,0 @@
|
|||
#pragma once
|
||||
#include <FreeRTOS.h>
|
||||
#include <nrf_font.h>
|
||||
#include <task.h>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include "drivers/BufferProvider.h"
|
||||
|
||||
namespace Pinetime {
|
||||
namespace Drivers {
|
||||
class St7789;
|
||||
}
|
||||
|
||||
namespace Components {
|
||||
class Gfx : public Pinetime::Drivers::BufferProvider {
|
||||
public:
|
||||
explicit Gfx(Drivers::St7789& lcd);
|
||||
void Init();
|
||||
void ClearScreen();
|
||||
void DrawString(uint8_t x, uint8_t y, uint16_t color, const char* text, const FONT_INFO* p_font, bool wrap);
|
||||
void DrawChar(const FONT_INFO* font, uint8_t c, uint8_t* x, uint8_t y, uint16_t color);
|
||||
void FillRectangle(uint8_t x, uint8_t y, uint8_t width, uint8_t height, uint16_t color);
|
||||
void FillRectangle(uint8_t x, uint8_t y, uint8_t w, uint8_t h, uint8_t* b);
|
||||
void SetScrollArea(uint16_t topFixedLines, uint16_t scrollLines, uint16_t bottomFixedLines);
|
||||
void SetScrollStartLine(uint16_t line);
|
||||
|
||||
void Sleep();
|
||||
void Wakeup();
|
||||
bool GetNextBuffer(uint8_t** buffer, size_t& size) override;
|
||||
void pixel_draw(uint8_t x, uint8_t y, uint16_t color);
|
||||
|
||||
private:
|
||||
static constexpr uint8_t width = 240;
|
||||
static constexpr uint8_t height = 240;
|
||||
|
||||
enum class Action { None, FillRectangle, DrawChar };
|
||||
|
||||
struct State {
|
||||
State() : busy {false}, action {Action::None}, remainingIterations {0}, currentIteration {0} {
|
||||
}
|
||||
|
||||
volatile bool busy;
|
||||
volatile Action action;
|
||||
volatile uint16_t remainingIterations;
|
||||
volatile uint16_t currentIteration;
|
||||
volatile FONT_INFO* font;
|
||||
volatile uint16_t color;
|
||||
volatile uint8_t character;
|
||||
volatile TaskHandle_t taskToNotify = nullptr;
|
||||
};
|
||||
|
||||
volatile State state;
|
||||
|
||||
uint16_t buffer[width]; // 1 line buffer
|
||||
Drivers::St7789& lcd;
|
||||
|
||||
void SetBackgroundColor(uint16_t color);
|
||||
void WaitTransferFinished() const;
|
||||
void NotifyEndOfTransfer(TaskHandle_t task);
|
||||
};
|
||||
}
|
||||
}
|
|
@ -40,15 +40,15 @@ void MotionController::Update(int16_t x, int16_t y, int16_t z, uint32_t nbSteps)
|
|||
service->OnNewStepCountValue(nbSteps);
|
||||
}
|
||||
|
||||
if (service != nullptr && (this->x != x || yHistory[0] != y || zHistory[0] != z)) {
|
||||
if (service != nullptr && (xHistory[0] != x || yHistory[0] != y || zHistory[0] != z)) {
|
||||
service->OnNewMotionValues(x, y, z);
|
||||
}
|
||||
|
||||
lastTime = time;
|
||||
time = xTaskGetTickCount();
|
||||
|
||||
lastX = this->x;
|
||||
this->x = x;
|
||||
xHistory++;
|
||||
xHistory[0] = x;
|
||||
yHistory++;
|
||||
yHistory[0] = y;
|
||||
zHistory++;
|
||||
|
@ -67,20 +67,26 @@ MotionController::AccelStats MotionController::GetAccelStats() const {
|
|||
AccelStats stats;
|
||||
|
||||
for (uint8_t i = 0; i < AccelStats::numHistory; i++) {
|
||||
stats.xMean += xHistory[histSize - i];
|
||||
stats.yMean += yHistory[histSize - i];
|
||||
stats.zMean += zHistory[histSize - i];
|
||||
stats.prevXMean += xHistory[1 + i];
|
||||
stats.prevYMean += yHistory[1 + i];
|
||||
stats.prevZMean += zHistory[1 + i];
|
||||
}
|
||||
stats.xMean /= AccelStats::numHistory;
|
||||
stats.yMean /= AccelStats::numHistory;
|
||||
stats.zMean /= AccelStats::numHistory;
|
||||
stats.prevXMean /= AccelStats::numHistory;
|
||||
stats.prevYMean /= AccelStats::numHistory;
|
||||
stats.prevZMean /= AccelStats::numHistory;
|
||||
|
||||
for (uint8_t i = 0; i < AccelStats::numHistory; i++) {
|
||||
stats.xVariance += (xHistory[histSize - i] - stats.xMean) * (xHistory[histSize - i] - stats.xMean);
|
||||
stats.yVariance += (yHistory[histSize - i] - stats.yMean) * (yHistory[histSize - i] - stats.yMean);
|
||||
stats.zVariance += (zHistory[histSize - i] - stats.zMean) * (zHistory[histSize - i] - stats.zMean);
|
||||
}
|
||||
stats.xVariance /= AccelStats::numHistory;
|
||||
stats.yVariance /= AccelStats::numHistory;
|
||||
stats.zVariance /= AccelStats::numHistory;
|
||||
|
||||
|
@ -93,7 +99,7 @@ bool MotionController::ShouldRaiseWake() const {
|
|||
constexpr int16_t yThresh = -64;
|
||||
constexpr int16_t rollDegreesThresh = -45;
|
||||
|
||||
if (x < -xThresh || x > xThresh) {
|
||||
if (std::abs(stats.xMean) > xThresh) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -107,8 +113,9 @@ bool MotionController::ShouldRaiseWake() const {
|
|||
|
||||
bool MotionController::ShouldShakeWake(uint16_t thresh) {
|
||||
/* Currently Polling at 10hz, If this ever goes faster scalar and EMA might need adjusting */
|
||||
int32_t speed =
|
||||
std::abs(zHistory[0] - zHistory[histSize - 1] + (yHistory[0] - yHistory[histSize - 1]) / 2 + (x - lastX) / 4) * 100 / (time - lastTime);
|
||||
int32_t speed = std::abs(zHistory[0] - zHistory[histSize - 1] + (yHistory[0] - yHistory[histSize - 1]) / 2 +
|
||||
(xHistory[0] - xHistory[histSize - 1]) / 4) *
|
||||
100 / (time - lastTime);
|
||||
// (.2 * speed) + ((1 - .2) * accumulatedSpeed);
|
||||
accumulatedSpeed = speed / 5 + accumulatedSpeed * 4 / 5;
|
||||
|
||||
|
@ -116,6 +123,11 @@ bool MotionController::ShouldShakeWake(uint16_t thresh) {
|
|||
}
|
||||
|
||||
bool MotionController::ShouldLowerSleep() const {
|
||||
if ((stats.xMean > 887 && DegreesRolled(stats.xMean, stats.zMean, stats.prevXMean, stats.prevZMean) > 30) ||
|
||||
(stats.xMean < -887 && DegreesRolled(stats.xMean, stats.zMean, stats.prevXMean, stats.prevZMean) < -30)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (stats.yMean < 724 || DegreesRolled(stats.yMean, stats.zMean, stats.prevYMean, stats.prevZMean) < 30) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ namespace Pinetime {
|
|||
void Update(int16_t x, int16_t y, int16_t z, uint32_t nbSteps);
|
||||
|
||||
int16_t X() const {
|
||||
return x;
|
||||
return xHistory[0];
|
||||
}
|
||||
|
||||
int16_t Y() const {
|
||||
|
@ -76,11 +76,14 @@ namespace Pinetime {
|
|||
struct AccelStats {
|
||||
static constexpr uint8_t numHistory = 2;
|
||||
|
||||
int16_t xMean = 0;
|
||||
int16_t yMean = 0;
|
||||
int16_t zMean = 0;
|
||||
int16_t prevXMean = 0;
|
||||
int16_t prevYMean = 0;
|
||||
int16_t prevZMean = 0;
|
||||
|
||||
uint32_t xVariance = 0;
|
||||
uint32_t yVariance = 0;
|
||||
uint32_t zVariance = 0;
|
||||
};
|
||||
|
@ -89,9 +92,8 @@ namespace Pinetime {
|
|||
|
||||
AccelStats stats = {};
|
||||
|
||||
int16_t lastX = 0;
|
||||
int16_t x = 0;
|
||||
static constexpr uint8_t histSize = 8;
|
||||
Utility::CircularBuffer<int16_t, histSize> xHistory = {};
|
||||
Utility::CircularBuffer<int16_t, histSize> yHistory = {};
|
||||
Utility::CircularBuffer<int16_t, histSize> zHistory = {};
|
||||
int32_t accumulatedSpeed = 0;
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include "displayapp/screens/BatteryInfo.h"
|
||||
#include "displayapp/screens/Steps.h"
|
||||
#include "displayapp/screens/Dice.h"
|
||||
#include "displayapp/screens/Weather.h"
|
||||
#include "displayapp/screens/PassKey.h"
|
||||
#include "displayapp/screens/Error.h"
|
||||
#include "displayapp/screens/Symbols.h"
|
||||
|
@ -143,9 +144,6 @@ void DisplayApp::Process(void* instance) {
|
|||
NRF_LOG_INFO("displayapp task started!");
|
||||
app->InitHw();
|
||||
|
||||
// Send a dummy notification to unlock the lvgl display driver for the first iteration
|
||||
xTaskNotifyGive(xTaskGetCurrentTaskHandle());
|
||||
|
||||
while (true) {
|
||||
app->Refresh();
|
||||
}
|
||||
|
@ -453,6 +451,7 @@ void DisplayApp::LoadScreen(Apps app, DisplayApp::FullRefreshDirections directio
|
|||
else {
|
||||
currentScreen.reset(userWatchFaces[0].create(controllers));
|
||||
}
|
||||
settingsController.SetAppMenu(0);
|
||||
} break;
|
||||
case Apps::Error:
|
||||
currentScreen = std::make_unique<Screens::Error>(bootError);
|
||||
|
@ -568,9 +567,7 @@ void DisplayApp::PushMessage(Messages msg) {
|
|||
if (in_isr()) {
|
||||
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
|
||||
xQueueSendFromISR(msgQueue, &msg, &xHigherPriorityTaskWoken);
|
||||
if (xHigherPriorityTaskWoken == pdTRUE) {
|
||||
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
|
||||
}
|
||||
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
|
||||
} else {
|
||||
TickType_t timeout = portMAX_DELAY;
|
||||
// Make xQueueSend() non-blocking if the message is a Notification message. We do this to avoid
|
||||
|
|
|
@ -38,9 +38,6 @@ void DisplayApp::Process(void* instance) {
|
|||
auto* app = static_cast<DisplayApp*>(instance);
|
||||
NRF_LOG_INFO("displayapp task started!");
|
||||
|
||||
// Send a dummy notification to unlock the lvgl display driver for the first iteration
|
||||
xTaskNotifyGive(xTaskGetCurrentTaskHandle());
|
||||
|
||||
app->InitHw();
|
||||
while (true) {
|
||||
app->Refresh();
|
||||
|
@ -94,7 +91,6 @@ void DisplayApp::DisplayLogo(uint16_t color) {
|
|||
Pinetime::Tools::RleDecoder rleDecoder(infinitime_nb, sizeof(infinitime_nb), color, colorBlack);
|
||||
for (int i = 0; i < displayWidth; i++) {
|
||||
rleDecoder.DecodeNext(displayBuffer, displayWidth * bytesPerPixel);
|
||||
ulTaskNotifyTake(pdTRUE, 500);
|
||||
lcd.DrawBuffer(0, i, displayWidth, 1, reinterpret_cast<const uint8_t*>(displayBuffer), displayWidth * bytesPerPixel);
|
||||
}
|
||||
}
|
||||
|
@ -103,20 +99,15 @@ void DisplayApp::DisplayOtaProgress(uint8_t percent, uint16_t color) {
|
|||
const uint8_t barHeight = 20;
|
||||
std::fill(displayBuffer, displayBuffer + (displayWidth * bytesPerPixel), color);
|
||||
for (int i = 0; i < barHeight; i++) {
|
||||
ulTaskNotifyTake(pdTRUE, 500);
|
||||
uint16_t barWidth = std::min(static_cast<float>(percent) * 2.4f, static_cast<float>(displayWidth));
|
||||
lcd.DrawBuffer(0, displayWidth - barHeight + i, barWidth, 1, reinterpret_cast<const uint8_t*>(displayBuffer), barWidth * bytesPerPixel);
|
||||
}
|
||||
}
|
||||
|
||||
void DisplayApp::PushMessage(Display::Messages msg) {
|
||||
BaseType_t xHigherPriorityTaskWoken;
|
||||
xHigherPriorityTaskWoken = pdFALSE;
|
||||
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
|
||||
xQueueSendFromISR(msgQueue, &msg, &xHigherPriorityTaskWoken);
|
||||
if (xHigherPriorityTaskWoken) {
|
||||
/* Actual macro used here is port specific. */
|
||||
// TODO : should I do something here?
|
||||
}
|
||||
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
|
||||
}
|
||||
|
||||
void DisplayApp::Register(Pinetime::System::SystemTask* /*systemTask*/) {
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
#include <drivers/SpiMaster.h>
|
||||
#include <bits/unique_ptr.h>
|
||||
#include <queue.h>
|
||||
#include "components/gfx/Gfx.h"
|
||||
#include "drivers/Cst816s.h"
|
||||
#include <drivers/Watchdog.h>
|
||||
#include <components/motor/MotorController.h>
|
||||
|
|
|
@ -152,10 +152,6 @@ void LittleVgl::SetFullRefresh(FullRefreshDirections direction) {
|
|||
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)) {
|
||||
|
@ -219,7 +215,6 @@ void LittleVgl::FlushDisplay(const lv_area_t* area, lv_color_t* color_p) {
|
|||
|
||||
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;
|
||||
|
|
|
@ -28,6 +28,7 @@ namespace Pinetime {
|
|||
Motion,
|
||||
Steps,
|
||||
Dice,
|
||||
Weather,
|
||||
PassKey,
|
||||
QuickSettings,
|
||||
Settings,
|
||||
|
@ -41,8 +42,7 @@ namespace Pinetime {
|
|||
SettingChimes,
|
||||
SettingShakeThreshold,
|
||||
SettingBluetooth,
|
||||
Error,
|
||||
Weather
|
||||
Error
|
||||
};
|
||||
|
||||
enum class WatchFace : uint8_t {
|
||||
|
|
|
@ -13,7 +13,7 @@ else ()
|
|||
set(DEFAULT_USER_APP_TYPES "${DEFAULT_USER_APP_TYPES}, Apps::Dice")
|
||||
set(DEFAULT_USER_APP_TYPES "${DEFAULT_USER_APP_TYPES}, Apps::Metronome")
|
||||
set(DEFAULT_USER_APP_TYPES "${DEFAULT_USER_APP_TYPES}, Apps::Navigation")
|
||||
#set(DEFAULT_USER_APP_TYPES "${DEFAULT_USER_APP_TYPES}, Apps::Weather")
|
||||
set(DEFAULT_USER_APP_TYPES "${DEFAULT_USER_APP_TYPES}, Apps::Weather")
|
||||
#set(DEFAULT_USER_APP_TYPES "${DEFAULT_USER_APP_TYPES}, Apps::Motion")
|
||||
set(USERAPP_TYPES "${DEFAULT_USER_APP_TYPES}" CACHE STRING "List of user apps to build into the firmware")
|
||||
endif ()
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
- Define the new symbols in `src/displayapp/screens/Symbols.h`:
|
||||
|
||||
```
|
||||
static constexpr const char* newSymbol = "\xEF\x86\x85";
|
||||
static constexpr const char* newSymbol = "\xEF\x99\x81";
|
||||
```
|
||||
|
||||
### the config file format:
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
},
|
||||
{
|
||||
"file": "FontAwesome5-Solid+Brands+Regular.woff",
|
||||
"range": "0xf294, 0xf242, 0xf54b, 0xf21e, 0xf1e6, 0xf017, 0xf129, 0xf03a, 0xf185, 0xf560, 0xf001, 0xf3fd, 0xf1fc, 0xf45d, 0xf59f, 0xf5a0, 0xf027, 0xf028, 0xf6a9, 0xf04b, 0xf04c, 0xf048, 0xf051, 0xf095, 0xf3dd, 0xf04d, 0xf2f2, 0xf024, 0xf252, 0xf569, 0xf06e, 0xf015, 0xf00c, 0xf522, 0xf743"
|
||||
"range": "0xf294, 0xf242, 0xf54b, 0xf21e, 0xf1e6, 0xf017, 0xf129, 0xf03a, 0xf185, 0xf560, 0xf001, 0xf3fd, 0xf1fc, 0xf45d, 0xf59f, 0xf5a0, 0xf027, 0xf028, 0xf6a9, 0xf04b, 0xf04c, 0xf048, 0xf051, 0xf095, 0xf3dd, 0xf04d, 0xf2f2, 0xf024, 0xf252, 0xf569, 0xf06e, 0xf015, 0xf00c, 0xf0f3, 0xf522, 0xf743"
|
||||
}
|
||||
],
|
||||
"bpp": 1,
|
||||
|
@ -18,7 +18,7 @@
|
|||
"sources": [
|
||||
{
|
||||
"file": "JetBrainsMono-Regular.ttf",
|
||||
"range": "0x25, 0x2b, 0x2d, 0x30-0x3a, 0x4b-0x4d, 0x66, 0x69, 0x6b, 0x6d, 0x74"
|
||||
"range": "0x25, 0x2b, 0x2d, 0x2e, 0x30-0x3a, 0x43, 0x46, 0x4b-0x4d, 0x66, 0x69, 0x6b, 0x6d, 0x74, 0xb0"
|
||||
}
|
||||
],
|
||||
"bpp": 1,
|
||||
|
@ -28,7 +28,7 @@
|
|||
"sources": [
|
||||
{
|
||||
"file": "JetBrainsMono-Light.ttf",
|
||||
"range": "0x25, 0x2D, 0x2F, 0x30-0x3a"
|
||||
"range": "0x25, 0x2D, 0x2F, 0x30-0x3a, 0x43, 0x46, 0xb0"
|
||||
}
|
||||
],
|
||||
"bpp": 1,
|
||||
|
|
|
@ -69,7 +69,7 @@ namespace Pinetime {
|
|||
template <>
|
||||
struct AppTraits<Apps::Alarm> {
|
||||
static constexpr Apps app = Apps::Alarm;
|
||||
static constexpr const char* icon = Screens::Symbols::clock;
|
||||
static constexpr const char* icon = Screens::Symbols::bell;
|
||||
|
||||
static Screens::Screen* Create(AppControllers& controllers) {
|
||||
return new Screens::Alarm(controllers.alarmController,
|
||||
|
|
|
@ -41,7 +41,7 @@ HeartRate::HeartRate(Controllers::HeartRateController& heartRateController, Syst
|
|||
lv_obj_set_style_local_text_color(label_hr, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Colors::lightGray);
|
||||
}
|
||||
|
||||
lv_label_set_text_static(label_hr, "000");
|
||||
lv_label_set_text_static(label_hr, "---");
|
||||
lv_obj_align(label_hr, nullptr, LV_ALIGN_CENTER, 0, -40);
|
||||
|
||||
label_bpm = lv_label_create(lv_scr_act(), nullptr);
|
||||
|
@ -82,10 +82,14 @@ void HeartRate::Refresh() {
|
|||
case Controllers::HeartRateController::States::NoTouch:
|
||||
case Controllers::HeartRateController::States::NotEnoughData:
|
||||
// case Controllers::HeartRateController::States::Stopped:
|
||||
lv_label_set_text_static(label_hr, "000");
|
||||
lv_label_set_text_static(label_hr, "---");
|
||||
break;
|
||||
default:
|
||||
lv_label_set_text_fmt(label_hr, "%03d", heartRateController.HeartRate());
|
||||
if (heartRateController.HeartRate() == 0) {
|
||||
lv_label_set_text_static(label_hr, "---");
|
||||
} else {
|
||||
lv_label_set_text_fmt(label_hr, "%03d", heartRateController.HeartRate());
|
||||
}
|
||||
}
|
||||
|
||||
lv_label_set_text_static(label_status, ToString(state));
|
||||
|
|
|
@ -53,9 +53,9 @@ void Motion::Refresh() {
|
|||
lv_label_set_text_fmt(labelStep, "Steps %lu", motionController.NbSteps());
|
||||
|
||||
lv_label_set_text_fmt(label,
|
||||
"X #FF0000 %d# Y #00B000 %d# Z #FFFF00 %d#",
|
||||
motionController.X() / 0x10,
|
||||
motionController.Y() / 0x10,
|
||||
motionController.Z() / 0x10);
|
||||
"X #FF0000 %d# Y #00B000 %d# Z #FFFF00 %d# mg",
|
||||
motionController.X(),
|
||||
motionController.Y(),
|
||||
motionController.Z());
|
||||
lv_obj_align(label, nullptr, LV_ALIGN_IN_TOP_MID, 0, 10);
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ namespace Pinetime {
|
|||
static constexpr const char* plug = "\xEF\x87\xA6";
|
||||
static constexpr const char* shoe = "\xEF\x95\x8B";
|
||||
static constexpr const char* clock = "\xEF\x80\x97";
|
||||
static constexpr const char* bell = "\xEF\x83\xB3";
|
||||
static constexpr const char* info = "\xEF\x84\xA9";
|
||||
static constexpr const char* list = "\xEF\x80\xBA";
|
||||
static constexpr const char* sun = "\xEF\x86\x85";
|
||||
|
|
|
@ -256,7 +256,7 @@ void WatchFaceAnalog::Refresh() {
|
|||
if (currentDateTime.IsUpdated()) {
|
||||
UpdateClock();
|
||||
|
||||
currentDate = std::chrono::time_point_cast<days>(currentDateTime.Get());
|
||||
currentDate = std::chrono::time_point_cast<std::chrono::days>(currentDateTime.Get());
|
||||
if (currentDate.IsUpdated()) {
|
||||
lv_label_set_text_fmt(label_date_day, "%s\n%02i", dateTimeController.DayOfWeekShortToString(), dateTimeController.Day());
|
||||
}
|
||||
|
|
|
@ -43,8 +43,7 @@ namespace Pinetime {
|
|||
Utility::DirtyValue<bool> bleState {};
|
||||
Utility::DirtyValue<std::chrono::time_point<std::chrono::system_clock, std::chrono::nanoseconds>> currentDateTime;
|
||||
Utility::DirtyValue<bool> notificationState {false};
|
||||
using days = std::chrono::duration<int32_t, std::ratio<86400>>; // TODO: days is standard in c++20
|
||||
Utility::DirtyValue<std::chrono::time_point<std::chrono::system_clock, days>> currentDate;
|
||||
Utility::DirtyValue<std::chrono::time_point<std::chrono::system_clock, std::chrono::days>> currentDate;
|
||||
|
||||
lv_obj_t* minor_scales;
|
||||
lv_obj_t* major_scales;
|
||||
|
|
|
@ -244,7 +244,7 @@ void WatchFaceCasioStyleG7710::Refresh() {
|
|||
}
|
||||
lv_obj_realign(label_time);
|
||||
|
||||
currentDate = std::chrono::time_point_cast<days>(currentDateTime.Get());
|
||||
currentDate = std::chrono::time_point_cast<std::chrono::days>(currentDateTime.Get());
|
||||
if (currentDate.IsUpdated()) {
|
||||
const char* weekNumberFormat = "%V";
|
||||
|
||||
|
|
|
@ -51,8 +51,7 @@ namespace Pinetime {
|
|||
Utility::DirtyValue<uint8_t> heartbeat {};
|
||||
Utility::DirtyValue<bool> heartbeatRunning {};
|
||||
Utility::DirtyValue<bool> notificationState {};
|
||||
using days = std::chrono::duration<int32_t, std::ratio<86400>>; // TODO: days is standard in c++20
|
||||
Utility::DirtyValue<std::chrono::time_point<std::chrono::system_clock, days>> currentDate;
|
||||
Utility::DirtyValue<std::chrono::time_point<std::chrono::system_clock, std::chrono::days>> currentDate;
|
||||
|
||||
lv_point_t line_icons_points[3] {{0, 5}, {117, 5}, {122, 0}};
|
||||
lv_point_t line_day_of_week_number_points[4] {{0, 0}, {100, 0}, {95, 95}, {0, 95}};
|
||||
|
|
|
@ -40,16 +40,16 @@ WatchFaceDigital::WatchFaceDigital(Controllers::DateTime& dateTimeController,
|
|||
lv_obj_align(notificationIcon, nullptr, LV_ALIGN_IN_TOP_LEFT, 0, 0);
|
||||
|
||||
weatherIcon = lv_label_create(lv_scr_act(), nullptr);
|
||||
lv_obj_set_style_local_text_color(weatherIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_WHITE);
|
||||
lv_obj_set_style_local_text_color(weatherIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x999999));
|
||||
lv_obj_set_style_local_text_font(weatherIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &fontawesome_weathericons);
|
||||
lv_label_set_text(weatherIcon, "");
|
||||
lv_obj_align(weatherIcon, nullptr, LV_ALIGN_IN_TOP_MID, -20, 0);
|
||||
lv_obj_align(weatherIcon, nullptr, LV_ALIGN_IN_TOP_MID, -20, 50);
|
||||
lv_obj_set_auto_realign(weatherIcon, true);
|
||||
|
||||
temperature = lv_label_create(lv_scr_act(), nullptr);
|
||||
lv_obj_set_style_local_text_color(temperature, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_WHITE);
|
||||
lv_obj_set_style_local_text_color(temperature, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x999999));
|
||||
lv_label_set_text(temperature, "");
|
||||
lv_obj_align(temperature, nullptr, LV_ALIGN_IN_TOP_MID, 20, 0);
|
||||
lv_obj_align(temperature, nullptr, LV_ALIGN_IN_TOP_MID, 20, 50);
|
||||
|
||||
label_date = lv_label_create(lv_scr_act(), nullptr);
|
||||
lv_obj_align(label_date, lv_scr_act(), LV_ALIGN_CENTER, 0, 60);
|
||||
|
@ -125,7 +125,7 @@ void WatchFaceDigital::Refresh() {
|
|||
lv_obj_align(label_time, lv_scr_act(), LV_ALIGN_CENTER, 0, 0);
|
||||
}
|
||||
|
||||
currentDate = std::chrono::time_point_cast<days>(currentDateTime.Get());
|
||||
currentDate = std::chrono::time_point_cast<std::chrono::days>(currentDateTime.Get());
|
||||
if (currentDate.IsUpdated()) {
|
||||
uint16_t year = dateTimeController.Year();
|
||||
uint8_t day = dateTimeController.Day();
|
||||
|
|
|
@ -43,10 +43,6 @@ namespace Pinetime {
|
|||
uint8_t displayedHour = -1;
|
||||
uint8_t displayedMinute = -1;
|
||||
|
||||
Utility::DirtyValue<uint8_t> batteryPercentRemaining {};
|
||||
Utility::DirtyValue<bool> powerPresent {};
|
||||
Utility::DirtyValue<bool> bleState {};
|
||||
Utility::DirtyValue<bool> bleRadioEnabled {};
|
||||
Utility::DirtyValue<std::chrono::time_point<std::chrono::system_clock, std::chrono::minutes>> currentDateTime {};
|
||||
Utility::DirtyValue<uint32_t> stepCount {};
|
||||
Utility::DirtyValue<uint8_t> heartbeat {};
|
||||
|
@ -54,8 +50,7 @@ namespace Pinetime {
|
|||
Utility::DirtyValue<bool> notificationState {};
|
||||
Utility::DirtyValue<std::optional<Pinetime::Controllers::SimpleWeatherService::CurrentWeather>> currentWeather {};
|
||||
|
||||
using days = std::chrono::duration<int32_t, std::ratio<86400>>; // TODO: days is standard in c++20
|
||||
Utility::DirtyValue<std::chrono::time_point<std::chrono::system_clock, days>> currentDate;
|
||||
Utility::DirtyValue<std::chrono::time_point<std::chrono::system_clock, std::chrono::days>> currentDate;
|
||||
|
||||
lv_obj_t* label_time;
|
||||
lv_obj_t* label_time_ampm;
|
||||
|
|
|
@ -423,10 +423,11 @@ void WatchFaceInfineat::Refresh() {
|
|||
lv_obj_align(labelMinutes, timeContainer, LV_ALIGN_IN_BOTTOM_MID, 0, 0);
|
||||
}
|
||||
|
||||
currentDate = std::chrono::time_point_cast<days>(currentDateTime.Get());
|
||||
currentDate = std::chrono::time_point_cast<std::chrono::days>(currentDateTime.Get());
|
||||
if (currentDate.IsUpdated()) {
|
||||
uint8_t day = dateTimeController.Day();
|
||||
lv_label_set_text_fmt(labelDate, "%s %02d", dateTimeController.DayOfWeekShortToStringLow(), day);
|
||||
Controllers::DateTime::Days dayOfWeek = dateTimeController.DayOfWeek();
|
||||
lv_label_set_text_fmt(labelDate, "%s %02d", dateTimeController.DayOfWeekShortToStringLow(dayOfWeek), day);
|
||||
lv_obj_realign(labelDate);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -55,8 +55,7 @@ namespace Pinetime {
|
|||
Utility::DirtyValue<std::chrono::time_point<std::chrono::system_clock, std::chrono::minutes>> currentDateTime {};
|
||||
Utility::DirtyValue<uint32_t> stepCount {};
|
||||
Utility::DirtyValue<bool> notificationState {};
|
||||
using days = std::chrono::duration<int32_t, std::ratio<86400>>; // TODO: days is standard in c++20
|
||||
Utility::DirtyValue<std::chrono::time_point<std::chrono::system_clock, days>> currentDate;
|
||||
Utility::DirtyValue<std::chrono::time_point<std::chrono::system_clock, std::chrono::days>> currentDate;
|
||||
|
||||
// Lines making up the side cover
|
||||
lv_obj_t* lineBattery;
|
||||
|
|
|
@ -125,7 +125,7 @@ void WatchFaceTerminal::Refresh() {
|
|||
lv_label_set_text_fmt(label_time, "[TIME]#11cc55 %02d:%02d:%02d", hour, minute, second);
|
||||
}
|
||||
|
||||
currentDate = std::chrono::time_point_cast<days>(currentDateTime.Get());
|
||||
currentDate = std::chrono::time_point_cast<std::chrono::days>(currentDateTime.Get());
|
||||
if (currentDate.IsUpdated()) {
|
||||
uint16_t year = dateTimeController.Year();
|
||||
Controllers::DateTime::Months month = dateTimeController.Month();
|
||||
|
|
|
@ -45,8 +45,7 @@ namespace Pinetime {
|
|||
Utility::DirtyValue<uint8_t> heartbeat {};
|
||||
Utility::DirtyValue<bool> heartbeatRunning {};
|
||||
Utility::DirtyValue<bool> notificationState {};
|
||||
using days = std::chrono::duration<int32_t, std::ratio<86400>>; // TODO: days is standard in c++20
|
||||
Utility::DirtyValue<std::chrono::time_point<std::chrono::system_clock, days>> currentDate;
|
||||
Utility::DirtyValue<std::chrono::time_point<std::chrono::system_clock, std::chrono::days>> currentDate;
|
||||
|
||||
lv_obj_t* label_time;
|
||||
lv_obj_t* label_date;
|
||||
|
|
198
src/displayapp/screens/Weather.cpp
Normal file
198
src/displayapp/screens/Weather.cpp
Normal file
|
@ -0,0 +1,198 @@
|
|||
#include "displayapp/screens/Weather.h"
|
||||
#include <lvgl/lvgl.h>
|
||||
#include "components/ble/SimpleWeatherService.h"
|
||||
#include "components/datetime/DateTimeController.h"
|
||||
#include "components/settings/Settings.h"
|
||||
#include "displayapp/DisplayApp.h"
|
||||
#include "displayapp/screens/WeatherSymbols.h"
|
||||
#include "displayapp/InfiniTimeTheme.h"
|
||||
|
||||
using namespace Pinetime::Applications::Screens;
|
||||
|
||||
namespace {
|
||||
lv_color_t TemperatureColor(int16_t temperature) {
|
||||
if (temperature <= 0) { // freezing
|
||||
return Colors::blue;
|
||||
} else if (temperature <= 400) { // ice
|
||||
return LV_COLOR_CYAN;
|
||||
} else if (temperature >= 2700) { // hot
|
||||
return Colors::deepOrange;
|
||||
}
|
||||
return Colors::orange; // normal
|
||||
}
|
||||
|
||||
uint8_t TemperatureStyle(int16_t temperature) {
|
||||
if (temperature <= 0) { // freezing
|
||||
return LV_TABLE_PART_CELL3;
|
||||
} else if (temperature <= 400) { // ice
|
||||
return LV_TABLE_PART_CELL4;
|
||||
} else if (temperature >= 2700) { // hot
|
||||
return LV_TABLE_PART_CELL6;
|
||||
}
|
||||
return LV_TABLE_PART_CELL5; // normal
|
||||
}
|
||||
|
||||
int16_t RoundTemperature(int16_t temp) {
|
||||
return temp = temp / 100 + (temp % 100 >= 50 ? 1 : 0);
|
||||
}
|
||||
}
|
||||
|
||||
Weather::Weather(Controllers::Settings& settingsController, Controllers::SimpleWeatherService& weatherService)
|
||||
: settingsController {settingsController}, weatherService {weatherService} {
|
||||
|
||||
temperature = lv_label_create(lv_scr_act(), nullptr);
|
||||
lv_obj_set_style_local_text_color(temperature, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_WHITE);
|
||||
lv_obj_set_style_local_text_font(temperature, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &jetbrains_mono_42);
|
||||
lv_label_set_text(temperature, "---");
|
||||
lv_obj_align(temperature, nullptr, LV_ALIGN_CENTER, 0, -30);
|
||||
lv_obj_set_auto_realign(temperature, true);
|
||||
|
||||
minTemperature = lv_label_create(lv_scr_act(), nullptr);
|
||||
lv_obj_set_style_local_text_color(minTemperature, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Colors::bg);
|
||||
lv_label_set_text(minTemperature, "");
|
||||
lv_obj_align(minTemperature, temperature, LV_ALIGN_OUT_LEFT_MID, -10, 0);
|
||||
lv_obj_set_auto_realign(minTemperature, true);
|
||||
|
||||
maxTemperature = lv_label_create(lv_scr_act(), nullptr);
|
||||
lv_obj_set_style_local_text_color(maxTemperature, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Colors::bg);
|
||||
lv_label_set_text(maxTemperature, "");
|
||||
lv_obj_align(maxTemperature, temperature, LV_ALIGN_OUT_RIGHT_MID, 10, 0);
|
||||
lv_obj_set_auto_realign(maxTemperature, true);
|
||||
|
||||
condition = lv_label_create(lv_scr_act(), nullptr);
|
||||
lv_obj_set_style_local_text_color(condition, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Colors::lightGray);
|
||||
lv_label_set_text(condition, "");
|
||||
lv_obj_align(condition, temperature, LV_ALIGN_OUT_TOP_MID, 0, -10);
|
||||
lv_obj_set_auto_realign(condition, true);
|
||||
|
||||
icon = lv_label_create(lv_scr_act(), nullptr);
|
||||
lv_obj_set_style_local_text_color(icon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_WHITE);
|
||||
lv_obj_set_style_local_text_font(icon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &fontawesome_weathericons);
|
||||
lv_label_set_text(icon, "");
|
||||
lv_obj_align(icon, condition, LV_ALIGN_OUT_TOP_MID, 0, 0);
|
||||
lv_obj_set_auto_realign(icon, true);
|
||||
|
||||
forecast = lv_table_create(lv_scr_act(), nullptr);
|
||||
lv_table_set_col_cnt(forecast, Controllers::SimpleWeatherService::MaxNbForecastDays);
|
||||
lv_table_set_row_cnt(forecast, 4);
|
||||
// LV_TABLE_PART_CELL1: Default table style
|
||||
lv_obj_set_style_local_border_color(forecast, LV_TABLE_PART_CELL1, LV_STATE_DEFAULT, LV_COLOR_BLACK);
|
||||
lv_obj_set_style_local_text_color(forecast, LV_TABLE_PART_CELL1, LV_STATE_DEFAULT, Colors::lightGray);
|
||||
// LV_TABLE_PART_CELL2: Condition icon
|
||||
lv_obj_set_style_local_border_color(forecast, LV_TABLE_PART_CELL2, LV_STATE_DEFAULT, LV_COLOR_BLACK);
|
||||
lv_obj_set_style_local_text_color(forecast, LV_TABLE_PART_CELL2, LV_STATE_DEFAULT, LV_COLOR_WHITE);
|
||||
lv_obj_set_style_local_text_font(forecast, LV_TABLE_PART_CELL2, LV_STATE_DEFAULT, &fontawesome_weathericons);
|
||||
// LV_TABLE_PART_CELL3: Freezing
|
||||
lv_obj_set_style_local_border_color(forecast, LV_TABLE_PART_CELL3, LV_STATE_DEFAULT, LV_COLOR_BLACK);
|
||||
lv_obj_set_style_local_text_color(forecast, LV_TABLE_PART_CELL3, LV_STATE_DEFAULT, Colors::blue);
|
||||
// LV_TABLE_PART_CELL4: Ice
|
||||
lv_obj_set_style_local_border_color(forecast, LV_TABLE_PART_CELL4, LV_STATE_DEFAULT, LV_COLOR_BLACK);
|
||||
lv_obj_set_style_local_text_color(forecast, LV_TABLE_PART_CELL4, LV_STATE_DEFAULT, LV_COLOR_CYAN);
|
||||
// LV_TABLE_PART_CELL5: Normal
|
||||
lv_obj_set_style_local_border_color(forecast, LV_TABLE_PART_CELL5, LV_STATE_DEFAULT, LV_COLOR_BLACK);
|
||||
lv_obj_set_style_local_text_color(forecast, LV_TABLE_PART_CELL5, LV_STATE_DEFAULT, Colors::orange);
|
||||
// LV_TABLE_PART_CELL6: Hot
|
||||
lv_obj_set_style_local_border_color(forecast, LV_TABLE_PART_CELL6, LV_STATE_DEFAULT, LV_COLOR_BLACK);
|
||||
lv_obj_set_style_local_text_color(forecast, LV_TABLE_PART_CELL6, LV_STATE_DEFAULT, Colors::deepOrange);
|
||||
|
||||
lv_obj_align(forecast, nullptr, LV_ALIGN_IN_BOTTOM_LEFT, 0, 0);
|
||||
|
||||
for (int i = 0; i < Controllers::SimpleWeatherService::MaxNbForecastDays; i++) {
|
||||
lv_table_set_col_width(forecast, i, 48);
|
||||
lv_table_set_cell_type(forecast, 1, i, LV_TABLE_PART_CELL2);
|
||||
lv_table_set_cell_align(forecast, 0, i, LV_LABEL_ALIGN_CENTER);
|
||||
lv_table_set_cell_align(forecast, 1, i, LV_LABEL_ALIGN_CENTER);
|
||||
lv_table_set_cell_align(forecast, 2, i, LV_LABEL_ALIGN_CENTER);
|
||||
lv_table_set_cell_align(forecast, 3, i, LV_LABEL_ALIGN_CENTER);
|
||||
}
|
||||
|
||||
taskRefresh = lv_task_create(RefreshTaskCallback, 1000, LV_TASK_PRIO_MID, this);
|
||||
Refresh();
|
||||
}
|
||||
|
||||
Weather::~Weather() {
|
||||
lv_task_del(taskRefresh);
|
||||
lv_obj_clean(lv_scr_act());
|
||||
}
|
||||
|
||||
void Weather::Refresh() {
|
||||
currentWeather = weatherService.Current();
|
||||
if (currentWeather.IsUpdated()) {
|
||||
auto optCurrentWeather = currentWeather.Get();
|
||||
if (optCurrentWeather) {
|
||||
int16_t temp = optCurrentWeather->temperature;
|
||||
int16_t minTemp = optCurrentWeather->minTemperature;
|
||||
int16_t maxTemp = optCurrentWeather->maxTemperature;
|
||||
lv_obj_set_style_local_text_color(temperature, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, TemperatureColor(temp));
|
||||
char tempUnit = 'C';
|
||||
if (settingsController.GetWeatherFormat() == Controllers::Settings::WeatherFormat::Imperial) {
|
||||
temp = Controllers::SimpleWeatherService::CelsiusToFahrenheit(temp);
|
||||
minTemp = Controllers::SimpleWeatherService::CelsiusToFahrenheit(minTemp);
|
||||
maxTemp = Controllers::SimpleWeatherService::CelsiusToFahrenheit(maxTemp);
|
||||
tempUnit = 'F';
|
||||
}
|
||||
lv_label_set_text(icon, Symbols::GetSymbol(optCurrentWeather->iconId));
|
||||
lv_label_set_text(condition, Symbols::GetCondition(optCurrentWeather->iconId));
|
||||
lv_label_set_text_fmt(temperature, "%d°%c", RoundTemperature(temp), tempUnit);
|
||||
lv_label_set_text_fmt(minTemperature, "%d°", RoundTemperature(minTemp));
|
||||
lv_label_set_text_fmt(maxTemperature, "%d°", RoundTemperature(maxTemp));
|
||||
} else {
|
||||
lv_label_set_text(icon, "");
|
||||
lv_label_set_text(condition, "");
|
||||
lv_label_set_text(temperature, "---");
|
||||
lv_obj_set_style_local_text_color(temperature, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_WHITE);
|
||||
lv_label_set_text(minTemperature, "");
|
||||
lv_label_set_text(maxTemperature, "");
|
||||
}
|
||||
}
|
||||
|
||||
currentForecast = weatherService.GetForecast();
|
||||
if (currentForecast.IsUpdated()) {
|
||||
auto optCurrentForecast = currentForecast.Get();
|
||||
if (optCurrentForecast) {
|
||||
std::tm localTime = *std::localtime(reinterpret_cast<const time_t*>(&optCurrentForecast->timestamp));
|
||||
|
||||
for (int i = 0; i < Controllers::SimpleWeatherService::MaxNbForecastDays; i++) {
|
||||
int16_t maxTemp = optCurrentForecast->days[i].maxTemperature;
|
||||
int16_t minTemp = optCurrentForecast->days[i].minTemperature;
|
||||
lv_table_set_cell_type(forecast, 2, i, TemperatureStyle(maxTemp));
|
||||
lv_table_set_cell_type(forecast, 3, i, TemperatureStyle(minTemp));
|
||||
if (settingsController.GetWeatherFormat() == Controllers::Settings::WeatherFormat::Imperial) {
|
||||
maxTemp = Controllers::SimpleWeatherService::CelsiusToFahrenheit(maxTemp);
|
||||
minTemp = Controllers::SimpleWeatherService::CelsiusToFahrenheit(minTemp);
|
||||
}
|
||||
uint8_t wday = localTime.tm_wday + i + 1;
|
||||
if (wday > 7) {
|
||||
wday -= 7;
|
||||
}
|
||||
maxTemp = RoundTemperature(maxTemp);
|
||||
minTemp = RoundTemperature(minTemp);
|
||||
const char* dayOfWeek = Controllers::DateTime::DayOfWeekShortToStringLow(static_cast<Controllers::DateTime::Days>(wday));
|
||||
lv_table_set_cell_value(forecast, 0, i, dayOfWeek);
|
||||
lv_table_set_cell_value(forecast, 1, i, Symbols::GetSymbol(optCurrentForecast->days[i].iconId));
|
||||
// Pad cells based on the largest number of digits on each column
|
||||
char maxPadding[3] = " ";
|
||||
char minPadding[3] = " ";
|
||||
int diff = snprintf(nullptr, 0, "%d", maxTemp) - snprintf(nullptr, 0, "%d", minTemp);
|
||||
if (diff <= 0) {
|
||||
maxPadding[-diff] = '\0';
|
||||
minPadding[0] = '\0';
|
||||
} else {
|
||||
maxPadding[0] = '\0';
|
||||
minPadding[diff] = '\0';
|
||||
}
|
||||
lv_table_set_cell_value_fmt(forecast, 2, i, "%s%d", maxPadding, maxTemp);
|
||||
lv_table_set_cell_value_fmt(forecast, 3, i, "%s%d", minPadding, minTemp);
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < Controllers::SimpleWeatherService::MaxNbForecastDays; i++) {
|
||||
lv_table_set_cell_value(forecast, 0, i, "");
|
||||
lv_table_set_cell_value(forecast, 1, i, "");
|
||||
lv_table_set_cell_value(forecast, 2, i, "");
|
||||
lv_table_set_cell_value(forecast, 3, i, "");
|
||||
lv_table_set_cell_type(forecast, 2, i, LV_TABLE_PART_CELL1);
|
||||
lv_table_set_cell_type(forecast, 3, i, LV_TABLE_PART_CELL1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
56
src/displayapp/screens/Weather.h
Normal file
56
src/displayapp/screens/Weather.h
Normal file
|
@ -0,0 +1,56 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <lvgl/lvgl.h>
|
||||
#include "displayapp/screens/Screen.h"
|
||||
#include "components/ble/SimpleWeatherService.h"
|
||||
#include "displayapp/apps/Apps.h"
|
||||
#include "displayapp/Controllers.h"
|
||||
#include "Symbols.h"
|
||||
#include "utility/DirtyValue.h"
|
||||
|
||||
namespace Pinetime {
|
||||
|
||||
namespace Controllers {
|
||||
class Settings;
|
||||
}
|
||||
|
||||
namespace Applications {
|
||||
namespace Screens {
|
||||
|
||||
class Weather : public Screen {
|
||||
public:
|
||||
Weather(Controllers::Settings& settingsController, Controllers::SimpleWeatherService& weatherService);
|
||||
~Weather() override;
|
||||
|
||||
void Refresh() override;
|
||||
|
||||
private:
|
||||
Controllers::Settings& settingsController;
|
||||
Controllers::SimpleWeatherService& weatherService;
|
||||
|
||||
Utility::DirtyValue<std::optional<Controllers::SimpleWeatherService::CurrentWeather>> currentWeather {};
|
||||
Utility::DirtyValue<std::optional<Controllers::SimpleWeatherService::Forecast>> currentForecast {};
|
||||
|
||||
lv_obj_t* icon;
|
||||
lv_obj_t* condition;
|
||||
lv_obj_t* temperature;
|
||||
lv_obj_t* minTemperature;
|
||||
lv_obj_t* maxTemperature;
|
||||
lv_obj_t* forecast;
|
||||
|
||||
lv_task_t* taskRefresh;
|
||||
};
|
||||
}
|
||||
|
||||
template <>
|
||||
struct AppTraits<Apps::Weather> {
|
||||
static constexpr Apps app = Apps::Weather;
|
||||
static constexpr const char* icon = Screens::Symbols::cloudSunRain;
|
||||
|
||||
static Screens::Screen* Create(AppControllers& controllers) {
|
||||
return new Screens::Weather(controllers.settingsController, *controllers.weatherController);
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
|
@ -34,3 +34,28 @@ const char* Pinetime::Applications::Screens::Symbols::GetSymbol(const Pinetime::
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const char* Pinetime::Applications::Screens::Symbols::GetCondition(const Pinetime::Controllers::SimpleWeatherService::Icons icon) {
|
||||
switch (icon) {
|
||||
case Pinetime::Controllers::SimpleWeatherService::Icons::Sun:
|
||||
return "Clear sky";
|
||||
case Pinetime::Controllers::SimpleWeatherService::Icons::CloudsSun:
|
||||
return "Few clouds";
|
||||
case Pinetime::Controllers::SimpleWeatherService::Icons::Clouds:
|
||||
return "Scattered clouds";
|
||||
case Pinetime::Controllers::SimpleWeatherService::Icons::BrokenClouds:
|
||||
return "Broken clouds";
|
||||
case Pinetime::Controllers::SimpleWeatherService::Icons::CloudShowerHeavy:
|
||||
return "Shower rain";
|
||||
case Pinetime::Controllers::SimpleWeatherService::Icons::CloudSunRain:
|
||||
return "Rain";
|
||||
case Pinetime::Controllers::SimpleWeatherService::Icons::Thunderstorm:
|
||||
return "Thunderstorm";
|
||||
case Pinetime::Controllers::SimpleWeatherService::Icons::Snow:
|
||||
return "Snow";
|
||||
case Pinetime::Controllers::SimpleWeatherService::Icons::Smog:
|
||||
return "Mist";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ namespace Pinetime {
|
|||
namespace Screens {
|
||||
namespace Symbols {
|
||||
const char* GetSymbol(const Pinetime::Controllers::SimpleWeatherService::Icons icon);
|
||||
const char* GetCondition(const Pinetime::Controllers::SimpleWeatherService::Icons icon);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,17 +36,19 @@ namespace {
|
|||
|
||||
SettingBluetooth::SettingBluetooth(Pinetime::Applications::DisplayApp* app, Pinetime::Controllers::Settings& settingsController)
|
||||
: app {app},
|
||||
settings {settingsController},
|
||||
checkboxList(
|
||||
0,
|
||||
1,
|
||||
"Bluetooth",
|
||||
Symbols::bluetooth,
|
||||
settingsController.GetBleRadioEnabled() ? 0 : 1,
|
||||
[&settings = settingsController](uint32_t index) {
|
||||
[this](uint32_t index) {
|
||||
const bool priorMode = settings.GetBleRadioEnabled();
|
||||
const bool newMode = options[index].radioEnabled;
|
||||
if (newMode != priorMode) {
|
||||
settings.SetBleRadioEnabled(newMode);
|
||||
this->app->PushMessage(Pinetime::Applications::Display::Messages::BleRadioEnableToggle);
|
||||
}
|
||||
},
|
||||
CreateOptionArray()) {
|
||||
|
@ -54,6 +56,4 @@ SettingBluetooth::SettingBluetooth(Pinetime::Applications::DisplayApp* app, Pine
|
|||
|
||||
SettingBluetooth::~SettingBluetooth() {
|
||||
lv_obj_clean(lv_scr_act());
|
||||
// Pushing the message in the OnValueChanged function causes a freeze?
|
||||
app->PushMessage(Pinetime::Applications::Display::Messages::BleRadioEnableToggle);
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ namespace Pinetime {
|
|||
|
||||
private:
|
||||
DisplayApp* app;
|
||||
Pinetime::Controllers::Settings& settings;
|
||||
CheckboxList checkboxList;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -22,6 +22,16 @@ namespace {
|
|||
void user_delay(uint32_t period_us, void* /*intf_ptr*/) {
|
||||
nrf_delay_us(period_us);
|
||||
}
|
||||
|
||||
// Scale factors to convert accelerometer counts to milli-g
|
||||
// from datasheet: https://files.pine64.org/doc/datasheet/pinetime/BST-BMA421-FL000.pdf
|
||||
// The array index to use is stored in accel_conf.range
|
||||
constexpr int16_t accelScaleFactors[] = {
|
||||
[BMA4_ACCEL_RANGE_2G] = 1024, // LSB/g +/- 2g range
|
||||
[BMA4_ACCEL_RANGE_4G] = 512, // LSB/g +/- 4g range
|
||||
[BMA4_ACCEL_RANGE_8G] = 256, // LSB/g +/- 8g range
|
||||
[BMA4_ACCEL_RANGE_16G] = 128 // LSB/g +/- 16g range
|
||||
};
|
||||
}
|
||||
|
||||
Bma421::Bma421(TwiMaster& twiMaster, uint8_t twiAddress) : twiMaster {twiMaster}, deviceAddress {twiAddress} {
|
||||
|
@ -74,7 +84,6 @@ void Bma421::Init() {
|
|||
if (ret != BMA4_OK)
|
||||
return;
|
||||
|
||||
struct bma4_accel_config accel_conf;
|
||||
accel_conf.odr = BMA4_OUTPUT_DATA_RATE_100HZ;
|
||||
accel_conf.range = BMA4_ACCEL_RANGE_2G;
|
||||
accel_conf.bandwidth = BMA4_ACCEL_NORMAL_AVG4;
|
||||
|
@ -102,8 +111,17 @@ void Bma421::Write(uint8_t registerAddress, const uint8_t* data, size_t size) {
|
|||
Bma421::Values Bma421::Process() {
|
||||
if (not isOk)
|
||||
return {};
|
||||
struct bma4_accel rawData;
|
||||
struct bma4_accel data;
|
||||
bma4_read_accel_xyz(&data, &bma);
|
||||
bma4_read_accel_xyz(&rawData, &bma);
|
||||
|
||||
// Scale the measured ADC counts to units of 'binary milli-g'
|
||||
// where 1g = 1024 'binary milli-g' units.
|
||||
// See https://github.com/InfiniTimeOrg/InfiniTime/pull/1950 for
|
||||
// discussion of why we opted for scaling to 1024 rather than 1000.
|
||||
data.x = 1024 * rawData.x / accelScaleFactors[accel_conf.range];
|
||||
data.y = 1024 * rawData.y / accelScaleFactors[accel_conf.range];
|
||||
data.z = 1024 * rawData.z / accelScaleFactors[accel_conf.range];
|
||||
|
||||
uint32_t steps = 0;
|
||||
bma423_step_counter_output(&steps, &bma);
|
||||
|
|
|
@ -41,6 +41,7 @@ namespace Pinetime {
|
|||
TwiMaster& twiMaster;
|
||||
uint8_t deviceAddress = 0x18;
|
||||
struct bma4_dev bma;
|
||||
struct bma4_accel_config accel_conf; // Store the device configuration for later reference.
|
||||
bool isOk = false;
|
||||
bool isResetOk = false;
|
||||
DeviceTypes deviceType = DeviceTypes::Unknown;
|
||||
|
|
|
@ -19,7 +19,7 @@ namespace {
|
|||
}
|
||||
|
||||
/** Driver for the HRS3300 heart rate sensor.
|
||||
* Original implementation from wasp-os : https://github.com/daniel-thompson/wasp-os/blob/master/wasp/drivers/hrs3300.py
|
||||
* Original implementation from wasp-os : https://github.com/wasp-os/wasp-os/blob/master/wasp/drivers/hrs3300.py
|
||||
*
|
||||
* Experimentaly derived changes to improve signal/noise (see comments below) - Ceimour
|
||||
*/
|
||||
|
|
|
@ -9,8 +9,8 @@ Spi::Spi(SpiMaster& spiMaster, uint8_t pinCsn) : spiMaster {spiMaster}, pinCsn {
|
|||
nrf_gpio_pin_set(pinCsn);
|
||||
}
|
||||
|
||||
bool Spi::Write(const uint8_t* data, size_t size) {
|
||||
return spiMaster.Write(pinCsn, data, size);
|
||||
bool Spi::Write(const uint8_t* data, size_t size, const std::function<void()>& preTransactionHook) {
|
||||
return spiMaster.Write(pinCsn, data, size, preTransactionHook);
|
||||
}
|
||||
|
||||
bool Spi::Read(uint8_t* cmd, size_t cmdSize, uint8_t* data, size_t dataSize) {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#pragma once
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
#include "drivers/SpiMaster.h"
|
||||
|
||||
namespace Pinetime {
|
||||
|
@ -14,7 +15,7 @@ namespace Pinetime {
|
|||
Spi& operator=(Spi&&) = delete;
|
||||
|
||||
bool Init();
|
||||
bool Write(const uint8_t* data, size_t size);
|
||||
bool Write(const uint8_t* data, size_t size, const std::function<void()>& preTransactionHook);
|
||||
bool Read(uint8_t* cmd, size_t cmdSize, uint8_t* data, size_t dataSize);
|
||||
bool WriteCmdAndBuffer(const uint8_t* cmd, size_t cmdSize, const uint8_t* data, size_t dataSize);
|
||||
void Sleep();
|
||||
|
|
|
@ -136,17 +136,11 @@ void SpiMaster::OnEndEvent() {
|
|||
|
||||
spiBaseAddress->TASKS_START = 1;
|
||||
} else {
|
||||
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
|
||||
if (taskToNotify != nullptr) {
|
||||
vTaskNotifyGiveFromISR(taskToNotify, &xHigherPriorityTaskWoken);
|
||||
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
|
||||
}
|
||||
|
||||
nrf_gpio_pin_set(this->pinCsn);
|
||||
currentBufferAddr = 0;
|
||||
BaseType_t xHigherPriorityTaskWoken2 = pdFALSE;
|
||||
xSemaphoreGiveFromISR(mutex, &xHigherPriorityTaskWoken2);
|
||||
portYIELD_FROM_ISR(xHigherPriorityTaskWoken | xHigherPriorityTaskWoken2);
|
||||
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
|
||||
xSemaphoreGiveFromISR(mutex, &xHigherPriorityTaskWoken);
|
||||
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -173,12 +167,11 @@ void SpiMaster::PrepareRx(const uint32_t bufferAddress, const size_t size) {
|
|||
spiBaseAddress->EVENTS_END = 0;
|
||||
}
|
||||
|
||||
bool SpiMaster::Write(uint8_t pinCsn, const uint8_t* data, size_t size) {
|
||||
bool SpiMaster::Write(uint8_t pinCsn, const uint8_t* data, size_t size, const std::function<void()>& preTransactionHook) {
|
||||
if (data == nullptr)
|
||||
return false;
|
||||
auto ok = xSemaphoreTake(mutex, portMAX_DELAY);
|
||||
ASSERT(ok == true);
|
||||
taskToNotify = xTaskGetCurrentTaskHandle();
|
||||
|
||||
this->pinCsn = pinCsn;
|
||||
|
||||
|
@ -188,6 +181,9 @@ bool SpiMaster::Write(uint8_t pinCsn, const uint8_t* data, size_t size) {
|
|||
DisableWorkaroundForFtpan58(spiBaseAddress, 0, 0);
|
||||
}
|
||||
|
||||
if (preTransactionHook != nullptr) {
|
||||
preTransactionHook();
|
||||
}
|
||||
nrf_gpio_pin_clear(this->pinCsn);
|
||||
|
||||
currentBufferAddr = (uint32_t) data;
|
||||
|
@ -216,8 +212,6 @@ bool SpiMaster::Write(uint8_t pinCsn, const uint8_t* data, size_t size) {
|
|||
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);
|
||||
|
@ -265,8 +259,6 @@ void SpiMaster::Wakeup() {
|
|||
bool SpiMaster::WriteCmdAndBuffer(uint8_t pinCsn, const uint8_t* cmd, size_t cmdSize, const uint8_t* data, size_t dataSize) {
|
||||
xSemaphoreTake(mutex, portMAX_DELAY);
|
||||
|
||||
taskToNotify = nullptr;
|
||||
|
||||
this->pinCsn = pinCsn;
|
||||
DisableWorkaroundForFtpan58(spiBaseAddress, 0, 0);
|
||||
spiBaseAddress->INTENCLR = (1 << 6);
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#pragma once
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
|
||||
#include <FreeRTOS.h>
|
||||
#include <semphr.h>
|
||||
|
@ -31,7 +32,7 @@ namespace Pinetime {
|
|||
SpiMaster& operator=(SpiMaster&&) = delete;
|
||||
|
||||
bool Init();
|
||||
bool Write(uint8_t pinCsn, const uint8_t* data, size_t size);
|
||||
bool Write(uint8_t pinCsn, const uint8_t* data, size_t size, const std::function<void()>& preTransactionHook);
|
||||
bool Read(uint8_t pinCsn, uint8_t* cmd, size_t cmdSize, uint8_t* data, size_t dataSize);
|
||||
|
||||
bool WriteCmdAndBuffer(uint8_t pinCsn, const uint8_t* cmd, size_t cmdSize, const uint8_t* data, size_t dataSize);
|
||||
|
@ -56,7 +57,6 @@ namespace Pinetime {
|
|||
|
||||
volatile uint32_t currentBufferAddr = 0;
|
||||
volatile size_t currentBufferSize = 0;
|
||||
volatile TaskHandle_t taskToNotify;
|
||||
SemaphoreHandle_t mutex = nullptr;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@ void SpiNorFlash::Uninit() {
|
|||
|
||||
void SpiNorFlash::Sleep() {
|
||||
auto cmd = static_cast<uint8_t>(Commands::DeepPowerDown);
|
||||
spi.Write(&cmd, sizeof(uint8_t));
|
||||
spi.Write(&cmd, sizeof(uint8_t), nullptr);
|
||||
NRF_LOG_INFO("[SpiNorFlash] Sleep")
|
||||
}
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
#include "drivers/St7789.h"
|
||||
#include <hal/nrf_gpio.h>
|
||||
#include <libraries/delay/nrf_delay.h>
|
||||
#include <nrfx_log.h>
|
||||
#include "drivers/Spi.h"
|
||||
#include "task.h"
|
||||
|
||||
using namespace Pinetime::Drivers;
|
||||
|
||||
|
@ -29,37 +29,77 @@ void St7789::Init() {
|
|||
DisplayOn();
|
||||
}
|
||||
|
||||
void St7789::WriteCommand(uint8_t cmd) {
|
||||
nrf_gpio_pin_clear(pinDataCommand);
|
||||
WriteSpi(&cmd, 1);
|
||||
}
|
||||
|
||||
void St7789::WriteData(uint8_t data) {
|
||||
nrf_gpio_pin_set(pinDataCommand);
|
||||
WriteSpi(&data, 1);
|
||||
WriteData(&data, 1);
|
||||
}
|
||||
|
||||
void St7789::WriteSpi(const uint8_t* data, size_t size) {
|
||||
spi.Write(data, size);
|
||||
void St7789::WriteData(const uint8_t* data, size_t size) {
|
||||
WriteSpi(data, size, [pinDataCommand = pinDataCommand]() {
|
||||
nrf_gpio_pin_set(pinDataCommand);
|
||||
});
|
||||
}
|
||||
|
||||
void St7789::WriteCommand(uint8_t data) {
|
||||
WriteCommand(&data, 1);
|
||||
}
|
||||
|
||||
void St7789::WriteCommand(const uint8_t* data, size_t size) {
|
||||
WriteSpi(data, size, [pinDataCommand = pinDataCommand]() {
|
||||
nrf_gpio_pin_clear(pinDataCommand);
|
||||
});
|
||||
}
|
||||
|
||||
void St7789::WriteSpi(const uint8_t* data, size_t size, const std::function<void()>& preTransactionHook) {
|
||||
spi.Write(data, size, preTransactionHook);
|
||||
}
|
||||
|
||||
void St7789::SoftwareReset() {
|
||||
EnsureSleepOutPostDelay();
|
||||
WriteCommand(static_cast<uint8_t>(Commands::SoftwareReset));
|
||||
nrf_delay_ms(150);
|
||||
// If sleep in: must wait 120ms before sleep out can sent (see driver datasheet)
|
||||
// Unconditionally wait as software reset doesn't need to be performant
|
||||
sleepIn = true;
|
||||
lastSleepExit = xTaskGetTickCount();
|
||||
vTaskDelay(pdMS_TO_TICKS(125));
|
||||
}
|
||||
|
||||
void St7789::SleepOut() {
|
||||
if (!sleepIn) {
|
||||
return;
|
||||
}
|
||||
WriteCommand(static_cast<uint8_t>(Commands::SleepOut));
|
||||
// Wait 5ms for clocks to stabilise
|
||||
// pdMS rounds down => 6 used here
|
||||
vTaskDelay(pdMS_TO_TICKS(6));
|
||||
// Cannot send sleep in or software reset for 120ms
|
||||
lastSleepExit = xTaskGetTickCount();
|
||||
sleepIn = false;
|
||||
}
|
||||
|
||||
void St7789::EnsureSleepOutPostDelay() {
|
||||
TickType_t delta = xTaskGetTickCount() - lastSleepExit;
|
||||
// Due to timer wraparound, there is a chance of delaying when not necessary
|
||||
// It is very low (pdMS_TO_TICKS(125)/2^32) and waiting an extra 125ms isn't too bad
|
||||
if (delta < pdMS_TO_TICKS(125)) {
|
||||
vTaskDelay(pdMS_TO_TICKS(125) - delta);
|
||||
}
|
||||
}
|
||||
|
||||
void St7789::SleepIn() {
|
||||
if (sleepIn) {
|
||||
return;
|
||||
}
|
||||
EnsureSleepOutPostDelay();
|
||||
WriteCommand(static_cast<uint8_t>(Commands::SleepIn));
|
||||
// Wait 5ms for clocks to stabilise
|
||||
// pdMS rounds down => 6 used here
|
||||
vTaskDelay(pdMS_TO_TICKS(6));
|
||||
sleepIn = true;
|
||||
}
|
||||
|
||||
void St7789::ColMod() {
|
||||
WriteCommand(static_cast<uint8_t>(Commands::ColMod));
|
||||
WriteData(0x55);
|
||||
nrf_delay_ms(10);
|
||||
}
|
||||
|
||||
void St7789::MemoryDataAccessControl() {
|
||||
|
@ -96,12 +136,10 @@ void St7789::RowAddressSet() {
|
|||
|
||||
void St7789::DisplayInversionOn() {
|
||||
WriteCommand(static_cast<uint8_t>(Commands::DisplayInversionOn));
|
||||
nrf_delay_ms(10);
|
||||
}
|
||||
|
||||
void St7789::NormalModeOn() {
|
||||
WriteCommand(static_cast<uint8_t>(Commands::NormalModeOn));
|
||||
nrf_delay_ms(10);
|
||||
}
|
||||
|
||||
void St7789::DisplayOn() {
|
||||
|
@ -120,12 +158,11 @@ void St7789::SetAddrWindow(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1) {
|
|||
WriteData(y0 & 0xff);
|
||||
WriteData(y1 >> 8);
|
||||
WriteData(y1 & 0xff);
|
||||
|
||||
WriteToRam();
|
||||
}
|
||||
|
||||
void St7789::WriteToRam() {
|
||||
void St7789::WriteToRam(const uint8_t* data, size_t size) {
|
||||
WriteCommand(static_cast<uint8_t>(Commands::WriteToRam));
|
||||
WriteData(data, size);
|
||||
}
|
||||
|
||||
void St7789::SetVdv() {
|
||||
|
@ -137,17 +174,6 @@ void St7789::SetVdv() {
|
|||
|
||||
void St7789::DisplayOff() {
|
||||
WriteCommand(static_cast<uint8_t>(Commands::DisplayOff));
|
||||
nrf_delay_ms(500);
|
||||
}
|
||||
|
||||
void St7789::VerticalScrollDefinition(uint16_t topFixedLines, uint16_t scrollLines, uint16_t bottomFixedLines) {
|
||||
WriteCommand(static_cast<uint8_t>(Commands::VerticalScrollDefinition));
|
||||
WriteData(topFixedLines >> 8u);
|
||||
WriteData(topFixedLines & 0x00ffu);
|
||||
WriteData(scrollLines >> 8u);
|
||||
WriteData(scrollLines & 0x00ffu);
|
||||
WriteData(bottomFixedLines >> 8u);
|
||||
WriteData(bottomFixedLines & 0x00ffu);
|
||||
}
|
||||
|
||||
void St7789::VerticalScrollStartAddress(uint16_t line) {
|
||||
|
@ -160,27 +186,20 @@ void St7789::VerticalScrollStartAddress(uint16_t line) {
|
|||
void St7789::Uninit() {
|
||||
}
|
||||
|
||||
void St7789::DrawPixel(uint16_t x, uint16_t y, uint32_t color) {
|
||||
if (x >= Width || y >= Height) {
|
||||
return;
|
||||
}
|
||||
|
||||
SetAddrWindow(x, y, x + 1, y + 1);
|
||||
|
||||
nrf_gpio_pin_set(pinDataCommand);
|
||||
WriteSpi(reinterpret_cast<const uint8_t*>(&color), 2);
|
||||
}
|
||||
|
||||
void St7789::DrawBuffer(uint16_t x, uint16_t y, uint16_t width, uint16_t height, const uint8_t* data, size_t size) {
|
||||
SetAddrWindow(x, y, x + width - 1, y + height - 1);
|
||||
nrf_gpio_pin_set(pinDataCommand);
|
||||
WriteSpi(data, size);
|
||||
WriteToRam(data, size);
|
||||
}
|
||||
|
||||
void St7789::HardwareReset() {
|
||||
nrf_gpio_pin_clear(pinReset);
|
||||
nrf_delay_ms(10);
|
||||
vTaskDelay(pdMS_TO_TICKS(1));
|
||||
nrf_gpio_pin_set(pinReset);
|
||||
// If hardware reset started while sleep out, reset time may be up to 120ms
|
||||
// Unconditionally wait as hardware reset doesn't need to be performant
|
||||
sleepIn = true;
|
||||
lastSleepExit = xTaskGetTickCount();
|
||||
vTaskDelay(pdMS_TO_TICKS(125));
|
||||
}
|
||||
|
||||
void St7789::Sleep() {
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
#pragma once
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
|
||||
#include <FreeRTOS.h>
|
||||
|
||||
namespace Pinetime {
|
||||
namespace Drivers {
|
||||
|
@ -16,9 +19,7 @@ namespace Pinetime {
|
|||
|
||||
void Init();
|
||||
void Uninit();
|
||||
void DrawPixel(uint16_t x, uint16_t y, uint32_t color);
|
||||
|
||||
void VerticalScrollDefinition(uint16_t topFixedLines, uint16_t scrollLines, uint16_t bottomFixedLines);
|
||||
void VerticalScrollStartAddress(uint16_t line);
|
||||
|
||||
void DrawBuffer(uint16_t x, uint16_t y, uint16_t width, uint16_t height, const uint8_t* data, size_t size);
|
||||
|
@ -31,23 +32,27 @@ namespace Pinetime {
|
|||
uint8_t pinDataCommand;
|
||||
uint8_t pinReset;
|
||||
uint8_t verticalScrollingStartAddress = 0;
|
||||
bool sleepIn;
|
||||
TickType_t lastSleepExit;
|
||||
|
||||
void HardwareReset();
|
||||
void SoftwareReset();
|
||||
void SleepOut();
|
||||
void EnsureSleepOutPostDelay();
|
||||
void SleepIn();
|
||||
void ColMod();
|
||||
void MemoryDataAccessControl();
|
||||
void DisplayInversionOn();
|
||||
void NormalModeOn();
|
||||
void WriteToRam();
|
||||
void WriteToRam(const uint8_t* data, size_t size);
|
||||
void DisplayOn();
|
||||
void DisplayOff();
|
||||
|
||||
void SetAddrWindow(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1);
|
||||
void SetVdv();
|
||||
void WriteCommand(uint8_t cmd);
|
||||
void WriteSpi(const uint8_t* data, size_t size);
|
||||
void WriteCommand(const uint8_t* data, size_t size);
|
||||
void WriteSpi(const uint8_t* data, size_t size, const std::function<void()>& preTransactionHook);
|
||||
|
||||
enum class Commands : uint8_t {
|
||||
SoftwareReset = 0x01,
|
||||
|
@ -67,6 +72,7 @@ namespace Pinetime {
|
|||
VdvSet = 0xc4,
|
||||
};
|
||||
void WriteData(uint8_t data);
|
||||
void WriteData(const uint8_t* data, size_t size);
|
||||
void ColumnAddressSet();
|
||||
|
||||
static constexpr uint16_t Width = 240;
|
||||
|
|
|
@ -103,10 +103,7 @@ void HeartRateTask::Work() {
|
|||
void HeartRateTask::PushMessage(HeartRateTask::Messages msg) {
|
||||
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
|
||||
xQueueSendFromISR(messageQueue, &msg, &xHigherPriorityTaskWoken);
|
||||
if (xHigherPriorityTaskWoken) {
|
||||
/* Actual macro used here is port specific. */
|
||||
// TODO : should I do something here?
|
||||
}
|
||||
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
|
||||
}
|
||||
|
||||
void HeartRateTask::StartMeasurement() {
|
||||
|
|
|
@ -730,7 +730,9 @@ typedef void* lv_obj_user_data_t;
|
|||
#define LV_USE_TABLE 1
|
||||
#if LV_USE_TABLE
|
||||
#define LV_TABLE_COL_MAX 12
|
||||
#define LV_TABLE_CELL_STYLE_CNT 5
|
||||
#define LV_TABLE_CELL_STYLE_CNT 6
|
||||
#define LV_TABLE_PART_CELL5 5
|
||||
#define LV_TABLE_PART_CELL6 6
|
||||
#endif
|
||||
|
||||
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
#include <libraries/gpiote/app_gpiote.h>
|
||||
#include <hal/nrf_wdt.h>
|
||||
#include <cstring>
|
||||
#include <components/gfx/Gfx.h>
|
||||
#include <drivers/St7789.h>
|
||||
#include <components/brightness/BrightnessController.h>
|
||||
#include <algorithm>
|
||||
|
@ -48,7 +47,6 @@ Pinetime::Drivers::SpiNorFlash spiNorFlash {flashSpi};
|
|||
Pinetime::Drivers::Spi lcdSpi {spi, Pinetime::PinMap::SpiLcdCsn};
|
||||
Pinetime::Drivers::St7789 lcd {lcdSpi, Pinetime::PinMap::LcdDataCommand, Pinetime::PinMap::LcdReset};
|
||||
|
||||
Pinetime::Components::Gfx gfx {lcd};
|
||||
Pinetime::Controllers::BrightnessController brightnessController;
|
||||
|
||||
void DisplayProgressBar(uint8_t percent, uint16_t color);
|
||||
|
@ -92,7 +90,6 @@ void Process(void* /*instance*/) {
|
|||
spiNorFlash.Wakeup();
|
||||
brightnessController.Init();
|
||||
lcd.Init();
|
||||
gfx.Init();
|
||||
|
||||
NRF_LOG_INFO("Display logo")
|
||||
DisplayLogo();
|
||||
|
@ -124,7 +121,6 @@ void DisplayLogo() {
|
|||
Pinetime::Tools::RleDecoder rleDecoder(infinitime_nb, sizeof(infinitime_nb));
|
||||
for (int i = 0; i < displayWidth; i++) {
|
||||
rleDecoder.DecodeNext(displayBuffer, displayWidth * bytesPerPixel);
|
||||
ulTaskNotifyTake(pdTRUE, 500);
|
||||
lcd.DrawBuffer(0, i, displayWidth, 1, reinterpret_cast<const uint8_t*>(displayBuffer), displayWidth * bytesPerPixel);
|
||||
}
|
||||
}
|
||||
|
@ -133,7 +129,6 @@ void DisplayProgressBar(uint8_t percent, uint16_t color) {
|
|||
static constexpr uint8_t barHeight = 20;
|
||||
std::fill(displayBuffer, displayBuffer + (displayWidth * bytesPerPixel), color);
|
||||
for (int i = 0; i < barHeight; i++) {
|
||||
ulTaskNotifyTake(pdTRUE, 500);
|
||||
uint16_t barWidth = std::min(static_cast<float>(percent) * 2.4f, static_cast<float>(displayWidth));
|
||||
lcd.DrawBuffer(0, displayWidth - barHeight + i, barWidth, 1, reinterpret_cast<const uint8_t*>(displayBuffer), barWidth * bytesPerPixel);
|
||||
}
|
||||
|
|
|
@ -370,9 +370,13 @@ void SystemTask::Work() {
|
|||
if (settingsController.GetNotificationStatus() != Controllers::Settings::Notification::Sleep &&
|
||||
settingsController.GetChimeOption() == Controllers::Settings::ChimesOption::Hours &&
|
||||
alarmController.State() != AlarmController::AlarmState::Alerting) {
|
||||
// if sleeping, we can't send a chime to displayApp yet (SPI flash switched off)
|
||||
// request running first and repush the chime message
|
||||
if (state == SystemTaskState::Sleeping) {
|
||||
touchHandler.SetWokenBy(Pinetime::Controllers::TouchHandler::WokenBy::Other, false);
|
||||
GoToRunning();
|
||||
PushMessage(msg);
|
||||
} else {
|
||||
displayApp.PushMessage(Pinetime::Applications::Display::Messages::Chime);
|
||||
}
|
||||
}
|
||||
|
@ -382,9 +386,13 @@ void SystemTask::Work() {
|
|||
if (settingsController.GetNotificationStatus() != Controllers::Settings::Notification::Sleep &&
|
||||
settingsController.GetChimeOption() == Controllers::Settings::ChimesOption::HalfHours &&
|
||||
alarmController.State() != AlarmController::AlarmState::Alerting) {
|
||||
// if sleeping, we can't send a chime to displayApp yet (SPI flash switched off)
|
||||
// request running first and repush the chime message
|
||||
if (state == SystemTaskState::Sleeping) {
|
||||
touchHandler.SetWokenBy(Pinetime::Controllers::TouchHandler::WokenBy::Other, false);
|
||||
GoToRunning();
|
||||
PushMessage(msg);
|
||||
} else {
|
||||
displayApp.PushMessage(Pinetime::Applications::Display::Messages::Chime);
|
||||
}
|
||||
}
|
||||
|
@ -539,10 +547,7 @@ void SystemTask::PushMessage(System::Messages msg) {
|
|||
if (in_isr()) {
|
||||
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
|
||||
xQueueSendFromISR(systemTasksMsgQueue, &msg, &xHigherPriorityTaskWoken);
|
||||
if (xHigherPriorityTaskWoken == pdTRUE) {
|
||||
/* Actual macro used here is port specific. */
|
||||
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
|
||||
}
|
||||
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
|
||||
} else {
|
||||
xQueueSend(systemTasksMsgQueue, &msg, portMAX_DELAY);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue