USBDevice with Nucleo 32L476RG support
Dependents: ObCP_ENSMM_V2020_Test_Accelero
Diff: targets/TARGET_Silicon_Labs/src/em_usbtimer.c
- Revision:
- 71:53949e6131f6
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/targets/TARGET_Silicon_Labs/src/em_usbtimer.c Thu Jul 27 12:14:04 2017 +0100 @@ -0,0 +1,381 @@ +/***************************************************************************//** + * @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 ) */