/**
* \file
*
* \brief SAM RTC Driver (Count Interrupt Mode)
*
* Copyright (c) 2013-2018 Microchip Technology Inc. and its subsidiaries.
*
* \asf_license_start
*
* \page License
*
* Subject to your compliance with these terms, you may use Microchip
* software and any derivatives exclusively with Microchip products.
* It is your responsibility to comply with third party license terms applicable
* to your use of third party software (including open source software) that
* may accompany Microchip software.
*
* THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES,
* WHETHER EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE,
* INCLUDING ANY IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY,
* AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL MICROCHIP BE
* LIABLE FOR ANY INDIRECT, SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL
* LOSS, DAMAGE, COST OR EXPENSE OF ANY KIND WHATSOEVER RELATED TO THE
* SOFTWARE, HOWEVER CAUSED, EVEN IF MICROCHIP HAS BEEN ADVISED OF THE
* POSSIBILITY OR THE DAMAGES ARE FORESEEABLE. TO THE FULLEST EXTENT
* ALLOWED BY LAW, MICROCHIP'S TOTAL LIABILITY ON ALL CLAIMS IN ANY WAY
* RELATED TO THIS SOFTWARE WILL NOT EXCEED THE AMOUNT OF FEES, IF ANY,
* THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR THIS SOFTWARE.
*
* \asf_license_stop
*
*/
/*
* Support and FAQ: visit Microchip Support
*/
#include "rtc_count_interrupt.h"
extern struct rtc_module *_rtc_instance[RTC_INST_NUM];
/**
* \brief Registers callback for the specified callback type.
*
* Associates the given callback function with the
* specified callback type.
* To enable the callback, the \ref rtc_count_enable_callback function
* must be used.
*
* \param[in,out] module Pointer to the software instance struct
* \param[in] callback Pointer to the function desired for the specified
* callback
* \param[in] callback_type Callback type to register
*
* \return Status of registering callback.
* \retval STATUS_OK Registering was done successfully
* \retval STATUS_ERR_INVALID_ARG If trying to register a callback not available
*/
enum status_code rtc_count_register_callback(
struct rtc_module *const module,
rtc_count_callback_t callback,
enum rtc_count_callback callback_type)
{
enum status_code status = STATUS_OK;
/* Overflow callback */
if (callback_type == RTC_COUNT_CALLBACK_OVERFLOW
#ifdef FEATURE_RTC_TAMPER_DETECTION
|| callback_type == RTC_COUNT_CALLBACK_TAMPER
#endif
|| (callback_type >= RTC_COUNT_CALLBACK_PERIODIC_INTERVAL_0
&& callback_type <= RTC_COUNT_CALLBACK_PERIODIC_INTERVAL_7)) {
status = STATUS_OK;
} else {
/* Make sure callback type can be registered */
switch (module->mode) {
case RTC_COUNT_MODE_32BIT:
/* Check sanity for 32-bit mode. */
if (callback_type > (RTC_COMP32_NUM + RTC_PER_NUM)) {
status = STATUS_ERR_INVALID_ARG;
}
break;
case RTC_COUNT_MODE_16BIT:
/* Check sanity for 16-bit mode. */
if (callback_type > (RTC_NUM_OF_COMP16 + RTC_PER_NUM)) {
status = STATUS_ERR_INVALID_ARG;
}
break;
default:
status = STATUS_ERR_INVALID_ARG;
}
}
if (status == STATUS_OK) {
/* Register callback */
module->callbacks[callback_type] = callback;
/* Set corresponding bit to set callback as registered */
module->registered_callback |= (1 << callback_type);
}
return status;
}
/**
* \brief Unregisters callback for the specified callback type.
*
* When called, the currently registered callback for the given callback type
* will be removed.
*
* \param[in,out] module Pointer to the software instance struct
* \param[in] callback_type Specifies the callback type to unregister
*
* \return Status of unregistering callback.
* \retval STATUS_OK Unregistering was done successfully
* \retval STATUS_ERR_INVALID_ARG If trying to unregister a callback not available
*/
enum status_code rtc_count_unregister_callback(
struct rtc_module *const module,
enum rtc_count_callback callback_type)
{
enum status_code status = STATUS_OK;
/* Overflow callback */
if (callback_type == RTC_COUNT_CALLBACK_OVERFLOW
#ifdef FEATURE_RTC_TAMPER_DETECTION
|| callback_type == RTC_COUNT_CALLBACK_TAMPER
#endif
|| (callback_type >= RTC_COUNT_CALLBACK_PERIODIC_INTERVAL_0
&& callback_type <= RTC_COUNT_CALLBACK_PERIODIC_INTERVAL_7)) {
status = STATUS_OK;
} else {
/* Make sure callback type can be unregistered */
switch (module->mode) {
case RTC_COUNT_MODE_32BIT:
/* Check sanity for 32-bit mode. */
if (callback_type > (RTC_COMP32_NUM + RTC_PER_NUM)) {
status = STATUS_ERR_INVALID_ARG;
}
break;
case RTC_COUNT_MODE_16BIT:
/* Check sanity for 16-bit mode. */
if (callback_type > (RTC_NUM_OF_COMP16 + RTC_PER_NUM)) {
status = STATUS_ERR_INVALID_ARG;
}
break;
default:
status = STATUS_ERR_INVALID_ARG;
}
}
if (status == STATUS_OK) {
/* Unregister callback */
module->callbacks[callback_type] = NULL;
/* Clear corresponding bit to set callback as unregistered */
module->registered_callback &= ~(1 << callback_type);
}
return status;
}
/**
* \brief Enables callback.
*
* Enables the callback specified by the callback_type.
*
* \param[in,out] module Pointer to the software instance struct
* \param[in] callback_type Callback type to enable
*/
void rtc_count_enable_callback(
struct rtc_module *const module,
enum rtc_count_callback callback_type)
{
/* Sanity check arguments */
Assert(module);
Assert(module->hw);
Rtc *const rtc_module = module->hw;
if (callback_type == RTC_COUNT_CALLBACK_OVERFLOW) {
rtc_module->MODE0.INTENSET.reg = RTC_MODE0_INTFLAG_OVF;
#ifdef FEATURE_RTC_TAMPER_DETECTION
} else if (callback_type == RTC_COUNT_CALLBACK_TAMPER) {
rtc_module->MODE0.INTENSET.reg = RTC_MODE0_INTFLAG_TAMPER;
#endif
} else if (callback_type >= RTC_COUNT_CALLBACK_PERIODIC_INTERVAL_0
&& callback_type <= RTC_COUNT_CALLBACK_PERIODIC_INTERVAL_7) {
rtc_module->MODE0.INTENSET.reg = RTC_MODE1_INTFLAG_PER(1 << callback_type);
}else {
rtc_module->MODE0.INTENSET.reg = RTC_MODE1_INTFLAG_CMP(1 << (callback_type - RTC_PER_NUM));
}
/* Mark callback as enabled. */
module->enabled_callback |= (1 << callback_type);
}
/**
* \brief Disables callback.
*
* Disables the callback specified by the callback_type.
*
* \param[in,out] module Pointer to the software instance struct
* \param[in] callback_type Callback type to disable
*/
void rtc_count_disable_callback(
struct rtc_module *const module,
enum rtc_count_callback callback_type)
{
/* Sanity check arguments */
Assert(module);
Assert(module->hw);
Rtc *const rtc_module = module->hw;
/* Disable interrupt */
if (callback_type == RTC_COUNT_CALLBACK_OVERFLOW) {
rtc_module->MODE0.INTENCLR.reg = RTC_MODE0_INTFLAG_OVF;
#ifdef FEATURE_RTC_TAMPER_DETECTION
} else if (callback_type == RTC_COUNT_CALLBACK_TAMPER) {
rtc_module->MODE0.INTENCLR.reg = RTC_MODE0_INTFLAG_TAMPER;
#endif
} else if(callback_type >= RTC_COUNT_CALLBACK_PERIODIC_INTERVAL_0
&& callback_type <= RTC_COUNT_CALLBACK_PERIODIC_INTERVAL_7){
rtc_module->MODE0.INTENCLR.reg = RTC_MODE1_INTFLAG_PER(1 << callback_type);;
}else {
rtc_module->MODE0.INTENCLR.reg = RTC_MODE1_INTFLAG_CMP(1 << (callback_type - RTC_PER_NUM));
}
/* Mark callback as disabled. */
module->enabled_callback &= ~(1 << callback_type);
}
/**
* \internal Interrupt handler for RTC
*
* \param [in] instance_index Default value 0
*/
static void _rtc_interrupt_handler(const uint32_t instance_index)
{
struct rtc_module *module = _rtc_instance[instance_index];
Rtc *const rtc_module = module->hw;
/* Combine callback registered and enabled masks */
uint16_t callback_mask = module->enabled_callback;
callback_mask &= module->registered_callback;
/* Read and mask interrupt flag register */
uint16_t interrupt_status = rtc_module->MODE0.INTFLAG.reg;
interrupt_status &= rtc_module->MODE0.INTENSET.reg;
if (interrupt_status & RTC_MODE0_INTFLAG_OVF) {
/* Overflow interrupt */
if (callback_mask & (1 << RTC_COUNT_CALLBACK_OVERFLOW)) {
module->callbacks[RTC_COUNT_CALLBACK_OVERFLOW]();
}
/* Clear interrupt flag */
rtc_module->MODE0.INTFLAG.reg = RTC_MODE0_INTFLAG_OVF;
#ifdef FEATURE_RTC_TAMPER_DETECTION
} else if (interrupt_status & RTC_MODE0_INTFLAG_TAMPER) {
/* Tamper interrupt */
if (callback_mask & (1 << RTC_COUNT_CALLBACK_TAMPER)) {
module->callbacks[RTC_COUNT_CALLBACK_TAMPER]();
}
/* Clear interrupt flag */
rtc_module->MODE0.INTFLAG.reg = RTC_MODE0_INTFLAG_TAMPER;
#endif
} else if (interrupt_status & RTC_MODE1_INTFLAG_PER(0xff)) {
uint8_t i = 0;
for ( i = 0;i < RTC_PER_NUM;i++) {
if ((interrupt_status & RTC_MODE1_INTFLAG_PER(1 << i))
&& (callback_mask & (1 << i))) {
module->callbacks[i]();
}
/* Clear interrupt flag */
rtc_module->MODE0.INTFLAG.reg = RTC_MODE1_INTFLAG_PER(1<callbacks[RTC_COUNT_CALLBACK_COMPARE_0]();
}
/* Clear interrupt flag */
rtc_module->MODE0.INTFLAG.reg = RTC_MODE1_INTFLAG_CMP(1 << 0);
} else if (interrupt_status & RTC_MODE1_INTFLAG_CMP(1 << 1)) {
#if (RTC_NUM_OF_COMP16 > 1) || defined(__DOXYGEN__)
/* Compare 1 interrupt */
if (callback_mask & (1 << RTC_COUNT_CALLBACK_COMPARE_1)) {
module->callbacks[RTC_COUNT_CALLBACK_COMPARE_1]();
}
/* Clear interrupt flag */
rtc_module->MODE0.INTFLAG.reg = RTC_MODE1_INTFLAG_CMP(1 << 1);
#endif
} else if (interrupt_status & RTC_MODE1_INTFLAG_CMP(1 << 2)) {
#if (RTC_NUM_OF_COMP16 > 2) || defined(__DOXYGEN__)
/* Compare 2 interrupt */
if (callback_mask & (1 << RTC_COUNT_CALLBACK_COMPARE_2)) {
module->callbacks[RTC_COUNT_CALLBACK_COMPARE_2]();
}
/* Clear interrupt flag */
rtc_module->MODE0.INTFLAG.reg = RTC_MODE1_INTFLAG_CMP(1 << 2);
#endif
} else if (interrupt_status & RTC_MODE1_INTFLAG_CMP(1 << 3)) {
#if (RTC_NUM_OF_COMP16 > 3) || defined(__DOXYGEN__)
/* Compare 3 interrupt */
if (callback_mask & (1 << RTC_COUNT_CALLBACK_COMPARE_3)) {
module->callbacks[RTC_COUNT_CALLBACK_COMPARE_3]();
}
/* Clear interrupt flag */
rtc_module->MODE0.INTFLAG.reg = RTC_MODE1_INTFLAG_CMP(1 << 3);
#endif
} else if (interrupt_status & RTC_MODE1_INTFLAG_CMP(1 << 4)) {
#if (RTC_NUM_OF_COMP16 > 4) || defined(__DOXYGEN__)
/* Compare 4 interrupt */
if (callback_mask & (1 << RTC_COUNT_CALLBACK_COMPARE_4)) {
module->callbacks[RTC_COUNT_CALLBACK_COMPARE_4]();
}
/* Clear interrupt flag */
rtc_module->MODE0.INTFLAG.reg = RTC_MODE1_INTFLAG_CMP(1 << 4);
#endif
} else if (interrupt_status & RTC_MODE1_INTFLAG_CMP(1 << 5)) {
#if (RTC_NUM_OF_COMP16 > 5) || defined(__DOXYGEN__)
/* Compare 5 interrupt */
if (callback_mask & (1 << RTC_COUNT_CALLBACK_COMPARE_5)) {
module->callbacks[RTC_COUNT_CALLBACK_COMPARE_5]();
}
/* Clear interrupt flag */
rtc_module->MODE0.INTFLAG.reg = RTC_MODE1_INTFLAG_CMP(1 << 5);
#endif
}
}
/**
* \internal ISR handler for RTC
*/
#if (RTC_INST_NUM == 1)
void RTC_Handler(void)
{
_rtc_interrupt_handler(0);
}
#elif (RTC_INST_NUM > 1)
# define _RTC_INTERRUPT_HANDLER(n, unused) \
void RTC##n##_Handler(void) \
{ \
_rtc_interrupt_handler(n); \
}
MREPEAT(RTC_INST_NUM, _RTC_INTERRUPT_HANDLER, ~)
#endif /* (RTC_INST_NUM > 1) */