Merge branch 'main' into more-asf-files
This commit is contained in:
@@ -271,7 +271,7 @@
|
||||
// <i> Indicates whether the external interrupt 5 filter is enabled or not
|
||||
// <id> eic_arch_filten5
|
||||
#ifndef CONF_EIC_FILTEN5
|
||||
#define CONF_EIC_FILTEN5 0
|
||||
#define CONF_EIC_FILTEN5 1
|
||||
#endif
|
||||
|
||||
// <q> External Interrupt 5 Event Output Enable
|
||||
@@ -723,7 +723,12 @@
|
||||
|
||||
// </e>
|
||||
|
||||
// my god this is a hack. need to refactor this out of ASF and into our driver. - joey 10/19
|
||||
#ifdef CRYSTALLESS
|
||||
#define CONFIG_EIC_EXTINT_MAP {2, PIN_PA02}, {5, PIN_PB05}, {7, PIN_PA07},
|
||||
#else
|
||||
#define CONFIG_EIC_EXTINT_MAP {2, PIN_PA02}, {6, PIN_PA22}, {7, PIN_PA23},
|
||||
#endif
|
||||
|
||||
// <<< end of configuration section >>>
|
||||
|
||||
|
||||
@@ -248,9 +248,14 @@
|
||||
// <i> This defines the clock source for generic clock generator 3
|
||||
// <id> gclk_gen_3_oscillator
|
||||
#ifndef CONF_GCLK_GEN_3_SOURCE
|
||||
#ifdef CRYSTALLESS
|
||||
#define CONF_GCLK_GEN_3_SOURCE GCLK_GENCTRL_SRC_OSCULP32K
|
||||
#else
|
||||
#define CONF_GCLK_GEN_3_SOURCE GCLK_GENCTRL_SRC_XOSC32K
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
// <q> Run in Standby
|
||||
// <i> Indicates whether Run in Standby is enabled or not
|
||||
// <id> gclk_arch_gen_3_runstdby
|
||||
|
||||
@@ -17,8 +17,12 @@
|
||||
// <i> This defines the clock source for RTC
|
||||
// <id> rtc_source_oscillator
|
||||
#ifndef CONF_RTCCTRL_SRC
|
||||
#ifdef CRYSTALLESS
|
||||
#define CONF_RTCCTRL_SRC GCLK_GENCTRL_SRC_OSCULP32K
|
||||
#else
|
||||
#define CONF_RTCCTRL_SRC GCLK_GENCTRL_SRC_XOSC32K
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// <q> Use 1 kHz output
|
||||
// <id> rtc_1khz_selection
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include <hpl_slcd_cm.h>
|
||||
#include <peripheral_clk_config.h>
|
||||
#include "pins.h"
|
||||
|
||||
// <h> Standard configuration
|
||||
|
||||
@@ -79,7 +80,7 @@
|
||||
// <3=>128
|
||||
// <id> slcd_arch_presc
|
||||
#ifndef CONF_SLCD_PRESC
|
||||
#define CONF_SLCD_PRESC 2
|
||||
#define CONF_SLCD_PRESC 1
|
||||
#endif
|
||||
|
||||
// <o> Clock Divider
|
||||
@@ -94,7 +95,7 @@
|
||||
// <7=>8
|
||||
// <id> slcd_arch_ckdiv
|
||||
#ifndef CONF_SLCD_CKDIV
|
||||
#define CONF_SLCD_CKDIV 3
|
||||
#define CONF_SLCD_CKDIV 5
|
||||
#endif
|
||||
|
||||
/* TODO add frame frequency check */
|
||||
@@ -2729,15 +2730,40 @@
|
||||
} \
|
||||
}
|
||||
|
||||
#define CONF_SLCD_LPENL \
|
||||
((uint32_t)1 << 0 | (uint32_t)1 << 1 | (uint32_t)1 << 2 | (uint32_t)1 << 3 | (uint32_t)1 << 4 | (uint32_t)1 << 5 \
|
||||
| (uint32_t)1 << 6 | (uint32_t)1 << 7 | (uint32_t)1 << 11 | (uint32_t)1 << 12 | (uint32_t)1 << 13 \
|
||||
| (uint32_t)1 << 14 | (uint32_t)1 << 21 | (uint32_t)1 << 22 | (uint32_t)1 << 23 | (uint32_t)1 << 24 \
|
||||
| (uint32_t)1 << 25 | (uint32_t)1 << 28 | (uint32_t)1 << 29 | (uint32_t)1 << 30 | (uint32_t)1 << 31 | 0)
|
||||
#ifndef CONF_SLCD_LPENL
|
||||
#define CONF_SLCD_LPENL (\
|
||||
(uint32_t)1 << 0 | \
|
||||
(uint32_t)1 << 1 | \
|
||||
(uint32_t)1 << 2 | \
|
||||
(uint32_t)1 << 3 | \
|
||||
(uint32_t)1 << 4 | \
|
||||
(uint32_t)1 << 5 | \
|
||||
(uint32_t)1 << 6 | \
|
||||
(uint32_t)1 << 7 | \
|
||||
(uint32_t)1 << 11 | \
|
||||
(uint32_t)1 << 12 | \
|
||||
(uint32_t)1 << 13 | \
|
||||
(uint32_t)1 << 14 | \
|
||||
(uint32_t)1 << 21 | \
|
||||
(uint32_t)1 << 22 | \
|
||||
(uint32_t)1 << 23 | \
|
||||
(uint32_t)1 << 24 | \
|
||||
(uint32_t)1 << 25 | \
|
||||
(uint32_t)1 << 28 | \
|
||||
(uint32_t)1 << 29 | \
|
||||
(uint32_t)1 << 30 | \
|
||||
(uint32_t)1 << 31 | 0)
|
||||
#endif // CONF_SLCD_LPENL
|
||||
|
||||
#define CONF_SLCD_LPENH \
|
||||
((uint32_t)1 << 0 | (uint32_t)1 << 1 | (uint32_t)1 << 2 | (uint32_t)1 << 3 | (uint32_t)1 << 10 | (uint32_t)1 << 11 \
|
||||
| 0) // </e>
|
||||
#ifndef CONF_SLCD_LPENH
|
||||
#define CONF_SLCD_LPENH (\
|
||||
(uint32_t)1 << (32 - 32) | \
|
||||
(uint32_t)1 << (33 - 32) | \
|
||||
(uint32_t)1 << (34 - 32) | \
|
||||
(uint32_t)1 << (35 - 32) | \
|
||||
(uint32_t)1 << (42 - 32) | \
|
||||
(uint32_t)1 << (43 - 32) | 0)
|
||||
#endif // CONF_SLCD_LPENH
|
||||
|
||||
// <<< end of configuration section >>>
|
||||
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
===============================
|
||||
The Calendar driver (bare-bone)
|
||||
===============================
|
||||
|
||||
The Calendar driver provides means to set and get current date and time.
|
||||
After enabling, an instance of the driver starts counting time from the base date with
|
||||
the resolution of one second. The default base date is 00:00:00 1st of January 1970.
|
||||
Only the base year of the base date can be changed via the driver API.
|
||||
|
||||
The current date and time is kept internally in a relative form as the difference between
|
||||
current date and time and the base date and time. This means that changing the base year changes
|
||||
current date.
|
||||
|
||||
The base date and time defines time "zero" or the earliest possible point in time that the calender driver can describe,
|
||||
this means that current time and alarms can not be set to anything earlier than this time.
|
||||
|
||||
The Calendar driver provides alarm functionality.
|
||||
An alarm is a software trigger which fires on particular date and time with particular periodicity.
|
||||
Upon firing the given callback function is called.
|
||||
|
||||
An alarm can be in single-shot mode, firing only once at matching time; or in repeating mode, meaning that it will
|
||||
reschedule a new alarm automatically based on repeating mode configuration.
|
||||
In single-shot mode an alarm is removed from the alarm queue before its callback is called. It allows an application to
|
||||
reuse the memory of expired alarm in the callback.
|
||||
|
||||
An alarm can be triggered on the following events: match on second, minute, hour, day, month or year.
|
||||
Matching on second means that the alarm is triggered when the value of seconds of the current time is equal to
|
||||
the alarm's value of seconds. This means repeating alarm with match on seconds is triggered with the period of a minute.
|
||||
Matching on minute means that the calendars minute and seconds values has to match the alarms, the rest of the date-time
|
||||
value is ignored. In repeating mode this means a new alarm every hour.
|
||||
The same logic is applied to match on hour, day, month and year.
|
||||
|
||||
Each instance of the Calendar driver supports infinite amount of software alarms, only limited by the amount of RAM available.
|
||||
|
||||
Features
|
||||
--------
|
||||
* Initialization and de-initialization
|
||||
* Enabling and disabling
|
||||
* Date and time operations
|
||||
* Software alarms
|
||||
|
||||
Applications
|
||||
------------
|
||||
* A source of current date and time for an embedded system.
|
||||
* Periodical functionality in low-power applications since the driver is designed to use 1Hz clock.
|
||||
* Periodical function calls in case if it is more convenient to operate with absolute time.
|
||||
|
||||
Dependencies
|
||||
------------
|
||||
* This driver expects a counter to be increased by one every second to count date and time correctly.
|
||||
* Each instance of the driver requires separate hardware timer.
|
||||
|
||||
Concurrency
|
||||
-----------
|
||||
The Calendar driver is an interrupt driven driver.This means that the interrupt that triggers an alarm may occur during
|
||||
the process of adding or removing an alarm via the driver's API. In such case the interrupt processing is postponed
|
||||
until the alarm adding or removing is complete.
|
||||
|
||||
The alarm queue is not protected from the access by interrupts not used by the driver. Due to this
|
||||
it is not recommended to add or remove an alarm from such interrupts: in case if a higher priority interrupt supersedes
|
||||
the driver's interrupt, adding or removing an alarm may cause unpredictable behavior of the driver.
|
||||
|
||||
Limitations
|
||||
-----------
|
||||
* Only years divisible by 4 are deemed a leap year, this gives a correct result between the years 1901 to 2099.
|
||||
* The driver is designed to work outside of an operating system environment, the software alarm queue is therefore processed in interrupt context which may delay execution of other interrupts.
|
||||
* If there are a lot of frequently called interrupts with the priority higher than the driver's one, it may cause delay in alarm's triggering.
|
||||
* Changing the base year or setting current date or time does not shift alarms' date and time accordingly or expires alarms.
|
||||
|
||||
Knows issues and workarounds
|
||||
----------------------------
|
||||
Not applicable
|
||||
@@ -1,159 +0,0 @@
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* \brief Generic CALENDAR functionality declaration.
|
||||
*
|
||||
* Copyright (c) 2014-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
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _HAL_CALENDER_H_INCLUDED
|
||||
#define _HAL_CALENDER_H_INCLUDED
|
||||
|
||||
#include "hpl_calendar.h"
|
||||
#include <utils_list.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* \addtogroup doc_driver_hal_calendar_async
|
||||
*
|
||||
*@{
|
||||
*/
|
||||
|
||||
/** \brief Prototype of callback on alarm match
|
||||
* \param calendar Pointer to the HAL Calendar instance.
|
||||
*/
|
||||
typedef void (*calendar_cb_alarm_t)(struct calendar_descriptor *const calendar);
|
||||
|
||||
/** \brief Struct for alarm time
|
||||
*/
|
||||
struct calendar_alarm {
|
||||
struct list_element elem;
|
||||
struct _calendar_alarm cal_alarm;
|
||||
calendar_cb_alarm_t callback;
|
||||
};
|
||||
|
||||
/** \brief Initialize the Calendar HAL instance and hardware
|
||||
*
|
||||
* \param calendar Pointer to the HAL Calendar instance.
|
||||
* \param hw Pointer to the hardware instance.
|
||||
* \return Operation status of init
|
||||
* \retval 0 Completed successfully.
|
||||
*/
|
||||
int32_t calendar_init(struct calendar_descriptor *const calendar, const void *hw);
|
||||
|
||||
/** \brief Reset the Calendar HAL instance and hardware
|
||||
*
|
||||
* Reset Calendar instance to hardware defaults.
|
||||
*
|
||||
* \param calendar Pointer to the HAL Calendar instance.
|
||||
* \return Operation status of reset.
|
||||
* \retval 0 Completed successfully.
|
||||
*/
|
||||
int32_t calendar_deinit(struct calendar_descriptor *const calendar);
|
||||
|
||||
/** \brief Enable the Calendar HAL instance and hardware
|
||||
*
|
||||
* \param calendar Pointer to the HAL Calendar instance.
|
||||
* \return Operation status of init
|
||||
* \retval 0 Completed successfully.
|
||||
*/
|
||||
int32_t calendar_enable(struct calendar_descriptor *const calendar);
|
||||
|
||||
/** \brief Disable the Calendar HAL instance and hardware
|
||||
*
|
||||
* Disable Calendar instance to hardware defaults.
|
||||
*
|
||||
* \param calendar Pointer to the HAL Calendar instance.
|
||||
* \return Operation status of reset.
|
||||
* \retval 0 Completed successfully.
|
||||
*/
|
||||
int32_t calendar_disable(struct calendar_descriptor *const calendar);
|
||||
|
||||
/** \brief Configure the base year for calendar HAL instance and hardware
|
||||
*
|
||||
* \param calendar Pointer to the HAL Calendar instance.
|
||||
* \param p_base_year The desired base year.
|
||||
* \retval 0 Completed successfully.
|
||||
*/
|
||||
int32_t calendar_set_baseyear(struct calendar_descriptor *const calendar, const uint32_t p_base_year);
|
||||
|
||||
/** \brief Configure the time for calendar HAL instance and hardware
|
||||
*
|
||||
* \param calendar Pointer to the HAL Calendar instance.
|
||||
* \param p_calendar_time Pointer to the time configuration.
|
||||
* \retval 0 Completed successfully.
|
||||
*/
|
||||
int32_t calendar_set_time(struct calendar_descriptor *const calendar, struct calendar_time *const p_calendar_time);
|
||||
|
||||
/** \brief Configure the date for calendar HAL instance and hardware
|
||||
*
|
||||
* \param calendar Pointer to the HAL Calendar instance.
|
||||
* \param p_calendar_date Pointer to the date configuration.
|
||||
* \return Operation status of time set.
|
||||
* \retval 0 Completed successfully.
|
||||
*/
|
||||
int32_t calendar_set_date(struct calendar_descriptor *const calendar, struct calendar_date *const p_calendar_date);
|
||||
|
||||
/** \brief Get the time for calendar HAL instance and hardware
|
||||
*
|
||||
* \param calendar Pointer to the HAL Calendar instance.
|
||||
* \param date_time Pointer to the value that will be filled with the current time.
|
||||
* \return Operation status of time retrieve.
|
||||
* \retval 0 Completed successfully.
|
||||
*/
|
||||
int32_t calendar_get_date_time(struct calendar_descriptor *const calendar, struct calendar_date_time *const date_time);
|
||||
|
||||
/** \brief Config the alarm time for calendar HAL instance and hardware
|
||||
*
|
||||
* Set the alarm time to calendar instance. If the callback is NULL, remove
|
||||
* the alarm if the alarm is already added, otherwise, ignore the alarm.
|
||||
*
|
||||
* \param calendar Pointer to the HAL Calendar instance.
|
||||
* \param alarm Pointer to the configuration.
|
||||
* \param callback Pointer to the callback function.
|
||||
* \return Operation status of alarm time set.
|
||||
* \retval 0 Completed successfully.
|
||||
*/
|
||||
int32_t calendar_set_alarm(struct calendar_descriptor *const calendar, struct calendar_alarm *const alarm,
|
||||
calendar_cb_alarm_t callback);
|
||||
|
||||
/** \brief Retrieve the current driver version
|
||||
* \return Current driver version.
|
||||
*/
|
||||
uint32_t calendar_get_version(void);
|
||||
|
||||
/**@}*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _HAL_CALENDER_H_INCLUDED */
|
||||
|
||||
@@ -33,68 +33,10 @@
|
||||
#ifndef _HPL_CALENDER_H_INCLUDED
|
||||
#define _HPL_CALENDER_H_INCLUDED
|
||||
|
||||
#include <compiler.h>
|
||||
#include <utils_list.h>
|
||||
#include "hpl_irq.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* \brief Calendar structure
|
||||
*
|
||||
* The Calendar structure forward declaration.
|
||||
*/
|
||||
struct calendar_dev;
|
||||
|
||||
/**
|
||||
* \brief Available mask options for alarms.
|
||||
*
|
||||
* Available mask options for alarms.
|
||||
*/
|
||||
enum calendar_alarm_option {
|
||||
/** Alarm disabled. */
|
||||
CALENDAR_ALARM_MATCH_DISABLED = 0,
|
||||
/** Alarm match on second. */
|
||||
CALENDAR_ALARM_MATCH_SEC,
|
||||
/** Alarm match on second and minute. */
|
||||
CALENDAR_ALARM_MATCH_MIN,
|
||||
/** Alarm match on second, minute, and hour. */
|
||||
CALENDAR_ALARM_MATCH_HOUR,
|
||||
/** Alarm match on second, minute, hour, and day. */
|
||||
CALENDAR_ALARM_MATCH_DAY,
|
||||
/** Alarm match on second, minute, hour, day, and month. */
|
||||
CALENDAR_ALARM_MATCH_MONTH,
|
||||
/** Alarm match on second, minute, hour, day, month and year. */
|
||||
CALENDAR_ALARM_MATCH_YEAR
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Available mode for alarms.
|
||||
*/
|
||||
enum calendar_alarm_mode { ONESHOT = 1, REPEAT };
|
||||
/**
|
||||
* \brief Prototype of callback on alarm match
|
||||
*/
|
||||
typedef void (*calendar_drv_cb_t)();
|
||||
typedef void (*calendar_drv_extwake_cb_t)(uint8_t reason);
|
||||
|
||||
/**
|
||||
* \brief Structure of Calendar instance
|
||||
*/
|
||||
struct calendar_dev {
|
||||
/** Pointer to the hardware base */
|
||||
void *hw;
|
||||
/** Alarm match callback */
|
||||
calendar_drv_cb_t callback_alarm;
|
||||
/** Tamper callback */
|
||||
calendar_drv_extwake_cb_t callback_tamper;
|
||||
/** Tick callback */
|
||||
calendar_drv_cb_t callback_tick;
|
||||
/** IRQ struct */
|
||||
struct _irq_descriptor irq;
|
||||
};
|
||||
/**
|
||||
* \brief Time struct for calendar
|
||||
*/
|
||||
@@ -119,17 +61,6 @@ struct calendar_date {
|
||||
uint16_t year;
|
||||
};
|
||||
|
||||
/** \brief Calendar driver struct
|
||||
*
|
||||
*/
|
||||
struct calendar_descriptor {
|
||||
struct calendar_dev device;
|
||||
struct list_descriptor alarms;
|
||||
/*base date/time = base_year/1/1/0/0/0(year/month/day/hour/min/sec)*/
|
||||
uint32_t base_year;
|
||||
uint8_t flags;
|
||||
};
|
||||
|
||||
/** \brief Date&Time struct for calendar
|
||||
*/
|
||||
struct calendar_date_time {
|
||||
@@ -137,188 +68,6 @@ struct calendar_date_time {
|
||||
struct calendar_date date;
|
||||
};
|
||||
|
||||
/** \brief struct for alarm time
|
||||
*/
|
||||
struct _calendar_alarm {
|
||||
struct calendar_date_time datetime;
|
||||
uint32_t timestamp;
|
||||
enum calendar_alarm_option option;
|
||||
enum calendar_alarm_mode mode;
|
||||
};
|
||||
|
||||
/** \enum for tamper detection mode
|
||||
*/
|
||||
enum tamper_detection_mode { TAMPER_MODE_OFF = 0U, TAMPER_MODE_WAKE, TAMPER_MODE_CAPTURE, TAMPER_MODE_ACTL };
|
||||
|
||||
/** \enum for tamper detection mode
|
||||
*/
|
||||
enum tamper_id { TAMPID0 = 0U, TAMPID1, TAMPID2, TAMPID3, TAMPID4 };
|
||||
/**
|
||||
* \brief Initialize Calendar instance
|
||||
*
|
||||
* \param[in] dev The pointer to calendar device struct
|
||||
*
|
||||
* \return ERR_NONE on success, or an error code on failure.
|
||||
*/
|
||||
int32_t _calendar_init(struct calendar_dev *const dev);
|
||||
|
||||
/**
|
||||
* \brief Deinitialize Calendar instance
|
||||
*
|
||||
* \param[in] dev The pointer to calendar device struct
|
||||
*
|
||||
* \return ERR_NONE on success, or an error code on failure.
|
||||
*/
|
||||
int32_t _calendar_deinit(struct calendar_dev *const dev);
|
||||
|
||||
/**
|
||||
* \brief Enable Calendar instance
|
||||
*
|
||||
* \param[in] dev The pointer to calendar device struct
|
||||
*
|
||||
* \return ERR_NONE on success, or an error code on failure.
|
||||
*/
|
||||
int32_t _calendar_enable(struct calendar_dev *const dev);
|
||||
|
||||
/**
|
||||
* \brief Disable Calendar instance
|
||||
*
|
||||
* \param[in] dev The pointer to calendar device struct
|
||||
*
|
||||
* \return ERR_NONE on success, or an error code on failure.
|
||||
*/
|
||||
int32_t _calendar_disable(struct calendar_dev *const dev);
|
||||
/**
|
||||
* \brief Set counter for calendar
|
||||
*
|
||||
* \param[in] dev The pointer to calendar device struct
|
||||
* \param[in] counter The counter for set
|
||||
*
|
||||
* \return ERR_NONE on success, or an error code on failure.
|
||||
*/
|
||||
int32_t _calendar_set_counter(struct calendar_dev *const dev, const uint32_t counter);
|
||||
|
||||
/**
|
||||
* \brief Get counter for calendar
|
||||
*
|
||||
* \param[in] dev The pointer to calendar device struct
|
||||
*
|
||||
* \return return current counter value
|
||||
*/
|
||||
uint32_t _calendar_get_counter(struct calendar_dev *const dev);
|
||||
|
||||
/**
|
||||
* \brief Set compare value for calendar
|
||||
*
|
||||
* \param[in] dev The pointer to calendar device struct
|
||||
* \param[in] comp The compare value for set
|
||||
*
|
||||
* \return ERR_NONE on success, or an error code on failure.
|
||||
*/
|
||||
int32_t _calendar_set_comp(struct calendar_dev *const dev, const uint32_t comp);
|
||||
|
||||
/**
|
||||
* \brief Get compare value for calendar
|
||||
*
|
||||
* \param[in] dev The pointer to calendar device struct
|
||||
*
|
||||
* \return return current compare value
|
||||
*/
|
||||
uint32_t _calendar_get_comp(struct calendar_dev *const dev);
|
||||
|
||||
/**
|
||||
* \brief Register callback for calendar alarm
|
||||
*
|
||||
* \param[in] dev The pointer to calendar device struct
|
||||
* \param[in] callback The pointer to callback function
|
||||
*
|
||||
* \return ERR_NONE on success, or an error code on failure.
|
||||
*/
|
||||
int32_t _calendar_register_callback(struct calendar_dev *const dev, calendar_drv_cb_t callback);
|
||||
|
||||
/**
|
||||
* \brief Set calendar IRQ
|
||||
*
|
||||
* \param[in] dev The pointer to calendar device struct
|
||||
*/
|
||||
void _calendar_set_irq(struct calendar_dev *const dev);
|
||||
|
||||
/**
|
||||
* \brief Register callback for 1Hz tick from prescaler
|
||||
*
|
||||
* \param[in] dev The pointer to calendar device struct
|
||||
* \param[in] callback The pointer to callback function
|
||||
*
|
||||
* \return ERR_NONE on success, or an error code on failure.
|
||||
*/
|
||||
int32_t _prescaler_register_callback(struct calendar_dev *const dev, calendar_drv_cb_t callback);
|
||||
|
||||
/**
|
||||
* \brief Register callback for tamper detection
|
||||
*
|
||||
* \param[in] dev The pointer to calendar device struct
|
||||
* \param[in] callback The pointer to callback function
|
||||
*
|
||||
* \return ERR_NONE on success, or an error code on failure.
|
||||
*/
|
||||
int32_t _extwake_register_callback(struct calendar_dev *const dev, calendar_drv_extwake_cb_t callback);
|
||||
|
||||
/**
|
||||
* \brief Find tamper is detected on specified pin
|
||||
*
|
||||
* \param[in] dev The pointer to calendar device struct
|
||||
* \param[in] enum Tamper ID number
|
||||
*
|
||||
* \return true on detection success and false on failure.
|
||||
*/
|
||||
bool _is_tamper_detected(struct calendar_dev *const dev, enum tamper_id tamper_id_pin);
|
||||
|
||||
/**
|
||||
* \brief brief Clear the Tamper ID flag
|
||||
*
|
||||
* \param[in] dev The pointer to calendar device struct
|
||||
* \param[in] enum Tamper ID number
|
||||
*
|
||||
* \return ERR_NONE
|
||||
*/
|
||||
int32_t _tamper_clear_tampid_flag(struct calendar_dev *const dev, enum tamper_id tamper_id_pin);
|
||||
|
||||
/**
|
||||
* \brief Enable Debounce Asynchronous Feature
|
||||
*
|
||||
* \param[in] dev The pointer to calendar device struct
|
||||
*
|
||||
* \return ERR_NONE on success, or an error code on failure.
|
||||
*/
|
||||
int32_t _tamper_enable_debounce_asynchronous(struct calendar_dev *const dev);
|
||||
|
||||
/**
|
||||
* \brief Disable Tamper Debounce Asynchronous Feature
|
||||
*
|
||||
* \param[in] dev The pointer to calendar device struct
|
||||
*
|
||||
* \return ERR_NONE on success, or an error code on failure.
|
||||
*/
|
||||
int32_t _tamper_disable_debounce_asynchronous(struct calendar_dev *const dev);
|
||||
|
||||
/**
|
||||
* \brief Enable Tamper Debounce Majority Feature
|
||||
*
|
||||
* \param[in] dev The pointer to calendar device struct
|
||||
*
|
||||
* \return ERR_NONE on success, or an error code on failure.
|
||||
*/
|
||||
int32_t _tamper_enable_debounce_majority(struct calendar_dev *const dev);
|
||||
|
||||
/**
|
||||
* \brief Enable Tamper Debounce Majority Feature
|
||||
*
|
||||
* \param[in] dev The pointer to calendar device struct
|
||||
*
|
||||
* \return ERR_NONE on success, or an error code on failure.
|
||||
*/
|
||||
int32_t _tamper_disable_debounce_majority(struct calendar_dev *const dev);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1,645 +0,0 @@
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* \brief Generic CALENDAR functionality implementation.
|
||||
*
|
||||
* Copyright (c) 2014-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
|
||||
*
|
||||
*/
|
||||
|
||||
#include "hal_calendar.h"
|
||||
#include <utils.h>
|
||||
#include <utils_assert.h>
|
||||
#include <hal_atomic.h>
|
||||
|
||||
#define CALENDAR_VERSION 0x00000001u
|
||||
#define SECS_IN_LEAP_YEAR 31622400
|
||||
#define SECS_IN_NON_LEAP_YEAR 31536000
|
||||
#define SECS_IN_31DAYS 2678400
|
||||
#define SECS_IN_30DAYS 2592000
|
||||
#define SECS_IN_29DAYS 2505600
|
||||
#define SECS_IN_28DAYS 2419200
|
||||
#define SECS_IN_DAY 86400
|
||||
#define SECS_IN_HOUR 3600
|
||||
#define SECS_IN_MINUTE 60
|
||||
#define DEFAULT_BASE_YEAR 1970
|
||||
|
||||
#define SET_ALARM_BUSY 1
|
||||
#define PROCESS_ALARM_BUSY 2
|
||||
|
||||
/** \brief leap year check
|
||||
* \retval false not leap year.
|
||||
* \retval true leap year.
|
||||
*/
|
||||
static bool leap_year(uint16_t year)
|
||||
{
|
||||
if (year & 3) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/** \brief calculate the seconds in specified year/month
|
||||
* \retval 0 month error.
|
||||
*/
|
||||
static uint32_t get_secs_in_month(uint32_t year, uint8_t month)
|
||||
{
|
||||
uint32_t sec_in_month = 0;
|
||||
|
||||
if (leap_year(year)) {
|
||||
switch (month) {
|
||||
case 1:
|
||||
case 3:
|
||||
case 5:
|
||||
case 7:
|
||||
case 8:
|
||||
case 10:
|
||||
case 12:
|
||||
sec_in_month = SECS_IN_31DAYS;
|
||||
break;
|
||||
case 2:
|
||||
sec_in_month = SECS_IN_29DAYS;
|
||||
break;
|
||||
case 4:
|
||||
case 6:
|
||||
case 9:
|
||||
case 11:
|
||||
sec_in_month = SECS_IN_30DAYS;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
switch (month) {
|
||||
case 1:
|
||||
case 3:
|
||||
case 5:
|
||||
case 7:
|
||||
case 8:
|
||||
case 10:
|
||||
case 12:
|
||||
sec_in_month = SECS_IN_31DAYS;
|
||||
break;
|
||||
case 2:
|
||||
sec_in_month = SECS_IN_28DAYS;
|
||||
break;
|
||||
case 4:
|
||||
case 6:
|
||||
case 9:
|
||||
case 11:
|
||||
sec_in_month = SECS_IN_30DAYS;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return sec_in_month;
|
||||
}
|
||||
|
||||
/** \brief convert timestamp to date/time
|
||||
*/
|
||||
static int32_t convert_timestamp_to_datetime(struct calendar_descriptor *const calendar, uint32_t ts,
|
||||
struct calendar_date_time *dt)
|
||||
{
|
||||
uint32_t tmp, sec_in_year, sec_in_month;
|
||||
uint32_t tmp_year = calendar->base_year;
|
||||
uint8_t tmp_month = 1;
|
||||
uint8_t tmp_day = 1;
|
||||
uint8_t tmp_hour = 0;
|
||||
uint8_t tmp_minutes = 0;
|
||||
|
||||
tmp = ts;
|
||||
|
||||
/* Find year */
|
||||
while (true) {
|
||||
sec_in_year = leap_year(tmp_year) ? SECS_IN_LEAP_YEAR : SECS_IN_NON_LEAP_YEAR;
|
||||
|
||||
if (tmp >= sec_in_year) {
|
||||
tmp -= sec_in_year;
|
||||
tmp_year++;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* Find month of year */
|
||||
while (true) {
|
||||
sec_in_month = get_secs_in_month(tmp_year, tmp_month);
|
||||
|
||||
if (tmp >= sec_in_month) {
|
||||
tmp -= sec_in_month;
|
||||
tmp_month++;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* Find day of month */
|
||||
while (true) {
|
||||
if (tmp >= SECS_IN_DAY) {
|
||||
tmp -= SECS_IN_DAY;
|
||||
tmp_day++;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* Find hour of day */
|
||||
while (true) {
|
||||
if (tmp >= SECS_IN_HOUR) {
|
||||
tmp -= SECS_IN_HOUR;
|
||||
tmp_hour++;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* Find minute in hour */
|
||||
while (true) {
|
||||
if (tmp >= SECS_IN_MINUTE) {
|
||||
tmp -= SECS_IN_MINUTE;
|
||||
tmp_minutes++;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
dt->date.year = tmp_year;
|
||||
dt->date.month = tmp_month;
|
||||
dt->date.day = tmp_day;
|
||||
dt->time.hour = tmp_hour;
|
||||
dt->time.min = tmp_minutes;
|
||||
dt->time.sec = tmp;
|
||||
|
||||
return ERR_NONE;
|
||||
}
|
||||
|
||||
/** \brief convert date/time to timestamp
|
||||
* \return timestamp
|
||||
*/
|
||||
static uint32_t convert_datetime_to_timestamp(struct calendar_descriptor *const calendar, struct calendar_date_time *dt)
|
||||
{
|
||||
uint32_t tmp = 0;
|
||||
uint32_t i = 0;
|
||||
uint8_t year, month, day, hour, minutes, seconds;
|
||||
|
||||
year = dt->date.year - calendar->base_year;
|
||||
month = dt->date.month;
|
||||
day = dt->date.day;
|
||||
hour = dt->time.hour;
|
||||
minutes = dt->time.min;
|
||||
seconds = dt->time.sec;
|
||||
|
||||
/* tot up year field */
|
||||
for (i = 0; i < year; ++i) {
|
||||
if (leap_year(calendar->base_year + i)) {
|
||||
tmp += SECS_IN_LEAP_YEAR;
|
||||
} else {
|
||||
tmp += SECS_IN_NON_LEAP_YEAR;
|
||||
}
|
||||
}
|
||||
|
||||
/* tot up month field */
|
||||
for (i = 1; i < month; ++i) {
|
||||
tmp += get_secs_in_month(dt->date.year, i);
|
||||
}
|
||||
|
||||
/* tot up day/hour/minute/second fields */
|
||||
tmp += (day - 1) * SECS_IN_DAY;
|
||||
tmp += hour * SECS_IN_HOUR;
|
||||
tmp += minutes * SECS_IN_MINUTE;
|
||||
tmp += seconds;
|
||||
|
||||
return tmp;
|
||||
}
|
||||
|
||||
/** \brief calibrate timestamp to make desired timestamp ahead of current timestamp
|
||||
*/
|
||||
static void calibrate_timestamp(struct calendar_descriptor *const calendar, struct calendar_alarm *alarm,
|
||||
struct calendar_alarm *current_dt)
|
||||
{
|
||||
uint32_t alarm_ts;
|
||||
uint32_t current_ts = current_dt->cal_alarm.timestamp;
|
||||
|
||||
(void)calendar;
|
||||
|
||||
alarm_ts = alarm->cal_alarm.timestamp;
|
||||
|
||||
/* calibrate timestamp */
|
||||
switch (alarm->cal_alarm.option) {
|
||||
case CALENDAR_ALARM_MATCH_SEC:
|
||||
|
||||
if (alarm_ts <= current_ts) {
|
||||
alarm_ts += SECS_IN_MINUTE;
|
||||
}
|
||||
|
||||
break;
|
||||
case CALENDAR_ALARM_MATCH_MIN:
|
||||
|
||||
if (alarm_ts <= current_ts) {
|
||||
alarm_ts += SECS_IN_HOUR;
|
||||
}
|
||||
|
||||
break;
|
||||
case CALENDAR_ALARM_MATCH_HOUR:
|
||||
|
||||
if (alarm_ts <= current_ts) {
|
||||
alarm_ts += SECS_IN_DAY;
|
||||
}
|
||||
|
||||
break;
|
||||
case CALENDAR_ALARM_MATCH_DAY:
|
||||
|
||||
if (alarm_ts <= current_ts) {
|
||||
alarm_ts += get_secs_in_month(current_dt->cal_alarm.datetime.date.year,
|
||||
current_dt->cal_alarm.datetime.date.month);
|
||||
}
|
||||
|
||||
break;
|
||||
case CALENDAR_ALARM_MATCH_MONTH:
|
||||
|
||||
if (alarm_ts <= current_ts) {
|
||||
if (leap_year(current_dt->cal_alarm.datetime.date.year)) {
|
||||
alarm_ts += SECS_IN_LEAP_YEAR;
|
||||
} else {
|
||||
alarm_ts += SECS_IN_NON_LEAP_YEAR;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
/* do nothing for year match */
|
||||
case CALENDAR_ALARM_MATCH_YEAR:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/* desired timestamp after calibration */
|
||||
alarm->cal_alarm.timestamp = alarm_ts;
|
||||
}
|
||||
|
||||
/** \brief complete alarm to absolute date/time, then fill up the timestamp
|
||||
*/
|
||||
static void fill_alarm(struct calendar_descriptor *const calendar, struct calendar_alarm *alarm)
|
||||
{
|
||||
struct calendar_alarm current_dt;
|
||||
uint32_t tmp, current_ts;
|
||||
|
||||
/* get current date/time */
|
||||
current_ts = _calendar_get_counter(&calendar->device);
|
||||
convert_timestamp_to_datetime(calendar, current_ts, ¤t_dt.cal_alarm.datetime);
|
||||
|
||||
current_dt.cal_alarm.timestamp = current_ts;
|
||||
|
||||
/* complete alarm */
|
||||
switch (alarm->cal_alarm.option) {
|
||||
case CALENDAR_ALARM_MATCH_SEC:
|
||||
alarm->cal_alarm.datetime.date.year = current_dt.cal_alarm.datetime.date.year;
|
||||
alarm->cal_alarm.datetime.date.month = current_dt.cal_alarm.datetime.date.month;
|
||||
alarm->cal_alarm.datetime.date.day = current_dt.cal_alarm.datetime.date.day;
|
||||
alarm->cal_alarm.datetime.time.hour = current_dt.cal_alarm.datetime.time.hour;
|
||||
alarm->cal_alarm.datetime.time.min = current_dt.cal_alarm.datetime.time.min;
|
||||
break;
|
||||
case CALENDAR_ALARM_MATCH_MIN:
|
||||
alarm->cal_alarm.datetime.date.year = current_dt.cal_alarm.datetime.date.year;
|
||||
alarm->cal_alarm.datetime.date.month = current_dt.cal_alarm.datetime.date.month;
|
||||
alarm->cal_alarm.datetime.date.day = current_dt.cal_alarm.datetime.date.day;
|
||||
alarm->cal_alarm.datetime.time.hour = current_dt.cal_alarm.datetime.time.hour;
|
||||
break;
|
||||
case CALENDAR_ALARM_MATCH_HOUR:
|
||||
alarm->cal_alarm.datetime.date.year = current_dt.cal_alarm.datetime.date.year;
|
||||
alarm->cal_alarm.datetime.date.month = current_dt.cal_alarm.datetime.date.month;
|
||||
alarm->cal_alarm.datetime.date.day = current_dt.cal_alarm.datetime.date.day;
|
||||
break;
|
||||
case CALENDAR_ALARM_MATCH_DAY:
|
||||
alarm->cal_alarm.datetime.date.year = current_dt.cal_alarm.datetime.date.year;
|
||||
alarm->cal_alarm.datetime.date.month = current_dt.cal_alarm.datetime.date.month;
|
||||
break;
|
||||
case CALENDAR_ALARM_MATCH_MONTH:
|
||||
alarm->cal_alarm.datetime.date.year = current_dt.cal_alarm.datetime.date.year;
|
||||
break;
|
||||
case CALENDAR_ALARM_MATCH_YEAR:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/* fill up the timestamp */
|
||||
tmp = convert_datetime_to_timestamp(calendar, &alarm->cal_alarm.datetime);
|
||||
alarm->cal_alarm.timestamp = tmp;
|
||||
|
||||
/* calibrate the timestamp */
|
||||
calibrate_timestamp(calendar, alarm, ¤t_dt);
|
||||
convert_timestamp_to_datetime(calendar, alarm->cal_alarm.timestamp, &alarm->cal_alarm.datetime);
|
||||
}
|
||||
|
||||
/** \brief add new alarm into the list in ascending order
|
||||
*/
|
||||
static int32_t calendar_add_new_alarm(struct list_descriptor *list, struct calendar_alarm *alarm)
|
||||
{
|
||||
struct calendar_descriptor *calendar = CONTAINER_OF(list, struct calendar_descriptor, alarms);
|
||||
struct calendar_alarm * head, *it, *prev = NULL;
|
||||
|
||||
/*get the head of alarms list*/
|
||||
head = (struct calendar_alarm *)list_get_head(list);
|
||||
|
||||
/*if head is null, insert new alarm as head*/
|
||||
if (!head) {
|
||||
list_insert_as_head(list, alarm);
|
||||
_calendar_set_comp(&calendar->device, alarm->cal_alarm.timestamp);
|
||||
return ERR_NONE;
|
||||
}
|
||||
|
||||
/*insert the new alarm in accending order, the head will be invoked firstly */
|
||||
for (it = head; it; it = (struct calendar_alarm *)list_get_next_element(it)) {
|
||||
if (alarm->cal_alarm.timestamp <= it->cal_alarm.timestamp) {
|
||||
break;
|
||||
}
|
||||
|
||||
prev = it;
|
||||
}
|
||||
|
||||
/*insert new alarm into the list */
|
||||
if (it == head) {
|
||||
list_insert_as_head(list, alarm);
|
||||
/*get the head and set it into register*/
|
||||
_calendar_set_comp(&calendar->device, alarm->cal_alarm.timestamp);
|
||||
|
||||
} else {
|
||||
list_insert_after(prev, alarm);
|
||||
}
|
||||
|
||||
return ERR_NONE;
|
||||
}
|
||||
|
||||
/** \brief callback for alarm
|
||||
*/
|
||||
static void calendar_alarm(struct calendar_dev *const dev)
|
||||
{
|
||||
struct calendar_descriptor *calendar = CONTAINER_OF(dev, struct calendar_descriptor, device);
|
||||
|
||||
struct calendar_alarm *head, *it, current_dt;
|
||||
|
||||
if ((calendar->flags & SET_ALARM_BUSY) || (calendar->flags & PROCESS_ALARM_BUSY)) {
|
||||
calendar->flags |= PROCESS_ALARM_BUSY;
|
||||
return;
|
||||
}
|
||||
|
||||
/* get current timestamp */
|
||||
current_dt.cal_alarm.timestamp = _calendar_get_counter(dev);
|
||||
|
||||
/* get the head */
|
||||
head = (struct calendar_alarm *)list_get_head(&calendar->alarms);
|
||||
ASSERT(head);
|
||||
|
||||
/* remove all alarms and invoke them*/
|
||||
for (it = head; it; it = (struct calendar_alarm *)list_get_head(&calendar->alarms)) {
|
||||
/* check the timestamp with current timestamp*/
|
||||
if (it->cal_alarm.timestamp <= current_dt.cal_alarm.timestamp) {
|
||||
list_remove_head(&calendar->alarms);
|
||||
it->callback(calendar);
|
||||
|
||||
if (it->cal_alarm.mode == REPEAT) {
|
||||
calibrate_timestamp(calendar, it, ¤t_dt);
|
||||
convert_timestamp_to_datetime(calendar, it->cal_alarm.timestamp, &it->cal_alarm.datetime);
|
||||
calendar_add_new_alarm(&calendar->alarms, it);
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*if no alarm in the list, register null */
|
||||
if (!it) {
|
||||
_calendar_register_callback(&calendar->device, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
/*put the new head into register */
|
||||
_calendar_set_comp(&calendar->device, it->cal_alarm.timestamp);
|
||||
}
|
||||
|
||||
/** \brief Initialize Calendar
|
||||
*/
|
||||
int32_t calendar_init(struct calendar_descriptor *const calendar, const void *hw)
|
||||
{
|
||||
int32_t ret = 0;
|
||||
|
||||
/* Sanity check arguments */
|
||||
ASSERT(calendar);
|
||||
|
||||
if (calendar->device.hw == hw) {
|
||||
/* Already initialized with current configuration */
|
||||
return ERR_NONE;
|
||||
} else if (calendar->device.hw != NULL) {
|
||||
/* Initialized with another configuration */
|
||||
return ERR_ALREADY_INITIALIZED;
|
||||
}
|
||||
calendar->device.hw = (void *)hw;
|
||||
ret = _calendar_init(&calendar->device);
|
||||
calendar->base_year = DEFAULT_BASE_YEAR;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** \brief Reset the Calendar
|
||||
*/
|
||||
int32_t calendar_deinit(struct calendar_descriptor *const calendar)
|
||||
{
|
||||
/* Sanity check arguments */
|
||||
ASSERT(calendar);
|
||||
|
||||
if (calendar->device.hw == NULL) {
|
||||
return ERR_NOT_INITIALIZED;
|
||||
}
|
||||
_calendar_deinit(&calendar->device);
|
||||
calendar->device.hw = NULL;
|
||||
|
||||
return ERR_NONE;
|
||||
}
|
||||
|
||||
/** \brief Enable the Calendar
|
||||
*/
|
||||
int32_t calendar_enable(struct calendar_descriptor *const calendar)
|
||||
{
|
||||
/* Sanity check arguments */
|
||||
ASSERT(calendar);
|
||||
|
||||
_calendar_enable(&calendar->device);
|
||||
|
||||
return ERR_NONE;
|
||||
}
|
||||
|
||||
/** \brief Disable the Calendar
|
||||
*/
|
||||
int32_t calendar_disable(struct calendar_descriptor *const calendar)
|
||||
{
|
||||
/* Sanity check arguments */
|
||||
ASSERT(calendar);
|
||||
|
||||
_calendar_disable(&calendar->device);
|
||||
|
||||
return ERR_NONE;
|
||||
}
|
||||
|
||||
/** \brief Set base year for calendar
|
||||
*/
|
||||
int32_t calendar_set_baseyear(struct calendar_descriptor *const calendar, const uint32_t p_base_year)
|
||||
{
|
||||
/* Sanity check arguments */
|
||||
ASSERT(calendar);
|
||||
|
||||
calendar->base_year = p_base_year;
|
||||
|
||||
return ERR_NONE;
|
||||
}
|
||||
|
||||
/** \brief Set time for calendar
|
||||
*/
|
||||
int32_t calendar_set_time(struct calendar_descriptor *const calendar, struct calendar_time *const p_calendar_time)
|
||||
{
|
||||
struct calendar_date_time dt;
|
||||
uint32_t current_ts, new_ts;
|
||||
|
||||
/* Sanity check arguments */
|
||||
ASSERT(calendar);
|
||||
|
||||
/* convert time to timestamp */
|
||||
current_ts = _calendar_get_counter(&calendar->device);
|
||||
convert_timestamp_to_datetime(calendar, current_ts, &dt);
|
||||
dt.time.sec = p_calendar_time->sec;
|
||||
dt.time.min = p_calendar_time->min;
|
||||
dt.time.hour = p_calendar_time->hour;
|
||||
|
||||
new_ts = convert_datetime_to_timestamp(calendar, &dt);
|
||||
|
||||
_calendar_set_counter(&calendar->device, new_ts);
|
||||
|
||||
return ERR_NONE;
|
||||
}
|
||||
|
||||
/** \brief Set date for calendar
|
||||
*/
|
||||
int32_t calendar_set_date(struct calendar_descriptor *const calendar, struct calendar_date *const p_calendar_date)
|
||||
{
|
||||
struct calendar_date_time dt;
|
||||
uint32_t current_ts, new_ts;
|
||||
|
||||
/* Sanity check arguments */
|
||||
ASSERT(calendar);
|
||||
|
||||
/* convert date to timestamp */
|
||||
current_ts = _calendar_get_counter(&calendar->device);
|
||||
convert_timestamp_to_datetime(calendar, current_ts, &dt);
|
||||
dt.date.day = p_calendar_date->day;
|
||||
dt.date.month = p_calendar_date->month;
|
||||
dt.date.year = p_calendar_date->year;
|
||||
|
||||
new_ts = convert_datetime_to_timestamp(calendar, &dt);
|
||||
|
||||
_calendar_set_counter(&calendar->device, new_ts);
|
||||
|
||||
return ERR_NONE;
|
||||
}
|
||||
|
||||
/** \brief Get date/time for calendar
|
||||
*/
|
||||
int32_t calendar_get_date_time(struct calendar_descriptor *const calendar, struct calendar_date_time *const date_time)
|
||||
{
|
||||
uint32_t current_ts;
|
||||
|
||||
/* Sanity check arguments */
|
||||
ASSERT(calendar);
|
||||
|
||||
/* convert current timestamp to date/time */
|
||||
current_ts = _calendar_get_counter(&calendar->device);
|
||||
convert_timestamp_to_datetime(calendar, current_ts, date_time);
|
||||
|
||||
return ERR_NONE;
|
||||
}
|
||||
|
||||
/** \brief Set alarm for calendar
|
||||
*/
|
||||
int32_t calendar_set_alarm(struct calendar_descriptor *const calendar, struct calendar_alarm *const alarm,
|
||||
calendar_cb_alarm_t callback)
|
||||
{
|
||||
struct calendar_alarm *head;
|
||||
|
||||
/* Sanity check arguments */
|
||||
ASSERT(calendar);
|
||||
ASSERT(alarm);
|
||||
|
||||
alarm->callback = callback;
|
||||
|
||||
fill_alarm(calendar, alarm);
|
||||
|
||||
calendar->flags |= SET_ALARM_BUSY;
|
||||
|
||||
head = (struct calendar_alarm *)list_get_head(&calendar->alarms);
|
||||
|
||||
if (head != NULL) {
|
||||
/* already added */
|
||||
if (is_list_element(&calendar->alarms, alarm)) {
|
||||
if (callback == NULL) {
|
||||
/* remove alarm */
|
||||
list_delete_element(&calendar->alarms, alarm);
|
||||
|
||||
if (!list_get_head(&calendar->alarms)) {
|
||||
_calendar_register_callback(&calendar->device, NULL);
|
||||
}
|
||||
} else {
|
||||
/* re-add */
|
||||
list_delete_element(&calendar->alarms, alarm);
|
||||
calendar_add_new_alarm(&calendar->alarms, alarm);
|
||||
}
|
||||
} else if (callback != NULL) {
|
||||
calendar_add_new_alarm(&calendar->alarms, alarm);
|
||||
}
|
||||
|
||||
calendar->flags &= ~SET_ALARM_BUSY;
|
||||
|
||||
if (calendar->flags & PROCESS_ALARM_BUSY) {
|
||||
CRITICAL_SECTION_ENTER()
|
||||
calendar->flags &= ~PROCESS_ALARM_BUSY;
|
||||
_calendar_set_irq(&calendar->device);
|
||||
CRITICAL_SECTION_LEAVE()
|
||||
}
|
||||
} else if (callback != NULL) {
|
||||
/* if head is NULL, Register callback*/
|
||||
_calendar_register_callback(&calendar->device, calendar_alarm);
|
||||
calendar_add_new_alarm(&calendar->alarms, alarm);
|
||||
}
|
||||
|
||||
calendar->flags &= ~SET_ALARM_BUSY;
|
||||
|
||||
return ERR_NONE;
|
||||
}
|
||||
|
||||
/** \brief Retrieve driver version
|
||||
* \return Current driver version
|
||||
*/
|
||||
uint32_t calendar_get_version(void)
|
||||
{
|
||||
return CALENDAR_VERSION;
|
||||
}
|
||||
|
||||
@@ -1,430 +0,0 @@
|
||||
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* \brief RTC Driver
|
||||
*
|
||||
* Copyright (c) 2014-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
|
||||
*
|
||||
*/
|
||||
|
||||
#include <hpl_calendar.h>
|
||||
#include <utils_assert.h>
|
||||
#include <hpl_rtc_config.h>
|
||||
|
||||
/*!< Pointer to hpl device */
|
||||
static struct calendar_dev *_rtc_dev = NULL;
|
||||
|
||||
/**
|
||||
* \brief Initializes the RTC module with given configurations.
|
||||
*/
|
||||
int32_t _calendar_init(struct calendar_dev *const dev)
|
||||
{
|
||||
ASSERT(dev && dev->hw);
|
||||
|
||||
_rtc_dev = dev;
|
||||
|
||||
if (hri_rtcmode0_get_CTRLA_ENABLE_bit(dev->hw)) {
|
||||
#if !CONF_RTC_INIT_RESET
|
||||
return ERR_DENIED;
|
||||
#else
|
||||
hri_rtcmode0_clear_CTRLA_ENABLE_bit(dev->hw);
|
||||
hri_rtcmode0_wait_for_sync(dev->hw, RTC_MODE0_SYNCBUSY_ENABLE);
|
||||
#endif
|
||||
}
|
||||
hri_rtcmode0_set_CTRLA_SWRST_bit(dev->hw);
|
||||
hri_rtcmode0_wait_for_sync(dev->hw, RTC_MODE0_SYNCBUSY_SWRST);
|
||||
|
||||
#if CONF_RTC_EVENT_CONTROL_ENABLE == 1
|
||||
hri_rtcmode0_write_EVCTRL_reg(
|
||||
dev->hw,
|
||||
(CONF_RTC_PEREO0 << RTC_MODE0_EVCTRL_PEREO0_Pos) | (CONF_RTC_PEREO1 << RTC_MODE0_EVCTRL_PEREO1_Pos)
|
||||
| (CONF_RTC_PEREO2 << RTC_MODE0_EVCTRL_PEREO2_Pos) | (CONF_RTC_PEREO3 << RTC_MODE0_EVCTRL_PEREO3_Pos)
|
||||
| (CONF_RTC_PEREO4 << RTC_MODE0_EVCTRL_PEREO4_Pos) | (CONF_RTC_PEREO5 << RTC_MODE0_EVCTRL_PEREO5_Pos)
|
||||
| (CONF_RTC_PEREO6 << RTC_MODE0_EVCTRL_PEREO6_Pos) | (CONF_RTC_PEREO7 << RTC_MODE0_EVCTRL_PEREO7_Pos)
|
||||
| (CONF_RTC_COMPE0 << RTC_MODE0_EVCTRL_CMPEO_Pos) | (CONF_RTC_OVFEO << RTC_MODE0_EVCTRL_OVFEO_Pos));
|
||||
#endif
|
||||
|
||||
hri_rtcmode0_write_CTRLA_reg(dev->hw, RTC_MODE0_CTRLA_PRESCALER(CONF_RTC_PRESCALER) | RTC_MODE0_CTRLA_COUNTSYNC);
|
||||
|
||||
hri_rtc_write_TAMPCTRL_reg(
|
||||
dev->hw,
|
||||
(CONF_RTC_TAMPER_INACT_0 << RTC_TAMPCTRL_IN0ACT_Pos) | (CONF_RTC_TAMPER_INACT_1 << RTC_TAMPCTRL_IN1ACT_Pos)
|
||||
| (CONF_RTC_TAMPER_INACT_2 << RTC_TAMPCTRL_IN2ACT_Pos)
|
||||
| (CONF_RTC_TAMPER_INACT_3 << RTC_TAMPCTRL_IN3ACT_Pos)
|
||||
| (CONF_RTC_TAMPER_INACT_4 << RTC_TAMPCTRL_IN4ACT_Pos) | (CONF_RTC_TAMP_LVL_0 << RTC_TAMPCTRL_TAMLVL0_Pos)
|
||||
| (CONF_RTC_TAMP_LVL_1 << RTC_TAMPCTRL_TAMLVL1_Pos) | (CONF_RTC_TAMP_LVL_2 << RTC_TAMPCTRL_TAMLVL2_Pos)
|
||||
| (CONF_RTC_TAMP_LVL_3 << RTC_TAMPCTRL_TAMLVL3_Pos) | (CONF_RTC_TAMP_LVL_4 << RTC_TAMPCTRL_TAMLVL4_Pos)
|
||||
| (CONF_RTC_TAMP_DEBNC_0 << RTC_TAMPCTRL_DEBNC0_Pos) | (CONF_RTC_TAMP_DEBNC_1 << RTC_TAMPCTRL_DEBNC1_Pos)
|
||||
| (CONF_RTC_TAMP_DEBNC_2 << RTC_TAMPCTRL_DEBNC2_Pos) | (CONF_RTC_TAMP_DEBNC_3 << RTC_TAMPCTRL_DEBNC3_Pos)
|
||||
| (CONF_RTC_TAMP_DEBNC_4 << RTC_TAMPCTRL_DEBNC4_Pos));
|
||||
|
||||
if ((CONF_RTC_TAMPER_INACT_0 == TAMPER_MODE_ACTL) | (CONF_RTC_TAMPER_INACT_1 == TAMPER_MODE_ACTL)
|
||||
| (CONF_RTC_TAMPER_INACT_2 == TAMPER_MODE_ACTL) | (CONF_RTC_TAMPER_INACT_3 == TAMPER_MODE_ACTL)
|
||||
| (CONF_RTC_TAMPER_INACT_4 == TAMPER_MODE_ACTL)) {
|
||||
hri_rtcmode0_set_CTRLB_RTCOUT_bit(dev->hw);
|
||||
}
|
||||
return ERR_NONE;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Deinit the RTC module
|
||||
*/
|
||||
int32_t _calendar_deinit(struct calendar_dev *const dev)
|
||||
{
|
||||
ASSERT(dev && dev->hw);
|
||||
|
||||
NVIC_DisableIRQ(RTC_IRQn);
|
||||
dev->callback_alarm = NULL;
|
||||
dev->callback_tick = NULL;
|
||||
dev->callback_tamper = NULL;
|
||||
|
||||
hri_rtcmode0_clear_CTRLA_ENABLE_bit(dev->hw);
|
||||
hri_rtcmode0_set_CTRLA_SWRST_bit(dev->hw);
|
||||
|
||||
return ERR_NONE;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Enable the RTC module
|
||||
*/
|
||||
int32_t _calendar_enable(struct calendar_dev *const dev)
|
||||
{
|
||||
ASSERT(dev && dev->hw);
|
||||
|
||||
hri_rtcmode0_set_CTRLA_ENABLE_bit(dev->hw);
|
||||
|
||||
return ERR_NONE;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Disable the RTC module
|
||||
*/
|
||||
int32_t _calendar_disable(struct calendar_dev *const dev)
|
||||
{
|
||||
ASSERT(dev && dev->hw);
|
||||
|
||||
hri_rtcmode0_clear_CTRLA_ENABLE_bit(dev->hw);
|
||||
|
||||
return ERR_NONE;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Set the current calendar time to desired time.
|
||||
*/
|
||||
int32_t _calendar_set_counter(struct calendar_dev *const dev, const uint32_t counter)
|
||||
{
|
||||
ASSERT(dev && dev->hw);
|
||||
|
||||
hri_rtcmode0_write_COUNT_reg(dev->hw, counter);
|
||||
|
||||
return ERR_NONE;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Get current counter
|
||||
*/
|
||||
uint32_t _calendar_get_counter(struct calendar_dev *const dev)
|
||||
{
|
||||
ASSERT(dev && dev->hw);
|
||||
|
||||
return hri_rtcmode0_read_COUNT_reg(dev->hw);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Set the compare for the specified value.
|
||||
*/
|
||||
int32_t _calendar_set_comp(struct calendar_dev *const dev, const uint32_t comp)
|
||||
{
|
||||
ASSERT(dev && dev->hw);
|
||||
|
||||
hri_rtcmode0_write_COMP_reg(dev->hw, 0, comp);
|
||||
|
||||
return ERR_NONE;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Get the compare value
|
||||
*/
|
||||
uint32_t _calendar_get_comp(struct calendar_dev *const dev)
|
||||
{
|
||||
ASSERT(dev && dev->hw);
|
||||
|
||||
return hri_rtcmode0_read_COMP_reg(dev->hw, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Find tamper is detected on specified pin
|
||||
*/
|
||||
bool _is_tamper_detected(struct calendar_dev *const dev, enum tamper_id tamper_id_pin)
|
||||
{
|
||||
bool value;
|
||||
|
||||
ASSERT(dev && dev->hw);
|
||||
|
||||
value = ((hri_rtc_read_TAMPID_reg(dev->hw) >> tamper_id_pin) & 0x01);
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Clear the Tamper ID flag
|
||||
*/
|
||||
int32_t _tamper_clear_tampid_flag(struct calendar_dev *const dev, enum tamper_id tamper_id_pin)
|
||||
{
|
||||
ASSERT(dev && dev->hw);
|
||||
|
||||
hri_rtc_write_TAMPID_reg(dev->hw, (true << tamper_id_pin));
|
||||
|
||||
return ERR_NONE;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Enable Tamper Debounce Asynchronous Feature
|
||||
*/
|
||||
int32_t _tamper_enable_debounce_asynchronous(struct calendar_dev *const dev)
|
||||
{
|
||||
int32_t return_value;
|
||||
|
||||
hri_rtcmode0_write_CTRLA_ENABLE_bit(dev->hw, false);
|
||||
|
||||
while (hri_rtcmode0_read_SYNCBUSY_reg(dev->hw) & RTC_MODE2_CTRLA_ENABLE) {
|
||||
}
|
||||
|
||||
if (hri_rtcmode0_read_CTRLA_reg(dev->hw) & RTC_MODE2_CTRLA_ENABLE) {
|
||||
return_value = ERR_FAILURE;
|
||||
} else {
|
||||
hri_rtcmode0_write_CTRLB_DEBASYNC_bit(dev->hw, true);
|
||||
return_value = ERR_NONE;
|
||||
while (hri_rtcmode0_read_SYNCBUSY_reg(dev->hw) & RTC_MODE2_CTRLA_ENABLE) {
|
||||
}
|
||||
hri_rtcmode0_write_CTRLA_ENABLE_bit(dev->hw, true);
|
||||
}
|
||||
|
||||
return return_value;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Disable Tamper Debounce Asynchronous Feature
|
||||
*/
|
||||
int32_t _tamper_disable_debounce_asynchronous(struct calendar_dev *const dev)
|
||||
{
|
||||
int32_t return_value;
|
||||
|
||||
hri_rtcmode0_write_CTRLA_ENABLE_bit(dev->hw, false);
|
||||
|
||||
while (hri_rtcmode0_read_SYNCBUSY_reg(dev->hw) & RTC_MODE2_CTRLA_ENABLE) {
|
||||
}
|
||||
|
||||
if (hri_rtcmode0_read_CTRLA_reg(dev->hw) & RTC_MODE2_CTRLA_ENABLE) {
|
||||
return_value = ERR_FAILURE;
|
||||
} else {
|
||||
hri_rtcmode0_write_CTRLB_DEBASYNC_bit(dev->hw, false);
|
||||
return_value = ERR_NONE;
|
||||
while (hri_rtcmode0_read_SYNCBUSY_reg(dev->hw) & RTC_MODE2_CTRLA_ENABLE) {
|
||||
}
|
||||
hri_rtcmode0_write_CTRLA_ENABLE_bit(dev->hw, true);
|
||||
}
|
||||
|
||||
return return_value;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Enable Tamper Debounce Majority Feature
|
||||
*/
|
||||
int32_t _tamper_enable_debounce_majority(struct calendar_dev *const dev)
|
||||
{
|
||||
int32_t return_value;
|
||||
|
||||
hri_rtcmode0_write_CTRLA_ENABLE_bit(dev->hw, false);
|
||||
|
||||
while (hri_rtcmode0_read_SYNCBUSY_reg(dev->hw) & RTC_MODE2_CTRLA_ENABLE) {
|
||||
}
|
||||
|
||||
if (hri_rtcmode0_read_CTRLA_reg(dev->hw) & RTC_MODE2_CTRLA_ENABLE) {
|
||||
return_value = ERR_FAILURE;
|
||||
} else {
|
||||
hri_rtcmode0_write_CTRLB_DEBMAJ_bit(dev->hw, true);
|
||||
return_value = ERR_NONE;
|
||||
|
||||
while (hri_rtcmode0_read_SYNCBUSY_reg(dev->hw) & RTC_MODE2_CTRLA_ENABLE) {
|
||||
}
|
||||
hri_rtcmode0_write_CTRLA_ENABLE_bit(dev->hw, true);
|
||||
}
|
||||
|
||||
return return_value;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Disable Tamper Debounce Majority Feature
|
||||
*/
|
||||
int32_t _tamper_disable_debounce_majority(struct calendar_dev *const dev)
|
||||
{
|
||||
int32_t return_value;
|
||||
|
||||
hri_rtcmode0_write_CTRLA_ENABLE_bit(dev->hw, false);
|
||||
|
||||
while (hri_rtcmode0_read_SYNCBUSY_reg(dev->hw) & RTC_MODE2_CTRLA_ENABLE) {
|
||||
}
|
||||
|
||||
if (hri_rtcmode0_read_CTRLA_reg(dev->hw) & RTC_MODE2_CTRLA_ENABLE) {
|
||||
return_value = ERR_FAILURE;
|
||||
} else {
|
||||
hri_rtcmode0_write_CTRLB_DEBMAJ_bit(dev->hw, false);
|
||||
return_value = ERR_NONE;
|
||||
|
||||
while (hri_rtcmode0_read_SYNCBUSY_reg(dev->hw) & RTC_MODE2_CTRLA_ENABLE) {
|
||||
}
|
||||
hri_rtcmode0_write_CTRLA_ENABLE_bit(dev->hw, true);
|
||||
}
|
||||
|
||||
return return_value;
|
||||
}
|
||||
|
||||
int32_t _prescaler_register_callback(struct calendar_dev *const dev, calendar_drv_cb_t callback)
|
||||
{
|
||||
ASSERT(dev && dev->hw);
|
||||
|
||||
/* Check callback */
|
||||
if (callback != NULL) {
|
||||
/* register the callback */
|
||||
dev->callback_tick = callback;
|
||||
|
||||
/* enable RTC_IRQn */
|
||||
NVIC_ClearPendingIRQ(RTC_IRQn);
|
||||
NVIC_EnableIRQ(RTC_IRQn);
|
||||
|
||||
/* enable periodic interrupt */
|
||||
hri_rtcmode0_set_INTEN_PER7_bit(dev->hw);
|
||||
} else {
|
||||
/* disable periodic interrupt */
|
||||
hri_rtcmode0_clear_INTEN_PER7_bit(dev->hw);
|
||||
}
|
||||
|
||||
return ERR_NONE;
|
||||
}
|
||||
|
||||
int32_t _extwake_register_callback(struct calendar_dev *const dev, calendar_drv_extwake_cb_t callback)
|
||||
{
|
||||
ASSERT(dev && dev->hw);
|
||||
|
||||
/* Check callback */
|
||||
if (callback != NULL) {
|
||||
/* register the callback */
|
||||
dev->callback_tamper = callback;
|
||||
|
||||
/* enable RTC_IRQn */
|
||||
NVIC_ClearPendingIRQ(RTC_IRQn);
|
||||
NVIC_EnableIRQ(RTC_IRQn);
|
||||
|
||||
hri_rtcmode0_clear_interrupt_TAMPER_bit(dev->hw);
|
||||
/* enable tamper interrupt */
|
||||
hri_rtcmode0_set_INTEN_TAMPER_bit(dev->hw);
|
||||
} else {
|
||||
/* disable tamper interrupt */
|
||||
hri_rtcmode0_clear_INTEN_TAMPER_bit(dev->hw);
|
||||
}
|
||||
|
||||
return ERR_NONE;
|
||||
}
|
||||
/**
|
||||
* \brief Registers callback for the specified callback type
|
||||
*/
|
||||
int32_t _calendar_register_callback(struct calendar_dev *const dev, calendar_drv_cb_t callback)
|
||||
{
|
||||
ASSERT(dev && dev->hw);
|
||||
|
||||
/* Check callback */
|
||||
if (callback != NULL) {
|
||||
/* register the callback */
|
||||
dev->callback_alarm = callback;
|
||||
|
||||
/* enable RTC_IRQn */
|
||||
NVIC_ClearPendingIRQ(RTC_IRQn);
|
||||
NVIC_EnableIRQ(RTC_IRQn);
|
||||
|
||||
/* enable cmp */
|
||||
hri_rtcmode0_set_INTEN_CMP0_bit(dev->hw);
|
||||
} else {
|
||||
/* disable cmp */
|
||||
hri_rtcmode0_clear_INTEN_CMP0_bit(dev->hw);
|
||||
}
|
||||
|
||||
return ERR_NONE;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief RTC interrupt handler
|
||||
*
|
||||
* \param[in] dev The pointer to calendar device struct
|
||||
*/
|
||||
static void _rtc_interrupt_handler(struct calendar_dev *dev)
|
||||
{
|
||||
/* Read and mask interrupt flag register */
|
||||
uint16_t interrupt_status = hri_rtcmode0_read_INTFLAG_reg(dev->hw);
|
||||
uint16_t interrupt_enabled = hri_rtcmode0_read_INTEN_reg(dev->hw);
|
||||
|
||||
if ((interrupt_status & interrupt_enabled) & RTC_MODE2_INTFLAG_ALARM0) {
|
||||
if (dev->callback_alarm != NULL) {
|
||||
dev->callback_alarm();
|
||||
}
|
||||
|
||||
/* Clear interrupt flag */
|
||||
hri_rtcmode0_clear_interrupt_CMP0_bit(dev->hw);
|
||||
} else if ((interrupt_status & interrupt_enabled) & RTC_MODE2_INTFLAG_PER7) {
|
||||
if (dev->callback_tick != NULL) {
|
||||
dev->callback_tick();
|
||||
}
|
||||
|
||||
/* Clear interrupt flag */
|
||||
hri_rtcmode0_clear_interrupt_PER7_bit(dev->hw);
|
||||
} else if ((interrupt_status & interrupt_enabled) & RTC_MODE2_INTFLAG_TAMPER) {
|
||||
uint8_t reason = hri_rtc_get_TAMPID_reg(dev->hw, 0x1F);
|
||||
if (dev->callback_tamper != NULL) {
|
||||
dev->callback_tamper(reason);
|
||||
}
|
||||
hri_rtc_write_TAMPID_reg(dev->hw, reason);
|
||||
|
||||
/* Clear interrupt flag */
|
||||
hri_rtcmode0_clear_interrupt_TAMPER_bit(dev->hw);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* \brief Set calendar IRQ
|
||||
*/
|
||||
void _calendar_set_irq(struct calendar_dev *const dev)
|
||||
{
|
||||
(void)dev;
|
||||
NVIC_SetPendingIRQ(RTC_IRQn);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Rtc interrupt handler
|
||||
*/
|
||||
void RTC_Handler(void)
|
||||
{
|
||||
_rtc_interrupt_handler(_rtc_dev);
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* \brief RTC
|
||||
*
|
||||
* Copyright (c) 2016-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
|
||||
*/
|
||||
|
||||
#ifndef _HPL_RTC2_V200_H_INCLUDED
|
||||
#define _HPL_RTC2_V200_H_INCLUDED
|
||||
|
||||
#include <hpl_timer.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* \brief Retrieve timer helper functions
|
||||
*
|
||||
* \return A pointer to set of timer helper functions
|
||||
*/
|
||||
struct _timer_hpl_interface *_rtc_get_timer(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif /* _HPL_RTC2_V200_H_INCLUDED */
|
||||
@@ -13,19 +13,8 @@
|
||||
|
||||
struct slcd_sync_descriptor SEGMENT_LCD_0;
|
||||
|
||||
struct calendar_descriptor CALENDAR_0;
|
||||
|
||||
struct i2c_m_sync_desc I2C_0;
|
||||
|
||||
void CALENDAR_0_CLOCK_init(void) {
|
||||
hri_mclk_set_APBAMASK_RTC_bit(MCLK);
|
||||
}
|
||||
|
||||
void CALENDAR_0_init(void) {
|
||||
CALENDAR_0_CLOCK_init();
|
||||
calendar_init(&CALENDAR_0, RTC);
|
||||
}
|
||||
|
||||
void I2C_0_PORT_init(void) {
|
||||
|
||||
gpio_set_pin_pull_mode(SDA,
|
||||
|
||||
@@ -31,43 +31,22 @@ extern "C" {
|
||||
#include <hal_io.h>
|
||||
#include <hal_sleep.h>
|
||||
#include <hal_ext_irq.h>
|
||||
#include <hal_calendar.h>
|
||||
#include <hal_i2c_m_sync.h>
|
||||
#include <hal_delay.h>
|
||||
#include <hal_slcd_sync.h>
|
||||
|
||||
extern struct adc_sync_descriptor ADC_0;
|
||||
|
||||
extern struct calendar_descriptor CALENDAR_0;
|
||||
|
||||
extern struct i2c_m_sync_desc I2C_0;
|
||||
|
||||
extern struct pwm_descriptor PWM_0;
|
||||
|
||||
extern struct pwm_descriptor PWM_1;
|
||||
extern struct slcd_sync_descriptor SEGMENT_LCD_0;
|
||||
|
||||
void ADC_0_PORT_init(void);
|
||||
void ADC_0_CLOCK_init(void);
|
||||
void ADC_0_init(void);
|
||||
|
||||
void CALENDAR_0_CLOCK_init(void);
|
||||
void CALENDAR_0_init(void);
|
||||
|
||||
void I2C_0_CLOCK_init(void);
|
||||
void I2C_0_init(void);
|
||||
void I2C_0_PORT_init(void);
|
||||
|
||||
void delay_driver_init(void);
|
||||
|
||||
void PWM_0_PORT_init(void);
|
||||
void PWM_0_CLOCK_init(void);
|
||||
void PWM_0_init(void);
|
||||
|
||||
void PWM_1_PORT_init(void);
|
||||
void PWM_1_CLOCK_init(void);
|
||||
void PWM_1_init(void);
|
||||
|
||||
void EXTERNAL_IRQ_0_init(void);
|
||||
|
||||
void SEGMENT_LCD_0_init(void);
|
||||
|
||||
@@ -57,7 +57,7 @@ int main(void) {
|
||||
// Ideally we should check if the TAMPER or CMP0 (alarm) flags are set.
|
||||
if (_watch_rtc_is_enabled()) {
|
||||
// User code. Give the application a chance to restore state from backup registers.
|
||||
app_wake_from_deep_sleep();
|
||||
app_wake_from_backup();
|
||||
|
||||
// disable the tamper interrupt and clear the tamper bit
|
||||
hri_rtcmode0_clear_INTEN_TAMPER_bit(RTC);
|
||||
@@ -75,9 +75,9 @@ int main(void) {
|
||||
bool can_sleep = app_loop();
|
||||
|
||||
if (can_sleep && !usb_enabled) {
|
||||
app_prepare_for_sleep();
|
||||
app_prepare_for_standby();
|
||||
sleep(4);
|
||||
app_wake_from_sleep();
|
||||
app_wake_from_standby();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -24,18 +24,6 @@
|
||||
|
||||
#include "watch.h"
|
||||
|
||||
#include "watch_rtc.c"
|
||||
#include "watch_slcd.c"
|
||||
#include "watch_extint.c"
|
||||
#include "watch_led.c"
|
||||
#include "watch_buzzer.c"
|
||||
#include "watch_adc.c"
|
||||
#include "watch_gpio.c"
|
||||
#include "watch_i2c.c"
|
||||
#include "watch_uart.c"
|
||||
#include "watch_deepsleep.c"
|
||||
#include "watch_private.c"
|
||||
|
||||
bool battery_is_low = false;
|
||||
|
||||
// receives interrupts from MCLK, OSC32KCTRL, OSCCTRL, PAC, PM, SUPC and TAL, whatever that is.
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
- @ref led - This section covers functions related to the bi-color red/green LED mounted behind the LCD.
|
||||
- @ref buzzer - This section covers functions related to the piezo buzzer.
|
||||
- @ref adc - This section covers functions related to the SAM L22's analog-to-digital converter, as well as
|
||||
configuring and reading values from the three analog-capable pins on the 9-pin connector.
|
||||
configuring and reading values from the five analog-capable pins on the 9-pin connector.
|
||||
- @ref gpio - This section covers functions related to general-purpose input and output signals.
|
||||
- @ref i2c - This section covers functions related to the SAM L22's built-I2C driver, including configuring
|
||||
the I2C bus, putting values directly on the bus and reading data from registers on I2C devices.
|
||||
|
||||
@@ -22,6 +22,8 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "watch_adc.h"
|
||||
|
||||
void _watch_sync_adc() {
|
||||
while (ADC->SYNCBUSY.reg);
|
||||
}
|
||||
|
||||
@@ -21,8 +21,12 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#ifndef _WATCH_ADC_H_INCLUDED
|
||||
#define _WATCH_ADC_H_INCLUDED
|
||||
////< @file watch_adc.h
|
||||
|
||||
#include "watch.h"
|
||||
|
||||
/** @addtogroup adc Analog Input
|
||||
* @brief This section covers functions related to the SAM L22's analog-to-digital converter,
|
||||
* as well as configuring and reading values from the five analog-capable pins on the
|
||||
@@ -103,3 +107,4 @@ void watch_disable_analog_input(const uint8_t pin);
|
||||
void watch_disable_adc();
|
||||
|
||||
/// @}
|
||||
#endif
|
||||
|
||||
@@ -21,6 +21,8 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#ifndef _WATCH_APP_H_INCLUDED
|
||||
#define _WATCH_APP_H_INCLUDED
|
||||
////< @file watch_app.h
|
||||
|
||||
/** @addtogroup app Application Framework
|
||||
@@ -31,7 +33,7 @@
|
||||
*
|
||||
* 1. Your app_init() function is called.
|
||||
* - This method should only be used to set your initial application state.
|
||||
* 2. If your app is waking from BACKUP, app_wake_from_deep_sleep() is called.
|
||||
* 2. If your app is waking from BACKUP, app_wake_from_backup() is called.
|
||||
* - If you saved state in the RTC's backup registers, you can restore it here.
|
||||
* 3. Your app_setup() method is called.
|
||||
* - You may wish to enable some functionality and peripherals here.
|
||||
@@ -41,11 +43,11 @@
|
||||
* - Return true if your app is prepared to enter STANDBY mode.
|
||||
* 5. This step differs depending on the value returned by app_loop:
|
||||
* - If you returned false, execution resumes at (4).
|
||||
* - If you returned true, app_prepare_for_sleep() is called; execution moves on to (6).
|
||||
* 6. The microcontroller enters the STANDBY sleep mode.
|
||||
* - If you returned true, app_prepare_for_standby() is called; execution moves on to (6).
|
||||
* 6. The microcontroller enters STANDBY mode.
|
||||
* - No user code will run, and the watch will enter a low power mode.
|
||||
* - The watch will remain in this state until an interrupt wakes it.
|
||||
* 7. Once woken from STANDBY, your app_wake_from_sleep() function is called.
|
||||
* 7. Once woken from STANDBY, your app_wake_from_standby() function is called.
|
||||
* - After this, execution resumes at (4).
|
||||
*/
|
||||
/// @{
|
||||
@@ -55,11 +57,11 @@
|
||||
*/
|
||||
void app_init();
|
||||
|
||||
/** @brief A function you will implement to wake from deep sleep mode. The app_wake_from_deep_sleep function is only
|
||||
* called if your app is waking from the ultra-low power BACKUP sleep mode. You may have chosen to store some
|
||||
* state in the RTC's backup registers prior to entering this mode. You may restore that state here.
|
||||
/** @brief A function you will implement to wake from BACKUP mode, which wipes the system's RAM, and with it, your
|
||||
* application's state. You may have chosen to store some important application state in the RTC's backup
|
||||
* registers prior to entering this mode. You may restore that state here.
|
||||
*/
|
||||
void app_wake_from_deep_sleep();
|
||||
void app_wake_from_backup();
|
||||
|
||||
/** @brief A function you will implement to set up your application. The app_setup function is like setup() in Arduino.
|
||||
* It is called once when the program begins. You should set pin modes and enable any peripherals you want to
|
||||
@@ -72,12 +74,12 @@ void app_wake_from_deep_sleep();
|
||||
void app_setup();
|
||||
|
||||
/** @brief A function you will implement to serve as the app's main run loop. This method will be called repeatedly,
|
||||
or if you enter STANDBY sleep mode, as soon as the device wakes from sleep.
|
||||
* @return You should return true if your app is prepared to enter STANDBY sleep mode. If you return false, your
|
||||
* app's app_loop method will be called again immediately. Note that in STANDBY mode, the watch will consume
|
||||
* only about 95 microamperes of power, whereas if you return false and keep the app awake, it will consume
|
||||
* about 355 microamperes. This is the difference between months of battery life and days. As much as
|
||||
* possible, you should limit the amount of time your app spends awake.
|
||||
or if you enter STANDBY mode, as soon as the device wakes from sleep.
|
||||
* @return You should return true if your app is prepared to enter STANDBY mode. If you return false, your app's
|
||||
* app_loop method will be called again immediately. Note that in STANDBY mode, the watch will consume only
|
||||
* about 95 microamperes of power, whereas if you return false and keep the app awake, it will consume about
|
||||
* 355 microamperes. This is the difference between months of battery life and days. As much as possible,
|
||||
* you should limit the amount of time your app spends awake.
|
||||
* @note Only the RTC, the segment LCD controller and the external interrupt controller run in STANDBY mode. If you
|
||||
* are using, e.g. the PWM function to set a custom LED color, you should return false here until you are
|
||||
* finished with that operation. Note however that the peripherals will continue running after waking up,
|
||||
@@ -86,20 +88,21 @@ void app_setup();
|
||||
*/
|
||||
bool app_loop();
|
||||
|
||||
/** @brief A function you will implement to prepare to enter STANDBY sleep mode. The app_prepare_for_sleep function is
|
||||
* called before the watch goes into the STANDBY sleep mode. In STANDBY mode, most peripherals are shut down,
|
||||
* and no code will run until the watch receives an interrupt (generally either the 1Hz tick or a press on one
|
||||
* of the buttons).
|
||||
/** @brief A function you will implement to prepare to enter STANDBY mode. The app_prepare_for_standby function is
|
||||
* called after your app_loop function returns true, and just before the watch enters STANDBY mode. In this
|
||||
* mode most peripherals are shut down, and no code will run until the watch receives an interrupt (generally
|
||||
* either the 1Hz tick or a press on one of the buttons).
|
||||
* @note If you are PWM'ing the LED or playing a sound on the buzzer, the TC/TCC peripherals that drive those operations
|
||||
* will not run in STANDBY. BUT! the output pins will retain the state they had when entering standby. This means
|
||||
* you could end up entering standby with an LED on and draining power, or with a DC potential across the piezo
|
||||
* buzzer that could damage it if left in this state. If your app_loop does not prevent sleep during these
|
||||
* activities, you should make sure to disable these outputs in app_prepare_for_sleep.
|
||||
* activities, you should make sure to disable these outputs in app_prepare_for_standby.
|
||||
*/
|
||||
void app_prepare_for_sleep();
|
||||
void app_prepare_for_standby();
|
||||
|
||||
/** @brief A method you will implement to configure the app after waking from STANDBY sleep mode.
|
||||
/** @brief A method you will implement to configure the app after waking from STANDBY mode.
|
||||
*/
|
||||
void app_wake_from_sleep();
|
||||
void app_wake_from_standby();
|
||||
|
||||
/// @}
|
||||
#endif
|
||||
|
||||
@@ -22,6 +22,8 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "watch_buzzer.h"
|
||||
|
||||
inline void watch_enable_buzzer() {
|
||||
if (!hri_tcc_get_CTRLA_reg(TCC0, TCC_CTRLA_ENABLE)) {
|
||||
_watch_enable_tcc();
|
||||
|
||||
@@ -21,8 +21,12 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#ifndef _WATCH_BUZZER_H_INCLUDED
|
||||
#define _WATCH_BUZZER_H_INCLUDED
|
||||
////< @file watch_buzzer.h
|
||||
|
||||
#include "watch.h"
|
||||
|
||||
/** @addtogroup buzzer Buzzer
|
||||
* @brief This section covers functions related to the piezo buzzer embedded in the F-91W's back plate.
|
||||
*/
|
||||
@@ -157,3 +161,4 @@ void watch_buzzer_play_note(BuzzerNote note, uint16_t duration_ms);
|
||||
extern const uint16_t NotePeriods[108];
|
||||
|
||||
/// @}
|
||||
#endif
|
||||
|
||||
@@ -22,24 +22,18 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
static void extwake_callback(uint8_t reason);
|
||||
ext_irq_cb_t btn_alarm_callback;
|
||||
ext_irq_cb_t a2_callback;
|
||||
ext_irq_cb_t a4_callback;
|
||||
#include "watch_extint.h"
|
||||
|
||||
static void extwake_callback(uint8_t reason) {
|
||||
if (reason & RTC_TAMPID_TAMPID2) {
|
||||
if (btn_alarm_callback != NULL) btn_alarm_callback();
|
||||
} else if (reason & RTC_TAMPID_TAMPID1) {
|
||||
if (a2_callback != NULL) a2_callback();
|
||||
} else if (reason & RTC_TAMPID_TAMPID0) {
|
||||
if (a4_callback != NULL) a4_callback();
|
||||
}
|
||||
}
|
||||
// this warning only appears when you `make BOARD=OSO-SWAT-A1-02`. it's annoying,
|
||||
// but i'd rather have it warn us at build-time than fail silently at run-time.
|
||||
// besides, no one but me really has any of these boards anyway.
|
||||
#if BTN_ALARM != GPIO(GPIO_PORTA, 2)
|
||||
#warning This board revision does not support external wake on BTN_ALARM, so watch_register_extwake_callback will not work with it. Use watch_register_interrupt_callback instead.
|
||||
#endif
|
||||
|
||||
void watch_register_extwake_callback(uint8_t pin, ext_irq_cb_t callback, bool level) {
|
||||
uint32_t pinmux;
|
||||
hri_rtc_tampctrl_reg_t config = hri_rtc_get_TAMPCTRL_reg(RTC, 0xFFFFFFFF);
|
||||
hri_rtc_tampctrl_reg_t config = RTC->MODE2.TAMPCTRL.reg;
|
||||
|
||||
switch (pin) {
|
||||
case A4:
|
||||
@@ -77,16 +71,45 @@ void watch_register_extwake_callback(uint8_t pin, ext_irq_cb_t callback, bool le
|
||||
gpio_set_pin_function(pin, pinmux);
|
||||
|
||||
// disable the RTC
|
||||
RTC->MODE2.CTRLA.bit.ENABLE = 0;
|
||||
while (RTC->MODE2.SYNCBUSY.bit.ENABLE);
|
||||
|
||||
// update the configuration
|
||||
RTC->MODE2.TAMPCTRL.reg = config;
|
||||
// re-enable the RTC
|
||||
RTC->MODE2.CTRLA.bit.ENABLE = 1;
|
||||
|
||||
NVIC_ClearPendingIRQ(RTC_IRQn);
|
||||
NVIC_EnableIRQ(RTC_IRQn);
|
||||
RTC->MODE2.INTENSET.reg = RTC_MODE2_INTENSET_TAMPER;
|
||||
}
|
||||
|
||||
void watch_disable_extwake_interrupt(uint8_t pin) {
|
||||
hri_rtc_tampctrl_reg_t config = hri_rtc_get_TAMPCTRL_reg(RTC, 0xFFFFFFFF);
|
||||
|
||||
switch (pin) {
|
||||
case A4:
|
||||
a4_callback = NULL;
|
||||
config &= ~(3 << RTC_TAMPCTRL_IN0ACT_Pos);
|
||||
break;
|
||||
case A2:
|
||||
a2_callback = NULL;
|
||||
config &= ~(3 << RTC_TAMPCTRL_IN1ACT_Pos);
|
||||
break;
|
||||
case BTN_ALARM:
|
||||
btn_alarm_callback = NULL;
|
||||
config &= ~(3 << RTC_TAMPCTRL_IN2ACT_Pos);
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
if (hri_rtcmode0_get_CTRLA_ENABLE_bit(RTC)) {
|
||||
hri_rtcmode0_clear_CTRLA_ENABLE_bit(RTC);
|
||||
hri_rtcmode0_wait_for_sync(RTC, RTC_MODE0_SYNCBUSY_ENABLE);
|
||||
}
|
||||
// update the configuration
|
||||
hri_rtc_write_TAMPCTRL_reg(RTC, config);
|
||||
// re-enable the RTC
|
||||
hri_rtcmode0_set_CTRLA_ENABLE_bit(RTC);
|
||||
|
||||
_extwake_register_callback(&CALENDAR_0.device, extwake_callback);
|
||||
}
|
||||
|
||||
void watch_store_backup_data(uint32_t data, uint8_t reg) {
|
||||
@@ -128,23 +151,12 @@ void _watch_disable_all_peripherals_except_slcd() {
|
||||
MCLK->APBCMASK.reg &= ~MCLK_APBCMASK_SERCOM3;
|
||||
}
|
||||
|
||||
void watch_enter_deep_sleep(char *message) {
|
||||
// configure the ALARM interrupt (the callback doesn't matter)
|
||||
watch_register_extwake_callback(BTN_ALARM, NULL, true);
|
||||
|
||||
if (message != NULL) {
|
||||
watch_display_string(" ", 0);
|
||||
watch_display_string(message, 0);
|
||||
} else {
|
||||
slcd_sync_deinit(&SEGMENT_LCD_0);
|
||||
hri_mclk_clear_APBCMASK_SLCD_bit(SLCD);
|
||||
}
|
||||
|
||||
void watch_enter_sleep_mode() {
|
||||
// disable all other peripherals
|
||||
_watch_disable_all_peripherals_except_slcd();
|
||||
|
||||
// disable tick interrupt
|
||||
watch_register_tick_callback(NULL);
|
||||
watch_rtc_disable_all_periodic_callbacks();
|
||||
|
||||
// disable brownout detector interrupt, which could inadvertently wake us up.
|
||||
SUPC->INTENCLR.bit.BOD33DET = 1;
|
||||
@@ -152,21 +164,29 @@ void watch_enter_deep_sleep(char *message) {
|
||||
// disable all pins
|
||||
_watch_disable_all_pins_except_rtc();
|
||||
|
||||
// turn off RAM completely.
|
||||
PM->STDBYCFG.bit.BBIASHS = 3;
|
||||
|
||||
// enter standby (4); we basically hang out here until an interrupt forces us to reset.
|
||||
// enter standby (4); we basically hang out here until an interrupt wakes us.
|
||||
sleep(4);
|
||||
|
||||
NVIC_SystemReset();
|
||||
// and we awake! re-enable the brownout detector
|
||||
SUPC->INTENSET.bit.BOD33DET = 1;
|
||||
|
||||
// call app_setup so the app can re-enable everything we disabled.
|
||||
app_setup();
|
||||
|
||||
// and call app_wake_from_standby (since main won't have a chance to do it)
|
||||
app_wake_from_standby();
|
||||
}
|
||||
|
||||
void watch_enter_deep_sleep_mode() {
|
||||
// identical to sleep mode except we disable the LCD first.
|
||||
slcd_sync_deinit(&SEGMENT_LCD_0);
|
||||
hri_mclk_clear_APBCMASK_SLCD_bit(SLCD);
|
||||
|
||||
watch_enter_sleep_mode();
|
||||
}
|
||||
|
||||
void watch_enter_backup_mode() {
|
||||
// this will not work on the current silicon revision, but I said in the documentation that we do it.
|
||||
// so let's do it!
|
||||
watch_register_extwake_callback(BTN_ALARM, NULL, true);
|
||||
|
||||
watch_register_tick_callback(NULL);
|
||||
watch_rtc_disable_all_periodic_callbacks();
|
||||
_watch_disable_all_peripherals_except_slcd();
|
||||
slcd_sync_deinit(&SEGMENT_LCD_0);
|
||||
hri_mclk_clear_APBCMASK_SLCD_bit(SLCD);
|
||||
@@ -175,3 +195,15 @@ void watch_enter_backup_mode() {
|
||||
// go into backup sleep mode (5). when we exit, the reset controller will take over.
|
||||
sleep(5);
|
||||
}
|
||||
|
||||
// deprecated
|
||||
void watch_enter_shallow_sleep(bool display_on) {
|
||||
if (display_on) watch_enter_sleep_mode();
|
||||
else watch_enter_deep_sleep_mode();
|
||||
}
|
||||
|
||||
// deprecated
|
||||
void watch_enter_deep_sleep() {
|
||||
watch_register_extwake_callback(BTN_ALARM, NULL, true);
|
||||
watch_enter_backup_mode();
|
||||
}
|
||||
|
||||
@@ -21,92 +21,127 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#ifndef _WATCH_DEEPSLEEP_H_INCLUDED
|
||||
#define _WATCH_DEEPSLEEP_H_INCLUDED
|
||||
////< @file watch_deepsleep.h
|
||||
|
||||
/** @addtogroup deepsleep Deep Sleep Control
|
||||
* @brief This section covers functions related to preparing for and entering BACKUP mode, the
|
||||
* deepest sleep mode available on the SAM L22
|
||||
#include "watch.h"
|
||||
|
||||
// These are declared in watch_rtc.c.
|
||||
extern ext_irq_cb_t btn_alarm_callback;
|
||||
extern ext_irq_cb_t a2_callback;
|
||||
extern ext_irq_cb_t a4_callback;
|
||||
|
||||
/** @addtogroup deepsleep Sleep Control
|
||||
* @brief This section covers functions related to the various sleep modes available to the watch,
|
||||
* including Sleep, Deep Sleep, and BACKUP mode.
|
||||
* @details These terms changed meaning a bit over the course of development; if you are coming
|
||||
* to this documentation after having worked with an earlier version of the library,
|
||||
* these definitions should clarify the terminology. Terms in all caps are modes of the
|
||||
* SAM L22; terms in Title Case are specific implementations in this library.
|
||||
* - ACTIVE mode is the mode the SAM L22 is in when both the main clock and the CPU are
|
||||
* running. It is the most power-hungry mode. If you ever call delay_ms to wait a beat,
|
||||
* the watch will remain in ACTIVE mode while taking that delay. In addition, whenever
|
||||
* your `app_loop` function returns false, the device will remain in ACTIVE mode and
|
||||
* call your `app_loop` function again.
|
||||
* - STANDBY mode turns off the main clock and halts the CPU. Since the PWM driver is
|
||||
* run from the main clock, it also stops the buzzer and any dimming of the LEDs.
|
||||
* In this mode, the watch can wake from any interrupt source. Whenever your `app_loop`
|
||||
* function returns true, the watch enters STANDBY mode until the next tick or other
|
||||
* interrupt. This mode uses much less power than ACTIVE mode.
|
||||
* - Sleep Mode is a special case of STANDBY mode. In this mode, the watch turns off
|
||||
* almost all peripherals (including the external interrupt controller), and disables
|
||||
* all pins except for the external wake pins. In this mode the watch can only wake
|
||||
* from the RTC alarm interrupt or an external wake pin (A2, A4 or the alarm button),
|
||||
* but the display remains on and your app's state is retained. You can enter this
|
||||
* mode by calling `watch_enter_sleep_mode`. It consumes an order of magnitude less
|
||||
* power than STANDBY mode.
|
||||
* - Deep Sleep Mode is identical to sleep mode, but it also turns off the LCD to save
|
||||
* a bit more power. You can enter this mode by calling `watch_enter_deep_sleep_mode`.
|
||||
* - BACKUP mode is the lowest possible power mode on the SAM L22. It turns off all pins
|
||||
* and peripherals except for the RTC. It also turns off the RAM, obliterating your
|
||||
* application's state. The only way to wake from this mode is by setting an external
|
||||
* wake interrupt on pin A2 or pin A4, and when you do wake it will be much like a
|
||||
* wake from reset. You can enter this mode by calling `watch_enter_backup_mode`.
|
||||
*/
|
||||
/// @{
|
||||
|
||||
/** @brief Registers a callback on one of the RTC's external wake pins, which can wake the device
|
||||
* from deep sleep (aka BACKUP) mode.
|
||||
* from Sleep, Deep Sleep and BACKUP modes (but see warning re: BACKUP mode).
|
||||
* @param pin Either pin BTN_ALARM, A2, or A4. These are the three external wake pins. If the pin
|
||||
* is BTN_ALARM, this function also enables an internal pull down on that pin.
|
||||
* @param callback The callback to be called if this pin triggers outside of deep sleep mode. If
|
||||
* this is NULL, no callback will be called even in normal mode, but the interrupt
|
||||
* will still be enabled so that it can wake from deep sleep or backup mode.
|
||||
* @param callback The callback to be called if this pin triggers outside of BACKUP mode. If this is
|
||||
* NULL, no callback will be called even in normal modes, but the interrupt will
|
||||
* still be enabled so that it can wake the device.
|
||||
* @param level The level you wish to scan for: true for rising, false for falling. Note that you
|
||||
* cannot scan for both rising and falling edges like you can with the external interrupt
|
||||
* pins; with the external wake interrupt, you can only get one or the other.
|
||||
* @note When in normal or STANDBY mode, this will function much like a standard external interrupt
|
||||
* situation: these pins will wake from standby, and your callback will be called. However,
|
||||
* if the device enters deep sleep and one of these pins wakes the device, your callback
|
||||
* WILL NOT be called, as the device is basically waking from reset at that point.
|
||||
* @note When in ACTIVE, STANDBY and Sleep / Deep sleep modes, this will function much like a standard
|
||||
* external interrupt situation: these pins will wake the device, and your callback will be
|
||||
* called. However, if the device enters BACKUP mode and one of these pins wakes the device, your
|
||||
* callback WILL NOT be called, as the device is basically waking from reset at that point.
|
||||
* @warning As of the current SAM L22 silicon revision (rev B), the BTN_ALARM pin cannot wake the
|
||||
* device from BACKUP mode. You can still use this function to register a BTN_ALARM interrupt
|
||||
* in normal or deep sleep mode, but to wake from BACKUP, you will need to use pin A2 or A4.
|
||||
*/
|
||||
void watch_register_extwake_callback(uint8_t pin, ext_irq_cb_t callback, bool level);
|
||||
|
||||
/** @brief Unregisters the interrupt on one of the EXTWAKE pins. This will prevent a value change on
|
||||
* one of these pins from waking the device from deep sleep or BACKUP modes.
|
||||
/** @brief Unregisters the RTC interrupt on one of the EXTWAKE pins. This will prevent a value change on
|
||||
* one of these pins from waking the device.
|
||||
* @param pin Either pin BTN_ALARM, A2, or A4. If the pin is BTN_ALARM, this function DOES NOT disable
|
||||
* the internal pull down on that pin.
|
||||
*/
|
||||
void watch_disable_extwake_interrupt(uint8_t pin, ext_irq_cb_t callback, bool level);
|
||||
void watch_disable_extwake_interrupt(uint8_t pin);
|
||||
|
||||
/** @brief Stores data in one of the RTC's backup registers, which retain their data in the deep sleep
|
||||
and backup modes.
|
||||
/** @brief Stores data in one of the RTC's backup registers, which retain their data in BACKUP mode.
|
||||
* @param data An unsigned 32 bit integer with the data you wish to store.
|
||||
* @param reg A register from 0-7.
|
||||
*/
|
||||
void watch_store_backup_data(uint32_t data, uint8_t reg);
|
||||
|
||||
/** @brief Gets 32 bits of data from the RTC's backup register.
|
||||
/** @brief Gets 32 bits of data from the RTC's BACKUP register.
|
||||
* @param reg A register from 0-7.
|
||||
* @return An unsigned 32 bit integer with the from the backup register.
|
||||
*/
|
||||
uint32_t watch_get_backup_data(uint8_t reg);
|
||||
|
||||
/** @brief Enters a deep sleep mode by disabling RAM retention and all peripherals except the RTC and
|
||||
* (optionally) the LCD. You can wake from this mode by pressing the ALARM button.
|
||||
* @param message Either NULL, or a string representing a message to display while in deep sleep mode. The
|
||||
* message will be displayed at position 0, so you should pad out the beginning of the string
|
||||
* with spaces if you wish for the message to appear on line 2, i.e. " SLEEP". If this
|
||||
* parameter is NULL, the screen will be blanked, and this function will disable the SLCD
|
||||
* peripheral for additional power savings. (also note that while the message will replace any
|
||||
* text on the display, this function will not clear any indicators you have set. This is by
|
||||
* design, in case you wish to leave an indicator lit in sleep mode.)
|
||||
* @details This deep sleep mode is not the lowest power mode available (see watch_enter_backup_mode), but
|
||||
* it has the benefit of being able to wake with a press of the ALARM button, and provides an option
|
||||
* for displaying a message to the user when asleep. The only way to wake from this mode is by
|
||||
* pressing the ALARM button, or receiving an interrupt on pin A2 or A4 of the nine-pin connector.
|
||||
* (An alarm interrupt would also work, but this has not yet been implemented.) This function enables
|
||||
* the ALARM button interrupt for you, but if you wish to wake from the A2 or A4 RTC interrupt, you
|
||||
* must configure them by calling watch_register_extwake_callback. Note however that your callback
|
||||
* will not be called in this case.
|
||||
* Power consumption in deep sleep mode varies a bit with the battery voltage and the temperature,
|
||||
* but at 3 V and ~25° C you can eoughly estimate:
|
||||
* * ~12µA current draw with the LCD controller on (message != NULL)
|
||||
* * ~6.5µA current draw with the LCD controller off (message == NULL)
|
||||
* @note With RAM powered off, your application state will be cleared as soon as you call this function, and
|
||||
* when the user wakes up the watch, your app will effectively be waking from reset. Your app's @ref
|
||||
* app_wake_from_deep_sleep function will be called to give your app a chance to restore any state that
|
||||
* you stored using @ref watch_store_backup_data.
|
||||
/** @brief enters Sleep Mode by disabling all pins and peripherals except the RTC and the LCD.
|
||||
* @details This sleep mode is not the lowest power mode available, but it has the benefit of allowing you
|
||||
* to display a message to the user while asleep. You can also set an alarm interrupt to wake at a
|
||||
* configfurable interval (every minute, hour or day) to update the display. You can wake from this
|
||||
* mode by pressing the ALARM button, if you registered an extwake callback on the ALARM button.
|
||||
* Also note that when your app wakes from this sleep mode, your app_setup method will be called
|
||||
* again, since this function will have disabled things you set up there.
|
||||
*
|
||||
* Note that to wake from either the ALARM button, the A2 interrupt or the A4 interrupt, you
|
||||
* must first configure this by calling watch_register_extwake_callback.
|
||||
*
|
||||
* You can estimate the power consumption of this mode to be on the order of 30 microwatts
|
||||
* (about 10 µA at 3 V).
|
||||
*/
|
||||
void watch_enter_deep_sleep(char *message);
|
||||
void watch_enter_sleep_mode();
|
||||
|
||||
/** @brief enters Deep Sleep Mode by disabling all pins and peripherals except the RTC.
|
||||
* @details Short of BACKUP mode, this is the lowest power mode you can enter while retaining your
|
||||
* application state (and the ability to wake with the alarm button). Just note that the display
|
||||
* will be completely off, so you should document to the user of your application that they will
|
||||
* need to press the alarm button to wake the device, or use a sensor board with support for
|
||||
* an external wake pin.
|
||||
*
|
||||
* All notes from watch_enter_sleep_mode apply here, except for power consumption. You can estimate
|
||||
* the power consumption of this mode to be on the order of 12 microwatts (about 4µA at 3 V).
|
||||
*/
|
||||
void watch_enter_deep_sleep_mode();
|
||||
|
||||
/** @brief Enters the SAM L22's lowest-power mode, BACKUP.
|
||||
* @details This function does some housekeeping before entering BACKUP mode. It first disables all
|
||||
* peripherals except for the RTC, and disables the tick interrupt (since that would wake
|
||||
* us up from deep sleep). It also sets an external wake source on the ALARM button, if one
|
||||
* was not already set. If you wish to wake from another source, such as one of the external
|
||||
* wake interrupt pins on the 9-pin connector, set that up prior to calling this function.
|
||||
* @details This function does some housekeeping before entering BACKUP mode. It first disables all pins
|
||||
* and peripherals except for the RTC, and disables the tick interrupt (since that would wake
|
||||
* us up from BACKUP mode). Once again, if you wish to wake from the A2 or the A4 interrupt,
|
||||
* you must first configure this by calling watch_register_extwake_callback.
|
||||
* @note If you have a callback set for an external wake interrupt, it will be called if triggered while
|
||||
* in ACTIVE, IDLE or STANDBY modes, but it *will not be called* when waking from BACKUP.
|
||||
* Waking from backup is effectively like waking from reset, except that your @ref
|
||||
* app_wake_from_deep_sleep function will be called.
|
||||
* in ACTIVE, STANDBY, Sleep and Deep Sleep modes, but it *will not be called* when waking from
|
||||
* BACKUP mode. Waking from backup is effectively like waking from reset, except that your
|
||||
* @ref app_wake_from_backup function will be called.
|
||||
* @warning On current revisions of the SAM L22 silicon, the ALARM_BTN pin (PA02 RTC/IN2) cannot wake
|
||||
* the device from deep sleep mode. There is an errata note (Reference: 15010) that says that
|
||||
* due to a silicon bug, RTC/IN2 is not functional in BACKUP. As a result, you should not call
|
||||
@@ -114,4 +149,11 @@ void watch_enter_deep_sleep(char *message);
|
||||
* on pin A2 or A4 (i.e. an accelerometer with an interrupt pin).
|
||||
*/
|
||||
void watch_enter_backup_mode();
|
||||
|
||||
__attribute__((deprecated("Use watch_enter_sleep_mode or watch_enter_deep_sleep_mode instead")))
|
||||
void watch_enter_shallow_sleep(bool display_on);
|
||||
|
||||
__attribute__((deprecated("Use watch_enter_backup_mode instead")))
|
||||
void watch_enter_deep_sleep();
|
||||
/// @}
|
||||
#endif
|
||||
|
||||
@@ -22,6 +22,8 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "watch_extint.h"
|
||||
|
||||
void watch_enable_external_interrupts() {
|
||||
// Configure EIC to use GCLK3 (the 32.768 kHz crystal)
|
||||
hri_gclk_write_PCHCTRL_reg(GCLK, EIC_GCLK_ID, GCLK_PCHCTRL_GEN_GCLK3_Val | (1 << GCLK_PCHCTRL_CHEN_Pos));
|
||||
@@ -63,18 +65,14 @@ void watch_register_interrupt_callback(const uint8_t pin, ext_irq_cb_t callback,
|
||||
sense_pos = 4 * (WATCH_A4_EIC_CHANNEL % 8);
|
||||
break;
|
||||
case BTN_ALARM:
|
||||
// for the buttons, we need an internal pull-down.
|
||||
gpio_set_pin_pull_mode(pin, GPIO_PULL_DOWN);
|
||||
config_index = (WATCH_BTN_ALARM_EIC_CHANNEL > 7) ? 1 : 0;
|
||||
sense_pos = 4 * (WATCH_BTN_ALARM_EIC_CHANNEL % 8);
|
||||
break;
|
||||
case BTN_LIGHT:
|
||||
gpio_set_pin_pull_mode(pin, GPIO_PULL_DOWN);
|
||||
config_index = (WATCH_BTN_LIGHT_EIC_CHANNEL > 7) ? 1 : 0;
|
||||
sense_pos = 4 * (WATCH_BTN_LIGHT_EIC_CHANNEL % 8);
|
||||
break;
|
||||
case BTN_MODE:
|
||||
gpio_set_pin_pull_mode(pin, GPIO_PULL_DOWN);
|
||||
config_index = (WATCH_BTN_MODE_EIC_CHANNEL > 7) ? 1 : 0;
|
||||
sense_pos = 4 * (WATCH_BTN_MODE_EIC_CHANNEL % 8);
|
||||
break;
|
||||
@@ -83,7 +81,6 @@ void watch_register_interrupt_callback(const uint8_t pin, ext_irq_cb_t callback,
|
||||
}
|
||||
|
||||
gpio_set_pin_direction(pin, GPIO_DIRECTION_IN);
|
||||
gpio_set_pin_function(pin, GPIO_PIN_FUNCTION_A);
|
||||
|
||||
// EIC configuration register is enable-protected, so we have to disable it first...
|
||||
if (hri_eic_get_CTRLA_reg(EIC, EIC_CTRLA_ENABLE)) {
|
||||
@@ -96,6 +93,9 @@ void watch_register_interrupt_callback(const uint8_t pin, ext_irq_cb_t callback,
|
||||
config &= ~(7 << sense_pos);
|
||||
config |= trigger << (sense_pos);
|
||||
hri_eic_write_CONFIG_reg(EIC, config_index, config);
|
||||
// ...set the pin mode...
|
||||
gpio_set_pin_function(pin, GPIO_PIN_FUNCTION_A);
|
||||
if (pin == BTN_ALARM || pin == BTN_LIGHT || pin == BTN_MODE) gpio_set_pin_pull_mode(pin, GPIO_PULL_DOWN);
|
||||
// ...and re-enable the EIC
|
||||
hri_eic_set_CTRLA_ENABLE_bit(EIC);
|
||||
|
||||
|
||||
@@ -21,8 +21,11 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#ifndef _WATCH_EXTINT_H_INCLUDED
|
||||
#define _WATCH_EXTINT_H_INCLUDED
|
||||
////< @file watch_extint.h
|
||||
|
||||
#include "watch.h"
|
||||
#include "hal_ext_irq.h"
|
||||
|
||||
/** @addtogroup buttons Buttons & External Interrupts
|
||||
@@ -78,3 +81,4 @@ void watch_register_button_callback(const uint8_t pin, ext_irq_cb_t callback);
|
||||
__attribute__((deprecated("Use watch_enable_external_interrupts instead")))
|
||||
void watch_enable_buttons();
|
||||
/// @}
|
||||
#endif
|
||||
|
||||
@@ -22,6 +22,8 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "watch_gpio.h"
|
||||
|
||||
void watch_enable_digital_input(const uint8_t pin) {
|
||||
gpio_set_pin_direction(pin, GPIO_DIRECTION_IN);
|
||||
gpio_set_pin_function(pin, GPIO_PIN_FUNCTION_OFF);
|
||||
|
||||
@@ -21,8 +21,12 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#ifndef _WATCH_GPIO_H_INCLUDED
|
||||
#define _WATCH_GPIO_H_INCLUDED
|
||||
////< @file watch_gpio.h
|
||||
|
||||
#include "watch.h"
|
||||
|
||||
/** @addtogroup gpio Digital Input and Output
|
||||
* @brief This section covers functions related to general-purpose input and output signals.
|
||||
*/
|
||||
@@ -69,3 +73,4 @@ void watch_disable_digital_output(const uint8_t pin);
|
||||
*/
|
||||
void watch_set_pin_level(const uint8_t pin, const bool level);
|
||||
/// @}
|
||||
#endif
|
||||
|
||||
@@ -22,7 +22,9 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
struct io_descriptor *I2C_0_io;
|
||||
#include "watch_i2c.h"
|
||||
|
||||
struct io_descriptor *I2C_0_io;
|
||||
|
||||
void watch_enable_i2c() {
|
||||
I2C_0_init();
|
||||
|
||||
@@ -21,8 +21,12 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#ifndef _WATCH_I2C_H_INCLUDED
|
||||
#define _WATCH_I2C_H_INCLUDED
|
||||
////< @file watch_i2c.h
|
||||
|
||||
#include "watch.h"
|
||||
|
||||
/** @addtogroup i2c I2C Controller Driver
|
||||
* @brief This section covers functions related to the SAM L22's built-I2C driver, including
|
||||
* configuring the I2C bus, putting values directly on the bus and reading data from
|
||||
@@ -99,3 +103,4 @@ uint32_t watch_i2c_read24(int16_t addr, uint8_t reg);
|
||||
*/
|
||||
uint32_t watch_i2c_read32(int16_t addr, uint8_t reg);
|
||||
/// @}
|
||||
#endif
|
||||
|
||||
@@ -22,6 +22,8 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "watch_led.h"
|
||||
|
||||
void watch_enable_leds() {
|
||||
if (!hri_tcc_get_CTRLA_reg(TCC0, TCC_CTRLA_ENABLE)) {
|
||||
_watch_enable_tcc();
|
||||
|
||||
@@ -21,8 +21,12 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#ifndef _WATCH_LED_H_INCLUDED
|
||||
#define _WATCH_LED_H_INCLUDED
|
||||
////< @file watch_led.h
|
||||
|
||||
#include "watch.h"
|
||||
|
||||
/** @addtogroup led LED Control
|
||||
* @brief This section covers functions related to the bi-color red/green LED mounted behind the LCD.
|
||||
* @details The SAM L22 is an exceedingly power efficient chip, whereas the LED's are relatively power-
|
||||
@@ -86,3 +90,4 @@ void watch_enable_led(bool unused);
|
||||
__attribute__((deprecated("Use watch_disable_leds instead")))
|
||||
void watch_disable_led(bool unused);
|
||||
/// @}
|
||||
#endif
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "watch_private.h"
|
||||
#include "tusb.h"
|
||||
|
||||
void _watch_init() {
|
||||
@@ -56,8 +57,7 @@ void _watch_init() {
|
||||
SUPC->BOD33.bit.ENABLE = 1;
|
||||
|
||||
// External wake depends on RTC; calendar is a required module.
|
||||
CALENDAR_0_init();
|
||||
calendar_enable(&CALENDAR_0);
|
||||
_watch_rtc_init();
|
||||
|
||||
// set up state
|
||||
btn_alarm_callback = NULL;
|
||||
|
||||
@@ -21,10 +21,17 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#ifndef _WATCH_PRIVATE_H_INCLUDED
|
||||
#define _WATCH_PRIVATE_H_INCLUDED
|
||||
|
||||
#include "watch.h"
|
||||
|
||||
/// Called by main.c while setting up the app. You should not call this from your app.
|
||||
void _watch_init();
|
||||
|
||||
/// Initializes the real-time clock peripheral.
|
||||
void _watch_rtc_init();
|
||||
|
||||
/// Called by buzzer and LED setup functions. You should not call this from your app.
|
||||
void _watch_enable_tcc();
|
||||
|
||||
@@ -33,3 +40,4 @@ void _watch_disable_tcc();
|
||||
|
||||
/// Called by main.c if plugged in to USB. You should not call this from your app.
|
||||
void _watch_enable_usb();
|
||||
#endif
|
||||
|
||||
@@ -22,19 +22,174 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
bool _watch_rtc_is_enabled() {
|
||||
return RTC->MODE0.CTRLA.bit.ENABLE;
|
||||
#include "watch_rtc.h"
|
||||
|
||||
ext_irq_cb_t tick_callbacks[8];
|
||||
ext_irq_cb_t alarm_callback;
|
||||
ext_irq_cb_t btn_alarm_callback;
|
||||
ext_irq_cb_t a2_callback;
|
||||
ext_irq_cb_t a4_callback;
|
||||
|
||||
bool _watch_rtc_is_enabled() {
|
||||
return RTC->MODE2.CTRLA.bit.ENABLE;
|
||||
}
|
||||
|
||||
void _sync_rtc() {
|
||||
while (RTC->MODE2.SYNCBUSY.reg);
|
||||
}
|
||||
|
||||
void _watch_rtc_init() {
|
||||
MCLK->APBAMASK.reg |= MCLK_APBAMASK_RTC;
|
||||
|
||||
if (_watch_rtc_is_enabled()) return; // don't reset the RTC if it's already set up.
|
||||
|
||||
RTC->MODE2.CTRLA.bit.ENABLE = 0;
|
||||
_sync_rtc();
|
||||
|
||||
RTC->MODE2.CTRLA.bit.SWRST = 1;
|
||||
_sync_rtc();
|
||||
|
||||
RTC->MODE2.CTRLA.bit.MODE = RTC_MODE2_CTRLA_MODE_CLOCK_Val;
|
||||
RTC->MODE2.CTRLA.bit.PRESCALER = RTC_MODE2_CTRLA_PRESCALER_DIV1024_Val;
|
||||
RTC->MODE2.CTRLA.bit.CLOCKSYNC = 1;
|
||||
RTC->MODE2.CTRLA.bit.ENABLE = 1;
|
||||
_sync_rtc();
|
||||
}
|
||||
|
||||
void watch_rtc_set_date_time(watch_date_time date_time) {
|
||||
RTC->MODE2.CLOCK.reg = date_time.reg;
|
||||
_sync_rtc();
|
||||
}
|
||||
|
||||
watch_date_time watch_rtc_get_date_time() {
|
||||
watch_date_time retval;
|
||||
|
||||
_sync_rtc();
|
||||
retval.reg = RTC->MODE2.CLOCK.reg;
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
void watch_rtc_register_tick_callback(ext_irq_cb_t callback) {
|
||||
watch_rtc_register_periodic_callback(callback, 1);
|
||||
}
|
||||
|
||||
void watch_rtc_disable_tick_callback() {
|
||||
watch_rtc_disable_periodic_callback(1);
|
||||
}
|
||||
|
||||
void watch_rtc_register_periodic_callback(ext_irq_cb_t callback, uint8_t frequency) {
|
||||
// we told them, it has to be a power of 2.
|
||||
if (__builtin_popcount(frequency) != 1) return;
|
||||
|
||||
// this left-justifies the period in a 32-bit integer.
|
||||
uint32_t tmp = frequency << 24;
|
||||
// now we can count the leading zeroes to get the value we need.
|
||||
// 0x01 (1 Hz) will have 7 leading zeros for PER7. 0xF0 (128 Hz) will have no leading zeroes for PER0.
|
||||
uint8_t per_n = __builtin_clz(tmp);
|
||||
|
||||
// this also maps nicely to an index for our list of tick callbacks.
|
||||
tick_callbacks[per_n] = callback;
|
||||
|
||||
NVIC_ClearPendingIRQ(RTC_IRQn);
|
||||
NVIC_EnableIRQ(RTC_IRQn);
|
||||
RTC->MODE2.INTENSET.reg = 1 << per_n;
|
||||
}
|
||||
|
||||
void watch_rtc_disable_periodic_callback(uint8_t frequency) {
|
||||
if (__builtin_popcount(frequency) != 1) return;
|
||||
uint8_t per_n = __builtin_clz(frequency << 24);
|
||||
RTC->MODE2.INTENCLR.reg = 1 << per_n;
|
||||
}
|
||||
|
||||
void watch_rtc_disable_all_periodic_callbacks() {
|
||||
RTC->MODE2.INTENCLR.reg = 0xFF;
|
||||
}
|
||||
|
||||
void watch_rtc_register_alarm_callback(ext_irq_cb_t callback, watch_date_time alarm_time, watch_rtc_alarm_match mask) {
|
||||
RTC->MODE2.Mode2Alarm[0].ALARM.reg = alarm_time.reg;
|
||||
RTC->MODE2.Mode2Alarm[0].MASK.reg = mask;
|
||||
RTC->MODE2.INTENSET.reg = RTC_MODE2_INTENSET_ALARM0;
|
||||
alarm_callback = callback;
|
||||
NVIC_ClearPendingIRQ(RTC_IRQn);
|
||||
NVIC_EnableIRQ(RTC_IRQn);
|
||||
RTC->MODE2.INTENSET.reg = RTC_MODE2_INTENSET_ALARM0;
|
||||
}
|
||||
|
||||
void watch_rtc_disable_alarm_callback() {
|
||||
RTC->MODE2.INTENCLR.reg = RTC_MODE2_INTENCLR_ALARM0;
|
||||
}
|
||||
|
||||
void RTC_Handler(void) {
|
||||
uint16_t interrupt_status = RTC->MODE2.INTFLAG.reg;
|
||||
uint16_t interrupt_enabled = RTC->MODE2.INTENSET.reg;
|
||||
|
||||
if ((interrupt_status & interrupt_enabled) & RTC_MODE2_INTFLAG_PER_Msk) {
|
||||
// handle the tick callback first, it's what we do the most.
|
||||
// start from PER7, the 1 Hz tick.
|
||||
for(int8_t i = 7; i >= 0; i--) {
|
||||
if ((interrupt_status & interrupt_enabled) & (1 << i)) {
|
||||
if (tick_callbacks[i] != NULL) {
|
||||
tick_callbacks[i]();
|
||||
}
|
||||
RTC->MODE2.INTFLAG.reg = 1 << i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if ((interrupt_status & interrupt_enabled) & RTC_MODE2_INTFLAG_TAMPER) {
|
||||
// handle the extwake interrupts next.
|
||||
uint8_t reason = RTC->MODE2.TAMPID.reg;
|
||||
if (reason & RTC_TAMPID_TAMPID2) {
|
||||
if (btn_alarm_callback != NULL) btn_alarm_callback();
|
||||
} else if (reason & RTC_TAMPID_TAMPID1) {
|
||||
if (a2_callback != NULL) a2_callback();
|
||||
} else if (reason & RTC_TAMPID_TAMPID0) {
|
||||
if (a4_callback != NULL) a4_callback();
|
||||
}
|
||||
RTC->MODE2.TAMPID.reg = reason;
|
||||
RTC->MODE2.INTFLAG.reg = RTC_MODE2_INTFLAG_TAMPER;
|
||||
} else if ((interrupt_status & interrupt_enabled) & RTC_MODE2_INTFLAG_ALARM0) {
|
||||
// finally handle the alarm.
|
||||
if (alarm_callback != NULL) {
|
||||
alarm_callback();
|
||||
}
|
||||
RTC->MODE2.INTFLAG.reg = RTC_MODE2_INTFLAG_ALARM0;
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////
|
||||
// Deprecated functions
|
||||
|
||||
void watch_set_date_time(struct calendar_date_time date_time) {
|
||||
calendar_set_date(&CALENDAR_0, &date_time.date);
|
||||
calendar_set_time(&CALENDAR_0, &date_time.time);
|
||||
RTC_MODE2_CLOCK_Type val;
|
||||
|
||||
val.bit.SECOND = date_time.time.sec;
|
||||
val.bit.MINUTE = date_time.time.min;
|
||||
val.bit.HOUR = date_time.time.hour;
|
||||
val.bit.DAY = date_time.date.day;
|
||||
val.bit.MONTH = date_time.date.month;
|
||||
val.bit.YEAR = (uint8_t)(date_time.date.year - WATCH_RTC_REFERENCE_YEAR);
|
||||
|
||||
RTC->MODE2.CLOCK.reg = val.reg;
|
||||
|
||||
_sync_rtc();
|
||||
}
|
||||
|
||||
void watch_get_date_time(struct calendar_date_time *date_time) {
|
||||
calendar_get_date_time(&CALENDAR_0, date_time);
|
||||
_sync_rtc();
|
||||
RTC_MODE2_CLOCK_Type val = RTC->MODE2.CLOCK;
|
||||
|
||||
date_time->time.sec = val.bit.SECOND;
|
||||
date_time->time.min = val.bit.MINUTE;
|
||||
date_time->time.hour = val.bit.HOUR;
|
||||
date_time->date.day = val.bit.DAY;
|
||||
date_time->date.month = val.bit.MONTH;
|
||||
date_time->date.year = val.bit.YEAR + WATCH_RTC_REFERENCE_YEAR;
|
||||
}
|
||||
|
||||
void watch_register_tick_callback(ext_irq_cb_t callback) {
|
||||
_prescaler_register_callback(&CALENDAR_0.device, callback);
|
||||
tick_callbacks[7] = callback;
|
||||
NVIC_ClearPendingIRQ(RTC_IRQn);
|
||||
NVIC_EnableIRQ(RTC_IRQn);
|
||||
RTC->MODE2.INTENSET.reg = RTC_MODE2_INTENSET_PER7;
|
||||
}
|
||||
|
||||
@@ -21,8 +21,11 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#ifndef _WATCH_RTC_H_INCLUDED
|
||||
#define _WATCH_RTC_H_INCLUDED
|
||||
////< @file watch_rtc.h
|
||||
|
||||
#include "watch.h"
|
||||
#include "hpl_calendar.h"
|
||||
|
||||
/** @addtogroup rtc Real-Time Clock
|
||||
@@ -35,24 +38,127 @@
|
||||
* to wake from STANDBY mode.
|
||||
*/
|
||||
/// @{
|
||||
|
||||
#define WATCH_RTC_REFERENCE_YEAR (2020)
|
||||
|
||||
typedef union {
|
||||
struct {
|
||||
uint32_t second : 6; // 0-59
|
||||
uint32_t minute : 6; // 0-59
|
||||
uint32_t hour : 5; // 0-23
|
||||
uint32_t day : 5; // 1-31
|
||||
uint32_t month : 4; // 1-12
|
||||
uint32_t year : 6; // 0-63 (representing 2020-2083)
|
||||
} unit;
|
||||
uint32_t reg; // the bit-packed value as expected by the RTC peripheral's CLOCK register.
|
||||
} watch_date_time;
|
||||
|
||||
typedef enum watch_rtc_alarm_match {
|
||||
ALARM_MATCH_DISABLED = 0,
|
||||
ALARM_MATCH_SS,
|
||||
ALARM_MATCH_MMSS,
|
||||
ALARM_MATCH_HHMMSS,
|
||||
} watch_rtc_alarm_match;
|
||||
|
||||
/** @brief Called by main.c to check if the RTC is enabled.
|
||||
* You may call this function, but outside of app_init, it sbould always return true.
|
||||
* You may call this function, but outside of app_init, it should always return true.
|
||||
*/
|
||||
bool _watch_rtc_is_enabled();
|
||||
|
||||
/** @brief Sets the date and time.
|
||||
* @param date_time The date and time you wish to set, with a year value from 0-63 representing 2020-2083.
|
||||
* @note The SAM L22 stores the year as six bits representing a value from 0 to 63. It treats this as a year
|
||||
* offset from a reference year, which must be a leap year. Since 2020 was a leap year, and it allows
|
||||
* useful dates through 2083, it is assumed that watch apps will use 2020 as the reference year; thus
|
||||
* 1 means 2021, 2 means 2022, etc. **You will be responsible for handling this offset in your code**,
|
||||
* if the calendar year is needed for timestamp calculation logic or display purposes.
|
||||
*/
|
||||
void watch_rtc_set_date_time(watch_date_time date_time);
|
||||
|
||||
/** @brief Returns the date and time.
|
||||
* @return A watch_date_time with the current date and time, with a year value from 0-63 representing 2020-2083.
|
||||
* @see watch_rtc_set_date_time for notes about how the year is stored.
|
||||
*/
|
||||
watch_date_time watch_rtc_get_date_time();
|
||||
|
||||
/** @brief Registers an alarm callback that will be called when the RTC time matches the target time, as masked
|
||||
* by the provided mask.
|
||||
* @param callback The function you wish to have called when the alarm fires. If this value is NULL, the alarm
|
||||
* interrupt will still be enabled, but no callback function will be called.
|
||||
* @param alarm_time The time that you wish to match. The date is currently ignored.
|
||||
* @param mask One of the values in watch_rtc_alarm_match indicating which values to check.
|
||||
* @details The alarm interrupt is a versatile tool for scheduling events in the future, especially since it can
|
||||
* wake the device from all sleep modes. The key to its versatility is the mask parameter.
|
||||
* Suppose we set an alarm for midnight, 00:00:00.
|
||||
* * if mask is ALARM_MATCH_SS, the alarm will fire every minute when the clock ticks to seconds == 0.
|
||||
* * with ALARM_MATCH_MMSS, the alarm will once an hour, at the top of each hour.
|
||||
* * with ALARM_MATCH_HHMMSS, the alarm will fire at midnight every day.
|
||||
* In theory the SAM L22's alarm function can match on days, months and even years, but I have not had
|
||||
* success with this yet; as such, I am omitting these options for now.
|
||||
*/
|
||||
void watch_rtc_register_alarm_callback(ext_irq_cb_t callback, watch_date_time alarm_time, watch_rtc_alarm_match mask);
|
||||
|
||||
/** @brief Disables the alarm callback.
|
||||
*/
|
||||
void watch_rtc_disable_alarm_callback();
|
||||
|
||||
/** @brief Registers a "tick" callback that will be called once per second.
|
||||
* @param callback The function you wish to have called when the clock ticks. If you pass in NULL, the tick
|
||||
* interrupt will still be enabled, but no callback function will be called.
|
||||
* @note this is equivalent to calling watch_rtc_register_periodic_callback with a frequency of 1. It can be
|
||||
* disabled with either watch_rtc_disable_tick_callback() or watch_rtc_disable_periodic_callback(1),
|
||||
* and will also be disabled when watch_rtc_disable_all_periodic_callbacks is called.
|
||||
*/
|
||||
void watch_rtc_register_tick_callback(ext_irq_cb_t callback);
|
||||
|
||||
/** @brief Disables the tick callback for the given period.
|
||||
*/
|
||||
void watch_rtc_disable_tick_callback();
|
||||
|
||||
/** @brief Registers a callback that will be called at a configurable period.
|
||||
* @param callback The function you wish to have called at the specified period. If you pass in NULL, the periodic
|
||||
* interrupt will still be enabled, but no callback function will be called.
|
||||
* @param frequency The frequency of the tick in Hz. **Must be a power of 2**, from 1 to 128 inclusive.
|
||||
* @note A 1 Hz tick (@see watch_rtc_register_tick_callback) is suitable for most applications, in that it gives you a
|
||||
* chance to update the display once a second — an ideal update rate for a watch! If however you are displaying
|
||||
* a value (such as an accelerometer output) that updates more frequently than once per second, you may want to
|
||||
* tick at 16 or 32 Hz to update the screen more quickly. Just remember that the more frequent the tick, the more
|
||||
* power your app will consume. Ideally you should enable the fast tick only when the user requires it (i.e. in
|
||||
* response to an input event), and move back to the slow tick after some time.
|
||||
*
|
||||
* Also note that the RTC peripheral does not have sub-second resolution, so even if you set a 2 or 4 Hz interval,
|
||||
* the system will not have any way of telling you where you are within a given second; watch_rtc_get_date_time
|
||||
* will return the exact same timestamp until the second ticks over.
|
||||
*/
|
||||
void watch_rtc_register_periodic_callback(ext_irq_cb_t callback, uint8_t frequency);
|
||||
|
||||
/** @brief Disables the tick callback for the given period.
|
||||
* @param frequency The frequency of the tick you wish to disable, in Hz. **Must be a power of 2**, from 1 to 128.
|
||||
*/
|
||||
void watch_rtc_disable_periodic_callback(uint8_t frequency);
|
||||
|
||||
/** @brief Disables all periodic callbacks, including the once-per-second tick callback.
|
||||
*/
|
||||
void watch_rtc_disable_all_periodic_callbacks();
|
||||
|
||||
/** @brief Sets the system date and time.
|
||||
* @param date_time A struct representing the date and time you wish to set.
|
||||
*/
|
||||
__attribute__((deprecated("Use watch_rtc_set_date_time function instead")))
|
||||
void watch_set_date_time(struct calendar_date_time date_time);
|
||||
|
||||
/** @brief Returns the system date and time in the provided struct.
|
||||
* @param date_time A pointer to a calendar_date_time struct.
|
||||
It will be populated with the correct date and time on return.
|
||||
* @param date_time A pointer to a calendar_date_time struct. It will have with the correct date and time on return.
|
||||
*/
|
||||
__attribute__((deprecated("Use the watch_rtc_get_date_time function instead")))
|
||||
void watch_get_date_time(struct calendar_date_time *date_time);
|
||||
|
||||
/** @brief Registers a "tick" callback that will be called once per second.
|
||||
* @param callback The function you wish to have called when the clock ticks.
|
||||
* @param callback The function you wish to have called when the clock ticks. If you pass in NULL, the tick
|
||||
* interrupt will still be enabled, but no callback function will be called.
|
||||
*/
|
||||
__attribute__((deprecated("Use the watch_rtc_register_tick_callback function instead")))
|
||||
void watch_register_tick_callback(ext_irq_cb_t callback);
|
||||
|
||||
/// @}
|
||||
#endif
|
||||
|
||||
@@ -22,6 +22,9 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "watch_slcd.h"
|
||||
#include "hpl_slcd_config.h"
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Segmented Display
|
||||
|
||||
@@ -112,8 +115,8 @@ static const uint8_t Character_Set[] =
|
||||
0b01010000, // r
|
||||
0b01101101, // s
|
||||
0b01111000, // t
|
||||
0b01100010, // u (appears as superscript to work in more positions)
|
||||
0b01100010, // v (appears as superscript to work in more positions)
|
||||
0b01100010, // u (appears in (u)pper half to work in more positions)
|
||||
0b00011100, // v (looks like u but in the lower half)
|
||||
0b10111110, // w (only works in position 0)
|
||||
0b01111110, // x
|
||||
0b01101110, // y
|
||||
@@ -147,6 +150,10 @@ static const uint32_t IndicatorSegments[6] = {
|
||||
SLCD_SEGID(1, 10), // WATCH_INDICATOR_LAP
|
||||
};
|
||||
|
||||
void _sync_slcd() {
|
||||
while (SLCD->SYNCBUSY.reg);
|
||||
}
|
||||
|
||||
void watch_enable_display() {
|
||||
SEGMENT_LCD_0_init();
|
||||
slcd_sync_enable(&SEGMENT_LCD_0);
|
||||
@@ -160,9 +167,23 @@ inline void watch_clear_pixel(uint8_t com, uint8_t seg) {
|
||||
slcd_sync_seg_off(&SEGMENT_LCD_0, SLCD_SEGID(com, seg));
|
||||
}
|
||||
|
||||
void watch_clear_display() {
|
||||
SLCD->SDATAL0.reg = 0;
|
||||
SLCD->SDATAL1.reg = 0;
|
||||
SLCD->SDATAL2.reg = 0;
|
||||
}
|
||||
|
||||
void watch_display_character(uint8_t character, uint8_t position) {
|
||||
// handle lowercase 7 if needed
|
||||
if (character == '7' && (position == 4 || position == 6)) character = '&';
|
||||
// special cases for positions 4 and 6
|
||||
if (position == 4 || position == 6) {
|
||||
if (character == '7') character = '&'; // "lowercase" 7
|
||||
if (character == 'v') character = 'u'; // bottom segment duplicated, so show in top half
|
||||
if (character == 'J') character = 'j'; // same
|
||||
} else if (position != 4 && position != 6) {
|
||||
if (character == 'u') character = 'v'; // we can use the bottom segment; move to lower half
|
||||
if (character == 'j') character = 'J'; // same but just display a normal J
|
||||
}
|
||||
if (position == 0) slcd_sync_seg_off(&SEGMENT_LCD_0, SLCD_SEGID(0, 15)); // clear funky ninth segment
|
||||
|
||||
uint64_t segmap = Segment_Map[position];
|
||||
uint64_t segdata = Character_Set[character - 0x20];
|
||||
@@ -181,7 +202,9 @@ void watch_display_character(uint8_t character, uint8_t position) {
|
||||
segmap = segmap >> 8;
|
||||
segdata = segdata >> 1;
|
||||
}
|
||||
if (character == 'T' && position == 1) slcd_sync_seg_on(&SEGMENT_LCD_0, SLCD_SEGID(1, 12));
|
||||
if (character == 'T' && position == 1) slcd_sync_seg_on(&SEGMENT_LCD_0, SLCD_SEGID(1, 12)); // add descender
|
||||
else if (position == 0 && (character == 'B' || character == 'D')) slcd_sync_seg_on(&SEGMENT_LCD_0, SLCD_SEGID(0, 15)); // add funky ninth segment
|
||||
else if (position == 0 && (character == 'B' || character == 'D' || character == '@')) slcd_sync_seg_on(&SEGMENT_LCD_0, SLCD_SEGID(0, 15)); // add funky ninth segment
|
||||
}
|
||||
|
||||
void watch_display_string(char *string, uint8_t position) {
|
||||
@@ -216,3 +239,51 @@ void watch_clear_all_indicators() {
|
||||
slcd_sync_seg_off(&SEGMENT_LCD_0, SLCD_SEGID(0, 16));
|
||||
slcd_sync_seg_off(&SEGMENT_LCD_0, SLCD_SEGID(1, 10));
|
||||
}
|
||||
|
||||
void watch_start_character_blink(char character, uint32_t duration) {
|
||||
SLCD->CTRLD.bit.FC0EN = 0;
|
||||
_sync_slcd();
|
||||
|
||||
if (duration <= SLCD_FC_BYPASS_MAX_MS) {
|
||||
SLCD->FC0.reg = SLCD_FC0_PB | ((duration / (1000 / SLCD_FRAME_FREQUENCY)) - 1);
|
||||
} else {
|
||||
SLCD->FC0.reg = (((duration / (1000 / SLCD_FRAME_FREQUENCY)) / 8 - 1));
|
||||
}
|
||||
SLCD->CTRLD.bit.FC0EN = 1;
|
||||
|
||||
watch_display_character(character, 7);
|
||||
watch_clear_pixel(2, 10); // clear segment B of position 7 since it can't blink
|
||||
|
||||
SLCD->CTRLD.bit.BLINK = 0;
|
||||
SLCD->CTRLA.bit.ENABLE = 0;
|
||||
_sync_slcd();
|
||||
|
||||
SLCD->BCFG.bit.BSS0 = 0x07;
|
||||
SLCD->BCFG.bit.BSS1 = 0x07;
|
||||
|
||||
SLCD->CTRLD.bit.BLINK = 1;
|
||||
_sync_slcd();
|
||||
SLCD->CTRLA.bit.ENABLE = 1;
|
||||
_sync_slcd();
|
||||
}
|
||||
|
||||
void watch_stop_blink() {
|
||||
SLCD->CTRLD.bit.FC0EN = 0;
|
||||
SLCD->CTRLD.bit.BLINK = 0;
|
||||
}
|
||||
|
||||
void watch_start_tick_animation(uint32_t duration) {
|
||||
watch_display_character(' ', 8);
|
||||
const uint32_t segs[] = { SLCD_SEGID(0, 2)};
|
||||
slcd_sync_start_animation(&SEGMENT_LCD_0, segs, 1, duration);
|
||||
}
|
||||
|
||||
bool watch_tick_animation_is_running() {
|
||||
return hri_slcd_get_CTRLD_CSREN_bit(SLCD);
|
||||
}
|
||||
|
||||
void watch_stop_tick_animation() {
|
||||
const uint32_t segs[] = { SLCD_SEGID(0, 2)};
|
||||
slcd_sync_stop_animation(&SEGMENT_LCD_0, segs, 1);
|
||||
watch_display_character(' ', 8);
|
||||
}
|
||||
|
||||
@@ -21,8 +21,12 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#ifndef _WATCH_SLCD_H_INCLUDED
|
||||
#define _WATCH_SLCD_H_INCLUDED
|
||||
////< @file watch_slcd.h
|
||||
|
||||
#include "watch.h"
|
||||
|
||||
/** @addtogroup slcd Segment LCD Display
|
||||
* @brief This section covers functions related to the Segment LCD display driver, which is responsible
|
||||
* for displaying strings of characters and indicators on the main watch display.
|
||||
@@ -65,6 +69,10 @@ void watch_set_pixel(uint8_t com, uint8_t seg);
|
||||
*/
|
||||
void watch_clear_pixel(uint8_t com, uint8_t seg);
|
||||
|
||||
/** @brief Clears all segments of the display, including incicators and the colon.
|
||||
*/
|
||||
void watch_clear_display();
|
||||
|
||||
/** @brief Displays a string at the given position, starting from the top left. There are ten digits.
|
||||
A space in any position will clear that digit.
|
||||
* @param string A null-terminated string.
|
||||
@@ -99,4 +107,45 @@ void watch_clear_indicator(WatchIndicatorSegment indicator);
|
||||
*/
|
||||
void watch_clear_all_indicators();
|
||||
|
||||
/** @brief Blinks a single character in position 7. Does not affect other positions.
|
||||
* @details Six of the seven segments in position 7 (and only position 7) are capable of autonomous
|
||||
* blinking. This blinking does not require any CPU resources, and will continue even in
|
||||
* STANDBY and Sleep mode (but not Deep Sleep mode, since that mode turns off the LCD).
|
||||
* @param character The character you wish to blink.
|
||||
* @param duration The duration of the on/off cycle in milliseconds, from 50 to ~4250 ms.
|
||||
* @note Segment B of position 7 cannot blink autonomously, so not all characters will work well.
|
||||
* Supported characters for blinking:
|
||||
* * Punctuation: underscore, apostrophe, comma, hyphen, equals sign, tilde (top segment only)
|
||||
* * Numbers: 5, 6, ampersand (lowercase 7)
|
||||
* * Letters: b, C, c, E, F, h, i, L, l, n, o, S, t
|
||||
*/
|
||||
void watch_start_character_blink(char character, uint32_t duration);
|
||||
|
||||
/** @brief Stops and clears all blinking segments.
|
||||
* @details This will stop all blinking in position 7, and clear all segments in that digit.
|
||||
*/
|
||||
void watch_stop_blink();
|
||||
|
||||
/** @brief Begins a two-segment "tick-tock" animation in position 8.
|
||||
* @details Six of the seven segments in position 8 (and only position 8) are capable of autonomous
|
||||
* animation. This animation is very basic, and consists of moving a bit pattern forward
|
||||
* or backward in a shift register whose positions map to fixed segments on the LCD. Given
|
||||
* this constraint, an animation across all six segments does not make sense; so the watch
|
||||
* library offers only a simple "tick/tock" in segments D and E. This animation does not
|
||||
* require any CPU resources, and will continue even in STANDBY and Sleep mode (but not Deep
|
||||
* Sleep mode, since that mode turns off the LCD).
|
||||
* @param duration The duration of each frame in ms. 500 milliseconds produces a classic tick/tock.
|
||||
*/
|
||||
void watch_start_tick_animation(uint32_t duration);
|
||||
|
||||
/** @brief Checks if the tick animation is currently running.
|
||||
* @return true if the animation is running; false otherwise.
|
||||
*/
|
||||
bool watch_tick_animation_is_running();
|
||||
|
||||
/** @brief Stops the tick/tock animation and clears all animating segments.
|
||||
* @details This will stop the animation and clear all segments in position 8.
|
||||
*/
|
||||
void watch_stop_tick_animation();
|
||||
/// @}
|
||||
#endif
|
||||
|
||||
@@ -50,6 +50,7 @@
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "watch_uart.h"
|
||||
#include "peripheral_clk_config.h"
|
||||
|
||||
void watch_enable_debug_uart(uint32_t baud) {
|
||||
|
||||
@@ -21,8 +21,12 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#ifndef _WATCH_UART_H_INCLUDED
|
||||
#define _WATCH_UART_H_INCLUDED
|
||||
////< @file watch_uart.h
|
||||
|
||||
#include "watch.h"
|
||||
|
||||
/** @addtogroup debug Debug UART
|
||||
* @brief This section covers functions related to the debug UART, available on
|
||||
* pin D1 of the 9-pin connector.
|
||||
@@ -51,3 +55,4 @@ void watch_debug_putc(char c);
|
||||
__attribute__((deprecated("Use printf to log debug messages over USB.")))
|
||||
void watch_debug_puts(char *s);
|
||||
/// @}
|
||||
#endif
|
||||
|
||||
35
watch-library/watch/watch_utility.c
Normal file
35
watch-library/watch/watch_utility.c
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2021 Joey Castillo
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "watch_utility.h"
|
||||
|
||||
const char * watch_utility_get_weekday(watch_date_time date_time) {
|
||||
static const char weekdays[7][3] = {"SA", "SU", "MO", "TU", "WE", "TH", "FR"};
|
||||
date_time.unit.year += 20;
|
||||
if (date_time.unit.month <= 2) {
|
||||
date_time.unit.month += 12;
|
||||
date_time.unit.year--;
|
||||
}
|
||||
return weekdays[(date_time.unit.day + 13 * (date_time.unit.month + 1) / 5 + date_time.unit.year + date_time.unit.year / 4 + 525) % 7];
|
||||
}
|
||||
41
watch-library/watch/watch_utility.h
Normal file
41
watch-library/watch/watch_utility.h
Normal file
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2021 Joey Castillo
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef _WATCH_UTILITY_H_INCLUDED
|
||||
#define _WATCH_UTILITY_H_INCLUDED
|
||||
////< @file watch_utility.h
|
||||
|
||||
#include "watch.h"
|
||||
|
||||
/** @addtogroup utility Utility Functions
|
||||
* @brief This section covers various useful functions that don't fit anywhere else.
|
||||
**/
|
||||
/// @{
|
||||
/** @brief Returns a two-letter weekday for the given timestamp, suitable for display
|
||||
* in positions 0-1 of the watch face
|
||||
* @param date_time The watch_date_time whose weekday you want.
|
||||
*/
|
||||
const char * watch_utility_get_weekday(watch_date_time date_time);
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user