EFM32 Happy Gecko Software Documentation  efm32hg-doc-4.2.1
em_usbtimer.c
Go to the documentation of this file.
1 /***************************************************************************/
16 #include "em_device.h"
17 #if defined( USB_PRESENT ) && ( USB_COUNT == 1 )
18 #include "em_usb.h"
19 #if defined( USB_DEVICE ) || defined( USB_HOST )
20 
21 #include "em_cmu.h"
22 #include "em_timer.h"
23 #include "em_usbtypes.h"
24 #include "em_usbhal.h"
25 
26 /*
27  * Use one HW timer to serve n software milisecond timers.
28  * A timer is, when running, in a linked list of timers.
29  * A given timers timeout period is the acculmulated timeout
30  * of all timers preceeding it in the queue.
31  * This makes timer start (linked list insertion) computing intensive,
32  * but the checking of the queue at each tick very effective.
33  * ______ ______ ______
34  * | | --->| | --->| |
35  * head --> | | | | | | | |
36  * |______|--- |______|--- |______|---/ NULL
37  */
38 
41 #ifndef USB_TIMER
42 #define USB_TIMER USB_TIMER0
43 #endif
44 
45 #if ( USB_TIMER == USB_TIMER0 ) && ( TIMER_COUNT >= 1 )
46  #define TIMER TIMER0
47  #define TIMER_CLK cmuClock_TIMER0
48  #define TIMER_IRQ TIMER0_IRQn
49  #define TIMER_IRQHandler TIMER0_IRQHandler
50 
51 #elif ( USB_TIMER == USB_TIMER1 ) && ( TIMER_COUNT >= 2 )
52  #define TIMER TIMER1
53  #define TIMER_CLK cmuClock_TIMER1
54  #define TIMER_IRQ TIMER1_IRQn
55  #define TIMER_IRQHandler TIMER1_IRQHandler
56 
57 #elif ( USB_TIMER == USB_TIMER2 ) && ( TIMER_COUNT >= 3 )
58  #define TIMER TIMER2
59  #define TIMER_CLK cmuClock_TIMER2
60  #define TIMER_IRQ TIMER2_IRQn
61  #define TIMER_IRQHandler TIMER2_IRQHandler
62 
63 #elif ( USB_TIMER == USB_TIMER3 ) && ( TIMER_COUNT == 4 )
64  #define TIMER TIMER3
65  #define TIMER_CLK cmuClock_TIMER3
66  #define TIMER_IRQ TIMER3_IRQn
67  #define TIMER_IRQHandler TIMER3_IRQHandler
68 
69 #else
70 #error "Illegal USB TIMER definition"
71 #endif
72 
73 typedef struct _timer
74 {
75  uint32_t timeout; /* Delta value relative to prev. timer */
76  struct _timer *next;
78  bool running;
79 } USBTIMER_Timer_TypeDef;
80 
81 #if ( NUM_QTIMERS > 0 )
82 static USBTIMER_Timer_TypeDef timers[ NUM_QTIMERS ];
83 static USBTIMER_Timer_TypeDef *head = NULL;
84 #endif
85 
86 static uint32_t ticksPrMs, ticksPr1us, ticksPr10us, ticksPr100us;
87 
88 #if ( NUM_QTIMERS > 0 )
89 
90 static void TimerTick( void );
91 
92 void TIMER_IRQHandler( void )
93 {
94  uint32_t flags;
95 
96  flags = TIMER_IntGet( TIMER );
97 
98  if ( flags & TIMER_IF_CC0 )
99  {
100  TIMER_IntClear( TIMER, TIMER_IFC_CC0 );
101  TIMER_CompareSet( TIMER, 0, TIMER_CaptureGet( TIMER, 0 ) + ticksPrMs );
102  TimerTick();
103  }
104 }
105 #endif /* ( NUM_QTIMERS > 0 ) */
106 
107 static void DelayTicks( uint16_t ticks )
108 {
109  uint16_t startTime;
110  volatile uint16_t now;
111 
112  if ( ticks )
113  {
114  startTime = TIMER_CounterGet( TIMER );
115  do
116  {
117  now = TIMER_CounterGet(TIMER);
118  } while ( (uint16_t)( now - startTime ) < ticks );
119  }
120 }
121 
127 /***************************************************************************/
135 void USBTIMER_DelayMs( uint32_t msec )
136 {
137  uint64_t totalTicks;
138 
139  totalTicks = (uint64_t)ticksPrMs * msec;
140  while ( totalTicks > 20000 )
141  {
142  DelayTicks( 20000 );
143  totalTicks -= 20000;
144  }
145  DelayTicks( (uint16_t)totalTicks );
146 }
147 
148 /***************************************************************************/
156 void USBTIMER_DelayUs( uint32_t usec )
157 {
158  uint64_t totalTicks;
159 
160  totalTicks = (uint64_t)ticksPr1us * usec;
161  if ( totalTicks == 0 )
162  {
163  usec /= 10;
164  totalTicks = (uint64_t)ticksPr10us * usec;
165 
166  if ( totalTicks == 0 )
167  {
168  usec /= 10;
169  totalTicks = (uint64_t)ticksPr100us * usec;
170  }
171  }
172 
173  while ( totalTicks > 60000 )
174  {
175  DelayTicks( 60000 );
176  totalTicks -= 60000;
177  }
178  DelayTicks( (uint16_t)totalTicks );
179 }
180 
181 /***************************************************************************/
190 void USBTIMER_Init( void )
191 {
192  uint32_t freq;
195 
197  ticksPrMs = ( freq + 500 ) / 1000;
198  ticksPr1us = ( freq + 500000 ) / 1000000;
199  ticksPr10us = ( freq + 50000 ) / 100000;
200  ticksPr100us = ( freq + 5000 ) / 10000;
201 
202  timerCCInit.mode = timerCCModeCompare;
203  CMU_ClockEnable( TIMER_CLK, true );
204  TIMER_TopSet( TIMER, 0xFFFF );
205  TIMER_InitCC( TIMER, 0, &timerCCInit );
206  TIMER_Init( TIMER, &timerInit );
207 
208 #if ( NUM_QTIMERS > 0 )
209  TIMER_IntClear( TIMER, 0xFFFFFFFF );
210  TIMER_IntEnable( TIMER, TIMER_IEN_CC0 );
211  TIMER_CompareSet( TIMER, 0, TIMER_CounterGet( TIMER ) + ticksPrMs );
212  NVIC_ClearPendingIRQ( TIMER_IRQ );
213  NVIC_EnableIRQ( TIMER_IRQ );
214 #endif /* ( NUM_QTIMERS > 0 ) */
215 }
216 
217 #if ( NUM_QTIMERS > 0 ) || defined( DOXY_DOC_ONLY )
218 /***************************************************************************/
234 void USBTIMER_Start( uint32_t id, uint32_t timeout,
235  USBTIMER_Callback_TypeDef callback )
236 {
237  uint32_t accumulated;
238  USBTIMER_Timer_TypeDef *this, **last;
239 
240  INT_Disable();
241 
242  if ( timers[ id ].running )
243  {
244  USBTIMER_Stop( id );
245  }
246 
247  if ( timeout == 0 )
248  {
249  callback();
250  INT_Enable();
251  return;
252  }
253 
254  timers[ id ].running = true;
255  timers[ id ].callback = callback;
256  timers[ id ].next = NULL;
257 
258  if ( !head ) /* Queue empty ? */
259  {
260  timers[ id ].timeout = timeout;
261  head = &timers[ id ];
262  }
263  else
264  {
265  this = head;
266  last = &head;
267  accumulated = 0;
268 
269  /* Do a sorted insert */
270  while ( this )
271  {
272  if ( timeout < accumulated + this->timeout ) /* Insert before "this" ? */
273  {
274  timers[ id ].timeout = timeout - accumulated;
275  timers[ id ].next = this;
276  *last = &timers[ id ];
277  this->timeout -= timers[ id ].timeout; /* Adjust timeout */
278  break;
279  }
280  else if ( this->next == NULL ) /* At end of queue ? */
281  {
282  timers[ id ].timeout = timeout - accumulated - this->timeout;
283  this->next = &timers[ id ];
284  break;
285  }
286  accumulated += this->timeout;
287  last = &this->next;
288  this = this->next;
289  }
290  }
291 
292  INT_Enable();
293 }
294 
295 /***************************************************************************/
302 void USBTIMER_Stop( uint32_t id )
303 {
304  USBTIMER_Timer_TypeDef *this, **last;
305 
306  INT_Disable();
307 
308  if ( head ) /* Queue empty ? */
309  {
310  this = head;
311  last = &head;
312  timers[ id ].running = false;
313 
314  while ( this )
315  {
316  if ( this == &timers[ id ] ) /* Correct timer ? */
317  {
318  if ( this->next )
319  {
320  this->next->timeout += timers[ id ].timeout; /* Adjust timeout */
321  }
322  *last = this->next;
323  break;
324  }
325  last = &this->next;
326  this = this->next;
327  }
328  }
329 
330  INT_Enable();
331 }
332 #endif /* ( NUM_QTIMERS > 0 ) */
333 
336 #if ( NUM_QTIMERS > 0 )
337 
339 static void TimerTick( void )
340 {
342 
343  INT_Disable();
344 
345  if ( head )
346  {
347  head->timeout--;
348 
349  while ( head )
350  {
351  if ( head->timeout == 0 )
352  {
353  cb = head->callback;
354  head->running = false;
355  head = head->next;
356  /* The callback may place new items in the queue !!! */
357  if ( cb )
358  {
359  (cb)();
360  }
361  continue; /* There might be more than one timeout pr. tick */
362  }
363  break;
364  }
365  }
366 
367  INT_Enable();
368 }
370 #endif /* ( NUM_QTIMERS > 0 ) */
371 
372 #endif /* defined( USB_DEVICE ) || defined( USB_HOST ) */
373 #endif /* defined( USB_PRESENT ) && ( USB_COUNT == 1 ) */
Clock management unit (CMU) API.
__STATIC_INLINE void TIMER_TopSet(TIMER_TypeDef *timer, uint32_t val)
Set top value for timer.
Definition: em_timer.h:900
void USBTIMER_Stop(uint32_t id)
Stop a timer.
Definition: em_usbtimer.c:302
#define TIMER_INITCC_DEFAULT
Definition: em_timer.h:397
__STATIC_INLINE uint32_t INT_Enable(void)
Enable interrupts.
Definition: em_int.h:94
#define TIMER_IEN_CC0
Timer/counter (TIMER) peripheral API.
__STATIC_INLINE void TIMER_IntClear(TIMER_TypeDef *timer, uint32_t flags)
Clear one or more pending TIMER interrupts.
Definition: em_timer.h:713
CMSIS Cortex-M Peripheral Access Layer for Silicon Laboratories microcontroller devices.
TIMER_CCMode_TypeDef mode
Definition: em_timer.h:375
void(* USBTIMER_Callback_TypeDef)(void)
USBTIMER callback function.
Definition: em_usb.h:673
__STATIC_INLINE void TIMER_IntEnable(TIMER_TypeDef *timer, uint32_t flags)
Enable one or more TIMER interrupts.
Definition: em_timer.h:752
void USBTIMER_DelayMs(uint32_t msec)
Active wait millisecond delay function. Can also be used inside interrupt handlers.
Definition: em_usbtimer.c:135
#define TIMER_IFC_CC0
__STATIC_INLINE uint32_t TIMER_IntGet(TIMER_TypeDef *timer)
Get pending TIMER interrupt flags.
Definition: em_timer.h:772
#define TIMER_INIT_DEFAULT
Definition: em_timer.h:317
USB protocol stack library API for EFM32/EZR32.
void CMU_ClockEnable(CMU_Clock_TypeDef clock, bool enable)
Enable/disable a clock.
Definition: em_cmu.c:1369
#define TIMER_IF_CC0
__STATIC_INLINE void TIMER_CompareSet(TIMER_TypeDef *timer, unsigned int ch, uint32_t val)
Set compare value for compare/capture channel when operating in compare or PWM mode.
Definition: em_timer.h:563
__STATIC_INLINE uint32_t TIMER_CounterGet(TIMER_TypeDef *timer)
Get TIMER counter value.
Definition: em_timer.h:581
__STATIC_INLINE uint32_t TIMER_CaptureGet(TIMER_TypeDef *timer, unsigned int ch)
Get capture value for compare/capture channel when operating in capture mode.
Definition: em_timer.h:516
void TIMER_Init(TIMER_TypeDef *timer, const TIMER_Init_TypeDef *init)
Initialize TIMER.
Definition: em_timer.c:76
USB protocol stack library, low level USB peripheral access.
void USBTIMER_Init(void)
Activate the hardware timer used to pace the 1 millisecond timer system.
Definition: em_usbtimer.c:190
__STATIC_INLINE uint32_t INT_Disable(void)
Disable interrupts.
Definition: em_int.h:71
USB protocol stack library, internal type definitions.
void TIMER_InitCC(TIMER_TypeDef *timer, unsigned int ch, const TIMER_InitCC_TypeDef *init)
Initialize TIMER compare/capture channel.
Definition: em_timer.c:130
uint32_t CMU_ClockFreqGet(CMU_Clock_TypeDef clock)
Get clock frequency for a clock point.
Definition: em_cmu.c:1482
void USBTIMER_Start(uint32_t id, uint32_t timeout, USBTIMER_Callback_TypeDef callback)
Start a timer.
Definition: em_usbtimer.c:234
void USBTIMER_DelayUs(uint32_t usec)
Active wait microsecond delay function. Can also be used inside interrupt handlers.
Definition: em_usbtimer.c:156