]> git.sur5r.net Git - freertos/blob - FreeRTOS/Demo/CORTEX_M4F_ATSAM4E_Atmel_Studio/src/ASF/sam/drivers/tc/tc.c
Rename SAM4E demo directory to include the 'F' in 'M4F' - minor point for the sake...
[freertos] / FreeRTOS / Demo / CORTEX_M4F_ATSAM4E_Atmel_Studio / src / ASF / sam / drivers / tc / tc.c
1 /**\r
2  * \file\r
3  *\r
4  * \brief Timer Counter (TC) driver for SAM.\r
5  *\r
6  * Copyright (c) 2011-2013 Atmel Corporation. All rights reserved.\r
7  *\r
8  * \asf_license_start\r
9  *\r
10  * \page License\r
11  *\r
12  * Redistribution and use in source and binary forms, with or without\r
13  * modification, are permitted provided that the following conditions are met:\r
14  *\r
15  * 1. Redistributions of source code must retain the above copyright notice,\r
16  *    this list of conditions and the following disclaimer.\r
17  *\r
18  * 2. Redistributions in binary form must reproduce the above copyright notice,\r
19  *    this list of conditions and the following disclaimer in the documentation\r
20  *    and/or other materials provided with the distribution.\r
21  *\r
22  * 3. The name of Atmel may not be used to endorse or promote products derived\r
23  *    from this software without specific prior written permission.\r
24  *\r
25  * 4. This software may only be redistributed and used in connection with an\r
26  *    Atmel microcontroller product.\r
27  *\r
28  * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED\r
29  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF\r
30  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE\r
31  * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR\r
32  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\r
33  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\r
34  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\r
35  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,\r
36  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN\r
37  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\r
38  * POSSIBILITY OF SUCH DAMAGE.\r
39  *\r
40  * \asf_license_stop\r
41  *\r
42  */\r
43 \r
44 #include <assert.h>\r
45 #include "tc.h"\r
46 \r
47 /// @cond 0\r
48 /**INDENT-OFF**/\r
49 #ifdef __cplusplus\r
50 extern "C" {\r
51 #endif\r
52 /**INDENT-ON**/\r
53 /// @endcond\r
54 \r
55 #define TC_WPMR_WPKEY_VALUE TC_WPMR_WPKEY((uint32_t)0x54494D)\r
56 \r
57 /**\r
58  * \defgroup sam_drivers_tc_group Timer Counter (TC)\r
59  *\r
60  * The Timer Counter (TC) includes three identical 32-bit Timer Counter\r
61  * channels. Each channel can be independently programmed to perform a wide\r
62  * range of functions including frequency measurement, event counting,\r
63  * interval measurement, pulse generation, delay timing and pulse width\r
64  * modulation.\r
65  *\r
66  * @{\r
67  */\r
68 \r
69 /**\r
70  * \brief Configure TC for timer, waveform generation or capture.\r
71  *\r
72  * \param p_tc Pointer to a TC instance.\r
73  * \param ul_channel Channel to configure.\r
74  * \param ul_mode Control mode register value to set.\r
75  *\r
76  * \attention If the TC is configured for waveform generation, the external\r
77  * event selection (EEVT) should only be set to \c TC_CMR_EEVT_TIOB or the\r
78  * equivalent value \c 0 if it really is the intention to use TIOB as an\r
79  * external event trigger.\n\r
80  * This is because the setting forces TIOB to be an input even if the\r
81  * external event trigger has not been enabled with \c TC_CMR_ENETRG, and\r
82  * thus prevents normal operation of TIOB.\r
83  */\r
84 void tc_init(Tc *p_tc, uint32_t ul_channel, uint32_t ul_mode)\r
85 {\r
86         TcChannel *tc_channel;\r
87 \r
88         Assert(ul_channel <\r
89                         (sizeof(p_tc->TC_CHANNEL) / sizeof(p_tc->TC_CHANNEL[0])));\r
90         tc_channel = p_tc->TC_CHANNEL + ul_channel;\r
91 \r
92         /*  Disable TC clock. */\r
93         tc_channel->TC_CCR = TC_CCR_CLKDIS;\r
94 \r
95         /*  Disable interrupts. */\r
96         tc_channel->TC_IDR = 0xFFFFFFFF;\r
97 \r
98         /*  Clear status register. */\r
99         tc_channel->TC_SR;\r
100 \r
101         /*  Set mode. */\r
102         tc_channel->TC_CMR = ul_mode;\r
103 }\r
104 \r
105 /**\r
106  * \brief Asserts a SYNC signal to generate a software trigger to\r
107  * all channels.\r
108  *\r
109  * \param p_tc Pointer to a TC instance.\r
110  *\r
111  */\r
112 void tc_sync_trigger(Tc *p_tc)\r
113 {\r
114   p_tc->TC_BCR = TC_BCR_SYNC;\r
115 }\r
116 \r
117 /**\r
118  * \brief Configure TC Block mode.\r
119  * \note tc_init() must be called first.\r
120  *\r
121  * \param p_tc Pointer to a TC instance.\r
122  * \param ul_blockmode Block mode register value to set.\r
123  *\r
124  */\r
125 void tc_set_block_mode(Tc *p_tc, uint32_t ul_blockmode)\r
126 {\r
127         p_tc->TC_BMR = ul_blockmode;\r
128 }\r
129 \r
130 #if (!SAM3U)\r
131 \r
132 /**\r
133  * \brief Configure TC for 2-bit Gray Counter for Stepper Motor.\r
134  * \note tc_init() must be called first.\r
135  *\r
136  * \param p_tc Pointer to a TC instance.\r
137  * \param ul_channel Channel to configure.\r
138  * \param ul_steppermode Stepper motor mode register value to set.\r
139  *\r
140  * \return 0 for OK.\r
141  */\r
142 uint32_t tc_init_2bit_gray(Tc *p_tc, uint32_t ul_channel,\r
143                 uint32_t ul_steppermode)\r
144 {\r
145         Assert(ul_channel <\r
146                         (sizeof(p_tc->TC_CHANNEL) / sizeof(p_tc->TC_CHANNEL[0])));\r
147 \r
148         p_tc->TC_CHANNEL[ul_channel].TC_SMMR = ul_steppermode;\r
149         return 0;\r
150 }\r
151 \r
152 #endif\r
153 \r
154 /**\r
155  * \brief Start TC clock counter on the selected channel.\r
156  *\r
157  * \param p_tc Pointer to a TC instance.\r
158  * \param ul_channel Channel to configure.\r
159  */\r
160 void tc_start(Tc *p_tc, uint32_t ul_channel)\r
161 {\r
162         Assert(ul_channel <\r
163                         (sizeof(p_tc->TC_CHANNEL) / sizeof(p_tc->TC_CHANNEL[0])));\r
164 \r
165         p_tc->TC_CHANNEL[ul_channel].TC_CCR = TC_CCR_CLKEN | TC_CCR_SWTRG;\r
166 }\r
167 \r
168 /**\r
169  * \brief Stop TC clock counter on the selected channel.\r
170  *\r
171  * \param p_tc Pointer to a TC instance.\r
172  * \param ul_channel Channel to configure.\r
173  */\r
174 void tc_stop(Tc *p_tc, uint32_t ul_channel)\r
175 {\r
176         Assert(ul_channel <\r
177                         (sizeof(p_tc->TC_CHANNEL) / sizeof(p_tc->TC_CHANNEL[0])));\r
178 \r
179         p_tc->TC_CHANNEL[ul_channel].TC_CCR = TC_CCR_CLKDIS;\r
180 }\r
181 \r
182 /**\r
183  * \brief Read counter value on the selected channel.\r
184  *\r
185  * \param p_tc Pointer to a TC instance.\r
186  * \param ul_channel Channel to configure.\r
187  *\r
188  * \return Counter value.\r
189  */\r
190 uint32_t tc_read_cv(Tc *p_tc, uint32_t ul_channel)\r
191 {\r
192         Assert(ul_channel <\r
193                         (sizeof(p_tc->TC_CHANNEL) / sizeof(p_tc->TC_CHANNEL[0])));\r
194 \r
195         return p_tc->TC_CHANNEL[ul_channel].TC_CV;\r
196 }\r
197 \r
198 /**\r
199  * \brief Read RA TC counter on the selected channel.\r
200  *\r
201  * \param p_tc Pointer to a TC instance.\r
202  * \param ul_channel Channel to configure.\r
203  *\r
204  * \return RA value.\r
205  */\r
206 uint32_t tc_read_ra(Tc *p_tc, uint32_t ul_channel)\r
207 {\r
208         Assert(ul_channel <\r
209                         (sizeof(p_tc->TC_CHANNEL) / sizeof(p_tc->TC_CHANNEL[0])));\r
210 \r
211         return p_tc->TC_CHANNEL[ul_channel].TC_RA;\r
212 }\r
213 \r
214 /**\r
215  * \brief Read RB TC counter on the selected channel.\r
216  *\r
217  * \param p_tc Pointer to a TC instance.\r
218  * \param ul_channel Channel to configure.\r
219  *\r
220  * \return RB value.\r
221  */\r
222 uint32_t tc_read_rb(Tc *p_tc, uint32_t ul_channel)\r
223 {\r
224         Assert(ul_channel <\r
225                         (sizeof(p_tc->TC_CHANNEL) / sizeof(p_tc->TC_CHANNEL[0])));\r
226 \r
227         return p_tc->TC_CHANNEL[ul_channel].TC_RB;\r
228 }\r
229 \r
230 /**\r
231  * \brief Read RC TC counter on the selected channel.\r
232  *\r
233  * \param p_tc Pointer to a TC instance.\r
234  * \param ul_channel Channel to configure.\r
235  *\r
236  * \return RC value.\r
237  */\r
238 uint32_t tc_read_rc(Tc *p_tc, uint32_t ul_channel)\r
239 {\r
240         Assert(ul_channel <\r
241                         (sizeof(p_tc->TC_CHANNEL) / sizeof(p_tc->TC_CHANNEL[0])));\r
242 \r
243         return p_tc->TC_CHANNEL[ul_channel].TC_RC;\r
244 }\r
245 \r
246 /**\r
247  * \brief Write RA TC counter on the selected channel.\r
248  *\r
249  * \param p_tc Pointer to a TC instance.\r
250  * \param ul_channel Channel to configure.\r
251  * \param ul_value Value to set in register.\r
252  */\r
253 void tc_write_ra(Tc *p_tc, uint32_t ul_channel,\r
254                 uint32_t ul_value)\r
255 {\r
256         Assert(ul_channel <\r
257                         (sizeof(p_tc->TC_CHANNEL) / sizeof(p_tc->TC_CHANNEL[0])));\r
258 \r
259         p_tc->TC_CHANNEL[ul_channel].TC_RA = ul_value;\r
260 }\r
261 \r
262 /**\r
263  * \brief Write RB TC counter on the selected channel.\r
264  *\r
265  * \param p_tc Pointer to a TC instance.\r
266  * \param ul_channel Channel to configure.\r
267  * \param ul_value Value to set in register.\r
268  */\r
269 void tc_write_rb(Tc *p_tc, uint32_t ul_channel,\r
270                 uint32_t ul_value)\r
271 {\r
272         Assert(ul_channel <\r
273                         (sizeof(p_tc->TC_CHANNEL) / sizeof(p_tc->TC_CHANNEL[0])));\r
274 \r
275         p_tc->TC_CHANNEL[ul_channel].TC_RB = ul_value;\r
276 }\r
277 \r
278 /**\r
279  * \brief Write RC TC counter on the selected channel.\r
280  *\r
281  * \param p_tc Pointer to a TC instance.\r
282  * \param ul_channel Channel to configure.\r
283  * \param ul_value Value to set in register.\r
284  */\r
285 void tc_write_rc(Tc *p_tc, uint32_t ul_channel,\r
286                 uint32_t ul_value)\r
287 {\r
288         Assert(ul_channel <\r
289                         (sizeof(p_tc->TC_CHANNEL) / sizeof(p_tc->TC_CHANNEL[0])));\r
290 \r
291         p_tc->TC_CHANNEL[ul_channel].TC_RC = ul_value;\r
292 }\r
293 \r
294 /**\r
295  * \brief Enable TC interrupts on the selected channel.\r
296  *\r
297  * \param p_tc Pointer to a TC instance.\r
298  * \param ul_channel Channel to configure.\r
299  * \param ul_sources Interrupt sources bit map.\r
300  */\r
301 void tc_enable_interrupt(Tc *p_tc, uint32_t ul_channel,\r
302                 uint32_t ul_sources)\r
303 {\r
304         TcChannel *tc_channel;\r
305 \r
306         Assert(ul_channel <\r
307                         (sizeof(p_tc->TC_CHANNEL) / sizeof(p_tc->TC_CHANNEL[0])));\r
308         tc_channel = p_tc->TC_CHANNEL + ul_channel;\r
309         tc_channel->TC_IER = ul_sources;\r
310 }\r
311 \r
312 /**\r
313  * \brief Disable TC interrupts on the selected channel.\r
314  *\r
315  * \param p_tc Pointer to a TC instance.\r
316  * \param ul_channel Channel to configure.\r
317  * \param ul_sources Interrupt sources bit map.\r
318  */\r
319 void tc_disable_interrupt(Tc *p_tc, uint32_t ul_channel,\r
320                 uint32_t ul_sources)\r
321 {\r
322         TcChannel *tc_channel;\r
323 \r
324         Assert(ul_channel <\r
325                         (sizeof(p_tc->TC_CHANNEL) / sizeof(p_tc->TC_CHANNEL[0])));\r
326         tc_channel = p_tc->TC_CHANNEL + ul_channel;\r
327         tc_channel->TC_IDR = ul_sources;\r
328 }\r
329 \r
330 /**\r
331  * \brief Read TC interrupt mask on the selected channel.\r
332  *\r
333  * \param p_tc Pointer to a TC instance.\r
334  * \param ul_channel Channel to configure.\r
335  *\r
336  * \return The interrupt mask value.\r
337  */\r
338 uint32_t tc_get_interrupt_mask(Tc *p_tc, uint32_t ul_channel)\r
339 {\r
340         TcChannel *tc_channel;\r
341 \r
342         Assert(ul_channel <\r
343                         (sizeof(p_tc->TC_CHANNEL) / sizeof(p_tc->TC_CHANNEL[0])));\r
344         tc_channel = p_tc->TC_CHANNEL + ul_channel;\r
345         return tc_channel->TC_IMR;\r
346 }\r
347 \r
348 /**\r
349  * \brief Get current status on the selected channel.\r
350  *\r
351  * \param p_tc Pointer to a TC instance.\r
352  * \param ul_channel Channel to configure.\r
353  *\r
354  * \return The current TC status.\r
355  */\r
356 uint32_t tc_get_status(Tc *p_tc, uint32_t ul_channel)\r
357 {\r
358         TcChannel *tc_channel;\r
359 \r
360         Assert(ul_channel <\r
361                         (sizeof(p_tc->TC_CHANNEL) / sizeof(p_tc->TC_CHANNEL[0])));\r
362         tc_channel = p_tc->TC_CHANNEL + ul_channel;\r
363         return tc_channel->TC_SR;\r
364 }\r
365 \r
366 /* TC divisor used to find the lowest acceptable timer frequency */\r
367 #define TC_DIV_FACTOR 65536\r
368 \r
369 #if (!SAM4L)\r
370 \r
371 #ifndef FREQ_SLOW_CLOCK_EXT\r
372 #define FREQ_SLOW_CLOCK_EXT 32768 /* External slow clock frequency (hz) */\r
373 #endif\r
374 \r
375 /**\r
376  * \brief Find the best MCK divisor.\r
377  *\r
378  * Finds the best MCK divisor given the timer frequency and MCK. The result\r
379  * is guaranteed to satisfy the following equation:\r
380  * \code\r
381  *   (MCK / (DIV * 65536)) <= freq <= (MCK / DIV)\r
382  * \endcode\r
383  * with DIV being the lowest possible value,\r
384  * to maximize timing adjust resolution.\r
385  *\r
386  * \param ul_freq  Desired timer frequency.\r
387  * \param ul_mck  Master clock frequency.\r
388  * \param p_uldiv  Divisor value.\r
389  * \param p_ultcclks  TCCLKS field value for divisor.\r
390  * \param ul_boardmck  Board clock frequency.\r
391  *\r
392  * \return 1 if a proper divisor has been found, otherwise 0.\r
393  */\r
394 uint32_t tc_find_mck_divisor(uint32_t ul_freq, uint32_t ul_mck,\r
395                 uint32_t *p_uldiv, uint32_t *p_ultcclks, uint32_t ul_boardmck)\r
396 {\r
397         const uint32_t divisors[5] = { 2, 8, 32, 128,\r
398                         ul_boardmck / FREQ_SLOW_CLOCK_EXT };\r
399         uint32_t ul_index;\r
400         uint32_t ul_high, ul_low;\r
401 \r
402         /*  Satisfy frequency bound. */\r
403         for (ul_index = 0;\r
404                         ul_index < (sizeof(divisors) / sizeof(divisors[0]));\r
405                         ul_index++) {\r
406                 ul_high = ul_mck / divisors[ul_index];\r
407                 ul_low  = ul_high / TC_DIV_FACTOR;\r
408                 if (ul_freq > ul_high) {\r
409                         return 0;\r
410                 } else if (ul_freq >= ul_low) {\r
411                         break;\r
412                 }\r
413         }\r
414         if (ul_index >= (sizeof(divisors) / sizeof(divisors[0]))) {\r
415                 return 0;\r
416         }\r
417 \r
418         /*  Store results. */\r
419         if (p_uldiv) {\r
420                 *p_uldiv = divisors[ul_index];\r
421         }\r
422 \r
423         if (p_ultcclks) {\r
424                 *p_ultcclks = ul_index;\r
425         }\r
426 \r
427         return 1;\r
428 }\r
429 \r
430 #endif\r
431 \r
432 #if (SAM4L)\r
433 /**\r
434  * \brief Find the best PBA clock divisor.\r
435  *\r
436  * Finds the best divisor given the timer frequency and PBA clock. The result\r
437  * is guaranteed to satisfy the following equation:\r
438  * \code\r
439  *   (ul_pbaclk / (2* DIV * 65536)) <= freq <= (ul_pbaclk / (2* DIV))\r
440  * \endcode\r
441  * with DIV being the lowest possible value,\r
442  * to maximize timing adjust resolution.\r
443  *\r
444  * \param ul_freq  Desired timer frequency.\r
445  * \param ul_mck  PBA clock frequency.\r
446  * \param p_uldiv  Divisor value.\r
447  * \param p_ultcclks  TCCLKS field value for divisor.\r
448  * \param ul_boardmck  useless here.\r
449  *\r
450  * \return 1 if a proper divisor has been found, otherwise 0.\r
451  */\r
452 uint32_t tc_find_mck_divisor(uint32_t ul_freq, uint32_t ul_mck,\r
453                 uint32_t *p_uldiv, uint32_t *p_ultcclks, uint32_t ul_boardmck)\r
454 {\r
455         const uint32_t divisors[5] = { 0, 2, 8, 32, 128};\r
456         uint32_t ul_index;\r
457         uint32_t ul_high, ul_low;\r
458 \r
459         UNUSED(ul_boardmck);\r
460 \r
461         /*  Satisfy frequency bound. */\r
462         for (ul_index = 1;\r
463                         ul_index < (sizeof(divisors) / sizeof(divisors[0]));\r
464                         ul_index++) {\r
465                 ul_high = ul_mck / divisors[ul_index];\r
466                 ul_low  = ul_high / TC_DIV_FACTOR;\r
467                 if (ul_freq > ul_high) {\r
468                         return 0;\r
469                 } else if (ul_freq >= ul_low) {\r
470                         break;\r
471                 }\r
472         }\r
473         if (ul_index >= (sizeof(divisors) / sizeof(divisors[0]))) {\r
474                 return 0;\r
475         }\r
476 \r
477         /*  Store results. */\r
478         if (p_uldiv) {\r
479                 *p_uldiv = divisors[ul_index];\r
480         }\r
481 \r
482         if (p_ultcclks) {\r
483                 *p_ultcclks = ul_index;\r
484         }\r
485 \r
486         return 1;\r
487 }\r
488 \r
489 #endif\r
490 \r
491 #if (!SAM4L)\r
492 \r
493 /**\r
494  * \brief Enable TC QDEC interrupts.\r
495  *\r
496  * \param p_tc Pointer to a TC instance.\r
497  * \param ul_sources Interrupts to be enabled.\r
498  */\r
499 void tc_enable_qdec_interrupt(Tc *p_tc, uint32_t ul_sources)\r
500 {\r
501         p_tc->TC_QIER = ul_sources;\r
502 }\r
503 \r
504 /**\r
505  * \brief Disable TC QDEC interrupts.\r
506  *\r
507  * \param p_tc Pointer to a TC instance.\r
508  * \param ul_sources Interrupts to be disabled.\r
509  */\r
510 void tc_disable_qdec_interrupt(Tc *p_tc, uint32_t ul_sources)\r
511 {\r
512         p_tc->TC_QIDR = ul_sources;\r
513 }\r
514 \r
515 /**\r
516  * \brief Read TC QDEC interrupt mask.\r
517  *\r
518  * \param p_tc Pointer to a TC instance.\r
519  *\r
520  * \return The interrupt mask value.\r
521  */\r
522 uint32_t tc_get_qdec_interrupt_mask(Tc *p_tc)\r
523 {\r
524         return p_tc->TC_QIMR;\r
525 }\r
526 \r
527 /**\r
528  * \brief Get current QDEC status.\r
529  *\r
530  * \param p_tc Pointer to a TC instance.\r
531  *\r
532  * \return The current TC status.\r
533  */\r
534 uint32_t tc_get_qdec_interrupt_status(Tc *p_tc)\r
535 {\r
536         return p_tc->TC_QISR;\r
537 }\r
538 \r
539 #endif\r
540 \r
541 #if (!SAM3U)\r
542 \r
543 /**\r
544  * \brief Enable or disable write protection of TC registers.\r
545  *\r
546  * \param p_tc Pointer to a TC instance.\r
547  * \param ul_enable 1 to enable, 0 to disable.\r
548  */\r
549 void tc_set_writeprotect(Tc *p_tc, uint32_t ul_enable)\r
550 {\r
551         if (ul_enable) {\r
552                 p_tc->TC_WPMR = TC_WPMR_WPKEY_VALUE | TC_WPMR_WPEN;\r
553         } else {\r
554                 p_tc->TC_WPMR = TC_WPMR_WPKEY_VALUE;\r
555         }\r
556 }\r
557 \r
558 #endif\r
559 \r
560 #if SAM4L\r
561 \r
562 /**\r
563  * \brief Indicate features.\r
564  *\r
565  * \param p_tc Pointer to a TC instance.\r
566  *\r
567  * \return TC_FEATURES value.\r
568  */\r
569 uint32_t tc_get_feature(Tc *p_tc)\r
570 {\r
571         return p_tc->TC_FEATURES;\r
572 }\r
573 \r
574 /**\r
575  * \brief Indicate version.\r
576  *\r
577  * \param p_tc Pointer to a TC instance.\r
578  *\r
579  * \return TC_VERSION value.\r
580  */\r
581 uint32_t tc_get_version(Tc *p_tc)\r
582 {\r
583         return p_tc->TC_VERSION;\r
584 }\r
585 \r
586 #endif\r
587 \r
588 //@}\r
589 \r
590 /// @cond 0\r
591 /**INDENT-OFF**/\r
592 #ifdef __cplusplus\r
593 }\r
594 #endif\r
595 /**INDENT-ON**/\r
596 /// @endcond\r