From e3e548ca90bfdac3a1fdefdf2f7dad96c07a96f6 Mon Sep 17 00:00:00 2001 From: archmina Date: Fri, 8 May 2026 03:23:09 +0300 Subject: [PATCH] First --- .gitignore | 1 + .vscode/launch.json | 23 ++ CMakeLists.txt | 11 + NOTES.md | 4 + boards/qalmari/nrf52840_qalmari/Kconfig | 26 ++ .../nrf52840_qalmari/Kconfig.defconfig | 16 + .../nrf52840_qalmari/Kconfig.nrf52840_qalmari | 7 + boards/qalmari/nrf52840_qalmari/board.cmake | 9 + boards/qalmari/nrf52840_qalmari/board.yml | 6 + boards/qalmari/nrf52840_qalmari/fstab.dtsi | 36 ++ .../nrf52840_qalmari-pinctrl.dtsi | 153 ++++++++ .../nrf52840_qalmari/nrf52840_qalmari.dts | 357 ++++++++++++++++++ .../nrf52840_qalmari/nrf52840_qalmari.yaml | 22 ++ .../nrf52840_qalmari_defconfig | 22 ++ .../nrf52840_qalmari/pre_dt_board.cmake | 7 + prj.conf | 45 +++ src/button.cpp | 49 +++ src/button.hpp | 25 ++ src/config.c | 64 ++++ src/config.h | 21 ++ src/config_shell.c | 62 +++ src/imu.c | 148 ++++++++ src/imu.h | 21 ++ src/led.c | 211 +++++++++++ src/led.h | 22 ++ src/led_shell.c | 54 +++ src/main.c | 44 +++ src/shell_commands.c | 18 + src/zbus_channels.c | 20 + src/zbus_channels.h | 39 ++ 30 files changed, 1543 insertions(+) create mode 100644 .gitignore create mode 100644 .vscode/launch.json create mode 100644 CMakeLists.txt create mode 100644 NOTES.md create mode 100755 boards/qalmari/nrf52840_qalmari/Kconfig create mode 100755 boards/qalmari/nrf52840_qalmari/Kconfig.defconfig create mode 100755 boards/qalmari/nrf52840_qalmari/Kconfig.nrf52840_qalmari create mode 100755 boards/qalmari/nrf52840_qalmari/board.cmake create mode 100755 boards/qalmari/nrf52840_qalmari/board.yml create mode 100755 boards/qalmari/nrf52840_qalmari/fstab.dtsi create mode 100755 boards/qalmari/nrf52840_qalmari/nrf52840_qalmari-pinctrl.dtsi create mode 100755 boards/qalmari/nrf52840_qalmari/nrf52840_qalmari.dts create mode 100755 boards/qalmari/nrf52840_qalmari/nrf52840_qalmari.yaml create mode 100755 boards/qalmari/nrf52840_qalmari/nrf52840_qalmari_defconfig create mode 100755 boards/qalmari/nrf52840_qalmari/pre_dt_board.cmake create mode 100644 prj.conf create mode 100644 src/button.cpp create mode 100644 src/button.hpp create mode 100644 src/config.c create mode 100644 src/config.h create mode 100644 src/config_shell.c create mode 100644 src/imu.c create mode 100644 src/imu.h create mode 100644 src/led.c create mode 100644 src/led.h create mode 100644 src/led_shell.c create mode 100644 src/main.c create mode 100644 src/shell_commands.c create mode 100644 src/zbus_channels.c create mode 100644 src/zbus_channels.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c795b05 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +build \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..a58d285 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,23 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Zephyr Remote Cortex-Debug (OpenOCD)", + "type": "cortex-debug", + "request": "launch", + + "executable": "${workspaceFolder}/build/zephyr/zephyr.elf", + + "servertype": "external", + "gdbTarget": "192.168.1.154:3333", + + "gdbPath": "/usr/bin/arm-none-eabi-gdb", + + "runToEntryPoint": "main", + + "preLaunchCommands": [ + "monitor reset halt" + ] + } + ] +} \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..4bce37c --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,11 @@ +# Copyright (c) 2022 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.20.0) +set(BOARD_ROOT ${CMAKE_CURRENT_SOURCE_DIR}) + +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(meditation) + +FILE(GLOB app_sources src/*.c) +target_sources(app PRIVATE ${app_sources}) diff --git a/NOTES.md b/NOTES.md new file mode 100644 index 0000000..8382e39 --- /dev/null +++ b/NOTES.md @@ -0,0 +1,4 @@ +socat serial +``` +nc 192.168.1.154 7777 +``` \ No newline at end of file diff --git a/boards/qalmari/nrf52840_qalmari/Kconfig b/boards/qalmari/nrf52840_qalmari/Kconfig new file mode 100755 index 0000000..2e282bf --- /dev/null +++ b/boards/qalmari/nrf52840_qalmari/Kconfig @@ -0,0 +1,26 @@ +# nRF52840 Dongle NRF52840 board configuration + +# Copyright (c) 2018-2023 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +if BOARD_NRF52840_QALMARI + +config BOARD_ENABLE_DCDC + bool "DCDC mode" + select SOC_DCDC_NRF52X + default n + +config BOARD_ENABLE_DCDC_HV + bool "High Voltage DCDC converter" + select SOC_DCDC_NRF52X_HV + default n + + +config BOARD_HAS_NRF5_BOOTLOADER + bool "Board has nRF5 bootloader" + default n + help + If selected, applications are linked so that they can be loaded by Nordic + nRF5 bootloader. + +endif # BOARD_NRF52840_QALMARI diff --git a/boards/qalmari/nrf52840_qalmari/Kconfig.defconfig b/boards/qalmari/nrf52840_qalmari/Kconfig.defconfig new file mode 100755 index 0000000..f2efffb --- /dev/null +++ b/boards/qalmari/nrf52840_qalmari/Kconfig.defconfig @@ -0,0 +1,16 @@ +# nRF52840 Dongle NRF52840 board configuration +# +# Copyright (c) 2018-2023 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: Apache-2.0 + +if BOARD_NRF52840_QALMARI + +config BOARD + default "nrf52840_qalmari" + +config FLASH_LOAD_OFFSET + default 0x12000 + depends on BOARD_HAS_NRF5_BOOTLOADER && (MCUBOOT || !USE_DT_CODE_PARTITION) + +endif # BOARD_NRF52840_QALMARI diff --git a/boards/qalmari/nrf52840_qalmari/Kconfig.nrf52840_qalmari b/boards/qalmari/nrf52840_qalmari/Kconfig.nrf52840_qalmari new file mode 100755 index 0000000..e8aa90e --- /dev/null +++ b/boards/qalmari/nrf52840_qalmari/Kconfig.nrf52840_qalmari @@ -0,0 +1,7 @@ +# nRF52840 Dongle NRF52840 board configuration + +# Copyright (c) 2018 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +config BOARD_NRF52840_QALMARI + select SOC_NRF52840_QIAA diff --git a/boards/qalmari/nrf52840_qalmari/board.cmake b/boards/qalmari/nrf52840_qalmari/board.cmake new file mode 100755 index 0000000..f958789 --- /dev/null +++ b/boards/qalmari/nrf52840_qalmari/board.cmake @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: Apache-2.0 + +board_runner_args(jlink "--device=nRF52840_xxAA" "--speed=4000") +board_runner_args(pyocd "--target=nrf52840" "--frequency=4000000") +include(${ZEPHYR_BASE}/boards/common/nrfjprog.board.cmake) +include(${ZEPHYR_BASE}/boards/common/nrfutil.board.cmake) +include(${ZEPHYR_BASE}/boards/common/jlink.board.cmake) +include(${ZEPHYR_BASE}/boards/common/pyocd.board.cmake) +include(${ZEPHYR_BASE}/boards/common/openocd-nrf5.board.cmake) diff --git a/boards/qalmari/nrf52840_qalmari/board.yml b/boards/qalmari/nrf52840_qalmari/board.yml new file mode 100755 index 0000000..47dab07 --- /dev/null +++ b/boards/qalmari/nrf52840_qalmari/board.yml @@ -0,0 +1,6 @@ +board: + name: nrf52840_qalmari + full_name: nRF52840 Qalmari + vendor: qalmari + socs: + - name: nrf52840 diff --git a/boards/qalmari/nrf52840_qalmari/fstab.dtsi b/boards/qalmari/nrf52840_qalmari/fstab.dtsi new file mode 100755 index 0000000..6e2a1dc --- /dev/null +++ b/boards/qalmari/nrf52840_qalmari/fstab.dtsi @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2019 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/* Flash partition table without support for Nordic nRF5 bootloader */ + +&flash0 { + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + /* The size of this partition ensures that MCUBoot can be built + * with an RTT console, CDC ACM support, and w/o optimizations. + */ + boot_partition: partition@0 { + label = "mcuboot"; + reg = <0x00000000 0x00087000>; + }; + + // slot0_partition: partition@12000 { + // label = "image-0"; + // reg = <0x00012000 0x00075000>; + // }; + slot1_partition: partition@87000 { + label = "image-1"; + reg = <0x00087000 0x00075000>; + }; + storage_partition: partition@fc000 { + label = "storage"; + reg = <0x000fc000 0x00004000>; + }; + }; +}; diff --git a/boards/qalmari/nrf52840_qalmari/nrf52840_qalmari-pinctrl.dtsi b/boards/qalmari/nrf52840_qalmari/nrf52840_qalmari-pinctrl.dtsi new file mode 100755 index 0000000..e8965c6 --- /dev/null +++ b/boards/qalmari/nrf52840_qalmari/nrf52840_qalmari-pinctrl.dtsi @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2022 Nordic Semiconductor + * SPDX-License-Identifier: Apache-2.0 + */ + +&pinctrl { + uart0_default: uart0_default { + group1 { + psels = ; + }; + group2 { + psels = ; + bias-pull-up; + }; + }; + + uart0_sleep: uart0_sleep { + group1 { + psels = , + , + , + ; + low-power-enable; + }; + }; + + i2c0_default: i2c0_default { + group1 { + psels = , ; + }; + }; + + i2c0_sleep: i2c0_sleep { + group1 { + psels = , ; + low-power-enable; + }; + }; + + i2c1_default: i2c1_default { + group1 { + psels = , ; + }; + }; + + i2c1_sleep: i2c1_sleep { + group1 { + psels = , ; + low-power-enable; + }; + }; + + i2s0_default: i2s0_default { + group1 { + psels = , + , + , + ; + }; + }; + + pwm0_default: pwm0_default { + group1 { + psels = , + ; + }; + }; + + pwm0_sleep: pwm0_sleep { + group1 { + psels = , + ; + low-power-enable; + }; + }; + + pwm1_leds_default: pwm1_leds_default { + group1 { + psels = , + , + , + ; + nordic,invert; + }; + }; + + pwm1_leds_sleep: pwm1_leds_sleep { + group1 { + psels = , + , + , + ; + nordic,invert; + low-power-enable; + }; + }; + + pwm2_leds_default: pwm2_leds_default { + group1 { + psels = , + , + , + ; + nordic,invert; + }; + }; + + pwm2_leds_sleep: pwm2_leds_sleep { + group1 { + psels = , + , + , + ; + nordic,invert; + low-power-enable; + }; + }; + + spi0_default: spi0_default { + group1 { + psels = , + , + ; + }; + }; + + spi0_sleep: spi0_sleep { + group1 { + psels = , + , + ; + low-power-enable; + }; + }; + + spi1_default: spi1_default { + group1 { + psels = , + , + ; + }; + }; + + spi1_sleep: spi1_sleep { + group1 { + psels = , + , + ; + low-power-enable; + }; + }; + +}; diff --git a/boards/qalmari/nrf52840_qalmari/nrf52840_qalmari.dts b/boards/qalmari/nrf52840_qalmari/nrf52840_qalmari.dts new file mode 100755 index 0000000..60a0fb1 --- /dev/null +++ b/boards/qalmari/nrf52840_qalmari/nrf52840_qalmari.dts @@ -0,0 +1,357 @@ +/* + * Copyright (c) 2018-2023 Nordic Semiconductor ASA + * Copyright (c) 2017 Linaro Limited + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/dts-v1/; +#include +#include "nrf52840_qalmari-pinctrl.dtsi" +#include +#include + +#include "fstab.dtsi" + +/ { + model = "Nordic NRF52840 QALMARI"; + compatible = "nordic,nrf52840-qalmari"; + + chosen { + zephyr,console = &cdc_acm_uart; + zephyr,shell-uart = &cdc_acm_uart; + zephyr,uart-mcumgr = &cdc_acm_uart; + zephyr,bt-mon-uart = &cdc_acm_uart; + zephyr,bt-c2h-uart = &cdc_acm_uart; + zephyr,sram = &sram0; + zephyr,flash = &flash0; + zephyr,code-partition = &boot_partition; + zephyr,ieee802154 = &ieee802154; + }; + + leds { + compatible = "gpio-leds"; + led1: led_1 { + gpios = <&gpio0 30 GPIO_ACTIVE_LOW>; + label = "LED 1"; + status = "okay"; + }; + led2: led_2 { + gpios = <&gpio0 8 GPIO_ACTIVE_LOW>; + label = "LED 2"; + status = "okay"; + }; + led3: led_3 { + gpios = <&gpio0 20 GPIO_ACTIVE_LOW>; + label = "LED 3"; + status = "okay"; + }; + led4: led_4 { + gpios = <&gpio1 15 GPIO_ACTIVE_LOW>; + label = "LED 4"; + status = "okay"; + }; + led5: led_5 { + gpios = <&gpio1 13 GPIO_ACTIVE_LOW>; + label = "LED 5"; + status = "okay"; + }; + led6: led_6 { + gpios = <&gpio1 10 GPIO_ACTIVE_LOW>; + label = "LED 6"; + status = "okay"; + }; + led7: led_7 { + gpios = <&gpio0 29 GPIO_ACTIVE_LOW>; + label = "LED 7"; + status = "okay"; + }; + led8: led_8 { + gpios = <&gpio0 31 GPIO_ACTIVE_LOW>; + label = "LED 8"; + status = "okay"; + }; + }; + + pwmleds { + compatible = "pwm-leds"; + pwm_led1: pwm_led_1 { + pwms = <&pwm1 0 PWM_MSEC(20) PWM_POLARITY_INVERTED>; + label = "PWM LED 1"; + }; + pwm_led2: pwm_led_2 { + pwms = <&pwm1 1 PWM_MSEC(20) PWM_POLARITY_INVERTED>; + label = "PWM LED 2"; + }; + pwm_led3: pwm_led_3 { + pwms = <&pwm1 2 PWM_MSEC(20) PWM_POLARITY_INVERTED>; + label = "PWM LED 3"; + }; + pwm_led4: pwm_led_4 { + pwms = <&pwm1 3 PWM_MSEC(20) PWM_POLARITY_INVERTED>; + label = "PWM LED 4"; + }; + pwm_led5: pwm_led_5 { + pwms = <&pwm2 0 PWM_MSEC(20) PWM_POLARITY_INVERTED>; + label = "PWM LED 5"; + }; + pwm_led6: pwm_led_6 { + pwms = <&pwm2 1 PWM_MSEC(20) PWM_POLARITY_INVERTED>; + label = "PWM LED 6"; + }; + pwm_led7: pwm_led_7 { + pwms = <&pwm2 2 PWM_MSEC(20) PWM_POLARITY_INVERTED>; + label = "PWM LED 7"; + }; + pwm_led8: pwm_led_8 { + pwms = <&pwm2 3 PWM_MSEC(20) PWM_POLARITY_INVERTED>; + label = "PWM LED 8"; + }; + }; + + pwm { + compatible = "pwm-leds"; + pwm_vibration: pwm_vibration { + pwms = <&pwm0 0 PWM_MSEC(20) PWM_POLARITY_NORMAL>; + label = "PWM vibration motor"; + }; + pwm_buzzer: pwm_buzzer { + pwms = <&pwm0 1 PWM_MSEC(20) PWM_POLARITY_NORMAL>; + label = "PWM buzzer"; + }; + + }; + + buttons { + compatible = "gpio-keys"; + debounce-interval-ms = <100>; + button1: button_1 { + gpios = <&gpio0 17 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>; + label = "Push button switch 1"; + zephyr,code = ; + }; + button2: button_2 { + gpios = <&gpio0 15 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>; + label = "Push button switch 2"; + zephyr,code = ; + }; + }; + + interrupts { + compatible = "gpio-keys"; + interrupt_head: interrupt_head { + gpios = <&gpio0 23 (GPIO_ACTIVE_HIGH)>; + label = "head interrupt pin"; + }; + }; + + aliases { + btn1 = &button1; + btn2 = &button2; + int-head = &interrupt_head; + int-btn-1 = &button1; + int-btn-2 = &button2; + led1 = &led1; + led2 = &led2; + led3 = &led3; + led4 = &led4; + led5 = &led5; + led6 = &led6; + led7 = &led7; + led8 = &led8; + pwm-led1 = &pwm_led1; + pwm-led2 = &pwm_led2; + pwm-led3 = &pwm_led3; + pwm-led4 = &pwm_led4; + pwm-led5 = &pwm_led5; + pwm-led6 = &pwm_led6; + pwm-led7 = &pwm_led7; + pwm-led8 = &pwm_led8; + buzzer = &pwm_buzzer; + vibration = &pwm_vibration; + mcuboot-button0 = &button1; + mcuboot-button1 = &button2; + mcuboot-led0 = &led1; + watchdog0 = &wdt0; + pwm0 = &pwm0; + pwm-leds = &pwm1; + led-strip = &led_strip; + i2c-1 = &i2c1; + uart-0 = &uart0; + }; + + vbatt { + status = "okay"; + compatible = "voltage-divider"; + io-channels = <&adc 2>; + output-ohms = <100000>; + full-ohms = <(100000 + 100000)>; + //power-gpios = <&gpio0 11 GPIO_PULL_UP>; + }; +}; + +&pwm0 { + status = "okay"; + pinctrl-0 = <&pwm0_default>; + pinctrl-1 = <&pwm0_sleep>; + pinctrl-names = "default", "sleep"; +}; + +&pwm1 { + status = "okay"; + pinctrl-0 = <&pwm1_leds_default>; + pinctrl-1 = <&pwm1_leds_sleep>; + pinctrl-names = "default", "sleep"; +}; + +&pwm2 { + status = "okay"; + pinctrl-0 = <&pwm2_leds_default>; + pinctrl-1 = <&pwm2_leds_sleep>; + pinctrl-names = "default", "sleep"; +}; + +&uart0 { + compatible = "nordic,nrf-uarte"; + status = "okay"; + current-speed = <115200>; + pinctrl-0 = <&uart0_default>; + pinctrl-names = "default"; +}; + +&spi0 { + compatible = "nordic,nrf-spi"; + /* Cannot be used together with i2c0. */ + status = "okay"; + pinctrl-0 = <&spi0_default>; + pinctrl-1 = <&spi0_sleep>; + pinctrl-names = "default", "sleep"; + led_strip: ws2812-spi@0 { + compatible = "worldsemi,ws2812-spi"; + reg = <0x0>; + + chain-length = <2>; + color-mapping = ; + spi-one-frame = <0x70>; + spi-zero-frame = <0x40>; + spi-max-frequency = <4000000>; + }; +}; + +&spi1 { + compatible = "nordic,nrf-spi"; + //status = "okay"; + status = "disabled"; + pinctrl-0 = <&spi1_default>; + pinctrl-1 = <&spi1_sleep>; + pinctrl-names = "default", "sleep"; +}; + +&i2c0 { + /* Cannot be used together with spi0. */ + status = "disabled"; + pinctrl-0 = <&i2c0_default>; + pinctrl-names = "default"; + clock-frequency = ; +}; + +&i2c1 { + status = "okay"; + pinctrl-0 = <&i2c1_default>; + pinctrl-names = "default"; + clock-frequency = ; +}; + +zephyr_udc0: &usbd { + compatible = "nordic,nrf-usbd"; + status = "okay"; + + cdc_acm_uart: cdc_acm_uart { + compatible = "zephyr,cdc-acm-uart"; + }; +}; + +&ieee802154 { + status = "okay"; +}; + +&adc { + status = "okay"; +}; + +&gpiote { + status = "okay"; +}; + +&uicr { + nfct-pins-as-gpios; + gpio-as-nreset; +}; + +&gpio0 { + status = "okay"; +}; + +&gpio1 { + status = "okay"; +}; + +&systick { + status = "disabled"; +}; + +&nfct { + status = "disabled"; +}; + +&egu0 { + status = "okay"; +}; + +&ccm { + status = "okay"; +}; + +&cryptocell { + status = "okay"; +}; + +&ecb { + status = "okay"; +}; + +&rng { + status = "okay"; +}; + +&ppi { + status = "okay"; +}; + +&clock { + status = "okay"; +}; + +// RTC0 is not okay anymore! v.4.3.99 +// &rtc0 { +// status = "okay"; +// }; + +&mwu { + status = "okay"; +}; + +&power { + status = "okay"; +}; + +&acl { + status = "okay"; +}; + +// ITM is used for printf debugging and tracing on the SWO pin +&itm { + status = "okay"; +}; diff --git a/boards/qalmari/nrf52840_qalmari/nrf52840_qalmari.yaml b/boards/qalmari/nrf52840_qalmari/nrf52840_qalmari.yaml new file mode 100755 index 0000000..9476abe --- /dev/null +++ b/boards/qalmari/nrf52840_qalmari/nrf52840_qalmari.yaml @@ -0,0 +1,22 @@ +identifier: nrf52840_qalmari +name: NRF52840-QALMARI +type: mcu +arch: arm +ram: 256 +flash: 1024 +toolchain: + - zephyr + - gnuarmemb + - xtools +supported: + - adc + - usb_device + - usb_cdc + - ble + - pwm + - spi + - watchdog + - counter + - netif:openthread + - gpio +vendor: qalmari diff --git a/boards/qalmari/nrf52840_qalmari/nrf52840_qalmari_defconfig b/boards/qalmari/nrf52840_qalmari/nrf52840_qalmari_defconfig new file mode 100755 index 0000000..cc4b76b --- /dev/null +++ b/boards/qalmari/nrf52840_qalmari/nrf52840_qalmari_defconfig @@ -0,0 +1,22 @@ +# SPDX-License-Identifier: Apache-2.0 + +# CONFIG_SOC_SERIES_NRF52X=y +# CONFIG_SOC_NRF52840_QIAA=y +# CONFIG_BOARD_NRF52840_QALMARI=y + +# Disable DCDC converter +CONFIG_BOARD_ENABLE_DCDC=n +CONFIG_BOARD_ENABLE_DCDC_HV=n + +# 32K clock +CONFIG_CLOCK_CONTROL_NRF_K32SRC_RC=y +CONFIG_CLOCK_CONTROL_NRF_K32SRC_RC_CALIBRATION=y + +# Enable MPU +CONFIG_ARM_MPU=y + +# enable GPIO +CONFIG_GPIO=y + +# Enable Zephyr application to be booted by MCUboot +# CONFIG_BOOTLOADER_MCUBOOT=y diff --git a/boards/qalmari/nrf52840_qalmari/pre_dt_board.cmake b/boards/qalmari/nrf52840_qalmari/pre_dt_board.cmake new file mode 100755 index 0000000..3369c21 --- /dev/null +++ b/boards/qalmari/nrf52840_qalmari/pre_dt_board.cmake @@ -0,0 +1,7 @@ +# Copyright (c) 2022 Nordic Semiconductor +# SPDX-License-Identifier: Apache-2.0 + +# Suppress "unique_unit_address_if_enabled" to handle the following overlaps: +# - power@40000000 & clock@40000000 & bprot@40000000 +# - acl@4001e000 & flash-controller@4001e000 +list(APPEND EXTRA_DTC_FLAGS "-Wno-unique_unit_address_if_enabled") diff --git a/prj.conf b/prj.conf new file mode 100644 index 0000000..3607be5 --- /dev/null +++ b/prj.conf @@ -0,0 +1,45 @@ +CONFIG_GPIO=y + +# PWM +CONFIG_PWM=y + +# LED Strip +CONFIG_LED_STRIP=y +CONFIG_SPI=y + +# Serial +CONFIG_SERIAL=y +CONFIG_CONSOLE=y +CONFIG_USB_DEVICE_STACK=y +# CONFIG_USB_DEVICE_STACK_NEXT=y +CONFIG_USB_CDC_ACM=y +CONFIG_CDC_ACM_SERIAL_INITIALIZE_AT_BOOT=n +CONFIG_USBD_LOOPBACK_CLASS=y +CONFIG_UDC_BUF_POOL_SIZE=4096 + +# Shell +CONFIG_SHELL=y +CONFIG_USBD_SHELL=y + +# Storage +CONFIG_NVS=y +CONFIG_FLASH=y +CONFIG_FLASH_MAP=y + +# I2C +CONFIG_I2C=y +CONFIG_I2C_TARGET=y + +# LOG +CONFIG_LOG=y +CONFIG_LOG_DEFAULT_LEVEL=3 +CONFIG_LOG_MODE_IMMEDIATE=y +CONFIG_USBD_LOG_LEVEL_WRN=y +CONFIG_UDC_DRIVER_LOG_LEVEL_WRN=y + +#ZBUS +CONFIG_ZBUS=y +CONFIG_ZBUS_LOG_LEVEL_INF=n +CONFIG_ZBUS_CHANNEL_NAME=y +CONFIG_ZBUS_OBSERVER_NAME=y +CONFIG_ZBUS_MSG_SUBSCRIBER=y \ No newline at end of file diff --git a/src/button.cpp b/src/button.cpp new file mode 100644 index 0000000..ff2719a --- /dev/null +++ b/src/button.cpp @@ -0,0 +1,49 @@ +#include "buttons.hpp" +#include +#include +#include + +LOG_MODULE_REGISTER(buttons, CONFIG_LOG_MAX_LEVEL); +K_WORK_DELAYABLE_DEFINE(debounce_work_btn, Buttons::debounce_cb); +#define DEBOUNCE_TIME_MS 100 + +static const struct gpio_dt_spec Buttons::buttons[Buttons::NR_BUTTONS] = { + GPIO_DT_SPEC_GET_OR(DT_ALIAS(int_btn_1), gpios, {0}), + GPIO_DT_SPEC_GET_OR(DT_ALIAS(int_btn_2), gpios, {0}) +}; +static struct gpio_callback Buttons::buttons_cb_data[Buttons::NR_BUTTONS]; + +Buttons::Buttons() { + buttons_init(); +} + +int Buttons::buttons_init() { + // BUTTON 1 + gpio_pin_configure_dt(&buttons[BUTTON1], GPIO_INPUT); + gpio_pin_interrupt_configure_dt(&(buttons[BUTTON1]), GPIO_INT_EDGE_TO_ACTIVE); + gpio_init_callback(&buttons_cb_data[0], buttons_cb, BIT(buttons[BUTTON1].pin)); + gpio_add_callback(buttons[BUTTON1].port, &buttons_cb_data[0]); + + // BUTTON 2 + gpio_pin_configure_dt(&buttons[BUTTON2], GPIO_INPUT); + gpio_pin_interrupt_configure_dt(&buttons[BUTTON2], GPIO_INT_EDGE_TO_ACTIVE); + gpio_init_callback(&buttons_cb_data[1], buttons_cb, BIT(buttons[BUTTON2].pin)); + gpio_add_callback(buttons[BUTTON2].port, &buttons_cb_data[1]); + + return 0; +} + +static void Buttons::debounce_cb(struct k_work *work) { + if ( gpio_pin_get_dt(&buttons[BUTTON1]) == GPIO_ACTIVE_HIGH || + gpio_pin_get_dt(&buttons[BUTTON2]) == GPIO_ACTIVE_HIGH) { + LOG_DBG("Callback"); + } +} + +static void Buttons::buttons_cb(const struct device *dev, struct gpio_callback *cb, uint32_t pins) { + LOG_DBG("Button:"); + if (k_work_delayable_is_pending(&debounce_work_btn)) { + } else { + k_work_schedule(&debounce_work_btn, K_MSEC(DEBOUNCE_TIME_MS)); + } +} \ No newline at end of file diff --git a/src/button.hpp b/src/button.hpp new file mode 100644 index 0000000..cbef963 --- /dev/null +++ b/src/button.hpp @@ -0,0 +1,25 @@ +#pragma once + + +#include + + +class Buttons { +public: + + enum Buttons_e { + BUTTON1, + BUTTON2, + NR_BUTTONS, + }; + + Buttons(); + ~Buttons(); + int buttons_init(); + static void buttons_cb(const struct device *dev, struct gpio_callback *cb, uint32_t pins); + static void debounce_cb(struct k_work *work); + +private: + static const struct gpio_dt_spec buttons[NR_BUTTONS]; + static struct gpio_callback buttons_cb_data[NR_BUTTONS]; +}; \ No newline at end of file diff --git a/src/config.c b/src/config.c new file mode 100644 index 0000000..c057678 --- /dev/null +++ b/src/config.c @@ -0,0 +1,64 @@ +#include "config.h" + +#include +#include +#include + +#define NVS_ID_CONFIG 1 + +static struct nvs_fs nvs; +static app_config_t ram_config; + +#define CONFIG_ID 0xDEADBEEF +#define CONFIG_VERSION 1 + +static const app_config_t default_config = { + .id = CONFIG_ID, + .version = CONFIG_VERSION, + .device_name = "my-device", + .baud_rate = 115200, + .log_level = 3, + .feature_enabled = false, +}; + +int config_init(void) +{ + struct flash_pages_info page; + const struct device *dev = FIXED_PARTITION_DEVICE(storage_partition); + + flash_get_page_info_by_offs(dev, FIXED_PARTITION_OFFSET(storage_partition), &page); + + nvs.flash_device = dev; + nvs.offset = FIXED_PARTITION_OFFSET(storage_partition); + nvs.sector_size = page.size; + nvs.sector_count = FIXED_PARTITION_SIZE(storage_partition) / page.size; + + int rc = nvs_mount(&nvs); + if (rc) { + return rc; + } + + ssize_t ret = nvs_read(&nvs, NVS_ID_CONFIG, &ram_config, sizeof(ram_config)); + if (ret != sizeof(ram_config) || ram_config.id != CONFIG_ID || ram_config.version != CONFIG_VERSION) { + ram_config = default_config; + nvs_write(&nvs, NVS_ID_CONFIG, &ram_config, sizeof(ram_config)); + } + + return 0; +} + +int config_save(const app_config_t *cfg) +{ + ram_config = *cfg; + return nvs_write(&nvs, NVS_ID_CONFIG, &ram_config, sizeof(ram_config)); +} + +void config_update(void) +{ + nvs_read(&nvs, NVS_ID_CONFIG, &ram_config, sizeof(ram_config)); +} + +void config_get(app_config_t *cfg) +{ + *cfg = ram_config; +} \ No newline at end of file diff --git a/src/config.h b/src/config.h new file mode 100644 index 0000000..82e8fb3 --- /dev/null +++ b/src/config.h @@ -0,0 +1,21 @@ +#ifndef CONFIG_H +#define CONFIG_H + +#include +#include + +typedef struct { + uint32_t id; + uint16_t version; + char device_name[32]; + uint32_t baud_rate; + uint8_t log_level; + bool feature_enabled; +} app_config_t; + +int config_init(void); +int config_save(const app_config_t *cfg); +void config_update(void); +void config_get(app_config_t *cfg); + +#endif /* CONFIG_H */ \ No newline at end of file diff --git a/src/config_shell.c b/src/config_shell.c new file mode 100644 index 0000000..26df794 --- /dev/null +++ b/src/config_shell.c @@ -0,0 +1,62 @@ +#include +#include "config.h" + + +static int cmd_config_read(const struct shell *sh, size_t argc, char **argv) +{ + ARG_UNUSED(argc); + ARG_UNUSED(argv); + + app_config_t cfg; + config_get(&cfg); + + shell_print(sh, "device_name: %s", cfg.device_name); + shell_print(sh, "baud_rate: %u", cfg.baud_rate); + shell_print(sh, "log_level: %u", cfg.log_level); + shell_print(sh, "feature_enabled: %s", cfg.feature_enabled ? "true" : "false"); + + return 0; +} + +static int cmd_config_update(const struct shell *sh, size_t argc, char **argv) +{ + ARG_UNUSED(argc); + ARG_UNUSED(argv); + + config_update(); + shell_print(sh, "Config refreshed from NVS"); + + return 0; +} + +static int cmd_config_baud(const struct shell *sh, size_t argc, char **argv) +{ + if (argc != 2) { + shell_error(sh, "Usage: config baud "); + return -EINVAL; + } + + uint32_t baud = (uint32_t)strtoul(argv[1], NULL, 10); + if (baud == 0) { + shell_error(sh, "Invalid baud rate"); + return -EINVAL; + } + + app_config_t cfg; + config_get(&cfg); + cfg.baud_rate = baud; + config_save(&cfg); + + shell_print(sh, "Baud rate updated to %u", baud); + + return 0; +} + +SHELL_STATIC_SUBCMD_SET_CREATE(config_cmds, + SHELL_CMD(read, NULL, "Print config from RAM", cmd_config_read), + SHELL_CMD(update, NULL, "Refresh RAM config from NVS", cmd_config_update), + SHELL_CMD(baud, NULL, "Set baud rate: config baud ", cmd_config_baud), + SHELL_SUBCMD_SET_END +); + +SHELL_CMD_REGISTER(config, &config_cmds, "Config commands", NULL); \ No newline at end of file diff --git a/src/imu.c b/src/imu.c new file mode 100644 index 0000000..f8d8f84 --- /dev/null +++ b/src/imu.c @@ -0,0 +1,148 @@ +#include "imu.h" +#include "zbus_channels.h" + +#include +#include +#include +#include + +LOG_MODULE_REGISTER(imu, CONFIG_LOG_MAX_LEVEL); +K_WORK_DELAYABLE_DEFINE(debounce_work_imu, debounce_cb); + +#define DEBOUNCE_TIME_MS 100 +#define DOUBLE_TAP_TIME_MS 500 + +/* Starting register for X-axis data (e.g., OUT_X_L register) */ +#define ACCEL_REG_OUT_X_L 0x3B + +static const uint8_t i2c_address = 0b1101001; + +static const struct gpio_dt_spec imu_int = GPIO_DT_SPEC_GET_OR(DT_ALIAS(int_head), gpios, {0}); +static struct gpio_callback imu_cb_data; +static const struct device *const i2c_dev = DEVICE_DT_GET(DT_ALIAS(i2c_1)); + +static uint32_t last_tap = 0; + +void imu_reset() { + const uint8_t PWR_MGMT_1[2] = {0x6B, (uint8_t)(1 << 7)}; + i2c_write(i2c_dev, PWR_MGMT_1, sizeof(PWR_MGMT_1), i2c_address); + k_sleep(K_MSEC(100)); + +} + +int imu_init() { + if (!device_is_ready(i2c_dev)) { + LOG_ERR("I2C device %s is not ready.\n", i2c_dev->name); + return 0; + } + + // imu_reset(); + + // Ensure that Accelerometer is running + const uint8_t PWR_MGMT_1[2] = {0x6B, 0x00}; + const uint8_t PWR_MGMT_2[2] = {0x6C, 0x00}; + + // Accelerometer Configuration + const uint8_t ACCEL_CONFIG_1[2] = {0x1C, 0b00011000}; + const uint8_t ACCEL_CONFIG_2[2] = {0x1D, 0b00000000}; + + // Enable Motion Interrupt + const uint8_t INT_ENABLE[2] = {0x38, 0b00100000}; + + // Set Motion Threshold + const uint8_t ACCEL_WOM_X_THR[2] = {0x20, 0xff}; + const uint8_t ACCEL_WOM_Y_THR[2] = {0x21, 0x8f}; + const uint8_t ACCEL_WOM_Z_THR[2] = {0x22, 0xff}; + + // Set Interrupt Mode & Enable Accelerometer Hardware Intelligence + const uint8_t ACCEL_INTEL_CTRL[2] = {0x69, 0xc0}; + + // Set Frequency of Wake-Up + const uint8_t SMPLRT_DIV[2] = {0x19, 0x00}; + + // Enable Cycle Mode (Accelerometer Low-Power Mode) + const uint8_t PWR_MGMT_1_A[2] = {0x6B, 0x20}; + + int ret; + i2c_write(i2c_dev, PWR_MGMT_1, sizeof(PWR_MGMT_1), i2c_address); + k_sleep(K_MSEC(1)); + i2c_write(i2c_dev, PWR_MGMT_2, sizeof(PWR_MGMT_2), i2c_address); + k_sleep(K_MSEC(1)); + i2c_write(i2c_dev, ACCEL_CONFIG_1, sizeof(ACCEL_CONFIG_1), i2c_address); + k_sleep(K_MSEC(1)); + i2c_write(i2c_dev, ACCEL_CONFIG_2, sizeof(ACCEL_CONFIG_2), i2c_address); + k_sleep(K_MSEC(1)); + i2c_write(i2c_dev, INT_ENABLE, sizeof(INT_ENABLE), i2c_address); + k_sleep(K_MSEC(1)); + // // i2c_write(i2c_dev, ACCEL_WOM_X_THR, sizeof(ACCEL_WOM_X_THR), i2c_address); + // // k_sleep(K_MSEC(1)); + // i2c_write(i2c_dev, ACCEL_WOM_Y_THR, sizeof(ACCEL_WOM_Y_THR), i2c_address); + // k_sleep(K_MSEC(1)); + i2c_write(i2c_dev, ACCEL_WOM_Z_THR, sizeof(ACCEL_WOM_Z_THR), i2c_address); + k_sleep(K_MSEC(1)); + i2c_write(i2c_dev, ACCEL_INTEL_CTRL, sizeof(ACCEL_INTEL_CTRL), i2c_address); + k_sleep(K_MSEC(1)); + i2c_write(i2c_dev, SMPLRT_DIV, sizeof(SMPLRT_DIV), i2c_address); + k_sleep(K_MSEC(1)); + i2c_write(i2c_dev, PWR_MGMT_1_A, sizeof(PWR_MGMT_1_A), i2c_address); + k_sleep(K_MSEC(100)); + + gpio_pin_configure_dt(&(imu_int), GPIO_INPUT); + gpio_pin_interrupt_configure_dt(&(imu_int), GPIO_INT_EDGE_TO_ACTIVE); + gpio_init_callback(&imu_cb_data, imu_cb, BIT(imu_int.pin)); + gpio_add_callback(imu_int.port, &imu_cb_data); + + last_tap = k_uptime_get(); + + return 0; +} + +void imu_read(struct accel_data_t *buf) { + uint8_t raw_accel_data[6]; + + // LIGHT + uint16_t reg = ACCEL_REG_OUT_X_L; + + /* Write register address, then read consecutive data bytes */ + int ret = i2c_write_read(i2c_dev, + i2c_address, + ®, + sizeof(reg), + raw_accel_data, + sizeof(raw_accel_data)); + + if (ret) { + printk("Error: I2C burst read failed (%d)\n", ret); + } else { + /* Combine low and high bytes for each axis */ + buf->x = (int16_t)((raw_accel_data[1] << 8) | raw_accel_data[0]); + buf->y = (int16_t)((raw_accel_data[3] << 8) | raw_accel_data[2]); + buf->z = (int16_t)((raw_accel_data[5] << 8) | raw_accel_data[4]); + } +} + +static void debounce_cb(struct k_work *work) { + if (gpio_pin_get_dt(&imu_int) == GPIO_ACTIVE_HIGH) { + if (k_uptime_get() - last_tap > DOUBLE_TAP_TIME_MS) { + LOG_INF("Tap"); + } + else { + LOG_INF("Double tap"); + struct button_msg_t msg; + msg.button = IMU; + msg.function = DOUBLE_TAP; + zbus_chan_pub(&buttons_data_chan, &msg, K_NO_WAIT); + } + last_tap = k_uptime_get(); + } +} + +static void imu_cb(const struct device *dev, struct gpio_callback *cb, uint32_t pins) { + + /* Check if the work item is already running */ + if (k_work_delayable_is_pending(&debounce_work_imu)) { + /* Do nothing, as the work item is already running */ + } else { + k_work_schedule(&debounce_work_imu, K_MSEC(DEBOUNCE_TIME_MS)); + } +} diff --git a/src/imu.h b/src/imu.h new file mode 100644 index 0000000..f4efb3a --- /dev/null +++ b/src/imu.h @@ -0,0 +1,21 @@ +#ifndef IMU_H +#define IMU_H + +#include + + +struct accel_data_t { + int16_t x; + int16_t y; + int16_t z; +}; + + +int imu_init(); +void imu_read(struct accel_data_t *buf); +static void imu_cb(const struct device *dev, struct gpio_callback *cb, uint32_t pins); +static void debounce_cb(struct k_work *work); + + + +#endif // IMU_H \ No newline at end of file diff --git a/src/led.c b/src/led.c new file mode 100644 index 0000000..68522dd --- /dev/null +++ b/src/led.c @@ -0,0 +1,211 @@ +// LOCAL +#include "led.h" + +// ZEPHYR +#include +#include +#include +#include +#include + +LOG_MODULE_REGISTER(leds, LOG_LEVEL_INF); + +#define STRIP_NODE DT_ALIAS(led_strip) +#define STRIP_NUM_PIXELS DT_PROP(STRIP_NODE, chain_length) +#define STRIP_MAX_BRIGHTNESS 255 + +static const struct device *const strip = DEVICE_DT_GET(STRIP_NODE); +static struct led_rgb pixels[STRIP_NUM_PIXELS]; + +#define NR_LEDS 8 +#define PWM_RESOLUTION 255 + +static const struct pwm_dt_spec led1 = PWM_DT_SPEC_GET(DT_ALIAS(pwm_led1)); +static const struct pwm_dt_spec led2 = PWM_DT_SPEC_GET(DT_ALIAS(pwm_led2)); +static const struct pwm_dt_spec led3 = PWM_DT_SPEC_GET(DT_ALIAS(pwm_led3)); +static const struct pwm_dt_spec led4 = PWM_DT_SPEC_GET(DT_ALIAS(pwm_led4)); +static const struct pwm_dt_spec led5 = PWM_DT_SPEC_GET(DT_ALIAS(pwm_led5)); +static const struct pwm_dt_spec led6 = PWM_DT_SPEC_GET(DT_ALIAS(pwm_led6)); +static const struct pwm_dt_spec led7 = PWM_DT_SPEC_GET(DT_ALIAS(pwm_led7)); +static const struct pwm_dt_spec led8 = PWM_DT_SPEC_GET(DT_ALIAS(pwm_led8)); + +static const struct pwm_dt_spec *leds[NR_LEDS] = {&led1, &led2, &led3, &led4, &led5, &led6, &led7, &led8}; + + +int set_pwm(const struct pwm_dt_spec *dev, uint32_t value, uint32_t max_brightness) { + if ((value <= max_brightness) && (max_brightness > 0)) { + uint32_t pulse_width_ns = value * (dev->period / max_brightness); + int ret = pwm_set_pulse_dt(dev, pulse_width_ns); + + return ret; + } + return -1; +} + +int led_init(void) { + // Checking if devices are ready + for (int i = 0; i < NR_LEDS; i++) { + if (!device_is_ready(leds[i]->dev)) { + LOG_ERR("PWM LEDs not ready"); + return -ENODEV; + } + } + + LOG_INF("PWM LEDs initialized"); + + if (!device_is_ready(strip)) { + LOG_ERR("LED strip device not ready"); + return -ENODEV; + } + + LOG_INF("LED strip initialized with %d pixels", STRIP_NUM_PIXELS); + + return 0; +} + +int led_fade(uint32_t duration) { + uint32_t step_duration = (duration * 1000) / (PWM_RESOLUTION * 2); + + for (int i = 0; i < PWM_RESOLUTION; i++) { + for (int j = 0; j < NR_LEDS; j++) { + set_pwm(leds[j], i, PWM_RESOLUTION); + } + k_sleep(K_USEC(step_duration)); + } + for (int i = 0; i < PWM_RESOLUTION; i++) { + for (int j = 0; j < NR_LEDS; j++) { + set_pwm(leds[j], (PWM_RESOLUTION - i - 1), PWM_RESOLUTION); + } + k_sleep(K_USEC(step_duration)); + } + + return 0; +} + +int led_fade_in(uint32_t duration) { + uint32_t step_duration = (duration * 1000) / (PWM_RESOLUTION); + + for (int i = 0; i < PWM_RESOLUTION; i++) { + for (int j = 0; j < NR_LEDS; j++) { + set_pwm(leds[j], i, PWM_RESOLUTION); + } + k_sleep(K_USEC(step_duration)); + } + return 0; +} + +int led_fade_out(uint32_t duration) { + uint32_t step_duration = (duration * 1000) / (PWM_RESOLUTION); + + for (int i = 0; i < PWM_RESOLUTION; i++) { + for (int j = 0; j < NR_LEDS; j++) { + set_pwm(leds[j], (PWM_RESOLUTION - i - 1), PWM_RESOLUTION); + } + k_sleep(K_USEC(step_duration)); + } + + return 0; +} + +int led_set_progress(float progress) { + uint32_t total_progress = (progress * NR_LEDS * PWM_RESOLUTION); + + for (int i = 0; i < NR_LEDS; i++) { + if (total_progress > PWM_RESOLUTION) { + set_pwm(leds[i], PWM_RESOLUTION, PWM_RESOLUTION); + total_progress -= PWM_RESOLUTION; + } + else { + set_pwm(leds[i], total_progress, PWM_RESOLUTION); + total_progress = 0; + } + } +} + + +/* LED strip */ + +int leds_set_color(uint8_t led_index, float r, float g, float b) { + if (led_index >= STRIP_NUM_PIXELS) { + LOG_ERR("LED index %d out of range (max %d)", led_index, STRIP_NUM_PIXELS - 1); + return -EINVAL; + } + + pixels[led_index].r = r * STRIP_MAX_BRIGHTNESS; + pixels[led_index].g = g * STRIP_MAX_BRIGHTNESS; + pixels[led_index].b = b * STRIP_MAX_BRIGHTNESS; + + return 0; +} + +int leds_set_all(float r, float g, float b) { + for (int i = 0; i < STRIP_NUM_PIXELS; i++) { + pixels[i].r = r * STRIP_MAX_BRIGHTNESS; + pixels[i].g = g * STRIP_MAX_BRIGHTNESS; + pixels[i].b = b * STRIP_MAX_BRIGHTNESS; + } + + return 0; +} + +int leds_update(void) { + int ret = led_strip_update_rgb(strip, pixels, STRIP_NUM_PIXELS); + if (ret) { + LOG_ERR("Failed to update LED strip: %d", ret); + return ret; + } + + return 0; +} + +int leds_clear(void) { + return leds_set_all(0, 0, 0) || leds_update(); +} + + + + + +int leds_fade(uint32_t duration, float r, float g, float b) { + uint32_t step_duration = (duration * 1000) / (STRIP_MAX_BRIGHTNESS * 2); + + for (int i = 0; i < STRIP_MAX_BRIGHTNESS; i++) { + float value = (float)i / STRIP_MAX_BRIGHTNESS; + leds_set_all((value * r), (value * g), (value * b)); + leds_update(); + k_sleep(K_USEC(step_duration)); + } + for (int i = STRIP_MAX_BRIGHTNESS - 1; i >= 0; i--) { + float value = (float)i / STRIP_MAX_BRIGHTNESS; + leds_set_all((value * r), (value * g), (value * b)); + leds_update(); + k_sleep(K_USEC(step_duration)); + } + + return 0; +} + +int leds_fade_in(uint32_t duration, float r, float g, float b) { + uint32_t step_duration = (duration * 1000) / (STRIP_MAX_BRIGHTNESS); + + for (int i = 0; i < STRIP_MAX_BRIGHTNESS; i++) { + float value = (float)i / STRIP_MAX_BRIGHTNESS; + leds_set_all((value * r), (value * g), (value * b)); + leds_update(); + k_sleep(K_USEC(step_duration)); + } + return 0; +} + +int leds_fade_out(uint32_t duration, float r, float g, float b) { + uint32_t step_duration = (duration * 1000) / (STRIP_MAX_BRIGHTNESS); + + for (int i = STRIP_MAX_BRIGHTNESS - 1; i >= 0; i--) { + float value = (float)i / STRIP_MAX_BRIGHTNESS; + leds_set_all((value * r), (value * g), (value * b)); + leds_update(); + k_sleep(K_USEC(step_duration)); + } + + return 0; +} diff --git a/src/led.h b/src/led.h new file mode 100644 index 0000000..34567bf --- /dev/null +++ b/src/led.h @@ -0,0 +1,22 @@ +#ifndef LED_H +#define LED_H + + +#include + + +int led_init(void); +int led_fade(uint32_t duration); +int led_fade_in(uint32_t duration); +int led_fade_out(uint32_t duration); +int led_set_progress(float progress); +int leds_set_color(uint8_t led_index, float r, float g, float b); +int leds_set_all(float r, float g, float b); +int leds_update(void); +int leds_clear(void); +int leds_fade(uint32_t duration, float r, float g, float b); +int leds_fade_in(uint32_t duration, float r, float g, float b); +int leds_fade_out(uint32_t duration, float r, float g, float b); + + +#endif // LED_H \ No newline at end of file diff --git a/src/led_shell.c b/src/led_shell.c new file mode 100644 index 0000000..e50df94 --- /dev/null +++ b/src/led_shell.c @@ -0,0 +1,54 @@ +#include +#include "led.h" + +/* single led */ + +static int cmd_led_fade(const struct shell *sh, size_t argc, char **argv) +{ + if (argc != 2) { shell_error(sh, "Usage: led fade "); return -EINVAL; } + return led_fade(atoi(argv[1])); +} + +static int cmd_led_progress(const struct shell *sh, size_t argc, char **argv) +{ + if (argc != 2) { shell_error(sh, "Usage: led progress <0-100>"); return -EINVAL; } + return led_set_progress(atoi(argv[1]) / 100.0f); +} + +SHELL_STATIC_SUBCMD_SET_CREATE(led_cmds, + SHELL_CMD(fade, NULL, "Fade over ", cmd_led_fade), + SHELL_CMD(progress, NULL, "Set progress <0.0-1.0>", cmd_led_progress), + SHELL_SUBCMD_SET_END +); +SHELL_CMD_REGISTER(led, &led_cmds, "Single LED commands", NULL); + +/* addressable leds */ + +static int cmd_leds_all(const struct shell *sh, size_t argc, char **argv) +{ + if (argc != 4) { shell_error(sh, "Usage: leds all (0-255)"); return -EINVAL; } + leds_set_all(atoi(argv[1]) / 255.0f, atoi(argv[2]) / 255.0f, atoi(argv[3]) / 255.0f); + return leds_update(); +} + +static int cmd_leds_fade(const struct shell *sh, size_t argc, char **argv) +{ + if (argc != 5) { shell_error(sh, "Usage: leds fade (0-255)"); return -EINVAL; } + return leds_fade(atoi(argv[1]), + atoi(argv[2]) / 255.0f, + atoi(argv[3]) / 255.0f, + atoi(argv[4]) / 255.0f); +} + +static int cmd_leds_clear(const struct shell *sh, size_t argc, char **argv) +{ + return leds_clear(); +} + +SHELL_STATIC_SUBCMD_SET_CREATE(leds_cmds, + SHELL_CMD(all, NULL, "Set all ", cmd_leds_all), + SHELL_CMD(clear, NULL, "Clear all", cmd_leds_clear), + SHELL_CMD(fade, NULL, "Fade to ", cmd_leds_fade), + SHELL_SUBCMD_SET_END +); +SHELL_CMD_REGISTER(leds, &leds_cmds, "Addressable LED commands", NULL); \ No newline at end of file diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..4d78863 --- /dev/null +++ b/src/main.c @@ -0,0 +1,44 @@ +#include +#include +#include +#include + +// LOCAL +#include "led.h" +#include "config.h" +#include "imu.h" + +/* 1000 msec = 1 sec */ +#define SLEEP_TIME_MS 1000 + +LOG_MODULE_REGISTER(main, LOG_LEVEL_INF); + +int main(void) +{ + int ret; + + ret = usb_enable(NULL); + if (ret != 0) { + LOG_ERR("Failed to enable USB: %d", ret); + return ret; + } + + ret = config_init(); + if (ret != 0) { + LOG_ERR("Failed to init config: %d", ret); + return ret; + } + + ret = led_init(); + ret = imu_init(); + + while (1) { + // for (int i = 0; i < 10000; i++) { + // led_set_progress((float)i / 10000.0); + // k_msleep(1); + // } + // leds_fade(1000, 0.1, 0.0, 0.05); + k_msleep(SLEEP_TIME_MS); + } + return 0; +} diff --git a/src/shell_commands.c b/src/shell_commands.c new file mode 100644 index 0000000..5ec0c1a --- /dev/null +++ b/src/shell_commands.c @@ -0,0 +1,18 @@ +#include +#include +#include "config.h" + +LOG_MODULE_REGISTER(commands); + +void foo(const struct shell *sh, size_t argc, char **argv) +{ + LOG_INF("info message"); + LOG_WRN("warning message"); + LOG_ERR("err message"); + ARG_UNUSED(argc); + ARG_UNUSED(argv); + shell_print(sh, "foo executed"); +} + +SHELL_CMD_REGISTER(foo, NULL, "foo command", foo); + diff --git a/src/zbus_channels.c b/src/zbus_channels.c new file mode 100644 index 0000000..37a5e49 --- /dev/null +++ b/src/zbus_channels.c @@ -0,0 +1,20 @@ +#include "zbus_channels.h" + + +ZBUS_CHAN_DEFINE(buttons_data_chan, /* Name */ + struct button_msg_t, /* Message type */ + + NULL, /* Validator */ + NULL, /* User data */ + ZBUS_OBSERVERS(), /* observers */ + ZBUS_MSG_INIT(.button = ERROR, .function = NONE) /* Initial value */ +); + +ZBUS_CHAN_DEFINE(geiger_data_chan, /* Name */ + struct geiger_msg_t, /* Message type */ + + NULL, /* Validator */ + NULL, /* User data */ + ZBUS_OBSERVERS(), /* observers */ + ZBUS_MSG_INIT(.cps = 0) /* Initial value */ +); \ No newline at end of file diff --git a/src/zbus_channels.h b/src/zbus_channels.h new file mode 100644 index 0000000..eed86ea --- /dev/null +++ b/src/zbus_channels.h @@ -0,0 +1,39 @@ +#ifndef ZBUS_CHANNEL +#define ZBUS_CHANNEL + + +#include +#include + +#include + + +typedef enum { + NONE, + TAP, + DOUBLE_TAP +} function_e; + +typedef enum { + ERROR = -1, + BTN1, + BTN2, + IMU +} buttons_e; + +struct button_msg_t { + buttons_e button; + function_e function; +}; + +struct geiger_msg_t { + uint32_t cps; // clicks per second +}; + + +ZBUS_CHAN_DECLARE(buttons_data_chan); +ZBUS_CHAN_DECLARE(geiger_data_chan); + + + +#endif // ZBUS_CHANNEL \ No newline at end of file