diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index 949a1061..bee7a817 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -56,13 +56,19 @@ include_directories(src/applications) include_directories(src/ui/watchfaces) if (CONFIG_DT_HAS_NORDIC_NPM1300_ENABLED) -add_subdirectory(src/fuel_gauge) + add_subdirectory(src/fuel_gauge) else() -add_subdirectory(src/basic_battery) + add_subdirectory(src/basic_battery) endif() -target_sources(app PRIVATE src/main.c) +# TODO target_sources(app PRIVATE src/zsw_clock.c) +#if (CONFIG_RTC) +# target_sources(app PRIVATE src/zsw_rtc.c) +#else() +#endif() + +target_sources(app PRIVATE src/main.c) target_sources(app PRIVATE src/zsw_cpu_freq.c) target_sources(app PRIVATE src/zsw_retained_ram_storage.c) target_sources(app PRIVATE src/zsw_coredump.c) diff --git a/app/Kconfig b/app/Kconfig index 32159cdd..e8142fa0 100644 --- a/app/Kconfig +++ b/app/Kconfig @@ -66,6 +66,30 @@ menu "ZSWatch" prompt "Activate the application 'PPT remote'" default n + menu "Configuration" + menu "Compass" + depends on APPLICATIONS_USE_COMPASS + + config APPLICATIONS_CONFIGURATION_COMPASS_REFRESH_INTERVAL_MS + int + prompt "Refresh interval in milliseconds" + default 50 + + config APPLICATIONS_CONFIGURATION_COMPASS_CALIBRATION_TIME_S + int + prompt "Calibration time in seconds" + default 30 + endmenu + + menu "Sensors Summary" + depends on APPLICATIONS_USE_SENSORS_SUMMARY + + config APPLICATIONS_CONFIGURATION_SENSORS_SUMMARY_REFRESH_INTERVAL_MS + int + prompt "Refresh interval in milliseconds" + default 100 + endmenu + endmenu endmenu menu "Watchface" @@ -107,29 +131,6 @@ menu "ZSWatch" default 85 endmenu - menu "Sensors Summary" - depends on APPLICATIONS_USE_SENSORS_SUMMARY - - config DEFAULT_CONFIGURATION_SENSORS_SUMMARY_REFRESH_INTERVAL_MS - int - prompt "Refresh interval in milliseconds" - default 100 - endmenu - - menu "Compass" - depends on APPLICATIONS_USE_COMPASS - - config DEFAULT_CONFIGURATION_COMPASS_REFRESH_INTERVAL_MS - int - prompt "Refresh interval in milliseconds" - default 50 - - config DEFAULT_CONFIGURATION_COMPASS_CALIBRATION_TIME_S - int - prompt "Calibration time in seconds" - default 30 - endmenu - menu "Power Management" config POWER_MANAGEMENT_IDLE_TIMEOUT_SECONDS int diff --git a/app/boards/arm/zswatch_nrf5340/zswatch_nrf5340_cpuapp_3.overlay b/app/boards/arm/zswatch_nrf5340/zswatch_nrf5340_cpuapp_3.overlay index 4273d1fb..4266ef71 100644 --- a/app/boards/arm/zswatch_nrf5340/zswatch_nrf5340_cpuapp_3.overlay +++ b/app/boards/arm/zswatch_nrf5340/zswatch_nrf5340_cpuapp_3.overlay @@ -83,6 +83,10 @@ }; }; +&uart0 { + status = "disabled"; +}; + &i2c1 { bmi270: bmi270@68 { compatible = "bosch,bmi270-plus"; diff --git a/app/boards/arm/zswatch_nrf5340/zswatch_nrf5340_cpuapp_4.overlay b/app/boards/arm/zswatch_nrf5340/zswatch_nrf5340_cpuapp_4.overlay index 4df49748..3402482f 100644 --- a/app/boards/arm/zswatch_nrf5340/zswatch_nrf5340_cpuapp_4.overlay +++ b/app/boards/arm/zswatch_nrf5340/zswatch_nrf5340_cpuapp_4.overlay @@ -127,6 +127,10 @@ }; +&uart0 { + status = "disabled"; +}; + &i2c1 { bmi270: bmi270@68 { compatible = "bosch,bmi270-plus"; diff --git a/app/boards/arm/zswatch_nrf5340/zswatch_nrf5340_cpuapp_5.conf b/app/boards/arm/zswatch_nrf5340/zswatch_nrf5340_cpuapp_5.conf index 65296226..a51dbde9 100644 --- a/app/boards/arm/zswatch_nrf5340/zswatch_nrf5340_cpuapp_5.conf +++ b/app/boards/arm/zswatch_nrf5340/zswatch_nrf5340_cpuapp_5.conf @@ -1,9 +1,7 @@ CONFIG_ZSWATCH_PCB_REV=5 CONFIG_BT_DIS_HW_REV_STR="5" -#--------------- # FLASH -#----------------- CONFIG_FLASH=y CONFIG_FLASH_PAGE_LAYOUT=y CONFIG_STREAM_FLASH=y @@ -46,3 +44,6 @@ CONFIG_SETTINGS_NVS=y CONFIG_NVS=y CONFIG_DEBUG_COREDUMP_BACKEND_OTHER=y + +CONFIG_RTC=y +CONFIG_RTC_UPDATE=y diff --git a/app/boards/arm/zswatch_nrf5340/zswatch_nrf5340_cpuapp_5.overlay b/app/boards/arm/zswatch_nrf5340/zswatch_nrf5340_cpuapp_5.overlay index eea3d212..60d3c70b 100644 --- a/app/boards/arm/zswatch_nrf5340/zswatch_nrf5340_cpuapp_5.overlay +++ b/app/boards/arm/zswatch_nrf5340/zswatch_nrf5340_cpuapp_5.overlay @@ -77,6 +77,7 @@ ; low-power-enable; }; + group2 { psels = ; low-power-enable; @@ -125,21 +126,25 @@ buttons: buttons { compatible = "gpio-keys"; + button1: button_1 { label = "top-right"; gpios = <&gpio0 27 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>; zephyr,code = ; }; + button2: button_2 { label = "bottom-left"; gpios = <&gpio1 10 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>; zephyr,code = ; }; + button3: button_3 { label = "bottom-right"; gpios = <&gpio0 31 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>; zephyr,code = ; }; + button4: button_4 { label = "top-left"; gpios = <&gpio0 26 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>; @@ -167,39 +172,54 @@ pwmleds { compatible = "pwm-leds"; + display_blk: pwm_led_0 { pwms = <&pwm0 0 PWM_USEC(15) PWM_POLARITY_INVERTED>; }; + vibrator_pwm: pwm_led_1 { pwms = <&pwm1 0 PWM_MSEC(20) 0>; }; + buzzer_pwm: pwm_led_2 { pwms = <&pwm2 0 PWM_HZ(880) PWM_POLARITY_NORMAL>; }; }; +}; +&uart0 { + status = "disabled"; }; &i2c1 { - bmi270: bmi270@68 { - compatible = "bosch,bmi270-plus"; - reg = <0x68>; - status = "okay"; - int-gpios = <&gpio1 4 (GPIO_PULL_DOWN | GPIO_ACTIVE_HIGH)>; - // NOTE: The swap is performed AFTER the axis inversion. So we have to invert the y axis of the sensor - // to get the x axis of the watch coordinate system. - swap-xy; - invert-y; - }; + rv_8263_c8: rv-8263-c8@51 { + compatible = "microcrystal,rv-8263-c8"; + reg = <0x51>; + status = "okay"; + clkout = <0>; + int-gpios = <&gpio1 13 (GPIO_PULL_DOWN | GPIO_ACTIVE_HIGH)>; + }; + + bmi270: bmi270@68 { + compatible = "bosch,bmi270-plus"; + reg = <0x68>; + status = "okay"; + int-gpios = <&gpio1 4 (GPIO_PULL_DOWN | GPIO_ACTIVE_HIGH)>; + // NOTE: The swap is performed AFTER the axis inversion. So we have to invert the y axis of the sensor + // to get the x axis of the watch coordinate system. + swap-xy; + invert-y; + }; + + apds9306: apds9306@52 { + compatible = "avago,apds9306"; + reg = <0x52>; + status = "okay"; + gain = <0>; + resolution = <0>; + frequency = <0>; + }; - apds9306: apds9306@52 { - compatible = "avago,apds9306"; - reg = <0x52>; - status = "okay"; - gain = <0>; - resolution = <0>; - frequency = <0>; - }; npm1300_pmic: pmic@6b { compatible = "nordic,npm1300"; reg = <0x6b>; @@ -254,6 +274,7 @@ thermistor-beta = <3380>; // Not used charging-enable; }; + npm1300_buttons: buttons { compatible = "gpio-keys"; status = "disabled"; @@ -287,6 +308,7 @@ pinctrl-0 = <&qspi_default>; pinctrl-1 = <&qspi_sleep>; pinctrl-names = "default", "sleep"; + mx25u51245g: mx25u51245g@0 { compatible = "nordic,qspi-nor"; reg = <0>; @@ -312,6 +334,7 @@ / { fstab { compatible = "zephyr,fstab"; + lvgl_lfs: lvgl_lfs { compatible = "zephyr,fstab,littlefs"; mount-point = "/lvgl_lfs"; @@ -339,10 +362,12 @@ label = "littlefs_storage"; reg = <0x00000000 0x00200000>; }; + lvgl_raw_partition: partition@200000 { label = "lvgl_raw_partition"; reg = <0x00200000 0x00200000>; }; + storage_partition: partition@400000 { label = "storage_partition"; reg = <0x400000 0x100000 >; diff --git a/app/child_image/hci_ipc.conf b/app/child_image/hci_ipc.conf index a4b4e04c..e94584eb 100644 --- a/app/child_image/hci_ipc.conf +++ b/app/child_image/hci_ipc.conf @@ -1,47 +1,47 @@ -CONFIG_BT_EXT_ADV=y - -CONFIG_BT_CTLR=y -CONFIG_BT_LL_SW_SPLIT=y - -CONFIG_BT_CTLR_ADV_EXT=y -CONFIG_BT_CTLR_ADV_PERIODIC=y - -CONFIG_BT_CTLR_DF=y - -CONFIG_BT_CTLR_DF_SCAN_CTE_RX=n -CONFIG_BT_CTLR_DF_ANT_SWITCH_RX=n -CONFIG_BT_CTLR_DF_CTE_RX=n - -# Limit number of possible connection to decrease memory usage -CONFIG_BT_MAX_CONN=1 - -# Enable chaining of multiple CTEs in periodic advertising -CONFIG_BT_CTLR_ADVANCED_FEATURES=y -CONFIG_BT_CTLR_ADV_SYNC_PDU_BACK2BACK=y -CONFIG_BT_CTLR_DF_PER_ADV_CTE_NUM_MAX=16 - -CONFIG_BT_CTLR_TX_PWR_0=y -CONFIG_BT_CTLR_ADV_DATA_LEN_MAX=191 - -# Enable chaining of multiple CTEs in periodic advertising -CONFIG_BT_CTLR_ADVANCED_FEATURES=y -CONFIG_BT_CTLR_ADV_SYNC_PDU_BACK2BACK=y -CONFIG_BT_CTLR_DF_PER_ADV_CTE_NUM_MAX=16 - -CONFIG_BT_CTLR_DF_ANT_SWITCH_TX=n -CONFIG_BT_BROADCASTER=y - -CONFIG_BT_EXT_ADV_MAX_ADV_SET=2 -CONFIG_BT_CTLR_ADV_AUX_SET=2 -CONFIG_BT_TICKER_UPDATE=y - -CONFIG_CLOCK_CONTROL_NRF_K32SRC_RC=y -# DFU over BT requires -# increase of ble throughput -CONFIG_BT_BUF_ACL_RX_SIZE=502 -CONFIG_BT_BUF_ACL_TX_SIZE=502 - -# Temporary fix as there is a strange behaviour with some Android -# phones. -CONFIG_BT_DATA_LEN_UPDATE=n -#CONFIG_BT_CTLR_DATA_LENGTH_MAX=251 +CONFIG_BT_EXT_ADV=y + +CONFIG_BT_CTLR=y +CONFIG_BT_LL_SW_SPLIT=y + +CONFIG_BT_CTLR_ADV_EXT=y +CONFIG_BT_CTLR_ADV_PERIODIC=y + +CONFIG_BT_CTLR_DF=y + +CONFIG_BT_CTLR_DF_SCAN_CTE_RX=n +CONFIG_BT_CTLR_DF_ANT_SWITCH_RX=n +CONFIG_BT_CTLR_DF_CTE_RX=n + +# Limit number of possible connection to decrease memory usage +CONFIG_BT_MAX_CONN=1 + +# Enable chaining of multiple CTEs in periodic advertising +CONFIG_BT_CTLR_ADVANCED_FEATURES=y +CONFIG_BT_CTLR_ADV_SYNC_PDU_BACK2BACK=y +CONFIG_BT_CTLR_DF_PER_ADV_CTE_NUM_MAX=16 + +CONFIG_BT_CTLR_TX_PWR_0=y +CONFIG_BT_CTLR_ADV_DATA_LEN_MAX=191 + +# Enable chaining of multiple CTEs in periodic advertising +CONFIG_BT_CTLR_ADVANCED_FEATURES=y +CONFIG_BT_CTLR_ADV_SYNC_PDU_BACK2BACK=y +CONFIG_BT_CTLR_DF_PER_ADV_CTE_NUM_MAX=16 + +CONFIG_BT_CTLR_DF_ANT_SWITCH_TX=n +CONFIG_BT_BROADCASTER=y + +CONFIG_BT_EXT_ADV_MAX_ADV_SET=2 +CONFIG_BT_CTLR_ADV_AUX_SET=2 +CONFIG_BT_TICKER_UPDATE=y + +CONFIG_CLOCK_CONTROL_NRF_K32SRC_RC=y +# DFU over BT requires +# increase of ble throughput +CONFIG_BT_BUF_ACL_RX_SIZE=502 +CONFIG_BT_BUF_ACL_TX_SIZE=502 + +# Temporary fix as there is a strange behaviour with some Android +# phones. +CONFIG_BT_DATA_LEN_UPDATE=n +#CONFIG_BT_CTLR_DATA_LENGTH_MAX=251 diff --git a/app/drivers/CMakeLists.txt b/app/drivers/CMakeLists.txt index 31f019e0..cd6d4278 100644 --- a/app/drivers/CMakeLists.txt +++ b/app/drivers/CMakeLists.txt @@ -1,4 +1,4 @@ add_subdirectory(display) add_subdirectory(input) -add_subdirectory_ifdef(CONFIG_SENSOR sensor) \ No newline at end of file +add_subdirectory_ifdef(CONFIG_SENSOR sensor) diff --git a/app/drivers/Kconfig b/app/drivers/Kconfig index aa785211..1c492e03 100644 --- a/app/drivers/Kconfig +++ b/app/drivers/Kconfig @@ -1,6 +1,3 @@ rsource "display/Kconfig" rsource "input/Kconfig" - -menu "Drivers" - rsource "sensor/Kconfig" -endmenu \ No newline at end of file +rsource "sensor/Kconfig" diff --git a/app/drivers/display/gc9a01/CMakeLists.txt b/app/drivers/display/gc9a01/CMakeLists.txt index a58a904e..860978b4 100644 --- a/app/drivers/display/gc9a01/CMakeLists.txt +++ b/app/drivers/display/gc9a01/CMakeLists.txt @@ -1 +1 @@ -zephyr_sources(gc9a01.c) \ No newline at end of file +zephyr_sources(buydisplay_gc9a01.c) \ No newline at end of file diff --git a/app/drivers/display/gc9a01/Kconfig b/app/drivers/display/gc9a01/Kconfig index 8bc74830..a4562ec0 100644 --- a/app/drivers/display/gc9a01/Kconfig +++ b/app/drivers/display/gc9a01/Kconfig @@ -1,5 +1,5 @@ menuconfig GC9A01 - bool "GC9A01 compatible display controller driver" + bool "GC9A01 Display" default n select SPI help diff --git a/app/drivers/display/gc9a01/gc9a01.c b/app/drivers/display/gc9a01/buydisplay_gc9a01.c similarity index 100% rename from app/drivers/display/gc9a01/gc9a01.c rename to app/drivers/display/gc9a01/buydisplay_gc9a01.c diff --git a/app/drivers/input/cst816s/Kconfig b/app/drivers/input/cst816s/Kconfig index 43adc895..5f4224f9 100644 --- a/app/drivers/input/cst816s/Kconfig +++ b/app/drivers/input/cst816s/Kconfig @@ -3,7 +3,7 @@ # SPDX-License-Identifier: Apache-2.0 menuconfig INPUT_MODIFIED_CST816S - bool "Use modified out of tree CST816S capacitive touch panel driver" + bool "CST816S Capacitive touch panel driver" default y depends on DT_HAS_HYNITRON_CST816S_ENABLED select I2C @@ -26,4 +26,4 @@ config INPUT_CST816S_INTERRUPT help Enable interrupt support (requires GPIO). -endif # INPUT_CST816S +endif diff --git a/app/drivers/sensor/apds9306/Kconfig b/app/drivers/sensor/apds9306/Kconfig index 60af26e3..ea704e35 100644 --- a/app/drivers/sensor/apds9306/Kconfig +++ b/app/drivers/sensor/apds9306/Kconfig @@ -5,7 +5,7 @@ # SPDX-License-Identifier: Apache-2.0 menuconfig APDS9306 - bool "APDS9306 Sensor" + bool "Avago APDS9306 Sensor" default y depends on DT_HAS_AVAGO_APDS9306_ENABLED select I2C diff --git a/app/drivers/sensor/bmi270/Kconfig b/app/drivers/sensor/bmi270/Kconfig index ef546f0d..4c7f01a4 100644 --- a/app/drivers/sensor/bmi270/Kconfig +++ b/app/drivers/sensor/bmi270/Kconfig @@ -5,7 +5,7 @@ # SPDX-License-Identifier: Apache-2.0 menuconfig BMI270_PLUS - bool "BMI270 Plus Sensor" + bool "Bosch BMI270 Plus Sensor" depends on DT_HAS_BOSCH_BMI270_PLUS_ENABLED default y select I2C @@ -27,25 +27,24 @@ if BMI270_PLUS prompt "BMI270 trigger mode" default BMI270_PLUS_TRIGGER_NONE - config BMI270_PLUS_TRIGGER_NONE - bool "No trigger" - help - Disable the trigger interface. - - config BMI270_PLUS_TRIGGER_GLOBAL_THREAD - depends on GPIO - select BMI270_PLUS_TRIGGER - bool "Use workqueue" - help - Use the global workqueue to process the interrupts from the BMI270. - - config BMI270_PLUS_TRIGGER_OWN_THREAD - depends on GPIO - select BMI270_PLUS_TRIGGER - bool "Use driver thread" - help - Use a separate thread to process the interrupts from the BMI270. - + config BMI270_PLUS_TRIGGER_NONE + bool "No trigger" + help + Disable the trigger interface. + + config BMI270_PLUS_TRIGGER_GLOBAL_THREAD + depends on GPIO + select BMI270_PLUS_TRIGGER + bool "Use workqueue" + help + Use the global workqueue to process the interrupts from the BMI270. + + config BMI270_PLUS_TRIGGER_OWN_THREAD + depends on GPIO + select BMI270_PLUS_TRIGGER + bool "Use driver thread" + help + Use a separate thread to process the interrupts from the BMI270. endchoice config BMI270_PLUS_TRIGGER diff --git a/app/drivers/sensor/bmi270/bosch_bmi270.c b/app/drivers/sensor/bmi270/bosch_bmi270.c index f72a3cdb..90d0707f 100644 --- a/app/drivers/sensor/bmi270/bosch_bmi270.c +++ b/app/drivers/sensor/bmi270/bosch_bmi270.c @@ -542,7 +542,7 @@ static int bmi270_pm_action(const struct device *p_dev, enum pm_device_action ac .swap_xy = DT_INST_PROP(inst, swap_xy), \ .invert_x = DT_INST_PROP(inst, invert_x), \ .invert_y = DT_INST_PROP(inst, invert_y), \ - .rotation = DT_INST_PROP(inst, rotation), \ + .rotation = DT_INST_PROP(inst, rotation), \ IF_ENABLED(CONFIG_BMI270_PLUS_TRIGGER, \ (.int_gpio = GPIO_DT_SPEC_INST_GET_OR(inst, int_gpios, { 0 }),)) \ }; \ diff --git a/app/drivers/sensor/bmi270/private/bosch_bmi270_types.h b/app/drivers/sensor/bmi270/private/bosch_bmi270_types.h index 22759684..4500a293 100644 --- a/app/drivers/sensor/bmi270/private/bosch_bmi270_types.h +++ b/app/drivers/sensor/bmi270/private/bosch_bmi270_types.h @@ -21,52 +21,50 @@ struct bmi270_config { struct i2c_dt_spec i2c; #ifdef CONFIG_BMI270_PLUS_TRIGGER - struct gpio_dt_spec int_gpio; + struct gpio_dt_spec int_gpio; #endif - bool swap_xy; - bool invert_x; - bool invert_y; - uint16_t rotation; + bool swap_xy; + bool invert_x; + bool invert_y; + uint16_t rotation; }; /** @brief */ struct bmi270_data { #ifdef CONFIG_BMI270_PLUS_TRIGGER - struct gpio_callback gpio_handler; + struct gpio_callback gpio_handler; - const struct device *dev; - const struct sensor_trigger *trig; + const struct device *dev; + const struct sensor_trigger *trig; - sensor_trigger_handler_t global; - sensor_trigger_handler_t sig_motion; - sensor_trigger_handler_t step; - sensor_trigger_handler_t activity; - sensor_trigger_handler_t wake; - sensor_trigger_handler_t gesture; - sensor_trigger_handler_t stationary; - sensor_trigger_handler_t motion; + sensor_trigger_handler_t global; + sensor_trigger_handler_t sig_motion; + sensor_trigger_handler_t step; + sensor_trigger_handler_t activity; + sensor_trigger_handler_t wake; + sensor_trigger_handler_t gesture; + sensor_trigger_handler_t stationary; + sensor_trigger_handler_t motion; #endif -#ifdef CONFIG_BMI270_PLUS_TRIGGER_OWN_THREAD - struct k_sem sem; +#if defined(CONFIG_BMI270_PLUS_TRIGGER_OWN_THREAD) + struct k_sem sem; +#elif defined(CONFIG_BMI270_PLUS_TRIGGER_GLOBAL_THREAD) + struct k_work work; #endif -#ifdef CONFIG_BMI270_PLUS_TRIGGER_GLOBAL_THREAD - struct k_work work; -#endif - - int16_t ax; + int16_t ax; int16_t ay; int16_t az; int16_t gx; int16_t gy; int16_t gz; uint16_t temp; - uint8_t acc_range; + uint8_t acc_range; uint8_t acc_odr; - uint16_t gyr_range; + uint16_t gyr_range; uint8_t gyr_odr; uint8_t gyr_osr; - struct bmi2_dev bmi2; -}; \ No newline at end of file + struct bmi2_dev bmi2; +}; diff --git a/app/drivers/sensor/bmp581/Kconfig b/app/drivers/sensor/bmp581/Kconfig index 6c8eef94..2ba582c5 100644 --- a/app/drivers/sensor/bmp581/Kconfig +++ b/app/drivers/sensor/bmp581/Kconfig @@ -5,7 +5,7 @@ # SPDX-License-Identifier: Apache-2.0 menuconfig BMP581 - bool "BMP581 Sensor" + bool "Bosch BMP581 Sensor" depends on DT_HAS_BOSCH_BMP581_ENABLED default y select I2C diff --git a/app/dts/bindings/sensor/bosch,bmi270-plus.yml b/app/dts/bindings/sensor/bosch,bmi270-plus.yml index bf48212b..f4a49e82 100644 --- a/app/dts/bindings/sensor/bosch,bmi270-plus.yml +++ b/app/dts/bindings/sensor/bosch,bmi270-plus.yml @@ -25,6 +25,7 @@ properties: type: boolean description: | Invert the y axis. + rotation: type: int default: 0 diff --git a/app/zephyr_patches/ntc_fix.patch b/app/patches/ntc_fix.patch similarity index 100% rename from app/zephyr_patches/ntc_fix.patch rename to app/patches/ntc_fix.patch diff --git a/app/patches/rv8263_rtc.patch b/app/patches/rv8263_rtc.patch new file mode 100644 index 00000000..35fdada1 --- /dev/null +++ b/app/patches/rv8263_rtc.patch @@ -0,0 +1,950 @@ +diff --git a/drivers/rtc/rtc_utils.h b/drivers/rtc/rtc_utils.c +new file mode 100644 +index 0000000000..035034da56 +--- /dev/null ++++ b/drivers/rtc/rtc_utils.c +@@ -0,0 +1,58 @@ ++/* ++ * Copyright (c) 2023 Bjarki Arge Andreasen ++ * Copyright (c) 2024 Andrew Featherstone ++ * ++ * SPDX-License-Identifier: Apache-2.0 ++ */ ++ ++#include ++#include ++ ++#include ++ ++#include "rtc_utils.h" ++ ++bool rtc_utils_validate_rtc_time(const struct rtc_time *timeptr, uint16_t mask) ++{ ++ if ((mask & RTC_ALARM_TIME_MASK_SECOND) && (timeptr->tm_sec < 0 || timeptr->tm_sec > 59)) { ++ return false; ++ } ++ ++ if ((mask & RTC_ALARM_TIME_MASK_MINUTE) && (timeptr->tm_min < 0 || timeptr->tm_min > 59)) { ++ return false; ++ } ++ ++ if ((mask & RTC_ALARM_TIME_MASK_HOUR) && (timeptr->tm_hour < 0 || timeptr->tm_hour > 23)) { ++ return false; ++ } ++ ++ if ((mask & RTC_ALARM_TIME_MASK_MONTH) && (timeptr->tm_mon < 0 || timeptr->tm_mon > 11)) { ++ return false; ++ } ++ ++ if ((mask & RTC_ALARM_TIME_MASK_MONTHDAY) && ++ (timeptr->tm_mday < 1 || timeptr->tm_mday > 31)) { ++ return false; ++ } ++ ++ if ((mask & RTC_ALARM_TIME_MASK_YEAR) && (timeptr->tm_year < 0 || timeptr->tm_year > 199)) { ++ return false; ++ } ++ ++ if ((mask & RTC_ALARM_TIME_MASK_WEEKDAY) && ++ (timeptr->tm_wday < 0 || timeptr->tm_wday > 6)) { ++ return false; ++ } ++ ++ if ((mask & RTC_ALARM_TIME_MASK_YEARDAY) && ++ (timeptr->tm_yday < 0 || timeptr->tm_yday > 365)) { ++ return false; ++ } ++ ++ if ((mask & RTC_ALARM_TIME_MASK_NSEC) && ++ (timeptr->tm_nsec < 0 || timeptr->tm_nsec > 999999999)) { ++ return false; ++ } ++ ++ return true; ++} +diff --git a/drivers/rtc/rtc_utils.h b/drivers/rtc/rtc_utils.h +new file mode 100644 +index 0000000000..035034da56 +--- /dev/null ++++ b/drivers/rtc/rtc_utils.h +@@ -0,0 +1,28 @@ ++/* ++ * Copyright (c) 2023 Bjarki Arge Andreasen ++ * Copyright (c) 2024 Andrew Featherstone ++ * ++ * SPDX-License-Identifier: Apache-2.0 ++ */ ++ ++#ifndef ZEPHYR_DRIVERS_RTC_RTC_UTILS_H_ ++#define ZEPHYR_DRIVERS_RTC_RTC_UTILS_H_ ++ ++#include ++#include ++ ++#include ++ ++/** ++ * @brief Validate a datetime with a mask ++ * ++ * Ensure that any fields selected by mask contain a valid value. ++ * ++ * @param timeptr The time to set ++ * @param mask Mask of fields to validate ++ * ++ * @return true if the required fields are valid. ++ */ ++bool rtc_utils_validate_rtc_time(const struct rtc_time *timeptr, uint16_t mask); ++ ++#endif /* ZEPHYR_DRIVERS_RTC_RTC_UTILS_H_ */ +diff --git a/drivers/rtc/CMakeLists.txt b/drivers/rtc/CMakeLists.txt +index 65d822d..4236ad5 100644 +--- a/drivers/rtc/CMakeLists.txt ++++ b/drivers/rtc/CMakeLists.txt +@@ -5,6 +5,9 @@ zephyr_syscall_header(${ZEPHYR_BASE}/include/zephyr/drivers/rtc.h) + + zephyr_library() + ++zephyr_library_sources(rtc_utils.c) ++ ++zephyr_library_sources_ifdef(CONFIG_RTC_RV8263 rtc_rv8263.c) + zephyr_library_sources_ifdef(CONFIG_RTC_AM1805 rtc_am1805.c) + zephyr_library_sources_ifdef(CONFIG_RTC_DS1307 rtc_ds1307.c) + zephyr_library_sources_ifdef(CONFIG_USERSPACE rtc_handlers.c) +index 46bd2562dd..2a1d5fa05b 100644 +--- a/drivers/rtc/Kconfig ++++ b/drivers/rtc/Kconfig +@@ -53,1 +53,2 @@ source "drivers/rtc/Kconfig.sam" ++source "drivers/rtc/Kconfig.rv8263" + + endif # RTC +diff --git a/drivers/rtc/Kconfig.rv8263 b/drivers/rtc/Kconfig.rv8263 +new file mode 100644 +index 0000000000..035034da56 +--- /dev/null ++++ b/drivers/rtc/Kconfig.rv8263 +@@ -0,0 +1,11 @@ ++# SPDX-License-Identifier: Apache-2.0 ++# Copyright (c) 2024 Daniel Kampert ++# Author: Daniel Kampert ++ ++config RTC_RV8263 ++ bool "Micro Crystal RV-8263-C8 RTC driver" ++ default y ++ depends on DT_HAS_MICROCRYSTAL_RV_8263_C8_ENABLED ++ select I2C ++ help ++ Micro Crystal RV-8263-C8 RTC driver. +diff --git a/drivers/rtc/rtc_rv8263.c b/drivers/rtc/rtc_rv8263.c +new file mode 100644 +index 0000000000..bb82ed7d9b +--- /dev/null ++++ b/drivers/rtc/rtc_rv8263.c +@@ -0,0 +1,771 @@ ++/* Copyright (c) 2024 Daniel Kampert ++ * Author: Daniel Kampert ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "rtc_utils.h" ++ ++#define RV8263C8_REGISTER_CONTROL_1 0x00 ++#define RV8263C8_REGISTER_CONTROL_2 0x01 ++#define RV8263C8_REGISTER_OFFSET 0x02 ++#define RV8263C8_REGISTER_RAM 0x03 ++#define RV8263C8_REGISTER_SECONDS 0x04 ++#define RV8263C8_REGISTER_MINUTES 0x05 ++#define RV8263C8_REGISTER_HOURS 0x06 ++#define RV8263C8_REGISTER_DATE 0x07 ++#define RV8263C8_REGISTER_WEEKDAY 0x08 ++#define RV8263C8_REGISTER_MONTH 0x09 ++#define RV8263C8_REGISTER_YEAR 0x0A ++#define RV8263C8_REGISTER_SECONDS_ALARM 0x0B ++#define RV8263C8_REGISTER_MINUTES_ALARM 0x0C ++#define RV8263C8_REGISTER_HOURS_ALARM 0x0D ++#define RV8263C8_REGISTER_DATE_ALARM 0x0E ++#define RV8263C8_REGISTER_WEEKDAY_ALARM 0x0F ++#define RV8263C8_REGISTER_TIMER_VALUE 0x10 ++#define RV8263C8_REGISTER_TIMER_MODE 0x11 ++ ++#define RV8263_BM_FAST_MODE (0x01 << 7) ++#define RV8263_BM_NORMAL_MODE (0x00 << 7) ++#define RV8263C8_BM_24H_MODE_ENABLE (0x00 << 1) ++#define RV8263C8_BM_24H_MODE_DISABLE (0x00 << 1) ++#define RV8263C8_BM_CLOCK_ENABLE (0x00 << 5) ++#define RV8263C8_BM_CLOCK_DISABLE (0x01 << 5) ++#define RV8263C8_BM_ALARM_INT_ENABLE (0x01 << 7) ++#define RV8263C8_BM_ALARM_INT_DISABLE (0x00 << 7) ++#define RV8263C8_BM_MINUTE_INT_ENABLE (0x01 << 5) ++#define RV8263C8_BM_MINUTE_INT_DISABLE (0x00 << 5) ++#define RV8263C8_BM_HALF_MINUTE_INT_ENABLE (0x01 << 4) ++#define RV8263C8_BM_HALF_MINUTE_INT_DISABLE (0x00 << 4) ++#define RV8263C8_BM_ALARM_ENABLE (0x00 << 7) ++#define RV8263C8_BM_ALARM_DISABLE (0x01 << 7) ++#define RV8263C8_BM_AF (0x01 << 6) ++#define RV8263C8_BM_TF (0x01 << 3) ++#define RV8263_BM_MODE (0x01 << 7) ++#define RV8263_BM_TD_1HZ (0x02 << 3) ++#define RV8263_BM_TE_ENABLE (0x01 << 2) ++#define RV8263_BM_TIE_ENABLE (0x01 << 1) ++#define RV8263_BM_TI_TP_PULSE (0x01 << 0) ++#define RV8263_BM_OS (0x01 << 7) ++#define RV8263C8_BM_SOFTWARE_RESET (0x58) ++#define RV8263C8_BM_REGISTER_OFFSET 0x7F ++#define RV8263_YEAR_OFFSET (2000 - 1900) ++ ++#define SECONDS_BITS GENMASK(6, 0) ++#define MINUTES_BITS GENMASK(7, 0) ++#define HOURS_BITS GENMASK(5, 0) ++#define DATE_BITS GENMASK(5, 0) ++#define MONTHS_BITS GENMASK(4, 0) ++#define WEEKDAY_BITS GENMASK(2, 0) ++#define YEAR_BITS GENMASK(7, 0) ++#define VALIDATE_24HR BIT(6) ++ ++#define MIN_SEC 0 ++#define MAX_SEC 59 ++#define MIN_MIN 0 ++#define MAX_MIN 59 ++#define MIN_HOUR 0 ++#define MAX_HOUR 23 ++#define MAX_WDAY 7 ++#define MIN_WDAY 1 ++#define MAX_MDAY 31 ++#define MIN_MDAY 1 ++#define MAX_MON 12 ++#define MIN_MON 1 ++#define MIN_YEAR_DIFF 0 ++#define MAX_YEAR_DIFF 99 ++ ++#define DT_DRV_COMPAT microcrystal_rv_8263_c8 ++ ++LOG_MODULE_REGISTER(microcrystal_rv8263c8, CONFIG_RTC_LOG_LEVEL); ++ ++struct rv8263c8_config { ++ struct i2c_dt_spec i2c_bus; ++ uint32_t clkout; ++ ++#if DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios) ++ struct gpio_dt_spec int_gpio; ++#endif ++}; ++ ++struct rv8263c8_data { ++ struct k_sem lock; ++ ++#if (CONFIG_RTC_ALARM || CONFIG_RTC_UPDATE) && DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios) ++ const struct device *dev; ++ struct gpio_callback gpio_cb; ++#endif ++ ++#if CONFIG_RTC_ALARM && DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios) ++ rtc_alarm_callback alarm_cb; ++ void *alarm_cb_data; ++ struct k_work alarm_work; ++#endif ++ ++#if CONFIG_RTC_UPDATE && DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios) ++ rtc_update_callback update_cb; ++ void *update_cb_data; ++ struct k_work update_work; ++#endif ++}; ++ ++static int rv8263c8_update_disable_timer(const struct device *dev) ++{ ++ int err; ++ uint8_t buf[2]; ++ const struct rv8263c8_config *config = dev->config; ++ ++ /* Value 0 disables the timer. */ ++ buf[0] = RV8263C8_REGISTER_TIMER_VALUE; ++ buf[1] = 0; ++ err = i2c_write_dt(&config->i2c_bus, buf, 2); ++ if (err < 0) { ++ return err; ++ } ++ ++ buf[0] = RV8263C8_REGISTER_TIMER_MODE; ++ return i2c_write_dt(&config->i2c_bus, buf, 2); ++} ++ ++#if (CONFIG_RTC_ALARM || CONFIG_RTC_UPDATE) && DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios) ++static void rv8263c8_gpio_callback_handler(const struct device *p_port, struct gpio_callback *p_cb, ++ gpio_port_pins_t pins) ++{ ++ ARG_UNUSED(pins); ++ ARG_UNUSED(p_port); ++ ++ struct rv8263c8_data *data = CONTAINER_OF(p_cb, struct rv8263c8_data, gpio_cb); ++ ++#if CONFIG_RTC_ALARM ++ k_work_submit(&data->alarm_work); ++#endif ++ ++#if CONFIG_RTC_UPDATE ++ k_work_submit(&data->update_work); ++#endif ++} ++#endif ++ ++#if CONFIG_RTC_ALARM && DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios) ++static void rv8263c8_alarm_worker(struct k_work *p_work) ++{ ++ struct rv8263c8_data *data = CONTAINER_OF(p_work, struct rv8263c8_data, alarm_work); ++ const struct rv8263c8_config *config = data->dev->config; ++ ++ LOG_DBG("Process alarm worker from interrupt"); ++ ++ if (data->alarm_cb != NULL) { ++ uint8_t reg; ++ ++ i2c_reg_read_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_CONTROL_2, ®); ++ ++ if (reg & RV8263C8_BM_AF) { ++ reg &= ~RV8263C8_BM_AF; ++ ++ LOG_DBG("Calling alarm callback"); ++ data->alarm_cb(data->dev, 0, data->alarm_cb_data); ++ ++ i2c_reg_write_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_CONTROL_2, reg); ++ } ++ } ++} ++#endif ++ ++#if CONFIG_RTC_UPDATE && DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios) ++static int rv8263c8_update_enable_timer(const struct device *dev) ++{ ++ int err; ++ const struct rv8263c8_config *config = dev->config; ++ uint8_t buf[2]; ++ ++ /* Set the timer preload value for 1 second. */ ++ buf[0] = RV8263C8_REGISTER_TIMER_VALUE; ++ buf[1] = 1; ++ err = i2c_write_dt(&config->i2c_bus, buf, 2); ++ if (err < 0) { ++ return err; ++ } ++ ++ buf[0] = RV8263C8_REGISTER_TIMER_MODE; ++ buf[1] = RV8263_BM_TD_1HZ | RV8263_BM_TE_ENABLE | RV8263_BM_TIE_ENABLE | ++ RV8263_BM_TI_TP_PULSE; ++ return i2c_write_dt(&config->i2c_bus, buf, 2); ++} ++ ++static void rv8263c8_update_worker(struct k_work *p_work) ++{ ++ uint8_t reg; ++ struct rv8263c8_data *data = CONTAINER_OF(p_work, struct rv8263c8_data, update_work); ++ const struct rv8263c8_config *config = data->dev->config; ++ ++ LOG_DBG("Process update worker from interrupt"); ++ ++ if (data->update_cb != NULL) { ++ i2c_reg_read_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_CONTROL_2, ®); ++ ++ if (reg & RV8263C8_BM_TF) { ++ LOG_DBG("Calling update callback"); ++ data->update_cb(data->dev, data->update_cb_data); ++ } ++ } ++ ++ rv8263c8_update_enable_timer(data->dev); ++ i2c_reg_update_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_CONTROL_2, RV8263C8_BM_TF, ++ RV8263C8_BM_TF); ++} ++#endif ++ ++static int rv8263c8_time_set(const struct device *dev, const struct rtc_time *timeptr) ++{ ++ uint8_t regs[8]; ++ const struct rv8263c8_config *config = dev->config; ++ ++ if (timeptr == NULL || (timeptr->tm_year < RV8263_YEAR_OFFSET)) { ++ LOG_ERR("invalid time"); ++ return -EINVAL; ++ } ++ ++ LOG_DBG("Set time: year = %u, mon = %u, mday = %u, wday = %u, hour = %u, min = %u, sec = " ++ "%u", ++ timeptr->tm_year, timeptr->tm_mon, timeptr->tm_mday, timeptr->tm_wday, ++ timeptr->tm_hour, timeptr->tm_min, timeptr->tm_sec); ++ ++ regs[0] = RV8263C8_REGISTER_SECONDS; ++ regs[1] = bin2bcd(timeptr->tm_sec) & SECONDS_BITS; ++ regs[2] = bin2bcd(timeptr->tm_min) & MINUTES_BITS; ++ regs[3] = bin2bcd(timeptr->tm_hour) & HOURS_BITS; ++ regs[4] = bin2bcd(timeptr->tm_mday) & DATE_BITS; ++ regs[5] = bin2bcd(timeptr->tm_wday) & WEEKDAY_BITS; ++ regs[6] = bin2bcd(timeptr->tm_mon) & MONTHS_BITS; ++ regs[7] = bin2bcd(timeptr->tm_year - RV8263_YEAR_OFFSET) & YEAR_BITS; ++ ++ return i2c_write_dt(&config->i2c_bus, regs, 8); ++} ++ ++static int rv8263c8_time_get(const struct device *dev, struct rtc_time *timeptr) ++{ ++ int err; ++ uint8_t regs[7]; ++ const struct rv8263c8_config *config = dev->config; ++ ++ if (timeptr == NULL) { ++ return -EINVAL; ++ } ++ ++ err = i2c_burst_read_dt(&config->i2c_bus, RV8263C8_REGISTER_SECONDS, regs, sizeof(regs)); ++ if (err < 0) { ++ return err; ++ } ++ ++ /* Return an error when the oscillator is stopped. */ ++ if (regs[0] & RV8263_BM_OS) { ++ return -ECANCELED; ++ } ++ ++ timeptr->tm_sec = bcd2bin(regs[0] & SECONDS_BITS); ++ timeptr->tm_min = bcd2bin(regs[1] & MINUTES_BITS); ++ timeptr->tm_hour = bcd2bin(regs[2] & HOURS_BITS); ++ timeptr->tm_mday = bcd2bin(regs[3] & DATE_BITS); ++ timeptr->tm_wday = bcd2bin(regs[4] & WEEKDAY_BITS); ++ timeptr->tm_mon = bcd2bin(regs[5] & MONTHS_BITS); ++ timeptr->tm_year = bcd2bin(regs[6] & YEAR_BITS) + RV8263_YEAR_OFFSET; ++ ++ /* Unused. */ ++ timeptr->tm_nsec = 0; ++ timeptr->tm_isdst = -1; ++ timeptr->tm_yday = -1; ++ ++ /* Validate the chip in 24hr mode. */ ++ if (regs[2] & VALIDATE_24HR) { ++ return -ENODATA; ++ } ++ ++ LOG_DBG("Get time: year = %u, mon = %u, mday = %u, wday = %u, hour = %u, min = %u, sec = " ++ "%u", ++ timeptr->tm_year, timeptr->tm_mon, timeptr->tm_mday, timeptr->tm_wday, ++ timeptr->tm_hour, timeptr->tm_min, timeptr->tm_sec); ++ ++ return 0; ++} ++ ++static int rv8263c8_init(const struct device *dev) ++{ ++ int err; ++ int temp; ++ struct rv8263c8_data *data = dev->data; ++ const struct rv8263c8_config *config = dev->config; ++ ++#if (CONFIG_RTC_ALARM || CONFIG_RTC_UPDATE) && DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios) ++ if (config->int_gpio.port == NULL) { ++ return -EINVAL; ++ } ++#endif ++ ++ if (!i2c_is_ready_dt(&config->i2c_bus)) { ++ LOG_ERR("I2C bus not ready!"); ++ return -ENODEV; ++ } ++ ++ k_sem_init(&data->lock, 1, 1); ++ ++ err = rv8263c8_update_disable_timer(dev); ++ if (err < 0) { ++ LOG_ERR("Error while disabling the timer! Error: %i", err); ++ return err; ++ } ++ ++ err = i2c_reg_write_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_CONTROL_1, ++ RV8263C8_BM_24H_MODE_DISABLE | RV8263C8_BM_CLOCK_ENABLE); ++ if (err < 0) { ++ LOG_ERR("Error while writing CONTROL_1! Error: %i", err); ++ return err; ++ } ++ ++ temp = config->clkout; ++ LOG_DBG("Configure ClkOut: %u", temp); ++ ++ err = i2c_reg_write_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_CONTROL_2, ++ RV8263C8_BM_AF | temp); ++ if (err < 0) { ++ LOG_ERR("Error while writing CONTROL_2! Error: %i", err); ++ return err; ++ } ++ ++ LOG_DBG("Configure ClkOut: %u", temp); ++ ++#if CONFIG_RTC_UPDATE && DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios) ++ uint8_t buf[2]; ++ ++ buf[0] = RV8263C8_REGISTER_TIMER_MODE; ++ buf[1] = 0; ++ err = i2c_write_dt(&config->i2c_bus, buf, 2); ++ if (err < 0) { ++ LOG_ERR("Error while writing CONTROL2! Error: %i", err); ++ return err; ++ } ++#endif ++ ++#if (CONFIG_RTC_ALARM || CONFIG_RTC_UPDATE) && DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios) ++ if (!gpio_is_ready_dt(&config->int_gpio)) { ++ LOG_ERR("GPIO not ready!"); ++ return err; ++ } ++ ++ err = gpio_pin_configure_dt(&config->int_gpio, GPIO_INPUT); ++ if (err < 0) { ++ LOG_ERR("Failed to configure GPIO! Error: %u", err); ++ return err; ++ } ++ ++ err = gpio_pin_interrupt_configure_dt(&config->int_gpio, GPIO_INT_EDGE_FALLING); ++ if (err < 0) { ++ LOG_ERR("Failed to configure interrupt! Error: %u", err); ++ return err; ++ } ++ ++ gpio_init_callback(&data->gpio_cb, rv8263c8_gpio_callback_handler, ++ BIT(config->int_gpio.pin)); ++ ++ err = gpio_add_callback_dt(&config->int_gpio, &data->gpio_cb); ++ if (err < 0) { ++ LOG_ERR("Failed to add GPIO callback! Error: %u", err); ++ return err; ++ } ++#endif ++ ++ (void)k_sem_take(&data->lock, K_FOREVER); ++#if CONFIG_RTC_ALARM && DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios) ++ data->alarm_work.handler = rv8263c8_alarm_worker; ++#endif ++ ++#if CONFIG_RTC_UPDATE && DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios) ++ data->update_work.handler = rv8263c8_update_worker; ++#endif ++ ++#if (CONFIG_RTC_ALARM || CONFIG_RTC_UPDATE) && DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios) ++ data->dev = dev; ++#endif ++ k_sem_give(&data->lock); ++ ++ return 0; ++} ++ ++#if CONFIG_RTC_ALARM ++static int rv8263c8_alarm_get_supported_fields(const struct device *dev, uint16_t id, ++ uint16_t *p_mask) ++{ ++ ARG_UNUSED(dev); ++ ARG_UNUSED(id); ++ ++ (*p_mask) = (RTC_ALARM_TIME_MASK_SECOND | RTC_ALARM_TIME_MASK_MINUTE | ++ RTC_ALARM_TIME_MASK_HOUR | RTC_ALARM_TIME_MASK_MONTHDAY | ++ RTC_ALARM_TIME_MASK_WEEKDAY); ++ ++ return 0; ++} ++ ++static int rv8263c8_alarm_set_time(const struct device *dev, uint16_t id, uint16_t mask, ++ const struct rtc_time *timeptr) ++{ ++ int err; ++ uint8_t regs[5]; ++ const struct rv8263c8_config *config = dev->config; ++ ++ ARG_UNUSED(id); ++ ++ if ((mask > 0) && (timeptr == NULL)) { ++ return -EINVAL; ++ } ++ ++ /* Disable the alarm when mask is zero. */ ++ if (mask == 0) { ++ return i2c_reg_update_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_CONTROL_2, ++ RV8263C8_BM_ALARM_INT_ENABLE, ++ RV8263C8_BM_ALARM_INT_DISABLE); ++ } ++ ++ if (!rtc_utils_validate_rtc_time(timeptr, mask)) { ++ LOG_ERR("Invalid mask!"); ++ return -EINVAL; ++ } ++ ++ regs[0] = bin2bcd(timeptr->tm_sec) & SECONDS_BITS; ++ regs[1] = bin2bcd(timeptr->tm_min) & MINUTES_BITS; ++ regs[2] = bin2bcd(timeptr->tm_hour) & HOURS_BITS; ++ regs[3] = bin2bcd(timeptr->tm_mday) & DATE_BITS; ++ regs[4] = bin2bcd(timeptr->tm_wday) & WEEKDAY_BITS; ++ ++ if (mask & RTC_ALARM_TIME_MASK_SECOND) { ++ err = i2c_reg_write_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_SECONDS_ALARM, ++ RV8263C8_BM_ALARM_ENABLE | regs[0]); ++ } else { ++ err = i2c_reg_write_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_SECONDS_ALARM, ++ RV8263C8_BM_ALARM_DISABLE); ++ } ++ ++ if (err < 0) { ++ LOG_ERR("Error while writing SECONDS alarm! Error: %i", err); ++ return err; ++ } ++ ++ if (mask & RTC_ALARM_TIME_MASK_MINUTE) { ++ err = i2c_reg_write_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_MINUTES_ALARM, ++ RV8263C8_BM_ALARM_ENABLE | regs[1]); ++ } else { ++ err = i2c_reg_write_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_MINUTES_ALARM, ++ RV8263C8_BM_ALARM_DISABLE); ++ } ++ ++ if (err < 0) { ++ LOG_ERR("Error while writing MINUTE alarm! Error: %i", err); ++ return err; ++ } ++ ++ if (mask & RTC_ALARM_TIME_MASK_HOUR) { ++ err = i2c_reg_write_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_HOURS_ALARM, ++ RV8263C8_BM_ALARM_ENABLE | regs[2]); ++ } else { ++ err = i2c_reg_write_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_HOURS_ALARM, ++ RV8263C8_BM_ALARM_DISABLE); ++ } ++ ++ if (err < 0) { ++ LOG_ERR("Error while writing HOUR alarm! Error: %i", err); ++ return err; ++ } ++ ++ if (mask & RTC_ALARM_TIME_MASK_MONTHDAY) { ++ err = i2c_reg_write_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_DATE_ALARM, ++ RV8263C8_BM_ALARM_ENABLE | regs[3]); ++ } else { ++ err = i2c_reg_write_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_DATE_ALARM, ++ RV8263C8_BM_ALARM_DISABLE); ++ } ++ ++ if (err < 0) { ++ LOG_ERR("Error while writing MONTHDAY alarm! Error: %i", err); ++ return err; ++ } ++ ++ if (mask & RTC_ALARM_TIME_MASK_WEEKDAY) { ++ err = i2c_reg_write_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_WEEKDAY_ALARM, ++ RV8263C8_BM_ALARM_ENABLE | regs[4]); ++ } else { ++ err = i2c_reg_write_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_WEEKDAY_ALARM, ++ RV8263C8_BM_ALARM_DISABLE); ++ } ++ ++ if (err < 0) { ++ LOG_ERR("Error while writing WEEKDAY alarm! Error: %i", err); ++ } ++ ++ return err; ++} ++ ++static int rv8263c8_alarm_get_time(const struct device *dev, uint16_t id, uint16_t *p_mask, ++ struct rtc_time *timeptr) ++{ ++ int err; ++ const struct rv8263c8_config *config = dev->config; ++ uint8_t value[5]; ++ ++ ARG_UNUSED(id); ++ ++ if (timeptr == NULL) { ++ return -EINVAL; ++ } ++ ++ (*p_mask) = 0; ++ ++ err = i2c_burst_read_dt(&config->i2c_bus, RV8263C8_REGISTER_SECONDS_ALARM, value, ++ sizeof(value)); ++ if (err < 0) { ++ LOG_ERR("Error while reading alarm! Error: %i", err); ++ return err; ++ } ++ ++ if (value[0] <= MAX_SEC) { ++ timeptr->tm_sec = bcd2bin(value[0]) & SECONDS_BITS; ++ (*p_mask) |= RTC_ALARM_TIME_MASK_SECOND; ++ } ++ ++ if (value[1] <= MAX_MIN) { ++ timeptr->tm_min = bcd2bin(value[1]) & MINUTES_BITS; ++ (*p_mask) |= RTC_ALARM_TIME_MASK_MINUTE; ++ } ++ ++ if (value[2] <= MAX_HOUR) { ++ timeptr->tm_hour = bcd2bin(value[2]) & HOURS_BITS; ++ (*p_mask) |= RTC_ALARM_TIME_MASK_HOUR; ++ } ++ ++ if (value[3] <= MAX_MDAY) { ++ timeptr->tm_mday = bcd2bin(value[3]) & DATE_BITS; ++ (*p_mask) |= RTC_ALARM_TIME_MASK_MONTHDAY; ++ } ++ ++ if (value[4] <= MAX_WDAY) { ++ timeptr->tm_wday = bcd2bin(value[4]) & WEEKDAY_BITS; ++ (*p_mask) |= RTC_ALARM_TIME_MASK_WEEKDAY; ++ } ++ ++ return 0; ++} ++ ++static int rv8263c8_alarm_set_callback(const struct device *dev, uint16_t id, ++ rtc_alarm_callback callback, void *user_data) ++{ ++ int err; ++ const struct rv8263c8_config *config = dev->config; ++ ++#if DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios) ++ struct rv8263c8_data *data = dev->data; ++#endif ++ ++ ARG_UNUSED(id); ++ ++#if DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios) ++ if (config->int_gpio.port == NULL) { ++ return -ENOTSUP; ++ } ++ ++ (void)k_sem_take(&data->lock, K_FOREVER); ++ data->alarm_cb = callback; ++ data->alarm_cb_data = user_data; ++ k_sem_give(&data->lock); ++#else ++ return -ENOTSUP; ++#endif ++ ++ if ((callback == NULL) && (user_data == NULL)) { ++ LOG_DBG("Disable alarm function"); ++ ++ err = i2c_reg_update_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_CONTROL_2, ++ RV8263C8_BM_ALARM_INT_ENABLE, ++ RV8263C8_BM_ALARM_INT_DISABLE); ++ } else { ++ LOG_DBG("Enable alarm function"); ++ ++ err = i2c_reg_update_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_CONTROL_2, ++ RV8263C8_BM_ALARM_INT_ENABLE | RV8263C8_BM_AF, ++ RV8263C8_BM_ALARM_INT_ENABLE); ++ } ++ ++ if (err < 0) { ++ LOG_ERR("Error while writing CONTROL2! Error: %i", err); ++ } ++ ++ return err; ++} ++ ++static int rv8263c8_alarm_is_pending(const struct device *dev, uint16_t id) ++{ ++ int err; ++ uint8_t reg; ++ const struct rv8263c8_config *config = dev->config; ++ ++ ARG_UNUSED(id); ++ ++ err = i2c_reg_read_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_CONTROL_2, ®); ++ if (err) { ++ return err; ++ } ++ ++ if (reg & RV8263C8_BM_AF) { ++ reg &= ~RV8263C8_BM_AF; ++ err = i2c_reg_write_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_CONTROL_2, reg); ++ if (err) { ++ return err; ++ } ++ ++ return 1; ++ } ++ ++ return 0; ++} ++#endif ++ ++#if CONFIG_RTC_UPDATE && DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios) ++int rv8263_update_callback(const struct device *dev, rtc_update_callback callback, void *user_data) ++{ ++ struct rv8263c8_data *const data = dev->data; ++ ++ (void)k_sem_take(&data->lock, K_FOREVER); ++ data->update_cb = callback; ++ data->update_cb_data = user_data; ++ k_sem_give(&data->lock); ++ ++ /* Disable the update callback. */ ++ if ((callback == NULL) && (user_data == NULL)) { ++ return rv8263c8_update_disable_timer(dev); ++ } ++ ++ return rv8263c8_update_enable_timer(dev); ++} ++#endif ++ ++#ifdef CONFIG_RTC_CALIBRATION ++int rv8263c8_calibration_set(const struct device *dev, int32_t calibration) ++{ ++ int8_t offset; ++ int32_t test_mode0; ++ int32_t test_mode1; ++ int32_t offset_ppm_mode0; ++ int32_t offset_ppm_mode1; ++ const struct rv8263c8_config *config = dev->config; ++ ++ /* NOTE: The RTC API is using a PPB (Parts Per Billion) value. The RTC is using PPM. ++ * Here we calculate the offset when using MODE = 0. ++ * Formula from the application manual: ++ * Offset [ppm] = (calibration [ppb] / (4.34 [ppm] x 1000)) ++ */ ++ offset_ppm_mode0 = calibration / 4340; ++ ++ /* Here we calculate the offset when using MODE = 1. ++ * Formula from the application manual: ++ * Offset [ppm] = (calibration [ppb] / (4.069 [ppm] x 1000)) ++ */ ++ offset_ppm_mode1 = calibration / 4069; ++ ++ LOG_DBG("Offset Mode = 0: %i", offset_ppm_mode0); ++ LOG_DBG("Offset Mode = 1: %i", offset_ppm_mode1); ++ ++ test_mode0 = offset_ppm_mode0 * 4340; ++ test_mode0 = calibration - test_mode0; ++ test_mode1 = offset_ppm_mode1 * 4069; ++ test_mode1 = calibration - test_mode1; ++ ++ /* Compare the values and select the value with the smallest error. */ ++ test_mode0 = test_mode0 < 0 ? -test_mode0 : test_mode0; ++ test_mode1 = test_mode1 < 0 ? -test_mode1 : test_mode1; ++ if (test_mode0 > test_mode1) { ++ LOG_DBG("Use fast mode (Mode = 1)"); ++ ++ /* Error with MODE = 1 is smaller -> Use MODE = 1. */ ++ offset = RV8263_BM_FAST_MODE | (offset_ppm_mode1 & GENMASK(7, 0)); ++ } else { ++ LOG_DBG("Use normal mode (Mode = 0)"); ++ ++ /* Error with MODE = 0 is smaller -> Use MODE = 0. */ ++ offset = RV8263_BM_NORMAL_MODE | (offset_ppm_mode0 & GENMASK(7, 0)); ++ } ++ ++ LOG_DBG("Set offset value: %i", (offset & RV8263C8_BM_REGISTER_OFFSET)); ++ ++ return i2c_reg_write_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_OFFSET, offset); ++} ++ ++int rv8263c8_calibration_get(const struct device *dev, int32_t *calibration) ++{ ++ int err; ++ int32_t temp; ++ int8_t offset; ++ const struct rv8263c8_config *config = dev->config; ++ ++ if (calibration == NULL) { ++ return -EINVAL; ++ } ++ ++ err = i2c_reg_read_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_OFFSET, &offset); ++ if (err) { ++ return err; ++ } ++ ++ /* Convert the signed 7 bit into a signed 8 bit value. */ ++ if (offset & (0x01 << 6)) { ++ temp = offset | (0x01 << 7); ++ } else { ++ temp = offset & (0x3F); ++ temp &= ~(0x01 << 7); ++ } ++ ++ LOG_DBG("Read offset: %i", temp); ++ ++ if (offset & RV8263_BM_FAST_MODE) { ++ temp = temp * 4340L; ++ } else { ++ temp = temp * 4069L; ++ } ++ ++ *calibration = temp; ++ ++ return 0; ++} ++#endif ++ ++static const struct rtc_driver_api rv8263c8_driver_api = { ++ .set_time = rv8263c8_time_set, ++ .get_time = rv8263c8_time_get, ++#if CONFIG_RTC_ALARM ++ .alarm_get_supported_fields = rv8263c8_alarm_get_supported_fields, ++ .alarm_set_time = rv8263c8_alarm_set_time, ++ .alarm_get_time = rv8263c8_alarm_get_time, ++ .alarm_is_pending = rv8263c8_alarm_is_pending, ++ .alarm_set_callback = rv8263c8_alarm_set_callback, ++#endif ++#if CONFIG_RTC_UPDATE && DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios) ++ .update_set_callback = rv8263_update_callback, ++#endif ++#ifdef CONFIG_RTC_CALIBRATION ++ .set_calibration = rv8263c8_calibration_set, ++ .get_calibration = rv8263c8_calibration_get, ++#endif ++}; ++ ++#define RV8263_DEFINE(inst) \ ++ static struct rv8263c8_data rv8263c8_data_##inst; \ ++ static const struct rv8263c8_config rv8263c8_config_##inst = { \ ++ .i2c_bus = I2C_DT_SPEC_INST_GET(inst), \ ++ .clkout = DT_INST_ENUM_IDX(inst, clkout), \ ++ IF_ENABLED(1, \ ++ (.int_gpio = GPIO_DT_SPEC_INST_GET_OR(inst, int_gpios, {0})))}; \ ++ DEVICE_DT_INST_DEFINE(inst, &rv8263c8_init, NULL, &rv8263c8_data_##inst, \ ++ &rv8263c8_config_##inst, POST_KERNEL, CONFIG_RTC_INIT_PRIORITY, \ ++ &rv8263c8_driver_api); ++ ++DT_INST_FOREACH_STATUS_OKAY(RV8263_DEFINE) +diff --git a/dts/bindings/rtc/microcrystal,rv-8263-c8.yaml b/dts/bindings/rtc/microcrystal,rv-8263-c8.yaml +new file mode 100644 +index 0000000000..da6c246dbc +--- /dev/null ++++ b/dts/bindings/rtc/microcrystal,rv-8263-c8.yaml +@@ -0,0 +1,31 @@ ++# SPDX-License-Identifier: Apache-2.0 ++# Copyright (c) 2024 Daniel Kampert ++# Author: Daniel Kampert ++ ++description: Micro Crystal RV-8263-C8 RTC ++ ++compatible: "microcrystal,rv-8263-c8" ++ ++include: ++ - name: rtc-device.yaml ++ - name: i2c-device.yaml ++ ++properties: ++ int-gpios: ++ type: phandle-array ++ ++ clkout: ++ type: int ++ default: 0 ++ enum: ++ - 32768 ++ - 16384 ++ - 8192 ++ - 4096 ++ - 2048 ++ - 1024 ++ - 1 ++ - 0 ++ description: ++ Enable CLKOUT at given frequency. When disabled, CLKOUT pin is LOW. ++ The default is 0 and corresponds to the disable the CLKOUT signal. diff --git a/app/prj.conf b/app/prj.conf index b1d77734..32125906 100644 --- a/app/prj.conf +++ b/app/prj.conf @@ -180,7 +180,7 @@ CONFIG_WATCHFACE_BACKGROUND_NONE=n # Default watch CONFIG_POWER_MANAGEMENT_IDLE_TIMEOUT_SECONDS=20 -CONFIG_DEFAULT_CONFIGURATION_SENSORS_SUMMARY_REFRESH_INTERVAL_MS=100 +CONFIG_APPLICATIONS_CONFIGURATION_SENSORS_SUMMARY_REFRESH_INTERVAL_MS=100 # CST816S CONFIG_INPUT_CST816S_INTERRUPT=y diff --git a/app/src/applications/compass/compass_app.c b/app/src/applications/compass/compass_app.c index ffec4d1c..de12cfaf 100644 --- a/app/src/applications/compass/compass_app.c +++ b/app/src/applications/compass/compass_app.c @@ -35,7 +35,7 @@ static uint32_t cal_start_ms; static void compass_app_start(lv_obj_t *root, lv_group_t *group) { compass_ui_show(root, on_start_calibration); - refresh_timer = lv_timer_create(timer_callback, CONFIG_DEFAULT_CONFIGURATION_COMPASS_REFRESH_INTERVAL_MS, NULL); + refresh_timer = lv_timer_create(timer_callback, CONFIG_APPLICATIONS_CONFIGURATION_COMPASS_REFRESH_INTERVAL_MS, NULL); zsw_sensor_fusion_init(); } @@ -59,14 +59,14 @@ static void on_start_calibration(void) cal_start_ms = lv_tick_get(); zsw_popup_show("Calibration", "Rotate the watch 360 degrees\naround each x,y,z.\n a few times.", NULL, - CONFIG_DEFAULT_CONFIGURATION_COMPASS_CALIBRATION_TIME_S, false); + CONFIG_APPLICATIONS_CONFIGURATION_COMPASS_CALIBRATION_TIME_S, false); } static void timer_callback(lv_timer_t *timer) { float heading; if (is_calibrating && - (lv_tick_elaps(cal_start_ms) >= (CONFIG_DEFAULT_CONFIGURATION_COMPASS_CALIBRATION_TIME_S * 1000UL))) { + (lv_tick_elaps(cal_start_ms) >= (CONFIG_APPLICATIONS_CONFIGURATION_COMPASS_CALIBRATION_TIME_S * 1000UL))) { zsw_magnetometer_stop_calibration(); is_calibrating = false; zsw_popup_remove(); diff --git a/app/src/applications/sensors_summary/sensors_summary_app.c b/app/src/applications/sensors_summary/sensors_summary_app.c index 00c6c114..7b13ed2a 100644 --- a/app/src/applications/sensors_summary/sensors_summary_app.c +++ b/app/src/applications/sensors_summary/sensors_summary_app.c @@ -39,7 +39,7 @@ static void sensors_summary_app_start(lv_obj_t *root, lv_group_t *group) // Increase ODR since we want high accuracy here. zsw_pressure_sensor_set_odr(BOSCH_BMP581_ODR_160_HZ); - refresh_timer = lv_timer_create(timer_callback, CONFIG_DEFAULT_CONFIGURATION_SENSORS_SUMMARY_REFRESH_INTERVAL_MS, + refresh_timer = lv_timer_create(timer_callback, CONFIG_APPLICATIONS_CONFIGURATION_SENSORS_SUMMARY_REFRESH_INTERVAL_MS, NULL); } diff --git a/app/src/drivers/zsw_buzzer.c b/app/src/drivers/zsw_buzzer.c index 0478acb2..979cd6e8 100644 --- a/app/src/drivers/zsw_buzzer.c +++ b/app/src/drivers/zsw_buzzer.c @@ -71,22 +71,6 @@ static void buzzer_off_work_handler(struct k_work *work); static void run_next_tone_state(note_duration_t *state); static void pattern_timer_timeout(struct k_timer *timer_id); -static struct note_duration_t funkytown_song[] = { - {.note = C5, .duration = quarter}, - {.note = REST, .duration = eigth}, - {.note = C5, .duration = quarter}, - {.note = Bb4, .duration = quarter}, - {.note = C5, .duration = quarter}, - {.note = REST, .duration = quarter}, - {.note = G4, .duration = quarter}, - {.note = REST, .duration = quarter}, - {.note = G4, .duration = quarter}, - {.note = C5, .duration = quarter}, - {.note = F5, .duration = quarter}, - {.note = E5, .duration = quarter}, - {.note = C5, .duration = quarter} -}; - static struct note_duration_t mario_song[] = { {.note = E6, .duration = quarter}, {.note = REST, .duration = eigth}, diff --git a/app/src/events/zsw_periodic_event.c b/app/src/events/zsw_periodic_event.c index 27209a96..d3981bb9 100644 --- a/app/src/events/zsw_periodic_event.c +++ b/app/src/events/zsw_periodic_event.c @@ -105,6 +105,7 @@ static int zsw_timer_init(void) k_work_init_delayable(work, handle_slow_timeout); zbus_chan_finish(&periodic_event_10s_chan); + // TODO: Use the RTC instead of the timer in rev 5 to generate this zbus_chan_claim(&periodic_event_1s_chan, K_FOREVER); work = (struct k_work_delayable *)zbus_chan_user_data(&periodic_event_1s_chan); k_work_init_delayable(work, handle_mid_timeout); diff --git a/app/src/main.c b/app/src/main.c index 9fc95e39..9bf3a5bc 100644 --- a/app/src/main.c +++ b/app/src/main.c @@ -590,6 +590,7 @@ static void on_zbus_ble_data_callback(const struct zbus_channel *chan) switch (event->data.type) { case BLE_COMM_DATA_TYPE_SET_TIME: { if (event->data.data.time.seconds > 0) { + // TODO: Replace with set time struct timespec tspec; tspec.tv_sec = event->data.data.time.seconds; tspec.tv_nsec = 0; diff --git a/app/src/ui/watchfaces/0_digital/zsw_watchface_digital_ui.c b/app/src/ui/watchfaces/0_digital/zsw_watchface_digital_ui.c index ce084374..98f852bb 100644 --- a/app/src/ui/watchfaces/0_digital/zsw_watchface_digital_ui.c +++ b/app/src/ui/watchfaces/0_digital/zsw_watchface_digital_ui.c @@ -309,7 +309,7 @@ static void watchface_show(lv_obj_t *parent, watchface_app_evt_listener evt_cb, lv_obj_add_flag(ui_battery_arc, LV_OBJ_FLAG_EVENT_BUBBLE); lv_obj_clear_flag(ui_battery_arc, LV_OBJ_FLAG_PRESS_LOCK | LV_OBJ_FLAG_CLICK_FOCUSABLE | LV_OBJ_FLAG_SNAPPABLE | LV_OBJ_FLAG_SCROLLABLE | LV_OBJ_FLAG_SCROLL_ELASTIC | LV_OBJ_FLAG_SCROLL_MOMENTUM | - LV_OBJ_FLAG_SCROLL_CHAIN); + LV_OBJ_FLAG_SCROLL_CHAIN | LV_OBJ_FLAG_CLICKABLE); lv_obj_set_style_arc_width(ui_battery_arc, 3, LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_set_style_arc_color(ui_battery_arc, lv_color_hex(0xFFB140), LV_PART_INDICATOR | LV_STATE_DEFAULT); @@ -324,8 +324,9 @@ static void watchface_show(lv_obj_t *parent, watchface_app_evt_listener evt_cb, lv_obj_set_width(ui_battery_arc_icon, LV_SIZE_CONTENT); lv_obj_set_height(ui_battery_arc_icon, LV_SIZE_CONTENT); lv_obj_set_align(ui_battery_arc_icon, LV_ALIGN_CENTER); + lv_obj_add_flag(ui_battery_arc_icon, LV_OBJ_FLAG_CLICKABLE | LV_OBJ_FLAG_CLICK_FOCUSABLE); lv_obj_clear_flag(ui_battery_arc_icon, - LV_OBJ_FLAG_PRESS_LOCK | LV_OBJ_FLAG_CLICK_FOCUSABLE | LV_OBJ_FLAG_SCROLLABLE | LV_OBJ_FLAG_SCROLL_ELASTIC | + LV_OBJ_FLAG_PRESS_LOCK | LV_OBJ_FLAG_SCROLLABLE | LV_OBJ_FLAG_SCROLL_ELASTIC | LV_OBJ_FLAG_SCROLL_MOMENTUM | LV_OBJ_FLAG_SCROLL_CHAIN); lv_obj_set_style_img_recolor(ui_battery_arc_icon, lv_color_hex(0xFFFFFF), LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_set_style_img_recolor_opa(ui_battery_arc_icon, 255, LV_PART_MAIN | LV_STATE_DEFAULT); @@ -348,10 +349,9 @@ static void watchface_show(lv_obj_t *parent, watchface_app_evt_listener evt_cb, lv_obj_set_x(ui_step_arc, -52); lv_obj_set_y(ui_step_arc, 67); lv_obj_set_align(ui_step_arc, LV_ALIGN_CENTER); - lv_obj_add_flag(ui_step_arc, LV_OBJ_FLAG_EVENT_BUBBLE); lv_obj_clear_flag(ui_step_arc, LV_OBJ_FLAG_PRESS_LOCK | LV_OBJ_FLAG_CLICK_FOCUSABLE | LV_OBJ_FLAG_SNAPPABLE | LV_OBJ_FLAG_SCROLLABLE | LV_OBJ_FLAG_SCROLL_ELASTIC | LV_OBJ_FLAG_SCROLL_MOMENTUM | - LV_OBJ_FLAG_SCROLL_CHAIN); + LV_OBJ_FLAG_SCROLL_CHAIN | LV_OBJ_FLAG_CLICKABLE); lv_obj_set_style_arc_width(ui_step_arc, 3, LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_set_style_arc_color(ui_step_arc, lv_color_hex(0x9D3BE0), LV_PART_INDICATOR | LV_STATE_DEFAULT); @@ -367,6 +367,7 @@ static void watchface_show(lv_obj_t *parent, watchface_app_evt_listener evt_cb, lv_obj_set_width(ui_step_arc_icon, LV_SIZE_CONTENT); lv_obj_set_height(ui_step_arc_icon, LV_SIZE_CONTENT); lv_obj_set_align(ui_step_arc_icon, LV_ALIGN_CENTER); + lv_obj_add_flag(ui_step_arc_icon, LV_OBJ_FLAG_CLICKABLE | LV_OBJ_FLAG_CLICK_FOCUSABLE); lv_obj_clear_flag(ui_step_arc_icon, LV_OBJ_FLAG_PRESS_LOCK | LV_OBJ_FLAG_CLICK_FOCUSABLE | LV_OBJ_FLAG_SCROLLABLE | LV_OBJ_FLAG_SCROLL_ELASTIC | LV_OBJ_FLAG_SCROLL_MOMENTUM | LV_OBJ_FLAG_SCROLL_CHAIN); lv_obj_set_style_img_recolor(ui_step_arc_icon, lv_color_hex(0xFFFFFF), LV_PART_MAIN | LV_STATE_DEFAULT); @@ -465,8 +466,8 @@ static void watchface_show(lv_obj_t *parent, watchface_app_evt_listener evt_cb, #endif // Listeners - lv_obj_add_event_cb(ui_battery_arc, arc_event_pressed, LV_EVENT_CLICKED, NULL); - lv_obj_add_event_cb(ui_step_arc, arc_event_pressed, LV_EVENT_CLICKED, NULL); + lv_obj_add_event_cb(ui_battery_arc_icon, arc_event_pressed, LV_EVENT_CLICKED, NULL); + lv_obj_add_event_cb(ui_step_arc_icon, arc_event_pressed, LV_EVENT_CLICKED, NULL); } static void watchface_remove(void) @@ -645,10 +646,10 @@ static const void *watchface_get_preview_img(void) static void arc_event_pressed(lv_event_t *e) { watchface_app_evt_t evt = {.type = WATCHFACE_APP_EVENT_OPEN_APP}; - if (lv_event_get_target(e) == ui_battery_arc) { + if (lv_event_get_target(e) == ui_battery_arc_icon) { evt.data.app = WATCHFACE_APP_EVT_CLICK_BATT; ui_evt_cb(evt); - } else if (lv_event_get_target(e) == ui_step_arc) { + } else if (lv_event_get_target(e) == ui_step_arc_icon) { evt.data.app = WATCHFACE_APP_EVT_CLICK_STEP; ui_evt_cb(evt); } diff --git a/app/src/zsw_clock.c b/app/src/zsw_clock.c index ce187f74..fc46a423 100644 --- a/app/src/zsw_clock.c +++ b/app/src/zsw_clock.c @@ -15,22 +15,19 @@ * along with this program. If not, see . */ -#include -#include -#include -#include #include #include #include +#include +#include + #include #include -#include #include #include -#include -#include -#include +#include "zsw_clock.h" +#include "zsw_retained_ram_storage.h" #include "events/zsw_periodic_event.h" static void zbus_periodic_slow_callback(const struct zbus_channel *chan); @@ -38,6 +35,16 @@ static void zbus_periodic_slow_callback(const struct zbus_channel *chan); ZBUS_CHAN_DECLARE(periodic_event_1s_chan); ZBUS_LISTENER_DEFINE(zsw_clock_lis, zbus_periodic_slow_callback); +void zsw_clock_set_time(zsw_timeval_t *ztm) +{ + struct timespec tspec; + + tspec.tv_sec = ztm->tm.tm_sec; + tspec.tv_nsec = 0; + + clock_settime(CLOCK_REALTIME, &tspec); +} + void zsw_clock_get_time(zsw_timeval_t *ztm) { struct timeval tv; @@ -49,10 +56,8 @@ void zsw_clock_get_time(zsw_timeval_t *ztm) // Add one to the month because we want to count from December instead of January ztm->tm.tm_mon += 1; - // Add 1900 to the year because we want to count from 0 + // Add 1900 to the year because we want to count from 1900 ztm->tm.tm_year += 1900; - - ztm->tv_usec = tv.tv_usec; } time_t zsw_clock_get_time_unix(void) @@ -72,8 +77,10 @@ static void zbus_periodic_slow_callback(const struct zbus_channel *chan) static int zsw_clock_init(void) { struct timespec tspec; + tspec.tv_sec = retained.current_time_seconds; tspec.tv_nsec = 0; + clock_settime(CLOCK_REALTIME, &tspec); if (strlen(retained.timezone) > 0) { setenv("TZ", retained.timezone, 1); diff --git a/app/src/zsw_clock.h b/app/src/zsw_clock.h index 5290ceb9..cbfd188d 100644 --- a/app/src/zsw_clock.h +++ b/app/src/zsw_clock.h @@ -20,10 +20,17 @@ #include #include +typedef void (*zsw_clock_on_update)(void); + typedef struct { struct tm tm; /**< Modified time object with 1900 added to the year and the month increased by one. */ uint32_t tv_usec; } zsw_timeval_t; +void zsw_clock_add_update(zsw_clock_on_update callback); + +void zsw_clock_set_time(zsw_timeval_t *ztm); + void zsw_clock_get_time(zsw_timeval_t *ztm); + time_t zsw_clock_get_time_unix(void); \ No newline at end of file diff --git a/app/src/zsw_rtc.c b/app/src/zsw_rtc.c new file mode 100644 index 00000000..33977beb --- /dev/null +++ b/app/src/zsw_rtc.c @@ -0,0 +1,98 @@ +/* + * This file is part of ZSWatch project . + * Copyright (c) 2023 Jakob Krantz. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "zsw_clock.h" +#include "events/zsw_periodic_event.h" + +const struct device *const rtc = DEVICE_DT_GET(DT_NODELABEL(rv_8263_c8)); + +LOG_MODULE_REGISTER(zsw_clock, LOG_LEVEL_DBG); + +void zsw_clock_add_update(zsw_clock_on_update callback) +{ + +} + +void zsw_clock_set_time(zsw_timeval_t *ztm) +{ + struct rtc_time time; + + time.tm_sec = ztm->tm.tm_sec; + time.tm_min = ztm->tm.tm_min; + time.tm_hour = ztm->tm.tm_hour; + time.tm_wday = ztm->tm.tm_wday; + time.tm_mday = ztm->tm.tm_mday; + time.tm_mon = ztm->tm.tm_mon - 1; + time.tm_year = ztm->tm.tm_year - 1900; + + //rtc_set_time(rtc, &time); +} + +void zsw_clock_get_time(zsw_timeval_t *ztm) +{ + struct rtc_time time; + + //rtc_get_time(rtc, &time); + + ztm->tm.tm_sec = time.tm_sec; + ztm->tm.tm_min = time.tm_min; + ztm->tm.tm_hour = time.tm_hour; + ztm->tm.tm_wday = time.tm_wday; + ztm->tm.tm_mday = time.tm_mday; + + // Add one to the month because we want to count from December instead of January + ztm->tm.tm_mon = time.tm_mon + 1; + + // Add 1900 to the year because we want to count from 0 + ztm->tm.tm_year = time.tm_year + 1900; +} + +time_t zsw_clock_get_time_unix(void) +{ + // TODO: + return 0; +} + +void on_rtc_update(const struct device *dev, void *user_data) +{ +} + +static int zsw_clock_init(void) +{ + if (!device_is_ready(rtc)) { + LOG_ERR("Device not ready!"); + return -1; + } + + rtc_update_set_callback(rtc, on_rtc_update, NULL); + + return 0; +} + +SYS_INIT(zsw_clock_init, APPLICATION, 2); \ No newline at end of file