Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Fork of USBDevice by
targets/TARGET_Silicon_Labs/src/em_usbtimer.c
- Committer:
- Patrickhalahan
- Date:
- 2018-05-11
- Revision:
- 72:e5c300b1df2f
- Parent:
- 71:53949e6131f6
File content as of revision 72:e5c300b1df2f:
/***************************************************************************//** * @file em_usbtimer.c * @brief USB protocol stack library, timer API. * @version 3.20.14 ******************************************************************************* * @section License * <b>(C) Copyright 2014 Silicon Labs, http://www.silabs.com</b> ******************************************************************************* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ******************************************************************************/ #include "em_device.h" #if defined( USB_PRESENT ) && ( USB_COUNT == 1 ) #include "em_usb.h" #if defined( USB_DEVICE ) || defined( USB_HOST ) #include "em_cmu.h" #include "em_timer.h" #include "em_usbtypes.h" #include "em_usbhal.h" #include "device_peripherals.h" /* * Use one HW timer to serve n software milisecond timers. * A timer is, when running, in a linked list of timers. * A given timers timeout period is the acculmulated timeout * of all timers preceeding it in the queue. * This makes timer start (linked list insertion) computing intensive, * but the checking of the queue at each tick very effective. * ______ ______ ______ * | | --->| | --->| | * head --> | | | | | | | | * |______|--- |______|--- |______|---/ NULL */ /** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */ #ifndef USB_TIMER #error HW platform must define the timer to use for USB #endif #if ( USB_TIMER == USB_TIMER0 ) && ( TIMER_COUNT >= 1 ) #define TIMER TIMER0 #define TIMER_CLK cmuClock_TIMER0 #define TIMER_IRQ TIMER0_IRQn #define TIMER_IRQHandler TIMER0_IRQHandler #elif ( USB_TIMER == USB_TIMER1 ) && ( TIMER_COUNT >= 2 ) #define TIMER TIMER1 #define TIMER_CLK cmuClock_TIMER1 #define TIMER_IRQ TIMER1_IRQn #define TIMER_IRQHandler TIMER1_IRQHandler #elif ( USB_TIMER == USB_TIMER2 ) && ( TIMER_COUNT >= 3 ) #define TIMER TIMER2 #define TIMER_CLK cmuClock_TIMER2 #define TIMER_IRQ TIMER2_IRQn #define TIMER_IRQHandler TIMER2_IRQHandler #elif ( USB_TIMER == USB_TIMER3 ) && ( TIMER_COUNT == 4 ) #define TIMER TIMER3 #define TIMER_CLK cmuClock_TIMER3 #define TIMER_IRQ TIMER3_IRQn #define TIMER_IRQHandler TIMER3_IRQHandler #else #error "Illegal USB TIMER definition" #endif typedef struct _timer { uint32_t timeout; /* Delta value relative to prev. timer */ struct _timer *next; USBTIMER_Callback_TypeDef callback; bool running; } USBTIMER_Timer_TypeDef; #if ( NUM_QTIMERS > 0 ) static USBTIMER_Timer_TypeDef timers[ NUM_QTIMERS ]; static USBTIMER_Timer_TypeDef *head = NULL; #endif static uint32_t ticksPrMs, ticksPr1us, ticksPr10us, ticksPr100us; #if ( NUM_QTIMERS > 0 ) static void TimerTick( void ); void TIMER_IRQHandler( void ) { uint32_t flags; flags = TIMER_IntGet( TIMER ); if ( flags & TIMER_IF_CC0 ) { TIMER_IntClear( TIMER, TIMER_IFC_CC0 ); TIMER_CompareSet( TIMER, 0, TIMER_CaptureGet( TIMER, 0 ) + ticksPrMs ); TimerTick(); } } #endif /* ( NUM_QTIMERS > 0 ) */ static void DelayTicks( uint16_t ticks ) { uint16_t startTime; volatile uint16_t now; if ( ticks ) { startTime = TIMER_CounterGet( TIMER ); do { now = TIMER_CounterGet(TIMER); } while ( (uint16_t)( now - startTime ) < ticks ); } } /** @endcond */ /** @addtogroup USB_COMMON * @{*/ /***************************************************************************//** * @brief * Active wait millisecond delay function. Can also be used inside * interrupt handlers. * * @param[in] msec * Number of milliseconds to wait. ******************************************************************************/ void USBTIMER_DelayMs( uint32_t msec ) { uint64_t totalTicks; totalTicks = (uint64_t)ticksPrMs * msec; while ( totalTicks > 20000 ) { DelayTicks( 20000 ); totalTicks -= 20000; } DelayTicks( (uint16_t)totalTicks ); } /***************************************************************************//** * @brief * Active wait microsecond delay function. Can also be used inside * interrupt handlers. * * @param[in] usec * Number of microseconds to wait. ******************************************************************************/ void USBTIMER_DelayUs( uint32_t usec ) { uint64_t totalTicks; totalTicks = (uint64_t)ticksPr1us * usec; if ( totalTicks == 0 ) { usec /= 10; totalTicks = (uint64_t)ticksPr10us * usec; if ( totalTicks == 0 ) { usec /= 10; totalTicks = (uint64_t)ticksPr100us * usec; } } while ( totalTicks > 60000 ) { DelayTicks( 60000 ); totalTicks -= 60000; } DelayTicks( (uint16_t)totalTicks ); } /***************************************************************************//** * @brief * Activate the hardware timer used to pace the 1 millisecond timer system. * * @details * Call this function whenever the HFPERCLK frequency is changed. * This function is initially called by HOST and DEVICE stack xxxx_Init() * functions. ******************************************************************************/ void USBTIMER_Init( void ) { uint32_t freq; TIMER_Init_TypeDef timerInit = TIMER_INIT_DEFAULT; TIMER_InitCC_TypeDef timerCCInit = TIMER_INITCC_DEFAULT; freq = CMU_ClockFreqGet( cmuClock_HFPER ); ticksPrMs = ( freq + 500 ) / 1000; ticksPr1us = ( freq + 500000 ) / 1000000; ticksPr10us = ( freq + 50000 ) / 100000; ticksPr100us = ( freq + 5000 ) / 10000; timerCCInit.mode = timerCCModeCompare; CMU_ClockEnable( TIMER_CLK, true ); TIMER_TopSet( TIMER, 0xFFFF ); TIMER_InitCC( TIMER, 0, &timerCCInit ); TIMER_Init( TIMER, &timerInit ); #if ( NUM_QTIMERS > 0 ) TIMER_IntClear( TIMER, 0xFFFFFFFF ); TIMER_IntEnable( TIMER, TIMER_IEN_CC0 ); TIMER_CompareSet( TIMER, 0, TIMER_CounterGet( TIMER ) + ticksPrMs ); NVIC_ClearPendingIRQ( TIMER_IRQ ); NVIC_EnableIRQ( TIMER_IRQ ); #endif /* ( NUM_QTIMERS > 0 ) */ } #if ( NUM_QTIMERS > 0 ) || defined( DOXY_DOC_ONLY ) /***************************************************************************//** * @brief * Start a timer. * * @details * If the timer is already running, it will be restarted with new timeout. * * @param[in] id * Timer id (0..). * * @param[in] timeout * Number of milliseconds before timer will elapse. * * @param[in] callback * Function to be called on timer elapse, ref. @ref USBTIMER_Callback_TypeDef. ******************************************************************************/ void USBTIMER_Start( uint32_t id, uint32_t timeout, USBTIMER_Callback_TypeDef callback ) { uint32_t accumulated; USBTIMER_Timer_TypeDef *this, **last; INT_Disable(); if ( timers[ id ].running ) { USBTIMER_Stop( id ); } if ( timeout == 0 ) { callback(); INT_Enable(); return; } timers[ id ].running = true; timers[ id ].callback = callback; timers[ id ].next = NULL; if ( !head ) /* Queue empty ? */ { timers[ id ].timeout = timeout; head = &timers[ id ]; } else { this = head; last = &head; accumulated = 0; /* Do a sorted insert */ while ( this ) { if ( timeout < accumulated + this->timeout ) /* Insert before "this" ? */ { timers[ id ].timeout = timeout - accumulated; timers[ id ].next = this; *last = &timers[ id ]; this->timeout -= timers[ id ].timeout; /* Adjust timeout */ break; } else if ( this->next == NULL ) /* At end of queue ? */ { timers[ id ].timeout = timeout - accumulated - this->timeout; this->next = &timers[ id ]; break; } accumulated += this->timeout; last = &this->next; this = this->next; } } INT_Enable(); } /***************************************************************************//** * @brief * Stop a timer. * * @param[in] id * Timer id (0..). ******************************************************************************/ void USBTIMER_Stop( uint32_t id ) { USBTIMER_Timer_TypeDef *this, **last; INT_Disable(); if ( head ) /* Queue empty ? */ { this = head; last = &head; timers[ id ].running = false; while ( this ) { if ( this == &timers[ id ] ) /* Correct timer ? */ { if ( this->next ) { this->next->timeout += timers[ id ].timeout; /* Adjust timeout */ } *last = this->next; break; } last = &this->next; this = this->next; } } INT_Enable(); } #endif /* ( NUM_QTIMERS > 0 ) */ /** @} (end addtogroup USB_COMMON) */ #if ( NUM_QTIMERS > 0 ) /** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */ static void TimerTick( void ) { USBTIMER_Callback_TypeDef cb; INT_Disable(); if ( head ) { head->timeout--; while ( head ) { if ( head->timeout == 0 ) { cb = head->callback; head->running = false; head = head->next; /* The callback may place new items in the queue !!! */ if ( cb ) { (cb)(); } continue; /* There might be more than one timeout pr. tick */ } break; } } INT_Enable(); } /** @endcond */ #endif /* ( NUM_QTIMERS > 0 ) */ #endif /* defined( USB_DEVICE ) || defined( USB_HOST ) */ #endif /* defined( USB_PRESENT ) && ( USB_COUNT == 1 ) */