blob: 5aa83328d269543288f851f5da12c83418354ee8 [file] [log] [blame]
/**
* \addtogroup BSP
* \{
* \addtogroup DEVICES
* \{
* \addtogroup Wakeup_Timer
* \{
* \brief Wakeup Timer
*/
/**
*****************************************************************************************
*
* @file hw_wkup.h
*
* @brief Definition of API for the Wakeup timer Low Level Driver.
*
* Copyright (c) 2016, Dialog Semiconductor
* All rights reserved.
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*
*
*****************************************************************************************
*/
#ifndef HW_WKUP_H_
#define HW_WKUP_H_
#if dg_configUSE_HW_WKUP
#include <stdbool.h>
#include <stdint.h>
#include <sdk_defs.h>
#include "hw_gpio.h"
#if (dg_configBLACK_ORCA_IC_REV != BLACK_ORCA_IC_REV_A) && dg_configLATCH_WKUP_SOURCE
#define WKUP_SEL_P0_BASE_REG (volatile uint16_t *)(&WAKEUP->WKUP_SEL_GPIO_P0_REG)
#else
#define WKUP_SEL_P0_BASE_REG (volatile uint16_t *)(&WAKEUP->WKUP_SELECT_P0_REG)
#endif
#if (dg_configBLACK_ORCA_IC_REV == BLACK_ORCA_IC_REV_A) && dg_configLATCH_WKUP_SOURCE
/**
* \brief Wakeup timer configuration
*
*/
typedef struct {
uint8_t pin_state[HW_GPIO_NUM_PORTS]; /**< pin states in each port, 1: enabled, 0: disabled */
uint8_t pin_trigger[HW_GPIO_NUM_PORTS]; /**< pin triggers in each port, 1: low, 0: high */
} wkup_pin_config_t;
extern volatile wkup_pin_config_t wkup_pin_config; /**< stores wkup pin configuration */
extern volatile uint8_t wkup_status[HW_GPIO_NUM_PORTS]; /**< stores wakeup sources */
#endif
/**
* \brief Get the mask of a field of an WKUP register.
*
* \param [in] reg is the register to access
* \param [in] field is the register field to access
*
*/
#define HW_WKUP_REG_FIELD_MASK(reg, field) \
(WAKEUP_WKUP_##reg##_REG_##field##_Msk)
/**
* \brief Get the bit position of a field of an WKUP register.
*
* \param [in] reg is the register to access
* \param [in] field is the register field to access
*
*/
#define HW_WKUP_REG_FIELD_POS(reg, field) \
(WAKEUP_WKUP_##reg##_REG_##field##_Pos)
/**
* \brief Get the value of a field of an WKUP register.
*
* \param [in] reg is the register to access
* \param [in] field is the register field to write
*
* \return the value of the register field
*
*/
#define HW_WKUP_REG_GETF(reg, field) \
((WAKEUP->WKUP_##reg##_REG & (WAKEUP_WKUP_##reg##_REG_##field##_Msk)) >> (WAKEUP_WKUP_##reg##_REG_##field##_Pos))
/**
* \brief Set the value of a field of an WKUP register.
*
* \param [in] reg is the register to access
* \param [in] field is the register field to write
* \param [in] new_val is the value to write
*
*/
#define HW_WKUP_REG_SETF(reg, field, new_val) \
WAKEUP->WKUP_##reg##_REG = ((WAKEUP->WKUP_##reg##_REG & ~(WAKEUP_WKUP_##reg##_REG_##field##_Msk)) | \
((WAKEUP_WKUP_##reg##_REG_##field##_Msk) & ((new_val) << (WAKEUP_WKUP_##reg##_REG_##field##_Pos))))
/**
* \brief Pin state which increments event counter
*
*/
typedef enum {
HW_WKUP_PIN_STATE_HIGH = 0,
HW_WKUP_PIN_STATE_LOW = 1
} HW_WKUP_PIN_STATE;
/**
* \brief Wakeup timer configuration
*
*/
typedef struct {
#if dg_configBLACK_ORCA_IC_REV == BLACK_ORCA_IC_REV_A
/**
* counter threshold
*
* \warning Supported only in DA14680/1 chips
*/
uint8_t threshold;
#endif
#if !((dg_configLATCH_WKUP_SOURCE) && (dg_configBLACK_ORCA_IC_REV != BLACK_ORCA_IC_REV_A))
uint8_t debounce; /**< debounce time in ms */
#endif
uint8_t pin_state[HW_GPIO_NUM_PORTS]; /**< pin states in each port, see hw_wkup_configure_port */
uint8_t pin_trigger[HW_GPIO_NUM_PORTS]; /**< pin triggers in each port, see hw_wkup_configure_port */
} wkup_config;
typedef void (*hw_wkup_interrupt_cb)(void);
/**
* \brief Initialize peripheral
*
* Resets Wakeup Timer to initial state, i.e. interrupt is disabled and all pin triggers are
* disabled.
*
* \p cfg can be NULL - no configuration is performed in such case.
*
* \param [in] cfg configuration
*
*/
void hw_wkup_init(const wkup_config *cfg);
/**
* \brief Configure peripheral
*
* Shortcut to call appropriate configuration function. If \p cfg is NULL, this function does
* nothing.
*
* \param [in] cfg configuration
*
*/
void hw_wkup_configure(const wkup_config *cfg);
/**
* \brief Register interrupt handler
*
* Callback function is called when interrupt is generated, i.e. event counter reaches configured
* value. Interrupt is automatically enabled after calling this function. Application should reset
* interrupt in callback function using hw_wkup_reset_interrupt(). If no callback is specified,
* interrupt will be automatically cleared by the driver.
*
* \param [in] cb callback function
* \param [in] prio the priority of the interrupt
*
* \sa hw_wkup_unregister_interrupt
* \sa hw_wkup_clear_interrupt
* \sa hw_wkup_set_counter_threshold
* \sa hw_wkup_get_counter
*
*/
void hw_wkup_register_interrupt(hw_wkup_interrupt_cb cb, uint32_t prio);
/**
* \brief Unregister interrupt handler
*
* Interrupt is automatically disabled after calling this function.
*
*/
void hw_wkup_unregister_interrupt(void);
/**
* \brief Reset interrupt
*
* This function MUST be called by any user-specified interrupt callback, to clear the interrupt.
*
*/
static inline void hw_wkup_reset_interrupt(void)
{
WAKEUP->WKUP_RESET_IRQ_REG = 1;
}
/**
* \brief Interrupt handler
*
*/
void hw_wkup_handler(void);
#if !((dg_configLATCH_WKUP_SOURCE) && (dg_configBLACK_ORCA_IC_REV != BLACK_ORCA_IC_REV_A))
/**
* \brief Set debounce time
*
* Setting debounce time to 0 will disable hardware debouncing. Maximum debounce time is 63ms.
*
* \param [in] time_ms debounce time in milliseconds
*
*/
static inline void hw_wkup_set_debounce_time(uint8_t time_ms)
{
HW_WKUP_REG_SETF(CTRL, WKUP_DEB_VALUE, time_ms);
}
#endif //!((dg_configLATCH_WKUP_SOURCE) && (dg_configBLACK_ORCA_IC_REV != BLACK_ORCA_IC_REV_A))
/**
* \brief Get current debounce time
*
* \return debounce time in milliseconds
*
*/
static inline uint8_t hw_wkup_get_debounce_time(void)
{
return HW_WKUP_REG_GETF(CTRL, WKUP_DEB_VALUE);
}
#if dg_configBLACK_ORCA_IC_REV == BLACK_ORCA_IC_REV_A
/**
* \brief Set threshold for event counter
*
* Interrupt is generated after event counter reaches configured value.
*
* \param [in] level number of events
*
* \warning Supported only in DA14680/1 chips
* \sa hw_wkup_register_interrupt
* \sa hw_wkup_get_counter
*
*/
static inline void hw_wkup_set_counter_threshold(uint8_t level)
{
HW_WKUP_REG_SETF(COMPARE, COMPARE, level);
}
/**
* \brief Get threshold for event counter
*
* \return number of events
*
* \warning Supported only in DA14680/1 chips
*/
static inline uint8_t hw_wkup_get_counter_threshold(void)
{
return HW_WKUP_REG_GETF(COMPARE, COMPARE);
}
/**
* \brief Get current value of event counter
*
* Number of events counted so far. Can be reset using hw_wkup_reset_counter().
*
* \return number of events
*
* \note The counter is automatically reset by the hardware when the interrupt is generated.
*
* \warning Supported only in DA14680/1 chips
*
* \sa hw_wkup_reset_counter
*
*/
static inline uint8_t hw_wkup_get_counter(void)
{
return HW_WKUP_REG_GETF(COUNTER, EVENT_VALUE);
}
/**
* \brief Reset event counter
*
* There is no need to reset counter manually in interrupt callback - it's reset automatically by
* hardware.
*
* \warning Supported only in DA14680/1 chips
*/
static inline void hw_wkup_reset_counter(void)
{
WAKEUP->WKUP_RESET_CNTR_REG = 1;
}
#endif
/**
* \brief Set GPIO pin event counting state
*
* Once enabled, state changes on pin will increment event counter. State which triggers event can
* be set using hw_wkup_set_pin_trigger().
*
* \param [in] port port number
* \param [in] pin pin number
* \param [in] enabled pin event counting state
*
* \sa hw_wkup_set_pin_trigger
* \sa hw_wkup_configure_pin
* \sa hw_wkup_configure_port
*
*/
static inline void hw_wkup_set_pin_state(HW_GPIO_PORT port, HW_GPIO_PIN pin, bool enabled)
{
volatile uint16_t *wkup_pin_enable_reg = WKUP_SEL_P0_BASE_REG + port;
uint16_t wkup_pin_enable_val = *wkup_pin_enable_reg;
wkup_pin_enable_val &= ~(1 << pin);
wkup_pin_enable_val |= (!!enabled) << pin;
*wkup_pin_enable_reg = wkup_pin_enable_val;
#if (dg_configBLACK_ORCA_IC_REV == BLACK_ORCA_IC_REV_A) && dg_configLATCH_WKUP_SOURCE
wkup_pin_config.pin_state[port] = wkup_pin_enable_val;
#endif
}
/** \brief Get GPIO pin event counting state
*
* \param [in] port port number
* \param [in] pin pin number
*
* \return pin event counting state
*
* \sa hw_wkup_set_pin_state
*
*/
static inline bool hw_wkup_get_pin_state(HW_GPIO_PORT port, HW_GPIO_PIN pin)
{
uint16_t wkup_pin_enable_val = *(WKUP_SEL_P0_BASE_REG + port);
return (wkup_pin_enable_val & (1 << pin)) >> pin;
}
/**
* \brief Set GPIO pin state which triggers event
*
* Pin event counting should be enabled for this setting to have any effect.
*
* \param [in] port port number
* \param [in] pin pin number
* \param [in] state pin state
*
* \sa hw_wkup_set_pin_counter_state
* \sa hw_wkup_configure_pin
* \sa hw_wkup_configure_port
*
*/
static inline void hw_wkup_set_pin_trigger(HW_GPIO_PORT port, HW_GPIO_PIN pin,
HW_WKUP_PIN_STATE state)
{
uint16_t pol_rx_reg = *((volatile uint16_t *)(&WAKEUP->WKUP_POL_P0_REG) + port);
pol_rx_reg &= ~(1 << pin);
pol_rx_reg |= (!!state) << pin;
*((volatile uint16_t *)(&WAKEUP->WKUP_POL_P0_REG) + port) = pol_rx_reg;
#if (dg_configBLACK_ORCA_IC_REV == BLACK_ORCA_IC_REV_A) && dg_configLATCH_WKUP_SOURCE
wkup_pin_config.pin_trigger[port] = pol_rx_reg;
#endif
}
/** \brief Get GPIO pin state which triggers event
*
* \param [in] port port number
* \param [in] pin pin number
*
* \return pin state
*
* \sa hw_wkup_set_pin_trigger
*
*/
static inline HW_WKUP_PIN_STATE hw_wkup_get_pin_trigger(HW_GPIO_PORT port, HW_GPIO_PIN pin)
{
uint16_t pol_rx_reg = *((volatile uint16_t *)(&WAKEUP->WKUP_POL_P0_REG) + port);
return (pol_rx_reg & (1 << pin)) >> pin;
}
/**
* \brief Set GPIO pin event counting and triggered state
*
* Effectively, this is shortcut for calling hw_wkup_set_pin_state() and hw_wkup_set_pin_trigger().
*
* \param [in] port port number
* \param [in] pin pin number
* \param [in] enabled pin event counting state
* \param [in] state pin state
*
* \sa hw_wkup_set_pin_counter_state
* \sa hw_wkup_set_pin_trigger
* \sa hw_wkup_configure_port
*
*/
static inline void hw_wkup_configure_pin(HW_GPIO_PORT port, HW_GPIO_PIN pin, bool enabled,
HW_WKUP_PIN_STATE state)
{
// first set up the proper polarity...
hw_wkup_set_pin_trigger(port, pin, state);
// ...then enable counting on the specific GPIO
hw_wkup_set_pin_state(port, pin, enabled);
}
/**
* \brief Configure event counting and triggering state for whole GPIO port
*
* In \p enabled and \p state bitmasks each bit describes state of corresponding pin in port.
* For \p enabled 0 means disabled and 1 means enabled.
* For \p state 0 means event is triggered on low state and 1 means trigger is on high state.
*
* \param [in] port port number
* \param [in] enabled pin event counting bitmask
* \param [in] state pin state bitmask
*
* \sa hw_wkup_set_pin_counter_state
* \sa hw_wkup_set_pin_trigger
* \sa hw_wkup_configure_pin
*
*/
static inline void hw_wkup_configure_port(HW_GPIO_PORT port, uint8_t enabled, uint8_t state)
{
*((volatile uint16_t *)(&WAKEUP->WKUP_POL_P0_REG) + port) = ~state; // register has inverted logic than state bitmask
*(WKUP_SEL_P0_BASE_REG + port) = enabled;
#if (dg_configBLACK_ORCA_IC_REV == BLACK_ORCA_IC_REV_A) && dg_configLATCH_WKUP_SOURCE
wkup_pin_config.pin_state[port] = enabled;
wkup_pin_config.pin_trigger[port] = ~state;
#endif
}
/**
* \brief Get state (enabled/disabled) of all pins in GPIO port
*
* Meaning of bits in returned bitmask is the same as in hw_wkup_configure_port().
*
* \return port pin event counter state bitmask
*
* \sa hw_wkup_configure_port
*
*/
static inline uint8_t hw_wkup_get_port_state(HW_GPIO_PORT port)
{
#if dg_configLATCH_WKUP_SOURCE
#if dg_configBLACK_ORCA_IC_REV == BLACK_ORCA_IC_REV_A
return wkup_pin_config.pin_state[port];
#else
return *((volatile uint16_t *)(&WAKEUP->WKUP_SEL_GPIO_P0_REG) + port);
#endif
#else
return *((volatile uint16_t *)(&WAKEUP->WKUP_SELECT_P0_REG) + port);
#endif
}
/**
* \brief Get event triggering state for all pins in GPIO port
*
* Meaning of bits in returned bitmask is the same as in hw_wkup_configure_port().
*
* \return port pin event triggering state bitmask
*
* \sa hw_wkup_configure_port
*
*/
static inline uint8_t hw_wkup_get_port_trigger(HW_GPIO_PORT port)
{
#if (dg_configBLACK_ORCA_IC_REV == BLACK_ORCA_IC_REV_A) && dg_configLATCH_WKUP_SOURCE
return wkup_pin_config.pin_trigger[port];
#else
return ~(*((volatile uint16_t *)(&WAKEUP->WKUP_POL_P0_REG) + port)); // register has inverted logic that returned bitmask
#endif
}
/**
* \brief Emulate key hit
*
* Event counter will be increased with debounce time taken into account (if enabled).
*
*/
static inline void hw_wkup_emulate_key_hit(void)
{
HW_WKUP_REG_SETF(CTRL, WKUP_SFT_KEYHIT, 1);
HW_WKUP_REG_SETF(CTRL, WKUP_SFT_KEYHIT, 0);
}
/**
* \brief Freeze wakeup timer
*
*/
static inline void hw_wkup_freeze(void)
{
GPREG->SET_FREEZE_REG = GPREG_SET_FREEZE_REG_FRZ_WKUPTIM_Msk;
}
/**
* \brief Unfreeze wakeup timer
*
*/
static inline void hw_wkup_unfreeze(void)
{
GPREG->RESET_FREEZE_REG = GPREG_RESET_FREEZE_REG_FRZ_WKUPTIM_Msk;
}
/**
* \brief Get port status on last wake up
*
* Meaning of bits in returned bitmask is the same as in hw_wkup_configure_port().
*
* \return port pin event counter state bitmask
*
* \sa hw_wkup_configure_port
*
*/
#if (dg_configBLACK_ORCA_IC_REV == BLACK_ORCA_IC_REV_A) && dg_configLATCH_WKUP_SOURCE
static inline uint8_t hw_wkup_get_status(HW_GPIO_PORT port)
{
return wkup_status[port];
}
#elif dg_configBLACK_ORCA_IC_REV != BLACK_ORCA_IC_REV_A
static inline uint8_t hw_wkup_get_status(HW_GPIO_PORT port)
{
switch (port) {
case HW_GPIO_PORT_0:
return HW_WKUP_REG_GETF(STATUS_0, WKUP_STAT_P0);
case HW_GPIO_PORT_1:
return HW_WKUP_REG_GETF(STATUS_0, WKUP_STAT_P1);
case HW_GPIO_PORT_2:
return HW_WKUP_REG_GETF(STATUS_1, WKUP_STAT_P2);
case HW_GPIO_PORT_3:
return HW_WKUP_REG_GETF(STATUS_2, WKUP_STAT_P3);
case HW_GPIO_PORT_4:
return HW_WKUP_REG_GETF(STATUS_2, WKUP_STAT_P4);
default:
ASSERT_WARNING(0);//Invalid argument
}
}
#endif
/**
* \brief Clear latch status
*
* This function MUST be called by any user-specified interrupt callback,
* to clear the interrupt latch status.
*
* \param [in] port port number
* \param [in] status pin status bitmask
*
* \sa hw_wkup_get_status
*/
#if (dg_configBLACK_ORCA_IC_REV == BLACK_ORCA_IC_REV_A) && dg_configLATCH_WKUP_SOURCE
static inline void hw_wkup_clear_status(HW_GPIO_PORT port, uint8_t status)
{
ASSERT_WARNING((port>=HW_GPIO_PORT_0) && (port<=HW_GPIO_PORT_4))
wkup_status[port] &= ~status;
}
#elif dg_configBLACK_ORCA_IC_REV != BLACK_ORCA_IC_REV_A
static inline void hw_wkup_clear_status(HW_GPIO_PORT port, uint8_t status)
{
switch (port) {
case HW_GPIO_PORT_0:
HW_WKUP_REG_SETF(CLEAR_0, WKUP_CLEAR_P0, status);
break;
case HW_GPIO_PORT_1:
HW_WKUP_REG_SETF(CLEAR_0, WKUP_CLEAR_P1, status);
break;
case HW_GPIO_PORT_2:
HW_WKUP_REG_SETF(CLEAR_1, WKUP_CLEAR_P2, status);
break;
case HW_GPIO_PORT_3:
HW_WKUP_REG_SETF(CLEAR_2, WKUP_CLEAR_P3, status);
break;
case HW_GPIO_PORT_4:
HW_WKUP_REG_SETF(CLEAR_2, WKUP_CLEAR_P4, status);
break;
default:
ASSERT_WARNING(0);//Invalid argument
}
}
#endif
#endif /* dg_configUSE_HW_WKUP */
#endif /* HW_WKUP_H_ */
/**
* \}
* \}
* \}
*/