USB device stack
Dependents: USBMSD_step1 USBMSD_step1_5 picossd_step1_2cs
targets/TARGET_Silicon_Labs/src/em_usbtimer.c
- Committer:
- Kojto
- Date:
- 2017-07-27
- Revision:
- 71:53949e6131f6
File content as of revision 71:53949e6131f6:
/***************************************************************************//** * @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 ) */