aboutsummaryrefslogtreecommitdiff
path: root/platform/atm2/ATM22xx-x1x/driver
diff options
context:
space:
mode:
Diffstat (limited to 'platform/atm2/ATM22xx-x1x/driver')
-rw-r--r--platform/atm2/ATM22xx-x1x/driver/atm_gpio/atm_gpio.h198
-rw-r--r--platform/atm2/ATM22xx-x1x/driver/atm_pm/atm_pm.h120
-rw-r--r--platform/atm2/ATM22xx-x1x/driver/atm_vkey/atm_vkey.h898
-rw-r--r--platform/atm2/ATM22xx-x1x/driver/batt_model/batt_model.h86
-rw-r--r--platform/atm2/ATM22xx-x1x/driver/gadc/gadc.h122
-rw-r--r--platform/atm2/ATM22xx-x1x/driver/ir/ir.h63
-rw-r--r--platform/atm2/ATM22xx-x1x/driver/ir/ir_ctl.h147
-rw-r--r--platform/atm2/ATM22xx-x1x/driver/ir/nec_ir.h47
-rw-r--r--platform/atm2/ATM22xx-x1x/driver/keyboard/keyboard.h60
-rw-r--r--platform/atm2/ATM22xx-x1x/driver/keyboard/keyboard_internal.h262
-rw-r--r--platform/atm2/ATM22xx-x1x/driver/led_blink/led_blink.h84
-rw-r--r--platform/atm2/ATM22xx-x1x/driver/sw_event/sw_event.h91
-rw-r--r--platform/atm2/ATM22xx-x1x/driver/sw_timer/sw_timer.h95
-rw-r--r--platform/atm2/ATM22xx-x1x/driver/timer/timer.h266
14 files changed, 2539 insertions, 0 deletions
diff --git a/platform/atm2/ATM22xx-x1x/driver/atm_gpio/atm_gpio.h b/platform/atm2/ATM22xx-x1x/driver/atm_gpio/atm_gpio.h
new file mode 100644
index 0000000..7a9d647
--- /dev/null
+++ b/platform/atm2/ATM22xx-x1x/driver/atm_gpio/atm_gpio.h
@@ -0,0 +1,198 @@
+/*
+ *-----------------------------------------------------------------------------
+ * The confidential and proprietary information contained in this file may
+ * only be used by a person authorised under and to the extent permitted
+ * by a subsisting licensing agreement from ARM Limited.
+ *
+ * (C) COPYRIGHT 2010-2013 ARM Limited.
+ * ALL RIGHTS RESERVED
+ *
+ * This entire notice must be reproduced on all copies of this file
+ * and copies of this file may only be made by a person if such person is
+ * permitted to do so under the terms of a subsisting license agreement
+ * from ARM Limited.
+ *
+ * SVN Information
+ *
+ * Checked In : $Date: 2012-05-28 18:02:18 +0100 (Mon, 28 May 2012) $
+ *
+ * Revision : $Revision: 210377 $
+ *
+ * Release Information : Cortex-M System Design Kit-r1p0-00rel0
+ *-----------------------------------------------------------------------------
+ */
+/*************************************************************************
+ * @file atm_gpio.h
+ * @brief ATM GPIO Device Driver Header File
+ * Copyright (C) Atmosic 2019-2020
+ ******************************************************************************/
+
+/** @addtogroup ATM_GPIO_Driver_definitions
+ This file defines Atmosic GPIO Driver functions.
+ @{
+ */
+
+#pragma once
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/// The atm_gpio error code
+typedef enum atm_gpio_err_s {
+ /// No Error
+ ATM_GPIO_NO_ERR,
+ /// GPIO invalid
+ ATM_GPIO_ERR_INVALID,
+ /// GPIO unconfigured
+ ATM_GPIO_ERR_UNCFG
+} atm_gpio_err_t;
+
+ /**
+ * @brief Sets up internal config for use as GPIO
+ * This function needs to be called prior to any GPIO configuration.
+ * @param[in] gpio pin number
+ */
+
+ void atm_gpio_setup(uint8_t gpio);
+
+ /**
+ * @brief Set GPIO PIN as input.
+ * @param[in] gpio pin number
+ */
+
+ void atm_gpio_set_input(uint8_t gpio);
+
+ /**
+ * @brief Clear GPIO PIN as input.
+ * @param[in] gpio pin number
+ */
+
+ void atm_gpio_clear_input(uint8_t gpio);
+
+ /**
+ * @brief Set GPIO PIN as output.
+ * @param[in] gpio pin number
+ */
+
+ void atm_gpio_set_output(uint8_t gpio);
+
+ /**
+ * @brief Clear GPIO PIN as output.
+ * @param[in] gpio pin number
+ */
+
+ void atm_gpio_clear_output(uint8_t gpio);
+
+ /**
+ * @brief Set GPIO PIN pullup.
+ * @param[in] gpio pin number
+ */
+
+ void atm_gpio_set_pullup(uint8_t gpio);
+
+ /**
+ * @brief Clear GPIO PIN pullup.
+ * @param[in] gpio pin number
+ */
+
+ void atm_gpio_clear_pullup(uint8_t gpio);
+
+ /**
+ * @brief Read GPIO Interrupt Status.
+ * @param[in] gpio pin number
+ */
+
+ bool atm_gpio_read_int_status(uint8_t gpio);
+
+ /**
+ * @brief Clear GPIO PIN Interrupt Status.
+ * @param[in] gpio pin number
+ */
+
+ void atm_gpio_clear_int_status(uint8_t gpio);
+
+ /**
+ * @brief Enable GPIO PIN Interrupt.
+ * @param[in] gpio pin number
+ */
+
+ void atm_gpio_set_int_enable(uint8_t gpio);
+
+ /**
+ * @brief Disable GPIO PIN Interrupt.
+ * @param[in] gpio pin number
+ */
+
+ void atm_gpio_set_int_disable(uint8_t gpio);
+
+ /**
+ * @brief Setup GPIO PIN Interrupt as high level.
+ * @param[in] gpio pin number
+ */
+
+ void atm_gpio_int_set_high(uint8_t gpio);
+
+ /**
+ * @brief Setup GPIO PIN Interrupt as rising edge.
+ * @param[in] gpio pin number
+ */
+
+ void atm_gpio_int_set_rising(uint8_t gpio);
+
+ /**
+ * @brief Setup GPIO PIN Interrupt as low level.
+ * @param[in] gpio pin number
+ */
+
+ void atm_gpio_int_set_low(uint8_t gpio);
+
+ /**
+ * @brief Setup GPIO PIN Interrupt as falling edge.
+ * @param[in] gpio pin number
+ */
+
+ void atm_gpio_int_set_falling(uint8_t gpio);
+
+ /**
+ * @brief Setup GPIO PIN Interrupt as high or low level.
+ * @param[in] gpio pin number
+ * @return Previous Interrupt edge configure.
+ */
+
+ bool atm_gpio_toggle_int_edge(uint8_t gpio);
+
+ /**
+ * @brief Write GPIO PIN output (1: high, 0: low)
+ * @param[in] gpio pin number
+ * @param[in] value (0/1)
+ */
+
+ void atm_gpio_write(uint8_t gpio, bool value);
+
+ /**
+ * @brief Read GPIO PIN.
+ * @param[in] gpio pin number
+ */
+
+ bool atm_gpio_read_gpio(uint8_t gpio);
+
+ /**
+ * @brief Toggle GPIO PIN.
+ * @param[in] gpio pin number
+ * @return GPIO pin value after toggling.
+ */
+ bool atm_gpio_toggle(uint8_t gpio);
+
+ /**
+ * @brief GPIO pin validation.
+ * @param[in] gpio pin number
+ * @return Error code (@ref atm_gpio_err_t)
+ */
+ uint8_t atm_gpio_validate_gpio(uint8_t gpio);
+
+#ifdef __cplusplus
+}
+#endif
+
+ /*@}*/ /* end of group ATM_GPIO_Driver_definitions Atmosic GPIO Driver definitions */
diff --git a/platform/atm2/ATM22xx-x1x/driver/atm_pm/atm_pm.h b/platform/atm2/ATM22xx-x1x/driver/atm_pm/atm_pm.h
new file mode 100644
index 0000000..fbab226
--- /dev/null
+++ b/platform/atm2/ATM22xx-x1x/driver/atm_pm/atm_pm.h
@@ -0,0 +1,120 @@
+/*******************************************************************************
+ *
+ * @file atm_pm.h
+ *
+ * @brief Power wakelock manager
+ *
+ * Copyright (C) Atmosic 2020
+ *
+ *******************************************************************************
+ */
+
+#pragma once
+
+/**
+ *******************************************************************************
+ * @defgroup ATM_DEV_PM Power managerment
+ * @ingroup DRIVERS
+ * @brief ATM power managerment driver
+ *
+ * This module contains the necessary functions to control the power modes
+ *
+ * @{
+ *******************************************************************************
+*/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/// Lock type of low power mode
+typedef enum {
+ /// Sleep mode
+ PM_LOCK_SLEEP,
+ /// Retention mode
+ PM_LOCK_RETENTION,
+ /// Hibernation mode
+ PM_LOCK_HIBERNATE,
+ PM_LOCK_TYPE_MAX,
+} pm_lock_type_e;
+
+/// Lock identifier
+typedef struct {
+ /// Lock id
+ uint32_t id;
+ /// Lock type
+ pm_lock_type_e type;
+} pm_lock_id_t;
+
+/**
+ *******************************************************************************
+ * @brief Allocate lock identifier with specified id
+ * @param[in] id Identifier of specific lock and id.
+ * @param[in] type Type of low power to be manipulated.
+ *******************************************************************************
+ */
+bool atm_pm_realloc(pm_lock_id_t id, pm_lock_type_e type);
+
+/**
+ *******************************************************************************
+ * @brief Allocate lock identifier.
+ * @param[in] type Type of low power to be manipulated.
+ * @return Identifier of specific lock.
+ *******************************************************************************
+ */
+pm_lock_id_t atm_pm_alloc(pm_lock_type_e type);
+
+/**
+ *******************************************************************************
+ * @brief Free allocated lock
+ * @param[in] id Identifier of specific lock and id.
+ *******************************************************************************
+ */
+void atm_pm_free(pm_lock_id_t id);
+
+/**
+ *******************************************************************************
+ * @brief Lock specific power mode
+ * @param[in] index Lock index
+ *******************************************************************************
+ */
+void atm_pm_lock(pm_lock_id_t index);
+
+/**
+ *******************************************************************************
+ * @brief Unlock specific power mode
+ * @param[in] index Lock index
+ *******************************************************************************
+ */
+void atm_pm_unlock(pm_lock_id_t index);
+
+/**
+ *******************************************************************************
+ * @brief Print wakelocks
+ *******************************************************************************
+ */
+void atm_pm_lock_info(void);
+
+/**
+ *******************************************************************************
+ * @brief Set restart time from hibernate
+ * @param[in] restart_time Hibernation time in centisec.
+ *******************************************************************************
+ */
+void atm_pm_set_hib_restart_time(uint32_t restart_time);
+
+/**
+ *******************************************************************************
+ * @brief Set function of atm_pm replacement vector of hibernate.
+ * @param[in] cb Function of replacement vector.
+ * @note The cb should be placed in RAM using __FAST.
+ *******************************************************************************
+ */
+void atm_pm_set_hibernate_cb(rep_vec_fn__ret_bool__int32_t__uint32_t__t cb);
+
+#ifdef __cplusplus
+}
+#endif
+
+///@}
+
diff --git a/platform/atm2/ATM22xx-x1x/driver/atm_vkey/atm_vkey.h b/platform/atm2/ATM22xx-x1x/driver/atm_vkey/atm_vkey.h
new file mode 100644
index 0000000..cc84616
--- /dev/null
+++ b/platform/atm2/ATM22xx-x1x/driver/atm_vkey/atm_vkey.h
@@ -0,0 +1,898 @@
+/**
+ *******************************************************************************
+ *
+ * @file atm_vkey.h
+ *
+ * @brief Virtual key event modeling
+ *
+ * Copyright (C) Atmosic 2021-2023
+ *
+ *******************************************************************************
+ */
+#pragma once
+
+#include "co_math.h"
+
+/**
+ *******************************************************************************
+ * @defgroup ATM_DEV_VKEY Virtual key models
+ * @ingroup DRIVERS
+ * @brief ATM virtual key handling module
+ *
+ * This module contains the necessary procedure to deal with keys based user
+ * events. In general, application registers interesting events initially and
+ * provids virtual key number with pressed(or released) information when user
+ * operated via some physical input like key matrixes and gpio buttons. If some
+ * event condition matched, callback function which was registered will be called.
+ *
+ * ## Register event table
+ * The Application could define preferred key events by atm_vk_reg_t type and
+ * form them as atm_vk_reg_t array table. Each array entry could be easily
+ * declared by macro such as @ref VKEY_CLICK, @ref VKEY_DB_CLICK, and so on.
+ * By calling @ref atm_vkey_add_table function, application could add the event
+ * table into atm_vkey module. Application could add many event tables and each
+ * table adding would return a handle to application.
+ *
+ * Below is two sets of event array registered in a HID keyboard example:
+ * @code
+ * // Key event on connected state
+ * static const atm_vk_reg_t kbd_conn_vkeys[] = {
+ * VKEY_DOWN_FIRST(kbd_send_rpt, atm_vk_any),
+ * VKEY_DOWN_MORE (kbd_send_rpt_more, atm_vk_any),
+ * VKEY_UP_INTER (kbd_send_rpt_more, atm_vk_any),
+ * VKEY_UP_LAST (kbd_send_rpt, atm_vk_any),
+ * VKEY_HOLD_1KEY (kbd_hold_test1, NULL, 1000, VK_FN),
+ * VKEY_HOLD_2KEY (kbd_hold_test2, NULL, 1000, VK_FN, VK_F1),
+ * VKEY_HOLD_1KEY_CLICK(kbd_clear_all_bond, VK_U, VK_FN),
+ * VKEY_HOLD_2KEY_CLICK(kbd_enter_rftest, VK_ENTER, VK_FN, VK_F1),
+ * };
+ *
+ * // Key event on disconnected state
+ * static const atm_vk_reg_t kbd_unconn_vkeys[] = {
+ * VKEY_DOWN_FIRST(kbd_save_key, atm_vk_any),
+ * VKEY_DOWN_MORE (kbd_save_key_more, atm_vk_any),
+ * VKEY_UP_INTER (kbd_save_key_more, atm_vk_any),
+ * VKEY_UP_LAST (kbd_save_key, atm_vk_any),
+ * VKEY_HOLD_1KEY (kbd_hold_test1, NULL, 1000, VK_FN),
+ * VKEY_HOLD_2KEY (kbd_hold_test2, NULL, 1000, VK_FN, VK_F1),
+ * VKEY_HOLD_1KEY_CLICK(kbd_clear_all_bond, VK_U, VK_FN),
+ * VKEY_HOLD_2KEY_CLICK(kbd_enter_rftest, VK_ENTER, VK_FN, VK_F1),
+ * };
+ *
+ * ...
+ *
+ * // Register events for connected state
+ * vkey_handles[0] = atm_vkey_add_table(kbd_unconn_vkeys,
+ * ARRAY_LEN(kbd_unconn_vkeys), &mmi_vkey_ctx);
+ * // Register events for disconnected state
+ * vkey_handles[1] = atm_vkey_add_table(kbd_conn_vkeys,
+ * ARRAY_LEN(kbd_conn_vkeys), &mmi_vkey_ctx);
+ * @endcode
+ *
+ * ## Feed virtual key
+ * When application was triggered by user input, it could use @ref atm_vkey_feed
+ * to provide the virtual key index and its pressed or release state.
+ *
+ * Below is the example of feeding virtual key into atm_vkey module:
+ * @code
+ *
+ * // key matrix callback function
+ * static void key_matrix_event(bool pressed, uint32_t idx)
+ * {
+ * switch (atm_asm_get_current_state(MMI_S_TBL_IDX)) {
+ * // Feed key when state is disconnected
+ * case MMI_S_BOOTED:
+ * case MMI_S_INITING:
+ * case MMI_S_IDLE: {
+ * atm_vkey_feed(vkey_handles[0], idx, pressed);
+ * } break;
+ * ...
+ * // Feed key when state is connected
+ * case MMI_S_HID_ONLY: {
+ * atm_vkey_feed(vkey_handles[1], idx, pressed);
+ * } break;
+ * ...
+ * }
+ * }
+ * @endcode
+ *
+ * @{
+ *
+ *******************************************************************************
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef ATM_VKEY_MAX
+#define ATM_VKEY_MAX 32
+#endif
+
+/**
+ *******************************************************************************
+ * @brief Create double click event entry.
+ * The entry is part of a @ref atm_vk_reg_t array which is used to
+ * registered into @ref atm_vkey_add_table function.
+ * @param[in] fn Callback function
+ * @ref atm_vk_dck_reg_t.cb
+ * @param[in] vkey Key index to monitor. @ref atm_vk_dck_reg_t.vkey
+ * @param[in] max_ms Maximal triggered interval within two clicks.
+ * @ref atm_vk_dck_reg_t.max_ms
+ *
+ * Here is the example of monitoring double click events on key index x or
+ * y while the time between two clicks is shorter than 100 milliseconds:
+ * - Method one:
+ * @code
+ *
+ * // Handler for double click event
+ * static void vkey_any_dbclick(atm_vk_dck_evt_t *evt, void const *ctx)
+ * {
+ * DEBUG_TRACE("Key (%03u) double clicked", evt->vkey);
+ *
+ * if (evt->vkey == x) {
+ * // Doing things when key x was double clicked
+ * ...
+ * } else if (evt->vkey == y) {
+ * // Doing things when key y was double clicked
+ * ...
+ * }
+ * }
+ *
+ * // Key event array
+ * static const atm_vk_reg_t key_event_array[] = {
+ * ...
+ * VKEY_DB_CLICK(vkey_any_dbclick, atm_vk_any, 100),
+ * ...
+ * };
+ * @endcode
+ * - Method two:
+ * @code
+ *
+ * // Handler for double click event of key 3
+ * static void vkey_x_dbclick(atm_vk_dck_evt_t *evt, void const *ctx)
+ * {
+ * DEBUG_TRACE("Key (%03u) double clicked", evt->vkey);
+ * // Doing things when key x was double clicked
+ * ...
+ * }
+ *
+ * // Handler for double click event of key y
+ * static void vkey_y_dbclick(atm_vk_dck_evt_t *evt, void const *ctx)
+ * {
+ * DEBUG_TRACE("Key (%03u) double clicked", evt->vkey);
+ * // Doing things when key y was double clicked
+ * ...
+ * }
+ *
+ * // Key event array
+ * static const atm_vk_reg_t key_event_array[] = {
+ * ...
+ * VKEY_DB_CLICK(vkey_x_dbclick, x, 100),
+ * VKEY_DB_CLICK(vkey_y_dbclick, y, 100),
+ * ...
+ * };
+ * @endcode
+ *******************************************************************************
+ */
+#define VKEY_DB_CLICK(fn, vkey, max_ms) { \
+ atm_vk_db_click, .db_click = {fn, vkey, max_ms} \
+}
+
+/**
+ *******************************************************************************
+ * @brief Create click event entry.
+ * The entry is part of a @ref atm_vk_reg_t array which is used to
+ * registered into @ref atm_vkey_add_table function.
+ * @param[in] fn Callback function
+ * @ref atm_vk_ck_reg_t.cb
+ * @param[in] vkey Key index to monitor. @ref atm_vk_ck_reg_t.vkey
+ * @param[in] min_ms Minimal triggered interval within up and down.
+ * @ref atm_vk_ck_reg_t.min_ms
+ * @param[in] max_ms Maximal triggered interval within up and down.
+ * @ref atm_vk_ck_reg_t.max_ms
+ *
+ * Here is the example of monitoring click event on key index x with click interval
+ * within 50 to 100 millisecond and 500 to 1000 millisecond correspondingly:
+ * - Method one
+ * @code
+ * // Handler for key x quick click event
+ * static void vkey_x_quick_click(atm_vk_ck_evt_t *evt, void const *ctx)
+ * {
+ * // evt->vkey shall be x
+ * // evt->time_ms shall be from 50 to 100.
+ * DEBUG_TRACE("Key (%03u) quick clicked in %ld ms", evt->vkey, evt->time_ms);
+ * // Do things of quick clicking
+ * ...
+ * }
+ *
+ * // Handler for key x slow click event
+ * static void vkey_x_slow_click(atm_vk_ck_evt_t *evt, void const *ctx)
+ * {
+ * // evt->vkey shall be x
+ * // evt->time_ms shall be from 500 to 1000.
+ * DEBUG_TRACE("Key (%03u) slow clicked in %ld ms", evt->vkey, evt->time_ms);
+ * // Do things of slow clicking
+ * ...
+ * }
+ *
+ * // Key event array
+ * static const atm_vk_reg_t key_event_array[] = {
+ * ...
+ * VKEY_CLICK(vkey_3_quick_click, x, 50, 100),
+ * ...
+ * VKEY_CLICK(vkey_3_slow_click, x, 500, 1000),
+ * ...
+ * };
+ * @endcode
+ * - Method two
+ * @code
+ * // Handler for key x quick click event
+ * static void vkey_x_click(atm_vk_ck_evt_t *evt, void const *ctx)
+ * {
+ * // evt->vkey shall be x
+ * // evt->time_ms shall be from 50 to 1000.
+ * DEBUG_TRACE("Key (%03u) clicked in %ld ms", evt->vkey, evt->time_ms);
+ * if (evt->time_ms <= 100) {
+ * // Do things of quick clicking
+ * ...
+ * } else if(evt->time_ms >= 500) {
+ * // Do things of slow clicking
+ * ...
+ * }
+ * }
+ *
+ *
+ * // Key event array
+ * static const atm_vk_reg_t key_event_array[] = {
+ * ...
+ * VKEY_CLICK(vkey_x_click, x, 50, 1000),
+ * ...
+ * };
+ * @endcode
+ *******************************************************************************
+ */
+#define VKEY_CLICK(fn, vkey, min_ms, max_ms) { \
+ atm_vk_click, .click = {fn, vkey, min_ms, max_ms} \
+}
+
+/**
+ *******************************************************************************
+ * @brief Create hold click event entry.
+ * The entry is part of a @ref atm_vk_reg_t array which is used to
+ * registered into @ref atm_vkey_add_table function.
+ * @param[in] fn Callback function
+ * @ref atm_vk_hc_reg_t.cb
+ * @param[in] vkey Virtual key index for clicking monitor.
+ * @param[in] ... Held keys' mask.
+ * @note VKEY_HOLD_CLICK don't support ATM_MASK_ANY
+ *
+ * Here is the example of monitoring click events on key index x clicking with
+ * key indexes a,b and c (where a < 32, b < 32, 32 < c < 64) held and key index
+ * y clicking with key index d (where d < 32) held:
+ * @code
+ *
+ * // Handler for key index x clicking with key indexes a,b and c held.
+ * static void vkey_x_click_in_abc(atm_vk_hc_evt_t *evt, void const *ctx)
+ * {
+ * // evt->vkey shall be x
+ * DEBUG_TRACE("Key (%03u) clicked", evt->vkey);
+ * // evt->held.mask[0] should equal to (1 << a) | (1 << b)
+ * DEBUG_TRACE("Key held mask[0] = %l#x", evt->held.mask[0]);
+ * // evt->held.mask[1] should equal to (1 << (c-32))
+ * DEBUG_TRACE("Key held mask[1] = %l#x", evt->held.mask[1]);
+ * }
+ *
+ * // Handler for key index y click with key index d held.
+ * static void vkey_y_click_in_d(atm_vk_hc_evt_t *evt, void const *ctx)
+ * {
+ * // evt->vkey will be y
+ * DEBUG_TRACE("Key (%03u) clicked", evt->vkey);
+ * // evt->held.mask[0] should equal to (1 << d)
+ * DEBUG_TRACE("Key held mask[0] = %l#x", evt->held.mask[0]);
+ * }
+ *
+ * // Key event array
+ * static const atm_vk_reg_t key_event_array[] = {
+ * ...
+ * VKEY_HOLD_CLICK(vkey_x_click_in_abc, x, (1 << a) | (1 << b), (1 << (c - 32))),
+ * ...
+ * VKEY_HOLD_CLICK(vkey_y_click_in_d, y, (1 << d)),
+ * ...
+ * };
+ * @endcode
+ *******************************************************************************
+ */
+#define VKEY_HOLD_CLICK(fn, vkey, ...) { \
+ atm_vk_hold_click, .hold_click = {fn, vkey, {__VA_ARGS__}} \
+}
+
+/**
+ *******************************************************************************
+ * @brief Create hold click event entry with 1 key holding.
+ * The entry is part of a @ref atm_vk_reg_t array which is used to
+ * registered into @ref atm_vkey_add_table function. This is convenient version
+ * of @ref VKEY_HOLD_CLICK with 1 key holding.
+ * @param[in] fn Callback function
+ * @ref atm_vk_hc_reg_t.cb
+ * @param[in] vkey Virtual key index for clicking monitor.
+ * @param[in] mk Virtual key index for holding.
+ *
+ * Here is the example of monitoring click event on key index x clicking with
+ * key index a held:
+ * @code
+ *
+ * // Handler for key x click with key index a held.
+ * static void vkey_x_click_with_a(atm_vk_hc_evt_t *evt, void const *ctx)
+ * {
+ * // evt->vkey will be x
+ * DEBUG_TRACE("Key (%03u) clicked", evt->vkey);
+ * // evt->held.mask[a/32] should equal to (1 << (a%32))
+ * DEBUG_TRACE("Key held mask[%x] = %l#x", a/32, evt->held.mask[a/32]);
+ * }
+ *
+ * // Key event array
+ * static const atm_vk_reg_t key_event_array[] = {
+ * ...
+ * // If a < 32, this equals to VKEY_HOLD_CLICK(vkey_x_click_with_a, x, (1 << a))
+ * // If a < 64, this equals to VKEY_HOLD_CLICK(vkey_x_click_with_a, x, 0, (1 << a % 32))
+ * // If a < 96, this equals to VKEY_HOLD_CLICK(vkey_x_click_with_a, x, 0, 0, (1 << a % 32))
+ * // Etc,.
+ * VKEY_HOLD_1KEY_CLICK(vkey_x_click_with_a, x, a),
+ * ...
+ * };
+ * @endcode
+ *******************************************************************************
+ */
+#define VKEY_HOLD_1KEY_CLICK(fn, vkey, mk) \
+ VKEY_HOLD_CLICK(fn, vkey, VKEY_1KEY_MASK(mk))
+
+/**
+ *******************************************************************************
+ * @brief Create hold click event entry with 2 key holding.
+ * The entry is part of a @ref atm_vk_reg_t array which is used to
+ * registered into @ref atm_vkey_add_table function. This is the convenient version
+ * of @ref VKEY_HOLD_CLICK with 2 key holding.
+ * @param[in] fn Callback function
+ * @ref atm_vk_hc_reg_t.cb
+ * @param[in] vkey Virtual key index for clicking monitor.
+ * @param[in] mk1 First virtual key index for holding.
+ * @param[in] mk2 Second virtual key index for holding.
+ * Here is the example of monitoring click event on key index x clicking with
+ * key index a and key index b held:
+ * @code
+ *
+ * // Handler for key index x click with key index a and b held.
+ * static void vkey_x_click_with_ab(atm_vk_hc_evt_t *evt, void const *ctx)
+ * {
+ * // evt->vkey shall be x
+ * DEBUG_TRACE("Key (%03u) clicked", evt->vkey);
+ * // evt->held.mask[a/32] bit a%32 shall be 1
+ * // evt->held.mask[b/32] bit b%32 shall be 1
+ * DEBUG_TRACE("mask %d = %l#x mask %d = %l#x", a/32, evt->held.mask[a/32], b/32,
+ * evt->held.mask[b/32]);
+ * }
+ *
+ * // Key event array
+ * static const atm_vk_reg_t key_event_array[] = {
+ * ...
+ * VKEY_HOLD_2KEY_CLICK(vkey_x_click_with_ab, x, a, b),
+ * ...
+ * };
+ * @endcode
+ *******************************************************************************
+ */
+#define VKEY_HOLD_2KEY_CLICK(fn, vkey, mk1, mk2) \
+ VKEY_HOLD_CLICK(fn, vkey, VKEY_2KEY_MASK(mk1, mk2))
+
+/**
+ *******************************************************************************
+ * @brief Create hold event entry.
+ * The entry is part of a @ref atm_vk_reg_t array which is used to
+ * registered into @ref atm_vkey_add_table function.
+ * @param[in] fn Callback function
+ * @ref atm_vk_hd_reg_t.cb
+ * @param[in] fn1 Callback function
+ * @ref atm_vk_hd_reg_t.status_cb
+ * @param[in] min_ms Minimal hold time to trigger the event.
+ * @ref atm_vk_hd_reg_t.time_ms
+ * @param[in] ... Held keys' mask.
+ *
+ * Here is the example of monitoring click events on key a, b, and c held for
+ * 500 milliseconds and key index d held for 1000 milliseconds.
+ * @code
+ *
+ * // Handler for key index a,b and c held.
+ * static void vkey_abc_hold(atm_vk_hd_evt_t *evt, void const *ctx)
+ * {
+ * // evt->held.mask[a/32] bit a%32 shall be 1
+ * // evt->held.mask[b/32] bit b%32 shall be 1
+ * // evt->held.mask[c/32] bit c%32 shall be 1
+ * DEBUG_TRACE("mask %d = %l#x mask %d = %l#x", a/32, evt->held.mask[a/32], b/32,
+ * evt->held.mask[b/32], c/32, evt->held.mask[c/32]);
+ * // evt->time_ms should be longer than 500 milliseconds
+ * DEBUG_TRACE("time = %ld", evt->time_ms);
+ * }
+ *
+ * // Handler for key index d held.
+ * static void vkey_d_hold(atm_vk_hd_evt_t *evt, void const *ctx)
+ * {
+ * // evt->held.mask[d/32] bit d%32 shall be 1
+ * DEBUG_TRACE("mask %d = %l#x", d/32, evt->held.mask[d/32]);
+ * // evt->time_ms should be longer than 1000 milliseconds
+ * DEBUG_TRACE("time = %ld", evt->time_ms);
+ * }
+ *
+ * // Key event array
+ * static const atm_vk_reg_t key_event_array[] = {
+ * ...
+ * // The (1 << (a%32)), (1 << (b%32)) and (1 << (c%32)) would be located in
+ * // mask a/32, b/32, c/32 respectively
+ * VKEY_HOLD(vkey_abc_hold, NULL, 500, ... (1 << (a%32)) ... (1 << (b%32)) ... (1 << (c%32))),
+ * ...
+ * // The (1 << (d%32)) would be located in mask d/32
+ * VKEY_HOLD(vkey_d_hold, NULL, 1000, ...(1 << (d%32)...),
+ * ...
+ * };
+ * @endcode
+ *******************************************************************************
+ */
+#define VKEY_HOLD(fn, fn1, min_ms, ...) { \
+ atm_vk_hold, .hold = {fn, fn1, min_ms, {__VA_ARGS__}} \
+}
+
+/**
+ *******************************************************************************
+ * @brief Create hold event entry with 1 key holding.
+ * The entry is part of a @ref atm_vk_reg_t array which is used to
+ * registered into @ref atm_vkey_add_table function. This is convenient version of
+ * @ref VKEY_HOLD with 1 key holding
+ * @param[in] fn Callback function
+ * @ref atm_vk_hd_reg_t.cb
+ * @param[in] fn1 Callback function
+ * @ref atm_vk_hd_reg_t.status_cb
+ * @param[in] min_ms Minimal hold time to trigger the event.
+ * @param[in] mk Virtual key index for holding.
+ *
+ * Here is the example of monitoring click event on and key index x holding for
+ * 1000 ms.
+ * @code
+ *
+ * // Handler for key x held.
+ * static void vkey_x_hold(atm_vk_hd_evt_t *evt, void const *ctx)
+ * {
+ * // evt->held.mask[x/32] bit x%32 shall be 1
+ * DEBUG_TRACE("Holding key mask[%d] = %l#x", x/32,evt->held.mask[x/32]);
+ * // evt->time_ms shall be longer than 1000
+ * DEBUG_TRACE("time = %ld", evt->time_ms);
+ * }
+ *
+ * // Key event array
+ * static const atm_vk_reg_t key_event_array[] = {
+ * ...
+ * VKEY_HOLD_1KEY(vkey_x_hold, NULL, 1000, x),
+ * ...
+ * };
+ * @endcode
+ *******************************************************************************
+ */
+#define VKEY_HOLD_1KEY(fn, fn1, min_ms, mk) \
+ VKEY_HOLD(fn, fn1, min_ms, VKEY_1KEY_MASK(mk))
+
+/**
+ *******************************************************************************
+ * @brief Create hold click event entry with 2 key holding.
+ * The entry is part of a @ref atm_vk_reg_t array which is used to
+ * registered into @ref atm_vkey_add_table function. This is convenient version
+ * of @ref VKEY_HOLD with 2 keys holding.
+ * @param[in] fn Callback function
+ * @ref atm_vk_hd_reg_t.cb
+ * @param[in] fn1 Callback function
+ * @ref atm_vk_hd_reg_t.status_cb
+ * @param[in] min_ms Minimal hold time to trigger the event.
+ * @param[in] mk1 First virtual key index for holding.
+ * @param[in] mk2 Second virtual key index for holding.
+ *
+ * Here is the example of monitoring click event on and key index x and y holding for
+ * 1500 ms.
+ * @code
+ *
+ * // Handler for key index x and y held.
+ * static void vkey_xy_hold(atm_vk_hd_evt_t *evt, void const *ctx)
+ * {
+ * // evt->held.mask[x/32] bit x%32 shall be 1
+ * DEBUG_TRACE("Holding key mask[%d] = %l#x", x/32, evt->held.mask[x/32]);
+ * // evt->held.mask[y/32] bit y%32 shall be 1
+ * DEBUG_TRACE("Holding key mask[%d] = %l#x", y/32, evt->held.mask[y/32]);
+ * // evt->time_ms shall be longer than 1000
+ * DEBUG_TRACE("time = %ld", evt->time_ms);
+ * }
+ *
+ * // Key event array
+ * static const atm_vk_reg_t key_event_array[] = {
+ * ...
+ * VKEY_HOLD_2KEY(vkey_xy_hold, NULL, 1500, x, y),
+ * ...
+ * };
+ * @endcode
+ *******************************************************************************
+ */
+#define VKEY_HOLD_2KEY(fn, fn1, min_ms, mk1, mk2) \
+ VKEY_HOLD(fn, fn1, min_ms, VKEY_2KEY_MASK(mk1, mk2))
+
+/**
+ *******************************************************************************
+ * @brief Create non-first down event entry.
+ * The entry is part of a @ref atm_vk_reg_t array which is used to
+ * registered into @ref atm_vkey_add_table function.
+ * @param[in] fn Callback function
+ * @ref atm_vk_dnup_reg_t.cb
+ * @param[in] vkey Virtual key index for down monitor.
+ *
+ * @anchor down_more_example
+ * Here is the example of monitoring all kinds of down and up events and report
+ * them to a single callback:
+ * @code
+ *
+ * static void vkey_any_up_down_inter(atm_vk_dnup_inter_evt_t *evt, void const *ctx)
+ * {
+ * if(evt->eid == atm_vk_down_1st || evt->eid == atm_vk_down_more) {
+ * DEBUG_TRACE("vkey %d is pressed", evt->vkey);
+ * } else {
+ * DEBUG_TRACE("vkey %d is release", evt->vkey);
+ * }
+ *
+ * if(evt->eid == atm_vk_down_more || evt->eid == atm_vk_up_inter) {
+ * DEBUG_TRACE("Some key held mask:")
+ * for (int i = 0; i < VKEY_MASK_NUM; i++) {
+ * DEBUG_TRACE("mask[%d] = %lu", evt->held.mask[i]);
+ * }
+ * }
+ * }
+ *
+ * static void vkey_any_up_down(atm_vk_dnup_evt_t *evt, void const *ctx)
+ * {
+ * atm_vk_dnup_inter_evt_t new_evt;
+ * memcpy(&new_evt, &evt, sizeof(atm_vk_dnup_evt_t));
+ * vkey_any_up_down_inter(&new_evt, ctx);
+ * }
+ *
+ * // Key event array
+ * static const atm_vk_reg_t key_event_array[] = {
+ * ...
+ * VKEY_DOWN_MORE(vkey_any_up_down, atm_vk_any),
+ * VKEY_DOWN_FIRST(vkey_any_up_down_inter, atm_vk_any),
+ * VKEY_UP_INTER(vkey_any_up_down_inter, atm_vk_any),
+ * VKEY_UP_LAST(vkey_any_up_down, atm_vk_any),
+ * ...
+ * };
+ * @endcode
+ *******************************************************************************
+ */
+#define VKEY_DOWN_MORE(fn, vkey) { \
+ atm_vk_down_more, .dn = {fn, vkey} \
+}
+
+/**
+ *******************************************************************************
+ * @brief Create first down event entry.
+ * The entry is part of a @ref atm_vk_reg_t array which is used to
+ * registered into @ref atm_vkey_add_table function.
+ * @param[in] fn Callback function
+ * @ref atm_vk_dnup_reg_t.cb
+ * @param[in] vkey Virtual key index for down monitor.
+ * @note Please refer example @ref down_more_example "here"
+ *******************************************************************************
+ */
+#define VKEY_DOWN_FIRST(fn, vkey) { \
+ atm_vk_down_1st, .dn_fst = {fn, vkey} \
+}
+
+/**
+ *******************************************************************************
+ * @brief Create non-last up event entry.
+ * The entry is part of a @ref atm_vk_reg_t array which is used to
+ * registered into @ref atm_vkey_add_table function.
+ * @param[in] fn Callback function
+ * @ref atm_vk_dnup_reg_t.cb
+ * @param[in] vkey Virtual key index for up monitor.
+ * @note Please refer example @ref down_more_example "here"
+ *******************************************************************************
+ */
+#define VKEY_UP_INTER(fn, vkey) { \
+ atm_vk_up_inter, .up = {fn, vkey} \
+}
+
+/**
+ *******************************************************************************
+ * @brief Create last up event entry.
+ * The entry is part of a @ref atm_vk_reg_t array which is used to
+ * registered into @ref atm_vkey_add_table function.
+ * @param[in] fn Callback function
+ * @ref atm_vk_dnup_reg_t.cb
+ * @param[in] vkey Virtual key index for up monitor.
+ * @note Please refer example @ref down_more_example "here"
+ *******************************************************************************
+ */
+#define VKEY_UP_LAST(fn, vkey) { \
+ atm_vk_up_last, .up_lst = {fn, vkey} \
+}
+
+/// Internal macro used by VKEY_1KEY_MASK
+#define _MK_N(n, v) ((v / 32 == n) ? CO_BIT(v % 32) : 0)
+/// Internal macro used by VKEY_2KEY_MASK
+#define _MK_N2(n, v1, v2) (_MK_N(n,v1) | _MK_N(n,v2))
+/// Vkey index to vkey mask conversion by maximum vkey number
+#define _MK_N_64(v) _MK_N(0, v), _MK_N(1, v)
+#define _MK_N_96(v) _MK_N_64(v), _MK_N(2, v)
+#define _MK_N_128(v) _MK_N_96(v), _MK_N(3, v)
+#define _MK_N_160(v) _MK_N_128(v), _MK_N(4, v)
+#define _MK_N_192(v) _MK_N_160(v), _MK_N(5, v)
+#define _MK_N2_64(v1, v2) _MK_N2(0, v1, v2), _MK_N2(1, v1, v2)
+#define _MK_N2_96(v1, v2) _MK_N2_64(v1, v2), _MK_N2(2, v1, v2)
+#define _MK_N2_128(v1, v2) _MK_N2_96(v1, v2), _MK_N2(3, v1, v2)
+#define _MK_N2_160(v1, v2) _MK_N2_128(v1, v2), _MK_N2(4, v1, v2)
+#define _MK_N2_192(v1, v2) _MK_N2_160(v1, v2), _MK_N2(5, v1, v2)
+#if (ATM_VKEY_MAX <= 32)
+#define VKEY_1KEY_MASK(v) _MK_N(0, v)
+#define VKEY_2KEY_MASK(v1, v2) _MK_N2(0, v1, v2)
+#elif (ATM_VKEY_MAX <= 64)
+#define VKEY_1KEY_MASK(v) _MK_N_64(v)
+#define VKEY_2KEY_MASK(v1, v2) _MK_N2_64(v1, v2)
+#elif (ATM_VKEY_MAX <= 96)
+#define VKEY_1KEY_MASK(v) _MK_N_96(v)
+#define VKEY_2KEY_MASK(v1, v2) _MK_N2_96(v1, v2)
+#elif (ATM_VKEY_MAX <= 128)
+#define VKEY_1KEY_MASK(v) _MK_N_128(v)
+#define VKEY_2KEY_MASK(v1, v2) _MK_N2_128(v1, v2)
+#elif (ATM_VKEY_MAX <= 160)
+#define VKEY_1KEY_MASK(v) _MK_N_160(v)
+#define VKEY_2KEY_MASK(v1, v2) _MK_N2_160(v1, v2)
+#else
+#define VKEY_1KEY_MASK(v) _MK_N_192(v)
+#define VKEY_2KEY_MASK(v1, v2) _MK_N2_192(v1, v2)
+#endif // (ATM_VKEY_MAX <= 32)
+/// Check if a key is in mask array
+#define VKEY_IS_MASK_HIT(m, k) (m[k / 32] & CO_BIT(k % 32))
+/// Check if two mask array is equal
+#define VKEY_IS_MASK_EQU(m1, m2) \
+ (!memcmp(m1, m2, sizeof(uint32_t) * VKEY_MASK_NUM))
+/// Special number for any mask
+#define ATM_MASK_ANY 0xFFFFFFFF
+/// Number for vkey mask
+#define VKEY_MASK_NUM CO_DIVIDE_CEIL(ATM_VKEY_MAX, 32)
+
+/// Key event ID
+typedef enum {
+ /// First key down event.
+ atm_vk_down_1st,
+ /// Non-first key down event. Other pressed key(s) exist.
+ atm_vk_down_more,
+ /// Key held event.
+ atm_vk_hold,
+ /// Non-last Key up event. Other pressed key(s) exist.
+ atm_vk_up_inter,
+ /// Last key up event.
+ atm_vk_up_last,
+ /// Event for key clicking.
+ atm_vk_click,
+ /// Event for key clicking with some key(s) held.
+ atm_vk_hold_click,
+ /// Event for key double clicking.
+ atm_vk_db_click,
+} atm_vk_eid_t;
+
+/// key index: 0 to 254 is valid index.
+/// Here defined is special usage.
+typedef enum {
+ /// any keys
+ atm_vk_any = 0xff
+} atm_vk_idx_t;
+
+/// Held keys
+typedef struct {
+ /// Masks of current held keys.
+ uint32_t mask[VKEY_MASK_NUM];
+} atm_vk_held_keys_t;
+
+
+/// Event structure header
+typedef struct {
+ /// Event id
+ atm_vk_eid_t eid;
+ union {
+ /// Key index have been acted. @ref atm_vk_idx_t
+ atm_vk_idx_t vkey;
+ /// Key index of uint8_t type.
+ uint8_t u8vkey;
+ };
+} atm_vk_basic_evt_t;
+
+/// Event structure for atm_vk_down_fist and atm_vk_up_last.
+typedef struct {
+ /// Basic event
+ atm_vk_basic_evt_t top;
+} atm_vk_dnup_evt_t;
+
+/// Event structure for atm_vk_hold
+typedef struct atm_vk_hd_evt_s {
+ /// Basic event
+ /// @note the vkey in top is unused in atm_vk_hold
+ atm_vk_basic_evt_t top;
+ /// Minimal time of holding. Unit is millisecond. Zero for ignoring.
+ uint16_t time_ms;
+ /// Current held key
+ atm_vk_held_keys_t held;
+} atm_vk_hd_evt_t;
+
+/// Event structure for atm_vk_hold_click, atm_vk_down_more and
+/// atm_vk_up_inter
+typedef struct {
+ /// Basic event;
+ atm_vk_basic_evt_t top;
+ /// Masks of current held key.
+ atm_vk_held_keys_t held;
+} atm_vk_hc_evt_t, atm_vk_dnup_inter_evt_t;
+
+/// Click and double click event structure
+typedef struct atm_vk_ck_evt_s {
+ /// Basic event;
+ atm_vk_basic_evt_t top;
+ /// Measured time for click and double click
+ /// click: time between press and release.
+ /// double click: time between two clicks.
+ uint16_t time_ms;
+} atm_vk_ck_evt_t, atm_vk_dck_evt_t;
+
+/// Entry structure for hold detection.
+typedef struct atm_vk_hd_reg_s {
+ /// callback while event happen
+ /// @brief This function will be called if hold condition matched.
+ /// @param[in] evt Event for hold and click structure
+ /// @param[in] ctx Context data.
+ /// @return Return true to keep detecting this hold. otherwise, false.
+ bool (*cb)(atm_vk_hd_evt_t const *evt, void const *ctx);
+ /// @brief This function will be called if hold key has been pressed or
+ /// released.
+ /// @param[in] pressed Indicate the hold key is pressed and a timer is
+ /// starting to count.
+ /// @param[in] ctx Context data.
+ void (*status_cb)(bool pressed, void const *ctx);
+ /// Minimal time of holding. Unit is millisecond. Zero for ignoring.
+ uint16_t time_ms;
+ /// Masks of expecting held keys.
+ uint32_t mask[VKEY_MASK_NUM];
+} atm_vk_hd_reg_t;
+
+/// Entry structure for hold and click
+typedef struct atm_vk_hc_reg_s {
+ /// callback while event happen
+ /// @brief This function will be called if hold and click condition matched.
+ /// @param[in] evt Event for hold structure
+ /// @param[in] ctx Context data.
+ void (*cb)(atm_vk_hc_evt_t const *evt, void const *ctx);
+ /// Key index of clicking. @ref atm_vk_idx_t
+ uint8_t vkey;
+ /// Masks of expecting held keys.
+ uint32_t mask[VKEY_MASK_NUM];
+} atm_vk_hc_reg_t;
+
+/// Entry structure of click event
+typedef struct atm_vk_ck_reg_s {
+ /// callback while event happen
+ /// @brief This function will be called if click condition matched.
+ /// @param[in] evt Event for click structure
+ /// @note min_ms and max_ms represent the real click interval and they should
+ /// equal.
+ /// @param[in] ctx Context data.
+ void (*cb)(atm_vk_ck_evt_t const *evt, void const *ctx);
+ /// Key index of clicking. @ref atm_vk_idx_t
+ uint8_t vkey;
+ /// Minimal time between press and release. zero for ignoring.
+ uint16_t min_ms;
+ /// Maximal time between press and release. zero for ignoring.
+ uint16_t max_ms;
+} atm_vk_ck_reg_t;
+
+/// Entry structure for double click detection
+typedef struct atm_vk_dck_reg_s {
+ /// callback while event happen
+ /// @brief This function will be called if double click condition matched.
+ /// @param[in] evt Event for double click structure
+ /// @param[in] ctx Context data.
+ void (*cb)(atm_vk_dck_evt_t const *evt, void const *ctx);
+ /// Listening key index of double clicking. @ref atm_vk_idx_t
+ uint8_t vkey;
+ /// Maximal time between two clicks. zero for ignoring.
+ uint16_t max_ms;
+} atm_vk_dck_reg_t;
+
+/// Entry structure for down and up
+typedef struct atm_vk_dnup_reg_s {
+ /// callback while event happen
+ /// @brief This function will be called if down or up condition matched.
+ /// @param[in] evt Event for down or up structure
+ /// @param[in] ctx Context data.
+ void (*cb)(atm_vk_dnup_evt_t const *evt, void const *ctx);
+ /// Key listen index for atm_vk_down_fist, atm_vk_up_last
+ /// @ref atm_vk_idx_t
+ uint8_t vkey;
+} atm_vk_dnup_reg_t;
+
+/// Event structure for non-lonely down and up
+typedef struct atm_vk_dnup_inter_reg_s {
+ /// callback while event happen
+ /// @brief This function will be called if down or up condition matched.
+ /// @param[in] evt Event for non-lonely down or up structure
+ /// @param[in] ctx Context data.
+ void (*cb)(atm_vk_dnup_inter_evt_t const *evt, void const *ctx);
+ /// Key index for atm_vk_down_more, atm_vk_up_inter
+ /// @ref atm_vk_idx_t
+ uint8_t vkey;
+} atm_vk_dnup_inter_reg_t;
+
+/// Event union for table
+typedef struct {
+ /// Event id
+ atm_vk_eid_t eid;
+ union {
+ /// Non-first down event
+ atm_vk_dnup_inter_reg_t dn;
+ /// Non-last up event
+ atm_vk_dnup_inter_reg_t up;
+ /// First down event
+ atm_vk_dnup_reg_t dn_fst;
+ /// Last up event
+ atm_vk_dnup_reg_t up_lst;
+ /// Hold event
+ atm_vk_hd_reg_t hold;
+ /// Hold and click event
+ atm_vk_hc_reg_t hold_click;
+ /// click event
+ atm_vk_ck_reg_t click;
+ /// double click event
+ atm_vk_dck_reg_t db_click;
+ };
+} atm_vk_reg_t;
+
+/**
+ *******************************************************************************
+ * @brief Add a virtual key event table
+ * @param[in] table Virtual key event table
+ * @param[in] ent_cnt Entry count of event table
+ * @param[in] ctx application context
+ * @return Handle of this table. This handle is used to feed virtual key while
+ * some user input happened.
+ *******************************************************************************
+ */
+__NONNULL(1)
+struct vkll_ctx_s *atm_vkey_add_table(atm_vk_reg_t const *table, uint16_t ent_cnt,
+ void const *ctx);
+
+/**
+ *******************************************************************************
+ * @brief Virtual key feed.
+ * When some user input happened, use this function to put virtual key into
+ * system.
+ * @param[in] handle Handle of virtual key event table.
+ * @param[in] vkey Virtual key to be inputted.
+ * @param[in] is_pressed True for key pressing. Otherwise for releasing.
+ *******************************************************************************
+ */
+__NONNULL(1)
+void atm_vkey_feed(struct vkll_ctx_s *handle, uint8_t vkey, bool is_pressed);
+
+/**
+ *******************************************************************************
+ * @brief Virtual key flush.
+ * @param[in] handle Handle of virtual key event table.
+ *******************************************************************************
+ */
+__NONNULL_ALL
+void atm_vkey_flush(struct vkll_ctx_s *handle);
+
+#ifdef __cplusplus
+}
+#endif
+
+/// @} ATM_BTFM_PVKEY
diff --git a/platform/atm2/ATM22xx-x1x/driver/batt_model/batt_model.h b/platform/atm2/ATM22xx-x1x/driver/batt_model/batt_model.h
new file mode 100644
index 0000000..916ddde
--- /dev/null
+++ b/platform/atm2/ATM22xx-x1x/driver/batt_model/batt_model.h
@@ -0,0 +1,86 @@
+/**
+ *******************************************************************************
+ *
+ * @file batt_model.h
+ *
+ * @brief Battery model common prototype
+ *
+ * Copyright (C) Atmosic 2022-2023
+ *
+ *******************************************************************************
+ */
+
+#pragma once
+
+/**
+ * @defgroup BATT_MODEL Battery model
+ * @ingroup DRIVERS
+ * @brief Driver for battery model
+ * @{
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/// Device abstract state for battery model
+typedef enum {
+ /// Invalid state. Use to report error when get state.
+ DEV_INVALID,
+ /// Reset state. It represents system is booting and doesn't decide whether
+ /// goes to normal function or not.
+ DEV_RESET,
+ /// Active state. System is in normal function.
+ DEV_ACTV,
+ /// Hibernate state. It represents system is going to hibernation or waking
+ /// up from hibernation.
+ DEV_HIB,
+ /// Soc off state. It represents system is going to SOC off or waking up
+ /// from SOC off.
+ DEV_SOCOFF,
+} dev_state_t;
+
+/// Device state getter and setter.
+typedef struct {
+ /// Get current device state.
+ dev_state_t (*get)(void);
+ /// Set current device state.
+ void (*set)(dev_state_t);
+} dev_state_fns_t;
+
+/// Hibernation flag getter and setter
+typedef struct {
+ /// Get hibernation flag
+ bool (*get)(uint8_t);
+ /// Set hibernation flag
+ void (*set)(uint8_t, bool);
+} hib_flag_fns_t;
+
+/// Callback functions from batt_model
+typedef struct {
+ /// Device state getter and setter.
+ dev_state_fns_t state;
+ /// Hibernation getter and setter.
+ hib_flag_fns_t flag;
+} batt_cbs;
+
+/// Battery model virtual functions
+typedef struct {
+ /// Initialization of the battery model.
+ void (*init)(batt_cbs const *);
+ /// Issue a battery capacity sampling.
+ bool (*sample)(void (*cb)(uint8_t level));
+} batt_fns;
+
+/**
+ *******************************************************************************
+ * @brief Retrieve the functions of battery model
+ * return Battery model functions.
+ *******************************************************************************
+ */
+batt_fns const *batt_model(void);
+#ifdef __cplusplus
+}
+#endif
+
+/// @} BATT_MODEL
diff --git a/platform/atm2/ATM22xx-x1x/driver/gadc/gadc.h b/platform/atm2/ATM22xx-x1x/driver/gadc/gadc.h
new file mode 100644
index 0000000..6f1719a
--- /dev/null
+++ b/platform/atm2/ATM22xx-x1x/driver/gadc/gadc.h
@@ -0,0 +1,122 @@
+/**
+ *******************************************************************************
+ *
+ * @file gadc.h
+ *
+ * @brief Analog-to-digital converter
+ *
+ * Copyright (C) Atmosic 2020
+ *
+ *******************************************************************************
+ */
+
+#pragma once
+
+/**
+ *******************************************************************************
+ * @defgroup GADC General purpose ADC
+ * @ingroup DRIVERS
+ * @brief User driver for General purpose Analog to Digital Converter
+ *
+ * @{
+ *******************************************************************************
+ */
+
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if defined(__IEEE_LITTLE_ENDIAN) || defined(__LITTLE_ENDIAN__)
+/// Calibration data type
+struct gadc_cal_s {
+ union {
+ /// 32 bits data which combine offset and gain
+ uint32_t value;
+ /// Decomposition structure.
+ struct {
+ /// Mantissa part of gain.
+ unsigned int c1_mantissa:12;
+ /// Exponent part of gain.
+ int c1_exponent:6;
+ /// Sign part of gain.
+ unsigned int c1_sign:1;
+ /// Double value of offset
+ int c0_x2:13;
+ };
+ };
+};
+
+STATIC_ASSERT(sizeof(struct gadc_cal_s) == 4, "wrong size");
+/// Float data type
+typedef union {
+ /// C float value
+ float value;
+ /// Decomposition structure.
+ struct {
+ /// Mantissa part of float.
+ unsigned int fraction: 23;
+ /// Exponent part of float.
+ int exponent: 8;
+ /// Sign part of float.
+ unsigned int sign : 1;
+ } number;
+} __ieee_float_shape_type;
+#else
+#error "Unsupported floating point endian"
+#endif
+
+/// Channels used by GADC
+typedef enum {
+ /// VBAT channel.
+ VBATT = 0,
+ /// VSTORE channel.
+ VSTORE = 1,
+ /// VDD1A channel.
+ CORE = 2,
+ /// Temperature channel.
+ TEMP = 3,
+ /// P10/P11 differential channel.
+ PORT0_DIFFERENTIAL = 4,
+ /// P10 single-ended channel.
+ PORT0_SINGLE_ENDED_0 = 6,
+ /// P11 single-ended channel.
+ PORT0_SINGLE_ENDED_1 = 7,
+ /// For GADC driver use only
+ ZV_PORT = 8,
+ /// P9 single-ended channel.
+ PORT1_SINGLE_ENDED_1 = 9,
+} gadc_chan_t;
+
+/// Callback context data
+typedef struct {
+ /// Sensed channel.
+ gadc_chan_t ch;
+ /// Gext setting. (0 = more range / 1 = better resolution)
+ uint8_t gext;
+ /// Application context.
+ void const *app_ctx;
+} gadc_cb_ctx_t;
+
+/// Callback prototype
+typedef void (*gadc_callback_t)(float result, int16_t raw, struct gadc_cal_s cal,
+ gadc_cb_ctx_t const *ctx);
+
+/**
+ * @brief Sample given GADC channel and invoke callback on completion of measurement
+ * @param[in] channel GADC channel to monitor
+ * @param[in] cb Callback invoked with channel measurement on completion.
+ * @param[in] gext gain setting
+ * @param[in] ctx Context associated.
+ */
+void gadc_sample_channel(gadc_chan_t channel, gadc_callback_t cb, uint8_t gext,
+ void const *ctx);
+
+#ifdef __cplusplus
+}
+#endif
+
+/// @} GADC
+
diff --git a/platform/atm2/ATM22xx-x1x/driver/ir/ir.h b/platform/atm2/ATM22xx-x1x/driver/ir/ir.h
new file mode 100644
index 0000000..42d222a
--- /dev/null
+++ b/platform/atm2/ATM22xx-x1x/driver/ir/ir.h
@@ -0,0 +1,63 @@
+/**
+ *******************************************************************************
+ *
+ * @file ir.h
+ *
+ * @brief IR driver interface
+ *
+ * Copyright (C) Atmosic 2019-2022
+ *
+ *******************************************************************************
+ */
+
+#pragma once
+
+/**
+ * @defgroup IR IR
+ * @ingroup DRIVERS
+ * @brief Driver for IR module
+ * @{
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief Reset IR sequence.
+ *
+ */
+void reset_ir_sequence(void);
+
+/**
+ * @brief Send IR sequence.
+ *
+ */
+void send_ir_sequence(void);
+
+/**
+ * @brief add IR period in period_us and carrier_on or off flag
+ * @param[in] carrier_on true if carrier freq needs to be used during the period
+ * @param[in] period_us Period in us
+ */
+void ir_add_period(uint8_t carrier_on, uint32_t period_us);
+
+/**
+ * @brief Initialize IR subsystem with given protocol
+ * @param[in] callback Callback to receive IR sequence completion event.
+ * @note This callback is invoked in interrupt context.
+ */
+void ir_init(void (*callback)(void));
+
+/**
+ * @brief Configure IR carrier parameters
+ * @param[in] freq Carrier frequency setting in Hz
+ * @param[in] duty Carrier duty cycle setting in percentage
+ */
+void ir_config_carr(uint32_t freq, uint8_t duty);
+
+#ifdef __cplusplus
+}
+#endif
+
+/// @} IR
diff --git a/platform/atm2/ATM22xx-x1x/driver/ir/ir_ctl.h b/platform/atm2/ATM22xx-x1x/driver/ir/ir_ctl.h
new file mode 100644
index 0000000..2645ef0
--- /dev/null
+++ b/platform/atm2/ATM22xx-x1x/driver/ir/ir_ctl.h
@@ -0,0 +1,147 @@
+/**
+ *******************************************************************************
+ *
+ * @file ir_ctl.h
+ *
+ * @brief IR control interface
+ *
+ * Copyright (C) Atmosic 2022
+ *
+ *******************************************************************************
+ */
+
+#pragma once
+
+/**
+ * @defgroup IR_CTL IR_CTL
+ * @ingroup DRIVERS
+ * @brief Driver for IR control
+ * @{
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum {
+ IR_MSG_TYPE_IR_CMD,
+ IR_MSG_TYPE_IR_SEQ,
+} ir_msg_type_t;
+
+/// IR protocol definition
+typedef struct {
+ /// Name of IR protocol
+ const char *name;
+ /// IR Protocol Header ON duration in us
+ uint32_t header_on_dur;
+ /// IR Protocol Header OFF duration in us
+ uint32_t header_off_dur;
+ /// IR Protocol address bits
+ uint8_t addr_len;
+ /// IR Protocol command bits
+ uint8_t cmd_len;
+ /// IR Protocol logical one ON duration in us
+ uint32_t one_on_dur;
+ /// IR Protocol logical one OFF duration in us
+ uint32_t one_off_dur;
+ /// IR Protocol logical zero ON duration in us
+ uint32_t zero_on_dur;
+ /// IR Protocol logical zero OFF duration in us
+ uint32_t zero_off_dur;
+ /// IR Protocol message end duration in us
+ uint32_t msg_end_dur;
+ /// IR Protocol message end space in cs
+ uint32_t msg_end_space;
+ /// IR Protocol repeat ON duration in us
+ uint32_t rept_on_dur;
+ /// IR Protocol repeat OFF duration in us
+ uint32_t rept_off_dur;
+ /// IR Protocol repeat end ON duration in us
+ uint32_t rept_end_on_dur;
+ /// IR Protocol repeat delay in us
+ uint32_t rept_delay;
+} __PACKED ir_prot_t;
+
+typedef struct {
+ uint32_t addr;
+ uint32_t inv_addr;
+ uint32_t cmd;
+ uint32_t inv_cmd;
+} ir_data_t;
+
+typedef struct {
+ ir_prot_t const *prot;
+ ir_data_t const *data;
+ bool repeat;
+} __PACKED ir_cmd_t;
+
+typedef struct {
+ uint16_t us_per_seq;
+ uint16_t seq_len;
+ uint16_t const *seq;
+ uint16_t rept_len;
+ uint16_t const *rept_seq;
+} ir_seq_t;
+
+typedef struct {
+ ir_msg_type_t type;
+ uint8_t duty;
+ uint32_t freq;
+ union {
+ ir_cmd_t ir_cmd;
+ ir_seq_t ir_seq;
+ };
+} ir_msg_t;
+
+/**
+ * @brief Initial IR control instance.
+ * @param[in] cb_end Callback to receive IR sequence completion event.
+ */
+__NONNULL_ALL
+void ir_ctl_init(void(*cb_end)(void));
+
+/**
+ * @brief Start transmiting IR signals
+ * @param[in] msg IR message
+ */
+__NONNULL_ALL
+void ir_ctl_start(ir_msg_t *msg);
+
+/**
+ * @brief Stop repeat IR sequence
+ */
+void ir_ctl_stop_rept(void);
+
+/**
+ * @brief Send IR data using NEC protocol.
+ * @param[in] data IR address, inverse address, IR command, inverse command
+ * @param[in] repeat true for repeating IR until ir_ctl_stop_rept() is invoked
+ * @note If repeat is set, need to call @ref ir_ctl_stop_rept to stop repeat
+ * code.
+ */
+__NONNULL(1)
+void nec_ir_send(ir_data_t const *data, bool repeat);
+
+#ifdef CFG_ATVRC_UNI_IR
+/**
+ * @brief Send Universal IR message.
+ * @param[in] ir_code Universal IR code data
+ * @param[in] size IR code data size
+ */
+__NONNULL(1)
+void uni_ir_send(uint8_t const *code, uint16_t size);
+
+/**
+ * @brief Get repeat delay from Universal IR code data
+ * @param[in] uni_ir_code Universal IR code data
+ * @return repeat delay in us. return 0 if IR code type not match
+ */
+__NONNULL_ALL
+uint32_t uni_ir_get_rept_delay(uint8_t const *uni_ir_code);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+/// @} IR_CTL
diff --git a/platform/atm2/ATM22xx-x1x/driver/ir/nec_ir.h b/platform/atm2/ATM22xx-x1x/driver/ir/nec_ir.h
new file mode 100644
index 0000000..87d6a48
--- /dev/null
+++ b/platform/atm2/ATM22xx-x1x/driver/ir/nec_ir.h
@@ -0,0 +1,47 @@
+/**
+ *******************************************************************************
+ *
+ * @file nec_ir.h
+ *
+ * @brief NEC IR driver interface
+ *
+ * Copyright (C) Atmosic 2022
+ *
+ *******************************************************************************
+ */
+
+#pragma once
+
+/**
+ * @defgroup NEC_IR NEC_IR
+ * @ingroup DRIVERS
+ * @brief Driver for NEC IR module
+ * @{
+ */
+
+#include "ir_ctl.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief Configure NEC IR instance.
+ * @param[in] cb_end Callback to receive IR sequence completion event.
+ */
+__NONNULL_ALL
+void nec_ir_init(void(*cb_end)(void));
+
+/**
+ * @brief Send IR address and cmd using NEC protocol.
+ * @param[in] data IR data: address, inverse address, command, inverse command
+ * @note If repeat is set, need to call @ref nec_ir_repeat_end to stop repeat
+ * code.
+ */
+void nec_ir_send(ir_data_t *data, bool repeat);
+
+#ifdef __cplusplus
+}
+#endif
+
+/// @} NEC_IR \ No newline at end of file
diff --git a/platform/atm2/ATM22xx-x1x/driver/keyboard/keyboard.h b/platform/atm2/ATM22xx-x1x/driver/keyboard/keyboard.h
new file mode 100644
index 0000000..d243602
--- /dev/null
+++ b/platform/atm2/ATM22xx-x1x/driver/keyboard/keyboard.h
@@ -0,0 +1,60 @@
+/**
+ *******************************************************************************
+ *
+ * @file keyboard.h
+ *
+ * @brief keyboard driver
+ *
+ * Copyright (C) Atmosic 2018-2021
+ *
+ *******************************************************************************
+ */
+
+#pragma once
+
+/**
+ * @defgroup KEYBOARD KEYBOARD
+ * @ingroup DRIVERS
+ * @brief User driver for KSM module.
+ * @{
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/// Key matrix event
+typedef enum ksm_event_s {
+ /// A key was released.
+ KSM_RELEASE,
+ /// A Key was pressed.
+ KSM_PRESS,
+ /// Hardware overflow error.
+ KSM_ERR_HW_OVF
+} ksm_event_t;
+
+/**
+ * @brief Callback function prototype
+ * @param[in] event True if the key is pressed
+ * @param[in] idx Key index which is used in KSM_RELEASE and KSM_PRESS event.
+ * The index is Ri*Cn + Ci where Ci, Cn and Ri are column index, number of
+ * columns and row index respectively.
+ * @param[in] ctx Context from keyboard_run()
+ */
+typedef void (*keyboard_cb)(ksm_event_t event, uint32_t idx, void const *ctx);
+
+/**
+ * @brief Register callbacks and activate device.
+ * The callback is called from SW event.
+ * @param[in] callback Callback function. Called when a new keyboard
+ * event occurred.
+ * @param[in] ctx Context passed to callback
+ */
+__NONNULL(1)
+void keyboard_run(keyboard_cb callback, void const *ctx);
+
+#ifdef __cplusplus
+}
+#endif
+
+/// @} KEYBOARD
diff --git a/platform/atm2/ATM22xx-x1x/driver/keyboard/keyboard_internal.h b/platform/atm2/ATM22xx-x1x/driver/keyboard/keyboard_internal.h
new file mode 100644
index 0000000..9f3b750
--- /dev/null
+++ b/platform/atm2/ATM22xx-x1x/driver/keyboard/keyboard_internal.h
@@ -0,0 +1,262 @@
+/**
+ *******************************************************************************
+ *
+ * @file keyboard_internal.h
+ *
+ * @brief keyboard ini helper macro
+ * This file is only included by keyboard_init() to reduce the context length.
+ *
+ * Copyright (C) Atmosic 2018-2021
+ *
+ *******************************************************************************
+ */
+#ifdef __KEYBOARD_INIT_USAGE__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Macro for setting mux
+#define KSM_COL_SET(x) do { \
+ if (!(ksm_pin_check & (1ULL << PIN_COL##x))) { \
+ DEBUG_TRACE("P%d is not supported.", PIN_COL##x); \
+ ASSERT_ERR(0); \
+ } \
+ PINMUX_KSO_SET(x); \
+ col[COL##x##_KSO] = x; \
+} while (0)
+
+#define KSM_ROW_SET(x) do { \
+ if (!(ksm_pin_check & (1ULL << PIN_ROW##x))) { \
+ DEBUG_TRACE("P%d is not supported.", PIN_ROW##x); \
+ ASSERT_ERR(0); \
+ } \
+ PINMUX_KSI_SET(x); \
+ row[ROW##x##_KSI] = x; \
+} while (0)
+
+#if (MAX_ROW > 0) && defined(PIN_ROW0) && defined(ROW0_KSI)
+ KSM_ROW_SET(0);
+#ifdef KSI_PULLUPS
+ PIN_PULLUP(PIN_ROW0);
+#endif // KSI_PULLUPS
+#endif // (MAX_ROW > 0) && defined(PIN_ROW0) && defined(ROW0_KSI)
+
+#if (MAX_ROW > 1) && defined(PIN_ROW1) && defined(ROW1_KSI)
+ KSM_ROW_SET(1);
+#ifdef KSI_PULLUPS
+ PIN_PULLUP(PIN_ROW1);
+#endif // KSI_PULLUPS
+#endif // (MAX_ROW > 1) && defined(PIN_ROW1) && defined(ROW1_KSI)
+
+#if (MAX_ROW > 2) && defined(PIN_ROW2) && defined(ROW2_KSI)
+ KSM_ROW_SET(2);
+#ifdef KSI_PULLUPS
+ PIN_PULLUP(PIN_ROW2);
+#endif // KSI_PULLUPS
+#endif // (MAX_ROW > 2) && defined(PIN_ROW2) && defined(ROW2_KSI)
+
+#if (MAX_ROW > 3) && defined(PIN_ROW3) && defined(ROW3_KSI)
+ KSM_ROW_SET(3);
+#ifdef KSI_PULLUPS
+ PIN_PULLUP(PIN_ROW3);
+#endif // KSI_PULLUPS
+#endif // (MAX_ROW > 3) && defined(PIN_ROW3) && defined(ROW3_KSI)
+
+#if (MAX_ROW > 4) && defined(PIN_ROW4) && defined(ROW4_KSI)
+ KSM_ROW_SET(4);
+#ifdef KSI_PULLUPS
+ PIN_PULLUP(PIN_ROW4);
+#endif // KSI_PULLUPS
+#endif // (MAX_ROW > 4) && defined(PIN_ROW4) && defined(ROW4_KSI)
+
+#if (MAX_ROW > 5) && defined(PIN_ROW5) && defined(ROW5_KSI)
+ KSM_ROW_SET(5);
+#ifdef KSI_PULLUPS
+ PIN_PULLUP(PIN_ROW5);
+#endif // KSI_PULLUPS
+#endif // (MAX_ROW > 5) && defined(PIN_ROW5) && defined(ROW5_KSI)
+
+#if (MAX_ROW > 6) && defined(PIN_ROW6) && defined(ROW6_KSI)
+ KSM_ROW_SET(6);
+#ifdef KSI_PULLUPS
+ PIN_PULLUP(PIN_ROW6);
+#endif // KSI_PULLUPS
+#endif // (MAX_ROW > 6) && defined(PIN_ROW6) && defined(ROW6_KSI)
+
+#if (MAX_ROW > 7) && defined(PIN_ROW7) && defined(ROW7_KSI)
+ KSM_ROW_SET(7);
+#ifdef KSI_PULLUPS
+ PIN_PULLUP(PIN_ROW7);
+#endif // KSI_PULLUPS
+#endif // (MAX_ROW > 7) && defined(PIN_ROW7) && defined(ROW7_KSI)
+
+#if (MAX_ROW > 8) && defined(PIN_ROW8) && defined(ROW8_KSI)
+ KSM_ROW_SET(8);
+#ifdef KSI_PULLUPS
+ PIN_PULLUP(PIN_ROW8);
+#endif // KSI_PULLUPS
+#endif // (MAX_ROW > 8) && defined(PIN_ROW8) && defined(ROW8_KSI)
+
+#if (MAX_ROW > 9) && defined(PIN_ROW9) && defined(ROW9_KSI)
+ KSM_ROW_SET(9);
+#ifdef KSI_PULLUPS
+ PIN_PULLUP(PIN_ROW9);
+#endif // KSI_PULLUPS
+#endif // (MAX_ROW > 9) && defined(PIN_ROW9) && defined(ROW9_KSI)
+
+#if (MAX_ROW > 10) && defined(PIN_ROW10) && defined(ROW10_KSI)
+ KSM_ROW_SET(10);
+#ifdef KSI_PULLUPS
+ PIN_PULLUP(PIN_ROW10);
+#endif // KSI_PULLUPS
+#endif // (MAX_ROW > 10) && defined(PIN_ROW10) && defined(ROW10_KSI)
+
+#if (MAX_ROW > 11) && defined(PIN_ROW11) && defined(ROW11_KSI)
+ KSM_ROW_SET(11);
+#ifdef KSI_PULLUPS
+ PIN_PULLUP(PIN_ROW11);
+#endif // KSI_PULLUPS
+#endif // (MAX_ROW > 11) && defined(PIN_ROW11) && defined(ROW11_KSI)
+
+#if (MAX_ROW > 12) && defined(PIN_ROW12) && defined(ROW12_KSI)
+ KSM_ROW_SET(12);
+#ifdef KSI_PULLUPS
+ PIN_PULLUP(PIN_ROW12);
+#endif // KSI_PULLUPS
+#endif // (MAX_ROW > 12) && defined(PIN_ROW12) && defined(ROW12_KSI)
+
+#if (MAX_ROW > 13) && defined(PIN_ROW13) && defined(ROW13_KSI)
+ KSM_ROW_SET(13);
+#ifdef KSI_PULLUPS
+ PIN_PULLUP(PIN_ROW13);
+#endif // KSI_PULLUPS
+#endif // (MAX_ROW > 13) && defined(PIN_ROW13) && defined(ROW13_KSI)
+
+#if (MAX_ROW > 14) && defined(PIN_ROW14) && defined(ROW14_KSI)
+ KSM_ROW_SET(14);
+#ifdef KSI_PULLUPS
+ PIN_PULLUP(PIN_ROW14);
+#endif // KSI_PULLUPS
+#endif // (MAX_ROW > 14) && defined(PIN_ROW14) && defined(ROW14_KSI)
+
+#if (MAX_ROW > 15) && defined(PIN_ROW15) && defined(ROW15_KSI)
+ KSM_ROW_SET(15);
+#ifdef KSI_PULLUPS
+ PIN_PULLUP(PIN_ROW15);
+#endif // KSI_PULLUPS
+#endif // (MAX_ROW > 15) && defined(PIN_ROW15) && defined(ROW15_KSI)
+
+#if (MAX_ROW > 16) && defined(PIN_ROW16) && defined(ROW16_KSI)
+ KSM_ROW_SET(16);
+#ifdef KSI_PULLUPS
+ PIN_PULLUP(PIN_ROW16);
+#endif // KSI_PULLUPS
+#endif // (MAX_ROW > 16) && defined(PIN_ROW16) && defined(ROW16_KSI)
+
+#if (MAX_ROW > 17) && defined(PIN_ROW17) && defined(ROW17_KSI)
+ KSM_ROW_SET(17);
+#ifdef KSI_PULLUPS
+ PIN_PULLUP(PIN_ROW17);
+#endif // KSI_PULLUPS
+#endif // (MAX_ROW > 17) && defined(PIN_ROW17) && defined(ROW17_KSI)
+
+#if (MAX_ROW > 18) && defined(PIN_ROW18) && defined(ROW18_KSI)
+ KSM_ROW_SET(18);
+#ifdef KSI_PULLUPS
+ PIN_PULLUP(PIN_ROW18);
+#endif // KSI_PULLUPS
+#endif // (MAX_ROW > 18) && defined(PIN_ROW18) && defined(ROW18_KSI)
+
+#if (MAX_COL > 0) && defined(PIN_COL0) && defined(COL0_KSO)
+ KSM_COL_SET(0);
+#endif // (MAX_COL > 0) && defined(PIN_COL0) && defined(COL0_KSO)
+
+#if (MAX_COL > 1) && defined(PIN_COL1) && defined(COL1_KSO)
+ KSM_COL_SET(1);
+#endif // (MAX_COL > 1) && defined(PIN_COL1) && defined(COL1_KSO)
+
+#if (MAX_COL > 2) && defined(PIN_COL2) && defined(COL2_KSO)
+ KSM_COL_SET(2);
+#endif // (MAX_COL > 2) && defined(PIN_COL2) && defined(COL2_KSO)
+
+#if (MAX_COL > 3) && defined(PIN_COL3) && defined(COL3_KSO)
+ KSM_COL_SET(3);
+#endif // (MAX_COL > 3) && defined(PIN_COL3) && defined(COL3_KSO)
+
+#if (MAX_COL > 4) && defined(PIN_COL4) && defined(COL4_KSO)
+ KSM_COL_SET(4);
+#endif // (MAX_COL > 4) && defined(PIN_COL4) && defined(COL4_KSO)
+
+#if (MAX_COL > 5) && defined(PIN_COL5) && defined(COL5_KSO)
+ KSM_COL_SET(5);
+#endif // (MAX_COL > 5) && defined(PIN_COL5) && defined(COL5_KSO)
+
+#if (MAX_COL > 6) && defined(PIN_COL6) && defined(COL6_KSO)
+ KSM_COL_SET(6);
+#endif // (MAX_COL > 6) && defined(PIN_COL6) && defined(COL6_KSO)
+
+#if (MAX_COL > 7) && defined(PIN_COL7) && defined(COL7_KSO)
+ KSM_COL_SET(7);
+#endif // (MAX_COL > 7) && defined(PIN_COL7) && defined(COL7_KSO)
+
+#if (MAX_COL > 8) && defined(PIN_COL8) && defined(COL8_KSO)
+ KSM_COL_SET(8);
+#endif // (MAX_COL > 8) && defined(PIN_COL8) && defined(COL8_KSO)
+
+#if (MAX_COL > 9) && defined(PIN_COL9) && defined(COL9_KSO)
+ KSM_COL_SET(9);
+/// In hw_cfg_pseq_init, the P1 was pullup to avoid leakage on designs
+/// that tie TMC high. But if P1 is used for column of keymatrix, pullup will
+/// cause current leakage when keyscan. So here clears the pull up for P1.
+#if (PIN_COL9 == 1)
+ PIN_PULL_CLR(PIN_COL9);
+#endif
+#endif // (MAX_COL > 9) && defined(PIN_COL9) && defined(COL9_KSO)
+
+#if (MAX_COL > 10) && defined(PIN_COL10) && defined(COL10_KSO)
+ KSM_COL_SET(10);
+#endif // (MAX_COL > 10) && defined(PIN_COL10) && defined(COL10_KSO)
+
+#if (MAX_COL > 11) && defined(PIN_COL11) && defined(COL11_KSO)
+ KSM_COL_SET(11);
+#endif // (MAX_COL > 11) && defined(PIN_COL11) && defined(COL11_KSO)
+
+#if (MAX_COL > 12) && defined(PIN_COL12) && defined(COL12_KSO)
+ KSM_COL_SET(12);
+#endif // (MAX_COL > 12) && defined(PIN_COL12) && defined(COL12_KSO)
+
+#if (MAX_COL > 13) && defined(PIN_COL13) && defined(COL13_KSO)
+ KSM_COL_SET(13);
+#endif // (MAX_COL > 13) && defined(PIN_COL13) && defined(COL13_KSO)
+
+#if (MAX_COL > 14) && defined(PIN_COL14) && defined(COL14_KSO)
+ KSM_COL_SET(14);
+#endif // (MAX_COL > 14) && defined(PIN_COL14) && defined(COL14_KSO)
+
+#if (MAX_COL > 15) && defined(PIN_COL15) && defined(COL15_KSO)
+ KSM_COL_SET(15);
+#endif // (MAX_COL > 15) && defined(PIN_COL15) && defined(COL15_KSO)
+
+#if (MAX_COL > 16) && defined(PIN_COL16) && defined(COL16_KSO)
+ KSM_COL_SET(16);
+#endif // (MAX_COL > 16) && defined(PIN_COL16) && defined(COL16_KSO)
+
+#if (MAX_COL > 17) && defined(PIN_COL17) && defined(COL17_KSO)
+ KSM_COL_SET(17);
+#endif // (MAX_COL > 17) && defined(PIN_COL17) && defined(COL17_KSO)
+
+#if (MAX_COL > 18) && defined(PIN_COL18) && defined(COL18_KSO)
+ KSM_COL_SET(18);
+#endif // (MAX_COL > 18) && defined(PIN_COL18) && defined(COL18_KSO)
+
+#if (MAX_COL > 19) && defined(PIN_COL19) && defined(COL19_KSO)
+ KSM_COL_SET(19);
+#endif // (MAX_COL > 19) && defined(PIN_COL19) && defined(COL19_KSO)
+
+#ifdef __cplusplus
+}
+#endif
+
+#undef __KEYBOARD_INIT_USAGE__
+#endif // __KEYBOARD_INIT_USAGE__
diff --git a/platform/atm2/ATM22xx-x1x/driver/led_blink/led_blink.h b/platform/atm2/ATM22xx-x1x/driver/led_blink/led_blink.h
new file mode 100644
index 0000000..278a792
--- /dev/null
+++ b/platform/atm2/ATM22xx-x1x/driver/led_blink/led_blink.h
@@ -0,0 +1,84 @@
+/**
+ *******************************************************************************
+ *
+ * @file led_blink.h
+ *
+ * @brief LED control
+ *
+ * Copyright (C) Atmosic 2020-2021
+ *
+ *******************************************************************************
+ */
+#pragma once
+
+/**
+ *******************************************************************************
+ * @defgroup LED LED driver
+ * @ingroup DRIVERS
+ * @brief ATM bluetooth framework LED driver
+ *
+ * This module contains the necessary function of LED.
+ *
+ * @{
+ *******************************************************************************
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/// LED ID type
+typedef uint8_t led_id_t;
+
+/// LED enum
+enum {
+ /// LED_0
+ LED_0,
+ /// LED_1
+ LED_1,
+
+ /// LED_MAX
+ LED_MAX
+};
+
+/**
+ *******************************************************************************
+ * @brief Config LED blink parameter
+ * @param[in] id LED ID
+ * @param[in] hi_dur LED hi activity time(unit: 10ms)
+ * @param[in] low_dur LED low activity time(unit: 10ms)
+ * @param[in] times The count for Hi+Low LED activity
+ *******************************************************************************
+ */
+void led_blink(led_id_t id, uint16_t hi_dur, uint16_t low_dur, uint16_t times);
+
+/**
+ *******************************************************************************
+ * @brief Turn on LED
+ * @param[in] id LED ID
+ *******************************************************************************
+ */
+void led_on(led_id_t id);
+
+/**
+ *******************************************************************************
+ * @brief Turn off LED
+ * @param[in] id LED ID
+ *******************************************************************************
+ */
+void led_off(led_id_t id);
+
+/**
+ *******************************************************************************
+ * @brief Get LED status
+ * @param[in] id LED ID
+ * @return LED pin value.
+ *******************************************************************************
+ */
+bool led_status(led_id_t id);
+
+#ifdef __cplusplus
+}
+#endif
+
+///@} LED
diff --git a/platform/atm2/ATM22xx-x1x/driver/sw_event/sw_event.h b/platform/atm2/ATM22xx-x1x/driver/sw_event/sw_event.h
new file mode 100644
index 0000000..adcf1df
--- /dev/null
+++ b/platform/atm2/ATM22xx-x1x/driver/sw_event/sw_event.h
@@ -0,0 +1,91 @@
+/**
+ ******************************************************************************
+ *
+ * @file sw_event.h
+ *
+ * @brief Atmosic Software Event Driver
+ *
+ * Copyright (C) Atmosic 2020
+ *
+ ******************************************************************************
+ */
+
+#ifndef __SW_EVENT_H__
+#define __SW_EVENT_H__
+
+/**
+ * @defgroup SW_EVENT Software Event APIs
+ * @ingroup DRIVERS
+ * @brief User driver for application to use software events
+ * @{
+ */
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/// Maximum number of concurrent events - adjust as needed
+#define SW_EVENT_ID_MAX 8
+
+/// Event ID type
+typedef uint8_t sw_event_id_t;
+
+/// Event triggered callback function type
+typedef void (*sw_event_func_t)(sw_event_id_t event_id, const void *ctx);
+
+/**
+ * @brief Allocate and configure event
+ * @param[in] handler Event triggered callback (called from main event loop)
+ * @param[in] ctx Context to pass to handler
+ * @return Event ID
+ */
+sw_event_id_t sw_event_alloc(sw_event_func_t handler, const void *ctx);
+
+/**
+ * @brief Deallocate event
+ * @note Caller must guarantee that set/clear methods are not invoked from
+ * an ISR while this method is running
+ * @param[in] event_id Event ID from sw_event_alloc()
+ */
+void sw_event_free(sw_event_id_t event_id);
+
+/**
+ * @brief Reconfigure event handler and context
+ * @param[in] event_id Event ID from sw_event_alloc()
+ * @param[in] handler Event triggered callback (called from main event loop)
+ * @param[in] ctx Context to pass to handler
+ */
+void sw_event_reconfig(sw_event_id_t event_id, sw_event_func_t handler,
+ const void *ctx);
+
+/**
+ * @brief Trigger event
+ * @note Safe to be called from ISR
+ * @param[in] event_id Event ID from sw_event_alloc()
+ */
+void sw_event_set(sw_event_id_t event_id);
+
+/**
+ * @brief Clear event
+ * @note Safe to be called from ISR
+ * @param[in] event_id Event ID from sw_event_alloc()
+ */
+void sw_event_clear(sw_event_id_t event_id);
+
+/**
+ * @brief Get event status
+ * @param[in] event_id Event ID from sw_event_alloc()
+ * @return True when event is valid and triggered
+ */
+bool sw_event_get(sw_event_id_t event_id);
+
+#ifdef __cplusplus
+}
+#endif
+
+/// @} SW_EVENT
+
+#endif // __SW_EVENT_H__
diff --git a/platform/atm2/ATM22xx-x1x/driver/sw_timer/sw_timer.h b/platform/atm2/ATM22xx-x1x/driver/sw_timer/sw_timer.h
new file mode 100644
index 0000000..1f249e5
--- /dev/null
+++ b/platform/atm2/ATM22xx-x1x/driver/sw_timer/sw_timer.h
@@ -0,0 +1,95 @@
+/**
+ ******************************************************************************
+ *
+ * @file sw_timer.h
+ *
+ * @brief Atmosic Software Timer Driver
+ *
+ * Copyright (C) Atmosic 2020-2021
+ *
+ ******************************************************************************
+ */
+
+#ifndef __SW_TIMER_H__
+#define __SW_TIMER_H__
+
+/**
+ * @defgroup SW_TIMER Software Timer APIs
+ * @ingroup DRIVERS
+ * @brief User driver for application to use software timers
+ * @{
+ */
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/// Maximum number of concurrent timers - adjust as needed
+#ifndef SW_TIMER_ID_MAX
+#define SW_TIMER_ID_MAX 8
+#endif // SW_TIMER_ID_MAX
+
+/// Smallest granularity for sw_timer_set()
+#define SW_TIMER_10_MS 1
+/// Commonly used quantity for sw_timer_set()
+#define SW_TIMER_1_SEC 100
+
+/// Timer ID type
+typedef uint8_t sw_timer_id_t;
+
+/// Timer expired callback function type
+typedef void (*sw_timer_func_t)(sw_timer_id_t timer_id, const void *ctx);
+
+/**
+ * @brief Allocate and configure timer
+ * @param[in] handler Timer expired callback (called from main event loop)
+ * @param[in] ctx Context to pass to handler
+ * @return Timer ID
+ */
+sw_timer_id_t sw_timer_alloc(sw_timer_func_t handler, const void *ctx);
+
+/**
+ * @brief Deallocate timer
+ * @param[in] timer_id Timer ID from sw_timer_alloc()
+ */
+void sw_timer_free(sw_timer_id_t timer_id);
+
+/**
+ * @brief Reconfigure timer handler and context
+ * @param[in] timer_id Timer ID from sw_timer_alloc()
+ * @param[in] handler Timer expired callback (called from main event loop)
+ * @param[in] ctx Context to pass to handler
+ */
+void sw_timer_reconfig(sw_timer_id_t timer_id, sw_timer_func_t handler,
+ const void *ctx);
+
+/**
+ * @brief Start one-shot timer running
+ * @param[in] timer_id Timer ID from sw_timer_alloc()
+ * @param[in] centisec Duration in hundredths of seconds
+ */
+void sw_timer_set(sw_timer_id_t timer_id, uint32_t centisec);
+
+/**
+ * @brief Stop/abort running timer
+ * @param[in] timer_id Timer ID from sw_timer_alloc()
+ */
+void sw_timer_clear(sw_timer_id_t timer_id);
+
+/**
+ * @brief Get timer status
+ * @param[in] timer_id Timer ID from sw_timer_alloc()
+ * @return True when timer is valid and running
+ */
+bool sw_timer_active(sw_timer_id_t timer_id);
+
+#ifdef __cplusplus
+}
+#endif
+
+/// @} SW_TIMER
+
+#endif // __SW_TIMER_H__
diff --git a/platform/atm2/ATM22xx-x1x/driver/timer/timer.h b/platform/atm2/ATM22xx-x1x/driver/timer/timer.h
new file mode 100644
index 0000000..ac5d77c
--- /dev/null
+++ b/platform/atm2/ATM22xx-x1x/driver/timer/timer.h
@@ -0,0 +1,266 @@
+/**
+ ******************************************************************************
+ *
+ * @file timer.h
+ *
+ * @brief Atmosic Timer Driver
+ *
+ * Copyright (C) Atmosic 2019-2022
+ *
+ ******************************************************************************
+ */
+
+#ifndef __TIMER_H__
+#define __TIMER_H__
+
+#include "at_wrpr.h"
+
+/**
+ * @defgroup TIMER HW Timer APIs
+ * @ingroup DRIVERS
+ * @brief User driver for application to use HW timers
+ * @{
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/// Number of centisecond per second
+#define CS_PER_SEC 100
+
+/// Number of millisecond per second
+#define MS_PER_SEC 1000
+
+/// Number of millisecond per centisecond
+#define MS_PER_CS 10
+
+/// Number of microsecond per centisecond
+#define US_PER_CS 10000
+
+/// Number of microsecond per millisecond
+#define US_PER_MS 1000
+
+/// Number of second per minute
+#define SEC_PER_MIN 60
+
+/// Number of minute per hour
+#define MIN_PER_HOUR 60
+
+typedef enum {
+ ATM_TIMER0 = 0,
+ ATM_TIMER1 = 1,
+ ATM_DUALTIMER1 = 2,
+ ATM_DUALTIMER2 = 3,
+ ATM_SYSTICK = 4,
+ ATM_SLWTIMER = 5, // Runs off 32Khz clock
+ ATM_TIMERS_MAX = 6
+} atm_timer_id_t;
+
+typedef enum {
+ ATM_TIMER_MODE_SINGLE_SHOT = 0,
+ ATM_TIMER_MODE_PERIODIC = 1,
+ ATM_TIMER_MODE_MAX = 2
+} atm_timer_mode_t;
+
+typedef enum {
+ ATM_TIMER_SUCCESS = 0,
+ ATM_TIMER_INVALID_PARAM = 1,
+ ATM_TIMER_RESOURCE_BUSY = 2,
+ ATM_TIMER_GENERIC_ERROR = 3
+} atm_timer_error_t;
+
+typedef void (*atm_timer_callback_t) (void *app_context);
+
+/**
+ * @brief Setup timer.
+ * @param[in] id Timer ID
+ * @param[in] mode SingleShot vs Periodic
+ * @param[in] timeout_handler Application layer callback handler
+ * (called from interrupt context)
+ * @return Success or Error status
+ */
+atm_timer_error_t atm_timer_setup(atm_timer_id_t id, atm_timer_mode_t mode,
+ atm_timer_callback_t timeout_handler);
+
+/**
+ * @brief Start timer.
+ * @param[in] id Timer ID
+ * @param[in] timeout_us Timer value in micro seconds
+ * @param[in] app_context Application specific context
+ * @return Success or Error status
+ */
+atm_timer_error_t atm_timer_start(atm_timer_id_t id, uint32_t timeout_us,
+ void *app_context);
+
+/**
+ * @brief Start dual timer with more accurate unit.(Only for single shot mode)
+ * @param[in] id Dual timer ID
+ * @param[in] timeout_cycles Timer value in cycles
+ */
+void atm_dual_timer_single_shot_quick_start(atm_timer_id_t id,
+ uint32_t timeout_cycles);
+
+/**
+ * @brief Stop timer.
+ * @param[in] id Timer ID
+ * @return Success or Error status
+ */
+atm_timer_error_t atm_timer_stop(atm_timer_id_t id);
+
+/**
+ * @brief Busy wait using CMSDK_PSEQ->CURRENT_REAL_TIME.
+ * @param[in] msec Delay value in milliseconds
+ * @return Success or Error status
+ */
+atm_timer_error_t atm_timer_mdelay(uint32_t msec);
+
+/**
+ * @brief Busy wait using the Cortex-M0 SysTick.
+ * @param[in] usec Delay value in microseconds
+ * @return Success or Error status
+ */
+atm_timer_error_t atm_timer_udelay(uint32_t usec);
+
+/**
+ * @brief Sleep in WFI using a timer.
+ * @param[in] id Timer ID
+ * @param[in] usec Timer value in microseconds
+ * @return Success or Error status
+ */
+atm_timer_error_t atm_timer_usleep(atm_timer_id_t id, uint32_t usec);
+
+// Permit customization for restrictive calling contexts
+// or to analyze rt{,1,2,3} locals
+#ifndef TIMER_ASSERT_ERR
+#define TIMER_ASSERT_ERR ASSERT_ERR
+#endif
+
+/**
+ * @brief Get the current system time based on the 32kHz clock
+ */
+__INLINE uint32_t atm_get_sys_time(void)
+{
+ // The high clock domain latch low clock domain signal directly
+ // would probably get bad value due to metastability. Considering
+ // the combinations, it could have 3 conditions:
+ // 1. bad -> good -> good
+ // 2. good -> good
+ // 3. good -> bad -> good -> good
+
+ uint32_t rt;
+ WRPR_CTRL_PUSH(CMSDK_PSEQ, WRPR_CTRL__CLK_ENABLE) {
+ GLOBAL_INT_DISABLE();
+ uint32_t rt1 = CMSDK_PSEQ->CURRENT_REAL_TIME;
+ rt = CMSDK_PSEQ->CURRENT_REAL_TIME;
+ if (rt != rt1) {
+ uint32_t rt3 = CMSDK_PSEQ->CURRENT_REAL_TIME;
+ if (rt != rt3) {
+ __UNUSED uint32_t rt2 = rt;
+ rt = CMSDK_PSEQ->CURRENT_REAL_TIME;
+ TIMER_ASSERT_ERR(rt == rt3);
+ }
+ }
+ GLOBAL_INT_RESTORE();
+ } WRPR_CTRL_POP();
+ return rt;
+}
+
+#ifdef LPC_RCOS
+/**
+ * @brief Fetch LP clock frequency
+ *
+ * @return Value in Hz, or 0 when using a xtal
+ */
+uint32_t lpc_rcos_hz(void);
+
+#define TIMER_GET_LPC_FREQ ({ \
+ uint32_t lp_hz = lpc_rcos_hz(); \
+ lp_hz ? lp_hz : 32768; \
+})
+#else
+#define TIMER_GET_LPC_FREQ (32768)
+#endif
+
+/**
+ * @brief Busy wait using CMSDK_PSEQ->CURRENT_REAL_TIME.
+ * @param[in] ticks Delay value in counts of CMSDK_PSEQ->CURRENT_REAL_TIME.
+ * @return Success or Error status
+ */
+__INLINE void atm_timer_lpc_delay(uint32_t ticks)
+{
+ uint32_t then = atm_get_sys_time();
+ while (atm_get_sys_time() - then < ticks) {
+ YIELD();
+ }
+}
+
+/**
+ * @brief Translate centiseconds to ticks
+ * @param[in] cs Number of centiseconds
+ * @return LPC duration
+ */
+static inline uint32_t atm_cs_to_lpc(uint32_t cs)
+{
+ return ((uint64_t)cs * TIMER_GET_LPC_FREQ) / CS_PER_SEC;
+}
+
+/**
+ * @brief Translate milliseconds to ticks
+ * @param[in] ms Number of milliseconds
+ * @return LPC duration
+ */
+static inline uint32_t atm_ms_to_lpc(uint32_t ms)
+{
+ return ((uint64_t)ms * TIMER_GET_LPC_FREQ) / MS_PER_SEC;
+}
+
+/**
+ * @brief Translate microseconds to ticks
+ * @param[in] us Number of microseconds
+ * @return LPC duration
+ */
+static inline uint32_t atm_us_to_lpc(uint64_t us)
+{
+ return (us * TIMER_GET_LPC_FREQ) / (US_PER_MS * MS_PER_SEC);
+}
+
+/**
+ * @brief Translate ticks to centiseconds
+ * @param[in] lpc LPC duration
+ * @return Number of centiseconds
+ */
+static inline uint32_t atm_lpc_to_cs(uint32_t lpc)
+{
+ return ((uint64_t)lpc * CS_PER_SEC) / TIMER_GET_LPC_FREQ;
+}
+
+/**
+ * @brief Translate ticks to milliseconds
+ * @param[in] lpc LPC duration
+ * @return Number of milliseconds
+ */
+static inline uint32_t atm_lpc_to_ms(uint32_t lpc)
+{
+ return ((uint64_t)lpc * MS_PER_SEC) / TIMER_GET_LPC_FREQ;
+}
+
+/**
+ * @brief Translate ticks to microseconds
+ * @param[in] lpc LPC duration
+ * @return Number of microseconds
+ */
+static inline uint64_t atm_lpc_to_us(uint32_t lpc)
+{
+ return ((uint64_t)lpc * US_PER_MS * MS_PER_SEC) / TIMER_GET_LPC_FREQ;
+}
+
+#undef TIMER_GET_LPC_FREQ
+
+#ifdef __cplusplus
+}
+#endif
+
+/// @} TIMER
+
+#endif // __TIMER_H__