diff --git a/CMakeLists.txt b/CMakeLists.txt index 7b142ba..78b102e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,4 +6,7 @@ project(blinky) zephyr_include_directories(inc) -target_sources(app PRIVATE src/main.c) +target_sources(app PRIVATE + src/main.c + src/battery_module.c +) diff --git a/boards/atguigu/mini_keyboard/mini_keyboard.dts b/boards/atguigu/mini_keyboard/mini_keyboard.dts index e25be1b..4dd8408 100644 --- a/boards/atguigu/mini_keyboard/mini_keyboard.dts +++ b/boards/atguigu/mini_keyboard/mini_keyboard.dts @@ -1,6 +1,7 @@ /dts-v1/; #include #include "mini_keyboard-pinctrl.dtsi" +#include / { model = "Mini keyboard"; @@ -22,6 +23,15 @@ gpios = <&gpio1 2 GPIO_ACTIVE_LOW>; }; }; + + vbatt: vbatt { + compatible = "voltage-divider"; + io-channels = <&adc 7>; + output-ohms = <100000>; + full-ohms = <200000>; + power-gpios = <&gpio0 9 GPIO_ACTIVE_HIGH>; + power-on-sample-delay-us = <200>; + }; }; &flash0 { @@ -52,6 +62,26 @@ }; }; +&uicr { + nfct-pins-as-gpios; +}; + +&adc { + status = "okay"; + #address-cells = <1>; + #size-cells = <0>; + + channel@7 { + reg = <7>; + zephyr,gain = "ADC_GAIN_1_4"; + zephyr,reference = "ADC_REF_INTERNAL"; + zephyr,acquisition-time = ; + zephyr,input-positive = ; + zephyr,resolution = <14>; + zephyr,oversampling = <4>; + }; +}; + &gpio0 { status = "okay"; }; diff --git a/prj.conf b/prj.conf index 5f11b8f..073bab0 100644 --- a/prj.conf +++ b/prj.conf @@ -3,6 +3,8 @@ CONFIG_CAF_BUTTONS=y CONFIG_CAF_BUTTONS_DEF_PATH="buttons_def.h" CONFIG_GPIO=y CONFIG_REBOOT=y +CONFIG_SENSOR=y +CONFIG_ADC=y CONFIG_HEAP_MEM_POOL_SIZE=2048 CONFIG_LOG=y CONFIG_ASSERT=y diff --git a/src/battery_module.c b/src/battery_module.c new file mode 100644 index 0000000..11f96ac --- /dev/null +++ b/src/battery_module.c @@ -0,0 +1,181 @@ +#include +#include + +#include + +#define MODULE battery_module +#include +#include + +#include +#include +#include +#include +#include +#include + +LOG_MODULE_REGISTER(MODULE, LOG_LEVEL_INF); + +#define VBATT_NODE DT_PATH(vbatt) +#define BATTERY_SAMPLE_INTERVAL K_SECONDS(1) + +BUILD_ASSERT(DT_NODE_HAS_STATUS(VBATT_NODE, okay), + "Missing /vbatt voltage-divider node in devicetree"); + +static const struct device *const vbatt_dev = DEVICE_DT_GET(VBATT_NODE); +static struct k_work_delayable battery_sample_work; +static bool initialized; +static bool running; + +static int sensor_value_to_mv(const struct sensor_value *value) +{ + return (value->val1 * 1000) + (value->val2 / 1000); +} + +static int measurement_enable(bool enable) +{ + enum pm_device_action action = enable ? PM_DEVICE_ACTION_RESUME + : PM_DEVICE_ACTION_SUSPEND; + int err = pm_device_action_run(vbatt_dev, action); + + if (err && (err != -EALREADY) && (err != -ENOTSUP)) { + LOG_ERR("Cannot %s vbatt sensor (%d)", enable ? "resume" : "suspend", err); + return err; + } + + return 0; +} + +static void battery_sample_fn(struct k_work *work) +{ + struct sensor_value voltage; + int err; + + ARG_UNUSED(work); + + if (!running) { + return; + } + + err = sensor_sample_fetch(vbatt_dev); + if (err) { + LOG_WRN("Battery sample fetch failed (%d)", err); + goto reschedule; + } + + err = sensor_channel_get(vbatt_dev, SENSOR_CHAN_VOLTAGE, &voltage); + if (err) { + LOG_WRN("Battery channel get failed (%d)", err); + goto reschedule; + } + + LOG_INF("Battery voltage: %d mV", sensor_value_to_mv(&voltage)); + +reschedule: + if (running) { + k_work_reschedule(&battery_sample_work, BATTERY_SAMPLE_INTERVAL); + } +} + +static int module_init(void) +{ + if (!device_is_ready(vbatt_dev)) { + LOG_ERR("vbatt device not ready"); + return -ENODEV; + } + + k_work_init_delayable(&battery_sample_work, battery_sample_fn); + + return 0; +} + +static int module_start(void) +{ + int err; + + if (running) { + return 0; + } + + err = measurement_enable(true); + if (err) { + return err; + } + + running = true; + k_work_reschedule(&battery_sample_work, K_NO_WAIT); + + return 0; +} + +static void module_pause(void) +{ + if (!running) { + return; + } + + (void)k_work_cancel_delayable(&battery_sample_work); + (void)measurement_enable(false); + running = false; +} + +static bool app_event_handler(const struct app_event_header *aeh) +{ + if (is_module_state_event(aeh)) { + const struct module_state_event *event = cast_module_state_event(aeh); + + if (check_state(event, MODULE_ID(main), MODULE_STATE_READY)) { + int err; + + if (!initialized) { + err = module_init(); + if (err) { + module_set_state(MODULE_STATE_ERROR); + return false; + } + + initialized = true; + } + + err = module_start(); + if (err) { + module_set_state(MODULE_STATE_ERROR); + } else { + module_set_state(MODULE_STATE_READY); + } + } + + return false; + } + + if (is_power_down_event(aeh)) { + if (initialized) { + module_pause(); + module_set_state(MODULE_STATE_STANDBY); + } + + return false; + } + + if (is_wake_up_event(aeh)) { + if (initialized) { + int err = module_start(); + + if (err) { + module_set_state(MODULE_STATE_ERROR); + } else { + module_set_state(MODULE_STATE_READY); + } + } + + return false; + } + + __ASSERT_NO_MSG(false); + return false; +} + +APP_EVENT_LISTENER(MODULE, app_event_handler); +APP_EVENT_SUBSCRIBE(MODULE, module_state_event); +APP_EVENT_SUBSCRIBE_EARLY(MODULE, power_down_event); +APP_EVENT_SUBSCRIBE(MODULE, wake_up_event);