LoRaMAC-node LoRaWAN 定时器的移植 (二)

因为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 */

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值