├── .github └── workflows │ └── main.yml ├── .gitignore ├── LICENSE ├── Makefile ├── README.md └── app ├── CMakeLists.txt ├── Kconfig.hypnos ├── boards └── arm │ ├── ds_d6 │ ├── Kconfig │ ├── Kconfig.board │ ├── Kconfig.defconfig │ ├── board.cmake │ ├── doc │ │ └── index.rst │ ├── ds_d6.dts │ ├── ds_d6.yaml │ └── ds_d6_defconfig │ ├── p8 │ ├── Kconfig │ ├── Kconfig.board │ ├── Kconfig.defconfig │ ├── board.cmake │ ├── doc │ │ └── index.rst │ ├── p8.dts │ ├── p8.yaml │ └── p8_defconfig │ └── pinetime │ ├── Kconfig │ ├── Kconfig.board │ ├── Kconfig.defconfig │ ├── board.cmake │ ├── doc │ └── index.rst │ ├── pinetime.dts │ ├── pinetime.yaml │ └── pinetime_defconfig ├── drivers ├── Kconfig └── sensor │ ├── CMakeLists.txt │ ├── Kconfig │ ├── bma421 │ ├── CMakeLists.txt │ ├── Kconfig │ ├── bma421.c │ ├── bma421.h │ ├── bma421_trigger.c │ └── bma421_trigger.c-todo │ ├── cst816s │ ├── CMakeLists.txt │ ├── Kconfig │ ├── cst816s.c │ ├── cst816s.h │ └── cst816s_trigger.c │ └── hrs3300 │ ├── CMakeLists.txt │ ├── Kconfig │ ├── hrs3300.c │ └── hrs3300.h ├── dts └── bindings │ └── sensor │ ├── bosch,bma421-i2c.yaml │ ├── hx,hrs3300.yaml │ └── hynitron,cst816s.yaml ├── hypnos ├── CMakeLists.txt ├── config │ ├── bootloader.conf │ ├── logging_rtt.conf │ └── prj.conf ├── hypnos-photo.png ├── include │ ├── backlight.h │ ├── battery.h │ ├── bt.h │ ├── clock.h │ ├── cts_sync.h │ ├── display.h │ ├── event_handler.h │ ├── gfx.h │ ├── gui.h │ ├── log.h │ └── version.h ├── src │ ├── backlight.c │ ├── battery.c │ ├── bt.c │ ├── clock.c │ ├── cts_sync.c │ ├── display.c │ ├── event_handler.c │ ├── fonts │ │ └── rubik │ │ │ ├── OFL.txt │ │ │ └── Rubik-Regular.ttf │ ├── gfx.c │ ├── gui.c │ ├── log.c │ ├── main.c │ ├── rubik_regular_34.c │ └── rubik_regular_68.c └── watch_photo.jpg ├── include ├── battery.h └── drivers │ └── sensor │ └── cst816s.h ├── pkglist ├── subsys ├── CMakeLists.txt ├── Kconfig └── battery │ ├── CMakeLists.txt │ ├── Kconfig │ └── battery.c ├── west.yml └── zephyr └── module.yml /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2020 ZMK Firmware Contributors 2 | # Copyright (c) 2020 Endian Technologies AB 3 | 4 | # SPDX-License-Identifier: MIT 5 | 6 | name: Build and upload Hypnos 7 | 8 | # When to run this Workflow 9 | on: 10 | 11 | # Run this Workflow when files are pushed to this Branch 12 | push: 13 | branches: [ master ] 14 | 15 | # Also run this Workflow when a Pull Request is created or updated in this Branch 16 | pull_request: 17 | branches: [ master ] 18 | 19 | # Steps to run for the Workflow 20 | jobs: 21 | build: 22 | strategy: 23 | matrix: 24 | device: [pinetime, p8] 25 | runs-on: ubuntu-latest 26 | name: Build and upload 27 | steps: 28 | # To use this repository's private action, 29 | # you must check out the repository 30 | # Fetch tags and print them as well as the short commit hash 31 | - name: Checkout 32 | uses: actions/checkout@v2 33 | with: 34 | fetch-depth: 0 35 | - name: Cache west modules 36 | uses: actions/cache@v2 37 | env: 38 | cache-name: cache-zephyr-modules 39 | with: 40 | path: | 41 | modules/ 42 | tools/ 43 | zephyr/ 44 | bootloader/ 45 | key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('app/west.yml') }} 46 | restore-keys: | 47 | ${{ runner.os }}-build-${{ env.cache-name }}- 48 | ${{ runner.os }}-build- 49 | ${{ runner.os }}- 50 | - name: West Init 51 | uses: 'docker://zmkfirmware/zephyr-west-action-arm:latest' 52 | id: west-init 53 | with: 54 | args: 'init "-l app/' 55 | - name: West Update 56 | uses: 'docker://zmkfirmware/zephyr-west-action-arm:latest' 57 | id: west-update 58 | with: 59 | args: 'update' 60 | - name: West Config Zephyr Base 61 | uses: 'docker://zmkfirmware/zephyr-west-action-arm:latest' 62 | id: west-config 63 | with: 64 | args: 'config "--global zephyr.base-prefer configfile"' 65 | - name: West Zephyr Export 66 | uses: 'docker://zmkfirmware/zephyr-west-action-arm:latest' 67 | id: west-zephyr-export 68 | with: 69 | args: 'zephyr-export' 70 | - name: West Build ${{ matrix.device }} 71 | uses: 'docker://zmkfirmware/zephyr-west-action-arm:latest' 72 | id: west-build 73 | with: 74 | args: 'build "-p -b ${{ matrix.device }} app/hypnos"' 75 | - name: Install adafruit-nrfutil 76 | run: | 77 | pip3 install --user wheel 78 | pip3 install --user setuptools 79 | pip3 install --user adafruit-nrfutil 80 | - name: Install imgtool dependencies 81 | run: pip3 install --user -r bootloader/mcuboot/scripts/requirements.txt 82 | - name: Create mcuboot app image for lupyuen's custom PineTime bootloader # Use mcuboot v1.6.0-rc2 referenced by west.yml 83 | run: | 84 | bootloader/mcuboot/scripts/imgtool.py create --align 4 --version 1.0.0 --header-size 512 --slot-size 475136 build/zephyr/zephyr.bin hypnos-mcuboot-app-img.bin 85 | bootloader/mcuboot/scripts/imgtool.py verify hypnos-mcuboot-app-img.bin 86 | - name: Create DFU package 87 | run: | 88 | ~/.local/bin/adafruit-nrfutil dfu genpkg --dev-type 0x0052 --application hypnos-mcuboot-app-img.bin hypnos-mcuboot-app-dfu.zip 89 | # Unzip the package because Upload Artifact will zip up the files 90 | unzip hypnos-mcuboot-app-dfu.zip -d hypnos-mcuboot-app-dfu 91 | - name: Get firmware version from git 92 | id: vars 93 | shell: bash 94 | run: 95 | echo "::set-output name=fw_ver::$(git describe --tags --dirty)" 96 | # Upload zephyr firmware build 97 | - name: Upload firmware build 98 | uses: actions/upload-artifact@v2 99 | with: 100 | # Append firmware version to file name 101 | name: ${{ matrix.device }}-hypnos-${{ steps.vars.outputs.fw_ver }} 102 | path: | 103 | build/zephyr/zephyr.* 104 | # Upload DFU package 105 | - name: Upload DFU package 106 | uses: actions/upload-artifact@v2 107 | with: 108 | # Append firmware version to file name 109 | name: ${{ matrix.device }}-hypnos-${{ steps.vars.outputs.fw_ver }}-mcuboot-app-dfu 110 | path: hypnos-mcuboot-app-dfu/* 111 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.west 2 | /bootloader 3 | /modules 4 | /tools 5 | /zephyr 6 | /app/build 7 | /build 8 | /images 9 | GPATH 10 | GRTAGS 11 | GTAGS 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | 203 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2020 Endian Technologies AB 2 | # 3 | # SPDX-License-Identifier: Apache-2.0 4 | 5 | ifneq ($(BOARD),p8) 6 | BOARD := $(shell cat build/zephyr/.config 2>/dev/null | grep CONFIG_BOARD= \ 7 | | cut -d'"' -f 2 2>/dev/null) 8 | ifndef BOARD 9 | BOARD := pinetime 10 | endif 11 | endif 12 | 13 | ifneq ($(BOOTLOADER),n) 14 | BOOTLOADER := $(shell cat build/zephyr/.config 2>/dev/null \ 15 | | grep BOOTLOADER_MCUBOOT \ 16 | | cut -d'=' -f 2 | tr -cd '[ny]\n') 17 | endif 18 | 19 | BUILD := build/zephyr/zephyr.bin 20 | VERSION := $(shell git describe --tags --dirty) 21 | IMGDIR := images 22 | IMAGE := $(IMGDIR)/$(BOARD)-hypnos-$(VERSION)-mcuboot-app-img.bin 23 | PACKAGE := $(IMGDIR)/$(BOARD)-hypnos-$(VERSION)-mcuboot-app-dfu.zip 24 | 25 | .PHONY: build clean help tools 26 | 27 | ifneq ($(BOOTLOADER),n) 28 | dfu: $(BUILD) $(IMAGE) $(PACKAGE) 29 | image: $(BUILD) $(IMAGE) 30 | 31 | $(IMAGE): $(BUILD) | $(IMGDIR) 32 | @echo "Creating an MCUBoot app image" 33 | bootloader/mcuboot/scripts/imgtool.py create --align 4 --version 1.0.0 \ 34 | --header-size 512 --slot-size 475136 $(BUILD) $(IMAGE) 35 | endif 36 | build: 37 | west build -p -b $(BOARD) app/hypnos 38 | 39 | help: 40 | @echo "Build and flash the Hypnos application firmware as well as" 41 | @echo "images and DFU packages for devices running Lup Yuen Lee's" 42 | @echo "custom MCUBoot bootloader\n" 43 | @echo "Usage:" 44 | @echo " make [OPTION]... [TARGET]\n" 45 | @echo "Options:" 46 | @echo " BOARD=p8 build for p8 instead of pinetime" 47 | @echo " BOOTLOADER=n disable bootloader support" 48 | @echo " LOGGING=y enable RTT logging" 49 | @echo "Targets:" 50 | @echo " build build application firmware" 51 | @echo " clean remove the build directory" 52 | @echo " dfu build a Device Firmare Upgrade package" 53 | @echo " flash flash the most recent app image or firmware build over SWD" 54 | @echo " help show this message and exit" 55 | @echo " image build an MCUBoot app image" 56 | @echo " print print variables and file paths for debugging this Makefile" 57 | @echo " tools install tools for creating images and DFU packages" 58 | 59 | tools: 60 | @echo "Installing tools for creating app images and DFU packages" 61 | pip3 install --user setuptools 62 | pip3 install --user -r bootloader/mcuboot/scripts/requirements.txt 63 | pip3 install --user pyocd 64 | @echo "Done" 65 | 66 | $(IMGDIR): 67 | mkdir $(IMGDIR) 68 | 69 | $(BUILD): 70 | west build -p auto -b $(BOARD) app/hypnos 71 | 72 | $(PACKAGE): $(IMAGE) 73 | @echo "Creating a Device Firmware Update package" 74 | adafruit-nrfutil dfu genpkg --dev-type 0x0052 --application $(IMAGE) \ 75 | $(PACKAGE) 76 | 77 | ifneq ($(BOOTLOADER),n) 78 | flash: $(BUILD) $(IMAGE) 79 | @echo "Flashing the last modified Hypnos image after the bootloader" 80 | pyocd flash -e sector -a 0x8000 -t nrf52 $(shell ls -t images/*.bin | head -1) 81 | else 82 | flash: $(BUILD) 83 | @echo "Flashing Hypnos firmware without bootloader support" 84 | west flash 85 | endif 86 | 87 | clean: 88 | rm -rf build/ 89 | 90 | print: 91 | @echo "Board: $(BOARD)" 92 | @echo "Bootloader: $(BOOTLOADER)" 93 | @echo "Version: $(VERSION)" 94 | @echo "Build: $(BUILD)" 95 | @echo "Image: $(IMAGE)" 96 | @echo "Package: $(PACKAGE)" 97 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Hypnos 2 | 3 | This is a [Zephyr](https://www.zephyrproject.org/)-based firmware for the 4 | [PineTime](https://www.pine64.org/pinetime/) and P8 smartwatches. 5 | 6 | 7 | 8 | > **Hypnos**, son of Night and Darkness
9 | > He is said to be a calm and gentle god, as he helps humans in need and, due to their sleep, owns 10 | > half of their lives.[1](https://en.wikipedia.org/wiki/Hypnos) 11 | 12 | ## Features and roadmap 13 | 14 | - [x] 100 % Free Software 15 | - [x] Battery life: about one week 16 | - [x] Battery status: get state of charge and whether it's charging 17 | - [x] Clock: accurately increment current time 18 | - [x] Time and date synchronization with Bluetooth-connected device 19 | - [x] Touch sensor: tap to light up the display, swipe to display version information 20 | - [x] LVGL graphics: show time, date, battery and Bluetooth status 21 | - [x] Support for the PineTime bootloader 22 | - [x] Over-the-air firmware updates (SMP over BLE) 23 | - [x] Optional debug output via JLink RTT 24 | - [ ] Show notifications from Bluetooth-connected device 25 | - [ ] Set alarm 26 | - [ ] Wrist vibration 27 | - [ ] Quick glance via lift-to-wake 28 | 29 | ## Developer getting started guide 30 | 31 | This document assumes that you run a GNU/Linux or Mac operating system. 32 | 33 | ### Set up the development environment 34 | 35 | Follow Zephyr's [Getting Started 36 | Guide](https://docs.zephyrproject.org/latest/getting_started/index.html) up to 37 | step 3.2 "Get the Zephyr source code". Here you should run the commands below 38 | instead of the ones in the guide: 39 | 40 | ``` 41 | $ git clone https://github.com/endian-albin/pinetime-hypnos 42 | $ cd pinetime-hypnos 43 | $ west init -l app/ 44 | $ west update 45 | ``` 46 | 47 | Then complete the remaining steps under section 3 and 4. Finally, run `make 48 | tools`. 49 | 50 | ### Build and flash Hypnos 51 | 52 | Run `make` to build everything with the defaults or `make help` to view all the 53 | options and targets. 54 | 55 | Then connect your in-circuit programmer and run `make flash`. To install 56 | without a programmer, see Firmware updates below. 57 | 58 | ### Build and flash the bootloader 59 | 60 | To install or upgrade the bootloader, follow Lup Yuen's [build 61 | instructions](https://lupyuen.github.io/pinetime-rust-mynewt/articles/mcuboot#build-and-flash-mcuboot-bootloader) 62 | or [fetch the prebuilt 63 | binary](https://github.com/lupyuen/pinetime-rust-mynewt/releases/tag/v5.0.4). 64 | 65 | Then flash it to the beginning of the internal memory: 66 | ``` 67 | pyocd flash -e sector -t nrf52 bootloader-image.bin 68 | ``` 69 | 70 | ## Firmware updates 71 | 72 | ### SMP over Bluetooth LE 73 | 74 | Hypnos supports firmware image management over the Simple Management Protocol. 75 | 76 | To make use of this feature, get the 77 | [mcumgr](https://github.com/apache/mynewt-mcumgr#command-line-tool) command-line 78 | tool. Then run the commands below to list, upload, test and confirm firmware 79 | images over BLE: 80 | 81 | ``` 82 | # mcumgr --conntype="ble" --connstring ctlr_name=hci0,peer_name='Hypnos' image list 83 | # mcumgr --conntype="ble" --connstring ctlr_name=hci0,peer_name='Hypnos' image upload hypnos-mcuboot-app-img.bin 84 | # mcumgr --conntype="ble" --connstring ctlr_name=hci0,peer_name='Hypnos' image test 85 | # mcumgr --conntype="ble" --connstring ctlr_name=hci0,peer_name='Hypnos' reset 86 | # mcumgr --conntype="ble" --connstring ctlr_name=hci0,peer_name='Hypnos' image confirm 87 | ``` 88 | 89 | If you are unhappy with the new image, simply run the `reset` command again 90 | instead of `image confirm` to revert to the old one. If the image has already 91 | been confirmed but you still want to revert, simply run the commands above but 92 | skip the upload step or perform a manual rollback (see below). [See this 93 | document for more 94 | information](https://docs.zephyrproject.org/latest/samples/subsys/mgmt/mcumgr/smp_svr/README.html). 95 | 96 | ### DFU over Bluetooth LE 97 | 98 | To install Hypnos over the air from 99 | [InfiniTime](https://github.com/JF002/Pinetime), run `make dfu` to create a 100 | (Nordic) DFU package and upload it using 101 | [ota-dfu.py](https://github.com/JF002/Pinetime/tree/master/bootloader/ota-dfu-python) 102 | or nRF Connect. 103 | 104 | ### Manual rollback 105 | 106 | Version 5 of Lup Yuen's bootloader allows you to revert to the old firmware 107 | image by holding the button during boot. 108 | 109 | ## Copying 110 | 111 | This software may be used under the terms of the Apache License 2.0, unless 112 | explicitly stated otherwise. 113 | 114 | The documentation contained in this README and on the wiki are under the CC 115 | BY-SA 4.0 license. 116 | -------------------------------------------------------------------------------- /app/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(PINETIME_DIR ${CMAKE_CURRENT_LIST_DIR} CACHE PATH "Hypnos root directory") 2 | 3 | zephyr_include_directories(include) 4 | 5 | add_subdirectory(drivers/sensor) 6 | add_subdirectory(subsys) 7 | -------------------------------------------------------------------------------- /app/Kconfig.hypnos: -------------------------------------------------------------------------------- 1 | menu "Hypnos" 2 | 3 | rsource "drivers/sensor/Kconfig" 4 | rsource "subsys/Kconfig" 5 | 6 | endmenu 7 | -------------------------------------------------------------------------------- /app/boards/arm/ds_d6/Kconfig: -------------------------------------------------------------------------------- 1 | # nRF52832-MDK board configuration 2 | 3 | # Copyright (c) 2018 makerdiary.com. 4 | # SPDX-License-Identifier: Apache-2.0 5 | 6 | if BOARD_DS_D6 7 | 8 | config BOARD_ENABLE_DCDC 9 | bool "Enable DCDC mode" 10 | select SOC_DCDC_NRF52X 11 | default y 12 | 13 | endif # BOARD_DS_D6 14 | -------------------------------------------------------------------------------- /app/boards/arm/ds_d6/Kconfig.board: -------------------------------------------------------------------------------- 1 | # nRF52832-MDK board configuration 2 | 3 | # Copyright (c) 2018 makerdiary.com. 4 | # SPDX-License-Identifier: Apache-2.0 5 | 6 | config BOARD_DS_D6 7 | bool "DS-D6" 8 | depends on SOC_NRF52832_QFAA 9 | -------------------------------------------------------------------------------- /app/boards/arm/ds_d6/Kconfig.defconfig: -------------------------------------------------------------------------------- 1 | # nRF52832-MDK board configuration 2 | 3 | # Copyright (c) 2018 makerdiary.com. 4 | # SPDX-License-Identifier: Apache-2.0 5 | 6 | if BOARD_DS_D6 7 | 8 | config BOARD 9 | default "ds_d6" 10 | 11 | if I2C 12 | 13 | config I2C_0 14 | default y 15 | 16 | endif # I2C 17 | 18 | if PWM 19 | 20 | config PWM_0 21 | default y 22 | 23 | endif # PWM 24 | 25 | config BT_CTLR 26 | default BT 27 | 28 | endif # BOARD_DS_D6 29 | -------------------------------------------------------------------------------- /app/boards/arm/ds_d6/board.cmake: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: Apache-2.0 2 | 3 | board_runner_args(pyocd "--target=nrf52") 4 | include(${ZEPHYR_BASE}/boards/common/pyocd.board.cmake) 5 | -------------------------------------------------------------------------------- /app/boards/arm/ds_d6/doc/index.rst: -------------------------------------------------------------------------------- 1 | .. _nrf52832_mdk: 2 | 3 | ds-d6 4 | ################# 5 | 6 | Overview 7 | ******** 8 | 9 | The ds_d6 board is a smart watch, produced by Desay. 10 | 11 | It is cheap and hackable. 12 | 13 | Over the air device firmware update is possible. 14 | 15 | which means you can flash firmware without opening the watch 16 | 17 | 18 | 19 | See `ds_d6 website`_ for more information about the development 20 | board and `nRF52832 website`_ for the official reference on the IC itself. 21 | 22 | References 23 | ********** 24 | .. target-notes:: 25 | 26 | .. _nRF52832 website: https://www.nordicsemi.com/Products/Low-power-short-range-wireless/nRF52832 27 | .. _ds-d6 website: https://gitter.im/nRF51822-Arduino-Mbed-smart-watch/Lobby 28 | 29 | 30 | -------------------------------------------------------------------------------- /app/boards/arm/ds_d6/ds_d6.dts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018 Zelin 3 | * Copyright (c) 2018 makerdiary.com. 4 | * 5 | * SPDX-License-Identifier: Apache-2.0 6 | */ 7 | 8 | /dts-v1/; 9 | #include 10 | 11 | / { 12 | model = "Desay D6 smart watch"; 13 | compatible = "ds-d6", "nordic,nrf52832-qfaa", 14 | "nordic,nrf52832"; 15 | 16 | chosen { 17 | zephyr,console = &uart0; 18 | zephyr,shell-uart = &uart0; 19 | zephyr,uart-mcumgr = &uart0; 20 | zephyr,bt-mon-uart = &uart0; 21 | zephyr,bt-c2h-uart = &uart0; 22 | zephyr,sram = &sram0; 23 | zephyr,flash = &flash0; 24 | zephyr,code-partition = &slot0_partition; 25 | }; 26 | 27 | leds { 28 | compatible = "gpio-leds"; 29 | led0_green: led_0 { 30 | gpios = <&gpio0 26 GPIO_INT_ACTIVE_LOW>; 31 | label = "Green LED 0"; 32 | }; 33 | led1_red: led_1 { 34 | gpios = <&gpio0 23 GPIO_INT_ACTIVE_LOW>; 35 | label = "Red LED 1"; 36 | }; 37 | led2_blue: led_2 { 38 | gpios = <&gpio0 24 GPIO_INT_ACTIVE_LOW>; 39 | label = "Blue LED 1"; 40 | }; 41 | }; 42 | 43 | buttons { 44 | compatible = "gpio-keys"; 45 | button0: button_0 { 46 | gpios = <&gpio0 18 GPIO_PUD_PULL_UP>; 47 | label = "Push button switch 0"; 48 | }; 49 | }; 50 | 51 | /* These aliases are provided for compatibility with samples */ 52 | aliases { 53 | sw0 = &button0; 54 | led0 = &led0_green; 55 | led1 = &led1_red; 56 | led2 = &led2_blue; 57 | led0-green = &led0_green; 58 | led1-red = &led1_red; 59 | led2-blue = &led2_blue; 60 | }; 61 | 62 | }; 63 | 64 | &gpiote { 65 | status = "okay"; 66 | }; 67 | 68 | &gpio0 { 69 | status = "okay"; 70 | }; 71 | 72 | &uart0 { 73 | status = "okay"; 74 | compatible = "nordic,nrf-uart"; 75 | current-speed = <115200>; 76 | tx-pin = <23>; 77 | rx-pin = <22>; 78 | /* rts-pin = <-1>; 79 | cts-pin = <-1>;*/ 80 | }; 81 | 82 | &i2c0 { 83 | compatible = "nordic,nrf-twi"; 84 | status = "okay"; 85 | sda-pin = <8>; 86 | scl-pin = <7>; 87 | }; 88 | 89 | &i2c1 { 90 | compatible = "nordic,nrf-twi"; 91 | status = "okay"; 92 | sda-pin = <14>; 93 | scl-pin = <13>; 94 | }; 95 | 96 | &pwm0 { 97 | status = "okay"; 98 | ch0-pin = <22>; 99 | ch0-inverted; 100 | }; 101 | 102 | &flash0 { 103 | /* 104 | * For more information, see: 105 | * http://docs.zephyrproject.org/latest/guides/dts/index.html#flash-partitions 106 | */ 107 | partitions { 108 | compatible = "fixed-partitions"; 109 | #address-cells = <1>; 110 | #size-cells = <1>; 111 | 112 | boot_partition: partition@0 { 113 | label = "mcuboot"; 114 | reg = <0x00000000 0xc000>; 115 | }; 116 | slot0_partition: partition@c000 { 117 | label = "image-0"; 118 | reg = <0x0000C000 0x32000>; 119 | }; 120 | slot1_partition: partition@3e000 { 121 | label = "image-1"; 122 | reg = <0x0003E000 0x32000>; 123 | }; 124 | scratch_partition: partition@70000 { 125 | label = "image-scratch"; 126 | reg = <0x00070000 0xa000>; 127 | }; 128 | storage_partition: partition@7a000 { 129 | label = "storage"; 130 | reg = <0x0007a000 0x00006000>; 131 | }; 132 | }; 133 | }; 134 | -------------------------------------------------------------------------------- /app/boards/arm/ds_d6/ds_d6.yaml: -------------------------------------------------------------------------------- 1 | identifier: ds_d6 2 | name: DS-D6 3 | type: mcu 4 | arch: arm 5 | toolchain: 6 | - zephyr 7 | - gnuarmemb 8 | - xtools 9 | ram: 64 10 | flash: 512 11 | supported: 12 | - pwm 13 | -------------------------------------------------------------------------------- /app/boards/arm/ds_d6/ds_d6_defconfig: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: Apache-2.0 2 | 3 | CONFIG_ARM=y 4 | CONFIG_SOC_FAMILY_NRF=y 5 | CONFIG_SOC_SERIES_NRF52X=y 6 | CONFIG_SOC_NRF52832_QFAA=y 7 | CONFIG_BOARD_DS_D6=y 8 | 9 | # Enable MPU 10 | CONFIG_ARM_MPU=y 11 | 12 | # enable GPIO 13 | CONFIG_GPIO=y 14 | 15 | # enable uart driver 16 | CONFIG_SERIAL=y 17 | CONFIG_UART_0_NRF_UART=y 18 | 19 | # enable console 20 | CONFIG_CONSOLE=y 21 | CONFIG_UART_CONSOLE=y 22 | 23 | # additional board options 24 | CONFIG_GPIO_AS_PINRESET=y 25 | 26 | # bluetooth no external crystal 27 | CONFIG_CLOCK_CONTROL_NRF=y 28 | CONFIG_CLOCK_CONTROL_NRF_K32SRC_XTAL=y 29 | CONFIG_CLOCK_CONTROL_NRF_K32SRC_20PPM=y 30 | -------------------------------------------------------------------------------- /app/boards/arm/p8/Kconfig: -------------------------------------------------------------------------------- 1 | # P8 smartwatch configuration 2 | 3 | # Copyright (c) 2018 makerdiary.com. 4 | # SPDX-License-Identifier: Apache-2.0 5 | 6 | if BOARD_P8 7 | 8 | config BOARD_ENABLE_DCDC 9 | bool "Enable DCDC mode" 10 | select SOC_DCDC_NRF52X 11 | default y 12 | 13 | endif # BOARD_P8 14 | -------------------------------------------------------------------------------- /app/boards/arm/p8/Kconfig.board: -------------------------------------------------------------------------------- 1 | # nRF52832-MDK board configuration 2 | 3 | # Copyright (c) 2018 makerdiary.com. 4 | # SPDX-License-Identifier: Apache-2.0 5 | 6 | config BOARD_P8 7 | bool "p8" 8 | depends on SOC_NRF52832_QFAA 9 | -------------------------------------------------------------------------------- /app/boards/arm/p8/Kconfig.defconfig: -------------------------------------------------------------------------------- 1 | # nRF52832-MDK board configuration 2 | 3 | # Copyright (c) 2018 makerdiary.com. 4 | # SPDX-License-Identifier: Apache-2.0 5 | 6 | if BOARD_P8 7 | 8 | config BOARD 9 | default "p8" 10 | 11 | if I2C 12 | 13 | config I2C_1 14 | default y 15 | 16 | endif # I2C 17 | 18 | if SPI 19 | 20 | config SPI_0 21 | default y 22 | 23 | endif # SPI 24 | 25 | config BT_CTLR 26 | default BT 27 | 28 | endif # P8 29 | -------------------------------------------------------------------------------- /app/boards/arm/p8/board.cmake: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: Apache-2.0 2 | 3 | board_runner_args(pyocd "--target=nrf52" "--frequency=4000000") 4 | board_runner_args(nrfjprog "--nrf-family=NRF52") 5 | board_runner_args(jlink "--device=nrf52" "--speed=4000") 6 | include(${ZEPHYR_BASE}/boards/common/pyocd.board.cmake) 7 | include(${ZEPHYR_BASE}/boards/common/nrfjprog.board.cmake) 8 | include(${ZEPHYR_BASE}/boards/common/jlink.board.cmake) 9 | #include(${ZEPHYR_BASE}/boards/common/openocd-nrf5.board.cmake) 10 | -------------------------------------------------------------------------------- /app/boards/arm/p8/doc/index.rst: -------------------------------------------------------------------------------- 1 | .. _p8: 2 | 3 | p8 4 | ######## 5 | 6 | Overview 7 | ******** 8 | 9 | P8 is a smartwatch similar to the PineTime. It comes with a proprietary 10 | bootloader which must be removed for Hypnos to be installed. This 11 | requires access to the SWD pins, i.e. the sealed casing must be opened. 12 | -------------------------------------------------------------------------------- /app/boards/arm/p8/p8.dts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018 Zelin 3 | * Copyright (c) 2018 makerdiary.com. 4 | * 5 | * SPDX-License-Identifier: Apache-2.0 6 | */ 7 | 8 | /dts-v1/; 9 | #include 10 | 11 | / { 12 | model = "p8 smartwatch"; 13 | compatible = "nrf52832"; 14 | 15 | chosen { 16 | zephyr,sram = &sram0; 17 | zephyr,console = &uart0; 18 | // zephyr,shell-uart = &uart0; 19 | //zephyr,uart-mcumgr = &uart0; 20 | //zephyr,bt-mon-uart = &uart0; 21 | //zephyr,bt-c2h-uart = &uart0; 22 | zephyr,flash = &flash0; 23 | //zephyr,flash = &mx25r64; 24 | zephyr,code-partition = &slot0_partition; 25 | }; 26 | 27 | leds { 28 | compatible = "gpio-leds"; 29 | led0_green: led_0 { 30 | gpios = <&gpio0 14 GPIO_ACTIVE_LOW>; 31 | label = "Background LED 0"; 32 | }; 33 | led1_red: led_1 { 34 | gpios = <&gpio0 22 GPIO_ACTIVE_LOW>; 35 | label = "Background LED 1"; 36 | }; 37 | led2_blue: led_2 { 38 | gpios = <&gpio0 23 GPIO_ACTIVE_LOW>; 39 | label = "Background LED 2"; 40 | }; 41 | }; 42 | 43 | buttons { 44 | compatible = "gpio-keys"; 45 | button0: button_0 { 46 | gpios = <&gpio0 17 0>; 47 | label = "Push button switch 0"; 48 | }; 49 | }; 50 | 51 | /* These aliases are provided for compatibility with samples */ 52 | aliases { 53 | sw0 = &button0; 54 | led0 = &led0_green; 55 | led1 = &led1_red; 56 | led2 = &led2_blue; 57 | led0-green = &led0_green; 58 | led1-red = &led1_red; 59 | led2-blue = &led2_blue; 60 | }; 61 | }; 62 | 63 | &gpiote { 64 | status = "okay"; 65 | }; 66 | 67 | &gpio0 { 68 | status = "okay"; 69 | }; 70 | 71 | &i2c1 { 72 | compatible = "nordic,nrf-twi"; 73 | status = "okay"; 74 | sda-pin = <6>; 75 | scl-pin = <7>; 76 | clock-frequency = <100000>; 77 | bma421@18 { 78 | compatible = "bosch,bma421"; 79 | reg=<0x18>; 80 | label="BMA421"; 81 | int1-gpios = <&gpio0 8 0>; 82 | }; 83 | hrs3300@44 { 84 | compatible = "hx,hrs3300"; 85 | reg = <0x44>; 86 | label = "HRS3300"; 87 | }; 88 | cst816s@15 { 89 | compatible = "hynitron,cst816s"; 90 | label = "CST816S"; 91 | reg = <0x15>; 92 | int1-gpios = <&gpio0 28 0>; 93 | reset-gpios = <&gpio0 10 0>; 94 | }; 95 | }; 96 | 97 | &spi0 { 98 | compatible = "nordic,nrf-spi"; 99 | status = "okay"; 100 | sck-pin = <2>; 101 | mosi-pin = <3>; 102 | miso-pin = <4>; 103 | cs-gpios = <&gpio0 25 GPIO_ACTIVE_LOW>,<&gpio0 5 GPIO_ACTIVE_LOW>; 104 | st7789v@0 { 105 | compatible = "sitronix,st7789v"; 106 | label = "DISPLAY"; 107 | spi-max-frequency = <8000000>; 108 | reg = <0>; 109 | cmd-data-gpios = <&gpio0 18 GPIO_ACTIVE_LOW>; 110 | reset-gpios = <&gpio0 26 GPIO_ACTIVE_LOW>; 111 | width = <240>; 112 | height = <240>; 113 | x-offset = <0>; 114 | y-offset = <0>; 115 | vcom = <0x19>; 116 | gctrl = <0x35>; 117 | vrhs = <0x12>; 118 | vdvs = <0x20>; 119 | mdac = <0x00>; 120 | gamma = <0x01>; 121 | colmod = <0x05>; 122 | lcm = <0x2c>; 123 | porch-param = [0c 0c 00 33 33]; 124 | cmd2en-param = [5a 69 02 01]; 125 | pwctrl1-param = [a4 a1]; 126 | pvgam-param = [D0 04 0D 11 13 2B 3F 54 4C 18 0D 0B 1F 23]; 127 | nvgam-param = [D0 04 0C 11 13 2C 3F 44 51 2F 1F 1F 20 23]; 128 | ram-param = [00 F0]; 129 | rgb-param = [CD 08 14]; 130 | }; 131 | 132 | flash1: mx25r6435f@1 { 133 | compatible = "jedec,spi-nor"; 134 | reg = <1>; 135 | spi-max-frequency = <1000000>; 136 | label = "MX25R64"; 137 | jedec-id = [20 40 16]; 138 | size = <67108864>; 139 | has-be32k; 140 | }; 141 | }; 142 | 143 | &flash0 { 144 | /* 145 | * For more information, see: 146 | * https://docs.zephyrproject.org/latest/guides/dts/legacy-macros.html#legacy-flash-partitions 147 | */ 148 | partitions { 149 | compatible = "fixed-partitions"; 150 | #address-cells = <1>; 151 | #size-cells = <1>; 152 | 153 | boot_partition: partition@0 { 154 | label = "mcuboot"; 155 | reg = <0x00000000 0x8000>; 156 | }; 157 | slot0_partition: partition@8000 { 158 | label = "image-0"; 159 | reg = <0x00008000 0x74000>; 160 | }; 161 | scratch_partition: partition@7c000 { 162 | label = "image-scratch"; 163 | reg = <0x0007c000 0xa000>; 164 | }; 165 | }; 166 | }; 167 | 168 | &flash1 { 169 | /* 170 | * For more information, see: 171 | * https://docs.zephyrproject.org/latest/guides/dts/legacy-macros.html#legacy-flash-partitions 172 | */ 173 | partitions { 174 | compatible = "fixed-partitions"; 175 | #address-cells = <1>; 176 | #size-cells = <1>; 177 | 178 | slot1_partition: partition@40000 { 179 | label = "image-1"; 180 | reg = <0x00040000 0x74000>; 181 | }; 182 | storage_partition: partition@b4000 { 183 | label = "storage"; 184 | reg = <0x000b4000 0x0034c000>; 185 | }; 186 | }; 187 | }; 188 | 189 | &adc { 190 | status = "okay"; 191 | }; 192 | 193 | &uart0 { 194 | status = "disabled"; 195 | }; 196 | -------------------------------------------------------------------------------- /app/boards/arm/p8/p8.yaml: -------------------------------------------------------------------------------- 1 | identifier: p8 2 | name: P8 3 | type: mcu 4 | arch: arm 5 | toolchain: 6 | - zephyr 7 | - gnuarmemb 8 | - xtools 9 | ram: 64 10 | flash: 512 11 | supported: 12 | - pwm 13 | - spi 14 | - i2c 15 | - gpio 16 | -------------------------------------------------------------------------------- /app/boards/arm/p8/p8_defconfig: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: Apache-2.0 2 | 3 | #CONFIG_ARM=y 4 | #CONFIG_SOC_FAMILY_NRF=y 5 | CONFIG_SOC_SERIES_NRF52X=y 6 | CONFIG_SOC_NRF52832_QFAA=y 7 | CONFIG_BOARD_P8=y 8 | 9 | # Enable MPU 10 | CONFIG_ARM_MPU=y 11 | 12 | # enable GPIO 13 | CONFIG_GPIO=y 14 | 15 | # enable uart driver 16 | #CONFIG_SERIAL=y 17 | #CONFIG_UART_0_NRF_UART=y 18 | 19 | # enable console 20 | #CONFIG_CONSOLE=y 21 | #CONFIG_UART_CONSOLE=y 22 | 23 | # additional board options 24 | CONFIG_GPIO_AS_PINRESET=y 25 | 26 | 27 | #CONFIG_CLOCK_CONTROL_NRF=y 28 | #CONFIG_CLOCK_CONTROL_NRF_K32SRC_SYNTH=y 29 | #CONFIG_CLOCK_CONTROL_NRF_K32SRC_20PPM=y 30 | #CONFIG_CLOCK_CONTROL_NRF_K32SRC_250PPM=y 31 | # 32 | CONFIG_CLOCK_CONTROL_NRF=y 33 | # CONFIG_CLOCK_CONTROL_NRF_K32SRC_RC is not set 34 | CONFIG_CLOCK_CONTROL_NRF_K32SRC_XTAL=y 35 | # # CONFIG_CLOCK_CONTROL_NRF_K32SRC_SYNTH is not set 36 | # # CONFIG_CLOCK_CONTROL_NRF_K32SRC_EXT_LOW_SWING is not set 37 | # # CONFIG_CLOCK_CONTROL_NRF_K32SRC_EXT_FULL_SWING is not set 38 | CONFIG_CLOCK_CONTROL_NRF_K32SRC_500PPM=y 39 | 40 | #CONFIG_SPI=y 41 | #CONFIG_DISPLAY=y 42 | #CONFIG_ST7789V=y 43 | #CONFIG_ST7789V_RGB565=y 44 | #CONFIG_ADXL362=y 45 | 46 | 47 | #CONFIG_LVGL=y 48 | #CONFIG_LVGL_BITS_PER_PIXEL=16 49 | #CONFIG_LVGL_HOR_RES=240 50 | #CONFIG_LVGL_VER_RES=240 51 | #CONFIG_LVGL_DISPLAY_DEV_NAME="DISPLAY" 52 | #CONFIG_NRFX_TWI=y 53 | # CONFIG_NRFX_TWI0 is not set 54 | # 55 | # 56 | # 57 | CONFIG_I2C=y 58 | CONFIG_I2C_1=y 59 | CONFIG_I2C_INIT_PRIORITY=60 60 | #CONFIG_SENSOR=y 61 | #CONFIG_SENSOR_INIT_PRIORITY=90 62 | # 63 | # 64 | #CONFIG_BMA421=y 65 | #CONFIG_ADXL372=y 66 | #CONFIG_ADXL372_I2C=y 67 | #CONFIG_ADXL362_ACCEL_RANGE_RUNTIME=y 68 | ## CONFIG_ADXL362_ACCEL_RANGE_2G is not set 69 | ## # CONFIG_ADXL362_ACCEL_RANGE_4G is not set 70 | ## # CONFIG_ADXL362_ACCEL_RANGE_8G is not set 71 | #CONFIG_ADXL362_ACCEL_ODR_RUNTIME=y 72 | ## # CONFIG_ADXL362_ACCEL_ODR_12_5 is not set 73 | ## # CONFIG_ADXL362_ACCEL_ODR_25 is not set 74 | ## # CONFIG_ADXL362_ACCEL_ODR_50 is not set 75 | ## # CONFIG_ADXL362_ACCEL_ODR_100 is not set 76 | ## # CONFIG_ADXL362_ACCEL_ODR_200 is not set 77 | ## # CONFIG_ADXL362_ACCEL_ODR_400 is not set 78 | ## # CONFIG_ADXL362_TRIGGER_NONE is not set 79 | ## CONFIG_ADXL362_TRIGGER_GLOBAL_THREAD=y 80 | ## # CONFIG_ADXL362_TRIGGER_OWN_THREAD is not set 81 | #CONFIG_ADXL362_TRIGGER=y 82 | #CONFIG_ADXL362_ACTIVITY_THRESHOLD=1000 83 | #CONFIG_ADXL362_INACTIVITY_THRESHOLD=100 84 | #CONFIG_ADXL362_INTERRUPT_MODE=1 85 | #CONFIG_ADXL362_ABS_REF_MODE=1 86 | -------------------------------------------------------------------------------- /app/boards/arm/pinetime/Kconfig: -------------------------------------------------------------------------------- 1 | # PINETIME smartwatch configuration 2 | 3 | # Copyright (c) 2018 makerdiary.com. 4 | # SPDX-License-Identifier: Apache-2.0 5 | 6 | if BOARD_PINETIME 7 | 8 | config BOARD_ENABLE_DCDC 9 | bool "Enable DCDC mode" 10 | select SOC_DCDC_NRF52X 11 | default y 12 | 13 | endif # BOARD_PINETIME 14 | -------------------------------------------------------------------------------- /app/boards/arm/pinetime/Kconfig.board: -------------------------------------------------------------------------------- 1 | # nRF52832-MDK board configuration 2 | 3 | # Copyright (c) 2018 makerdiary.com. 4 | # SPDX-License-Identifier: Apache-2.0 5 | 6 | config BOARD_PINETIME 7 | bool "pinetime" 8 | depends on SOC_NRF52832_QFAA 9 | -------------------------------------------------------------------------------- /app/boards/arm/pinetime/Kconfig.defconfig: -------------------------------------------------------------------------------- 1 | # nRF52832-MDK board configuration 2 | 3 | # Copyright (c) 2018 makerdiary.com. 4 | # SPDX-License-Identifier: Apache-2.0 5 | 6 | if BOARD_PINETIME 7 | 8 | config BOARD 9 | default "pinetime" 10 | 11 | if I2C 12 | 13 | config I2C_1 14 | default y 15 | 16 | endif # I2C 17 | 18 | if SPI 19 | 20 | config SPI_0 21 | default y 22 | 23 | endif # SPI 24 | 25 | config BT_CTLR 26 | default BT 27 | 28 | endif # PINETIME 29 | -------------------------------------------------------------------------------- /app/boards/arm/pinetime/board.cmake: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: Apache-2.0 2 | 3 | board_runner_args(pyocd "--target=nrf52" "--frequency=4000000") 4 | board_runner_args(nrfjprog "--nrf-family=NRF52") 5 | board_runner_args(jlink "--device=nrf52" "--speed=4000") 6 | include(${ZEPHYR_BASE}/boards/common/pyocd.board.cmake) 7 | include(${ZEPHYR_BASE}/boards/common/nrfjprog.board.cmake) 8 | include(${ZEPHYR_BASE}/boards/common/jlink.board.cmake) 9 | #include(${ZEPHYR_BASE}/boards/common/openocd-nrf5.board.cmake) 10 | -------------------------------------------------------------------------------- /app/boards/arm/pinetime/doc/index.rst: -------------------------------------------------------------------------------- 1 | .. _pinetime: 2 | 3 | pinetime 4 | ######## 5 | 6 | Overview 7 | ******** 8 | 9 | PineTime is an nRF52832-based smartwatch that is shipped with free bootloader 10 | and application firmware. The PineTime contains, among other things, a battery, 11 | touch screen, vibrator, heart-rate sensor, an accelerometer and Bluetooth LE. 12 | -------------------------------------------------------------------------------- /app/boards/arm/pinetime/pinetime.dts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018 Zelin 3 | * Copyright (c) 2018 makerdiary.com. 4 | * 5 | * SPDX-License-Identifier: Apache-2.0 6 | */ 7 | 8 | /dts-v1/; 9 | #include 10 | 11 | / { 12 | model = "pinetime smartwatch"; 13 | compatible = "nrf52832"; 14 | 15 | chosen { 16 | zephyr,sram = &sram0; 17 | zephyr,console = &uart0; 18 | // zephyr,shell-uart = &uart0; 19 | //zephyr,uart-mcumgr = &uart0; 20 | //zephyr,bt-mon-uart = &uart0; 21 | //zephyr,bt-c2h-uart = &uart0; 22 | zephyr,flash = &flash0; 23 | //zephyr,flash = &mx25r64; 24 | zephyr,code-partition = &slot0_partition; 25 | }; 26 | 27 | leds { 28 | compatible = "gpio-leds"; 29 | led0_green: led_0 { 30 | gpios = <&gpio0 14 GPIO_ACTIVE_LOW>; 31 | label = "Background LED 0"; 32 | }; 33 | led1_red: led_1 { 34 | gpios = <&gpio0 22 GPIO_ACTIVE_LOW>; 35 | label = "Background LED 1"; 36 | }; 37 | led2_blue: led_2 { 38 | gpios = <&gpio0 23 GPIO_ACTIVE_LOW>; 39 | label = "Background LED 2"; 40 | }; 41 | }; 42 | 43 | buttons { 44 | compatible = "gpio-keys"; 45 | button0: button_0 { 46 | gpios = <&gpio0 13 0>; 47 | label = "Push button switch 0"; 48 | }; 49 | }; 50 | 51 | /* These aliases are provided for compatibility with samples */ 52 | aliases { 53 | sw0 = &button0; 54 | led0 = &led0_green; 55 | led1 = &led1_red; 56 | led2 = &led2_blue; 57 | led0-green = &led0_green; 58 | led1-red = &led1_red; 59 | led2-blue = &led2_blue; 60 | }; 61 | }; 62 | 63 | &gpiote { 64 | status = "okay"; 65 | }; 66 | 67 | &gpio0 { 68 | status = "okay"; 69 | }; 70 | 71 | &i2c1 { 72 | compatible = "nordic,nrf-twi"; 73 | status = "okay"; 74 | sda-pin = <6>; 75 | scl-pin = <7>; 76 | clock-frequency = <100000>; 77 | bma421@18 { 78 | compatible = "bosch,bma421"; 79 | reg=<0x18>; 80 | label="BMA421"; 81 | int1-gpios = <&gpio0 8 0>; 82 | }; 83 | hrs3300@44 { 84 | compatible = "hx,hrs3300"; 85 | reg = <0x44>; 86 | label = "HRS3300"; 87 | }; 88 | cst816s@15 { 89 | compatible = "hynitron,cst816s"; 90 | label = "CST816S"; 91 | reg = <0x15>; 92 | int1-gpios = <&gpio0 28 0>; 93 | reset-gpios = <&gpio0 10 0>; 94 | }; 95 | }; 96 | 97 | &spi0 { 98 | compatible = "nordic,nrf-spi"; 99 | status = "okay"; 100 | sck-pin = <2>; 101 | mosi-pin = <3>; 102 | miso-pin = <4>; 103 | cs-gpios = <&gpio0 25 GPIO_ACTIVE_LOW>,<&gpio0 5 GPIO_ACTIVE_LOW>; 104 | st7789v@0 { 105 | compatible = "sitronix,st7789v"; 106 | label = "DISPLAY"; 107 | spi-max-frequency = <8000000>; 108 | reg = <0>; 109 | cmd-data-gpios = <&gpio0 18 GPIO_ACTIVE_LOW>; 110 | reset-gpios = <&gpio0 26 GPIO_ACTIVE_LOW>; 111 | width = <240>; 112 | height = <240>; 113 | x-offset = <0>; 114 | y-offset = <0>; 115 | vcom = <0x19>; 116 | gctrl = <0x35>; 117 | vrhs = <0x12>; 118 | vdvs = <0x20>; 119 | mdac = <0x00>; 120 | gamma = <0x01>; 121 | colmod = <0x05>; 122 | lcm = <0x2c>; 123 | porch-param = [0c 0c 00 33 33]; 124 | cmd2en-param = [5a 69 02 01]; 125 | pwctrl1-param = [a4 a1]; 126 | pvgam-param = [D0 04 0D 11 13 2B 3F 54 4C 18 0D 0B 1F 23]; 127 | nvgam-param = [D0 04 0C 11 13 2C 3F 44 51 2F 1F 1F 20 23]; 128 | ram-param = [00 F0]; 129 | rgb-param = [CD 08 14]; 130 | }; 131 | 132 | flash1: mx25r6435f@1 { 133 | compatible = "jedec,spi-nor"; 134 | reg = <1>; 135 | spi-max-frequency = <1000000>; 136 | label = "MX25R64"; 137 | jedec-id = [0b 40 16]; 138 | size = <67108864>; 139 | has-be32k; 140 | }; 141 | }; 142 | 143 | &flash0 { 144 | /* 145 | * For more information, see: 146 | * https://docs.zephyrproject.org/latest/guides/dts/legacy-macros.html#legacy-flash-partitions 147 | */ 148 | partitions { 149 | compatible = "fixed-partitions"; 150 | #address-cells = <1>; 151 | #size-cells = <1>; 152 | 153 | boot_partition: partition@0 { 154 | label = "mcuboot"; 155 | reg = <0x00000000 0x8000>; 156 | }; 157 | slot0_partition: partition@8000 { 158 | label = "image-0"; 159 | reg = <0x00008000 0x74000>; 160 | }; 161 | scratch_partition: partition@7c000 { 162 | label = "image-scratch"; 163 | reg = <0x0007c000 0xa000>; 164 | }; 165 | }; 166 | }; 167 | 168 | &flash1 { 169 | /* 170 | * For more information, see: 171 | * https://docs.zephyrproject.org/latest/guides/dts/legacy-macros.html#legacy-flash-partitions 172 | */ 173 | partitions { 174 | compatible = "fixed-partitions"; 175 | #address-cells = <1>; 176 | #size-cells = <1>; 177 | 178 | slot1_partition: partition@40000 { 179 | label = "image-1"; 180 | reg = <0x00040000 0x74000>; 181 | }; 182 | storage_partition: partition@b4000 { 183 | label = "storage"; 184 | reg = <0x000b4000 0x0034c000>; 185 | }; 186 | }; 187 | }; 188 | 189 | &adc { 190 | status = "okay"; 191 | }; 192 | 193 | &uart0 { 194 | status = "disabled"; 195 | }; 196 | -------------------------------------------------------------------------------- /app/boards/arm/pinetime/pinetime.yaml: -------------------------------------------------------------------------------- 1 | identifier: pinetime 2 | name: PINETIME 3 | type: mcu 4 | arch: arm 5 | toolchain: 6 | - zephyr 7 | - gnuarmemb 8 | - xtools 9 | ram: 64 10 | flash: 512 11 | supported: 12 | - pwm 13 | - spi 14 | - i2c 15 | - gpio 16 | -------------------------------------------------------------------------------- /app/boards/arm/pinetime/pinetime_defconfig: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: Apache-2.0 2 | 3 | #CONFIG_ARM=y 4 | #CONFIG_SOC_FAMILY_NRF=y 5 | CONFIG_SOC_SERIES_NRF52X=y 6 | CONFIG_SOC_NRF52832_QFAA=y 7 | CONFIG_BOARD_PINETIME=y 8 | 9 | # Enable MPU 10 | CONFIG_ARM_MPU=y 11 | 12 | # enable GPIO 13 | CONFIG_GPIO=y 14 | 15 | # enable uart driver 16 | #CONFIG_SERIAL=y 17 | #CONFIG_UART_0_NRF_UART=y 18 | 19 | # enable console 20 | #CONFIG_CONSOLE=y 21 | #CONFIG_UART_CONSOLE=y 22 | 23 | # additional board options 24 | CONFIG_GPIO_AS_PINRESET=y 25 | 26 | 27 | #CONFIG_CLOCK_CONTROL_NRF=y 28 | #CONFIG_CLOCK_CONTROL_NRF_K32SRC_SYNTH=y 29 | #CONFIG_CLOCK_CONTROL_NRF_K32SRC_20PPM=y 30 | #CONFIG_CLOCK_CONTROL_NRF_K32SRC_250PPM=y 31 | # 32 | CONFIG_CLOCK_CONTROL_NRF=y 33 | # CONFIG_CLOCK_CONTROL_NRF_K32SRC_RC is not set 34 | CONFIG_CLOCK_CONTROL_NRF_K32SRC_XTAL=y 35 | # # CONFIG_CLOCK_CONTROL_NRF_K32SRC_SYNTH is not set 36 | # # CONFIG_CLOCK_CONTROL_NRF_K32SRC_EXT_LOW_SWING is not set 37 | # # CONFIG_CLOCK_CONTROL_NRF_K32SRC_EXT_FULL_SWING is not set 38 | CONFIG_CLOCK_CONTROL_NRF_K32SRC_500PPM=y 39 | 40 | #CONFIG_SPI=y 41 | #CONFIG_DISPLAY=y 42 | #CONFIG_ST7789V=y 43 | #CONFIG_ST7789V_RGB565=y 44 | #CONFIG_ADXL362=y 45 | 46 | 47 | #CONFIG_LVGL=y 48 | #CONFIG_LVGL_BITS_PER_PIXEL=16 49 | #CONFIG_LVGL_HOR_RES=240 50 | #CONFIG_LVGL_VER_RES=240 51 | #CONFIG_LVGL_DISPLAY_DEV_NAME="DISPLAY" 52 | #CONFIG_NRFX_TWI=y 53 | # CONFIG_NRFX_TWI0 is not set 54 | # 55 | # 56 | # 57 | CONFIG_I2C=y 58 | CONFIG_I2C_1=y 59 | CONFIG_I2C_INIT_PRIORITY=60 60 | #CONFIG_SENSOR=y 61 | #CONFIG_SENSOR_INIT_PRIORITY=90 62 | # 63 | # 64 | #CONFIG_BMA421=y 65 | #CONFIG_ADXL372=y 66 | #CONFIG_ADXL372_I2C=y 67 | #CONFIG_ADXL362_ACCEL_RANGE_RUNTIME=y 68 | ## CONFIG_ADXL362_ACCEL_RANGE_2G is not set 69 | ## # CONFIG_ADXL362_ACCEL_RANGE_4G is not set 70 | ## # CONFIG_ADXL362_ACCEL_RANGE_8G is not set 71 | #CONFIG_ADXL362_ACCEL_ODR_RUNTIME=y 72 | ## # CONFIG_ADXL362_ACCEL_ODR_12_5 is not set 73 | ## # CONFIG_ADXL362_ACCEL_ODR_25 is not set 74 | ## # CONFIG_ADXL362_ACCEL_ODR_50 is not set 75 | ## # CONFIG_ADXL362_ACCEL_ODR_100 is not set 76 | ## # CONFIG_ADXL362_ACCEL_ODR_200 is not set 77 | ## # CONFIG_ADXL362_ACCEL_ODR_400 is not set 78 | ## # CONFIG_ADXL362_TRIGGER_NONE is not set 79 | ## CONFIG_ADXL362_TRIGGER_GLOBAL_THREAD=y 80 | ## # CONFIG_ADXL362_TRIGGER_OWN_THREAD is not set 81 | #CONFIG_ADXL362_TRIGGER=y 82 | #CONFIG_ADXL362_ACTIVITY_THRESHOLD=1000 83 | #CONFIG_ADXL362_INACTIVITY_THRESHOLD=100 84 | #CONFIG_ADXL362_INTERRUPT_MODE=1 85 | #CONFIG_ADXL362_ABS_REF_MODE=1 86 | -------------------------------------------------------------------------------- /app/drivers/Kconfig: -------------------------------------------------------------------------------- 1 | # drivers configuration options 2 | 3 | # Copyright (c) 2015 Intel Corporation 4 | # SPDX-License-Identifier: Apache-2.0 5 | 6 | menu "Device Drivers" 7 | 8 | 9 | source "drivers/sensor/Kconfig" 10 | 11 | 12 | endmenu 13 | -------------------------------------------------------------------------------- /app/drivers/sensor/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: Apache-2.0 2 | 3 | add_subdirectory_ifdef(CONFIG_BMA421 bma421) 4 | add_subdirectory_ifdef(CONFIG_HRS3300 hrs3300) 5 | add_subdirectory_ifdef(CONFIG_CST816S cst816s) 6 | 7 | -------------------------------------------------------------------------------- /app/drivers/sensor/Kconfig: -------------------------------------------------------------------------------- 1 | # Sensor configuration options 2 | 3 | # Copyright (c) 2016 Intel Corporation 4 | # SPDX-License-Identifier: Apache-2.0 5 | 6 | 7 | menu "Sensor drivers" 8 | 9 | 10 | rsource "bma421/Kconfig" 11 | 12 | rsource "hrs3300/Kconfig" 13 | 14 | rsource "cst816s/Kconfig" 15 | 16 | endmenu 17 | -------------------------------------------------------------------------------- /app/drivers/sensor/bma421/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: Apache-2.0 2 | 3 | zephyr_library() 4 | 5 | zephyr_library_sources_ifdef(CONFIG_BMA421 bma421.c) 6 | zephyr_library_sources_ifdef(CONFIG_BMA421_TRIGGER bma421_trigger.c) 7 | -------------------------------------------------------------------------------- /app/drivers/sensor/bma421/Kconfig: -------------------------------------------------------------------------------- 1 | # BMA421 Three Axis Accelerometer configuration options 2 | 3 | # Copyright (c) 2016 Intel Corporation 4 | # SPDX-License-Identifier: Apache-2.0 5 | 6 | menuconfig BMA421 7 | bool "BMA421 Three Axis Accelerometer Family" 8 | depends on I2C 9 | help 10 | Enable driver for BMA421 I2C-based triaxial accelerometer sensor 11 | family. 12 | 13 | if BMA421 14 | 15 | choice 16 | prompt "Chip type" 17 | default BMA421_CHIP_BMA421 18 | help 19 | Choose desired chip type from the BMA421 family. 20 | 21 | config BMA421_CHIP_BMA421 22 | bool "BMA421" 23 | help 24 | Choose this option to enable the BMA421 chip. 25 | 26 | endchoice 27 | 28 | config BMA421_NAME 29 | string "Driver name" 30 | default "BMA421" if BMA421_CHIP_BMA421 31 | help 32 | Device name with which the sensor is identified. 33 | 34 | config BMA421_I2C_ADDR 35 | hex "BMA421 I2C address" 36 | default 0x18 if BMA421_CHIP_BMA421 37 | help 38 | I2C address of the BMA421 sensor. 39 | 40 | 0x10: Use if the SDO pin is pulled to GND. 41 | 0x10: Use if the SDO pin is pulled to VDDIO. 42 | 0x18: Use if the SDO pin is pulled to GND. 43 | 0x19: Use if the SDO pin is pulled to VDDIO. 44 | 45 | config BMA421_I2C_MASTER_DEV_NAME 46 | string "I2C master device name" 47 | default "I2C_1" 48 | help 49 | Specify the device name of the I2C master device to which chip is 50 | connected. 51 | 52 | choice 53 | prompt "Trigger mode" 54 | default BMA421_TRIGGER_GLOBAL_THREAD 55 | help 56 | Specify the type of triggering to be used by the driver. 57 | 58 | config BMA421_TRIGGER_NONE 59 | bool "No trigger" 60 | 61 | config BMA421_TRIGGER_GLOBAL_THREAD 62 | bool "Use global thread" 63 | depends on GPIO 64 | select BMA421_TRIGGER 65 | 66 | config BMA421_TRIGGER_OWN_THREAD 67 | bool "Use own thread" 68 | depends on GPIO 69 | select BMA421_TRIGGER 70 | 71 | endchoice 72 | 73 | config BMA421_TRIGGER 74 | bool 75 | 76 | config BMA421_GPIO_DEV_NAME 77 | string "GPIO device" 78 | default "GPIO_0" 79 | depends on BMA421_TRIGGER 80 | help 81 | The device name of the GPIO device to which the chip's interrupt pins 82 | are connected. 83 | 84 | config BMA421_GPIO_PIN_NUM 85 | int "Interrupt GPIO pin number" 86 | default 8 87 | depends on BMA421_TRIGGER 88 | help 89 | The number of the GPIO on which the interrupt signal from the chip 90 | will be received. 91 | 92 | config BMA421_THREAD_PRIORITY 93 | int "Thread priority" 94 | depends on BMA421_TRIGGER_OWN_THREAD 95 | default 10 96 | help 97 | Priority of thread used by the driver to handle interrupts. 98 | 99 | config BMA421_THREAD_STACK_SIZE 100 | int "Thread stack size" 101 | depends on BMA421_TRIGGER_OWN_THREAD 102 | default 1024 103 | help 104 | Stack size of thread used by the driver to handle interrupts. 105 | 106 | choice 107 | prompt "Acceleration measurement range" 108 | default BMA421_ACC_RANGE_2G 109 | help 110 | Measurement range for acceleration values. 111 | 112 | config BMA421_ACC_RANGE_2G 113 | bool "+/-2g" 114 | 115 | config BMA421_ACC_RANGE_4G 116 | bool "+/-4g" 117 | 118 | config BMA421_ACC_RANGE_8G 119 | bool "+/-8g" 120 | 121 | config BMA421_ACC_RANGE_16G 122 | bool "+/-16g" 123 | 124 | endchoice 125 | 126 | choice 127 | prompt "Acceleration data filter bandwidth" 128 | default BMA421_ACC_ODR_7 129 | help 130 | Bandwidth of filtered acceleration data. 131 | 132 | config BMA421_ACC_ODR_1 133 | bool "7.81Hz" 134 | 135 | config BMA421_ACC_ODR_2 136 | bool "15.63HZ" 137 | 138 | config BMA421_ACC_ODR_3 139 | bool "31.25Hz" 140 | 141 | config BMA421_ACC_ODR_4 142 | bool "62.5Hz" 143 | 144 | config BMA421_ACC_ODR_5 145 | bool "125Hz" 146 | 147 | config BMA421_ACC_ODR_6 148 | bool "250HZ" 149 | 150 | config BMA421_ACC_ODR_7 151 | bool "500Hz" 152 | 153 | config BMA421_ACC_ODR_8 154 | bool "1000Hz" 155 | 156 | config BMA421_ACC_ODR_9 157 | bool "2000Hz" 158 | 159 | config BMA421_ACC_ODR_10 160 | bool "4000Hz" 161 | 162 | config BMA421_ACC_ODR_11 163 | bool "8000Hz" 164 | 165 | config BMA421_ACC_ODR_12 166 | bool "16000Hz" 167 | 168 | config BMA421_PMU_ODR_13 169 | bool "unfiltered" 170 | 171 | endchoice 172 | 173 | endif # BMA421 174 | -------------------------------------------------------------------------------- /app/drivers/sensor/bma421/bma421.c: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "bma421.h" 13 | 14 | #define MY_REGISTER1 (*(volatile uint8_t*)0x2000F005) 15 | #define MY_REGISTER2 (*(volatile uint8_t*)0x2000F006) 16 | #define MY_REGISTER3 (*(volatile uint8_t*)0x2000F007) 17 | #define MY_REGISTER4 (*(volatile uint8_t*)0x2000F008) 18 | #define MY_REGISTER5 (*(volatile uint8_t*)0x2000F009) 19 | #define MY_REGISTER6 (*(volatile uint8_t*)0x2000F00A) 20 | 21 | 22 | //#define BMA421_CMD_ADDR UINT8_C(0X7E) 23 | 24 | LOG_MODULE_REGISTER(BMA421, CONFIG_SENSOR_LOG_LEVEL); 25 | 26 | 27 | 28 | static void i2c_delay(unsigned int cycles_to_wait) 29 | { 30 | u32_t start = k_cycle_get_32(); 31 | 32 | /* Wait until the given number of cycles have passed */ 33 | while (k_cycle_get_32() - start < cycles_to_wait) { 34 | } 35 | } 36 | 37 | 38 | 39 | 40 | 41 | static int bma421_sample_fetch(struct device *dev, enum sensor_channel chan) 42 | { 43 | struct bma421_data *drv_data = dev->driver_data; 44 | u8_t buf[6]; 45 | u8_t lsb; 46 | u8_t id = 0U; 47 | i2c_delay(1000); 48 | 49 | __ASSERT_NO_MSG(chan == SENSOR_CHAN_ALL); 50 | 51 | /* 52 | * since all accel data register addresses are consecutive, 53 | * a burst read can be used to read all the samples 54 | */ 55 | if (i2c_burst_read(drv_data->i2c, BMA421_I2C_ADDRESS, 56 | BMA421_REG_ACCEL_X_LSB, buf, 6) < 0) { 57 | LOG_DBG("Could not read accel axis data"); 58 | return -EIO; 59 | } 60 | 61 | lsb = (buf[0] & BMA421_ACCEL_LSB_MASK) >> BMA421_ACCEL_LSB_SHIFT; 62 | drv_data->x_sample = (((s8_t)buf[1]) << BMA421_ACCEL_LSB_BITS) | lsb; 63 | 64 | lsb = (buf[2] & BMA421_ACCEL_LSB_MASK) >> BMA421_ACCEL_LSB_SHIFT; 65 | drv_data->y_sample = (((s8_t)buf[3]) << BMA421_ACCEL_LSB_BITS) | lsb; 66 | 67 | lsb = (buf[4] & BMA421_ACCEL_LSB_MASK) >> BMA421_ACCEL_LSB_SHIFT; 68 | drv_data->z_sample = (((s8_t)buf[5]) << BMA421_ACCEL_LSB_BITS) | lsb; 69 | 70 | if (i2c_reg_read_byte(drv_data->i2c, BMA421_I2C_ADDRESS, 71 | BMA421_REG_TEMP, 72 | (u8_t *)&drv_data->temp_sample) < 0) { 73 | LOG_DBG("Could not read temperature data"); 74 | return -EIO; 75 | } 76 | return 0; 77 | } 78 | 79 | static void bma421_channel_accel_convert(struct sensor_value *val, 80 | s64_t raw_val) 81 | { 82 | /* 83 | * accel_val = (sample * BMA280_PMU_FULL_RAGE) / 84 | * (2^data_width * 10^6) 85 | */ 86 | raw_val = (raw_val * BMA421_ACC_FULL_RANGE) / 87 | (1 << (8 + BMA421_ACCEL_LSB_BITS)); 88 | val->val1 = raw_val / 1000000; 89 | val->val2 = raw_val % 1000000; 90 | 91 | /* normalize val to make sure val->val2 is positive */ 92 | if (val->val2 < 0) { 93 | val->val1 -= 1; 94 | val->val2 += 1000000; 95 | } 96 | } 97 | 98 | static void bma421_channel_value_add(struct sensor_value *val) 99 | { 100 | val->val1 = 32; //todo -- here values can be read from REG 0x1E step counter 101 | val->val2 = 88; 102 | } 103 | 104 | static int bma421_channel_get(struct device *dev, 105 | enum sensor_channel chan, 106 | struct sensor_value *val) 107 | { 108 | struct bma421_data *drv_data = dev->driver_data; 109 | 110 | /* 111 | * See datasheet "Sensor data" section for 112 | * more details on processing sample data. 113 | */ 114 | if (chan == SENSOR_CHAN_ACCEL_X) { 115 | bma421_channel_accel_convert(val, drv_data->x_sample); 116 | } else if (chan == SENSOR_CHAN_ACCEL_Y) { 117 | bma421_channel_accel_convert(val, drv_data->y_sample); 118 | } else if (chan == SENSOR_CHAN_ACCEL_Z) { 119 | bma421_channel_accel_convert(val, drv_data->z_sample); 120 | } else if (chan == SENSOR_CHAN_ACCEL_XYZ) { 121 | bma421_channel_accel_convert(val, drv_data->x_sample); 122 | bma421_channel_accel_convert(val + 1, drv_data->y_sample); 123 | bma421_channel_accel_convert(val + 2, drv_data->z_sample); 124 | bma421_channel_value_add(val + 3); //todo check how extra data can be passed 125 | } else if (chan == SENSOR_CHAN_DIE_TEMP) { 126 | /* temperature_val = 23 + sample / 2 */ 127 | val->val1 = (drv_data->temp_sample >> 1) + 23; 128 | val->val2 = 500000 * (drv_data->temp_sample & 1); 129 | return 0; 130 | } else { 131 | return -ENOTSUP; 132 | } 133 | 134 | return 0; 135 | } 136 | 137 | static const struct sensor_driver_api bma421_driver_api = { 138 | #if CONFIG_BMA421_TRIGGER 139 | .attr_set = bma421_attr_set, 140 | .trigger_set = bma421_trigger_set, 141 | #endif 142 | .sample_fetch = bma421_sample_fetch, 143 | .channel_get = bma421_channel_get, 144 | }; 145 | 146 | 147 | 148 | 149 | 150 | int bma421_init(struct device *dev) 151 | { 152 | struct bma421_data *drv_data = dev->driver_data; 153 | u8_t id = 0U; 154 | drv_data->i2c = device_get_binding(CONFIG_BMA421_I2C_MASTER_DEV_NAME); 155 | if (drv_data->i2c == NULL) { 156 | LOG_DBG("Could not get pointer to %s device", 157 | CONFIG_BMA421_I2C_MASTER_DEV_NAME); 158 | return -EINVAL; 159 | } 160 | 161 | /* read device ID */ 162 | if (i2c_reg_read_byte(drv_data->i2c, BMA421_I2C_ADDRESS, 163 | BMA421_REG_CHIP_ID, &id) < 0) { 164 | LOG_DBG("Could not read chip id"); 165 | return -EIO; 166 | } 167 | 168 | if (id != BMA421_CHIP_ID) { 169 | LOG_DBG("Unexpected chip id (%x)", id); 170 | return -EIO; 171 | } 172 | 173 | 174 | if (i2c_reg_update_byte(drv_data->i2c, BMA421_I2C_ADDRESS, BMA421_REG_CMD, BMA421_CMD_SOFT_RESET_MASK, BMA421_CMD_SOFT_RESET) < 0) { //soft reset 175 | // MY_REGISTER5=0x33; 176 | } 177 | //todo clean up debug registers 178 | 179 | i2c_delay(100); 180 | 181 | if (i2c_reg_update_byte(drv_data->i2c, BMA421_I2C_ADDRESS, BMA421_REG_ACC_CONF, 0x80, 0x80) < 0) { //acc performance 1 182 | // MY_REGISTER4=0x33; 183 | } 184 | 185 | //todo create names for hardcoded stuff, review masks - should cover multiple bits... 186 | i2c_delay(100); 187 | if (i2c_reg_update_byte(drv_data->i2c, BMA421_I2C_ADDRESS, BMA421_REG_PWR_CTRL , 0x04, 0x04) < 0) { //enable accelerometer 188 | // MY_REGISTER5=0x33; 189 | } 190 | i2c_delay(100); 191 | if (i2c_reg_update_byte(drv_data->i2c, BMA421_I2C_ADDRESS, BMA421_REG_PWR_CONF , 0x03, 0x00) < 0) { //disable powersave for testing (todo powersave) 192 | // MY_REGISTER5=0x33; 193 | } 194 | i2c_delay(100); 195 | if (i2c_reg_read_byte(drv_data->i2c, BMA421_I2C_ADDRESS, 0x40, &id) < 0) { 196 | //could not read 0x40 197 | // MY_REGISTER3=0xFF; 198 | } 199 | MY_REGISTER3=id; // read statement to check if update took place -- useless afterwards todo delete 200 | /* set g-range */ 201 | i2c_reg_read_byte(drv_data->i2c, BMA421_I2C_ADDRESS,BMA421_REG_ACC_RANGE, &id); 202 | id=id & 0xFC; // bit 1 and 0 of 0x41 are set to 0 203 | id=id | BMA421_ACC_RANGE; //this is set with a variable from Kconfig 204 | 205 | 206 | //todo use update_byte instead of write_byte 207 | if (i2c_reg_write_byte(drv_data->i2c, BMA421_I2C_ADDRESS, 208 | BMA421_REG_ACC_RANGE, id) < 0) { 209 | MY_REGISTER5=0xCC; 210 | LOG_DBG("Could not set data g-range"); 211 | return -EIO; 212 | } 213 | else 214 | MY_REGISTER5=id; //todo remove 215 | 216 | i2c_delay(100); 217 | i2c_reg_read_byte(drv_data->i2c, BMA421_I2C_ADDRESS,BMA421_REG_ACC_CONF, &id); 218 | id=id & 0xF0; //bit 3,2,1,0 are set to 0 219 | id=id | BMA421_ACC_ODR; 220 | if (i2c_reg_write_byte(drv_data->i2c, BMA421_I2C_ADDRESS, BMA421_REG_ACC_CONF, id) < 0) { 221 | MY_REGISTER6=0xCC; 222 | } 223 | else 224 | MY_REGISTER6=id; //todo remove 225 | 226 | #ifdef CONFIG_BMA421_TRIGGER 227 | if (bma421_init_interrupt(dev) < 0) { 228 | LOG_DBG("Could not initialize interrupts"); 229 | return -EIO; 230 | } 231 | #endif 232 | 233 | return 0; 234 | } 235 | 236 | struct bma421_data bma421_driver; 237 | 238 | DEVICE_AND_API_INIT(bma421, CONFIG_BMA421_NAME, bma421_init, &bma421_driver, 239 | NULL, POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY, 240 | &bma421_driver_api); 241 | -------------------------------------------------------------------------------- /app/drivers/sensor/bma421/bma421.h: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | #ifndef ZEPHYR_DRIVERS_SENSOR_BMA421_BMA421_H_ 8 | #define ZEPHYR_DRIVERS_SENSOR_BMA421_BMA421_H_ 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #define BMA421_I2C_ADDRESS CONFIG_BMA421_I2C_ADDR 16 | 17 | #define BMA421_REG_CHIP_ID 0x00 18 | #if CONFIG_BMA421_CHIP_BMA421 19 | #define BMA421_CHIP_ID 0x11 20 | #endif 21 | 22 | #define BMA421_REG_ACC_CONF 0x40 //set the output data rate, bandwidth and read mode 23 | #define BMA421_REG_ACC_RANGE 0x41 //set the output data rate, bandwidth and read mode 24 | #define BMA421_REG_PWR_CONF 0x7C //power mode configuration 25 | #define BMA421_REG_PWR_CTRL 0x7D //sensor enable 26 | #define BMA421_REG_CMD 0x7E //command register 27 | #define BMA421_REG_FEATURE 0x5E //feature register 28 | #define BMA421_REG_INT_LATCH 0x55 29 | #define BMA421_INT_MODE_LATCH 0x01 //permanent latched 30 | #define BMA421_REG_INT1_MAP 0x56 31 | #define BMA421_INT_MAP_MOTION 0x40 //bit 6 for any motion 32 | #define BMA421_REG_INT_STATUS_0 0x1C //interrupt feature status 33 | 34 | //#define BMA421_FEATURE_SIZE UINT8_C(64) 35 | #define BMA421_FEATURE_SIZE 64 36 | 37 | #define BMA421_CMD_SOFT_RESET 0xB6 38 | #define BMA421_CMD_SOFT_RESET_MASK 0xB6 39 | #define BMA421_ACC_PERF_MODE 0x80 //continues filter function 40 | 41 | //#define BMA421_REG_PMU_BW 0x10 42 | #if CONFIG_BMA421_ACC_ODR_1 43 | #define BMA421_ACC_ODR 0x01 44 | #elif CONFIG_BMA421_ACC_ODR_2 45 | #define BMA421_ACC_ODR 0x02 46 | #elif CONFIG_BMA421_ACC_ODR_3 47 | #define BMA421_ACC_ODR 0x03 48 | #elif CONFIG_BMA421_ACC_ODR_4 49 | #define BMA421_ACC_ODR 0x04 50 | #elif CONFIG_BMA421_ACC_ODR_5 51 | #define BMA421_ACC_ODR 0x05 52 | #elif CONFIG_BMA421_ACC_ODR_6 53 | #define BMA421_ACC_ODR 0x06 54 | #elif CONFIG_BMA421_ACC_ODR_7 55 | #define BMA421_ACC_ODR 0x07 56 | #elif CONFIG_BMA421_ACC_ODR_8 57 | #define BMA421_ACC_ODR 0x08 58 | #elif CONFIG_BMA421_ACC_ODR_9 59 | #define BMA421_ACC_ODR 0x09 60 | #elif CONFIG_BMA421_ACC_ODR_10 61 | #define BMA421_ACC_ODR 0x0a 62 | #elif CONFIG_BMA421_ACC_ODR_11 63 | #define BMA421_ACC_ODR 0x0b 64 | #elif CONFIG_BMA421_ACC_ODR_12 65 | #define BMA421_ACC_ODR 0x0c 66 | #elif CONFIG_BMA421_ACC_ODR_13 67 | #define BMA421_ACC_ODR 0x00 68 | #endif 69 | 70 | 71 | 72 | 73 | #if CONFIG_BMA421_ACC_RANGE_2G 74 | #define BMA421_ACC_RANGE 0x00 75 | #define BMA421_ACC_FULL_RANGE (4 * SENSOR_G) 76 | #elif CONFIG_BMA421_ACC_RANGE_4G 77 | #define BMA421_ACC_RANGE 0x01 78 | #define BMA421_ACC_FULL_RANGE (8 * SENSOR_G) 79 | #elif CONFIG_BMA421_ACC_RANGE_8G 80 | #define BMA421_ACC_RANGE 0x02 81 | #define BMA421_ACC_FULL_RANGE (16 * SENSOR_G) 82 | #elif CONFIG_BMA421_ACC_RANGE_16G 83 | #define BMA421_ACC_RANGE 0x03 84 | #define BMA421_ACC_FULL_RANGE (32 * SENSOR_G) 85 | #endif 86 | 87 | #define BMA421_REG_TEMP 0x08 88 | #define BMA421_REG_ACCEL_X_LSB 0x12 89 | #define BMA421_REG_ACCEL_Y_LSB 0x14 90 | #define BMA421_REG_ACCEL_Z_LSB 0x16 91 | 92 | #if CONFIG_BMA421_CHIP_BMA421 93 | #define BMA421_ACCEL_LSB_BITS 6 94 | #define BMA421_ACCEL_LSB_SHIFT 2 95 | #endif 96 | #define BMA421_ACCEL_LSB_MASK \ 97 | (BIT_MASK(BMA421_ACCEL_LSB_BITS) << BMA421_ACCEL_LSB_SHIFT) 98 | 99 | #define BMA421_REG_ACCEL_X_MSB 0x3 100 | #define BMA421_REG_ACCEL_Y_MSB 0x5 101 | #define BMA421_REG_ACCEL_Z_MSB 0x7 102 | 103 | #define BMA421_THREAD_PRIORITY 10 104 | #define BMA421_THREAD_STACKSIZE_UNIT 1024 105 | 106 | struct bma421_data { 107 | struct device *i2c; 108 | s16_t x_sample; 109 | s16_t y_sample; 110 | s16_t z_sample; 111 | s8_t temp_sample; 112 | 113 | #ifdef CONFIG_BMA421_TRIGGER 114 | struct device *gpio; 115 | struct gpio_callback gpio_cb; 116 | 117 | struct sensor_trigger data_ready_trigger; 118 | sensor_trigger_handler_t data_ready_handler; 119 | 120 | struct sensor_trigger any_motion_trigger; 121 | sensor_trigger_handler_t any_motion_handler; 122 | 123 | #if defined(CONFIG_BMA421_TRIGGER_OWN_THREAD) 124 | K_THREAD_STACK_MEMBER(thread_stack, CONFIG_BMA421_THREAD_STACK_SIZE); 125 | struct k_thread thread; 126 | struct k_sem gpio_sem; 127 | #elif defined(CONFIG_BMA421_TRIGGER_GLOBAL_THREAD) 128 | struct k_work work; 129 | struct device *dev; 130 | #endif 131 | 132 | #endif /* CONFIG_BMA421_TRIGGER */ 133 | }; 134 | 135 | #ifdef CONFIG_BMA421_TRIGGER 136 | int bma421_trigger_set(struct device *dev, 137 | const struct sensor_trigger *trig, 138 | sensor_trigger_handler_t handler); 139 | 140 | int bma421_attr_set(struct device *dev, 141 | enum sensor_channel chan, 142 | enum sensor_attribute attr, 143 | const struct sensor_value *val); 144 | 145 | int bma421_init_interrupt(struct device *dev); 146 | #endif 147 | 148 | #endif /* ZEPHYR_DRIVERS_SENSOR_BMA421_BMA421_H_ */ 149 | -------------------------------------------------------------------------------- /app/drivers/sensor/bma421/bma421_trigger.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Intel Corporation 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "bma421.h" 14 | 15 | #include 16 | LOG_MODULE_DECLARE(BMA421, CONFIG_SENSOR_LOG_LEVEL); 17 | 18 | int bma421_attr_set(struct device *dev, 19 | enum sensor_channel chan, 20 | enum sensor_attribute attr, 21 | const struct sensor_value *val) 22 | { 23 | struct bma421_data *drv_data = dev->driver_data; 24 | u64_t slope_th; 25 | u8_t buf[BMA421_FEATURE_SIZE]; 26 | //default anymotion is selected 27 | //todo set any parameter eg stepcounter, tap double tap, wrist tilt etc 28 | // if (i2c_burst_read(drv_data->i2c, BMA421_I2C_ADDRESS, BMA421_REG_FEATURE, buf, BMA421_FEATURE_SIZE) < 0) {} 29 | 30 | if (chan != SENSOR_CHAN_ACCEL_XYZ) { 31 | return -ENOTSUP; 32 | } 33 | //0x5E features register burst read 34 | /* if (attr == SENSOR_ATTR_SLOPE_TH) { 35 | } 36 | } else if (attr == SENSOR_ATTR_SLOPE_DUR) { 37 | } 38 | } else { 39 | return -ENOTSUP; 40 | } 41 | */ 42 | return 0; 43 | } 44 | 45 | static void bma421_gpio_callback(struct device *dev, 46 | struct gpio_callback *cb, u32_t pins) 47 | { 48 | struct bma421_data *drv_data = 49 | CONTAINER_OF(cb, struct bma421_data, gpio_cb); 50 | 51 | ARG_UNUSED(pins); 52 | 53 | gpio_pin_disable_callback(dev, CONFIG_BMA421_GPIO_PIN_NUM); 54 | 55 | #if defined(CONFIG_BMA421_TRIGGER_OWN_THREAD) 56 | k_sem_give(&drv_data->gpio_sem); 57 | #elif defined(CONFIG_BMA421_TRIGGER_GLOBAL_THREAD) 58 | k_work_submit(&drv_data->work); 59 | #endif 60 | } 61 | 62 | static void bma421_thread_cb(void *arg) 63 | { 64 | struct device *dev = arg; 65 | struct bma421_data *drv_data = dev->driver_data; 66 | u8_t status = 0U; 67 | int err = 0; 68 | 69 | /* check for data ready */ 70 | /* err = i2c_reg_read_byte(drv_data->i2c, BMA421_I2C_ADDRESS, 71 | BMA421_REG_INT_STATUS_1, &status); 72 | if (status & BMA421_BIT_DATA_INT_STATUS && 73 | drv_data->data_ready_handler != NULL && 74 | err == 0) { 75 | drv_data->data_ready_handler(dev, 76 | &drv_data->data_ready_trigger); 77 | } 78 | */ 79 | /* check for any motion */ 80 | err = i2c_reg_read_byte(drv_data->i2c, BMA421_I2C_ADDRESS, 81 | BMA421_REG_INT_STATUS_0, &status); 82 | if (status && 83 | drv_data->any_motion_handler != NULL && 84 | err == 0) { 85 | drv_data->any_motion_handler(dev, 86 | &drv_data->data_ready_trigger); 87 | 88 | /* clear latched interrupt -- according this is already done by reading the register*/ 89 | /* err = i2c_reg_update_byte(drv_data->i2c, BMA421_I2C_ADDRESS, 90 | BMA421_REG_INT_RST_LATCH, 91 | BMA421_BIT_INT_LATCH_RESET, 92 | BMA421_BIT_INT_LATCH_RESET); 93 | 94 | if (err < 0) { 95 | LOG_DBG("Could not update clear the interrupt"); 96 | return; 97 | }*/ 98 | } 99 | 100 | gpio_pin_enable_callback(drv_data->gpio, CONFIG_BMA421_GPIO_PIN_NUM); 101 | } 102 | 103 | #ifdef CONFIG_BMA421_TRIGGER_OWN_THREAD 104 | static void bma421_thread(int dev_ptr, int unused) 105 | { 106 | struct device *dev = INT_TO_POINTER(dev_ptr); 107 | struct bma421_data *drv_data = dev->driver_data; 108 | 109 | ARG_UNUSED(unused); 110 | 111 | while (1) { 112 | k_sem_take(&drv_data->gpio_sem, K_FOREVER); 113 | bma421_thread_cb(dev); 114 | } 115 | } 116 | #endif 117 | 118 | #ifdef CONFIG_BMA421_TRIGGER_GLOBAL_THREAD 119 | static void bma421_work_cb(struct k_work *work) 120 | { 121 | struct bma421_data *drv_data = 122 | CONTAINER_OF(work, struct bma421_data, work); 123 | 124 | bma421_thread_cb(drv_data->dev); 125 | } 126 | #endif 127 | 128 | int bma421_trigger_set(struct device *dev, 129 | const struct sensor_trigger *trig, 130 | sensor_trigger_handler_t handler) 131 | { 132 | struct bma421_data *drv_data = dev->driver_data; 133 | 134 | if (trig->type == SENSOR_TRIG_DATA_READY) { 135 | /* disable data ready interrupt while changing trigger params */ 136 | /* if (i2c_reg_update_byte(drv_data->i2c, BMA421_I2C_ADDRESS, 137 | BMA421_REG_INT_EN_1, 138 | BMA421_BIT_DATA_EN, 0) < 0) { 139 | LOG_DBG("Could not disable data ready interrupt"); 140 | return -EIO; 141 | } 142 | 143 | drv_data->data_ready_handler = handler; 144 | if (handler == NULL) { 145 | return 0; 146 | } 147 | drv_data->data_ready_trigger = *trig; 148 | */ 149 | /* enable data ready interrupt */ 150 | /* if (i2c_reg_update_byte(drv_data->i2c, BMA421_I2C_ADDRESS, 151 | BMA421_REG_INT_EN_1, 152 | BMA421_BIT_DATA_EN, 153 | BMA421_BIT_DATA_EN) < 0) { 154 | LOG_DBG("Could not enable data ready interrupt"); 155 | return -EIO; 156 | }*/ 157 | } else if (trig->type == SENSOR_TRIG_DELTA) { 158 | /* disable any-motion interrupt while changing trigger params */ 159 | if (i2c_reg_update_byte(drv_data->i2c, BMA421_I2C_ADDRESS, 160 | BMA421_REG_INT1_MAP, 161 | BMA421_INT_MAP_MOTION, 0) < 0) { 162 | LOG_DBG("Could not disable data ready interrupt"); 163 | return -EIO; 164 | } 165 | 166 | drv_data->any_motion_handler = handler; 167 | if (handler == NULL) { 168 | return 0; 169 | } 170 | drv_data->any_motion_trigger = *trig; 171 | 172 | /* enable any-motion interrupt */ 173 | if (i2c_reg_update_byte(drv_data->i2c, BMA421_I2C_ADDRESS, 174 | BMA421_REG_INT1_MAP, 175 | BMA421_INT_MAP_MOTION, 176 | BMA421_INT_MAP_MOTION) < 0) { 177 | LOG_DBG("Could not enable data ready interrupt"); 178 | return -EIO; 179 | } 180 | } else { 181 | return -ENOTSUP; 182 | } 183 | 184 | return 0; 185 | } 186 | 187 | int bma421_init_interrupt(struct device *dev) 188 | { 189 | struct bma421_data *drv_data = dev->driver_data; 190 | 191 | /* set latched interrupts */ 192 | if (i2c_reg_write_byte(drv_data->i2c, BMA421_I2C_ADDRESS, 193 | BMA421_REG_INT_LATCH, 194 | BMA421_INT_MODE_LATCH) < 0) { 195 | LOG_DBG("Could not set latched interrupts"); 196 | return -EIO; 197 | } 198 | 199 | /* setup data ready gpio interrupt */ 200 | /* drv_data->gpio = device_get_binding(CONFIG_BMA421_GPIO_DEV_NAME); 201 | if (drv_data->gpio == NULL) { 202 | LOG_DBG("Cannot get pointer to %s device", 203 | CONFIG_BMA421_GPIO_DEV_NAME); 204 | return -EINVAL; 205 | } 206 | 207 | gpio_pin_configure(drv_data->gpio, CONFIG_BMA421_GPIO_PIN_NUM, 208 | GPIO_DIR_IN | GPIO_INT | GPIO_INT_LEVEL | 209 | GPIO_INT_ACTIVE_HIGH | GPIO_INT_DEBOUNCE); 210 | 211 | gpio_init_callback(&drv_data->gpio_cb, 212 | bma421_gpio_callback, 213 | BIT(CONFIG_BMA421_GPIO_PIN_NUM)); 214 | 215 | if (gpio_add_callback(drv_data->gpio, &drv_data->gpio_cb) < 0) { 216 | LOG_DBG("Could not set gpio callback"); 217 | return -EIO; 218 | } 219 | */ 220 | /* map data ready interrupt to INT1 */ 221 | /* if (i2c_reg_update_byte(drv_data->i2c, BMA421_I2C_ADDRESS, 222 | BMA421_REG_INT_MAP_1, 223 | BMA421_INT_MAP_1_BIT_DATA, 224 | BMA421_INT_MAP_1_BIT_DATA) < 0) { 225 | LOG_DBG("Could not map data ready interrupt pin"); 226 | return -EIO; 227 | } 228 | */ 229 | /* map any-motion interrupt to INT1 */ 230 | if (i2c_reg_update_byte(drv_data->i2c, BMA421_I2C_ADDRESS, 231 | BMA421_REG_INT1_MAP, 232 | BMA421_INT_MAP_MOTION, 233 | BMA421_INT_MAP_MOTION) < 0) { 234 | LOG_DBG("Could not map any-motion interrupt pin"); 235 | return -EIO; 236 | } 237 | 238 | /* if (i2c_reg_update_byte(drv_data->i2c, BMA421_I2C_ADDRESS, 239 | BMA421_REG_INT1_MAP, 240 | BMA421_BIT_DATA_EN, 0) < 0) { 241 | LOG_DBG("Could not disable data ready interrupt"); 242 | return -EIO; 243 | } 244 | */ 245 | /* disable any-motion interrupt */ 246 | /* if (i2c_reg_update_byte(drv_data->i2c, BMA421_I2C_ADDRESS, 247 | BMA421_REG_INT_EN_0, 248 | BMA421_SLOPE_EN_XYZ, 0) < 0) { 249 | LOG_DBG("Could not disable data ready interrupt"); 250 | return -EIO; 251 | } 252 | */ 253 | #if defined(CONFIG_BMA421_TRIGGER_OWN_THREAD) 254 | k_sem_init(&drv_data->gpio_sem, 0, UINT_MAX); 255 | 256 | k_thread_create(&drv_data->thread, drv_data->thread_stack, 257 | CONFIG_BMA421_THREAD_STACK_SIZE, 258 | (k_thread_entry_t)bma421_thread, dev, 259 | 0, NULL, K_PRIO_COOP(CONFIG_BMA421_THREAD_PRIORITY), 260 | 0, K_NO_WAIT); 261 | #elif defined(CONFIG_BMA421_TRIGGER_GLOBAL_THREAD) 262 | drv_data->work.handler = bma421_work_cb; 263 | drv_data->dev = dev; 264 | #endif 265 | 266 | gpio_pin_enable_callback(drv_data->gpio, CONFIG_BMA421_GPIO_PIN_NUM); 267 | 268 | return 0; 269 | } 270 | -------------------------------------------------------------------------------- /app/drivers/sensor/bma421/bma421_trigger.c-todo: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Intel Corporation 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "bma421.h" 14 | 15 | #include 16 | LOG_MODULE_DECLARE(BMA280, CONFIG_SENSOR_LOG_LEVEL); 17 | 18 | int bma280_attr_set(struct device *dev, 19 | enum sensor_channel chan, 20 | enum sensor_attribute attr, 21 | const struct sensor_value *val) 22 | { 23 | struct bma280_data *drv_data = dev->driver_data; 24 | u64_t slope_th; 25 | 26 | if (chan != SENSOR_CHAN_ACCEL_XYZ) { 27 | return -ENOTSUP; 28 | } 29 | 30 | if (attr == SENSOR_ATTR_SLOPE_TH) { 31 | /* slope_th = (val * 10^6 * 2^10) / BMA280_PMU_FULL_RAGE */ 32 | slope_th = (u64_t)val->val1 * 1000000U + (u64_t)val->val2; 33 | slope_th = (slope_th * (1 << 10)) / BMA280_PMU_FULL_RANGE; 34 | if (i2c_reg_write_byte(drv_data->i2c, BMA280_I2C_ADDRESS, 35 | BMA280_REG_SLOPE_TH, (u8_t)slope_th) 36 | < 0) { 37 | LOG_DBG("Could not set slope threshold"); 38 | return -EIO; 39 | } 40 | } else if (attr == SENSOR_ATTR_SLOPE_DUR) { 41 | if (i2c_reg_update_byte(drv_data->i2c, BMA280_I2C_ADDRESS, 42 | BMA280_REG_INT_5, 43 | BMA280_SLOPE_DUR_MASK, 44 | val->val1 << BMA280_SLOPE_DUR_SHIFT) 45 | < 0) { 46 | LOG_DBG("Could not set slope duration"); 47 | return -EIO; 48 | } 49 | } else { 50 | return -ENOTSUP; 51 | } 52 | 53 | return 0; 54 | } 55 | 56 | static void bma280_gpio_callback(struct device *dev, 57 | struct gpio_callback *cb, u32_t pins) 58 | { 59 | struct bma280_data *drv_data = 60 | CONTAINER_OF(cb, struct bma280_data, gpio_cb); 61 | 62 | ARG_UNUSED(pins); 63 | 64 | gpio_pin_disable_callback(dev, CONFIG_BMA280_GPIO_PIN_NUM); 65 | 66 | #if defined(CONFIG_BMA280_TRIGGER_OWN_THREAD) 67 | k_sem_give(&drv_data->gpio_sem); 68 | #elif defined(CONFIG_BMA280_TRIGGER_GLOBAL_THREAD) 69 | k_work_submit(&drv_data->work); 70 | #endif 71 | } 72 | 73 | static void bma280_thread_cb(void *arg) 74 | { 75 | struct device *dev = arg; 76 | struct bma280_data *drv_data = dev->driver_data; 77 | u8_t status = 0U; 78 | int err = 0; 79 | 80 | /* check for data ready */ 81 | err = i2c_reg_read_byte(drv_data->i2c, BMA280_I2C_ADDRESS, 82 | BMA280_REG_INT_STATUS_1, &status); 83 | if (status & BMA280_BIT_DATA_INT_STATUS && 84 | drv_data->data_ready_handler != NULL && 85 | err == 0) { 86 | drv_data->data_ready_handler(dev, 87 | &drv_data->data_ready_trigger); 88 | } 89 | 90 | /* check for any motion */ 91 | err = i2c_reg_read_byte(drv_data->i2c, BMA280_I2C_ADDRESS, 92 | BMA280_REG_INT_STATUS_0, &status); 93 | if (status & BMA280_BIT_SLOPE_INT_STATUS && 94 | drv_data->any_motion_handler != NULL && 95 | err == 0) { 96 | drv_data->any_motion_handler(dev, 97 | &drv_data->data_ready_trigger); 98 | 99 | /* clear latched interrupt */ 100 | err = i2c_reg_update_byte(drv_data->i2c, BMA280_I2C_ADDRESS, 101 | BMA280_REG_INT_RST_LATCH, 102 | BMA280_BIT_INT_LATCH_RESET, 103 | BMA280_BIT_INT_LATCH_RESET); 104 | 105 | if (err < 0) { 106 | LOG_DBG("Could not update clear the interrupt"); 107 | return; 108 | } 109 | } 110 | 111 | gpio_pin_enable_callback(drv_data->gpio, CONFIG_BMA280_GPIO_PIN_NUM); 112 | } 113 | 114 | #ifdef CONFIG_BMA280_TRIGGER_OWN_THREAD 115 | static void bma280_thread(int dev_ptr, int unused) 116 | { 117 | struct device *dev = INT_TO_POINTER(dev_ptr); 118 | struct bma280_data *drv_data = dev->driver_data; 119 | 120 | ARG_UNUSED(unused); 121 | 122 | while (1) { 123 | k_sem_take(&drv_data->gpio_sem, K_FOREVER); 124 | bma280_thread_cb(dev); 125 | } 126 | } 127 | #endif 128 | 129 | #ifdef CONFIG_BMA280_TRIGGER_GLOBAL_THREAD 130 | static void bma280_work_cb(struct k_work *work) 131 | { 132 | struct bma280_data *drv_data = 133 | CONTAINER_OF(work, struct bma280_data, work); 134 | 135 | bma280_thread_cb(drv_data->dev); 136 | } 137 | #endif 138 | 139 | int bma280_trigger_set(struct device *dev, 140 | const struct sensor_trigger *trig, 141 | sensor_trigger_handler_t handler) 142 | { 143 | struct bma280_data *drv_data = dev->driver_data; 144 | 145 | if (trig->type == SENSOR_TRIG_DATA_READY) { 146 | /* disable data ready interrupt while changing trigger params */ 147 | if (i2c_reg_update_byte(drv_data->i2c, BMA280_I2C_ADDRESS, 148 | BMA280_REG_INT_EN_1, 149 | BMA280_BIT_DATA_EN, 0) < 0) { 150 | LOG_DBG("Could not disable data ready interrupt"); 151 | return -EIO; 152 | } 153 | 154 | drv_data->data_ready_handler = handler; 155 | if (handler == NULL) { 156 | return 0; 157 | } 158 | drv_data->data_ready_trigger = *trig; 159 | 160 | /* enable data ready interrupt */ 161 | if (i2c_reg_update_byte(drv_data->i2c, BMA280_I2C_ADDRESS, 162 | BMA280_REG_INT_EN_1, 163 | BMA280_BIT_DATA_EN, 164 | BMA280_BIT_DATA_EN) < 0) { 165 | LOG_DBG("Could not enable data ready interrupt"); 166 | return -EIO; 167 | } 168 | } else if (trig->type == SENSOR_TRIG_DELTA) { 169 | /* disable any-motion interrupt while changing trigger params */ 170 | if (i2c_reg_update_byte(drv_data->i2c, BMA280_I2C_ADDRESS, 171 | BMA280_REG_INT_EN_0, 172 | BMA280_SLOPE_EN_XYZ, 0) < 0) { 173 | LOG_DBG("Could not disable data ready interrupt"); 174 | return -EIO; 175 | } 176 | 177 | drv_data->any_motion_handler = handler; 178 | if (handler == NULL) { 179 | return 0; 180 | } 181 | drv_data->any_motion_trigger = *trig; 182 | 183 | /* enable any-motion interrupt */ 184 | if (i2c_reg_update_byte(drv_data->i2c, BMA280_I2C_ADDRESS, 185 | BMA280_REG_INT_EN_0, 186 | BMA280_SLOPE_EN_XYZ, 187 | BMA280_SLOPE_EN_XYZ) < 0) { 188 | LOG_DBG("Could not enable data ready interrupt"); 189 | return -EIO; 190 | } 191 | } else { 192 | return -ENOTSUP; 193 | } 194 | 195 | return 0; 196 | } 197 | 198 | int bma280_init_interrupt(struct device *dev) 199 | { 200 | struct bma280_data *drv_data = dev->driver_data; 201 | 202 | /* set latched interrupts */ 203 | if (i2c_reg_write_byte(drv_data->i2c, BMA280_I2C_ADDRESS, 204 | BMA280_REG_INT_RST_LATCH, 205 | BMA280_BIT_INT_LATCH_RESET | 206 | BMA280_INT_MODE_LATCH) < 0) { 207 | LOG_DBG("Could not set latched interrupts"); 208 | return -EIO; 209 | } 210 | 211 | /* setup data ready gpio interrupt */ 212 | drv_data->gpio = device_get_binding(CONFIG_BMA280_GPIO_DEV_NAME); 213 | if (drv_data->gpio == NULL) { 214 | LOG_DBG("Cannot get pointer to %s device", 215 | CONFIG_BMA280_GPIO_DEV_NAME); 216 | return -EINVAL; 217 | } 218 | 219 | gpio_pin_configure(drv_data->gpio, CONFIG_BMA280_GPIO_PIN_NUM, 220 | GPIO_DIR_IN | GPIO_INT | GPIO_INT_LEVEL | 221 | GPIO_INT_ACTIVE_HIGH | GPIO_INT_DEBOUNCE); 222 | 223 | gpio_init_callback(&drv_data->gpio_cb, 224 | bma280_gpio_callback, 225 | BIT(CONFIG_BMA280_GPIO_PIN_NUM)); 226 | 227 | if (gpio_add_callback(drv_data->gpio, &drv_data->gpio_cb) < 0) { 228 | LOG_DBG("Could not set gpio callback"); 229 | return -EIO; 230 | } 231 | 232 | /* map data ready interrupt to INT1 */ 233 | if (i2c_reg_update_byte(drv_data->i2c, BMA280_I2C_ADDRESS, 234 | BMA280_REG_INT_MAP_1, 235 | BMA280_INT_MAP_1_BIT_DATA, 236 | BMA280_INT_MAP_1_BIT_DATA) < 0) { 237 | LOG_DBG("Could not map data ready interrupt pin"); 238 | return -EIO; 239 | } 240 | 241 | /* map any-motion interrupt to INT1 */ 242 | if (i2c_reg_update_byte(drv_data->i2c, BMA280_I2C_ADDRESS, 243 | BMA280_REG_INT_MAP_0, 244 | BMA280_INT_MAP_0_BIT_SLOPE, 245 | BMA280_INT_MAP_0_BIT_SLOPE) < 0) { 246 | LOG_DBG("Could not map any-motion interrupt pin"); 247 | return -EIO; 248 | } 249 | 250 | if (i2c_reg_update_byte(drv_data->i2c, BMA280_I2C_ADDRESS, 251 | BMA280_REG_INT_EN_1, 252 | BMA280_BIT_DATA_EN, 0) < 0) { 253 | LOG_DBG("Could not disable data ready interrupt"); 254 | return -EIO; 255 | } 256 | 257 | /* disable any-motion interrupt */ 258 | if (i2c_reg_update_byte(drv_data->i2c, BMA280_I2C_ADDRESS, 259 | BMA280_REG_INT_EN_0, 260 | BMA280_SLOPE_EN_XYZ, 0) < 0) { 261 | LOG_DBG("Could not disable data ready interrupt"); 262 | return -EIO; 263 | } 264 | 265 | #if defined(CONFIG_BMA280_TRIGGER_OWN_THREAD) 266 | k_sem_init(&drv_data->gpio_sem, 0, UINT_MAX); 267 | 268 | k_thread_create(&drv_data->thread, drv_data->thread_stack, 269 | CONFIG_BMA280_THREAD_STACK_SIZE, 270 | (k_thread_entry_t)bma280_thread, dev, 271 | 0, NULL, K_PRIO_COOP(CONFIG_BMA280_THREAD_PRIORITY), 272 | 0, K_NO_WAIT); 273 | #elif defined(CONFIG_BMA280_TRIGGER_GLOBAL_THREAD) 274 | drv_data->work.handler = bma280_work_cb; 275 | drv_data->dev = dev; 276 | #endif 277 | 278 | gpio_pin_enable_callback(drv_data->gpio, CONFIG_BMA280_GPIO_PIN_NUM); 279 | 280 | return 0; 281 | } 282 | -------------------------------------------------------------------------------- /app/drivers/sensor/cst816s/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: Apache-2.0 2 | 3 | zephyr_library() 4 | 5 | zephyr_library_sources_ifdef(CONFIG_CST816S cst816s.c) 6 | zephyr_library_sources_ifdef(CONFIG_CST816S_TRIGGER cst816s_trigger.c) 7 | -------------------------------------------------------------------------------- /app/drivers/sensor/cst816s/Kconfig: -------------------------------------------------------------------------------- 1 | # CST816S Hynitron touchscreen 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | #menuconfig CST816S 5 | # bool "hynitron cst816 touchscreen " 6 | # depends on I2C 7 | # help 8 | # Enable driver for hynitron touchscreen. 9 | menuconfig CST816S 10 | bool "CST816S Touchscreen" 11 | depends on I2C 12 | help 13 | Enable driver for hynitron touchscreen. 14 | 15 | if CST816S 16 | 17 | choice 18 | prompt "Trigger mode" 19 | default CST816S_TRIGGER_NONE 20 | help 21 | Specify the type of triggering used by the driver. 22 | 23 | config CST816S_TRIGGER_NONE 24 | bool "No trigger" 25 | 26 | config CST816S_TRIGGER_GLOBAL_THREAD 27 | bool "Use global thread" 28 | depends on GPIO 29 | select CST816S_TRIGGER 30 | 31 | config CST816S_TRIGGER_OWN_THREAD 32 | bool "Use own thread" 33 | depends on GPIO 34 | select CST816S_TRIGGER 35 | 36 | endchoice 37 | 38 | config CST816S_TRIGGER 39 | bool 40 | 41 | config CST816S_THREAD_PRIORITY 42 | int "Thread priority" 43 | depends on CST816S_TRIGGER_OWN_THREAD && CST816S_TRIGGER 44 | default 10 45 | help 46 | Priority of thread used by the driver to handle interrupts. 47 | 48 | config CST816S_THREAD_STACK_SIZE 49 | int "Thread stack size" 50 | depends on CST816S_TRIGGER_OWN_THREAD && CST816S_TRIGGER 51 | default 512 52 | help 53 | Stack size of thread used by the driver to handle interrupts. 54 | 55 | endif # CST816S 56 | -------------------------------------------------------------------------------- /app/drivers/sensor/cst816s/cst816s.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Intel Corporation 3 | * Copyright (c) 2020 Stephane Dorre 4 | * 5 | * SPDX-License-Identifier: Apache-2.0 6 | */ 7 | 8 | #define DT_DRV_COMPAT hynitron_cst816s 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include "cst816s.h" 17 | 18 | #define RESET_PIN DT_INST_GPIO_PIN(0, reset_gpios) 19 | 20 | LOG_MODULE_REGISTER(CST816S, CONFIG_SENSOR_LOG_LEVEL); 21 | 22 | static int cst816s_sample_fetch(const struct device *dev, enum sensor_channel chan) 23 | { 24 | struct cst816s_data *drv_data = dev->data; 25 | uint8_t buf[9]; 26 | uint8_t msb; 27 | uint8_t lsb; 28 | __ASSERT_NO_MSG(chan == SENSOR_CHAN_ALL); 29 | 30 | /* 31 | * since all accel data register addresses are consecutive, 32 | * a burst read can be used to read all the samples 33 | */ 34 | if (i2c_burst_read(drv_data->i2c, CST816S_I2C_ADDRESS, 35 | CST816S_REG_DATA, buf, 9) < 0) { 36 | LOG_DBG("Could not read data"); 37 | return -EIO; 38 | } 39 | 40 | switch (buf[CST816S_REG_GESTURE_ID]) { 41 | case CST816S_GESTURE_NONE: 42 | drv_data->gesture = NONE; 43 | break; 44 | case CST816S_GESTURE_SLIDE_UP: 45 | drv_data->gesture = SLIDE_UP; 46 | break; 47 | case CST816S_GESTURE_SLIDE_DOWN: 48 | drv_data->gesture = SLIDE_DOWN; 49 | break; 50 | case CST816S_GESTURE_SLIDE_LEFT: 51 | drv_data->gesture = SLIDE_LEFT; 52 | break; 53 | case CST816S_GESTURE_SLIDE_RIGHT: 54 | drv_data->gesture = SLIDE_RIGHT; 55 | break; 56 | case CST816S_GESTURE_CLICK: 57 | drv_data->gesture = CLICK; 58 | break; 59 | case CST816S_GESTURE_DOUBLE_CLICK: 60 | drv_data->gesture = DOUBLE_CLICK; 61 | break; 62 | case CST816S_GESTURE_LONG_PRESS: 63 | drv_data->gesture = LONG_PRESS; 64 | break; 65 | } 66 | 67 | drv_data->number_touch_point = buf[CST816S_REG_FINGER_NUM]; 68 | 69 | msb = buf[CST816S_REG_XPOS_H] & 0x0f; 70 | lsb = buf[CST816S_REG_XPOS_L]; 71 | drv_data->touch_point_1.x = (msb<<8)|lsb; 72 | 73 | msb = buf[CST816S_REG_YPOS_H] & 0x0f; 74 | lsb = buf[CST816S_REG_YPOS_L]; 75 | drv_data->touch_point_1.y = (msb<<8)|lsb; 76 | 77 | drv_data->action = (enum cst816s_action)(buf[CST816S_REG_XPOS_H] >> 6); 78 | return 0; 79 | } 80 | 81 | static int cst816s_channel_get(const struct device *dev, 82 | enum sensor_channel chan, 83 | struct sensor_value *val) 84 | { 85 | struct cst816s_data *drv_data = dev->data; 86 | 87 | if ((uint16_t)chan == CST816S_CHAN_GESTURE) { 88 | val->val1=drv_data->gesture; 89 | } else if ((uint16_t)chan == CST816S_CHAN_TOUCH_POINT_1) { 90 | val->val1=drv_data->touch_point_1.x; 91 | val->val2=drv_data->touch_point_1.y; 92 | } else if ((uint16_t)chan == CST816S_CHAN_TOUCH_POINT_2) { 93 | val->val1=drv_data->touch_point_2.x; 94 | val->val2=drv_data->touch_point_2.y; 95 | } else { 96 | return -ENOTSUP; 97 | } 98 | 99 | return 0; 100 | } 101 | 102 | static const struct sensor_driver_api cst816s_driver_api = { 103 | #if CONFIG_CST816S_TRIGGER 104 | .attr_set = cst816s_attr_set, 105 | .trigger_set = cst816s_trigger_set, 106 | #endif 107 | .sample_fetch = cst816s_sample_fetch, 108 | .channel_get = cst816s_channel_get, 109 | }; 110 | 111 | static void cst816s_chip_reset(const struct device* dev) 112 | { 113 | struct cst816s_data *drv_data = dev->data; 114 | 115 | gpio_pin_set_raw(drv_data->reset_gpio, RESET_PIN, 0); 116 | k_msleep(5); 117 | gpio_pin_set_raw(drv_data->reset_gpio, RESET_PIN, 1); 118 | k_msleep(50); 119 | } 120 | 121 | static int cst816s_chip_init(const struct device *dev) 122 | { 123 | struct cst816s_data *drv_data = dev->data; 124 | 125 | cst816s_chip_reset(dev); 126 | 127 | if (i2c_reg_read_byte(drv_data->i2c, CST816S_I2C_ADDRESS, 128 | CST816S_REG_CHIP_ID, &drv_data->chip_id) < 0) { 129 | LOG_ERR("failed reading chip id"); 130 | return -EIO; 131 | } 132 | 133 | if (drv_data->chip_id != CST816S_CHIP_ID) { 134 | LOG_ERR("CST816S wrong chip id: returned 0x%x", drv_data->chip_id); 135 | } 136 | 137 | if (i2c_reg_update_byte(drv_data->i2c, CST816S_I2C_ADDRESS, 138 | CST816S_REG_MOTION_MASK, 139 | (CST816S_MOTION_EN_DCLICK), (CST816S_MOTION_EN_DCLICK)) < 0) { 140 | LOG_ERR("Could not enable double click"); 141 | return -EIO; 142 | } 143 | 144 | return 0; 145 | } 146 | 147 | int cst816s_init(const struct device *dev) 148 | { 149 | struct cst816s_data *drv_data = dev->data; 150 | 151 | drv_data->i2c = device_get_binding(DT_INST_BUS_LABEL(0)); 152 | if (drv_data->i2c == NULL) { 153 | LOG_DBG("Could not get pointer to %s device", 154 | DT_INST_BUS_LABEL(0)); 155 | return -EINVAL; 156 | } 157 | 158 | /* setup reset gpio */ 159 | drv_data->reset_gpio = device_get_binding( 160 | DT_INST_GPIO_LABEL(0, reset_gpios)); 161 | if (drv_data->reset_gpio == NULL) { 162 | LOG_DBG("Cannot get pointer to %s device", 163 | DT_INST_GPIO_LABEL(0, reset_gpios)); 164 | return -EINVAL; 165 | } 166 | 167 | /* Configure pin as output and initialize it to high. */ 168 | LOG_INF("configure OUTPUT ACTIVE"); 169 | gpio_pin_configure(drv_data->reset_gpio, RESET_PIN, 170 | GPIO_OUTPUT 171 | | DT_INST_GPIO_FLAGS(0, reset_gpios)); 172 | 173 | cst816s_chip_init(dev); 174 | 175 | #ifdef CONFIG_CST816S_TRIGGER 176 | if (cst816s_init_interrupt(dev) < 0) { 177 | LOG_DBG("Could not initialize interrupts"); 178 | return -EIO; 179 | } 180 | #endif 181 | return 0; 182 | } 183 | 184 | struct cst816s_data cst816s_driver; 185 | 186 | DEVICE_AND_API_INIT(cst816s, DT_INST_LABEL(0), cst816s_init, &cst816s_driver, 187 | NULL, POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY, 188 | &cst816s_driver_api); 189 | -------------------------------------------------------------------------------- /app/drivers/sensor/cst816s/cst816s.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Intel Corporation 3 | * Copyright (c) 2020 Stephane Dorre 4 | * 5 | * SPDX-License-Identifier: Apache-2.0 6 | */ 7 | 8 | #ifndef ZEPHYR_DRIVERS_SENSOR_CST816S_CST816S_H_ 9 | #define ZEPHYR_DRIVERS_SENSOR_CST816S_CST816S_H_ 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #define CST816S_I2C_ADDRESS 0x15 18 | 19 | #define CST816S_CHIP_ID 0xB4 20 | 21 | /*** 22 | * TODO 23 | * - CST816S_REG_YPOS_H : Bits 4-7 => fingerID 24 | * - Reg addr 0x07 : Pressure ? 25 | * - Reg addr 0x08 : Bit 4-7: Area ? 26 | */ 27 | 28 | /* See Register documentation here: 29 | * https://github.com/endian-albin/pinetime-hypnos/wiki/ 30 | * Technical-documentation#cst816s-touch-controller 31 | */ 32 | 33 | #define CST816S_REG_DATA 0x00 34 | #define CST816S_REG_GESTURE_ID 0x01 35 | #define CST816S_REG_FINGER_NUM 0x02 36 | #define CST816S_REG_XPOS_H 0x03 37 | #define CST816S_REG_XPOS_L 0x04 38 | #define CST816S_REG_YPOS_H 0x05 39 | #define CST816S_REG_YPOS_L 0x06 40 | #define CST816S_REG_BPC0H 0xB0 41 | #define CST816S_REG_BPC0L 0xB1 42 | #define CST816S_REG_BPC1H 0xB2 43 | #define CST816S_REG_BPC1L 0xB3 44 | #define CST816S_REG_POWER_MODE 0xA5 45 | #define CST816S_REG_CHIP_ID 0xA7 46 | #define CST816S_REG_PROJ_ID 0xA8 47 | #define CST816S_REG_FW_VERSION 0xA9 48 | #define CST816S_REG_MOTION_MASK 0xEC 49 | #define CST816S_REG_IRQ_PULSE_WIDTH 0xED 50 | #define CST816S_REG_NOR_SCAN_PER 0xEE 51 | #define CST816S_REG_MOTION_S1_ANGLE 0xEF 52 | #define CST816S_REG_LP_SCAN_RAW1H 0xF0 53 | #define CST816S_REG_LP_SCAN_RAW1L 0xF1 54 | #define CST816S_REG_LP_SCAN_RAW2H 0xF2 55 | #define CST816S_REG_LP_SCAN_RAW2L 0xF3 56 | #define CST816S_REG_LP_AUTO_WAKEUP_TIME 0xF4 57 | #define CST816S_REG_LP_SCAN_TH 0xF5 58 | #define CST816S_REG_LP_SCAN_WIN 0xF6 59 | #define CST816S_REG_LP_SCAN_FREQ 0xF7 60 | #define CST816S_REG_LP_SCAN_I_DAC 0xF8 61 | #define CST816S_REG_AUTOSLEEP_TIME 0xF9 62 | #define CST816S_REG_IRQ_CTL 0xFA 63 | #define CST816S_REG_DEBOUNCE_TIME 0xFB 64 | #define CST816S_REG_LONG_PRESS_TIME 0xFC 65 | #define CST816S_REG_IOCTL 0xFD 66 | #define CST816S_REG_DIS_AUTO_SLEEP 0xFE 67 | 68 | #define CST816S_GESTURE_NONE (0x00) 69 | #define CST816S_GESTURE_SLIDE_UP (0x01) 70 | #define CST816S_GESTURE_SLIDE_DOWN (0x02) 71 | #define CST816S_GESTURE_SLIDE_LEFT (0x03) 72 | #define CST816S_GESTURE_SLIDE_RIGHT (0x04) 73 | #define CST816S_GESTURE_CLICK (0x05) 74 | #define CST816S_GESTURE_DOUBLE_CLICK (0x0B) 75 | #define CST816S_GESTURE_LONG_PRESS (0x0C) 76 | 77 | #define CST816S_MOTION_EN_CON_LR (1<<2) 78 | #define CST816S_MOTION_EN_CON_UR (1<<1) 79 | #define CST816S_MOTION_EN_DCLICK (1<<0) 80 | 81 | #define CST816S_IRQ_EN_TEST (1<<7) 82 | #define CST816S_IRQ_EN_TOUCH (1<<6) 83 | #define CST816S_IRQ_EN_CHANGE (1<<5) 84 | #define CST816S_IRQ_EN_MOTION (1<<4) 85 | #define CST816S_IRQ_ONCE_WLP (1<<0) 86 | 87 | #define CST816S_IOCTL_SOFT_RTS (1<<2) 88 | #define CST816S_IOCTL_IIC_OD (1<<1) 89 | #define CST816S_IOCTL_EN_1V8 (1<<0) 90 | 91 | #define CST816S_POWER_MODE_SLEEP (0x03) 92 | #define CST816S_POWER_MODE_EXPERIMENTAL (0x05) 93 | 94 | typedef struct { 95 | int16_t x; 96 | int16_t y; 97 | } coordinate_t; 98 | 99 | struct cst816s_data { 100 | const struct device *i2c; 101 | 102 | uint8_t chip_id; 103 | 104 | enum cst816s_gesture gesture; 105 | uint8_t number_touch_point; 106 | enum cst816s_action action; 107 | 108 | coordinate_t touch_point_1; 109 | coordinate_t touch_point_2; 110 | 111 | const struct device *reset_gpio; 112 | #ifdef CONFIG_CST816S_TRIGGER 113 | const struct device *dev; 114 | const struct device *gpio; 115 | struct gpio_callback gpio_cb; 116 | 117 | struct sensor_trigger data_ready_trigger; 118 | sensor_trigger_handler_t data_ready_handler; 119 | 120 | #if defined(CONFIG_CST816S_TRIGGER_OWN_THREAD) 121 | K_THREAD_STACK_MEMBER(thread_stack, CONFIG_CST816S_THREAD_STACK_SIZE); 122 | struct k_thread thread; 123 | struct k_sem gpio_sem; 124 | #elif defined(CONFIG_CST816S_TRIGGER_GLOBAL_THREAD) 125 | struct k_work work; 126 | #endif 127 | 128 | #endif /* CONFIG_CST816S_TRIGGER */ 129 | }; 130 | 131 | #ifdef CONFIG_CST816S_TRIGGER 132 | int cst816s_trigger_set(const struct device *dev, 133 | const struct sensor_trigger *trig, 134 | sensor_trigger_handler_t handler); 135 | 136 | int cst816s_attr_set(const struct device *dev, 137 | enum sensor_channel chan, 138 | enum sensor_attribute attr, 139 | const struct sensor_value *val); 140 | 141 | int cst816s_init_interrupt(const struct device *dev); 142 | #endif 143 | 144 | #endif /* ZEPHYR_DRIVERS_SENSOR_CST816S_CST816S_H_ */ 145 | -------------------------------------------------------------------------------- /app/drivers/sensor/cst816s/cst816s_trigger.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Stephane Dorre 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | #define DT_DRV_COMPAT hynitron_cst816s 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include "cst816s.h" 16 | 17 | #include 18 | 19 | #define INTERRUPT_PIN DT_INST_GPIO_PIN(0, int1_gpios) 20 | 21 | LOG_MODULE_DECLARE(CST816S, CONFIG_SENSOR_LOG_LEVEL); 22 | 23 | int cst816s_attr_set(const struct device *dev, 24 | enum sensor_channel chan, 25 | enum sensor_attribute attr, 26 | const struct sensor_value *val) 27 | { 28 | //struct cst816s_data *drv_data = dev->driver_data; 29 | 30 | if (chan != SENSOR_CHAN_ACCEL_XYZ) { 31 | return -ENOTSUP; 32 | } 33 | 34 | return 0; 35 | } 36 | 37 | static void cst816s_gpio_callback(const struct device *dev, 38 | struct gpio_callback *cb, uint32_t pins) 39 | { 40 | struct cst816s_data *drv_data = 41 | CONTAINER_OF(cb, struct cst816s_data, gpio_cb); 42 | 43 | ARG_UNUSED(pins); 44 | 45 | #if defined(CONFIG_CST816S_TRIGGER_OWN_THREAD) 46 | k_sem_give(&drv_data->gpio_sem); 47 | #elif defined(CONFIG_CST816S_TRIGGER_GLOBAL_THREAD) 48 | k_work_submit(&drv_data->work); 49 | #endif 50 | } 51 | 52 | static void cst816s_thread_cb(const struct device *dev) 53 | { 54 | struct cst816s_data *drv_data = dev->data; 55 | 56 | if (drv_data->data_ready_handler != NULL) { 57 | drv_data->data_ready_handler(dev, &drv_data->data_ready_trigger); 58 | } 59 | } 60 | 61 | #ifdef CONFIG_CST816S_TRIGGER_OWN_THREAD 62 | static void cst816s_thread(struct cst816s_data *drv_data) 63 | { 64 | while (true) { 65 | k_sem_take(&drv_data->gpio_sem, K_FOREVER); 66 | cst816s_thread_cb(drv_data->dev); 67 | } 68 | } 69 | #endif 70 | 71 | #ifdef CONFIG_CST816S_TRIGGER_GLOBAL_THREAD 72 | static void cst816s_work_cb(struct k_work *work) 73 | { 74 | struct cst816s_data *drv_data = 75 | CONTAINER_OF(work, struct cst816s_data, work); 76 | 77 | cst816s_thread_cb(drv_data->dev); 78 | } 79 | #endif 80 | 81 | int cst816s_trigger_set(const struct device *dev, 82 | const struct sensor_trigger *trig, 83 | sensor_trigger_handler_t handler) 84 | { 85 | struct cst816s_data *drv_data = dev->data; 86 | 87 | if (trig->type == SENSOR_TRIG_DATA_READY) { 88 | 89 | drv_data->data_ready_handler = handler; 90 | if (handler == NULL) { 91 | return 0; 92 | } 93 | drv_data->data_ready_trigger = *trig; 94 | } 95 | else { 96 | return -ENOTSUP; 97 | } 98 | 99 | return 0; 100 | } 101 | 102 | int cst816s_init_interrupt(const struct device *dev) 103 | { 104 | struct cst816s_data *drv_data = dev->data; 105 | 106 | /* setup data ready gpio interrupt */ 107 | drv_data->gpio = device_get_binding(DT_INST_GPIO_LABEL(0, int1_gpios)); 108 | if (drv_data->gpio == NULL) { 109 | LOG_DBG("Cannot get pointer to %s device", 110 | DT_INST_GPIO_LABEL(0, int1_gpios)); 111 | return -EINVAL; 112 | } 113 | 114 | gpio_init_callback(&drv_data->gpio_cb, 115 | cst816s_gpio_callback, 116 | BIT(INTERRUPT_PIN)); 117 | 118 | if (gpio_add_callback(drv_data->gpio, &drv_data->gpio_cb) < 0) { 119 | LOG_DBG("Could not set gpio callback"); 120 | return -EIO; 121 | } 122 | 123 | gpio_pin_configure(drv_data->gpio, 124 | INTERRUPT_PIN, GPIO_INPUT | GPIO_PULL_UP 125 | | GPIO_INT_EDGE_FALLING | GPIO_ACTIVE_LOW); 126 | 127 | #if defined(CONFIG_CST816S_TRIGGER_OWN_THREAD) 128 | k_sem_init(&drv_data->gpio_sem, 0, UINT_MAX); 129 | 130 | k_thread_create(&drv_data->thread, drv_data->thread_stack, 131 | CONFIG_CST816S_THREAD_STACK_SIZE, 132 | (k_thread_entry_t)cst816s_thread, drv_data, 133 | NULL, NULL, K_PRIO_COOP(CONFIG_CST816S_THREAD_PRIORITY), 134 | 0, K_NO_WAIT); 135 | #elif defined(CONFIG_CST816S_TRIGGER_GLOBAL_THREAD) 136 | drv_data->work.handler = cst816s_work_cb; 137 | drv_data->dev = dev; 138 | #endif 139 | return 0; 140 | } 141 | -------------------------------------------------------------------------------- /app/drivers/sensor/hrs3300/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Makefile - MAX30101 heart rate sensor 2 | # 3 | # Copyright (c) 2017, NXP 4 | # 5 | # SPDX-License-Identifier: Apache-2.0 6 | # 7 | zephyr_library() 8 | 9 | zephyr_library_sources_ifdef(CONFIG_HRS3300 hrs3300.c) 10 | -------------------------------------------------------------------------------- /app/drivers/sensor/hrs3300/Kconfig: -------------------------------------------------------------------------------- 1 | # HRS3300 heart rate sensor 2 | 3 | # SPDX-License-Identifier: Apache-2.0 4 | 5 | menuconfig HRS3300 6 | bool "HRS3300 Pulse Oximeter and Heart Rate Sensor" 7 | select SENSOR 8 | depends on I2C && HAS_DTS_I2C 9 | 10 | if HRS3300 11 | 12 | config HRS3300_CMDS 13 | bool "Shell commands" 14 | depends on SHELL 15 | default y 16 | help 17 | Enable shell commands for HRS3300 sensor. 18 | 19 | config HRS3300_SMP_AVE 20 | int "Sample averaging" 21 | range 0 7 22 | default 0 23 | help 24 | To reduce the amount of data throughput, adjacent samples (in each 25 | individual channel) can be averaged and decimated on the chip by 26 | setting this register. Set to 0 for no averaging. 27 | 0 = 1 sample (no averaging) 28 | 1 = 2 samples 29 | 2 = 4 samples 30 | 3 = 8 samples 31 | 4 = 16 samples 32 | 5 = 32 samples 33 | 6 = 32 samples 34 | 7 = 32 samples 35 | 36 | config HRS3300_FIFO_ROLLOVER_EN 37 | bool "FIFO rolls on full" 38 | help 39 | Controls the behavior of the FIFO when the FIFO becomes completely 40 | filled with data. If set, the FIFO address rolls over to zero and the 41 | FIFO continues to fill with new data. If not set, then the FIFO is 42 | not updated until FIFO_DATA is read or the WRITE/READ pointer 43 | positions are changed. 44 | 45 | config HRS3300_FIFO_A_FULL 46 | int "FIFO almost full value" 47 | range 0 15 48 | default 0 49 | help 50 | Set the trigger for the FIFO_A_FULL interrupt 51 | 52 | choice HRS3300_MODE 53 | prompt "Mode control" 54 | default HRS3300_MULTI_LED_MODE 55 | 56 | config HRS3300_HEART_RATE_MODE 57 | bool "Heart rate mode" 58 | help 59 | Set to operate in heart rate only mode. The red LED channel is 60 | active. 61 | 62 | config HRS3300_SPO2_MODE 63 | bool "SpO2 mode" 64 | help 65 | Set to operate in SpO2 mode. The red and IR LED channels are active. 66 | 67 | config HRS3300_MULTI_LED_MODE 68 | bool "Multi-LED mode" 69 | help 70 | Set to operate in multi-LED mode. The green, red, and/or IR LED 71 | channels are active. 72 | 73 | endchoice 74 | 75 | config HRS3300_ADC_RGE 76 | int "ADC range control" 77 | range 0 3 78 | default 2 79 | help 80 | Set the ADC's full-scale range. 81 | 0 = 7.81 pA/LSB 82 | 1 = 15.63 pA/LSB 83 | 2 = 31.25 pA/LSB 84 | 3 = 62.5 pA/LSB 85 | 86 | config HRS3300_SR 87 | int "ADC sample rate control" 88 | range 0 7 89 | default 0 90 | help 91 | Set the effective sampling rate with one sample consisting of one 92 | pulse/conversion per active LED channel. In SpO2 mode, these means 93 | one IR pulse/conversion and one red pulse/conversion per sample 94 | period. 95 | 0 = 50 Hz 96 | 1 = 100 Hz 97 | 2 = 200 Hz 98 | 3 = 400 Hz 99 | 4 = 800 Hz 100 | 5 = 1000 Hz 101 | 6 = 1600 Hz 102 | 7 = 3200 Hz 103 | 104 | config HRS3300_LED1_PA 105 | hex "LED1 (red) pulse amplitude" 106 | range 0 0xff 107 | default 0xff 108 | help 109 | Set the pulse amplitude to control the LED1 (red) current. The actual 110 | measured LED current for each part can vary significantly due to the 111 | trimming methodology. 112 | 0x00 = 0.0 mA 113 | 0x01 = 0.2 mA 114 | 0x02 = 0.4 mA 115 | 0x0f = 3.1 mA 116 | 0xff = 50.0 mA 117 | 118 | config HRS3300_LED2_PA 119 | hex "LED2 (IR) pulse amplitude" 120 | range 0 0xff 121 | default 0x33 122 | help 123 | Set the pulse amplitude to control the LED2 (IR) current. The actual 124 | measured LED current for each part can vary significantly due to the 125 | trimming methodology. 126 | 0x00 = 0.0 mA 127 | 0x01 = 0.2 mA 128 | 0x02 = 0.4 mA 129 | 0x0f = 3.1 mA 130 | 0xff = 50.0 mA 131 | 132 | config HRS3300_LED3_PA 133 | hex "LED2 (green) pulse amplitude" 134 | range 0 0xff 135 | default 0xff 136 | help 137 | Set the pulse amplitude to control the LED3 (green) current. The 138 | actual measured LED current for each part can vary significantly due 139 | to the trimming methodology. 140 | 0x00 = 0.0 mA 141 | 0x01 = 0.2 mA 142 | 0x02 = 0.4 mA 143 | 0x0f = 3.1 mA 144 | 0xff = 50.0 mA 145 | 146 | if HRS3300_MULTI_LED_MODE 147 | 148 | config HRS3300_SLOT1 149 | int "Slot 1" 150 | range 0 7 151 | default 3 152 | help 153 | Set which LED and pulse amplitude are active in time slot 1. 154 | 0: None (disabled) 155 | 1: LED1 (red), LED1_PA 156 | 2: LED2 (IR), LED2_PA 157 | 3: LED3 (green), LED3_PA 158 | 4: None (disabled) 159 | 5: LED1 (red), PILOT_PA 160 | 6: LED2 (IR), PILOT_PA 161 | 7: LED3 (green), PILOT_PA 162 | 163 | config HRS3300_SLOT2 164 | int "Slot 2" 165 | range 0 7 166 | default 0 167 | help 168 | Set which LED and pulse amplitude are active in time slot 2. 169 | 0: None (disabled) 170 | 1: LED1 (red), LED1_PA 171 | 2: LED2 (IR), LED2_PA 172 | 3: LED3 (green), LED3_PA 173 | 4: None (disabled) 174 | 5: LED1 (red), PILOT_PA 175 | 6: LED2 (IR), PILOT_PA 176 | 7: LED3 (green), PILOT_PA 177 | 178 | config HRS3300_SLOT3 179 | int "Slot 3" 180 | range 0 7 181 | default 0 182 | help 183 | Set which LED and pulse amplitude are active in time slot 3. 184 | 0: None (disabled) 185 | 1: LED1 (red), LED1_PA 186 | 2: LED2 (IR), LED2_PA 187 | 3: LED3 (green), LED3_PA 188 | 4: None (disabled) 189 | 5: LED1 (red), PILOT_PA 190 | 6: LED2 (IR), PILOT_PA 191 | 7: LED3 (green), PILOT_PA 192 | 193 | config HRS3300_SLOT4 194 | int "Slot 4" 195 | range 0 7 196 | default 0 197 | help 198 | Set which LED and pulse amplitude are active in time slot 4. 199 | 0: None (disabled) 200 | 1: LED1 (red), LED1_PA 201 | 2: LED2 (IR), LED2_PA 202 | 3: LED3 (green), LED3_PA 203 | 4: None (disabled) 204 | 5: LED1 (red), PILOT_PA 205 | 6: LED2 (IR), PILOT_PA 206 | 7: LED3 (green), PILOT_PA 207 | 208 | endif # HRS3300_MULTI_LED_MODE 209 | 210 | endif # HRS3300 211 | -------------------------------------------------------------------------------- /app/drivers/sensor/hrs3300/hrs3300.c: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: Apache-2.0 3 | */ 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #define MY_REGISTER3 (*(volatile uint8_t*)0x2000F002) 13 | 14 | LOG_MODULE_REGISTER(HRS3300, CONFIG_SENSOR_LOG_LEVEL); 15 | 16 | #define HRS3300_I2C_ADDRESS 0x44 17 | 18 | #define HRS3300_PDRIVE_12_5 0 19 | #define HRS3300_PDRIVE_20 1 20 | #define HRS3300_PDRIVE_30 2 21 | #define HRS3300_PDRIVE_40 3 22 | 23 | #define HRS3300_DEVICE_ID_ADDR 0x00 24 | #define HRS3300_DEVICE_ID 0x21 25 | 26 | #define HRS3300_ENABLED_ADDR 0x01 27 | 28 | #define HRS3300_HWT_800MS 0x0 29 | #define HRS3300_HWT_400MS 0x1 30 | #define HRS3300_HWT_200MS 0x2 31 | #define HRS3300_HWT_100MS 0x3 32 | #define HRS3300_HWT_75MS 0x4 33 | #define HRS3300_HWT_50MS 0x5 34 | #define HRS3300_HWT_12_5MS 0x6 35 | #define HRS3300_HWT_0MS 0x7 36 | 37 | struct hrs3300_reg_enable { 38 | u8_t reserved :3; 39 | u8_t pdrive1 :1; 40 | u8_t hwt :3; 41 | u8_t enable :1; 42 | }; 43 | 44 | #define HRS3300_HRS_LED_DRIVER_SET_ADDR 0x0C 45 | struct hrs3300_reg_hrs_led_driver_set { 46 | u8_t reserved :5; 47 | u8_t pon :1; 48 | u8_t pdrive0 :1; 49 | u8_t reserved1 :1; 50 | }; 51 | 52 | 53 | #define HRS3300_RESOLUTION_ADDR 0x16 54 | struct hrs3300_reg_resolution { 55 | u8_t als_res :4; 56 | u8_t reserved :4; 57 | }; 58 | 59 | #define HRS3300_HGAIN_ADDR 0x17 60 | struct hrs3300_reg_hgain { 61 | u8_t reserved :2; 62 | u8_t hgain :3; 63 | u8_t reserved1 :3; 64 | }; 65 | 66 | union hrs3300_reg { 67 | struct hrs3300_reg_enable enable; 68 | struct hrs3300_reg_hrs_led_driver_set hrs_led_driver_set; 69 | struct hrs3300_reg_resolution resolution; 70 | struct hrs3300_reg_hgain hgain; 71 | u8_t raw; 72 | }; 73 | 74 | struct hrs3300_addr_reg { 75 | u8_t addr; 76 | union hrs3300_reg reg; 77 | }; 78 | 79 | struct hrs3300_data { 80 | struct device *i2c; 81 | u32_t raw[2]; 82 | struct hrs3300_addr_reg config[4]; 83 | }; 84 | 85 | struct hrs3300_config { 86 | }; 87 | 88 | static u32_t get_hrs_adc(u8_t *buf) 89 | { 90 | return ((u32_t)buf[7] & 0xF) | 91 | (((u32_t)buf[2] & 0xf) << 4) | 92 | ((u32_t)buf[1] << 8) | 93 | (((u32_t)buf[7] & 0x30) << 16); 94 | } 95 | 96 | static u32_t get_als(u8_t *buf) 97 | { 98 | return ((u32_t)buf[6] & 0x7) | 99 | ((u32_t)buf[0] << 3) | 100 | ((u32_t)buf[5] << 10); 101 | } 102 | 103 | static int hrs3300_sample_fetch(struct device *dev, enum sensor_channel chan) 104 | { 105 | struct hrs3300_data *data = dev->driver_data; 106 | u8_t buf[8]; 107 | int err; 108 | 109 | err = i2c_burst_read(data->i2c, HRS3300_I2C_ADDRESS, 0x8, 110 | buf, sizeof(buf)); 111 | if (err < 0) { 112 | LOG_ERR("Failed to read data (err:%d)", err); 113 | return err; 114 | } 115 | 116 | data->raw[0] = get_hrs_adc(buf); 117 | data->raw[1] = get_als(buf); 118 | 119 | return 0; 120 | } 121 | 122 | //todo method to switch heart rate sensor off 123 | int hrs3300_attr_set(struct device *dev, 124 | enum sensor_channel chan, 125 | enum sensor_attribute attr, 126 | const struct sensor_value *val) 127 | { 128 | return 0; 129 | } 130 | 131 | static int hrs3300_channel_get(struct device *dev, enum sensor_channel chan, 132 | struct sensor_value *val) 133 | { 134 | struct hrs3300_data *data = dev->driver_data; 135 | 136 | val->val1 = data->raw[0]; 137 | val->val2 = data->raw[1]; 138 | 139 | return 0; 140 | } 141 | 142 | static const struct sensor_driver_api hrs3300_driver_api = { 143 | .sample_fetch = hrs3300_sample_fetch, 144 | .channel_get = hrs3300_channel_get, 145 | .attr_set = hrs3300_attr_set, 146 | }; 147 | 148 | static int hrs3300_init(struct device *dev) 149 | { 150 | struct hrs3300_data *data = dev->driver_data; 151 | u8_t device_id; 152 | int err; 153 | 154 | /* Get the I2C device */ 155 | data->i2c = device_get_binding(DT_INST_0_HX_HRS3300_BUS_NAME); 156 | if (!data->i2c) { 157 | LOG_ERR("Could not find I2C device"); 158 | return -EINVAL; 159 | } 160 | 161 | err = i2c_reg_read_byte(data->i2c, HRS3300_I2C_ADDRESS, 162 | HRS3300_DEVICE_ID_ADDR, &device_id); 163 | if ((err < 0) || (device_id != HRS3300_DEVICE_ID)) { 164 | LOG_ERR("Failed to read device id (%d)", err); 165 | return err; 166 | } 167 | 168 | for (int i = 0; i < ARRAY_SIZE(data->config); i++) { 169 | err = i2c_reg_write_byte(data->i2c, HRS3300_I2C_ADDRESS, 170 | data->config[i].addr, data->config[i].reg.raw); 171 | if (err < 0) { 172 | LOG_ERR("Failed to write %x register (err:%d)", 173 | data->config[i].addr, err); 174 | return err; 175 | } 176 | } 177 | 178 | return 0; 179 | } 180 | 181 | static int write_reg(struct device *dev, struct hrs3300_addr_reg *reg) 182 | { 183 | struct hrs3300_data *data = dev->driver_data; 184 | 185 | return i2c_reg_write_byte(data->i2c, HRS3300_I2C_ADDRESS, 186 | reg->addr, reg->reg.raw); 187 | } 188 | 189 | static int hrs3300_en(struct device *dev, bool en) 190 | { 191 | struct hrs3300_data *data = dev->driver_data; 192 | 193 | data->config[0].reg.enable.enable = en ? 1 : 0; 194 | return write_reg(dev, &data->config[0]); 195 | } 196 | 197 | int hrs3300_enable(struct device *dev) 198 | { 199 | return hrs3300_en(dev, true); 200 | } 201 | 202 | int hrs3300_disable(struct device *dev) 203 | { 204 | return hrs3300_en(dev, false); 205 | } 206 | 207 | int hrs3300_hgain_set(struct device *dev, enum hrs3300_hgain val) 208 | { 209 | struct hrs3300_data *data = dev->driver_data; 210 | data->config[3].reg.hgain.hgain = val; 211 | return write_reg(dev, &data->config[3]); 212 | } 213 | 214 | int hrs3300_hgain_get(struct device *dev, enum hrs3300_hgain *val) 215 | { 216 | struct hrs3300_data *data = dev->driver_data; 217 | 218 | *val = data->config[3].reg.hgain.hgain; 219 | 220 | return 0; 221 | } 222 | 223 | int hrs3300_hwt_set(struct device *dev, enum hrs3300_hwt val) 224 | { 225 | struct hrs3300_data *data = dev->driver_data; 226 | 227 | data->config[0].reg.enable.hwt = val; 228 | 229 | return write_reg(dev, &data->config[0]); 230 | } 231 | 232 | int hrs3300_hwt_get(struct device *dev, enum hrs3300_hwt *val) 233 | { 234 | struct hrs3300_data *data = dev->driver_data; 235 | 236 | *val = data->config[0].reg.enable.hwt; 237 | 238 | return 0; 239 | } 240 | 241 | int hrs3300_pdrive_set(struct device *dev, enum hrs3300_pdrive val) 242 | { 243 | struct hrs3300_data *data = dev->driver_data; 244 | int err; 245 | 246 | data->config[0].reg.enable.pdrive1 = (val & 2) ? 1 : 0; 247 | data->config[1].reg.hrs_led_driver_set.pdrive0 = val & 1; 248 | 249 | err = write_reg(dev, &data->config[0]); 250 | if (err < 0) { 251 | return err; 252 | } 253 | 254 | return write_reg(dev, &data->config[1]); 255 | } 256 | 257 | int hrs3300_pdrive_get(struct device *dev, enum hrs3300_pdrive *val) 258 | { 259 | struct hrs3300_data *data = dev->driver_data; 260 | 261 | *val = data->config[1].reg.hrs_led_driver_set.pdrive0 | 262 | (data->config[0].reg.enable.pdrive1 << 1); 263 | 264 | return 0; 265 | } 266 | 267 | static struct hrs3300_config hrs3300_config; 268 | static struct hrs3300_data hrs3300_data = { 269 | .config = { 270 | { 271 | .addr = HRS3300_ENABLED_ADDR, 272 | .reg = { 273 | .enable = { 274 | .enable = 0, 275 | .pdrive1 = (HRS3300_PDRIVE_40 & 0x2) ? 276 | 1 : 0, 277 | .hwt = HRS3300_HWT_12_5MS 278 | } 279 | } 280 | }, 281 | { 282 | .addr = HRS3300_HRS_LED_DRIVER_SET_ADDR, 283 | .reg = { 284 | .hrs_led_driver_set = { 285 | .pon = 1, 286 | .pdrive0 = HRS3300_PDRIVE_40 & 0x1, 287 | .reserved = 0x8 288 | } 289 | } 290 | }, 291 | { 292 | .addr = HRS3300_RESOLUTION_ADDR, 293 | .reg = { 294 | .resolution = { 295 | .reserved = 0x6, 296 | .als_res = 0x8 /* 16bit*/ 297 | } 298 | } 299 | }, 300 | { 301 | .addr = HRS3300_HGAIN_ADDR, 302 | .reg = { 303 | .hgain = { 304 | .hgain = 0 305 | } 306 | } 307 | } 308 | } 309 | }; 310 | 311 | DEVICE_AND_API_INIT(hrs3300, DT_INST_0_HX_HRS3300_LABEL, hrs3300_init, 312 | &hrs3300_data, &hrs3300_config, 313 | POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY, 314 | &hrs3300_driver_api); 315 | 316 | static int cmd_enable(const struct shell *shell, size_t argc, char **argv) 317 | { 318 | int err; 319 | 320 | err = hrs3300_enable(DEVICE_GET(hrs3300)); 321 | shell_print(shell, "Enabled (err:%d)", err); 322 | 323 | return 0; 324 | } 325 | 326 | static int cmd_disable(const struct shell *shell, size_t argc, char **argv) 327 | { 328 | int err; 329 | 330 | err = hrs3300_disable(DEVICE_GET(hrs3300)); 331 | shell_print(shell, "Disabled (err:%d)", err); 332 | 333 | return 0; 334 | } 335 | 336 | static int cmd_dump_registers(const struct shell *shell, 337 | size_t argc, char **argv) 338 | { 339 | static const u8_t addr[] = {0, 1, 8, 9, 10, 12, 13, 14, 15, 22, 23}; 340 | struct hrs3300_data *data = DEVICE_GET(hrs3300)->driver_data; 341 | int err; 342 | u8_t val; 343 | 344 | for (int i = 0; i < ARRAY_SIZE(addr); i++) { 345 | err = i2c_reg_read_byte(data->i2c, HRS3300_I2C_ADDRESS, addr[i], 346 | &val); 347 | if (err < 0) { 348 | shell_error(shell, "Failed to read %x (err: %d)", 349 | addr[i], err); 350 | } else { 351 | shell_print(shell, "Reg:%x value:%x", addr[i], val); 352 | } 353 | } 354 | 355 | return 0; 356 | } 357 | 358 | static int cmd_sample(const struct shell *shell, size_t argc, char **argv) 359 | { 360 | int err; 361 | struct sensor_value value; 362 | 363 | err = sensor_sample_fetch(DEVICE_GET(hrs3300)); 364 | if (err < 0) { 365 | shell_error(shell, "Failed to read (err:%d)", err); 366 | } 367 | 368 | err = sensor_channel_get(DEVICE_GET(hrs3300), 0, &value); 369 | if (err < 0) { 370 | shell_error(shell, "Failed to read channel (err:%d)", err); 371 | } 372 | 373 | shell_print(shell, "hrs:%d, als:%d", value.val1, value.val2); 374 | 375 | return 0; 376 | } 377 | 378 | static int cmd_read_gain(const struct shell *shell, size_t argc, char **argv) 379 | { 380 | enum hrs3300_hgain gain_reg; 381 | u8_t gain_val; 382 | 383 | hrs3300_hgain_get(DEVICE_GET(hrs3300), &gain_reg); 384 | gain_val = (gain_reg < 4) ? (1 << gain_reg) : 64; 385 | 386 | shell_print(shell, "Gain:%dx", gain_val); 387 | 388 | return 0; 389 | } 390 | 391 | static int cmd_set_gain1(const struct shell *shell, size_t argc, char **argv) 392 | { 393 | return hrs3300_hgain_set(DEVICE_GET(hrs3300), 0); 394 | } 395 | 396 | static int cmd_set_gain2(const struct shell *shell, size_t argc, char **argv) 397 | { 398 | return hrs3300_hgain_set(DEVICE_GET(hrs3300), 1); 399 | } 400 | 401 | static int cmd_set_gain4(const struct shell *shell, size_t argc, char **argv) 402 | { 403 | return hrs3300_hgain_set(DEVICE_GET(hrs3300), 2); 404 | } 405 | 406 | static int cmd_set_gain8(const struct shell *shell, size_t argc, char **argv) 407 | { 408 | return hrs3300_hgain_set(DEVICE_GET(hrs3300), 3); 409 | } 410 | 411 | static int cmd_set_gain64(const struct shell *shell, size_t argc, char **argv) 412 | { 413 | return hrs3300_hgain_set(DEVICE_GET(hrs3300), 4); 414 | } 415 | 416 | static int cmd_read_hwt(const struct shell *shell, size_t argc, char **argv) 417 | { 418 | enum hrs3300_hwt val; 419 | static const char *hwt[] = {"800", "400", "200", "100", 420 | "75", "50", "12.5", "0"}; 421 | 422 | hrs3300_hwt_get(DEVICE_GET(hrs3300), &val); 423 | 424 | shell_print(shell, "HWT:%sms", hwt[val]); 425 | 426 | return 0; 427 | } 428 | 429 | static int cmd_set_hwt800(const struct shell *shell, size_t argc, char **argv) 430 | { 431 | return hrs3300_hwt_set(DEVICE_GET(hrs3300), 0); 432 | } 433 | 434 | static int cmd_set_hwt400(const struct shell *shell, size_t argc, char **argv) 435 | { 436 | return hrs3300_hwt_set(DEVICE_GET(hrs3300), 1); 437 | } 438 | 439 | static int cmd_set_hwt200(const struct shell *shell, size_t argc, char **argv) 440 | { 441 | return hrs3300_hwt_set(DEVICE_GET(hrs3300), 2); 442 | } 443 | 444 | static int cmd_set_hwt100(const struct shell *shell, size_t argc, char **argv) 445 | { 446 | return hrs3300_hwt_set(DEVICE_GET(hrs3300), 3); 447 | } 448 | 449 | static int cmd_set_hwt75(const struct shell *shell, size_t argc, char **argv) 450 | { 451 | return hrs3300_hwt_set(DEVICE_GET(hrs3300), 4); 452 | } 453 | 454 | static int cmd_set_hwt50(const struct shell *shell, size_t argc, char **argv) 455 | { 456 | return hrs3300_hwt_set(DEVICE_GET(hrs3300), 5); 457 | } 458 | 459 | static int cmd_set_hwt12_5(const struct shell *shell, size_t argc, char **argv) 460 | { 461 | return hrs3300_hwt_set(DEVICE_GET(hrs3300), 6); 462 | } 463 | 464 | static int cmd_set_hwt0(const struct shell *shell, size_t argc, char **argv) 465 | { 466 | return hrs3300_hwt_set(DEVICE_GET(hrs3300), 7); 467 | } 468 | 469 | static int cmd_read_pdrive(const struct shell *shell, size_t argc, char **argv) 470 | { 471 | enum hrs3300_pdrive val; 472 | static const char *pdrive[] = {"12.5", "20", "30", "40"}; 473 | 474 | hrs3300_pdrive_get(DEVICE_GET(hrs3300), &val); 475 | 476 | shell_print(shell, "PDrive:%sms", pdrive[val]); 477 | 478 | return 0; 479 | } 480 | 481 | static int cmd_set_pdrive12_5(const struct shell *shell, size_t argc, 482 | char **argv) 483 | { 484 | return hrs3300_pdrive_set(DEVICE_GET(hrs3300), HRS3300_PDRIVE_12_5); 485 | } 486 | 487 | static int cmd_set_pdrive20(const struct shell *shell, size_t argc, 488 | char **argv) 489 | { 490 | return hrs3300_pdrive_set(DEVICE_GET(hrs3300), HRS3300_PDRIVE_20); 491 | } 492 | 493 | static int cmd_set_pdrive30(const struct shell *shell, size_t argc, 494 | char **argv) 495 | { 496 | return hrs3300_pdrive_set(DEVICE_GET(hrs3300), HRS3300_PDRIVE_30); 497 | } 498 | 499 | static int cmd_set_pdrive40(const struct shell *shell, size_t argc, 500 | char **argv) 501 | { 502 | return hrs3300_pdrive_set(DEVICE_GET(hrs3300), HRS3300_PDRIVE_40); 503 | } 504 | 505 | static int cmd_read(const struct shell *shell, size_t argc, char **argv, 506 | bool hrs) 507 | { 508 | int len = strtol(argv[1], NULL, 10); 509 | enum hrs3300_hwt freq_reg; 510 | u32_t ms[] = {800,400, 200, 100, 75, 50, 12, 0}; 511 | int err; 512 | static u16_t samples[1024]; 513 | struct sensor_value value; 514 | int sleep_ms; 515 | 516 | if (len == 0) { 517 | return 0; 518 | } else if (len > ARRAY_SIZE(samples)) { 519 | shell_error(shell, "Request too big, limited to %d", 520 | ARRAY_SIZE(samples)); 521 | } 522 | 523 | err = hrs3300_hwt_get(DEVICE_GET(hrs3300), &freq_reg); 524 | if (err < 0) { 525 | shell_error(shell, "Failed to read hwt (err:%d)", err); 526 | return 0; 527 | } 528 | 529 | sleep_ms = ms[freq_reg]; 530 | 531 | for (int i = 0; i < len; i++) { 532 | err = sensor_sample_fetch(DEVICE_GET(hrs3300)); 533 | if (err < 0) { 534 | shell_error(shell, "Failed to read (err:%d)", err); 535 | return 0; 536 | } 537 | 538 | err = sensor_channel_get(DEVICE_GET(hrs3300), 0, &value); 539 | if (err < 0) { 540 | shell_error(shell, "Failed to read channel (err:%d)", err); 541 | return 0; 542 | } 543 | 544 | samples[i] = (u16_t)(hrs ? value.val1 : value.val2); 545 | if (sleep_ms == 12) { 546 | sleep_ms++; 547 | } else if (sleep_ms == 13) { 548 | sleep_ms--; 549 | } 550 | 551 | k_sleep(K_MSEC(sleep_ms)); 552 | } 553 | 554 | for (int i = 0; i < len; i++) { 555 | shell_print(shell, "%d", samples[i]); 556 | } 557 | 558 | shell_print(shell, "Sampling period %d", sleep_ms); 559 | 560 | return 0; 561 | } 562 | 563 | static int cmd_read_hrs(const struct shell *shell, size_t argc, char **argv) 564 | { 565 | return cmd_read(shell, argc, argv, true); 566 | } 567 | 568 | static int cmd_read_als(const struct shell *shell, size_t argc, char **argv) 569 | { 570 | return cmd_read(shell, argc, argv, false); 571 | } 572 | 573 | SHELL_STATIC_SUBCMD_SET_CREATE(gain_cmds, 574 | SHELL_CMD_ARG(x1, NULL, "Control Gain", cmd_set_gain1, 1, 0), 575 | SHELL_CMD_ARG(x2, NULL, "Control Gain", cmd_set_gain2, 1, 0), 576 | SHELL_CMD_ARG(x4, NULL, "Control Gain", cmd_set_gain4, 1, 0), 577 | SHELL_CMD_ARG(x8, NULL, "Control Gain", cmd_set_gain8, 1, 0), 578 | SHELL_CMD_ARG(x64, NULL, "Control Gain", cmd_set_gain64, 1, 0), 579 | SHELL_SUBCMD_SET_END 580 | ); 581 | 582 | SHELL_STATIC_SUBCMD_SET_CREATE(hwt_cmds, 583 | SHELL_CMD_ARG(800ms, NULL, "Set HWT", cmd_set_hwt800, 1, 0), 584 | SHELL_CMD_ARG(400ms, NULL, "Set HWT", cmd_set_hwt400, 1, 0), 585 | SHELL_CMD_ARG(200ms, NULL, "Set HWT", cmd_set_hwt200, 1, 0), 586 | SHELL_CMD_ARG(100ms, NULL, "Set HWT", cmd_set_hwt100, 1, 0), 587 | SHELL_CMD_ARG(75ms, NULL, "Set HWT", cmd_set_hwt75, 1, 0), 588 | SHELL_CMD_ARG(50ms, NULL, "Set HWT", cmd_set_hwt50, 1, 0), 589 | SHELL_CMD_ARG(12_5ms, NULL, "Set HWT", cmd_set_hwt12_5, 1, 0), 590 | SHELL_CMD_ARG(0ms, NULL, "Set HWT", cmd_set_hwt0, 1, 0), 591 | SHELL_SUBCMD_SET_END 592 | ); 593 | 594 | SHELL_STATIC_SUBCMD_SET_CREATE(pdrive_cmds, 595 | SHELL_CMD_ARG(12_5mA, NULL, "Control PDrive", cmd_set_pdrive12_5, 1, 0), 596 | SHELL_CMD_ARG(20mA, NULL, "Control PDrive", cmd_set_pdrive20, 1, 0), 597 | SHELL_CMD_ARG(30mA, NULL, "Control PDrive", cmd_set_pdrive30, 1, 0), 598 | SHELL_CMD_ARG(40mA, NULL, "Control PDrive", cmd_set_pdrive40, 1, 0), 599 | SHELL_SUBCMD_SET_END 600 | ); 601 | 602 | SHELL_STATIC_SUBCMD_SET_CREATE(read_cmds, 603 | SHELL_CMD_ARG(hrs, NULL, "Read samples", cmd_read_hrs, 2, 0), 604 | SHELL_CMD_ARG(als, NULL, "Read samples", cmd_read_als, 2, 0), 605 | SHELL_SUBCMD_SET_END 606 | ); 607 | 608 | SHELL_STATIC_SUBCMD_SET_CREATE(hrs3300_cmds, 609 | SHELL_CMD_ARG(hgain, &gain_cmds, "Control Gain", cmd_read_gain, 1, 1), 610 | SHELL_CMD_ARG(hwt, &hwt_cmds, "Control HWT", cmd_read_hwt, 1, 1), 611 | SHELL_CMD_ARG(pdrive, &pdrive_cmds, "Control PDrive", cmd_read_pdrive, 1, 1), 612 | SHELL_CMD_ARG(read, &read_cmds, "Read HRS and ALS", cmd_read, 1, 0), 613 | SHELL_CMD_ARG(sample, NULL, "Sample HRS and ALS", cmd_sample, 1, 0), 614 | SHELL_CMD_ARG(dump, NULL, "Dump registers", cmd_dump_registers, 1, 0), 615 | SHELL_CMD_ARG(disable, NULL, "Disable", cmd_disable, 1, 0), 616 | SHELL_CMD_ARG(enable, NULL, "Enable", cmd_enable, 1, 0), 617 | SHELL_SUBCMD_SET_END 618 | ); 619 | 620 | static int cmd_dummy(const struct shell *shell, size_t argc, char **argv) 621 | { 622 | return 0; 623 | } 624 | 625 | SHELL_COND_CMD_REGISTER(CONFIG_HRS3300_CMDS, hrs3300, &hrs3300_cmds, 626 | "HRS3300 shell commands", cmd_dummy); 627 | -------------------------------------------------------------------------------- /app/drivers/sensor/hrs3300/hrs3300.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: Apache-2.0 3 | */ 4 | #ifndef PINETIME_INCLUDE_HRS3300_H 5 | #define PINETIME_INCLUDE_HRS3300_H 6 | 7 | #include 8 | 9 | #ifdef __cplusplus 10 | extern "C" { 11 | #endif 12 | 13 | enum hrs3300_hwt { 14 | HRS3300_HWT_800MS = 0x0, 15 | HRS3300_HWT_400MS = 0x1, 16 | HRS3300_HWT_200MS = 0x2, 17 | HRS3300_HWT_100MS = 0x3, 18 | HRS3300_HWT_75MS = 0x4, 19 | HRS3300_HWT_50MS = 0x5, 20 | HRS3300_HWT_12_5MS = 0x6, 21 | HRS3300_HWT_0MS = 0x7 22 | }; 23 | 24 | enum hrs3300_pdrive { 25 | HRS3300_PDRIVE_12_5 = 0, 26 | HRS3300_PDRIVE_20 = 1, 27 | HRS3300_PDRIVE_30 = 2, 28 | HRS3300_PDRIVE_40 = 3 29 | }; 30 | 31 | enum hrs3300_hgain { 32 | HRS3300_HGAIN_1 = 0, 33 | HRS3300_HGAIN_2 = 1, 34 | HRS3300_HGAIN_4 = 2, 35 | HRS3300_HGAIN_8 = 3, 36 | HRS3300_HGAIN_64 = 4, 37 | }; 38 | 39 | /** @brief Enable measurement. 40 | * 41 | * @param dev Device. 42 | * 43 | * @return 0 on suucess, negative error code otherwise. 44 | */ 45 | int hrs3300_enable(struct device *dev); 46 | 47 | /** @brief Disable measurement. 48 | * 49 | * @param dev Device. 50 | * 51 | * @return 0 on suucess, negative error code otherwise. 52 | */ 53 | int hrs3300_disable(struct device *dev); 54 | 55 | 56 | /** @brief Set hgain. 57 | * 58 | * @param dev Device. 59 | * @param val Hgain. 60 | * 61 | * @return 0 on suucess, negative error code otherwise. 62 | */ 63 | int hrs3300_hgain_set(struct device *dev, enum hrs3300_hgain val); 64 | 65 | 66 | /** @brief Get hgain. 67 | * 68 | * @param dev Device. 69 | * @param val Location to store hgain. 70 | * 71 | * @return 0 on suucess, negative error code otherwise. 72 | */ 73 | int hrs3300_hgain_get(struct device *dev, enum hrs3300_hgain *val); 74 | 75 | /** @brief Set HWT. 76 | * 77 | * @param dev Device. 78 | * @param val Gain. 79 | * 80 | * @return 0 on suucess, negative error code otherwise. 81 | */ 82 | int hrs3300_hwt_set(struct device *dev, enum hrs3300_hwt val); 83 | 84 | /** @brief Get HWT. 85 | * 86 | * @param dev Device. 87 | * @param val Location to store HWT. 88 | * 89 | * @return 0 on suucess, negative error code otherwise. 90 | */ 91 | int hrs3300_hwt_get(struct device *dev, enum hrs3300_hwt *val); 92 | 93 | /** @brief Set PDrive. 94 | * 95 | * @param dev Device. 96 | * @param val Gain. 97 | * 98 | * @return 0 on suucess, negative error code otherwise. 99 | */ 100 | int hrs3300_pdrive_set(struct device *dev, enum hrs3300_pdrive val); 101 | 102 | /** @brief Get PDrive. 103 | * 104 | * @param dev Device. 105 | * @param val Location to store PDrive. 106 | * 107 | * @return 0 on suucess, negative error code otherwise. 108 | */ 109 | int hrs3300_pdrive_get(struct device *dev, enum hrs3300_pdrive *val); 110 | 111 | #ifdef __cplusplus 112 | } 113 | #endif 114 | 115 | #endif /* PINETIME_INCLUDE_HRS3300_H */ 116 | -------------------------------------------------------------------------------- /app/dts/bindings/sensor/bosch,bma421-i2c.yaml: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2019, Linaro Limited 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | 5 | description: This is a representation of the BME280 Integrated environmental sensor 6 | 7 | compatible: "bosch,bma421" 8 | 9 | include: i2c-device.yaml 10 | 11 | properties: 12 | int1-gpios: 13 | type: phandle-array 14 | required: false 15 | -------------------------------------------------------------------------------- /app/dts/bindings/sensor/hx,hrs3300.yaml: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2018, NXP 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | 5 | description: This is a representation of the HRS3300 heart rate sensor 6 | 7 | compatible: "hx,hrs3300" 8 | 9 | include: i2c-device.yaml 10 | -------------------------------------------------------------------------------- /app/dts/bindings/sensor/hynitron,cst816s.yaml: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: Apache-2.0 2 | 3 | 4 | description: This is a representation of the Hynitron,CST816S touchscreen sensor 5 | 6 | compatible: "hynitron,cst816s" 7 | 8 | include: i2c-device.yaml 9 | 10 | properties: 11 | int1-gpios: 12 | type: phandle-array 13 | required: false 14 | reset-gpios: 15 | type: phandle-array 16 | required: false 17 | -------------------------------------------------------------------------------- /app/hypnos/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: Apache-2.0 2 | 3 | cmake_minimum_required(VERSION 3.13.1) 4 | 5 | # Add the board 6 | list(APPEND BOARD_ROOT ${CMAKE_CURRENT_LIST_DIR}/..) 7 | list(APPEND DTS_ROOT ${CMAKE_CURRENT_LIST_DIR}/..) 8 | 9 | # Enable configurations through environment variables 10 | set(CONF_FILE ${CMAKE_CURRENT_LIST_DIR}/config/prj.conf ${CMAKE_CURRENT_LIST_DIR}/config/bootloader.conf) 11 | IF("$ENV{BOOTLOADER}" STREQUAL n) 12 | set(CONF_FILE ${CMAKE_CURRENT_LIST_DIR}/config/prj.conf) 13 | ENDIF() 14 | IF("$ENV{LOGGING}" STREQUAL y) 15 | set(CONF_FILE ${CONF_FILE} ${CMAKE_CURRENT_LIST_DIR}/config/logging_rtt.conf) 16 | ENDIF() 17 | 18 | find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) 19 | include_directories(include) 20 | project(hypnos) 21 | 22 | FILE(GLOB app_sources src/*.c) 23 | target_sources(app PRIVATE ${app_sources}) 24 | 25 | # Get current time 26 | string(TIMESTAMP CURRENT_TIME %Y-%m-%dT%H:%M:%S) 27 | 28 | # Get short commit hash 29 | execute_process( 30 | COMMAND ${GIT_EXECUTABLE} describe --tags --dirty 31 | WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR} 32 | OUTPUT_VARIABLE FW_BUILD_GIT 33 | OUTPUT_STRIP_TRAILING_WHITESPACE 34 | ) 35 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DCURRENT_TIME_OF_BUILD=\"${CURRENT_TIME}\" -DFW_BUILD=\"${FW_BUILD_GIT}\"") 36 | -------------------------------------------------------------------------------- /app/hypnos/config/bootloader.conf: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: Apache-2.0 2 | 3 | # Bootloader 4 | CONFIG_BOOTLOADER_MCUBOOT=y 5 | 6 | # Flash operations 7 | CONFIG_FLASH=y 8 | CONFIG_SPI_NOR=y 9 | CONFIG_SPI_2=y 10 | CONFIG_SPI_NRFX=y 11 | CONFIG_SPI_NOR_IDLE_IN_DPD=y 12 | 13 | # Allow for large Bluetooth data packets. 14 | CONFIG_BT_L2CAP_TX_MTU=252 15 | CONFIG_BT_L2CAP_RX_MTU=252 16 | CONFIG_BT_RX_BUF_LEN=260 17 | 18 | # Enable SMP over Bluetooth (unauthenticated). 19 | CONFIG_MCUMGR_SMP_BT=y 20 | CONFIG_MCUMGR_SMP_BT_AUTHEN=n 21 | 22 | # Enable the LittleFS file system. 23 | CONFIG_FILE_SYSTEM=y 24 | CONFIG_FILE_SYSTEM_LITTLEFS=y 25 | 26 | # Enable file system commands 27 | CONFIG_MCUMGR_CMD_FS_MGMT=y 28 | 29 | # Enable mcumgr. 30 | CONFIG_MCUMGR=y 31 | 32 | # Enable most core commands. 33 | CONFIG_MCUMGR_CMD_IMG_MGMT=y 34 | CONFIG_MCUMGR_CMD_OS_MGMT=y 35 | -------------------------------------------------------------------------------- /app/hypnos/config/logging_rtt.conf: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: Apache-2.0 2 | 3 | # Logging 4 | CONFIG_LOG=y 5 | CONFIG_USE_SEGGER_RTT=y 6 | CONFIG_RTT_CONSOLE=y 7 | CONFIG_CONSOLE=y 8 | -------------------------------------------------------------------------------- /app/hypnos/config/prj.conf: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: Apache-2.0 2 | 3 | # Battery status 4 | CONFIG_ADC=y 5 | CONFIG_ADC_ASYNC=y 6 | 7 | # GPIO 8 | CONFIG_GPIO=y 9 | 10 | # Time functions 11 | CONFIG_NEWLIB_LIBC=y 12 | 13 | # Display 14 | CONFIG_SPI=y 15 | CONFIG_DISPLAY=y 16 | CONFIG_ST7789V=y 17 | CONFIG_ST7789V_RGB565=y 18 | 19 | # Graphics 20 | CONFIG_LVGL=y 21 | CONFIG_LVGL_USE_LABEL=y 22 | CONFIG_LVGL_USE_IMG=y 23 | CONFIG_LVGL_COLOR_DEPTH_16=y 24 | CONFIG_LVGL_COLOR_16_SWAP=y 25 | CONFIG_LVGL_BITS_PER_PIXEL=24 26 | CONFIG_LVGL_HOR_RES_MAX=240 27 | CONFIG_LVGL_VER_RES_MAX=240 28 | CONFIG_LVGL_DISPLAY_DEV_NAME="DISPLAY" 29 | CONFIG_LVGL_FONT_MONTSERRAT_22=y 30 | CONFIG_LVGL_TXT_ENC_UTF8=y 31 | CONFIG_LVGL_USE_THEME_MONO=y 32 | 33 | # Touch screen 34 | CONFIG_SENSOR=y 35 | CONFIG_CST816S=y 36 | CONFIG_CST816S_TRIGGER_GLOBAL_THREAD=y 37 | 38 | # Bluetooth 39 | CONFIG_BT=y 40 | CONFIG_BT_PERIPHERAL=y 41 | CONFIG_BT_DIS=y 42 | CONFIG_BT_ATT_PREPARE_COUNT=5 43 | CONFIG_BT_DEVICE_NAME="Hypnos" 44 | CONFIG_BT_DEVICE_APPEARANCE=833 45 | CONFIG_BT_DEVICE_NAME_DYNAMIC=y 46 | CONFIG_BT_DEVICE_NAME_MAX=65 47 | CONFIG_BT_GATT_CLIENT=y 48 | CONFIG_BT_DIS_PNP=n 49 | CONFIG_BT_DIS_FW_REV=y 50 | CONFIG_BT_DIS_HW_REV=y 51 | CONFIG_BT_DIS_SW_REV=y 52 | 53 | # Let DIS information be read from settings 54 | CONFIG_BT_SETTINGS=y 55 | CONFIG_SETTINGS_RUNTIME=y 56 | CONFIG_SETTINGS=y 57 | CONFIG_SETTINGS_NONE=y 58 | CONFIG_BT_DIS_SETTINGS=y 59 | CONFIG_BT_DIS_STR_MAX=21 60 | 61 | # Remove warning 62 | CONFIG_BT_GATT_CACHING=n 63 | 64 | # Power management 65 | CONFIG_DEVICE_POWER_MANAGEMENT=y 66 | 67 | # Misc 68 | CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=2304 69 | -------------------------------------------------------------------------------- /app/hypnos/hypnos-photo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/albsod/pinetime-hypnos/8c634b89e0dc742042f6fad8515234618ecc1fa1/app/hypnos/hypnos-photo.png -------------------------------------------------------------------------------- /app/hypnos/include/backlight.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: Apache-2.0 3 | */ 4 | 5 | #ifndef BACKLIGHT__H 6 | #define BACKLIGHT__H 7 | 8 | #include 9 | 10 | void backlight_init(void); 11 | void backlight_enable(bool); 12 | bool backlight_is_enabled(void); 13 | 14 | #endif /* BACKLIGHT__H */ 15 | -------------------------------------------------------------------------------- /app/hypnos/include/battery.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Endian Technologies AB 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | #ifndef BATTERY__H 8 | #define BATTERY__H 9 | 10 | void battery_init(); 11 | void battery_update_percentage(); 12 | void battery_update_charging_status(bool); 13 | bool battery_get_charging_status(); 14 | uint32_t battery_raw_to_mv(int16_t raw); 15 | uint32_t battery_mv_to_ppt(uint32_t mv); 16 | void battery_show_status(); 17 | void battery_gfx_init(); 18 | 19 | #endif /* BATTERY__H */ 20 | -------------------------------------------------------------------------------- /app/hypnos/include/bt.h: -------------------------------------------------------------------------------- 1 | #ifndef BT__H 2 | #define BT__H 3 | 4 | #include 5 | 6 | void bt_init(void); 7 | 8 | #endif /* BT__H */ 9 | -------------------------------------------------------------------------------- /app/hypnos/include/clock.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Endian Technologies AB 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | #ifndef CLOCK__H 8 | #define CLOCK__H 9 | 10 | #include 11 | 12 | void clock_init(void); 13 | void clock_increment_local_time(void); 14 | void clock_sync_time(cts_datetime_t *cts); 15 | void clock_show_time(void); 16 | 17 | #endif /* CLOCK__H */ 18 | -------------------------------------------------------------------------------- /app/hypnos/include/cts_sync.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Modifications: Copyright (c) 2020 Endian Technologies AB 3 | * Copyright (c) 2016 Intel Corporation 4 | * 5 | * SPDX-License-Identifier: Apache-2.0 6 | */ 7 | 8 | #ifndef CTS__H 9 | #define CTS__H 10 | 11 | #include 12 | #include 13 | 14 | void cts_sync_init(void); 15 | void cts_sync_loop(void); 16 | void cts_sync_enable(bool enable); 17 | void cts_update_datetime(struct tm *); 18 | 19 | typedef struct { 20 | uint16_t year; 21 | uint8_t month; 22 | uint8_t day; 23 | uint8_t hours; 24 | uint8_t minutes; 25 | uint8_t seconds; 26 | uint8_t day_of_week; 27 | uint8_t exact_time_256; 28 | uint8_t adjust_reason; 29 | } cts_datetime_t; 30 | 31 | #endif /* CTS__H */ 32 | -------------------------------------------------------------------------------- /app/hypnos/include/display.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Endian Technologies AB 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | #ifndef DISPLAY__H 8 | #define DISPLAY__H 9 | 10 | void display_init(void); 11 | void display_sleep(void); 12 | void display_wake_up(void); 13 | 14 | #endif /* DISPLAY__H */ 15 | -------------------------------------------------------------------------------- /app/hypnos/include/event_handler.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Endian Technologies AB 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | #ifndef EVENT_HANDLER__H 8 | #define EVENT_HANDLER__H 9 | 10 | #include 11 | #include 12 | 13 | void event_handler_init(void); 14 | void display_off_isr(struct k_timer *); 15 | #ifdef CONFIG_BOOTLOADER_MCUBOOT 16 | void watchdog_refresh_isr(struct k_timer *); 17 | #endif 18 | void battery_percentage_isr(struct k_timer *); 19 | void battery_charging_isr(const struct device*, struct gpio_callback *, uint32_t); 20 | void button_pressed_isr(const struct device *, struct gpio_callback *, uint32_t); 21 | void touch_tap_isr(const struct device *, struct sensor_trigger *); 22 | 23 | #endif /* EVENT_HANDLER */ 24 | -------------------------------------------------------------------------------- /app/hypnos/include/gfx.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Endian Technologies AB 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | #ifndef GFX__H 8 | #define GFX__H 9 | 10 | enum bt_symbol { 11 | BT_CONNECTED, 12 | BT_ADVERTISING_ON, 13 | BT_ADVERTISING_OFF, 14 | }; 15 | 16 | enum battery_symbol { 17 | BAT_CHARGE, 18 | BAT_FULL, 19 | BAT_3, 20 | BAT_2, 21 | BAT_1, 22 | BAT_EMPTY 23 | }; 24 | 25 | void gfx_init(void); 26 | void gfx_battery_set_label(enum battery_symbol); 27 | void gfx_bt_set_label(enum bt_symbol); 28 | void gfx_time_set_label(char *); 29 | void gfx_date_set_label(char *); 30 | void gfx_update(void); 31 | void gfx_show_info(void); 32 | void gfx_show_watch(void); 33 | 34 | #endif /* GFX__H */ 35 | -------------------------------------------------------------------------------- /app/hypnos/include/gui.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Endian Technologies AB 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | #ifndef GUI__H 8 | #define GUI__H 9 | 10 | #include 11 | 12 | enum screen { 13 | WATCH, 14 | INFO 15 | }; 16 | 17 | void gui_handle_touch_event(const struct device *, enum cst816s_gesture); 18 | int gui_handle_button_event(void); 19 | 20 | #endif /* GUI__H */ 21 | -------------------------------------------------------------------------------- /app/hypnos/include/log.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Endian Technologies AB 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | #ifndef LOGGING__H 8 | #define LOGGING__H 9 | 10 | #include 11 | 12 | LOG_MODULE_DECLARE(hypnos, LOG_LEVEL_INF); 13 | 14 | #endif /* LOGGING__H */ 15 | -------------------------------------------------------------------------------- /app/hypnos/include/version.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Endian Technologies AB 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | #ifndef VERSION__H 8 | #define VERSION__H 9 | 10 | /* Stringify firmware build data included by cmake */ 11 | #define _xstr(s) _str(s) 12 | #define _str(s) #s 13 | #define TIME_OF_BUILD _xstr(CURRENT_TIME_OF_BUILD) 14 | #define FW_VERSION _xstr(FW_BUILD) 15 | 16 | #endif /* VERSION__H */ 17 | -------------------------------------------------------------------------------- /app/hypnos/src/backlight.c: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: Apache-2.0 3 | */ 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "backlight.h" 10 | #include "log.h" 11 | 12 | /* ********** ********** DEFINES ********** ********** ********** */ 13 | #define BACKLIGHT_PORT DT_GPIO_LABEL(DT_ALIAS(led1), gpios) 14 | #define BACKLIGHT_1 DT_GPIO_PIN(DT_ALIAS(led0), gpios) 15 | #define BACKLIGHT_2 DT_GPIO_PIN(DT_ALIAS(led1), gpios) 16 | #define BACKLIGHT_3 DT_GPIO_PIN(DT_ALIAS(led2), gpios) 17 | /* ********** ********** ********** ********** ********** */ 18 | 19 | /* ********** ********** VARIABLES AND STRUCTS ********** ********** */ 20 | static const struct device* backlight_dev; 21 | static bool backlight_enabled = false; 22 | /* ********** ********** ********** ********** ********** ********** */ 23 | 24 | /* ********** ********** FUNCTIONS ********** ********** */ 25 | void backlight_init() 26 | { 27 | backlight_dev = device_get_binding(BACKLIGHT_PORT); 28 | gpio_pin_configure(backlight_dev, BACKLIGHT_1, GPIO_OUTPUT); 29 | gpio_pin_configure(backlight_dev, BACKLIGHT_2, GPIO_OUTPUT); 30 | gpio_pin_configure(backlight_dev, BACKLIGHT_3, GPIO_OUTPUT); 31 | backlight_enable(true); 32 | LOG_DBG("Backlight init: Done"); 33 | } 34 | 35 | void backlight_enable(bool enable) 36 | { 37 | gpio_pin_set_raw(backlight_dev, BACKLIGHT_1, enable ? 0 : 1); 38 | gpio_pin_set_raw(backlight_dev, BACKLIGHT_2, enable ? 0 : 1); 39 | gpio_pin_set_raw(backlight_dev, BACKLIGHT_3, enable ? 0 : 1); 40 | backlight_enabled = enable; 41 | } 42 | 43 | bool backlight_is_enabled() 44 | { 45 | return backlight_enabled; 46 | } 47 | /* ********** ********** ********** ********** ********** */ 48 | -------------------------------------------------------------------------------- /app/hypnos/src/battery.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Endian Technologies AB 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | * 6 | * battery_mv_to_ppt, battery_level_point, battery_raw_to_mv: 7 | * Copyright (c) 2020 Nordic Semiconductor 8 | */ 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include "battery.h" 17 | #include "gfx.h" 18 | #include "log.h" 19 | 20 | #define CHANNEL_ID 7 21 | #define RESOLUTION 12 22 | #define DIVIDER 2 23 | #define COUNT_DOWN 5000 24 | 25 | /* ********** ********** VARIABLES ********** ********** */ 26 | static const struct device* percentage_dev; 27 | static int16_t data[1]; 28 | static uint32_t percentage; 29 | static bool charging; 30 | /* ********** ********** ********** ********** ********** */ 31 | 32 | /* ********** ********** STRUCTS ********** ********** */ 33 | struct battery_level_point { 34 | /** Remaining life at #lvl_mV. */ 35 | uint16_t lvl_pptt; 36 | 37 | /** Battery voltage at #lvl_pptt remaining life. */ 38 | uint16_t lvl_mV; 39 | }; 40 | 41 | static const struct battery_level_point lipo[] = { 42 | { 10000, 4200 }, 43 | { 5000, 3660}, 44 | { 2100, 3600 }, 45 | { 1000, 3560}, 46 | { 0, 3100 }, 47 | }; 48 | 49 | static const struct adc_sequence sequence = { 50 | .channels = BIT(CHANNEL_ID), 51 | .buffer = data, 52 | .buffer_size = sizeof(data), 53 | .resolution = RESOLUTION, 54 | }; 55 | 56 | static const struct adc_channel_cfg m_1st_channel_cfg = { 57 | .gain = ADC_GAIN_1_4, 58 | .reference = ADC_REF_INTERNAL, 59 | .acquisition_time = ADC_ACQ_TIME(ADC_ACQ_TIME_MICROSECONDS, 20), 60 | .channel_id = CHANNEL_ID, 61 | #if defined(CONFIG_ADC_CONFIGURABLE_INPUTS) 62 | .input_positive = 8, 63 | #endif 64 | }; 65 | 66 | /* ********** ********** ********** ********** ********** */ 67 | 68 | /* ********** ********** FUNCTIONS ********** ********** */ 69 | void battery_init() 70 | { 71 | percentage_dev = device_get_binding("ADC_0"); 72 | if (percentage_dev == NULL) { 73 | LOG_ERR("Failed to get binding for ADC_0 device!"); 74 | } 75 | 76 | if (adc_channel_setup(percentage_dev, &m_1st_channel_cfg) < 0) { 77 | LOG_ERR("Failed to setup channel for adc!"); 78 | } 79 | 80 | LOG_DBG("Battery status init: Done"); 81 | } 82 | 83 | void battery_update_percentage() 84 | { 85 | adc_read(percentage_dev, &sequence); 86 | uint32_t mv = battery_raw_to_mv(data[0]); 87 | percentage = battery_mv_to_ppt(mv)/100; 88 | } 89 | 90 | void battery_update_charging_status(bool value) 91 | { 92 | charging = value; 93 | } 94 | 95 | bool battery_get_charging_status() 96 | { 97 | return charging; 98 | } 99 | 100 | uint32_t battery_raw_to_mv(int16_t raw) 101 | { 102 | return (DIVIDER*600*(((uint32_t)raw*4*1000) >> RESOLUTION))/1000; 103 | } 104 | 105 | uint32_t battery_mv_to_ppt(uint32_t mv) 106 | { 107 | const struct battery_level_point *pb = lipo; 108 | 109 | if (mv >= pb->lvl_mV) { 110 | return pb->lvl_pptt; 111 | } 112 | 113 | while ((pb->lvl_pptt > 0) 114 | && (mv < pb->lvl_mV)) { 115 | ++pb; 116 | } 117 | if (mv < pb->lvl_mV) { 118 | 119 | return pb->lvl_pptt; 120 | } 121 | 122 | const struct battery_level_point *pa = pb - 1; 123 | 124 | return pb->lvl_pptt 125 | + ((pa->lvl_pptt - pb->lvl_pptt) 126 | * (mv - pb->lvl_mV) 127 | / (pa->lvl_mV - pb->lvl_mV)); 128 | } 129 | 130 | void battery_show_status() 131 | { 132 | battery_update_percentage(); 133 | if (charging) { 134 | LOG_INF("Battery status: %u%% (charging)", 135 | percentage); 136 | gfx_battery_set_label(BAT_CHARGE); 137 | } else { 138 | LOG_INF("Battery status: %u%% (discharging)", 139 | percentage); 140 | 141 | if (percentage > 89) { 142 | gfx_battery_set_label(BAT_FULL); 143 | } else if (percentage > 74) { 144 | gfx_battery_set_label(BAT_3); 145 | } else if (percentage > 49) { 146 | gfx_battery_set_label(BAT_2); 147 | } else if (percentage > 24) { 148 | gfx_battery_set_label(BAT_1); 149 | } else { 150 | gfx_battery_set_label(BAT_EMPTY); 151 | } 152 | } 153 | } 154 | 155 | /* ********** ********** ********** ********** ********** */ 156 | -------------------------------------------------------------------------------- /app/hypnos/src/bt.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012-2014 Wind River Systems, Inc. 3 | * Copyright (c) 2015-2016 Intel Corporation 4 | * Copyright (c) 2020 Endian Technologies AB 5 | * Copyright (c) 2020 max00 6 | * Copyright (c) 2020 Prevas A/S 7 | * 8 | * SPDX-License-Identifier: Apache-2.0 9 | */ 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #ifdef CONFIG_MCUMGR 27 | #include 28 | #endif 29 | 30 | #include "cts_sync.h" 31 | #include "gfx.h" 32 | #include "log.h" 33 | #include "version.h" 34 | 35 | /* ********** Function prototypes ********** */ 36 | static void connected(struct bt_conn *conn, uint8_t err); 37 | static void disconnected(struct bt_conn *conn, uint8_t reason); 38 | static bool le_param_req(struct bt_conn *conn, struct bt_le_conn_param *param); 39 | static void le_param_updated(struct bt_conn *conn, uint16_t interval, uint16_t latency, uint16_t timeout); 40 | 41 | /* ********** Variables ********** */ 42 | 43 | static struct k_work advertise_work; 44 | 45 | static const struct bt_data ad[] = { 46 | BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)), 47 | /* Device information */ 48 | BT_DATA_BYTES(BT_DATA_UUID16_ALL, 49 | 0x0a, 0x18), 50 | /* Current time */ 51 | BT_DATA_BYTES(BT_DATA_UUID16_ALL, 52 | 0x05, 0x18), 53 | #ifdef CONFIG_MCUMGR 54 | /* SMP */ 55 | BT_DATA_BYTES(BT_DATA_UUID128_ALL, 56 | 0x84, 0xaa, 0x60, 0x74, 0x52, 0x8a, 0x8b, 0x86, 57 | 0xd3, 0x4c, 0xb7, 0x1d, 0x1d, 0xdc, 0x53, 0x8d), 58 | #endif 59 | }; 60 | 61 | static struct bt_conn_cb m_conn_callbacks = { 62 | .connected = connected, 63 | .disconnected = disconnected, 64 | .le_param_req = le_param_req, 65 | .le_param_updated = le_param_updated 66 | }; 67 | 68 | static struct bt_le_adv_param param = BT_LE_ADV_PARAM_INIT( 69 | BT_LE_ADV_OPT_CONNECTABLE | BT_LE_ADV_OPT_USE_NAME, 70 | BT_GAP_ADV_SLOW_INT_MIN, 71 | BT_GAP_ADV_SLOW_INT_MAX, 72 | NULL 73 | ); 74 | 75 | /* ********** Functions ********** */ 76 | static int settings_runtime_load(void) 77 | { 78 | #if defined(CONFIG_BOARD_PINETIME) 79 | settings_runtime_set("bt/dis/model", 80 | "PineTime", 81 | sizeof("PineTime")); 82 | settings_runtime_set("bt/dis/manuf", 83 | "PINE64", 84 | sizeof("PINE64")); 85 | settings_runtime_set("bt/dis/hw", 86 | "1.0a", 87 | sizeof("1.0a")); 88 | #endif 89 | #if defined(CONFIG_BOARD_P8) 90 | settings_runtime_set("bt/dis/model", 91 | "P8", 92 | sizeof("P8")); 93 | settings_runtime_set("bt/dis/manuf", 94 | "Colmi", 95 | sizeof("Colmi")); 96 | #endif 97 | settings_runtime_set("bt/dis/sw", 98 | CONFIG_BT_DEVICE_NAME, 99 | sizeof(CONFIG_BT_DEVICE_NAME)); 100 | settings_runtime_set("bt/dis/fw", 101 | FW_VERSION, 102 | sizeof(FW_VERSION)); 103 | return 0; 104 | } 105 | 106 | static void advertise(struct k_work *work) 107 | { 108 | int rc; 109 | 110 | bt_le_adv_stop(); 111 | 112 | rc = bt_le_adv_start(¶m, ad, ARRAY_SIZE(ad), NULL, 0); 113 | if (rc) { 114 | LOG_ERR("Advertising failed to start (rc %d)", rc); 115 | return; 116 | } 117 | 118 | LOG_INF("Advertising successfully started"); 119 | } 120 | 121 | static void connected(struct bt_conn *conn, uint8_t err) 122 | { 123 | if (err) { 124 | return; 125 | } 126 | LOG_INF("connected"); 127 | cts_sync_enable(true); 128 | gfx_bt_set_label(BT_CONNECTED); 129 | gfx_update(); 130 | } 131 | 132 | static void disconnected(struct bt_conn *conn, uint8_t reason) 133 | { 134 | LOG_INF("disconnected (reason: %u)", reason); 135 | cts_sync_enable(false); 136 | gfx_bt_set_label(BT_ADVERTISING_ON); 137 | gfx_update(); 138 | } 139 | 140 | static bool le_param_req(struct bt_conn *conn, struct bt_le_conn_param *param) 141 | { 142 | return true; 143 | } 144 | 145 | static void le_param_updated(struct bt_conn *conn, uint16_t interval, uint16_t latency, uint16_t timeout) 146 | { 147 | 148 | } 149 | 150 | void bt_init(void) 151 | { 152 | int err = bt_enable(NULL); 153 | if (err) { 154 | LOG_ERR("Bluetooth init failed (err %d)", err); 155 | return; 156 | } 157 | 158 | settings_load(); 159 | settings_runtime_load(); 160 | 161 | k_work_init(&advertise_work, advertise); 162 | bt_conn_cb_register(&m_conn_callbacks); 163 | 164 | k_work_submit(&advertise_work); 165 | #ifdef CONFIG_MCUMGR 166 | /* Initialize the Bluetooth mcumgr transport. */ 167 | smp_bt_register(); 168 | #endif 169 | cts_sync_init(); 170 | 171 | LOG_DBG("Bluetooth initialized"); 172 | } 173 | 174 | void bt_adv_stop(void) 175 | { 176 | k_sleep(K_MSEC(400)); 177 | 178 | int err = bt_le_adv_stop(); 179 | if (err) { 180 | LOG_ERR("Advertising failed to stop (err %d)", err); 181 | return; 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /app/hypnos/src/clock.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Endian Technologies AB 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include "battery.h" 12 | #include "bt.h" 13 | #include "version.h" 14 | #include "cts_sync.h" 15 | #include "log.h" 16 | #include "event_handler.h" 17 | #include "gfx.h" 18 | 19 | /* ********** ********** VARIABLES ********** ********** */ 20 | static char time_label_str[32]; 21 | static char date_label_str[32]; 22 | static uint64_t uptime_ms; 23 | static uint64_t last_uptime_ms; 24 | static uint64_t elapsed_ms; 25 | 26 | static struct tm ti = { 27 | .tm_sec = 0, 28 | .tm_min = 0, 29 | .tm_hour = 0, 30 | .tm_mday = 0, 31 | .tm_mon = 0, 32 | .tm_year = 0, 33 | .tm_wday = 0, 34 | }; 35 | 36 | /* ********** ********** FUNCTIONS *********** ********** */ 37 | void clock_str_to_local_time(const char *str) 38 | { 39 | if (sscanf(str, "%d-%d-%dT%d:%d:%d", &ti.tm_year, &ti.tm_mon, 40 | &ti.tm_mday, &ti.tm_hour, &ti.tm_min, &ti.tm_sec) != 6) { 41 | LOG_ERR("Failed to parse time of build."); 42 | } 43 | ti.tm_year-=1900; 44 | ti.tm_mon-=1; 45 | mktime(&ti); 46 | } 47 | 48 | void clock_init() 49 | { 50 | /* Set time to time of build */ 51 | clock_str_to_local_time(TIME_OF_BUILD); 52 | LOG_DBG("Time set to time of build"); 53 | LOG_DBG("Clock init: Done"); 54 | } 55 | 56 | void clock_update_elapsed_ms() 57 | { 58 | uptime_ms = k_uptime_get(); 59 | elapsed_ms = uptime_ms - last_uptime_ms; 60 | last_uptime_ms = uptime_ms; 61 | } 62 | 63 | /* Called by cts sync */ 64 | void clock_sync_time(cts_datetime_t *cts) 65 | { 66 | ti.tm_year = cts->year -1900; 67 | ti.tm_mon = cts->month -1; 68 | ti.tm_mday = cts->day; 69 | ti.tm_hour = cts->hours; 70 | ti.tm_min = cts->minutes; 71 | ti.tm_sec = cts->seconds; 72 | mktime(&ti); 73 | clock_update_elapsed_ms(); 74 | } 75 | 76 | /* Called by event handler */ 77 | void clock_increment_local_time() 78 | { 79 | clock_update_elapsed_ms(); 80 | ti.tm_sec += elapsed_ms / 1000; 81 | mktime(&ti); 82 | } 83 | 84 | void clock_show_time() 85 | { 86 | strftime(time_label_str, 32, "%H:%M", &ti); 87 | strftime(date_label_str, 32, "%a %d %b", &ti); 88 | gfx_time_set_label(time_label_str); 89 | gfx_date_set_label(date_label_str); 90 | } 91 | -------------------------------------------------------------------------------- /app/hypnos/src/cts_sync.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Dejvino 3 | * Copyright (c) 2020 Endian Technologies AB 4 | * Copyright (c) 2020 max00 5 | * 6 | * SPDX-License-Identifier: Apache-2.0 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include "log.h" 16 | #include "cts_sync.h" 17 | #include "clock.h" 18 | 19 | /* ********** Definitions ********** */ 20 | #define CTS_SYNC_INTERVAL K_SECONDS(60) 21 | 22 | /* ********** Function prototypes ********** */ 23 | static void cts_sync_timer_timeout_handler(struct k_timer *); 24 | static void cts_sync_processor(struct bt_conn *, void *); 25 | 26 | /* ********** Variables ********** */ 27 | static struct k_timer m_cts_sync_timer; 28 | static struct bt_gatt_discover_params cts_discovery_params; 29 | static struct bt_uuid_16 uuid = BT_UUID_INIT_16(0); 30 | static struct bt_gatt_read_params read_params; 31 | cts_datetime_t clock_datetime; 32 | static struct { 33 | int offset; 34 | cts_datetime_t datetime; 35 | } m_read_buf; 36 | 37 | /* ********** Functions ********** */ 38 | void cts_sync_init() 39 | { 40 | k_timer_init(&m_cts_sync_timer, cts_sync_timer_timeout_handler, NULL); 41 | } 42 | 43 | void cts_sync_enable(bool enable) 44 | { 45 | if (enable) { 46 | k_timer_start(&m_cts_sync_timer, K_NO_WAIT, CTS_SYNC_INTERVAL); 47 | } else { 48 | k_timer_stop(&m_cts_sync_timer); 49 | } 50 | } 51 | 52 | static void sync_cts_to_clock(cts_datetime_t* cts_datetime) 53 | { 54 | if (cts_datetime->year + cts_datetime->day + cts_datetime->hours 55 | + cts_datetime->minutes + cts_datetime->seconds == 0) { 56 | LOG_WRN("Ignoring suspicious time data from companion application."); 57 | return; 58 | } 59 | 60 | LOG_INF("CTS sync to clock started.\n Y%04d D%03d T%2d:%2d:%2d", 61 | cts_datetime->year, cts_datetime->day, 62 | cts_datetime->hours, cts_datetime->minutes, cts_datetime->seconds); 63 | 64 | memcpy(&clock_datetime, cts_datetime, sizeof(clock_datetime)); 65 | clock_sync_time(cts_datetime); 66 | LOG_INF("CTS sync to clock complete."); 67 | } 68 | 69 | void cts_update_datetime(struct tm *t) 70 | { 71 | t->tm_year = clock_datetime.year -1900; 72 | t->tm_mon = clock_datetime.month -1; 73 | t->tm_mday = clock_datetime.day; 74 | t->tm_hour = clock_datetime.hours; 75 | t->tm_min = clock_datetime.minutes; 76 | t->tm_sec = clock_datetime.seconds; 77 | } 78 | 79 | static void cts_sync_timer_timeout_handler(struct k_timer *tmr) 80 | { 81 | bt_conn_foreach(BT_CONN_TYPE_LE, cts_sync_processor, NULL); 82 | } 83 | 84 | uint8_t cts_sync_read(struct bt_conn *conn, uint8_t err, 85 | struct bt_gatt_read_params *params, 86 | const void *data, uint16_t length) 87 | { 88 | LOG_DBG("Reading CCC data: err %d, %d bytes, offset %d.", err, length, m_read_buf.offset); 89 | 90 | if (!data || length <= 0) { 91 | sync_cts_to_clock(&m_read_buf.datetime); 92 | return BT_GATT_ITER_STOP; 93 | } 94 | 95 | memcpy(&m_read_buf.datetime + m_read_buf.offset, data, length); 96 | m_read_buf.offset += length; 97 | 98 | return BT_GATT_ITER_CONTINUE; 99 | } 100 | 101 | uint8_t cts_sync_service_discovered(struct bt_conn* conn, const struct bt_gatt_attr* attr, 102 | struct bt_gatt_discover_params* params) 103 | { 104 | if (!attr) { 105 | LOG_INF("CTS Service Discovery completed"); 106 | return BT_GATT_ITER_STOP; 107 | } 108 | LOG_DBG("Discovered attribute, handle: %u", attr->handle); 109 | 110 | memset(&read_params, 0, sizeof(read_params)); 111 | read_params.func = cts_sync_read; 112 | read_params.by_uuid.uuid = (struct bt_uuid *) &uuid; 113 | read_params.by_uuid.start_handle = attr->handle; 114 | read_params.by_uuid.end_handle = 0xffff; 115 | m_read_buf.offset = 0; 116 | if (bt_gatt_read(conn, &read_params) < 0) { 117 | LOG_DBG("Could not initiate read of CCC data."); 118 | } 119 | 120 | return BT_GATT_ITER_STOP; 121 | } 122 | 123 | static void cts_sync_processor(struct bt_conn *conn, void *data) 124 | { 125 | memcpy(&uuid, BT_UUID_CTS_CURRENT_TIME, sizeof(uuid)); 126 | cts_discovery_params.func = cts_sync_service_discovered; 127 | cts_discovery_params.start_handle = 0x0001; 128 | cts_discovery_params.end_handle = 0xFFFF; 129 | cts_discovery_params.type = BT_GATT_DISCOVER_CHARACTERISTIC; 130 | cts_discovery_params.uuid = (struct bt_uuid *) &uuid; 131 | 132 | if (bt_gatt_discover(conn, &cts_discovery_params) != 0) { 133 | LOG_ERR("CTS Sync > GATT discovery FAILED."); 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /app/hypnos/src/display.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Endian Technologies AB 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include "display.h" 12 | #include "log.h" 13 | 14 | /* ********** ********** VARIABLES AND STRUCTS ********** ********** */ 15 | static const struct device *display_dev; 16 | /* ********** ********** ********** ********** ********** ********** */ 17 | 18 | /* ********** ********** FUNCTIONS ********** ********** */ 19 | void display_init(void) 20 | { 21 | display_dev = device_get_binding(CONFIG_LVGL_DISPLAY_DEV_NAME); 22 | display_blanking_off(display_dev); 23 | LOG_DBG("Display init: Done"); 24 | } 25 | 26 | void display_sleep(void) 27 | { 28 | (void)device_set_power_state(display_dev, DEVICE_PM_LOW_POWER_STATE, NULL, 29 | NULL); 30 | } 31 | 32 | void display_wake_up(void) 33 | { 34 | (void)device_set_power_state(display_dev, DEVICE_PM_ACTIVE_STATE, NULL, 35 | NULL); 36 | } 37 | 38 | 39 | /* ********** ********** ********** ********** ********** */ 40 | -------------------------------------------------------------------------------- /app/hypnos/src/event_handler.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Endian Technologies AB 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include "backlight.h" 13 | #include "battery.h" 14 | #include "bt.h" 15 | #include "clock.h" 16 | #include "display.h" 17 | #include "event_handler.h" 18 | #include "gui.h" 19 | #include "log.h" 20 | 21 | /* ********** defines ********** */ 22 | #define BAT_CHA 12 23 | #define BTN_PORT DT_GPIO_LABEL(DT_ALIAS(sw0), gpios) 24 | #define BTN_IN DT_GPIO_PIN(DT_ALIAS(sw0), gpios) 25 | #define BTN_OUT 15 26 | #define EDGE (GPIO_INT_EDGE | GPIO_INT_DOUBLE_EDGE) 27 | #define PULL_UP DT_GPIO_FLAGS(DT_ALIAS(sw0), gpios) 28 | #define TOUCH_PORT CONFIG_CST816S_NAME 29 | #define DISPLAY_TIMEOUT K_SECONDS(5) 30 | #ifdef CONFIG_BOOTLOADER_MCUBOOT 31 | /* The watchdog released by the PineTime bootloader v5.0.0-rc1 32 | * will try to bite every 7 seconds. 33 | */ 34 | #define WDT_REFRESH 6 35 | #endif 36 | /* ********** ******* ********** */ 37 | 38 | /* ********** variables ********** */ 39 | #ifdef CONFIG_BOOTLOADER_MCUBOOT 40 | static struct k_timer watchdog_refresh_timer; 41 | #endif 42 | static struct k_timer display_off_timer; 43 | static const struct device *charging_dev; 44 | static struct gpio_callback charging_cb; 45 | static const struct device *button_dev; 46 | static struct gpio_callback button_cb; 47 | static const struct device *touch_dev; 48 | static struct sensor_trigger tap = { 49 | .type = SENSOR_TRIG_DATA_READY, 50 | .chan = SENSOR_CHAN_ACCEL_XYZ, 51 | }; 52 | static enum cst816s_gesture gesture; 53 | static struct sensor_value touch_point; 54 | 55 | /* ********** ********* ********** */ 56 | 57 | /* ********** init function ********** */ 58 | void event_handler_init() 59 | { 60 | /* Initialize GPIOs */ 61 | charging_dev = device_get_binding("GPIO_0"); 62 | gpio_pin_configure(charging_dev, BAT_CHA, GPIO_INPUT | GPIO_INT_EDGE_BOTH); 63 | gpio_init_callback(&charging_cb, battery_charging_isr, BIT(BAT_CHA)); 64 | button_dev = device_get_binding(BTN_PORT); 65 | gpio_pin_configure(button_dev, BTN_IN, GPIO_INPUT | GPIO_INT_EDGE_FALLING | PULL_UP); 66 | gpio_init_callback(&button_cb, button_pressed_isr, BIT(BTN_IN)); 67 | touch_dev = device_get_binding(DT_LABEL(DT_INST(0, hynitron_cst816s))); 68 | 69 | /* Enable GPIOs */ 70 | gpio_add_callback(charging_dev, &charging_cb); 71 | gpio_add_callback(button_dev, &button_cb); 72 | sensor_trigger_set(touch_dev, &tap, touch_tap_isr); 73 | 74 | /* Set button out pin to high to enable the button */ 75 | uint32_t button_out = 1U; 76 | gpio_pin_configure(button_dev, BTN_OUT, GPIO_OUTPUT); 77 | gpio_pin_set_raw(button_dev, BTN_OUT, button_out); 78 | 79 | /* Start timers */ 80 | #ifdef CONFIG_BOOTLOADER_MCUBOOT 81 | if (NRF_WDT->RUNSTATUS) { 82 | LOG_INF("Watchdog detected. Let's kick it every %d seconds.", 83 | WDT_REFRESH); 84 | k_timer_init(&watchdog_refresh_timer, watchdog_refresh_isr, 85 | NULL); 86 | k_timer_start(&watchdog_refresh_timer, K_NO_WAIT, 87 | K_SECONDS(WDT_REFRESH)); 88 | } else { 89 | LOG_INF("No watchdog detected."); 90 | } 91 | #endif 92 | k_timer_init(&display_off_timer, display_off_isr, NULL); 93 | k_timer_start(&display_off_timer, DISPLAY_TIMEOUT, K_NO_WAIT); 94 | 95 | /* Special cases */ 96 | /* Get battery charging status */ 97 | k_sleep(K_MSEC(10)); 98 | uint32_t res = gpio_pin_get(charging_dev, BAT_CHA); 99 | battery_update_charging_status(res != 1U); 100 | 101 | /* Show time, date and battery status */ 102 | clock_show_time(); 103 | battery_show_status(); 104 | 105 | LOG_DBG("Event handler init: Done"); 106 | } 107 | /* ********** ************ ********** */ 108 | 109 | /* ********** interrupt handlers ********** */ 110 | #ifdef CONFIG_BOOTLOADER_MCUBOOT 111 | void watchdog_refresh_isr(struct k_timer *wdt_refresh) 112 | { 113 | NRF_WDT->RR[0] = WDT_RR_RR_Reload; 114 | } 115 | #endif 116 | 117 | void display_off_isr(struct k_timer *light_off) 118 | { 119 | backlight_enable(false); 120 | display_sleep(); 121 | } 122 | 123 | void battery_charging_isr(const struct device *gpiobat, struct gpio_callback *cb, uint32_t pins) 124 | { 125 | uint32_t res = gpio_pin_get(charging_dev, BAT_CHA); 126 | battery_update_charging_status(res != 1U); 127 | } 128 | 129 | void button_pressed_isr(const struct device *gpiobtn, struct gpio_callback *cb, uint32_t pins) 130 | { 131 | display_wake_up(); 132 | backlight_enable(true); 133 | k_timer_start(&display_off_timer, DISPLAY_TIMEOUT, K_NO_WAIT); 134 | 135 | gui_handle_button_event(); 136 | } 137 | 138 | void touch_tap_isr(const struct device *touch_dev, struct sensor_trigger *tap) 139 | { 140 | if (sensor_sample_fetch(touch_dev) < 0) { 141 | LOG_ERR("Touch sample update error."); 142 | } 143 | 144 | display_wake_up(); 145 | backlight_enable(true); 146 | k_timer_start(&display_off_timer, DISPLAY_TIMEOUT, K_NO_WAIT); 147 | 148 | struct sensor_value gesture_val; 149 | sensor_channel_get(touch_dev, CST816S_CHAN_GESTURE, &gesture_val); 150 | gesture = gesture_val.val1; 151 | sensor_channel_get(touch_dev, CST816S_CHAN_TOUCH_POINT_1, &touch_point); 152 | 153 | LOG_INF("Gesture %d on x=%d, y=%d", gesture, touch_point.val1, touch_point.val2); 154 | gui_handle_touch_event(touch_dev, gesture); 155 | } 156 | 157 | /* ********** ************** ********** */ 158 | -------------------------------------------------------------------------------- /app/hypnos/src/fonts/rubik/OFL.txt: -------------------------------------------------------------------------------- 1 | Copyright 2015 The Rubik Project Authors, 2 | 3 | This Font Software is licensed under the SIL Open Font License, Version 1.1. 4 | This license is copied below, and is also available with a FAQ at: 5 | http://scripts.sil.org/OFL 6 | 7 | 8 | ----------------------------------------------------------- 9 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 10 | ----------------------------------------------------------- 11 | 12 | PREAMBLE 13 | The goals of the Open Font License (OFL) are to stimulate worldwide 14 | development of collaborative font projects, to support the font creation 15 | efforts of academic and linguistic communities, and to provide a free and 16 | open framework in which fonts may be shared and improved in partnership 17 | with others. 18 | 19 | The OFL allows the licensed fonts to be used, studied, modified and 20 | redistributed freely as long as they are not sold by themselves. The 21 | fonts, including any derivative works, can be bundled, embedded, 22 | redistributed and/or sold with any software provided that any reserved 23 | names are not used by derivative works. The fonts and derivatives, 24 | however, cannot be released under any other type of license. The 25 | requirement for fonts to remain under this license does not apply 26 | to any document created using the fonts or their derivatives. 27 | 28 | DEFINITIONS 29 | "Font Software" refers to the set of files released by the Copyright 30 | Holder(s) under this license and clearly marked as such. This may 31 | include source files, build scripts and documentation. 32 | 33 | "Reserved Font Name" refers to any names specified as such after the 34 | copyright statement(s). 35 | 36 | "Original Version" refers to the collection of Font Software components as 37 | distributed by the Copyright Holder(s). 38 | 39 | "Modified Version" refers to any derivative made by adding to, deleting, 40 | or substituting -- in part or in whole -- any of the components of the 41 | Original Version, by changing formats or by porting the Font Software to a 42 | new environment. 43 | 44 | "Author" refers to any designer, engineer, programmer, technical 45 | writer or other person who contributed to the Font Software. 46 | 47 | PERMISSION & CONDITIONS 48 | Permission is hereby granted, free of charge, to any person obtaining 49 | a copy of the Font Software, to use, study, copy, merge, embed, modify, 50 | redistribute, and sell modified and unmodified copies of the Font 51 | Software, subject to the following conditions: 52 | 53 | 1) Neither the Font Software nor any of its individual components, 54 | in Original or Modified Versions, may be sold by itself. 55 | 56 | 2) Original or Modified Versions of the Font Software may be bundled, 57 | redistributed and/or sold with any software, provided that each copy 58 | contains the above copyright notice and this license. These can be 59 | included either as stand-alone text files, human-readable headers or 60 | in the appropriate machine-readable metadata fields within text or 61 | binary files as long as those fields can be easily viewed by the user. 62 | 63 | 3) No Modified Version of the Font Software may use the Reserved Font 64 | Name(s) unless explicit written permission is granted by the corresponding 65 | Copyright Holder. This restriction only applies to the primary font name as 66 | presented to the users. 67 | 68 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 69 | Software shall not be used to promote, endorse or advertise any 70 | Modified Version, except to acknowledge the contribution(s) of the 71 | Copyright Holder(s) and the Author(s) or with their explicit written 72 | permission. 73 | 74 | 5) The Font Software, modified or unmodified, in part or in whole, 75 | must be distributed entirely under this license, and must not be 76 | distributed under any other license. The requirement for fonts to 77 | remain under this license does not apply to any document created 78 | using the Font Software. 79 | 80 | TERMINATION 81 | This license becomes null and void if any of the above conditions are 82 | not met. 83 | 84 | DISCLAIMER 85 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 86 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 87 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 88 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 89 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 90 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 91 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 92 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 93 | OTHER DEALINGS IN THE FONT SOFTWARE. 94 | -------------------------------------------------------------------------------- /app/hypnos/src/fonts/rubik/Rubik-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/albsod/pinetime-hypnos/8c634b89e0dc742042f6fad8515234618ecc1fa1/app/hypnos/src/fonts/rubik/Rubik-Regular.ttf -------------------------------------------------------------------------------- /app/hypnos/src/gfx.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Endian Technologies AB 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | #include 8 | #include "gfx.h" 9 | #include "log.h" 10 | #include "version.h" 11 | #include 12 | 13 | /* ********** Macros and constants ********** */ 14 | LV_FONT_DECLARE(rubik_regular_68); 15 | LV_FONT_DECLARE(rubik_regular_34); 16 | 17 | #define BAT_LABEL_MARGIN 3 18 | #define TIME_LABEL_VALIGNMENT -25 19 | #define DATE_LABEL_VALIGNMENT 30 20 | /* ********** ******* ********** */ 21 | 22 | /* ********** Variables ********** */ 23 | static lv_obj_t *battery_label; 24 | static lv_obj_t *bt_label; 25 | static lv_obj_t *time_label; 26 | static lv_obj_t *date_label; 27 | static lv_obj_t *info_label; 28 | static lv_style_t style; 29 | static lv_style_t style_time; 30 | static lv_style_t style_date; 31 | 32 | /* ********** Functions ********** */ 33 | void gfx_init(void) 34 | { 35 | /* Create styles for time, date and the rest */ 36 | lv_style_init(&style); 37 | lv_style_init(&style_time); 38 | lv_style_init(&style_date); 39 | 40 | /* Default style */ 41 | lv_style_set_text_color(&style, LV_STATE_DEFAULT, LV_COLOR_WHITE); 42 | lv_style_set_text_font(&style, LV_STATE_DEFAULT, &lv_font_montserrat_22); 43 | 44 | /* Battery label */ 45 | battery_label = lv_label_create(lv_scr_act(), NULL); 46 | lv_obj_add_style(battery_label, LV_LABEL_PART_MAIN, &style); 47 | lv_label_set_text(battery_label, ""); 48 | 49 | /* Bluetooth label */ 50 | bt_label = lv_label_create(lv_scr_act(), NULL); 51 | lv_obj_align(bt_label, NULL, LV_ALIGN_IN_TOP_LEFT, 6, 4); 52 | lv_obj_add_style(bt_label, LV_LABEL_PART_MAIN, &style); 53 | lv_label_set_text(bt_label, LV_SYMBOL_WIFI); 54 | 55 | /* Time label and style */ 56 | lv_style_set_text_font(&style_time, LV_STATE_DEFAULT, &rubik_regular_68); 57 | lv_style_set_text_color(&style_time, LV_STATE_DEFAULT, LV_COLOR_WHITE); 58 | time_label = lv_label_create(lv_scr_act(), NULL); 59 | lv_obj_add_style(time_label, LV_LABEL_PART_MAIN, &style_time); 60 | lv_label_set_text(time_label, "00:00"); 61 | lv_obj_align(time_label, NULL, LV_ALIGN_CENTER, 0, TIME_LABEL_VALIGNMENT); 62 | 63 | /* Date label and style */ 64 | lv_style_set_text_color(&style_date, LV_STATE_DEFAULT, LV_COLOR_YELLOW); 65 | lv_style_set_text_font(&style_date, LV_STATE_DEFAULT, &rubik_regular_34); 66 | date_label = lv_label_create(lv_scr_act(), NULL); 67 | lv_obj_add_style(date_label, LV_LABEL_PART_MAIN, &style_date); 68 | lv_label_set_text(date_label, "Mon 10 Jan"); 69 | lv_obj_align(date_label, NULL, LV_ALIGN_CENTER, 0, DATE_LABEL_VALIGNMENT); 70 | LOG_DBG("Graphics init: Done"); 71 | 72 | /* Info label */ 73 | info_label = lv_label_create(lv_scr_act(), NULL); 74 | lv_obj_add_style(info_label, LV_LABEL_PART_MAIN, &style); 75 | if (strlen(FW_VERSION) < 10) { 76 | lv_label_set_text(info_label, "Hypnos " FW_VERSION "\n\n" 77 | "This is Free Software" "\n" 78 | "without any warranty." "\n\n" 79 | "https://github.com/" "\n" 80 | "endian-albin/" "\n" 81 | "pinetime-hypnos"); 82 | } else { 83 | lv_label_set_text(info_label, "Hypnos" "\n" 84 | FW_VERSION "\n\n" 85 | "This is Free Software" "\n" 86 | "without any warranty." "\n" 87 | "https://github.com/" "\n" 88 | "endian-albin/" "\n" 89 | "pinetime-hypnos"); 90 | } 91 | lv_obj_set_hidden(info_label, true); 92 | } 93 | 94 | void gfx_update(void) 95 | { 96 | lv_task_handler(); 97 | } 98 | 99 | void gfx_time_set_label(char *str) 100 | { 101 | lv_label_set_text(time_label, str); 102 | lv_obj_align(time_label, NULL, LV_ALIGN_CENTER, 0, TIME_LABEL_VALIGNMENT); 103 | } 104 | 105 | void gfx_date_set_label(char *str) 106 | { 107 | lv_label_set_text(date_label, str); 108 | lv_obj_align(date_label, NULL, LV_ALIGN_CENTER, 0, DATE_LABEL_VALIGNMENT); 109 | } 110 | 111 | void gfx_bt_set_label(enum bt_symbol s) 112 | { 113 | switch (s) { 114 | case BT_ADVERTISING_ON: 115 | lv_label_set_text(bt_label, LV_SYMBOL_WIFI); 116 | break; 117 | case BT_CONNECTED: 118 | lv_label_set_text(bt_label, LV_SYMBOL_BLUETOOTH); 119 | break; 120 | default: 121 | lv_label_set_text(bt_label, ""); 122 | } 123 | } 124 | 125 | void gfx_battery_set_label(enum battery_symbol s) 126 | { 127 | switch (s) { 128 | case BAT_CHARGE: 129 | lv_label_set_text(battery_label, LV_SYMBOL_CHARGE); 130 | lv_obj_align(battery_label, NULL, LV_ALIGN_IN_TOP_RIGHT, -BAT_LABEL_MARGIN, BAT_LABEL_MARGIN); 131 | break; 132 | case BAT_FULL: 133 | lv_label_set_text(battery_label, LV_SYMBOL_BATTERY_FULL); 134 | break; 135 | case BAT_3: 136 | lv_label_set_text(battery_label, LV_SYMBOL_BATTERY_3); 137 | break; 138 | case BAT_2: 139 | lv_label_set_text(battery_label, LV_SYMBOL_BATTERY_2); 140 | break; 141 | case BAT_1: 142 | lv_label_set_text(battery_label, LV_SYMBOL_BATTERY_1); 143 | break; 144 | default: 145 | lv_label_set_text(battery_label, LV_SYMBOL_BATTERY_EMPTY); 146 | } 147 | lv_obj_align(battery_label, NULL, LV_ALIGN_IN_TOP_RIGHT, -BAT_LABEL_MARGIN, 0); 148 | } 149 | 150 | void gfx_show_info(void) 151 | { 152 | lv_obj_set_hidden(time_label, true); 153 | lv_obj_set_hidden(date_label, true); 154 | lv_obj_set_hidden(bt_label, true); 155 | lv_obj_set_hidden(info_label, false); 156 | } 157 | 158 | void gfx_show_watch(void) 159 | { 160 | lv_obj_set_hidden(time_label, false); 161 | lv_obj_set_hidden(date_label, false); 162 | lv_obj_set_hidden(bt_label, false); 163 | lv_obj_set_hidden(info_label, true); 164 | } 165 | -------------------------------------------------------------------------------- /app/hypnos/src/gui.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Endian Technologies AB 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | #include 8 | #include "gfx.h" 9 | #include "gui.h" 10 | #include "clock.h" 11 | #include "battery.h" 12 | #include "bt.h" 13 | #include "log.h" 14 | 15 | static enum screen sc; 16 | 17 | void gui_handle_touch_event(const struct device *touch_dev, enum cst816s_gesture gesture) 18 | { 19 | switch (gesture) { 20 | case SLIDE_UP ... SLIDE_RIGHT: 21 | if (sc == WATCH) { 22 | gfx_show_info(); 23 | sc = INFO; 24 | break; 25 | } 26 | sc = WATCH; 27 | /* Fallthough */ 28 | default: 29 | clock_increment_local_time(); 30 | clock_show_time(); 31 | battery_show_status(); 32 | gfx_show_watch(); 33 | } 34 | gfx_update(); 35 | } 36 | 37 | int gui_handle_button_event(void) 38 | { 39 | clock_increment_local_time(); 40 | clock_show_time(); 41 | battery_show_status(); 42 | gfx_update(); 43 | 44 | return 1; 45 | } 46 | -------------------------------------------------------------------------------- /app/hypnos/src/log.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Endian Technologies AB 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | #include 8 | 9 | LOG_MODULE_REGISTER(hypnos, LOG_LEVEL_INF); 10 | -------------------------------------------------------------------------------- /app/hypnos/src/main.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Hypnos: Smartwatch firmware for the PineTime 3 | * Copyright (c) 2020 Endian Technologies AB 4 | * 5 | * This is free software with ABSOLUTELY NO WARRANTY. 6 | * 7 | * SPDX-License-Identifier: Apache-2.0 8 | */ 9 | 10 | #include 11 | #include "backlight.h" 12 | #include "battery.h" 13 | #include "bt.h" 14 | #include "clock.h" 15 | #include "cts_sync.h" 16 | #include "display.h" 17 | #include "event_handler.h" 18 | #include "gfx.h" 19 | #include "log.h" 20 | #ifdef CONFIG_BOOTLOADER_MCUBOOT 21 | #include "dfu/mcuboot.h" 22 | #endif 23 | #ifdef CONFIG_MCUMGR_CMD_FS_MGMT 24 | #include 25 | #include 26 | #include "fs_mgmt/fs_mgmt.h" 27 | #include 28 | #endif 29 | #ifdef CONFIG_MCUMGR_CMD_OS_MGMT 30 | #include "os_mgmt/os_mgmt.h" 31 | #endif 32 | #ifdef CONFIG_MCUMGR_CMD_IMG_MGMT 33 | #include "img_mgmt/img_mgmt.h" 34 | #endif 35 | 36 | #ifdef CONFIG_MCUMGR_CMD_FS_MGMT 37 | FS_LITTLEFS_DECLARE_DEFAULT_CONFIG(cstorage); 38 | static struct fs_mount_t littlefs_mnt = { 39 | .type = FS_LITTLEFS, 40 | .fs_data = &cstorage, 41 | .storage_dev = (void *)FLASH_AREA_ID(storage), 42 | .mnt_point = "/lfs" 43 | }; 44 | #endif 45 | 46 | /* ******** Functions ******** */ 47 | void main(void) 48 | { 49 | LOG_INF("Welcome to Hypnos!"); 50 | LOG_INF("This is free software with ABSOLUTELY NO WARRANTY."); 51 | 52 | gfx_init(); 53 | clock_init(); 54 | battery_init(); 55 | display_init(); 56 | event_handler_init(); 57 | gfx_update(); 58 | backlight_init(); 59 | 60 | /* Register the built-in mcumgr command handlers. */ 61 | #ifdef CONFIG_MCUMGR_CMD_FS_MGMT 62 | int rc = fs_mount(&littlefs_mnt); 63 | if (rc < 0) { 64 | LOG_ERR("Error mounting littlefs [%d]", rc); 65 | } 66 | 67 | fs_mgmt_register_group(); 68 | #endif 69 | #ifdef CONFIG_MCUMGR_CMD_OS_MGMT 70 | os_mgmt_register_group(); 71 | #endif 72 | #ifdef CONFIG_MCUMGR_CMD_IMG_MGMT 73 | img_mgmt_register_group(); 74 | #endif 75 | bt_init(); 76 | 77 | while (true) { 78 | k_cpu_idle(); 79 | #ifdef CONFIG_LOG 80 | k_msleep(1); /* Allows messages to show up in the console */ 81 | #endif 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /app/hypnos/watch_photo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/albsod/pinetime-hypnos/8c634b89e0dc742042f6fad8515234618ecc1fa1/app/hypnos/watch_photo.jpg -------------------------------------------------------------------------------- /app/include/battery.h: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | #ifndef BATTERY_H__ 7 | #define BATTERY_H__ 8 | 9 | 10 | #ifdef __cplusplus 11 | extern "C" { 12 | #endif 13 | 14 | #include 15 | 16 | #ifdef __cplusplus 17 | } 18 | #endif 19 | 20 | /** @brief Battery events. */ 21 | enum battery_evt { 22 | BATTERY_EVT_CHARGING, /* Ext power connected and charging. */ 23 | BATTERY_EVT_NOT_CHARGING, /* Ext power connected, not charging. */ 24 | BATTERY_EVT_DISCONNECTED, /* Ext power not connected. */ 25 | }; 26 | 27 | typedef void (*battery_callback_t)(enum battery_evt); 28 | 29 | /** @brief Battery monitor initialization. 30 | * 31 | * Initializes ADC and gpios for monitoring external power state. Callback is 32 | * called from interrupt context. 33 | * 34 | * @param callback Notifies ext power state changes. Can be NULL. 35 | * 36 | * @return 0 on success, negative number on error. 37 | */ 38 | int battery_init(battery_callback_t callback); 39 | 40 | /** @brief Read raw ADC value. 41 | * 42 | * Must be called from thread context. 43 | * 44 | * @param raw Location to put raw value. 45 | * 46 | * @return 0 on success, negative number on error. 47 | */ 48 | int battery_read(int16_t *raw); 49 | 50 | /** @brief Convert raw value to millivolts. 51 | * 52 | * @param raw Raw value. 53 | * @return Value in millivolts. 54 | */ 55 | int battery_raw_to_mv(int16_t raw); 56 | 57 | /** @brief Coonvert millivolts to pptt (points per ten thousend) 58 | * 59 | * @param mv Millivots. 60 | * @return pptt value calculated based on estimated discharge curve. 61 | */ 62 | int battery_mv_to_ppt(int mv); 63 | 64 | /** @brief Check if external power is connected. 65 | * 66 | * @return true if connected. 67 | */ 68 | bool battery_is_powered(void); 69 | 70 | /** @brief Check if charging is active. 71 | * 72 | * @return true if charging. 73 | */ 74 | bool battery_is_charging(void); 75 | 76 | #endif /* BATTERY_H__ */ 77 | -------------------------------------------------------------------------------- /app/include/drivers/sensor/cst816s.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Stephane Dorre 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | #ifndef ZEPHYR_INCLUDE_DRIVERS_SENSOR_CTS816A_H_ 8 | #define ZEPHYR_INCLUDE_DRIVERS_SENSOR_CTS816A_H_ 9 | 10 | #include 11 | 12 | enum cst816s_gesture { 13 | NONE, 14 | SLIDE_UP, 15 | SLIDE_DOWN, 16 | SLIDE_LEFT, 17 | SLIDE_RIGHT, 18 | CLICK, 19 | DOUBLE_CLICK, 20 | LONG_PRESS, 21 | }; 22 | 23 | enum cst816s_action { 24 | DOWN = 0, 25 | UP = 1, 26 | CONTACT = 2, 27 | }; 28 | 29 | enum cst816s_channel { 30 | CST816S_CHAN_GESTURE = SENSOR_CHAN_PRIV_START, 31 | CST816S_CHAN_TOUCH_POINT_1, 32 | CST816S_CHAN_TOUCH_POINT_2, 33 | }; 34 | 35 | /* If necessary at some point */ 36 | /* 37 | enum cst816s_attribute { 38 | CST816S_ATTR_XXX = SENSOR_ATTR_PRIV_START, 39 | CST816S_ATTR_YYY, 40 | }; 41 | 42 | enum cts816s_trigger { 43 | CST816S_TRIG_XXX = SENSOR_TRIG_PRIV_START, 44 | CST816S_TRIG_YYY, 45 | }; 46 | */ 47 | 48 | #endif /* ZEPHYR_INCLUDE_DRIVERS_SENSOR_CTS816A_H_ */ -------------------------------------------------------------------------------- /app/pkglist: -------------------------------------------------------------------------------- 1 | accountsservice 2 | acl 3 | acpid 4 | adduser 5 | adwaita-icon-theme 6 | apparmor 7 | apport 8 | apport-symptoms 9 | apt 10 | apt-utils 11 | at 12 | at-spi2-core 13 | base-files 14 | base-passwd 15 | bash 16 | bash-completion 17 | bc 18 | bcache-tools 19 | bind9-host 20 | binutils 21 | binutils-common:amd64 22 | binutils-x86-64-linux-gnu 23 | blt 24 | bsdmainutils 25 | bsdutils 26 | btrfs-progs 27 | btrfs-tools 28 | busybox-initramfs 29 | busybox-static 30 | byobu 31 | bzip2 32 | ca-certificates 33 | ccache 34 | cloud-guest-utils 35 | cloud-init 36 | cloud-initramfs-copymods 37 | cloud-initramfs-dyn-netconf 38 | cmake 39 | cmake-data 40 | command-not-found 41 | command-not-found-data 42 | console-setup 43 | console-setup-linux 44 | coreutils 45 | cpio 46 | cpp 47 | cpp-7 48 | cron 49 | cryptsetup 50 | cryptsetup-bin 51 | curl 52 | dash 53 | dbus 54 | dbus-x11 55 | dconf-gsettings-backend:amd64 56 | dconf-service 57 | debconf 58 | debconf-i18n 59 | debianutils 60 | device-tree-compiler 61 | dfu-util 62 | diffutils 63 | dirmngr 64 | distro-info-data 65 | dmeventd 66 | dmidecode 67 | dmsetup 68 | dns-root-data 69 | dnsmasq-base 70 | dnsutils 71 | dosfstools 72 | dpkg 73 | e2fsprogs 74 | eatmydata 75 | ebtables 76 | ed 77 | eject 78 | ethtool 79 | fdisk 80 | file 81 | findutils 82 | fontconfig 83 | fontconfig-config 84 | fonts-dejavu-core 85 | fonts-droid-fallback 86 | fonts-lato 87 | fonts-lmodern 88 | fonts-noto-mono 89 | fonts-texgyre 90 | fonts-ubuntu-console 91 | friendly-recovery 92 | ftp 93 | fuse 94 | gawk 95 | gcc 96 | gcc-7 97 | gcc-7-base:amd64 98 | gcc-7-multilib 99 | gcc-8-base:amd64 100 | gcc-multilib 101 | gdisk 102 | geoip-database 103 | gettext-base 104 | ghostscript 105 | ghostscript-x 106 | gir1.2-glib-2.0:amd64 107 | git 108 | git-man 109 | glib-networking:amd64 110 | glib-networking-common 111 | glib-networking-services 112 | gnupg 113 | gnupg-l10n 114 | gnupg-utils 115 | gperf 116 | gpg 117 | gpg-agent 118 | gpg-wks-client 119 | gpg-wks-server 120 | gpgconf 121 | gpgsm 122 | gpgv 123 | grep 124 | groff-base 125 | gsettings-desktop-schemas 126 | gsfonts 127 | gtk-update-icon-cache 128 | gv 129 | gzip 130 | hdparm 131 | hicolor-icon-theme 132 | hostname 133 | htop 134 | humanity-icon-theme 135 | info 136 | init 137 | init-system-helpers 138 | initramfs-tools 139 | initramfs-tools-bin 140 | initramfs-tools-core 141 | install-info 142 | iproute2 143 | iptables 144 | iputils-ping 145 | iputils-tracepath 146 | irqbalance 147 | isc-dhcp-client 148 | isc-dhcp-common 149 | iso-codes 150 | javascript-common 151 | kbd 152 | keyboard-configuration 153 | klibc-utils 154 | kmod 155 | krb5-locales 156 | landscape-common 157 | language-selector-common 158 | latexmk 159 | less 160 | lib32asan4 161 | lib32atomic1 162 | lib32cilkrts5 163 | lib32gcc-7-dev 164 | lib32gcc1 165 | lib32gomp1 166 | lib32itm1 167 | lib32mpx2 168 | lib32quadmath0 169 | lib32stdc++6 170 | lib32ubsan0 171 | libaccountsservice0:amd64 172 | libacl1:amd64 173 | libapparmor1:amd64 174 | libapt-inst2.0:amd64 175 | libapt-pkg5.0:amd64 176 | libarchive13:amd64 177 | libargon2-0:amd64 178 | libasan4:amd64 179 | libasn1-8-heimdal:amd64 180 | libassuan0:amd64 181 | libasyncns0:amd64 182 | libatk-bridge2.0-0:amd64 183 | libatk1.0-0:amd64 184 | libatk1.0-data 185 | libatm1:amd64 186 | libatomic1:amd64 187 | libatspi2.0-0:amd64 188 | libattr1:amd64 189 | libaudit-common 190 | libaudit1:amd64 191 | libauthen-sasl-perl 192 | libavahi-client3:amd64 193 | libavahi-common-data:amd64 194 | libavahi-common3:amd64 195 | libbind9-160:amd64 196 | libbinutils:amd64 197 | libblkid1:amd64 198 | libbsd0:amd64 199 | libbz2-1.0:amd64 200 | libc-bin 201 | libc-dev-bin 202 | libc6:amd64 203 | libc6-dev:amd64 204 | libc6-dev-i386 205 | libc6-dev-x32 206 | libc6-i386 207 | libc6-x32 208 | libcairo-gobject2:amd64 209 | libcairo2:amd64 210 | libcap-ng0:amd64 211 | libcap2:amd64 212 | libcap2-bin 213 | libcc1-0:amd64 214 | libcilkrts5:amd64 215 | libcolord2:amd64 216 | libcom-err2:amd64 217 | libcroco3:amd64 218 | libcryptsetup12:amd64 219 | libcups2:amd64 220 | libcupsfilters1:amd64 221 | libcupsimage2:amd64 222 | libcurl3-gnutls:amd64 223 | libcurl4:amd64 224 | libdata-dump-perl 225 | libdatrie1:amd64 226 | libdb5.3:amd64 227 | libdbus-1-3:amd64 228 | libdconf1:amd64 229 | libdebconfclient0:amd64 230 | libdevmapper-event1.02.1:amd64 231 | libdevmapper1.02.1:amd64 232 | libdns-export1100 233 | libdns1100:amd64 234 | libdrm-amdgpu1:amd64 235 | libdrm-common 236 | libdrm-intel1:amd64 237 | libdrm-nouveau2:amd64 238 | libdrm-radeon1:amd64 239 | libdrm2:amd64 240 | libdumbnet1:amd64 241 | libeatmydata1:amd64 242 | libedit2:amd64 243 | libelf1:amd64 244 | libencode-locale-perl 245 | libepoxy0:amd64 246 | liberror-perl 247 | libestr0:amd64 248 | libevent-2.1-6:amd64 249 | libexpat1:amd64 250 | libext2fs2:amd64 251 | libfastjson4:amd64 252 | libfdisk1:amd64 253 | libffi6:amd64 254 | libfile-basedir-perl 255 | libfile-desktopentry-perl 256 | libfile-listing-perl 257 | libfile-mimeinfo-perl 258 | libflac8:amd64 259 | libfont-afm-perl 260 | libfontconfig1:amd64 261 | libfontenc1:amd64 262 | libfreetype6:amd64 263 | libfribidi0:amd64 264 | libfuse2:amd64 265 | libgcc-7-dev:amd64 266 | libgcc1:amd64 267 | libgcrypt20:amd64 268 | libgdbm-compat4:amd64 269 | libgdbm5:amd64 270 | libgdk-pixbuf2.0-0:amd64 271 | libgdk-pixbuf2.0-bin 272 | libgdk-pixbuf2.0-common 273 | libgeoip1:amd64 274 | libgirara-gtk3-3:amd64 275 | libgirepository-1.0-1:amd64 276 | libgl1:amd64 277 | libgl1-mesa-dri:amd64 278 | libgl1-mesa-glx:amd64 279 | libglapi-mesa:amd64 280 | libglib2.0-0:amd64 281 | libglib2.0-data 282 | libglvnd0:amd64 283 | libglx-mesa0:amd64 284 | libglx0:amd64 285 | libgmp10:amd64 286 | libgnutls30:amd64 287 | libgomp1:amd64 288 | libgpg-error0:amd64 289 | libgpm2:amd64 290 | libgraphite2-3:amd64 291 | libgs9:amd64 292 | libgs9-common 293 | libgssapi-krb5-2:amd64 294 | libgssapi3-heimdal:amd64 295 | libgtk-3-0:amd64 296 | libgtk-3-bin 297 | libgtk-3-common 298 | libharfbuzz-icu0:amd64 299 | libharfbuzz0b:amd64 300 | libhcrypto4-heimdal:amd64 301 | libheimbase1-heimdal:amd64 302 | libheimntlm0-heimdal:amd64 303 | libhogweed4:amd64 304 | libhtml-form-perl 305 | libhtml-format-perl 306 | libhtml-parser-perl 307 | libhtml-tagset-perl 308 | libhtml-tree-perl 309 | libhttp-cookies-perl 310 | libhttp-daemon-perl 311 | libhttp-date-perl 312 | libhttp-message-perl 313 | libhttp-negotiate-perl 314 | libhx509-5-heimdal:amd64 315 | libice6:amd64 316 | libicu60:amd64 317 | libidn11:amd64 318 | libidn2-0:amd64 319 | libijs-0.35:amd64 320 | libio-html-perl 321 | libio-socket-ssl-perl 322 | libip4tc0:amd64 323 | libip6tc0:amd64 324 | libipc-system-simple-perl 325 | libiptc0:amd64 326 | libirs160:amd64 327 | libisc-export169:amd64 328 | libisc169:amd64 329 | libisccc160:amd64 330 | libisccfg160:amd64 331 | libisl19:amd64 332 | libisns0:amd64 333 | libitm1:amd64 334 | libjbig0:amd64 335 | libjbig2dec0:amd64 336 | libjpeg-turbo8:amd64 337 | libjpeg8:amd64 338 | libjs-jquery 339 | libjson-c3:amd64 340 | libjson-glib-1.0-0:amd64 341 | libjson-glib-1.0-common 342 | libjsoncpp1:amd64 343 | libk5crypto3:amd64 344 | libkeyutils1:amd64 345 | libklibc 346 | libkmod2:amd64 347 | libkpathsea6:amd64 348 | libkrb5-26-heimdal:amd64 349 | libkrb5-3:amd64 350 | libkrb5support0:amd64 351 | libksba8:amd64 352 | liblcms2-2:amd64 353 | libldap-2.4-2:amd64 354 | libldap-common 355 | libllvm8:amd64 356 | liblocale-gettext-perl 357 | liblsan0:amd64 358 | liblvm2app2.2:amd64 359 | liblvm2cmd2.02:amd64 360 | liblwp-mediatypes-perl 361 | liblwp-protocol-https-perl 362 | liblwres160:amd64 363 | liblxc-common 364 | liblxc1 365 | liblz4-1:amd64 366 | liblzma5:amd64 367 | liblzo2-2:amd64 368 | libmagic-mgc 369 | libmagic1:amd64 370 | libmailtools-perl 371 | libmnl0:amd64 372 | libmount1:amd64 373 | libmpc3:amd64 374 | libmpdec2:amd64 375 | libmpfr6:amd64 376 | libmpx2:amd64 377 | libmspack0:amd64 378 | libncurses5:amd64 379 | libncursesw5:amd64 380 | libnet-dbus-perl 381 | libnet-http-perl 382 | libnet-smtp-ssl-perl 383 | libnet-ssleay-perl 384 | libnetfilter-conntrack3:amd64 385 | libnettle6:amd64 386 | libnewt0.52:amd64 387 | libnfnetlink0:amd64 388 | libnghttp2-14:amd64 389 | libnih1:amd64 390 | libnotify4:amd64 391 | libnpth0:amd64 392 | libnspr4:amd64 393 | libnss-systemd:amd64 394 | libnss3:amd64 395 | libntfs-3g88 396 | libnuma1:amd64 397 | libogg0:amd64 398 | libp11-kit0:amd64 399 | libpam-cap:amd64 400 | libpam-modules:amd64 401 | libpam-modules-bin 402 | libpam-runtime 403 | libpam-systemd:amd64 404 | libpam0g:amd64 405 | libpango-1.0-0:amd64 406 | libpangocairo-1.0-0:amd64 407 | libpangoft2-1.0-0:amd64 408 | libpaper-utils 409 | libpaper1:amd64 410 | libparted2:amd64 411 | libpcap0.8:amd64 412 | libpci3:amd64 413 | libpciaccess0:amd64 414 | libpcre3:amd64 415 | libperl5.26:amd64 416 | libpipeline1:amd64 417 | libpixman-1-0:amd64 418 | libplymouth4:amd64 419 | libpng16-16:amd64 420 | libpolkit-agent-1-0:amd64 421 | libpolkit-backend-1-0:amd64 422 | libpolkit-gobject-1-0:amd64 423 | libpoppler-glib8:amd64 424 | libpoppler73:amd64 425 | libpopt0:amd64 426 | libpotrace0 427 | libprocps6:amd64 428 | libproxy1v5:amd64 429 | libpsl5:amd64 430 | libptexenc1:amd64 431 | libpulse0:amd64 432 | libpulsedsp:amd64 433 | libpython-stdlib:amd64 434 | libpython2.7-minimal:amd64 435 | libpython2.7-stdlib:amd64 436 | libpython3-stdlib:amd64 437 | libpython3.6:amd64 438 | libpython3.6-minimal:amd64 439 | libpython3.6-stdlib:amd64 440 | libquadmath0:amd64 441 | libreadline5:amd64 442 | libreadline7:amd64 443 | librest-0.7-0:amd64 444 | librhash0:amd64 445 | libroken18-heimdal:amd64 446 | librsvg2-2:amd64 447 | librsvg2-common:amd64 448 | librtmp1:amd64 449 | libruby2.5:amd64 450 | libsasl2-2:amd64 451 | libsasl2-modules:amd64 452 | libsasl2-modules-db:amd64 453 | libseccomp2:amd64 454 | libselinux1:amd64 455 | libsemanage-common 456 | libsemanage1:amd64 457 | libsensors4:amd64 458 | libsepol1:amd64 459 | libsigsegv2:amd64 460 | libslang2:amd64 461 | libsm6:amd64 462 | libsmartcols1:amd64 463 | libsndfile1:amd64 464 | libsoup-gnome2.4-1:amd64 465 | libsoup2.4-1:amd64 466 | libsqlite3-0:amd64 467 | libss2:amd64 468 | libssl1.0.0:amd64 469 | libssl1.1:amd64 470 | libstdc++6:amd64 471 | libsynctex1:amd64 472 | libsystemd0:amd64 473 | libtasn1-6:amd64 474 | libtcl8.6:amd64 475 | libtexlua52:amd64 476 | libtexluajit2:amd64 477 | libtext-charwidth-perl 478 | libtext-iconv-perl 479 | libtext-wrapi18n-perl 480 | libthai-data 481 | libthai0:amd64 482 | libtie-ixhash-perl 483 | libtiff5:amd64 484 | libtimedate-perl 485 | libtinfo5:amd64 486 | libtk8.6:amd64 487 | libtry-tiny-perl 488 | libtsan0:amd64 489 | libubsan0:amd64 490 | libudev1:amd64 491 | libunistring2:amd64 492 | libunwind8:amd64 493 | liburi-perl 494 | libusb-1.0-0:amd64 495 | libutempter0:amd64 496 | libuuid1:amd64 497 | libuv1:amd64 498 | libvorbis0a:amd64 499 | libvorbisenc2:amd64 500 | libwayland-client0:amd64 501 | libwayland-cursor0:amd64 502 | libwayland-egl1:amd64 503 | libwind0-heimdal:amd64 504 | libwrap0:amd64 505 | libwww-perl 506 | libwww-robotrules-perl 507 | libx11-6:amd64 508 | libx11-data 509 | libx11-protocol-perl 510 | libx11-xcb1:amd64 511 | libx32asan4 512 | libx32atomic1 513 | libx32cilkrts5 514 | libx32gcc-7-dev 515 | libx32gcc1 516 | libx32gomp1 517 | libx32itm1 518 | libx32quadmath0 519 | libx32stdc++6 520 | libx32ubsan0 521 | libxau6:amd64 522 | libxaw7:amd64 523 | libxcb-dri2-0:amd64 524 | libxcb-dri3-0:amd64 525 | libxcb-glx0:amd64 526 | libxcb-present0:amd64 527 | libxcb-render0:amd64 528 | libxcb-shape0:amd64 529 | libxcb-shm0:amd64 530 | libxcb-sync1:amd64 531 | libxcb1:amd64 532 | libxcomposite1:amd64 533 | libxcursor1:amd64 534 | libxdamage1:amd64 535 | libxdmcp6:amd64 536 | libxext6:amd64 537 | libxfixes3:amd64 538 | libxft2:amd64 539 | libxi6:amd64 540 | libxinerama1:amd64 541 | libxkbcommon0:amd64 542 | libxml-parser-perl 543 | libxml-twig-perl 544 | libxml-xpathengine-perl 545 | libxml2:amd64 546 | libxmlsec1:amd64 547 | libxmlsec1-openssl:amd64 548 | libxmu6:amd64 549 | libxmuu1:amd64 550 | libxpm4:amd64 551 | libxrandr2:amd64 552 | libxrender1:amd64 553 | libxshmfence1:amd64 554 | libxslt1.1:amd64 555 | libxss1:amd64 556 | libxt6:amd64 557 | libxtables12:amd64 558 | libxtst6:amd64 559 | libxv1:amd64 560 | libxxf86dga1:amd64 561 | libxxf86vm1:amd64 562 | libyaml-0-2:amd64 563 | libzstd1:amd64 564 | libzzip-0-13:amd64 565 | linux-base 566 | linux-libc-dev:amd64 567 | lmodern 568 | locales 569 | login 570 | logrotate 571 | lsb-base 572 | lsb-release 573 | lshw 574 | lsof 575 | ltrace 576 | lvm2 577 | lxcfs 578 | lxd 579 | lxd-client 580 | make 581 | man-db 582 | manpages 583 | mawk 584 | mdadm 585 | mime-support 586 | mlocate 587 | mount 588 | mtr-tiny 589 | multiarch-support 590 | nano 591 | ncurses-base 592 | ncurses-bin 593 | ncurses-term 594 | net-tools 595 | netbase 596 | netcat-openbsd 597 | netplan.io 598 | networkd-dispatcher 599 | ninja-build 600 | notification-daemon 601 | nplan 602 | ntfs-3g 603 | open-iscsi 604 | open-vm-tools 605 | openssh-client 606 | openssh-server 607 | openssh-sftp-server 608 | openssl 609 | overlayroot 610 | parted 611 | passwd 612 | pastebinit 613 | patch 614 | pciutils 615 | perl 616 | perl-base 617 | perl-modules-5.26 618 | perl-openssl-defaults:amd64 619 | pinentry-curses 620 | plymouth 621 | plymouth-theme-ubuntu-text 622 | policykit-1 623 | pollinate 624 | poppler-data 625 | popularity-contest 626 | powermgmt-base 627 | preview-latex-style 628 | procps 629 | psmisc 630 | publicsuffix 631 | pulseaudio-utils 632 | python 633 | python-apt-common 634 | python-minimal 635 | python-pip-whl 636 | python2.7 637 | python2.7-minimal 638 | python3 639 | python3-apport 640 | python3-apt 641 | python3-asn1crypto 642 | python3-attr 643 | python3-automat 644 | python3-blinker 645 | python3-certifi 646 | python3-cffi-backend 647 | python3-chardet 648 | python3-click 649 | python3-colorama 650 | python3-commandnotfound 651 | python3-configobj 652 | python3-constantly 653 | python3-cryptography 654 | python3-dbus 655 | python3-debconf 656 | python3-debian 657 | python3-distro-info 658 | python3-distupgrade 659 | python3-distutils 660 | python3-gdbm:amd64 661 | python3-gi 662 | python3-httplib2 663 | python3-hyperlink 664 | python3-idna 665 | python3-incremental 666 | python3-jinja2 667 | python3-json-pointer 668 | python3-jsonpatch 669 | python3-jsonschema 670 | python3-jwt 671 | python3-lib2to3 672 | python3-markupsafe 673 | python3-minimal 674 | python3-netifaces 675 | python3-newt:amd64 676 | python3-oauthlib 677 | python3-openssl 678 | python3-pam 679 | python3-pip 680 | python3-pkg-resources 681 | python3-problem-report 682 | python3-pyasn1 683 | python3-pyasn1-modules 684 | python3-requests 685 | python3-requests-unixsocket 686 | python3-serial 687 | python3-service-identity 688 | python3-setuptools 689 | python3-six 690 | python3-software-properties 691 | python3-systemd 692 | python3-tk:amd64 693 | python3-twisted 694 | python3-twisted-bin:amd64 695 | python3-update-manager 696 | python3-urllib3 697 | python3-wheel 698 | python3-yaml 699 | python3-zope.interface 700 | python3.6 701 | python3.6-minimal 702 | rake 703 | readline-common 704 | rsync 705 | rsyslog 706 | ruby 707 | ruby-did-you-mean 708 | ruby-minitest 709 | ruby-net-telnet 710 | ruby-power-assert 711 | ruby-test-unit 712 | ruby2.5 713 | rubygems-integration 714 | run-one 715 | screen 716 | sed 717 | sensible-utils 718 | shared-mime-info 719 | snapd 720 | software-properties-common 721 | sosreport 722 | squashfs-tools 723 | ssh-import-id 724 | strace 725 | sudo 726 | systemd 727 | systemd-sysv 728 | sysvinit-utils 729 | t1utils 730 | tar 731 | tcl 732 | tcl8.6 733 | tcpdump 734 | telnet 735 | tex-common 736 | tex-gyre 737 | texlive-base 738 | texlive-binaries 739 | texlive-fonts-recommended 740 | texlive-latex-base 741 | texlive-latex-extra 742 | texlive-latex-recommended 743 | texlive-pictures 744 | texlive-plain-generic 745 | time 746 | tipa 747 | tk 748 | tk8.6 749 | tk8.6-blt2.5 750 | tmux 751 | tzdata 752 | ubuntu-advantage-tools 753 | ubuntu-keyring 754 | ubuntu-minimal 755 | ubuntu-mono 756 | ubuntu-release-upgrader-core 757 | ubuntu-server 758 | ubuntu-standard 759 | ubuntu-wsl 760 | ucf 761 | udev 762 | ufw 763 | uidmap 764 | unattended-upgrades 765 | unzip 766 | update-manager-core 767 | update-notifier-common 768 | ureadahead 769 | usbutils 770 | util-linux 771 | uuid-runtime 772 | vim 773 | vim-common 774 | vim-runtime 775 | vim-tiny 776 | wget 777 | whiptail 778 | wslu 779 | x11-common 780 | x11-utils 781 | x11-xserver-utils 782 | xauth 783 | xaw3dg:amd64 784 | xbitmaps 785 | xdelta3 786 | xdg-user-dirs 787 | xdg-utils 788 | xfsprogs 789 | xkb-data 790 | xterm 791 | xxd 792 | xz-utils 793 | xzdec 794 | zathura 795 | zathura-pdf-poppler 796 | zerofree 797 | zip 798 | zlib1g:amd64 799 | -------------------------------------------------------------------------------- /app/subsys/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: Apache-2.0 2 | 3 | add_subdirectory_ifdef(CONFIG_BATTERY battery) -------------------------------------------------------------------------------- /app/subsys/Kconfig: -------------------------------------------------------------------------------- 1 | 2 | rsource "battery/Kconfig" 3 | -------------------------------------------------------------------------------- /app/subsys/battery/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | zephyr_library() 2 | zephyr_library_sources(battery.c) 3 | -------------------------------------------------------------------------------- /app/subsys/battery/Kconfig: -------------------------------------------------------------------------------- 1 | config BATTERY 2 | bool "Battery measurement" 3 | select ADC 4 | select ADC_0 5 | #select CONFIG_NRFX_SAADC #if battery skips adc api 6 | help 7 | Enable battery measurement 8 | 9 | if BATTERY 10 | 11 | config BATTERY_SHELL 12 | bool "Shell commands" 13 | default y 14 | depends on SHELL 15 | help 16 | Enable shell commands for battery 17 | 18 | endif 19 | -------------------------------------------------------------------------------- /app/subsys/battery/battery.c: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | LOG_MODULE_REGISTER(battery); 15 | 16 | #if !defined(CONFIG_BOARD_PINETIME) 17 | #error "Unsupported board." 18 | #endif 19 | 20 | #define USE_NRFX_SAADC 0 21 | #define RESOLUTION 12 22 | 23 | #define CHARGE_PIN 12 24 | #define EXT_POWER_PIN 19 25 | #define DIVIDER 2 /* On board resistor divider (full 2M, top 1M) */ 26 | 27 | 28 | static struct device *adc; 29 | static struct device *gpio; 30 | 31 | #define CHANNEL_ID 1 32 | #define SAADC_OVERSAMPLE NRF_SAADC_OVERSAMPLE_8X 33 | 34 | /* Factors for selecting ADC settings: 35 | * - Max battery voltage: 4200mV 36 | * - After on board divider: 2100mV 37 | * - In order to fit below 600mV internal reference 1/4 gain must be applied. 38 | * - After 1/4 gain max voltage: 525mV (below reference) 39 | */ 40 | static const struct adc_channel_cfg m_1st_channel_cfg = { 41 | .gain = ADC_GAIN_1_4, 42 | .reference = ADC_REF_INTERNAL, 43 | .acquisition_time = ADC_ACQ_TIME(ADC_ACQ_TIME_MICROSECONDS, 20), 44 | .channel_id = CHANNEL_ID, 45 | #if defined(CONFIG_ADC_CONFIGURABLE_INPUTS) 46 | .input_positive = NRF_SAADC_INPUT_AIN7, 47 | #endif 48 | }; 49 | 50 | static struct k_sem sem; 51 | static bool charge_active; 52 | static bool ext_power_active; 53 | static battery_callback_t callback; 54 | 55 | struct battery_level_point { 56 | /** Remaining life at #lvl_mV. */ 57 | uint16_t lvl_pptt; 58 | 59 | /** Battery voltage at #lvl_pptt remaining life. */ 60 | uint16_t lvl_mV; 61 | }; 62 | 63 | static const struct battery_level_point lipo[] = { 64 | 65 | /* "Curve" from https://forum.pine64.org/showthread.php?tid=8147 66 | */ 67 | 68 | { 10000, 4200 }, 69 | { 5000, 3660}, 70 | { 2100, 3600 }, 71 | { 1000, 3560}, 72 | { 0, 3100 }, 73 | }; 74 | 75 | static void saadc_handler(nrfx_saadc_evt_t const * p_event) 76 | { 77 | if (p_event->type == NRFX_SAADC_EVT_DONE) { 78 | k_sem_give(&sem); 79 | } 80 | } 81 | 82 | static int init_nrfx_saadc(void) 83 | { 84 | #define OVERSAMPLE 0 85 | #define RESOLUTION_CFG \ 86 | ((RESOLUTION == 12) ? NRF_SAADC_RESOLUTION_12BIT : \ 87 | ((RESOLUTION == 10) ? NRF_SAADC_RESOLUTION_10BIT : \ 88 | NRF_SAADC_RESOLUTION_14BIT)) 89 | nrfx_err_t err; 90 | 91 | static const nrfx_saadc_channel_t channel = { 92 | .channel_config = { 93 | .resistor_p = NRF_SAADC_RESISTOR_DISABLED, 94 | .resistor_n = NRF_SAADC_RESISTOR_DISABLED, 95 | .gain = NRF_SAADC_GAIN1_6, 96 | .reference = NRF_SAADC_REFERENCE_VDD4, 97 | .acq_time = NRF_SAADC_ACQTIME_20US, 98 | .mode = NRF_SAADC_MODE_SINGLE_ENDED, 99 | .burst = OVERSAMPLE ? NRF_SAADC_BURST_ENABLED : 100 | NRF_SAADC_BURST_DISABLED 101 | }, 102 | .pin_p = NRF_SAADC_INPUT_AIN7, 103 | .pin_n = NRF_SAADC_INPUT_DISABLED, 104 | .channel_index = 0 105 | }; 106 | 107 | IRQ_CONNECT(DT_NORDIC_NRF_SAADC_ADC_0_IRQ_0, 108 | DT_NORDIC_NRF_SAADC_ADC_0_IRQ_0_PRIORITY, 109 | nrfx_isr, nrfx_saadc_irq_handler, 0); 110 | 111 | err = nrfx_saadc_init(5); 112 | if (err != NRFX_SUCCESS) { 113 | LOG_ERR("init failed %x", err); 114 | return -EIO; 115 | } 116 | 117 | err = nrfx_saadc_channels_config(&channel, 1); 118 | if (err != NRFX_SUCCESS) { 119 | LOG_ERR("channel config failed %x", err); 120 | return -EIO; 121 | } 122 | 123 | err = nrfx_saadc_simple_mode_set(BIT(0), 124 | RESOLUTION_CFG, 125 | OVERSAMPLE ? NRF_SAADC_OVERSAMPLE_8X : 126 | NRF_SAADC_OVERSAMPLE_DISABLED, 127 | saadc_handler); 128 | if (err != NRFX_SUCCESS) { 129 | LOG_ERR("mode set failed %x", err); 130 | return -EIO; 131 | } 132 | 133 | 134 | k_sem_init(&sem, 0, 1); 135 | 136 | return 0; 137 | } 138 | 139 | static void timeout_handler(struct k_timer *timer); 140 | 141 | K_TIMER_DEFINE(debounce_timer, timeout_handler, NULL); 142 | 143 | static void timeout_handler(struct k_timer *timer) 144 | { 145 | uint32_t ext_power_val; 146 | uint32_t charge_val; 147 | bool log_action; 148 | 149 | gpio_pin_read(gpio, CHARGE_PIN, &ext_power_val); 150 | 151 | if (ext_power_val) { 152 | log_action = true; 153 | ext_power_active = false; 154 | charge_active = false; 155 | if (callback) { 156 | callback(BATTERY_EVT_DISCONNECTED); 157 | } 158 | } else { 159 | log_action = !ext_power_active; 160 | ext_power_active = true; 161 | gpio_pin_read(gpio, CHARGE_PIN, &charge_val); 162 | charge_active = charge_val ? false : true; 163 | 164 | if (callback) { 165 | callback(charge_active ? BATTERY_EVT_CHARGING : 166 | BATTERY_EVT_NOT_CHARGING); 167 | } 168 | /* If power is connected use timer to periodically check if 169 | * charging is active. 170 | */ 171 | k_timer_start(&debounce_timer, K_MSEC(50), 0); 172 | if (!log_action) { 173 | log_action = !charge_active; 174 | } 175 | } 176 | 177 | if (log_action) { 178 | LOG_INF("power pin change. Ext power %sconnected%s", 179 | ext_power_active ? "" : "not ", 180 | charge_active ? " (charging)" : ""); 181 | } 182 | } 183 | 184 | bool battery_is_powered(void) 185 | { 186 | return ext_power_active; 187 | } 188 | 189 | bool battery_is_charging(void) 190 | { 191 | return charge_active; 192 | } 193 | 194 | static void pin_handler(struct device *gpiob, struct gpio_callback *cb, 195 | uint32_t pins) 196 | { 197 | static bool flop = true; 198 | 199 | gpio_pin_configure(gpio, EXT_POWER_PIN, 200 | GPIO_DIR_IN | GPIO_INT | GPIO_PUD_NORMAL | 201 | GPIO_INT_LEVEL | 202 | (flop ? 203 | GPIO_INT_ACTIVE_HIGH : GPIO_INT_ACTIVE_LOW)); 204 | flop = !flop; 205 | k_timer_stop(&debounce_timer); 206 | k_timer_start(&debounce_timer, K_MSEC(2), 0); 207 | 208 | } 209 | 210 | static struct gpio_callback gpio_cb; 211 | 212 | static int charge_pins_init(void) 213 | { 214 | gpio = device_get_binding("GPIO_0"); 215 | if (gpio == NULL) { 216 | return -ENODEV; 217 | } 218 | 219 | gpio_pin_configure(gpio, CHARGE_PIN, 220 | GPIO_DIR_IN | GPIO_PUD_NORMAL); 221 | 222 | gpio_pin_configure(gpio, EXT_POWER_PIN, 223 | GPIO_DIR_IN | GPIO_INT | GPIO_PUD_NORMAL | 224 | GPIO_INT_LEVEL | GPIO_INT_ACTIVE_LOW); 225 | 226 | gpio_init_callback(&gpio_cb, pin_handler, BIT(EXT_POWER_PIN)); 227 | 228 | gpio_add_callback(gpio, &gpio_cb); 229 | gpio_pin_enable_callback(gpio, EXT_POWER_PIN); 230 | 231 | return 0; 232 | } 233 | 234 | static int adc_monitor_init(void) 235 | { 236 | if (USE_NRFX_SAADC) { 237 | return init_nrfx_saadc(); 238 | } 239 | 240 | adc = device_get_binding(DT_ADC_0_NAME); 241 | if (adc == NULL) { 242 | return -ENODEV; 243 | } 244 | 245 | return adc_channel_setup(adc, &m_1st_channel_cfg); 246 | } 247 | 248 | int battery_init(battery_callback_t cb) 249 | { 250 | int err; 251 | 252 | err = adc_monitor_init(); 253 | if (err < 0) { 254 | return err; 255 | } 256 | 257 | err = charge_pins_init(); 258 | if (err < 0) { 259 | return err; 260 | } 261 | 262 | callback = cb; 263 | 264 | return 0; 265 | } 266 | 267 | 268 | int battery_read(int16_t *raw) 269 | { 270 | int16_t data[1]; 271 | int err; 272 | 273 | const struct adc_sequence sequence = { 274 | .channels = BIT(CHANNEL_ID), 275 | .buffer = data, 276 | .buffer_size = sizeof(data), 277 | .resolution = RESOLUTION, 278 | }; 279 | 280 | if (USE_NRFX_SAADC) { 281 | nrfx_saadc_buffer_set(raw, 1); 282 | nrfx_saadc_mode_trigger(); 283 | return k_sem_take(&sem, K_MSEC(100)); 284 | } 285 | 286 | err = adc_read(adc, &sequence); 287 | if (err < 0) { 288 | return err; 289 | } 290 | 291 | *raw = data[0]; 292 | 293 | return 0; 294 | } 295 | 296 | int battery_raw_to_mv(int16_t raw) 297 | { 298 | return (DIVIDER*600*(((int)raw*4*1000) >> RESOLUTION))/1000; 299 | } 300 | 301 | /* converstion to pptt taken from 302 | * https://github.com/zephyrproject-rtos/zephyr/pull/21606 303 | */ 304 | int battery_mv_to_ppt(int mv) 305 | { 306 | const struct battery_level_point *pb = lipo; 307 | 308 | if (mv >= pb->lvl_mV) { 309 | /* Measured voltage above highest point, cap at maximum. */ 310 | return pb->lvl_pptt; 311 | } 312 | /* Go down to the last point at or below the measured voltage. */ 313 | while ((pb->lvl_pptt > 0) 314 | && (mv < pb->lvl_mV)) { 315 | ++pb; 316 | } 317 | if (mv < pb->lvl_mV) { 318 | /* Below lowest point, cap at minimum */ 319 | return pb->lvl_pptt; 320 | } 321 | 322 | /* Linear interpolation between below and above points. */ 323 | const struct battery_level_point *pa = pb - 1; 324 | 325 | return pb->lvl_pptt 326 | + ((pa->lvl_pptt - pb->lvl_pptt) 327 | * (mv - pb->lvl_mV) 328 | / (pa->lvl_mV - pb->lvl_mV)); 329 | } 330 | 331 | static int cmd_battery_status(const struct shell *shell, 332 | size_t argc, char **argv) 333 | { 334 | int err; 335 | int mv; 336 | int proc; 337 | int16_t raw; 338 | 339 | err = battery_read(&raw); 340 | if (err < 0) { 341 | shell_error(shell, "Failed to read"); 342 | } 343 | mv = battery_raw_to_mv(raw); 344 | proc = battery_mv_to_ppt(mv); 345 | shell_print(shell, "Charger %sconnected%s", 346 | battery_is_powered() ? "" : "not ", 347 | battery_is_charging() ? " (charging)" : ""); 348 | shell_print(shell, "raw:%d(0x%x) mV:%d level:%d.%02d%%", 349 | raw, raw, mv, proc/100, proc % 100); 350 | 351 | return 0; 352 | } 353 | 354 | SHELL_STATIC_SUBCMD_SET_CREATE(sub_battery, 355 | SHELL_CMD_ARG(status, NULL, 356 | "Status", cmd_battery_status, 1, 0), 357 | SHELL_SUBCMD_SET_END 358 | ); 359 | 360 | SHELL_CMD_REGISTER(battery, &sub_battery, "Commands for battery monitor", 361 | NULL); 362 | -------------------------------------------------------------------------------- /app/west.yml: -------------------------------------------------------------------------------- 1 | # The west manifest file for Hypnos. 2 | # 3 | # The per-installation west configuration file specifies the location of this 4 | # manifest file. The "path" option in the [manifest] section of .west/config 5 | # defines the folder that contains west.yml. The path is relative to the folder 6 | # that contains .west. 7 | # 8 | # You can create your own manifest files and put them in any 9 | # repository you want, to create your own custom Pinetime installation. 10 | # For example, you could create a manifest file in your own 11 | # out-of-tree application directory, which would pull the pinetime repository 12 | # in as an ordinary project. 13 | # 14 | # You can pass your manifest repositories to west init when creating a 15 | # new pinetime installation. See the west documentation for more 16 | # information. 17 | 18 | manifest: 19 | defaults: 20 | remote: app 21 | 22 | remotes: 23 | - name: app 24 | url-base: https://github.com/zephyrproject-rtos 25 | 26 | # External projects 27 | # 28 | projects: 29 | - name: cmsis 30 | path: modules/hal/cmsis 31 | revision: 542b2296e6d515b265e25c6b7208e8fea3014f90 32 | - name: hal_nordic 33 | path: modules/hal/nordic 34 | revision: d8a6ea9695ddf792bb18bb6035c13b1daac5d79c 35 | - name: lvgl 36 | path: modules/lib/gui/lvgl 37 | revision: 928b61c7c8ef5f770f10e6fd36d4fea0cf375b5e 38 | - name: littlefs 39 | path: modules/fs/littlefs 40 | revision: 9e4498d1c73009acd84bb36036ee5e2869112a6c 41 | - name: mcuboot 42 | revision: a5d79cf8ccb2c71e68ef32a71d6a2716e831d12e 43 | path: bootloader/mcuboot 44 | - name: mcumgr 45 | revision: 5051f9d900bbb194a23d4ce80f3c6dc6d6780cc2 46 | path: modules/lib/mcumgr 47 | - name: segger 48 | path: modules/debug/segger 49 | revision: 874d9e9696b00c09f9eeefe839028dc25fe44983 50 | - name: tinycbor 51 | path: modules/lib/tinycbor 52 | revision: 40daca97b478989884bffb5226e9ab73ca54b8c4 53 | - name: tinycrypt 54 | path: modules/crypto/tinycrypt 55 | revision: 3e9a49d2672ec01435ffbf0d788db6d95ef28de0 56 | - name: zephyr 57 | west-commands: scripts/west-commands.yml 58 | revision: v2.4.0 59 | self: 60 | path: app 61 | -------------------------------------------------------------------------------- /app/zephyr/module.yml: -------------------------------------------------------------------------------- 1 | build: 2 | cmake: . 3 | kconfig: Kconfig.hypnos 4 | --------------------------------------------------------------------------------