mbed library sources. Supersedes mbed-src.
Dependents: Nucleo_Hello_Encoder BLE_iBeaconScan AM1805_DEMO DISCO-F429ZI_ExportTemplate1 ... more
targets/TARGET_Analog_Devices/TARGET_ADUCM302X/TARGET_ADUCM3029/api/us_ticker.c
- Committer:
- AnnaBridge
- Date:
- 2019-02-20
- Revision:
- 189:f392fc9709a3
- Parent:
- 188:bcfe06ba3d64
File content as of revision 189:f392fc9709a3:
/******************************************************************************* * Copyright (c) 2010-2018 Analog Devices, Inc. * * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * - 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. * - Modified versions of the software must be conspicuously marked as such. * - This software is licensed solely and exclusively for use with processors * manufactured by or for Analog Devices, Inc. * - This software may not be combined or merged with other code in any manner * that would cause the software to become subject to terms and conditions * which differ from those listed here. * - Neither the name of Analog Devices, Inc. nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * - The use of this software may or may not infringe the patent rights of one * or more patent holders. This license does not release you from the * requirement that you obtain separate licenses from these patent holders * to use this software. * * THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES, INC. AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, NON- * INFRINGEMENT, TITLE, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL ANALOG DEVICES, INC. OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, PUNITIVE OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, DAMAGES ARISING OUT OF * CLAIMS OF INTELLECTUAL PROPERTY RIGHTS INFRINGEMENT; 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. ******************************************************************************/ #include <stdint.h> #include <stdio.h> #include <drivers/tmr/adi_tmr.h> #include <drivers/pwr/adi_pwr.h> #include <drivers/gpio/adi_gpio.h> #ifndef BITM_TMR_RGB_CTL_EN #define BITM_TMR_RGB_CTL_EN BITM_TMR_CTL_EN #endif typedef uint32_t timestamp_t; // defined in mbed_us_ticker_api.c which calls the ticker_irq_handler() routine // defined in mbed_ticker_api.c void us_ticker_irq_handler(void); static int us_ticker_inited = 0; static ADI_TMR_CONFIG tmrConfig; static volatile uint32_t Upper_count = 0, largecnt = 0; static ADI_TMR_TypeDef * adi_tmr_registers[ADI_TMR_DEVICE_NUM] = {pADI_TMR0, pADI_TMR1, pADI_TMR2}; #if defined(__ADUCM302x__) static const IRQn_Type adi_tmr_interrupt[ADI_TMR_DEVICE_NUM] = {TMR0_EVT_IRQn, TMR1_EVT_IRQn, TMR2_EVT_IRQn}; #elif defined(__ADUCM4x50__) static const IRQn_Type adi_tmr_interrupt[ADI_TMR_DEVICE_NUM] = {TMR0_EVT_IRQn, TMR1_EVT_IRQn, TMR2_EVT_IRQn, TMR_RGB_EVT_IRQn}; #else #error TMR is not ported for this processor #endif /*---------------------------------------------------------------------------* Local functions *---------------------------------------------------------------------------*/ static void GP1CallbackFunction(void *pCBParam, uint32_t Event, void * pArg) { Upper_count++; } static uint32_t get_current_time(void) { uint16_t tmrcnt0, tmrcnt1; uint32_t totaltmr0, totaltmr1; uint32_t uc1, tmrpend0, tmrpend1; do { volatile uint32_t *ucptr = &Upper_count; /* * Carefully coded to prevent race conditions. Do not make changes unless you understand all the * implications. * * Note this function can be called with interrupts globally disabled or enabled. It has been coded to work in both cases. * * TMR0 and TMR1 both run from the same synchronous clock. TMR0 runs at 26MHz and TMR1 runs at 26/256MHz. * TMR1 generates an interrupt every time it overflows its 16 bit counter. TMR0 runs faster and provides * the lowest 8 bits of the current time count. When TMR0 and TMR1 are combined, they provide 24 bits of * timer precision. i.e. (TMR0.CURCNT & 0xff) + (TMR1.CURCNT << 8) * * There are several race conditions protected against: * 1. TMR0 and TMR1 are both read at the same time, however, on rare occasions, one will have incremented before the other. * Therefore we read both timer counters, and check if the middle 8 bits match, if they don't then read the counts again * until they do. This ensures that one or the other counters are stable with respect to each other. * * 2. TMR1.CURCNT and Upper_count racing. Prevent this by disabling the TMR1 interrupt, which stops Upper_count increment interrupt (GP1CallbackFunction). * Then check pending bit of TMR1 to see if we missed Upper_count interrupt, and add it manually later. * * 3. Race between the TMR1 pend, and the TMR1.CURCNT read. Even with TMR1 interrupt disabled, the pend bit * may be set while TMR1.CURCNT is being read. We don't know if the pend bit matches the TMR1 state. * To prevent this, the pending bit is read twice, and we see if it matches; if it doesn't, loop around again. * * Note the TMR1 interrupt is enabled on each iteration of the loop to flush out any pending TMR1 interrupt, * thereby clearing any TMR1 pend's. This have no effect if this routine is called with interrupts globally disabled. */ NVIC_DisableIRQ(adi_tmr_interrupt[ADI_TMR_DEVICE_GP1]); // Prevent Upper_count increment tmrpend0 = NVIC_GetPendingIRQ(adi_tmr_interrupt[ADI_TMR_DEVICE_GP1]); // Check if there is a pending interrupt for timer 1 __DMB(); // memory barrier: read GP0 before GP1 tmrcnt0 = adi_tmr_registers[ADI_TMR_DEVICE_GP0]->CURCNT; // to minimize skew, read both timers manually __DMB(); // memory barrier: read GP0 before GP1 tmrcnt1 = adi_tmr_registers[ADI_TMR_DEVICE_GP1]->CURCNT; // read both timers manually totaltmr0 = tmrcnt0; // expand to u32 bits totaltmr1 = tmrcnt1; // expand to u32 bits tmrcnt0 &= 0xff00u; tmrcnt1 <<= 8; __DMB(); uc1 = *ucptr; // Read Upper_count tmrpend1 = NVIC_GetPendingIRQ(adi_tmr_interrupt[ADI_TMR_DEVICE_GP1]); // Check for a pending interrupt again. Only leave loop if they match NVIC_EnableIRQ(adi_tmr_interrupt[ADI_TMR_DEVICE_GP1]); // enable interrupt on every loop to allow TMR1 interrupt to run } while ((tmrcnt0 != tmrcnt1) || (tmrpend0 != tmrpend1)); totaltmr1 <<= 8; // Timer1 runs 256x slower totaltmr1 += totaltmr0 & 0xffu; // Use last 8 bits of Timer0 as it runs faster // totaltmr1 now contain 24 bits of significance if (tmrpend0) { // If an interrupt is pending, then increment local copy of upper count uc1++; } uint64_t Uc = totaltmr1; // expand out to 64 bits unsigned Uc += ((uint64_t) uc1) << 24; // Add on the upper count to get the full precision count // Divide Uc by 26 (26MHz converted to 1MHz) todo scale for other clock freqs Uc *= 1290555u; // Divide total(1/26) << 25 Uc >>= 25; // shift back. Fixed point avoid use of floating point divide. // Compiler does this inline using shifts and adds. return Uc; } static void calc_event_counts(uint32_t timestamp) { uint32_t calc_time, blocks, offset; uint64_t aa; calc_time = get_current_time(); offset = timestamp - calc_time; // offset in useconds if (offset > 0xf0000000u) // if offset is a really big number, assume that timer has already expired (i.e. negative) offset = 0u; if (offset > 10u) { // it takes 10us to user timer routine after interrupt. Offset timer to account for that. offset -= 10u; } else offset = 0u; aa = (uint64_t) offset; aa *= 26u; // convert from 1MHz to 26MHz clock. todo scale for other clock freqs blocks = aa >> 7; blocks++; // round largecnt = blocks>>1; // communicate to event_timer() routine } static void event_timer() { if (largecnt) { uint32_t cnt = largecnt; if (cnt > 65535u) { cnt = 0u; } else { cnt = 65536u - cnt; } tmrConfig.nLoad = cnt; tmrConfig.nAsyncLoad = cnt; adi_tmr_ConfigTimer(ADI_TMR_DEVICE_GP2, &tmrConfig); adi_tmr_Enable(ADI_TMR_DEVICE_GP2, true); } else { tmrConfig.nLoad = 65535u; tmrConfig.nAsyncLoad = 65535u; adi_tmr_ConfigTimer(ADI_TMR_DEVICE_GP2, &tmrConfig); adi_tmr_Enable(ADI_TMR_DEVICE_GP2, true); } } /* * Interrupt routine for timer 2 * * largecnt counts how many timer ticks should be counted to reach timer event. * Each interrupt happens every 65536 timer ticks, unless there are less than 65536 ticks to count. * In that case do the remaining timers ticks. * * largecnt is a global that is used to communicate between event_timer and the interrupt routine * On entry, largecnt will be any value larger than 0. */ static void GP2CallbackFunction(void *pCBParam, uint32_t Event, void * pArg) { if (largecnt >= 65536u) { largecnt -= 65536u; } else { largecnt = 0; } if (largecnt < 65536u) { adi_tmr_Enable(ADI_TMR_DEVICE_GP2, false); if (largecnt) { event_timer(); } else { us_ticker_irq_handler(); } } } /*---------------------------------------------------------------------------* us_ticker HAL APIs *---------------------------------------------------------------------------*/ void us_ticker_init(void) { if (us_ticker_inited) { // Disable ticker interrupt on reinitialization adi_tmr_Enable(ADI_TMR_DEVICE_GP2, false); return; } us_ticker_inited = 1; /*--------------------- GP TIMER INITIALIZATION --------------------------*/ /* Set up GP0 callback function */ adi_tmr_Init(ADI_TMR_DEVICE_GP0, NULL, NULL, false); /* Set up GP1 callback function */ adi_tmr_Init(ADI_TMR_DEVICE_GP1, GP1CallbackFunction, NULL, true); /* Set up GP1 callback function */ adi_tmr_Init(ADI_TMR_DEVICE_GP2, GP2CallbackFunction, NULL, true); /* Configure GP0 to run at 26MHz */ tmrConfig.bCountingUp = true; tmrConfig.bPeriodic = true; tmrConfig.ePrescaler = ADI_TMR_PRESCALER_1; // TMR0 at 26MHz tmrConfig.eClockSource = ADI_TMR_CLOCK_PCLK; // TMR source is PCLK (most examples use HFOSC) tmrConfig.nLoad = 0; tmrConfig.nAsyncLoad = 0; tmrConfig.bReloading = false; tmrConfig.bSyncBypass = true; // Allow x1 prescale: requires PCLK as a clk adi_tmr_ConfigTimer(ADI_TMR_DEVICE_GP0, &tmrConfig); /* Configure GP1 to have a period 256 times longer than GP0 */ tmrConfig.nLoad = 0; tmrConfig.nAsyncLoad = 0; tmrConfig.ePrescaler = ADI_TMR_PRESCALER_256; // TMR1 = 26MHz/256 adi_tmr_ConfigTimer(ADI_TMR_DEVICE_GP1, &tmrConfig); /* Configure GP2 for doing event counts */ tmrConfig.bCountingUp = true; tmrConfig.bPeriodic = true; tmrConfig.ePrescaler = ADI_TMR_PRESCALER_256; // TMR2 at 26MHz/256 tmrConfig.eClockSource = ADI_TMR_CLOCK_PCLK; // TMR source is PCLK (most examples use HFOSC) tmrConfig.nLoad = 0; tmrConfig.nAsyncLoad = 0; tmrConfig.bReloading = false; tmrConfig.bSyncBypass = true; // Allow x1 prescale adi_tmr_ConfigTimer(ADI_TMR_DEVICE_GP2, &tmrConfig); /*------------------------- GP TIMER ENABLE ------------------------------*/ /* Manually enable both timers to get them started at the same time * */ adi_tmr_registers[ADI_TMR_DEVICE_GP0]->CTL |= (uint16_t) BITM_TMR_RGB_CTL_EN; adi_tmr_registers[ADI_TMR_DEVICE_GP1]->CTL |= (uint16_t) BITM_TMR_RGB_CTL_EN; } uint32_t us_ticker_read() { uint32_t curr_time; if (!us_ticker_inited) { us_ticker_init(); } curr_time = get_current_time(); return curr_time; } void us_ticker_disable_interrupt(void) { adi_tmr_Enable(ADI_TMR_DEVICE_GP2, false); } void us_ticker_clear_interrupt(void) { NVIC_ClearPendingIRQ(TMR2_EVT_IRQn); } void us_ticker_set_interrupt(timestamp_t timestamp) { // if timestamp is already past, do not set interrupt if ((timestamp + 10) <= us_ticker_read()) return; /* timestamp is when interrupt should fire. * * This MUST not be called if another timer event is currently enabled. * */ calc_event_counts(timestamp); // use timestamp to calculate largecnt to control number of timer interrupts tmrConfig.ePrescaler = ADI_TMR_PRESCALER_256; // TMR2 at 26MHz/256 event_timer(); // uses largecnt to initiate timer interrupts } /** Set pending interrupt that should be fired right away. * * The ticker should be initialized prior calling this function. * * This MUST not be called if another timer event is currently enabled. */ void us_ticker_fire_interrupt(void) { largecnt = 1; // set a minimal interval so interrupt fire immediately tmrConfig.ePrescaler = ADI_TMR_PRESCALER_1; // TMR2 at 26MHz/1 event_timer(); // enable the timer and interrupt } void us_ticker_free(void) { adi_tmr_Enable(ADI_TMR_DEVICE_GP2, false); } /* ** EOF */