因为ST的移植比价简单就不提了,他是基于日历和闹钟实现,我们现在主要分析下saml21的定时器实现,它是一个很普通的rtc定时器很有代表性,其他平台可以参考
我在代码里写了详细的备注大家可以去分析
/*!
* \file rtc-board.c
*
* \brief Target board RTC timer and low power modes management
*
* \copyright Revised BSD License, see section \ref LICENSE.
*
* \code
* ______ _
* / _____) _ | |
* ( (____ _____ ____ _| |_ _____ ____| |__
* \____ \| ___ | (_ _) ___ |/ ___) _ \
* _____) ) ____| | | || |_| ____( (___| | | |
* (______/|_____)_|_|_| \__)_____)\____)_| |_|
* (C)2013-2017 Semtech
*
* \endcode
*
* \author Miguel Luis ( Semtech )
*
* \author Gregory Cristian ( Semtech )
*
* \author Marten Lootsma(TWTG) on behalf of Microchip/Atmel (c)2017
*/
#include <hal_init.h>
#include <hw_timer.h>
#include "board-config.h"
#include "board.h"
#include "timer.h"
#include "systime.h"
#include "gpio.h"
#include "rtc-board.h"
#define RTC_DEBUG_ENABLE 1
#define RTC_DEBUG_DISABLE 0
#define RTC_DEBUG_GPIO_STATE RTC_DEBUG_DISABLE
#define RTC_DEBUG_PRINTF_STATE RTC_DEBUG_DISABLE
#define MIN_ALARM_DELAY 3 // in ticks
/*!
* \brief Indicates if the RTC is already Initialized or not
*/
static bool RtcInitialized = false;
static volatile bool RtcTimeoutPendingInterrupt = false;
static volatile bool RtcTimeoutPendingPolling = false;
typedef enum AlarmStates_e
{
ALARM_STOPPED = 0,
ALARM_RUNNING = !ALARM_STOPPED
} AlarmStates_t;
/*!
* RTC timer context
*/
typedef struct
{
uint32_t Time; // Reference time
uint32_t Delay; // Reference Timeout duration
uint32_t AlarmState;
}RtcTimerContext_t;
/*!
* Keep the value of the RTC timer when the RTC alarm is set
* Set with the \ref RtcSetTimerContext function
* Value is kept as a Reference to calculate alarm
*/
static RtcTimerContext_t RtcTimerContext;
#if( RTC_DEBUG_GPIO_STATE == RTC_DEBUG_ENABLE )
Gpio_t DbgRtcPin0;
Gpio_t DbgRtcPin1;
#endif
/*!
* Used to store the Seconds and SubSeconds.
*
* WARNING: Temporary fix fix. Should use MCU NVM internal
* registers
*/
uint32_t RtcBkupRegisters[] = { 0, 0 };
/*!
* \brief Callback for the hw_timer when alarm expired
*/
static void RtcAlarmIrq( void );
/*!
* \brief Callback for the hw_timer when counter overflows
*/
static void RtcOverflowIrq( void );
void RtcInit( void )
{
if( RtcInitialized == false )
{
#if( RTC_DEBUG_GPIO_STATE == RTC_DEBUG_ENABLE )
GpioInit( &DbgRtcPin0, RTC_DBG_PIN_0, PIN_OUTPUT, PIN_PUSH_PULL, PIN_NO_PULL, 0 );
GpioInit( &DbgRtcPin1, RTC_DBG_PIN_1, PIN_OUTPUT, PIN_PUSH_PULL, PIN_NO_PULL, 0 );
#endif
// RTC timer
HwTimerInit( ); //初始化定时器
HwTimerAlarmSetCallback( RtcAlarmIrq ); //设置超时中断的回调函数
HwTimerOverflowSetCallback( RtcOverflowIrq ); //设置超时中断的溢出函数
RtcTimerContext.AlarmState = ALARM_STOPPED;
RtcSetTimerContext( );
RtcInitialized = true;
}
}
uint32_t RtcSetTimerContext( void ) //获取RTC寄存器中的计数值 这是目前实时更新的一个值
{
RtcTimerContext.Time = ( uint32_t )HwTimerGetTime( );
return ( uint32_t )RtcTimerContext.Time;
}
uint32_t RtcGetTimerContext( void ) //获取上一次 调用RtcSetTimerContext时 的那个值
{
return RtcTimerContext.Time;
}
uint32_t RtcGetMinimumTimeout( void )
{
return( MIN_ALARM_DELAY );
}
uint32_t RtcMs2Tick( TimerTime_t milliseconds ) // milliseconds/1000 / (1/1024)
{
return ( uint32_t )( ( ( ( uint64_t )milliseconds ) << 10 ) / 1000 );
}
TimerTime_t RtcTick2Ms( uint32_t tick ) //1 个tick 是 1/1024 S 这里1024 怎么来的 是因为我查了SAM的寄存器手册,RTC的频率就是1024 而不是常见的32.768 这里大家一定要注意
{
uint32_t seconds = tick >> 10; //这是除以1024的商
tick = tick & 0x3FF; //余数
return ( ( seconds * 1000 ) + ( ( tick * 1000 ) >> 10 ) ); //
}
void RtcDelayMs( TimerTime_t milliseconds )
{
uint32_t delayTicks = 0;
uint32_t refTicks = RtcGetTimerValue( );
delayTicks = RtcMs2Tick( milliseconds );
// Wait delay ms
while( ( ( RtcGetTimerValue( ) - refTicks ) ) < delayTicks )
{
__NOP( );
}
}
//设置超时时间 就是开启定时器
void RtcSetAlarm( uint32_t timeout )
{
RtcStartAlarm( timeout );
}
void RtcStopAlarm( void )
{
RtcTimerContext.AlarmState = ALARM_STOPPED;
}
void RtcStartAlarm( uint32_t timeout )
{
CRITICAL_SECTION_BEGIN( );
RtcStopAlarm( );
RtcTimerContext.Delay = timeout; // Delay 就是一个设置的超时值
#if( RTC_DEBUG_PRINTF_STATE == RTC_DEBUG_ENABLE )
printf( "TIMEOUT \t%010ld\t%010ld\n", RtcTimerContext.Time, RtcTimerContext.Delay );
#endif
#if( RTC_DEBUG_GPIO_STATE == RTC_DEBUG_ENABLE )
GpioWrite( &DbgRtcPin0, 0 );
GpioWrite( &DbgRtcPin1, 0 );
#endif
RtcTimeoutPendingInterrupt = true; //开启中断模式
RtcTimeoutPendingPolling = false; //轮询关闭
RtcTimerContext.AlarmState = ALARM_RUNNING;
if( HwTimerLoadAbsoluteTicks( RtcTimerContext.Time + RtcTimerContext.Delay ) == false ) //这个是一个选择轮询还是中断方式的一个函数 如果是flase就是轮询模式
// 我感觉如果是自己移植 是可以去掉这个判断的 ,直接设置寄存器的比较值就可以 开启比较中断
{
// If timer already passed
if( RtcTimeoutPendingInterrupt == true )
{
// And interrupt not handled, mark as polling
RtcTimeoutPendingPolling = true;
RtcTimeoutPendingInterrupt = false;
#if( RTC_DEBUG_GPIO_STATE == RTC_DEBUG_ENABLE )
GpioWrite( &DbgRtcPin0, 1 );
#endif
}
}
CRITICAL_SECTION_END( );
}
uint32_t RtcGetTimerValue( void )
{
return ( uint32_t )HwTimerGetTime( );
}
uint32_t RtcGetTimerElapsedTime( void )
{
return ( uint32_t)( HwTimerGetTime( ) - RtcTimerContext.Time );
}
uint32_t RtcGetCalendarTime( uint16_t *milliseconds )
{
uint32_t ticks = 0;
uint32_t calendarValue = HwTimerGetTime( );
uint32_t seconds = ( uint32_t )calendarValue >> 10;
ticks = ( uint32_t )calendarValue & 0x3FF;
*milliseconds = RtcTick2Ms( ticks );
return seconds;
}
void RtcBkupWrite( uint32_t data0, uint32_t data1 )
{
CRITICAL_SECTION_BEGIN( );
RtcBkupRegisters[0] = data0;
RtcBkupRegisters[1] = data1;
CRITICAL_SECTION_END( );
}
void RtcBkupRead( uint32_t* data0, uint32_t* data1 )
{
CRITICAL_SECTION_BEGIN( );
*data0 = RtcBkupRegisters[0];
*data1 = RtcBkupRegisters[1];
CRITICAL_SECTION_END( );
}
// 如果通过中断实现 其实轮询方式是可以去掉的 这个函数是要放在主循环中一直轮询
void RtcProcess( void )
{
CRITICAL_SECTION_BEGIN( );
if( ( RtcTimerContext.AlarmState == ALARM_RUNNING ) && ( RtcTimeoutPendingPolling == true ) )
{
if( RtcGetTimerElapsedTime( ) >= RtcTimerContext.Delay )
{
RtcTimerContext.AlarmState = ALARM_STOPPED;
// Because of one shot the task will be removed after the callback
RtcTimeoutPendingPolling = false;
#if( RTC_DEBUG_GPIO_STATE == RTC_DEBUG_ENABLE )
GpioWrite( &DbgRtcPin0, 0 );
GpioWrite( &DbgRtcPin1, 1 );
#endif
// NOTE: The handler should take less then 1 ms otherwise the clock shifts
TimerIrqHandler( );
#if( RTC_DEBUG_GPIO_STATE == RTC_DEBUG_ENABLE )
GpioWrite( &DbgRtcPin1, 0 );
#endif
}
}
CRITICAL_SECTION_END( );
}
TimerTime_t RtcTempCompensation( TimerTime_t period, float temperature )
{
return period;
}
static void RtcAlarmIrq( void )
{
RtcTimerContext.AlarmState = ALARM_STOPPED;
// Because of one shot the task will be removed after the callback
RtcTimeoutPendingInterrupt = false; //标记中断结束
#if( RTC_DEBUG_GPIO_STATE == RTC_DEBUG_ENABLE )
GpioWrite( &DbgRtcPin1, 1 );
#endif
// NOTE: The handler should take less then 1 ms otherwise the clock shifts
TimerIrqHandler( );
#if( RTC_DEBUG_GPIO_STATE == RTC_DEBUG_ENABLE )
GpioWrite( &DbgRtcPin1, 0 );
#endif
}
//溢出没有做处理
static void RtcOverflowIrq( void )
{
//RtcTimerContext.Time += ( uint64_t )( 1 << 32 );
}
后面这段和平台相关
可以根据不同去修改
/** * \file hw_timer.c * * \brief Wrapper used by sw_timer utility using ASF timer api's * * Copyright (C) 2016 Atmel Corporation. All rights reserved. * * \asf_license_start * * \page License * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * 3. The name of Atmel may not be used to endorse or promote products derived * from this software without specific prior written permission. * * 4. This software may only be redistributed and used in connection with an * Atmel microcontroller product. * * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * \asf_license_stop * * Modified for use with Atmel START hpl_rtc functions */ /**************************************** INCLUDES*****************************/ #include <stdint.h> #include <stdbool.h> #include <utils_assert.h> #include <utils.h> #include <hal_atomic.h> #include <hpl_irq.h> #include "board-config.h" #include "gpio.h" #include "hw_timer.h" /**************************************** MACROS*****************************/ //#define USE_HWTMR_DEBUG #define COMPARE_COUNT_MAX_VALUE ( uint32_t )( -1 ) /**************************************** GLOBALS*****************************/ HwTimerCallback_t HwTimerAlarmCallback = NULL; HwTimerCallback_t HwTimerOverflowCallback = NULL; #if defined( USE_HWTMR_DEBUG ) Gpio_t DbgHwTmrPin; #endif /************************************** IMPLEMENTATION************************/ /** * \brief Initializes the hw timer module */ void HwTimerInit(void) { #if defined( USE_HWTMR_DEBUG ) GpioInit( &DbgHwTmrPin, HWTMR_DBG_PIN_0, PIN_OUTPUT, PIN_PUSH_PULL, PIN_NO_PULL, 0 ); #endif hri_mclk_set_APBAMASK_RTC_bit(MCLK); //设置时钟 hri_rtcmode0_write_CTRLA_reg(RTC, RTC_MODE0_CTRLA_SWRST); hri_rtcmode0_wait_for_sync(RTC, RTC_MODE0_SYNCBUSY_SWRST); //等待同步 这个后面会经常出现 等待同步后才可以去读定时器的计数值 hri_rtcmode0_write_CTRLA_reg(RTC, RTC_MODE0_CTRLA_PRESCALER(0) | RTC_MODE0_CTRLA_COUNTSYNC); hri_rtcmode0_write_EVCTRL_reg(RTC, RTC_MODE0_EVCTRL_CMPEO0); hri_rtcmode0_write_COMP_reg(RTC, 0, ( uint32_t )COMPARE_COUNT_MAX_VALUE); hri_rtcmode0_set_INTEN_CMP0_bit(RTC); NVIC_EnableIRQ(RTC_IRQn); hri_rtcmode0_write_COUNT_reg(RTC, 0); hri_rtcmode0_wait_for_sync(RTC, RTC_MODE0_SYNCBUSY_COUNT); hri_rtcmode0_set_CTRLA_ENABLE_bit(RTC); } /** * \brief This function is used to set the callback when the hw timer * expires. * \param callback Callback to be registered */ void HwTimerAlarmSetCallback(HwTimerCallback_t callback) { HwTimerAlarmCallback = callback; } /** * \brief This function is used to set the callback when the hw timer * overflows. * \param callback Callback to be registered */ void HwTimerOverflowSetCallback(HwTimerCallback_t callback) { HwTimerOverflowCallback = callback; } /** * \brief Loads the timeout in terms of ticks into the hardware * \ticks Time value in terms of timer ticks */ bool HwTimerLoadAbsoluteTicks(uint32_t ticks) { #if defined( USE_HWTMR_DEBUG ) GpioWrite( &DbgHwTmrPin, 1 ); #endif RTC_CRITICAL_SECTION_ENTER(); //设置比较器的寄存器值 hri_rtcmode0_write_COMP_reg(RTC, 0, ticks); hri_rtcmode0_wait_for_sync(RTC, RTC_MODE0_SYNCBUSY_MASK); uint32_t current = hri_rtcmode0_read_COUNT_reg(RTC); RTC_CRITICAL_SECTION_LEAVE(); if((ticks - current - 1) >= (COMPARE_COUNT_MAX_VALUE >> 1)) { //唯一的问题是这里 没有理解 // if difference is more than half of max assume timer has passed return false; } if((ticks - current) < 10) { //时间太短采用轮询 // if too close the matching interrupt does not trigger, so handle same as passed return false; } return true; } /** * \brief Gets the absolute time value * \retval Absolute time in ticks */ uint32_t HwTimerGetTime(void) { hri_rtcmode0_wait_for_sync(RTC, RTC_MODE0_SYNCBUSY_COUNT); return hri_rtcmode0_read_COUNT_reg(RTC); } /** * \brief Disables the hw timer module */ void HwTimerDisable(void) { } /** * \brief Rtc interrupt handler */ void RTC_Handler(void) { /* Read and mask interrupt flag register */ uint16_t flag = hri_rtcmode0_read_INTFLAG_reg(RTC); if (flag & RTC_MODE0_INTFLAG_CMP0) { #if defined( USE_HWTMR_DEBUG ) GpioWrite( &DbgHwTmrPin, 0 ); #endif hri_rtcmode0_clear_interrupt_CMP0_bit(RTC); if (HwTimerAlarmCallback != NULL) { HwTimerAlarmCallback(); //进入定时器的中断回调 } /* Clear interrupt flag */ } else if ( flag & RTC_MODE0_INTFLAG_OVF) { hri_rtcmode0_clear_interrupt_OVF_bit(RTC); if (HwTimerOverflowCallback != NULL) { //进入定时器的溢出回调 HwTimerOverflowCallback(); } } } /* eof hw_timer.c */