]> git.sur5r.net Git - freertos/blob - FreeRTOS/Demo/CORTEX_M4_ATSAM4S_Atmel_Studio/src/asf/sam/drivers/tc/tc.c
Demo code only:
[freertos] / FreeRTOS / Demo / CORTEX_M4_ATSAM4S_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-2012 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 RA TC counter 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 RA value.\r
189  */\r
190 int tc_read_ra(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_RA;\r
196 }\r
197 \r
198 /**\r
199  * \brief Read RB 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 RB value.\r
205  */\r
206 int tc_read_rb(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_RB;\r
212 }\r
213 \r
214 /**\r
215  * \brief Read RC 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 RC value.\r
221  */\r
222 int tc_read_rc(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_RC;\r
228 }\r
229 \r
230 /**\r
231  * \brief Write RA 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  * \param ul_value Value to set in register.\r
236  */\r
237 void tc_write_ra(Tc *p_tc, uint32_t ul_channel,\r
238                 uint32_t ul_value)\r
239 {\r
240         Assert(ul_channel <\r
241                         (sizeof(p_tc->TC_CHANNEL) / sizeof(p_tc->TC_CHANNEL[0])));\r
242 \r
243         p_tc->TC_CHANNEL[ul_channel].TC_RA = ul_value;\r
244 }\r
245 \r
246 /**\r
247  * \brief Write RB 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_rb(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_RB = ul_value;\r
260 }\r
261 \r
262 /**\r
263  * \brief Write RC 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_rc(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_RC = ul_value;\r
276 }\r
277 \r
278 /**\r
279  * \brief Enable TC interrupts 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_sources Interrupt sources bit map.\r
284  */\r
285 void tc_enable_interrupt(Tc *p_tc, uint32_t ul_channel,\r
286                 uint32_t ul_sources)\r
287 {\r
288         TcChannel *tc_channel;\r
289 \r
290         Assert(ul_channel <\r
291                         (sizeof(p_tc->TC_CHANNEL) / sizeof(p_tc->TC_CHANNEL[0])));\r
292         tc_channel = p_tc->TC_CHANNEL + ul_channel;\r
293         tc_channel->TC_IER = ul_sources;\r
294 }\r
295 \r
296 /**\r
297  * \brief Disable TC interrupts on the selected channel.\r
298  *\r
299  * \param p_tc Pointer to a TC instance.\r
300  * \param ul_channel Channel to configure.\r
301  * \param ul_sources Interrupt sources bit map.\r
302  */\r
303 void tc_disable_interrupt(Tc *p_tc, uint32_t ul_channel,\r
304                 uint32_t ul_sources)\r
305 {\r
306         TcChannel *tc_channel;\r
307 \r
308         Assert(ul_channel <\r
309                         (sizeof(p_tc->TC_CHANNEL) / sizeof(p_tc->TC_CHANNEL[0])));\r
310         tc_channel = p_tc->TC_CHANNEL + ul_channel;\r
311         tc_channel->TC_IDR = ul_sources;\r
312 }\r
313 \r
314 /**\r
315  * \brief Read TC interrupt mask on the selected channel.\r
316  *\r
317  * \param p_tc Pointer to a TC instance.\r
318  * \param ul_channel Channel to configure.\r
319  *\r
320  * \return The interrupt mask value.\r
321  */\r
322 uint32_t tc_get_interrupt_mask(Tc *p_tc, uint32_t ul_channel)\r
323 {\r
324         TcChannel *tc_channel;\r
325 \r
326         Assert(ul_channel <\r
327                         (sizeof(p_tc->TC_CHANNEL) / sizeof(p_tc->TC_CHANNEL[0])));\r
328         tc_channel = p_tc->TC_CHANNEL + ul_channel;\r
329         return tc_channel->TC_IMR;\r
330 }\r
331 \r
332 /**\r
333  * \brief Get current status on the selected channel.\r
334  *\r
335  * \param p_tc Pointer to a TC instance.\r
336  * \param ul_channel Channel to configure.\r
337  *\r
338  * \return The current TC status.\r
339  */\r
340 uint32_t tc_get_status(Tc *p_tc, uint32_t ul_channel)\r
341 {\r
342         TcChannel *tc_channel;\r
343 \r
344         Assert(ul_channel <\r
345                         (sizeof(p_tc->TC_CHANNEL) / sizeof(p_tc->TC_CHANNEL[0])));\r
346         tc_channel = p_tc->TC_CHANNEL + ul_channel;\r
347         return tc_channel->TC_SR;\r
348 }\r
349 \r
350 /* TC divisor used to find the lowest acceptable timer frequency */\r
351 #define TC_DIV_FACTOR 65536\r
352 \r
353 #if (!SAM4L)\r
354 \r
355 #ifndef FREQ_SLOW_CLOCK_EXT\r
356 #define FREQ_SLOW_CLOCK_EXT 32768 /* External slow clock frequency (hz) */\r
357 #endif\r
358 \r
359 /**\r
360  * \brief Find the best MCK divisor.\r
361  *\r
362  * Finds the best MCK divisor given the timer frequency and MCK. The result\r
363  * is guaranteed to satisfy the following equation:\r
364  * \code\r
365  *   (MCK / (DIV * 65536)) <= freq <= (MCK / DIV)\r
366  * \endcode\r
367  * with DIV being the lowest possible value,\r
368  * to maximize timing adjust resolution.\r
369  *\r
370  * \param ul_freq  Desired timer frequency.\r
371  * \param ul_mck  Master clock frequency.\r
372  * \param p_uldiv  Divisor value.\r
373  * \param p_ultcclks  TCCLKS field value for divisor.\r
374  * \param ul_boardmck  Board clock frequency.\r
375  *\r
376  * \return 1 if a proper divisor has been found, otherwise 0.\r
377  */\r
378 uint32_t tc_find_mck_divisor(uint32_t ul_freq, uint32_t ul_mck,\r
379                 uint32_t *p_uldiv, uint32_t *p_ultcclks, uint32_t ul_boardmck)\r
380 {\r
381         const uint32_t divisors[5] = { 2, 8, 32, 128,\r
382                         ul_boardmck / FREQ_SLOW_CLOCK_EXT };\r
383         uint32_t ul_index;\r
384         uint32_t ul_high, ul_low;\r
385 \r
386         /*  Satisfy frequency bound. */\r
387         for (ul_index = 0;\r
388                         ul_index < (sizeof(divisors) / sizeof(divisors[0]));\r
389                         ul_index++) {\r
390                 ul_high = ul_mck / divisors[ul_index];\r
391                 ul_low  = ul_high / TC_DIV_FACTOR;\r
392                 if (ul_freq > ul_high) {\r
393                         return 0;\r
394                 } else if (ul_freq >= ul_low) {\r
395                         break;\r
396                 }\r
397         }\r
398         if (ul_index >= (sizeof(divisors) / sizeof(divisors[0]))) {\r
399                 return 0;\r
400         }\r
401 \r
402         /*  Store results. */\r
403         if (p_uldiv) {\r
404                 *p_uldiv = divisors[ul_index];\r
405         }\r
406 \r
407         if (p_ultcclks) {\r
408                 *p_ultcclks = ul_index;\r
409         }\r
410 \r
411         return 1;\r
412 }\r
413 \r
414 #endif\r
415 \r
416 #if (SAM4L)\r
417 /**\r
418  * \brief Find the best PBA clock divisor.\r
419  *\r
420  * Finds the best divisor given the timer frequency and PBA clock. The result\r
421  * is guaranteed to satisfy the following equation:\r
422  * \code\r
423  *   (ul_pbaclk / (2* DIV * 65536)) <= freq <= (ul_pbaclk / (2* DIV))\r
424  * \endcode\r
425  * with DIV being the lowest possible value,\r
426  * to maximize timing adjust resolution.\r
427  *\r
428  * \param ul_freq  Desired timer frequency.\r
429  * \param ul_mck  PBA clock frequency.\r
430  * \param p_uldiv  Divisor value.\r
431  * \param p_ultcclks  TCCLKS field value for divisor.\r
432  * \param ul_boardmck  useless here.\r
433  *\r
434  * \return 1 if a proper divisor has been found, otherwise 0.\r
435  */\r
436 uint32_t tc_find_mck_divisor(uint32_t ul_freq, uint32_t ul_mck,\r
437                 uint32_t *p_uldiv, uint32_t *p_ultcclks, uint32_t ul_boardmck)\r
438 {\r
439         const uint32_t divisors[5] = { 0, 2, 8, 32, 128};\r
440         uint32_t ul_index;\r
441         uint32_t ul_high, ul_low;\r
442 \r
443         UNUSED(ul_boardmck);\r
444 \r
445         /*  Satisfy frequency bound. */\r
446         for (ul_index = 1;\r
447                         ul_index < (sizeof(divisors) / sizeof(divisors[0]));\r
448                         ul_index++) {\r
449                 ul_high = ul_mck / divisors[ul_index];\r
450                 ul_low  = ul_high / TC_DIV_FACTOR;\r
451                 if (ul_freq > ul_high) {\r
452                         return 0;\r
453                 } else if (ul_freq >= ul_low) {\r
454                         break;\r
455                 }\r
456         }\r
457         if (ul_index >= (sizeof(divisors) / sizeof(divisors[0]))) {\r
458                 return 0;\r
459         }\r
460 \r
461         /*  Store results. */\r
462         if (p_uldiv) {\r
463                 *p_uldiv = divisors[ul_index];\r
464         }\r
465 \r
466         if (p_ultcclks) {\r
467                 *p_ultcclks = ul_index;\r
468         }\r
469 \r
470         return 1;\r
471 }\r
472 \r
473 #endif\r
474 \r
475 #if (!SAM4L)\r
476 \r
477 /**\r
478  * \brief Enable TC QDEC interrupts.\r
479  *\r
480  * \param p_tc Pointer to a TC instance.\r
481  * \param ul_sources Interrupts to be enabled.\r
482  */\r
483 void tc_enable_qdec_interrupt(Tc *p_tc, uint32_t ul_sources)\r
484 {\r
485         p_tc->TC_QIER = ul_sources;\r
486 }\r
487 \r
488 /**\r
489  * \brief Disable TC QDEC interrupts.\r
490  *\r
491  * \param p_tc Pointer to a TC instance.\r
492  * \param ul_sources Interrupts to be disabled.\r
493  */\r
494 void tc_disable_qdec_interrupt(Tc *p_tc, uint32_t ul_sources)\r
495 {\r
496         p_tc->TC_QIDR = ul_sources;\r
497 }\r
498 \r
499 /**\r
500  * \brief Read TC QDEC interrupt mask.\r
501  *\r
502  * \param p_tc Pointer to a TC instance.\r
503  *\r
504  * \return The interrupt mask value.\r
505  */\r
506 uint32_t tc_get_qdec_interrupt_mask(Tc *p_tc)\r
507 {\r
508         return p_tc->TC_QIMR;\r
509 }\r
510 \r
511 /**\r
512  * \brief Get current QDEC status.\r
513  *\r
514  * \param p_tc Pointer to a TC instance.\r
515  *\r
516  * \return The current TC status.\r
517  */\r
518 uint32_t tc_get_qdec_interrupt_status(Tc *p_tc)\r
519 {\r
520         return p_tc->TC_QISR;\r
521 }\r
522 \r
523 #endif\r
524 \r
525 #if (!SAM3U)\r
526 \r
527 /**\r
528  * \brief Enable or disable write protection of TC registers.\r
529  *\r
530  * \param p_tc Pointer to a TC instance.\r
531  * \param ul_enable 1 to enable, 0 to disable.\r
532  */\r
533 void tc_set_writeprotect(Tc *p_tc, uint32_t ul_enable)\r
534 {\r
535         if (ul_enable) {\r
536                 p_tc->TC_WPMR = TC_WPMR_WPKEY_VALUE | TC_WPMR_WPEN;\r
537         } else {\r
538                 p_tc->TC_WPMR = TC_WPMR_WPKEY_VALUE;\r
539         }\r
540 }\r
541 \r
542 #endif\r
543 \r
544 #if SAM4L\r
545 \r
546 /**\r
547  * \brief Indicate features.\r
548  *\r
549  * \param p_tc Pointer to a TC instance.\r
550  *\r
551  * \return TC_FEATURES value.\r
552  */\r
553 uint32_t tc_get_feature(Tc *p_tc)\r
554 {\r
555         return p_tc->TC_FEATURES;\r
556 }\r
557 \r
558 /**\r
559  * \brief Indicate version.\r
560  *\r
561  * \param p_tc Pointer to a TC instance.\r
562  *\r
563  * \return TC_VERSION value.\r
564  */\r
565 uint32_t tc_get_version(Tc *p_tc)\r
566 {\r
567         return p_tc->TC_VERSION;\r
568 }\r
569 \r
570 #endif\r
571 \r
572 //@}\r
573 \r
574 /// @cond 0\r
575 /**INDENT-OFF**/\r
576 #ifdef __cplusplus\r
577 }\r
578 #endif\r
579 /**INDENT-ON**/\r
580 /// @endcond\r