Files
bacnet_stack/ports/xplained/ASF/xmega/services/timeout/timeout.c
T
2019-10-08 23:47:53 -05:00

232 lines
6.3 KiB
C

/**
* \file timeout.c
*
* \brief Timeout service for XMEGA
*
* Copyright (C) 2011-2012 Atmel Corporation. All rights reserved.
*
* \asf_license_start
*
* \page License
*
* 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. The name of Atmel may not be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* 4. This software may only be redistributed and used in connection with an
* Atmel microcontroller product.
*
* THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
* EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL 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.
*
* \asf_license_stop
*
*/
#include <asf.h>
#include <conf_timeout.h>
/* Check if RTC32 is defined, otherwise use RTC as default */
#if defined(CLOCK_SOURCE_RTC32)
#include <rtc32.h>
#else
#include <rtc.h>
#endif
/** \brief Timeout timekeeping data */
struct timeout_struct {
/**
* Current count-down value. Counts down for every tick.
* Will be considered as expired when it reaches 0, and
* may then be reloaded with period.
*/
uint16_t count;
/**
* Period between expires. Used to reload count.
* If 0, the count won't be reloaded.
*/
uint16_t period;
};
/** Array of configurable timeout timekeeping data */
static struct timeout_struct timeout_array[TIMEOUT_COUNT];
/** Bitmask of active timeouts */
static uint8_t timeout_active;
/** Bitmask of expired timeouts */
static uint8_t timeout_expired;
/**
* \brief Callback function for RTC compare interrupt handler
*
* The function executes when the RTC compare interrupt occurs and loop
* through all timeout channels. The timeout_array[channel_index] which
* contains the remaining ticks before timeout is decremented and the timeout
* active/expired masks are updated.
*/
static void tick_handler(uint32_t time)
{
uint8_t i;
/* Loop through all timeout channels */
for (i = 0; i < TIMEOUT_COUNT; i++) {
/* Skip processing on current channel if not active */
if (!(timeout_active & (1 << i))) {
continue;
}
/* Decrement current channel with one tick */
timeout_array[i].count--;
/* Skip further processing on current channel if not expired */
if (timeout_array[i].count) {
continue;
} else {
/* Update expired bit mask with current channel */
timeout_expired |= 1 << i;
/* If Periodic timer, reset timeout counter to period
* time */
if (timeout_array[i].period) {
timeout_array[i].count
= timeout_array[i].period;
}
/* If not periodic timeout, set current channel to
* in-active */
else {
timeout_active &= ~(1 << i);
}
}
}
/* Reset RTC before next tick */
rtc_set_time(0);
rtc_set_alarm(TIMEOUT_COMP);
}
/**
* \brief Initialize timeout
*
* Initializes timeout counter for desired tick rate and starts it. The device
* interrupt controller should be initialized prior to calling this function,
* and global interrupts must be enabled.
*
* \note If the service is configured to use the asynchronous RTC32 module,
* there are restrictions on the timeout period that can be used - see
* to \ref rtc32_min_alarm_time for details.
*/
void timeout_init(void)
{
rtc_init();
rtc_set_callback(tick_handler);
rtc_set_time(0);
rtc_set_alarm(TIMEOUT_COMP);
}
/**
* \brief Start periodic timeout with a specific start timeout
*
* \param id \ref timeout_id_t
* \param period Time period in number of ticks
* \param offset Time to first timeout in number of ticks
*/
void timeout_start_offset(timeout_id_t id, uint16_t period, uint16_t offset)
{
/* Check that ID within the TIMEOUT_COUNT range */
if (id < TIMEOUT_COUNT) {
/* Disable interrupts before tweaking the bitmasks */
irqflags_t flags;
flags = cpu_irq_save();
/* Update timeout struct with offset and period */
timeout_array[id].count = offset;
timeout_array[id].period = period;
/* Set current timeout channel bitmasks to active and not
* expired */
timeout_active |= 1 << id;
timeout_expired &= ~(1 << id);
/* Restore interrupts */
cpu_irq_restore(flags);
}
}
/**
* \brief Start singleshot timeout
*
* \param id \ref timeout_id_t
* \param timeout Timeout in number of ticks
*/
void timeout_start_singleshot(timeout_id_t id, uint16_t timeout)
{
timeout_start_offset(id, 0, timeout);
}
/**
* \brief Start periodic timeout
*
* \param id \ref timeout_id_t
* \param period Time period in number of ticks
*/
void timeout_start_periodic(timeout_id_t id, uint16_t period)
{
timeout_start_offset(id, period, period);
}
/**
* \brief Test and clear expired flag for running timeout
*
* \param id \ref timeout_id_t
* \retval true Timer have expired; clearing expired flag
* \retval false Timer still running
*/
bool timeout_test_and_clear_expired(timeout_id_t id)
{
/* Check that ID within the TIMEOUT_COUNT range */
if (id < TIMEOUT_COUNT) {
irqflags_t flags;
/* Check if timeout has expired */
if (timeout_expired & (1 << id)) {
flags = cpu_irq_save();
timeout_expired &= ~(1 << id);
cpu_irq_restore(flags);
return true;
}
}
return false;
}
/**
* \brief Stop running timeout
*
* \param id \ref timeout_id_t
*/
void timeout_stop(timeout_id_t id)
{
irqflags_t flags;
flags = cpu_irq_save();
timeout_active &= ~(1 << id);
cpu_irq_restore(flags);
}