Merge branch 'develop' of JF/PineTime into master
This commit is contained in:
commit
b33ad92600
|
@ -1,5 +1,5 @@
|
||||||
cmake_minimum_required(VERSION 3.10)
|
cmake_minimum_required(VERSION 3.10)
|
||||||
project(pinetime VERSION 0.7.0 LANGUAGES C CXX ASM)
|
project(pinetime VERSION 0.7.1 LANGUAGES C CXX ASM)
|
||||||
|
|
||||||
set(NRF_TARGET "nrf52")
|
set(NRF_TARGET "nrf52")
|
||||||
|
|
||||||
|
|
306
README.md
306
README.md
|
@ -3,255 +3,87 @@
|
||||||
|
|
||||||
> We envision the PineTime as a companion for not only your PinePhone but also for your favorite devices — any phone, tablet, or even PC.
|
> We envision the PineTime as a companion for not only your PinePhone but also for your favorite devices — any phone, tablet, or even PC.
|
||||||
|
|
||||||
*https://www.pine64.org/pinetime/*
|
*https://www.pine64.org/pinetime/*
|
||||||
|
|
||||||
The goal of this project is to build a firmware designed for the PineTime around FreeRTOS.
|
The **Pinetime** smartwatch is built around the NRF52832 MCU (512KB Flash, 64KB RAM), a 240*240 LCD display driven by the ST7789 controller, an accelerometer, a heartrate sensor and a vibrator.
|
||||||
It's coded using a mix of C and C++ : the NRF52-SDK and FreeRTOS are written in C, but I prefer to write my own code in C++.
|
|
||||||
|
|
||||||
Please **NOTE : for now, this project is in heavy development state**. Feel free to use it, contribute to it or fork it !
|
The goal of this project is to design an open-source firmware for the Pinetime smartwatch :
|
||||||
|
|
||||||
This project is based on https://github.com/JF002/nrf52-baseproject.
|
- Code written in **modern C++**;
|
||||||
It's a CMake project that configure everything needed to build applications for the PineTime (https://wiki.pine64.org/index.php/PineTime).
|
- Build system based on **CMake**;
|
||||||
|
- Based on **[FreeRTOS 10.0.0](https://freertos.org)** real-time OS.
|
||||||
|
- Using **[LittleVGL/LVGL 6.1.2](https://lvgl.io/)** as UI library...
|
||||||
|
- ... and **[NimBLE 1.3.0](https://github.com/apache/mynewt-nimble)** as BLE stack.
|
||||||
|
|
||||||
I tested this project (compile only) with the following versions:
|
##Overview
|
||||||
|
|
||||||
* gcc-arm-none-eabi-8-2019-q3-update (from https://developer.arm.com/tools-and-software/open-source-software/developer-tools/gnu-toolchain/gnu-rm/downloads)
|
![Pinetime screens](images/0.7.0/montage.jpg "PinetimeScreens")
|
||||||
* nRF5_SDK_15.3.0_59ac345 (from https://www.nordicsemi.com/Software-and-Tools/Software/nRF5-SDK)
|
|
||||||
|
|
||||||
I've tested this project on the actual PineTime hardware.
|
|
||||||
|
|
||||||
## Current state
|
As of now, here is the list of achievements of this project:
|
||||||
|
|
||||||
![Pinetime](./images/v0.3.0.png "Pinetime")
|
|
||||||
|
|
||||||
* Project builds and runs on the Pinetime;
|
|
||||||
* Logs available via JLink RTT;
|
|
||||||
* SPI (DMA & IRQ based) LCD driver;
|
|
||||||
* Open source BLE stack : [NimBLE](https://github.com/apache/mynewt-nimble);
|
|
||||||
* BLE advertising and connection connection;
|
|
||||||
* BLE CTS client (retrieves the time from the connected device if it implements a CTS server);
|
|
||||||
* Push button to go to disable screen (and go to low power mode) / enable screen (and wake-up) and UI navigation
|
|
||||||
* Touch panel support;
|
|
||||||
* Rich user interface (using [LittleVGL](https://littlevgl.com/)) via display, touchpanel and push button.
|
|
||||||
* Digital watch face and 4 demo applications (spinning meter, analog gauche, push button and message box);
|
|
||||||
* Watchdog (automatic reset in case of firmware crash) and reset support (push and hold the button for 7 - 10s);
|
|
||||||
* BLE Notification support (still Work-In-Progress, [companion app](https://github.com/JF002/gobbledegook) needed);
|
|
||||||
* Supported by companion app [Amazfish](https://openrepos.net/content/piggz/amazfish) (time synchronization and notifications are integrated).
|
|
||||||
* **[EXPERIMENTAL]** Firmware update (OTA) via BLE.
|
|
||||||
|
|
||||||
|
- Fast and optimized LCD driver
|
||||||
|
- BLE communication
|
||||||
|
- Rich user interface via display, touchpanel and push button
|
||||||
|
- Time synchronisation via BLE
|
||||||
|
- Notification via BLE
|
||||||
|
- Multiple 'apps' :
|
||||||
|
* Clock (displays the date, time, battery level, ble connection status, heart rate and step count)
|
||||||
|
* Heart rate
|
||||||
|
* Motion
|
||||||
|
* System info (displays various info : BLE MAC, build date/time, uptime, version,...)
|
||||||
|
* Brightess (allows the user to configure the brightness of the display)
|
||||||
|
- Supported by 2 companion apps (developpments ongoing):
|
||||||
|
* [Gadgetbridge](https://codeberg.org/Freeyourgadget/Gadgetbridge/src/branch/pinetime-jf) (on Android)
|
||||||
|
* [Amazfish](https://openrepos.net/content/piggz/amazfish) (on SailfishOS)
|
||||||
|
- **[Experimental]** OTA (Over-the-air) update via BLE
|
||||||
|
- **[Experimental]** Bootloader based on [MCUBoot](https://juullabs-oss.github.io/mcuboot/)
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
### Build, flash and debug
|
||||||
* [BLE implementation and API](./doc/ble.md)
|
- [Build the project](doc/buildAndProgram.md)
|
||||||
* [Bootloader and DFU](./bootloader/README.md)
|
- [Bootloader, OTA and DFU](./bootloader/README.md)
|
||||||
|
- [Stub using NRF52-DK](./doc/PinetimeStubWithNrf52DK.md)
|
||||||
## Stub using NRF52-DK
|
- Logging with JLink RTT.
|
||||||
![Pinetime stub](./images/pinetimestub1.jpg "PinetimeStub")
|
- Using files from the releases
|
||||||
|
|
||||||
See [this page](./doc/PinetimeStubWithNrf52DK.md)
|
|
||||||
|
|
||||||
## How to build
|
|
||||||
|
|
||||||
* Download and unzip arm-none-eabi and NRF52 SDK
|
|
||||||
* Clone this repo
|
|
||||||
* **[JLINK]** Call CMake with the following command line argument
|
|
||||||
|
|
||||||
- -DARM_NONE_EABI_TOOLCHAIN_PATH=[Path to the toolchain directory]
|
|
||||||
- -DNRF5_SDK_PATH=[Path to the SDK directory]
|
|
||||||
- -DUSE_JLINK=1
|
|
||||||
- -DNRFJPROG=[Path to NRFJProg executable]
|
|
||||||
|
|
||||||
* OR
|
|
||||||
* **[GDB CLIENT (if you use a BlackMagicProbe, for example)]** Call CMake with the following command line argument
|
|
||||||
|
|
||||||
- -DARM_NONE_EABI_TOOLCHAIN_PATH=[Path to the toolchain directory]
|
|
||||||
- -DNRF5_SDK_PATH=[Path to the SDK directory]
|
|
||||||
- -DUSE_GDB_CLIENT=1
|
|
||||||
- -DGDB_CLIENT_BIN_PATH=[Path to arm-none-eabi-gdb executable]
|
|
||||||
- -DGDB_CLIENT_TARGET_REMOTE=[Target remote connetion string. Ex : /dev/ttyACM0]
|
|
||||||
|
|
||||||
* OR
|
|
||||||
* **[OPENOCD (if you use a STlink v2 clone, for example)]** Call CMake with the following command line argument
|
|
||||||
|
|
||||||
- -DARM_NONE_EABI_TOOLCHAIN_PATH=[Path to the toolchain directory]
|
|
||||||
- -DNRF5_SDK_PATH=[Path to the SDK directory]
|
|
||||||
- -DUSE_OPENOCD=1
|
|
||||||
|
|
||||||
* Optionally, if you want to use a another version then whats on your path
|
|
||||||
|
|
||||||
- -DOPENOCD_BIN_PATH=[path to openocd]
|
|
||||||
|
|
||||||
|
|
||||||
JLINK
|
|
||||||
```
|
|
||||||
$ mkdir build
|
|
||||||
$ cd build
|
|
||||||
$ cmake -DCMAKE_BUILD_TYPE=Debug -DARM_NONE_EABI_TOOLCHAIN_PATH=... -DNRF5_SDK_PATH=... -DUSE_JLINK=1 -DNRFJPROG=... ../
|
|
||||||
```
|
|
||||||
|
|
||||||
GDB (Back Magic Probe)
|
|
||||||
```
|
|
||||||
$ mkdir build
|
|
||||||
$ cd build
|
|
||||||
$ cmake -DARM_NONE_EABI_TOOLCHAIN_PATH=... -DNRF5_SDK_PATH=... -DUSE_GDB_CLIENT=1 -DGDB_CLIENT_BIN_PATH=... -DGDB_CLIENT_TARGET_REMOTE=... ../
|
|
||||||
```
|
|
||||||
|
|
||||||
OpenOCD (STlink v2 clones)
|
|
||||||
```
|
|
||||||
$ mkdir build
|
|
||||||
$ cd build
|
|
||||||
$ cmake -DARM_NONE_EABI_TOOLCHAIN_PATH=... -DNRF5_SDK_PATH=... -DUSE_OPENOCD=1 -DGDB_CLIENT_BIN_PATH=[optional] ../
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
**Note** : By default, the build configuration *Release* is applied. It means that the code is built with optimisations enabled (-O3). If you wanto to compile in *Debug* mode to ease debugging and step-by-step debugging, specify the CMake variable `CMAKE_BUILD_TYPE` in the CMake command line:
|
|
||||||
|
|
||||||
```
|
|
||||||
-DCMAKE_BUILD_TYPE=Debug
|
|
||||||
```
|
|
||||||
|
|
||||||
|
### API
|
||||||
* Make
|
- [BLE implementation and API](./doc/ble.md)
|
||||||
```
|
|
||||||
$ make -j pinetime-app
|
|
||||||
```
|
|
||||||
|
|
||||||
## How to program
|
|
||||||
### Using make
|
|
||||||
|
|
||||||
* Erase
|
|
||||||
|
|
||||||
```
|
### Architecture and technical topics
|
||||||
$ make FLASH_ERASE
|
- [Memory analysis](./doc/MemoryAnalysis.md)
|
||||||
```
|
|
||||||
|
|
||||||
* Flash application
|
|
||||||
|
|
||||||
```
|
|
||||||
$ make FLASH_pinetime-app
|
|
||||||
```
|
|
||||||
|
|
||||||
* For your information : list make targets :
|
|
||||||
|
|
||||||
```
|
|
||||||
$ make help
|
|
||||||
```
|
|
||||||
|
|
||||||
### Using GDB and a Black Magic Probe (BMP)
|
|
||||||
You can use this procedure if you want to flash the firmware (from a release, for example) without having to build the project locally.
|
|
||||||
|
|
||||||
Enter the following command into GDB:
|
|
||||||
|
|
||||||
```
|
|
||||||
target extended-remote /dev/ttyACM0
|
|
||||||
monitor swdp_scan
|
|
||||||
attach 1
|
|
||||||
file ./pinetime-app-full.hex
|
|
||||||
load
|
|
||||||
run
|
|
||||||
```
|
|
||||||
|
|
||||||
*Note* : Sometimes, I need to run this procedure 2 times : the first time, the screen stays blank, and the second time, it works. This is something I'll need to debug...
|
|
||||||
|
|
||||||
Example:
|
|
||||||
```
|
|
||||||
$ /home/jf/nrf52/gcc-arm-none-eabi-8-2019-q3-update/bin/arm-none-eabi-gdb
|
|
||||||
|
|
||||||
(gdb) target extended-remote /dev/ttyACM0
|
|
||||||
Remote debugging using /dev/ttyACM0
|
|
||||||
(gdb) monitor swdp_scan
|
|
||||||
Target voltage: ABSENT!
|
|
||||||
Available Targets:
|
|
||||||
No. Att Driver
|
|
||||||
1 Nordic nRF52 M3/M4
|
|
||||||
2 Nordic nRF52 Access Port
|
|
||||||
|
|
||||||
(gdb) attach 1
|
|
||||||
Attaching to Remote target
|
|
||||||
warning: No executable has been specified and target does not support
|
|
||||||
determining executable automatically. Try using the "file" command.
|
|
||||||
0xfffffffe in ?? ()
|
|
||||||
(gdb) file ./pinetime-app-full.hex
|
|
||||||
A program is being debugged already.
|
|
||||||
Are you sure you want to change the file? (y or n) y
|
|
||||||
Reading symbols from ./pinetime-app-full.hex...
|
|
||||||
(No debugging symbols found in ./pinetime-app-full.hex)
|
|
||||||
(gdb) load
|
|
||||||
Loading section .sec1, size 0xb00 lma 0x0
|
|
||||||
Loading section .sec2, size 0xf000 lma 0x1000
|
|
||||||
Loading section .sec3, size 0x10000 lma 0x10000
|
|
||||||
Loading section .sec4, size 0x5150 lma 0x20000
|
|
||||||
Loading section .sec5, size 0xa000 lma 0x26000
|
|
||||||
Loading section .sec6, size 0x10000 lma 0x30000
|
|
||||||
Loading section .sec7, size 0xdf08 lma 0x40000
|
|
||||||
Start address 0x0, load size 314200
|
|
||||||
Transfer rate: 45 KB/sec, 969 bytes/write.
|
|
||||||
```
|
|
||||||
|
|
||||||
## RTT
|
|
||||||
|
|
||||||
RTT is a feature from Segger's JLink devices that allows bidirectionnal communication between the debugger and the target.
|
|
||||||
This feature can be used to get the logs from the embedded software on the development computer.
|
|
||||||
|
|
||||||
* Program the MCU with the code (see above)
|
|
||||||
* Start JLinkExe
|
|
||||||
|
|
||||||
```
|
|
||||||
$ JLinkExe -device nrf52 -if swd -speed 4000 -autoconnect 1
|
|
||||||
```
|
|
||||||
|
|
||||||
* Start JLinkRTTClient
|
|
||||||
|
|
||||||
```
|
|
||||||
$ JLinkRTTClient
|
|
||||||
```
|
|
||||||
|
|
||||||
## Tools
|
|
||||||
|
|
||||||
- https://github.com/eliotstock/memory : display the memory usage (FLASH/RAM) using the .map file from GCC.
|
|
||||||
|
|
||||||
## BLE connection and time synchronization
|
### Using the firmware
|
||||||
At runtime, BLE advertising is started. You can then use a smartphone or computer to connect and bond to your Pinetime.
|
- Integration with Gadgetbridge
|
||||||
As soon as a device is bonded, Pinetime will look for a **CTS** server (**C**urrent **T**ime **S**ervice) on the connected device.
|
- Integration with AmazFish
|
||||||
|
- Integration with NRFConnect
|
||||||
### Using Android and NRFConnect
|
- Firmware update, OTA
|
||||||
Here is how to do it with an Android smartphone running NRFConnect:
|
|
||||||
|
|
||||||
* Build and program the firmware on the Pinetime
|
|
||||||
* Install NRFConnect (https://www.nordicsemi.com/Software-and-Tools/Development-Tools/nRF-Connect-for-desktop)
|
|
||||||
* Start NRFConnect and create a CTS server :
|
|
||||||
- Tap the hamburger button on the top left and select "Configure GATT server"
|
|
||||||
- Tap "Add service" on the bottom
|
|
||||||
- Select server configuration "Current Time Service" and tap OK
|
|
||||||
* Go back to the main screen and scan for BLE devices. A device called "PineTime" should appear
|
|
||||||
* Tap the button "Connect" next to the PineTime device. It should connect to the PineTime and switch to a new tab.
|
|
||||||
* If a CTS server is found, the Pinetime should update its time with the time provided by the server.
|
|
||||||
|
|
||||||
### Using Linux and bluetoothctl
|
|
||||||
* Ensure that your bluetooth controller is enabled and working fine. I've tested this on a x86 Debian computer and on a RaspberryPi 3.
|
|
||||||
* Run bluetoothctl as root : `sudo bluetoothctl`
|
|
||||||
* Enter the following commands:
|
|
||||||
* `scan on` and wait for you Pinetime to be detected. Note the BLE MAC address
|
|
||||||
* `scan off'
|
|
||||||
* `trust <MAC ADDRESS>`
|
|
||||||
* `pair <MAC ADDRESS>`
|
|
||||||
* Wait for some time, and the connection should be established.
|
|
||||||
|
|
||||||
**NOTE : ** The commands above establish a BLE connection between your PC, but the time synchronization and notifications won't work because there is not CTS or ANS server running. I'm currently working on an application that'll provide both of these servers.
|
|
||||||
|
## TODO - contribute
|
||||||
|
|
||||||
### Troubleshooting
|
This project is far from beeing finished, and there are still a lot of things to do for this project to become a firmware usable by the general public.
|
||||||
If the connection cannot be established, or the time synchronization does not work, try the following steps.
|
|
||||||
|
|
||||||
On Android:
|
Here a quick list out of my head of things to do for this project:
|
||||||
* Disable and re-enable your bluetooth device
|
|
||||||
* In NRFConnect, in the device tab corresponding to your pinetime, tap on the menu on the top left of the screen and select "Delete bond information".
|
|
||||||
* Then re-try to bond/connect.
|
|
||||||
|
|
||||||
On Linux:
|
- Improve BLE communication stability and reliability
|
||||||
* Reset the bluetooth device : `sudo hciconfig hci0 reset`
|
- Improve OTA and MCUBoot bootloader
|
||||||
* Restart the Bluetooth service : `sudo systemctl restart bluetooth.service`
|
- Add more functionalities : Alarm, chrono, configuration, activities, heart rate logging, games,...
|
||||||
* In bluetootctl:
|
- Add more BLE functionalities : call notifications, agenda, configuration, data logging,...
|
||||||
* `remove <MAC ADDRESS>`
|
- Measure power consumption and improve battery life
|
||||||
* `trust <MAC ADDRESS>`
|
- Improve documentation, take better pictures and video than mine
|
||||||
* `pair <MAC ADDRESS>`
|
- Improve the UI
|
||||||
|
- Create companion app for multiple OSes (Linux, Android, IoS) and platforms (desktop, ARM, mobile). Do not forget the other devices from Pine64 like [the Pinephone](https://www.pine64.org/pinephone/) and the [Pinebook Pro](https://www.pine64.org/pinebook-pro/).
|
||||||
Note that the current firmware only advertise for the first 3 minutes. If you cannot connect after more than 3 minutes, try resetting the device (push the button and hold it for 7-10 seconds).
|
- Design a simple CI (preferably selfhosted and easy to reproduce).
|
||||||
|
|
||||||
|
Do not hesitate to clone/fork the code, hack it and create pull-requests. I'll do my best to review and merge them :)
|
||||||
|
|
||||||
|
## Credits
|
||||||
|
I’m not working alone on this project. First, many people create PR for this projects. Then, there is the whole #pinetime community : a lot of people all around the world who are hacking, searching, experimenting and programming the Pinetime. We exchange our ideas, experiments and code in the chat rooms and forums.
|
||||||
|
|
||||||
|
Here are some people I would like to highlight:
|
||||||
|
|
||||||
|
- [Atc1441](https://github.com/atc1441/) : He works on an Arduino based firmware for the Pinetime and many other smartwatches based on similar hardware. He was of great help when I was implementing support for the BMA421 motion sensor and I²C driver.
|
||||||
|
- [Koen](https://github.com/bosmoment) : He’s working on a firmware based on RiotOS. He integrated similar libs than me : NimBLE, LittleVGL,… His help was invaluable too!
|
||||||
|
- [Lup Yuen Lee](https://github.com/lupyuen) : He is everywhere : he works on a Rust firmware, buils a MCUBoot based bootloader for the Pinetime, design a Flutter based companion app for smartphones and write a lot of articles about the Pinetime!
|
||||||
|
|
||||||
|
*If you feel like you should appear on this list, just get in touch with me or submit a PR :)*
|
||||||
|
|
|
@ -72,4 +72,7 @@ add_definitions(-D__STACK_SIZE=8192)
|
||||||
*TODO*
|
*TODO*
|
||||||
|
|
||||||
#NimBLE buffers
|
#NimBLE buffers
|
||||||
*TODO*
|
*TODO*
|
||||||
|
|
||||||
|
#Tools
|
||||||
|
- https://github.com/eliotstock/memory : display the memory usage (FLASH/RAM) using the .map file from GCC.
|
209
doc/buildAndProgram.md
Normal file
209
doc/buildAndProgram.md
Normal file
|
@ -0,0 +1,209 @@
|
||||||
|
# Build
|
||||||
|
##Dependencies
|
||||||
|
To build this project, you'll need:
|
||||||
|
- A cross-compiler : [gcc-arm-none-eabi-8-2019-q3-update](https://developer.arm.com/tools-and-software/open-source-software/developer-tools/gnu-toolchain/gnu-rm/downloads/8-2019q3-update)
|
||||||
|
- The NRF52 SDK 15.3.0 : [nRF5_SDK_15.3.0_59ac345](https://developer.nordicsemi.com/nRF5_SDK/nRF5_SDK_v15.x.x/nRF5_SDK_15.3.0_59ac345.zip)
|
||||||
|
- A reasonably recent version of CMake (I use 3.16.5)
|
||||||
|
|
||||||
|
##Build steps
|
||||||
|
###Clone the repo
|
||||||
|
```
|
||||||
|
git clone https://github.com/JF002/Pinetime.git
|
||||||
|
cd Pinetime
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
```
|
||||||
|
###Project generation using CMake
|
||||||
|
CMake configures the project according to variables you specify the command line. The variables are:
|
||||||
|
|
||||||
|
Variable | Description | Example|
|
||||||
|
----------|-------------|--------|
|
||||||
|
**ARM_NONE_EABI_TOOLCHAIN_PATH**|path to the toolchain directory|`-DARM_NONE_EABI_TOOLCHAIN_PATH=/home/jf/nrf52/gcc-arm-none-eabi-9-2019-q4-major/`|
|
||||||
|
**NRF5_SDK_PATH**|path to the NRF52 SDK|`-DNRF5_SDK_PATH=/home/jf/nrf52/Pinetime/sdk`|
|
||||||
|
**USE_JLINK, USE_GDB_CLIENT and USE_OPENOCD**|Enable *JLink* mode, *GDB Client* (Black Magic Probe) mode or *OpenOCD* mode (set the one you want to use to `1`)|`-DUSE_JLINK=1`
|
||||||
|
**CMAKE_BUILD_TYPE**| Build type (Release or Debug). Release is applied by default if this variable is not specified.|`-DCMAKE_BUILD_TYPE=Debug`
|
||||||
|
**NRFJPROG**|Path to the NRFJProg executable. Used only if `USE_JLINK` is 1.|`-DNRFJPROG=/opt/nrfjprog/nrfjprog`
|
||||||
|
**GDB_CLIENT_BIN_PATH**|Path to arm-none-eabi-gdb executable. Used only if `USE_GDB_CLIENT` is 1.|`-DGDB_CLIENT_BIN_PATH=/home/jf/nrf52/gcc-arm-none-eabi-9-2019-q4-major/bin/arm-none-eabi-gdb`
|
||||||
|
**GDB_CLIENT_TARGET_REMOTE**|Target remote connection string. Used only if `USE_GDB_CLIENT` is 1.|`-DGDB_CLIENT_TARGET_REMOTE=/dev/ttyACM0`
|
||||||
|
|
||||||
|
|
||||||
|
####CMake command line for JLink
|
||||||
|
```
|
||||||
|
cmake -DCMAKE_BUILD_TYPE=Debug -DARM_NONE_EABI_TOOLCHAIN_PATH=... -DNRF5_SDK_PATH=... -DUSE_JLINK=1 -DNRFJPROG=... ../
|
||||||
|
```
|
||||||
|
|
||||||
|
####CMake command line for GDB Client (Black Magic Probe)
|
||||||
|
```
|
||||||
|
cmake -DARM_NONE_EABI_TOOLCHAIN_PATH=... -DNRF5_SDK_PATH=... -DUSE_GDB_CLIENT=1 -DGDB_CLIENT_BIN_PATH=... -DGDB_CLIENT_TARGET_REMOTE=... ../
|
||||||
|
```
|
||||||
|
|
||||||
|
####CMake command line for OpenOCD
|
||||||
|
```
|
||||||
|
cmake -DARM_NONE_EABI_TOOLCHAIN_PATH=... -DNRF5_SDK_PATH=... -DUSE_OPENOCD=1 -DGDB_CLIENT_BIN_PATH=[optional] ../
|
||||||
|
```
|
||||||
|
|
||||||
|
###Build the project
|
||||||
|
During the project generation, CMake created the following targets:
|
||||||
|
- FLASH_ERASE : mass erase the flash memory of the NRF52.
|
||||||
|
- FLASH_pinetime-app : flash the firmware into the NRF52.
|
||||||
|
- pinetime-app : build the standalone (without bootloader support) version of the firmware.
|
||||||
|
- pinetime-mcuboot-app : build the firmware with the support of the bootloader (based on MCUBoot).
|
||||||
|
- pinetime-graphics : small firmware that writes the boot graphics into the SPI flash.
|
||||||
|
|
||||||
|
If you just want to build the project and run it on the Pinetime, using *pinetime-app* is recommanded. See ???? for more info about bootloader support.
|
||||||
|
|
||||||
|
Build:
|
||||||
|
```
|
||||||
|
make -j pinetime-app
|
||||||
|
```
|
||||||
|
|
||||||
|
List of files generated:
|
||||||
|
Binary files are generated into the folder `src`:
|
||||||
|
- **pinetime-app.bin, .hex and .out** : standalone firmware in bin, hex and out formats.
|
||||||
|
- **pinetime-app.map** : map file
|
||||||
|
- **pinetime-mcuboot-app.bin, .hex and .out** : firmware with bootloader support in bin, hex and out formats.
|
||||||
|
- **pinetime-mcuboot-app.map** : map file
|
||||||
|
- **pinetime-graphics.bin, .hex and .out** : firmware for the boot graphic in bin, hex and out formats.
|
||||||
|
- **pinetime-graphics.map** : map file
|
||||||
|
|
||||||
|
###Program and run
|
||||||
|
####Using CMake targets
|
||||||
|
These target have been configured during the project generation by CMake according to the parameters you provided to the command line.
|
||||||
|
|
||||||
|
Mass erase:
|
||||||
|
```
|
||||||
|
make FLASH_ERASE
|
||||||
|
```
|
||||||
|
|
||||||
|
Flash the application:
|
||||||
|
```
|
||||||
|
make FLASH_pinetime-app
|
||||||
|
```
|
||||||
|
|
||||||
|
###Using JLink
|
||||||
|
Start JLinkExe:
|
||||||
|
```
|
||||||
|
$ /opt/SEGGER/JLink/JLinkExe -device nrf52 -if swd -speed 4000 -autoconnect 1
|
||||||
|
SEGGER J-Link Commander V6.70d (Compiled Apr 16 2020 17:59:37)
|
||||||
|
DLL version V6.70d, compiled Apr 16 2020 17:59:25
|
||||||
|
|
||||||
|
Connecting to J-Link via USB...O.K.
|
||||||
|
Firmware: J-Link OB-SAM3U128-V2-NordicSemi compiled Mar 17 2020 14:43:00
|
||||||
|
Hardware version: V1.00
|
||||||
|
S/N: 682579153
|
||||||
|
License(s): RDI, FlashBP, FlashDL, JFlash, GDB
|
||||||
|
VTref=3.300V
|
||||||
|
Device "NRF52" selected.
|
||||||
|
|
||||||
|
|
||||||
|
Connecting to target via SWD
|
||||||
|
InitTarget() start
|
||||||
|
InitTarget() end
|
||||||
|
Found SW-DP with ID 0x2BA01477
|
||||||
|
DPIDR: 0x2BA01477
|
||||||
|
Scanning AP map to find all available APs
|
||||||
|
AP[2]: Stopped AP scan as end of AP map has been reached
|
||||||
|
AP[0]: AHB-AP (IDR: 0x24770011)
|
||||||
|
AP[1]: JTAG-AP (IDR: 0x02880000)
|
||||||
|
Iterating through AP map to find AHB-AP to use
|
||||||
|
AP[0]: Core found
|
||||||
|
AP[0]: AHB-AP ROM base: 0xE00FF000
|
||||||
|
CPUID register: 0x410FC241. Implementer code: 0x41 (ARM)
|
||||||
|
Found Cortex-M4 r0p1, Little endian.
|
||||||
|
FPUnit: 6 code (BP) slots and 2 literal slots
|
||||||
|
CoreSight components:
|
||||||
|
ROMTbl[0] @ E00FF000
|
||||||
|
ROMTbl[0][0]: E000E000, CID: B105E00D, PID: 000BB00C SCS-M7
|
||||||
|
ROMTbl[0][1]: E0001000, CID: B105E00D, PID: 003BB002 DWT
|
||||||
|
ROMTbl[0][2]: E0002000, CID: B105E00D, PID: 002BB003 FPB
|
||||||
|
ROMTbl[0][3]: E0000000, CID: B105E00D, PID: 003BB001 ITM
|
||||||
|
ROMTbl[0][4]: E0040000, CID: B105900D, PID: 000BB9A1 TPIU
|
||||||
|
ROMTbl[0][5]: E0041000, CID: B105900D, PID: 000BB925 ETM
|
||||||
|
Cortex-M4 identified.
|
||||||
|
J-Link>
|
||||||
|
```
|
||||||
|
|
||||||
|
Use the command loadfile to program the .hex file:
|
||||||
|
```
|
||||||
|
J-Link>loadfile pinetime-app.hex
|
||||||
|
Downloading file [pinetime-app.hex]...
|
||||||
|
Comparing flash [100%] Done.
|
||||||
|
Erasing flash [100%] Done.
|
||||||
|
Programming flash [100%] Done.
|
||||||
|
Verifying flash [100%] Done.
|
||||||
|
J-Link: Flash download: Bank 0 @ 0x00000000: 1 range affected (4096 bytes)
|
||||||
|
J-Link: Flash download: Total time needed: 0.322s (Prepare: 0.043s, Compare: 0.202s, Erase: 0.003s, Program: 0.064s, Verify: 0.000s, Restore: 0.007s)
|
||||||
|
O.K.
|
||||||
|
```
|
||||||
|
|
||||||
|
Then reset (r) and start (g) the CPU:
|
||||||
|
```
|
||||||
|
J-Link>r
|
||||||
|
Reset delay: 0 ms
|
||||||
|
Reset type NORMAL: Resets core & peripherals via SYSRESETREQ & VECTRESET bit.
|
||||||
|
Reset: Halt core after reset via DEMCR.VC_CORERESET.
|
||||||
|
Reset: Reset device via AIRCR.SYSRESETREQ.
|
||||||
|
J-Link>g
|
||||||
|
```
|
||||||
|
|
||||||
|
####JLink RTT
|
||||||
|
RTT is a feature from Segger's JLink devices that allows bidirectionnal communication between the debugger and the target. This feature can be used to get the logs from the embedded software on the development computer.
|
||||||
|
|
||||||
|
- Program the MCU with the code (see above)
|
||||||
|
- Start JLinkExe
|
||||||
|
|
||||||
|
```
|
||||||
|
$ JLinkExe -device nrf52 -if swd -speed 4000 -autoconnect 1
|
||||||
|
```
|
||||||
|
|
||||||
|
Start JLinkRTTClient
|
||||||
|
```
|
||||||
|
$ JLinkRTTClient
|
||||||
|
```
|
||||||
|
|
||||||
|
###Using GDB and Black Magic Probe (BMP)
|
||||||
|
Enter the following command into GDB:
|
||||||
|
|
||||||
|
```
|
||||||
|
target extended-remote /dev/ttyACM0
|
||||||
|
monitor swdp_scan
|
||||||
|
attach 1
|
||||||
|
file ./pinetime-app-full.hex
|
||||||
|
load
|
||||||
|
run
|
||||||
|
```
|
||||||
|
|
||||||
|
Example :
|
||||||
|
```
|
||||||
|
$ /home/jf/nrf52/gcc-arm-none-eabi-8-2019-q3-update/bin/arm-none-eabi-gdb
|
||||||
|
|
||||||
|
(gdb) target extended-remote /dev/ttyACM0
|
||||||
|
Remote debugging using /dev/ttyACM0
|
||||||
|
(gdb) monitor swdp_scan
|
||||||
|
Target voltage: ABSENT!
|
||||||
|
Available Targets:
|
||||||
|
No. Att Driver
|
||||||
|
1 Nordic nRF52 M3/M4
|
||||||
|
2 Nordic nRF52 Access Port
|
||||||
|
|
||||||
|
(gdb) attach 1
|
||||||
|
Attaching to Remote target
|
||||||
|
warning: No executable has been specified and target does not support
|
||||||
|
determining executable automatically. Try using the "file" command.
|
||||||
|
0xfffffffe in ?? ()
|
||||||
|
(gdb) file ./pinetime-app-full.hex
|
||||||
|
A program is being debugged already.
|
||||||
|
Are you sure you want to change the file? (y or n) y
|
||||||
|
Reading symbols from ./pinetime-app-full.hex...
|
||||||
|
(No debugging symbols found in ./pinetime-app-full.hex)
|
||||||
|
(gdb) load
|
||||||
|
Loading section .sec1, size 0xb00 lma 0x0
|
||||||
|
Loading section .sec2, size 0xf000 lma 0x1000
|
||||||
|
Loading section .sec3, size 0x10000 lma 0x10000
|
||||||
|
Loading section .sec4, size 0x5150 lma 0x20000
|
||||||
|
Loading section .sec5, size 0xa000 lma 0x26000
|
||||||
|
Loading section .sec6, size 0x10000 lma 0x30000
|
||||||
|
Loading section .sec7, size 0xdf08 lma 0x40000
|
||||||
|
Start address 0x0, load size 314200
|
||||||
|
Transfer rate: 45 KB/sec, 969 bytes/write.
|
||||||
|
```
|
BIN
images/0.7.0/montage.jpg
Normal file
BIN
images/0.7.0/montage.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 576 KiB |
|
@ -371,6 +371,7 @@ list(APPEND SOURCE_FILES
|
||||||
DisplayApp/Fonts/jetbrains_mono_bold_20.c
|
DisplayApp/Fonts/jetbrains_mono_bold_20.c
|
||||||
|
|
||||||
SystemTask/SystemTask.cpp
|
SystemTask/SystemTask.cpp
|
||||||
|
drivers/TwiMaster.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
list(APPEND GRAPHICS_SOURCE_FILES
|
list(APPEND GRAPHICS_SOURCE_FILES
|
||||||
|
@ -444,6 +445,7 @@ set(INCLUDE_FILES
|
||||||
SystemTask/SystemTask.h
|
SystemTask/SystemTask.h
|
||||||
SystemTask/SystemMonitor.h
|
SystemTask/SystemMonitor.h
|
||||||
DisplayApp/Screens/Symbols.h
|
DisplayApp/Screens/Symbols.h
|
||||||
|
drivers/TwiMaster.h
|
||||||
)
|
)
|
||||||
|
|
||||||
include_directories(
|
include_directories(
|
||||||
|
|
|
@ -24,12 +24,14 @@ void IdleTimerCallback(TimerHandle_t xTimer) {
|
||||||
|
|
||||||
|
|
||||||
SystemTask::SystemTask(Drivers::SpiMaster &spi, Drivers::St7789 &lcd,
|
SystemTask::SystemTask(Drivers::SpiMaster &spi, Drivers::St7789 &lcd,
|
||||||
Pinetime::Drivers::SpiNorFlash& spiNorFlash, Drivers::Cst816S &touchPanel,
|
Pinetime::Drivers::SpiNorFlash& spiNorFlash,
|
||||||
|
Drivers::TwiMaster& twiMaster, Drivers::Cst816S &touchPanel,
|
||||||
Components::LittleVgl &lvgl,
|
Components::LittleVgl &lvgl,
|
||||||
Controllers::Battery &batteryController, Controllers::Ble &bleController,
|
Controllers::Battery &batteryController, Controllers::Ble &bleController,
|
||||||
Controllers::DateTime &dateTimeController,
|
Controllers::DateTime &dateTimeController,
|
||||||
Pinetime::Controllers::NotificationManager& notificationManager) :
|
Pinetime::Controllers::NotificationManager& notificationManager) :
|
||||||
spi{spi}, lcd{lcd}, spiNorFlash{spiNorFlash}, touchPanel{touchPanel}, lvgl{lvgl}, batteryController{batteryController},
|
spi{spi}, lcd{lcd}, spiNorFlash{spiNorFlash},
|
||||||
|
twiMaster{twiMaster}, touchPanel{touchPanel}, lvgl{lvgl}, batteryController{batteryController},
|
||||||
bleController{bleController}, dateTimeController{dateTimeController},
|
bleController{bleController}, dateTimeController{dateTimeController},
|
||||||
watchdog{}, watchdogView{watchdog}, notificationManager{notificationManager},
|
watchdog{}, watchdogView{watchdog}, notificationManager{notificationManager},
|
||||||
nimbleController(*this, bleController,dateTimeController, notificationManager, spiNorFlash) {
|
nimbleController(*this, bleController,dateTimeController, notificationManager, spiNorFlash) {
|
||||||
|
@ -67,6 +69,7 @@ void SystemTask::Work() {
|
||||||
nimbleController.StartAdvertising();
|
nimbleController.StartAdvertising();
|
||||||
lcd.Init();
|
lcd.Init();
|
||||||
|
|
||||||
|
twiMaster.Init();
|
||||||
touchPanel.Init();
|
touchPanel.Init();
|
||||||
batteryController.Init();
|
batteryController.Init();
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,8 @@ namespace Pinetime {
|
||||||
};
|
};
|
||||||
|
|
||||||
SystemTask(Drivers::SpiMaster &spi, Drivers::St7789 &lcd,
|
SystemTask(Drivers::SpiMaster &spi, Drivers::St7789 &lcd,
|
||||||
Pinetime::Drivers::SpiNorFlash& spiNorFlash, Drivers::Cst816S &touchPanel,
|
Pinetime::Drivers::SpiNorFlash& spiNorFlash,
|
||||||
|
Drivers::TwiMaster& twiMaster, Drivers::Cst816S &touchPanel,
|
||||||
Components::LittleVgl &lvgl,
|
Components::LittleVgl &lvgl,
|
||||||
Controllers::Battery &batteryController, Controllers::Ble &bleController,
|
Controllers::Battery &batteryController, Controllers::Ble &bleController,
|
||||||
Controllers::DateTime &dateTimeController,
|
Controllers::DateTime &dateTimeController,
|
||||||
|
@ -42,6 +43,7 @@ namespace Pinetime {
|
||||||
Pinetime::Drivers::SpiMaster& spi;
|
Pinetime::Drivers::SpiMaster& spi;
|
||||||
Pinetime::Drivers::St7789& lcd;
|
Pinetime::Drivers::St7789& lcd;
|
||||||
Pinetime::Drivers::SpiNorFlash& spiNorFlash;
|
Pinetime::Drivers::SpiNorFlash& spiNorFlash;
|
||||||
|
Pinetime::Drivers::TwiMaster& twiMaster;
|
||||||
Pinetime::Drivers::Cst816S& touchPanel;
|
Pinetime::Drivers::Cst816S& touchPanel;
|
||||||
Pinetime::Components::LittleVgl& lvgl;
|
Pinetime::Components::LittleVgl& lvgl;
|
||||||
Pinetime::Controllers::Battery& batteryController;
|
Pinetime::Controllers::Battery& batteryController;
|
||||||
|
|
|
@ -13,51 +13,42 @@ using namespace Pinetime::Drivers;
|
||||||
* TODO : we need a complete datasheet and protocol reference!
|
* TODO : we need a complete datasheet and protocol reference!
|
||||||
* */
|
* */
|
||||||
|
|
||||||
void Pinetime::Drivers::Cst816S::Init() {
|
Cst816S::Cst816S(TwiMaster &twiMaster, uint8_t twiAddress) : twiMaster{twiMaster}, twiAddress{twiAddress} {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void Cst816S::Init() {
|
||||||
nrf_gpio_cfg_output(pinReset);
|
nrf_gpio_cfg_output(pinReset);
|
||||||
nrf_gpio_pin_clear(pinReset);
|
|
||||||
vTaskDelay(20);
|
|
||||||
nrf_gpio_pin_set(pinReset);
|
nrf_gpio_pin_set(pinReset);
|
||||||
vTaskDelay(200);
|
vTaskDelay(50);
|
||||||
|
nrf_gpio_pin_clear(pinReset);
|
||||||
|
vTaskDelay(5);
|
||||||
|
nrf_gpio_pin_set(pinReset);
|
||||||
|
vTaskDelay(50);
|
||||||
|
|
||||||
nrfx_twi_config_t config;
|
// Wake the touchpanel up
|
||||||
config.frequency = NRF_TWI_FREQ_400K;
|
uint8_t dummy;
|
||||||
config.scl = 7;
|
twiMaster.Read(twiAddress, 0x15, &dummy, 1);
|
||||||
config.sda = 6;
|
vTaskDelay(5);
|
||||||
config.interrupt_priority = NRFX_TWI_DEFAULT_CONFIG_IRQ_PRIORITY;
|
twiMaster.Read(twiAddress, 0xa7, &dummy, 1);
|
||||||
config.hold_bus_uninit = NRFX_TWI_DEFAULT_CONFIG_HOLD_BUS_UNINIT;
|
|
||||||
|
|
||||||
// Configure TWI in blocking mode (event_handler = nullptr)
|
|
||||||
auto ret = nrfx_twi_init(&twi, &config, nullptr, this);
|
|
||||||
nrfx_twi_enable(&twi);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Cst816S::Probe() {
|
|
||||||
nrfx_err_t ret;
|
|
||||||
for(int i = 0; i < 127; i++) {
|
|
||||||
uint8_t data;
|
|
||||||
ret = nrfx_twi_rx(&twi, i, &data, 1);
|
|
||||||
if(ret == NRFX_SUCCESS) {
|
|
||||||
NRF_LOG_INFO("I2C device detected at address %d", i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Cst816S::TouchInfos Cst816S::GetTouchInfo() {
|
Cst816S::TouchInfos Cst816S::GetTouchInfo() {
|
||||||
Cst816S::TouchInfos info;
|
Cst816S::TouchInfos info;
|
||||||
|
|
||||||
nrfx_twi_rx(&twi, address, touchData, 63);
|
twiMaster.Read(twiAddress, 0, touchData, 63);
|
||||||
auto nbTouchPoints = touchData[2] & 0x0f;
|
auto nbTouchPoints = touchData[2] & 0x0f;
|
||||||
|
|
||||||
// uint8_t i = 0;
|
// uint8_t i = 0;
|
||||||
// NRF_LOG_INFO("#########################")
|
// NRF_LOG_INFO("#########################")
|
||||||
for(int i = 0; i < 1; i++) {
|
for(int i = 0; i < 1; i++) {
|
||||||
uint8_t pointId = (touchData[touchIdIndex + (touchStep * i)]) >> 4;
|
uint8_t pointId = (touchData[touchIdIndex + (touchStep * i)]) >> 4;
|
||||||
if(nbTouchPoints == 0 && pointId == lastTouchId) return info;
|
if(nbTouchPoints == 0 && pointId == lastTouchId) return info;
|
||||||
|
|
||||||
// We fetch only the first touch point (the controller seems to handle only one anyway...)
|
// We fetch only the first touch point (the controller seems to handle only one anyway...)
|
||||||
info.isTouch = true;
|
info.isTouch = true;
|
||||||
|
|
||||||
|
|
||||||
auto xHigh = touchData[touchXHighIndex + (touchStep * i)] & 0x0f;
|
auto xHigh = touchData[touchXHighIndex + (touchStep * i)] & 0x0f;
|
||||||
|
@ -106,11 +97,12 @@ Cst816S::TouchInfos Cst816S::GetTouchInfo() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Cst816S::Sleep() {
|
void Cst816S::Sleep() {
|
||||||
nrfx_twi_disable(&twi);
|
// TODO re enable sleep mode
|
||||||
|
//twiMaster.Sleep();
|
||||||
nrf_gpio_cfg_default(6);
|
nrf_gpio_cfg_default(6);
|
||||||
nrf_gpio_cfg_default(7);
|
nrf_gpio_cfg_default(7);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Cst816S::Wakeup() {
|
void Cst816S::Wakeup() {
|
||||||
Init();
|
Init();
|
||||||
}
|
}
|
|
@ -1,20 +1,21 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <nrfx_twi.h>
|
#include <nrfx_twi.h>
|
||||||
|
#include "TwiMaster.h"
|
||||||
|
|
||||||
namespace Pinetime {
|
namespace Pinetime {
|
||||||
namespace Drivers {
|
namespace Drivers {
|
||||||
class Cst816S {
|
class Cst816S {
|
||||||
public :
|
public :
|
||||||
enum class Gestures : uint8_t {
|
enum class Gestures : uint8_t {
|
||||||
None = 0x00,
|
None = 0x00,
|
||||||
SlideDown = 0x01,
|
SlideDown = 0x01,
|
||||||
SlideUp = 0x02,
|
SlideUp = 0x02,
|
||||||
SlideLeft = 0x03,
|
SlideLeft = 0x03,
|
||||||
SlideRight = 0x04,
|
SlideRight = 0x04,
|
||||||
SingleTap = 0x05,
|
SingleTap = 0x05,
|
||||||
DoubleTap = 0x0B,
|
DoubleTap = 0x0B,
|
||||||
LongPress = 0x0C
|
LongPress = 0x0C
|
||||||
};
|
};
|
||||||
struct TouchInfos {
|
struct TouchInfos {
|
||||||
uint16_t x;
|
uint16_t x;
|
||||||
|
@ -27,21 +28,19 @@ namespace Pinetime {
|
||||||
bool isTouch = false;
|
bool isTouch = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
Cst816S() = default;
|
Cst816S(TwiMaster& twiMaster, uint8_t twiAddress);
|
||||||
Cst816S(const Cst816S&) = delete;
|
Cst816S(const Cst816S&) = delete;
|
||||||
Cst816S& operator=(const Cst816S&) = delete;
|
Cst816S& operator=(const Cst816S&) = delete;
|
||||||
Cst816S(Cst816S&&) = delete;
|
Cst816S(Cst816S&&) = delete;
|
||||||
Cst816S& operator=(Cst816S&&) = delete;
|
Cst816S& operator=(Cst816S&&) = delete;
|
||||||
|
|
||||||
void Init();
|
void Init();
|
||||||
void Probe();
|
|
||||||
TouchInfos GetTouchInfo();
|
TouchInfos GetTouchInfo();
|
||||||
void Sleep();
|
void Sleep();
|
||||||
void Wakeup();
|
void Wakeup();
|
||||||
private:
|
private:
|
||||||
static constexpr uint8_t pinIrq = 28;
|
static constexpr uint8_t pinIrq = 28;
|
||||||
static constexpr uint8_t pinReset = 10;
|
static constexpr uint8_t pinReset = 10;
|
||||||
static constexpr uint8_t address = 0x15;
|
|
||||||
static constexpr uint8_t lastTouchId = 0x0f;
|
static constexpr uint8_t lastTouchId = 0x0f;
|
||||||
static constexpr uint8_t touchPointNumIndex = 2;
|
static constexpr uint8_t touchPointNumIndex = 2;
|
||||||
static constexpr uint8_t touchMiscIndex = 8;
|
static constexpr uint8_t touchMiscIndex = 8;
|
||||||
|
@ -56,12 +55,9 @@ namespace Pinetime {
|
||||||
static constexpr uint8_t gestureIndex = 1;
|
static constexpr uint8_t gestureIndex = 1;
|
||||||
|
|
||||||
uint8_t touchData[63];
|
uint8_t touchData[63];
|
||||||
|
TwiMaster& twiMaster;
|
||||||
// TODO TWI (i²C) should be created outside and injected into this class
|
uint8_t twiAddress;
|
||||||
// It will be needed when implementing other I²C devices
|
|
||||||
// (0x15 = touch, 0x18 = accelerometer, 0x44 = HR sensor)
|
|
||||||
nrfx_twi_t twi = NRFX_TWI_INSTANCE(1); // Use instance 1, because instance 0 is already used by SPI
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
140
src/drivers/TwiMaster.cpp
Normal file
140
src/drivers/TwiMaster.cpp
Normal file
|
@ -0,0 +1,140 @@
|
||||||
|
#include <sdk/integration/nrfx/nrfx_log.h>
|
||||||
|
#include <sdk/modules/nrfx/hal/nrf_gpio.h>
|
||||||
|
#include <cstring>
|
||||||
|
#include "TwiMaster.h"
|
||||||
|
|
||||||
|
using namespace Pinetime::Drivers;
|
||||||
|
|
||||||
|
// TODO use shortcut to automatically send STOP when receive LastTX, for example
|
||||||
|
// TODO use DMA/IRQ
|
||||||
|
|
||||||
|
TwiMaster::TwiMaster(const Modules module, const Parameters& params) : module{module}, params{params} {
|
||||||
|
mutex = xSemaphoreCreateBinary();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TwiMaster::Init() {
|
||||||
|
NRF_GPIO->PIN_CNF[params.pinScl] = ((uint32_t)GPIO_PIN_CNF_DIR_Input << GPIO_PIN_CNF_DIR_Pos)
|
||||||
|
| ((uint32_t)GPIO_PIN_CNF_INPUT_Connect << GPIO_PIN_CNF_INPUT_Pos)
|
||||||
|
| ((uint32_t)GPIO_PIN_CNF_PULL_Pullup << GPIO_PIN_CNF_PULL_Pos)
|
||||||
|
| ((uint32_t)GPIO_PIN_CNF_DRIVE_S0D1 << GPIO_PIN_CNF_DRIVE_Pos)
|
||||||
|
| ((uint32_t)GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos);
|
||||||
|
|
||||||
|
NRF_GPIO->PIN_CNF[params.pinSda] = ((uint32_t)GPIO_PIN_CNF_DIR_Input << GPIO_PIN_CNF_DIR_Pos)
|
||||||
|
| ((uint32_t)GPIO_PIN_CNF_INPUT_Connect << GPIO_PIN_CNF_INPUT_Pos)
|
||||||
|
| ((uint32_t)GPIO_PIN_CNF_PULL_Pullup << GPIO_PIN_CNF_PULL_Pos)
|
||||||
|
| ((uint32_t)GPIO_PIN_CNF_DRIVE_S0D1 << GPIO_PIN_CNF_DRIVE_Pos)
|
||||||
|
| ((uint32_t)GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos);
|
||||||
|
|
||||||
|
switch(module) {
|
||||||
|
case Modules::TWIM1: twiBaseAddress = NRF_TWIM1; break;
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(static_cast<Frequencies>(params.frequency)) {
|
||||||
|
case Frequencies::Khz100 : twiBaseAddress->FREQUENCY = TWIM_FREQUENCY_FREQUENCY_K100; break;
|
||||||
|
case Frequencies::Khz250 : twiBaseAddress->FREQUENCY = TWIM_FREQUENCY_FREQUENCY_K250; break;
|
||||||
|
case Frequencies::Khz400 : twiBaseAddress->FREQUENCY = TWIM_FREQUENCY_FREQUENCY_K400; break;
|
||||||
|
}
|
||||||
|
|
||||||
|
twiBaseAddress->PSEL.SCL = params.pinScl;
|
||||||
|
twiBaseAddress->PSEL.SDA = params.pinSda;
|
||||||
|
twiBaseAddress->EVENTS_LASTRX = 0;
|
||||||
|
twiBaseAddress->EVENTS_STOPPED = 0;
|
||||||
|
twiBaseAddress->EVENTS_LASTTX = 0;
|
||||||
|
twiBaseAddress->EVENTS_ERROR = 0;
|
||||||
|
twiBaseAddress->EVENTS_RXSTARTED = 0;
|
||||||
|
twiBaseAddress->EVENTS_SUSPENDED = 0;
|
||||||
|
twiBaseAddress->EVENTS_TXSTARTED = 0;
|
||||||
|
|
||||||
|
twiBaseAddress->ENABLE = (TWIM_ENABLE_ENABLE_Enabled << TWIM_ENABLE_ENABLE_Pos);
|
||||||
|
|
||||||
|
|
||||||
|
/* // IRQ
|
||||||
|
NVIC_ClearPendingIRQ(_IRQn);
|
||||||
|
NVIC_SetPriority(_IRQn, 2);
|
||||||
|
NVIC_EnableIRQ(_IRQn);
|
||||||
|
*/
|
||||||
|
|
||||||
|
xSemaphoreGive(mutex);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void TwiMaster::Read(uint8_t deviceAddress, uint8_t registerAddress, uint8_t *data, size_t size) {
|
||||||
|
xSemaphoreTake(mutex, portMAX_DELAY);
|
||||||
|
Write(deviceAddress, ®isterAddress, 1, false);
|
||||||
|
Read(deviceAddress, data, size, true);
|
||||||
|
xSemaphoreGive(mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TwiMaster::Write(uint8_t deviceAddress, uint8_t registerAddress, const uint8_t *data, size_t size) {
|
||||||
|
ASSERT(size <= maxDataSize);
|
||||||
|
xSemaphoreTake(mutex, portMAX_DELAY);
|
||||||
|
internalBuffer[0] = registerAddress;
|
||||||
|
std::memcpy(internalBuffer+1, data, size);
|
||||||
|
Write(deviceAddress, internalBuffer, size+1, true);
|
||||||
|
xSemaphoreGive(mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void TwiMaster::Read(uint8_t deviceAddress, uint8_t *buffer, size_t size, bool stop) {
|
||||||
|
twiBaseAddress->ADDRESS = deviceAddress;
|
||||||
|
twiBaseAddress->TASKS_RESUME = 0x1UL;
|
||||||
|
twiBaseAddress->RXD.PTR = (uint32_t)buffer;
|
||||||
|
twiBaseAddress->RXD.MAXCNT = size;
|
||||||
|
|
||||||
|
twiBaseAddress->TASKS_STARTRX = 1;
|
||||||
|
|
||||||
|
while(!twiBaseAddress->EVENTS_RXSTARTED && !twiBaseAddress->EVENTS_ERROR);
|
||||||
|
twiBaseAddress->EVENTS_RXSTARTED = 0x0UL;
|
||||||
|
|
||||||
|
while(!twiBaseAddress->EVENTS_LASTRX && !twiBaseAddress->EVENTS_ERROR);
|
||||||
|
twiBaseAddress->EVENTS_LASTRX = 0x0UL;
|
||||||
|
|
||||||
|
if (stop || twiBaseAddress->EVENTS_ERROR) {
|
||||||
|
twiBaseAddress->TASKS_STOP = 0x1UL;
|
||||||
|
while(!twiBaseAddress->EVENTS_STOPPED);
|
||||||
|
twiBaseAddress->EVENTS_STOPPED = 0x0UL;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
twiBaseAddress->TASKS_SUSPEND = 0x1UL;
|
||||||
|
while(!twiBaseAddress->EVENTS_SUSPENDED);
|
||||||
|
twiBaseAddress->EVENTS_SUSPENDED = 0x0UL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (twiBaseAddress->EVENTS_ERROR) {
|
||||||
|
twiBaseAddress->EVENTS_ERROR = 0x0UL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TwiMaster::Write(uint8_t deviceAddress, const uint8_t *data, size_t size, bool stop) {
|
||||||
|
twiBaseAddress->ADDRESS = deviceAddress;
|
||||||
|
twiBaseAddress->TASKS_RESUME = 0x1UL;
|
||||||
|
twiBaseAddress->TXD.PTR = (uint32_t)data;
|
||||||
|
twiBaseAddress->TXD.MAXCNT = size;
|
||||||
|
|
||||||
|
twiBaseAddress->TASKS_STARTTX = 1;
|
||||||
|
|
||||||
|
while(!twiBaseAddress->EVENTS_TXSTARTED && !twiBaseAddress->EVENTS_ERROR);
|
||||||
|
twiBaseAddress->EVENTS_TXSTARTED = 0x0UL;
|
||||||
|
|
||||||
|
while(!twiBaseAddress->EVENTS_LASTTX && !twiBaseAddress->EVENTS_ERROR);
|
||||||
|
twiBaseAddress->EVENTS_LASTTX = 0x0UL;
|
||||||
|
|
||||||
|
if (stop || twiBaseAddress->EVENTS_ERROR) {
|
||||||
|
twiBaseAddress->TASKS_STOP = 0x1UL;
|
||||||
|
while(!twiBaseAddress->EVENTS_STOPPED);
|
||||||
|
twiBaseAddress->EVENTS_STOPPED = 0x0UL;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
twiBaseAddress->TASKS_SUSPEND = 0x1UL;
|
||||||
|
while(!twiBaseAddress->EVENTS_SUSPENDED);
|
||||||
|
twiBaseAddress->EVENTS_SUSPENDED = 0x0UL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (twiBaseAddress->EVENTS_ERROR) {
|
||||||
|
twiBaseAddress->EVENTS_ERROR = 0x0UL;
|
||||||
|
uint32_t error = twiBaseAddress->ERRORSRC;
|
||||||
|
twiBaseAddress->ERRORSRC = error;
|
||||||
|
}
|
||||||
|
}
|
38
src/drivers/TwiMaster.h
Normal file
38
src/drivers/TwiMaster.h
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
#pragma once
|
||||||
|
#include <FreeRTOS.h>
|
||||||
|
#include <semphr.h>
|
||||||
|
#include <drivers/include/nrfx_twi.h>
|
||||||
|
|
||||||
|
|
||||||
|
namespace Pinetime {
|
||||||
|
namespace Drivers {
|
||||||
|
class TwiMaster {
|
||||||
|
public:
|
||||||
|
enum class Modules { TWIM1 };
|
||||||
|
enum class Frequencies {Khz100, Khz250, Khz400};
|
||||||
|
struct Parameters {
|
||||||
|
uint32_t frequency;
|
||||||
|
uint8_t pinSda;
|
||||||
|
uint8_t pinScl;
|
||||||
|
};
|
||||||
|
|
||||||
|
TwiMaster(const Modules module, const Parameters& params);
|
||||||
|
|
||||||
|
void Init();
|
||||||
|
void Read(uint8_t deviceAddress, uint8_t registerAddress, uint8_t* buffer, size_t size);
|
||||||
|
void Write(uint8_t deviceAddress, uint8_t registerAddress, const uint8_t* data, size_t size);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void Read(uint8_t deviceAddress, uint8_t* buffer, size_t size, bool stop);
|
||||||
|
void Write(uint8_t deviceAddress, const uint8_t* data, size_t size, bool stop);
|
||||||
|
NRF_TWIM_Type* twiBaseAddress;
|
||||||
|
SemaphoreHandle_t mutex;
|
||||||
|
const Modules module;
|
||||||
|
const Parameters params;
|
||||||
|
static constexpr uint8_t maxDataSize{8};
|
||||||
|
static constexpr uint8_t registerSize{1};
|
||||||
|
uint8_t internalBuffer[maxDataSize + registerSize];
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
15
src/main.cpp
15
src/main.cpp
|
@ -42,6 +42,9 @@ static constexpr uint8_t pinSpiMiso = 4;
|
||||||
static constexpr uint8_t pinSpiFlashCsn = 5;
|
static constexpr uint8_t pinSpiFlashCsn = 5;
|
||||||
static constexpr uint8_t pinLcdCsn = 25;
|
static constexpr uint8_t pinLcdCsn = 25;
|
||||||
static constexpr uint8_t pinLcdDataCommand = 18;
|
static constexpr uint8_t pinLcdDataCommand = 18;
|
||||||
|
static constexpr uint8_t pinTwiScl = 7;
|
||||||
|
static constexpr uint8_t pinTwiSda = 6;
|
||||||
|
static constexpr uint8_t touchPanelTwiAddress = 0x15;
|
||||||
|
|
||||||
Pinetime::Drivers::SpiMaster spi{Pinetime::Drivers::SpiMaster::SpiModule::SPI0, {
|
Pinetime::Drivers::SpiMaster spi{Pinetime::Drivers::SpiMaster::SpiModule::SPI0, {
|
||||||
Pinetime::Drivers::SpiMaster::BitOrder::Msb_Lsb,
|
Pinetime::Drivers::SpiMaster::BitOrder::Msb_Lsb,
|
||||||
|
@ -58,7 +61,15 @@ Pinetime::Drivers::St7789 lcd {lcdSpi, pinLcdDataCommand};
|
||||||
|
|
||||||
Pinetime::Drivers::Spi flashSpi {spi, pinSpiFlashCsn};
|
Pinetime::Drivers::Spi flashSpi {spi, pinSpiFlashCsn};
|
||||||
Pinetime::Drivers::SpiNorFlash spiNorFlash {flashSpi};
|
Pinetime::Drivers::SpiNorFlash spiNorFlash {flashSpi};
|
||||||
Pinetime::Drivers::Cst816S touchPanel {};
|
|
||||||
|
// 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
|
||||||
|
// at ~390Khz with correct timings.
|
||||||
|
static constexpr uint32_t MaxTwiFrequencyWithoutHardwareBug{0x06200000};
|
||||||
|
Pinetime::Drivers::TwiMaster twiMaster{Pinetime::Drivers::TwiMaster::Modules::TWIM1,
|
||||||
|
Pinetime::Drivers::TwiMaster::Parameters {
|
||||||
|
MaxTwiFrequencyWithoutHardwareBug, pinTwiSda, pinTwiScl}};
|
||||||
|
Pinetime::Drivers::Cst816S touchPanel {twiMaster, touchPanelTwiAddress};
|
||||||
Pinetime::Components::LittleVgl lvgl {lcd, touchPanel};
|
Pinetime::Components::LittleVgl lvgl {lcd, touchPanel};
|
||||||
|
|
||||||
|
|
||||||
|
@ -213,7 +224,7 @@ int main(void) {
|
||||||
|
|
||||||
debounceTimer = xTimerCreate ("debounceTimer", 200, pdFALSE, (void *) 0, DebounceTimerCallback);
|
debounceTimer = xTimerCreate ("debounceTimer", 200, pdFALSE, (void *) 0, DebounceTimerCallback);
|
||||||
|
|
||||||
systemTask.reset(new Pinetime::System::SystemTask(spi, lcd, spiNorFlash, touchPanel, lvgl, batteryController, bleController,
|
systemTask.reset(new Pinetime::System::SystemTask(spi, lcd, spiNorFlash, twiMaster, touchPanel, lvgl, batteryController, bleController,
|
||||||
dateTimeController, notificationManager));
|
dateTimeController, notificationManager));
|
||||||
systemTask->Start();
|
systemTask->Start();
|
||||||
nimble_port_init();
|
nimble_port_init();
|
||||||
|
|
|
@ -5244,7 +5244,7 @@
|
||||||
// <e> NRFX_TWI_ENABLED - nrfx_twi - TWI peripheral driver
|
// <e> NRFX_TWI_ENABLED - nrfx_twi - TWI peripheral driver
|
||||||
//==========================================================
|
//==========================================================
|
||||||
#ifndef NRFX_TWI_ENABLED
|
#ifndef NRFX_TWI_ENABLED
|
||||||
#define NRFX_TWI_ENABLED 1
|
#define NRFX_TWI_ENABLED 0
|
||||||
#endif
|
#endif
|
||||||
// <q> NRFX_TWI0_ENABLED - Enable TWI0 instance
|
// <q> NRFX_TWI0_ENABLED - Enable TWI0 instance
|
||||||
|
|
||||||
|
@ -5257,7 +5257,7 @@
|
||||||
|
|
||||||
|
|
||||||
#ifndef NRFX_TWI1_ENABLED
|
#ifndef NRFX_TWI1_ENABLED
|
||||||
#define NRFX_TWI1_ENABLED 1
|
#define NRFX_TWI1_ENABLED 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// <o> NRFX_TWI_DEFAULT_CONFIG_FREQUENCY - Frequency
|
// <o> NRFX_TWI_DEFAULT_CONFIG_FREQUENCY - Frequency
|
||||||
|
|
Loading…
Reference in New Issue
Block a user