]> git.sur5r.net Git - freertos/blob - Demo/CORTEX_AT91SAM3U256_IAR/AT91Lib/peripherals/pwmc/pwmc.c
Start to re-arrange files to include FreeRTOS+ in main download.
[freertos] / Demo / CORTEX_AT91SAM3U256_IAR / AT91Lib / peripherals / pwmc / pwmc.c
1 /* ----------------------------------------------------------------------------\r
2  *         ATMEL Microcontroller Software Support \r
3  * ----------------------------------------------------------------------------\r
4  * Copyright (c) 2008, Atmel Corporation\r
5  *\r
6  * All rights reserved.\r
7  *\r
8  * Redistribution and use in source and binary forms, with or without\r
9  * modification, are permitted provided that the following conditions are met:\r
10  *\r
11  * - Redistributions of source code must retain the above copyright notice,\r
12  * this list of conditions and the disclaimer below.\r
13  *\r
14  * Atmel's name may not be used to endorse or promote products derived from\r
15  * this software without specific prior written permission.\r
16  *\r
17  * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR\r
18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF\r
19  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE\r
20  * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT,\r
21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\r
22  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,\r
23  * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF\r
24  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\r
25  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,\r
26  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
27  * ----------------------------------------------------------------------------\r
28  */\r
29 \r
30 //------------------------------------------------------------------------------\r
31 //         Headers\r
32 //------------------------------------------------------------------------------\r
33 \r
34 #include "pwmc.h"\r
35 #include <board.h>\r
36 #include <utility/assert.h>\r
37 #include <utility/trace.h>\r
38 \r
39 //------------------------------------------------------------------------------\r
40 //         Local functions\r
41 //------------------------------------------------------------------------------\r
42 \r
43 //------------------------------------------------------------------------------\r
44 /// Finds a prescaler/divisor couple to generate the desired frequency from\r
45 /// MCK.\r
46 /// Returns the value to enter in PWMC_MR or 0 if the configuration cannot be\r
47 /// met.\r
48 /// \param frequency  Desired frequency in Hz.\r
49 /// \param mck  Master clock frequency in Hz.\r
50 //------------------------------------------------------------------------------\r
51 static unsigned short FindClockConfiguration(\r
52     unsigned int frequency,\r
53     unsigned int mck)\r
54 {\r
55     unsigned int divisors[11] = {1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024};\r
56     unsigned char divisor = 0;\r
57     unsigned int prescaler;\r
58 \r
59     SANITY_CHECK(frequency < mck);\r
60 \r
61     // Find prescaler and divisor values\r
62     prescaler = (mck / divisors[divisor]) / frequency;\r
63     while ((prescaler > 255) && (divisor < 11)) {\r
64 \r
65         divisor++;\r
66         prescaler = (mck / divisors[divisor]) / frequency;\r
67     }\r
68 \r
69     // Return result\r
70     if (divisor < 11) {\r
71 \r
72         TRACE_DEBUG("Found divisor=%u and prescaler=%u for freq=%uHz\n\r",\r
73                   divisors[divisor], prescaler, frequency);\r
74         return prescaler | (divisor << 8);\r
75     }\r
76     else {\r
77 \r
78         return 0;\r
79     }\r
80 }\r
81 \r
82 //------------------------------------------------------------------------------\r
83 //         Global functions\r
84 //------------------------------------------------------------------------------\r
85 \r
86 //------------------------------------------------------------------------------\r
87 /// Configures PWM a channel with the given parameters.\r
88 /// The PWM controller must have been clocked in the PMC prior to calling this\r
89 /// function. \r
90 /// Beware: this function disables the channel. It waits until disable is effective.\r
91 /// \param channel  Channel number.\r
92 /// \param prescaler  Channel prescaler.\r
93 /// \param alignment  Channel alignment.\r
94 /// \param polarity  Channel polarity.\r
95 //------------------------------------------------------------------------------\r
96 void PWMC_ConfigureChannel(\r
97     unsigned char channel,\r
98     unsigned int prescaler,\r
99     unsigned int alignment,\r
100     unsigned int polarity)\r
101 {\r
102     SANITY_CHECK(prescaler < AT91C_PWMC_CPRE_MCKB);\r
103     SANITY_CHECK((alignment & ~AT91C_PWMC_CALG) == 0);\r
104     SANITY_CHECK((polarity & ~AT91C_PWMC_CPOL) == 0);\r
105 \r
106     // Disable channel (effective at the end of the current period)\r
107     if ((AT91C_BASE_PWMC->PWMC_SR & (1 << channel)) != 0) {\r
108         AT91C_BASE_PWMC->PWMC_DIS = 1 << channel;\r
109         while ((AT91C_BASE_PWMC->PWMC_SR & (1 << channel)) != 0);\r
110     }\r
111 \r
112     // Configure channel\r
113     AT91C_BASE_PWMC->PWMC_CH[channel].PWMC_CMR = prescaler | alignment | polarity;\r
114 }\r
115 \r
116 //------------------------------------------------------------------------------\r
117 /// Configures PWM clocks A & B to run at the given frequencies. This function\r
118 /// finds the best MCK divisor and prescaler values automatically.\r
119 /// \param clka  Desired clock A frequency (0 if not used).\r
120 /// \param clkb  Desired clock B frequency (0 if not used).\r
121 /// \param mck  Master clock frequency.\r
122 //------------------------------------------------------------------------------\r
123 void PWMC_ConfigureClocks(unsigned int clka, unsigned int clkb, unsigned int mck)\r
124 {\r
125     unsigned int mode = 0;\r
126     unsigned int result;\r
127 \r
128     // Clock A\r
129     if (clka != 0) {\r
130 \r
131         result = FindClockConfiguration(clka, mck);\r
132         ASSERT(result != 0, "-F- Could not generate the desired PWM frequency (%uHz)\n\r", clka);\r
133         mode |= result;\r
134     }\r
135 \r
136     // Clock B\r
137     if (clkb != 0) {\r
138 \r
139         result = FindClockConfiguration(clkb, mck);\r
140         ASSERT(result != 0, "-F- Could not generate the desired PWM frequency (%uHz)\n\r", clkb);\r
141         mode |= (result << 16);\r
142     }\r
143 \r
144     // Configure clocks\r
145     TRACE_DEBUG("Setting PWMC_MR = 0x%08X\n\r", mode);\r
146     AT91C_BASE_PWMC->PWMC_MR = mode;\r
147 }\r
148 \r
149 //------------------------------------------------------------------------------\r
150 /// Sets the period value used by a PWM channel. This function writes directly\r
151 /// to the CPRD register if the channel is disabled; otherwise, it uses the\r
152 /// update register CUPD.\r
153 /// \param channel  Channel number.\r
154 /// \param period  Period value.\r
155 //------------------------------------------------------------------------------\r
156 void PWMC_SetPeriod(unsigned char channel, unsigned short period)\r
157 {\r
158     // If channel is disabled, write to CPRD\r
159     if ((AT91C_BASE_PWMC->PWMC_SR & (1 << channel)) == 0) {\r
160 \r
161         AT91C_BASE_PWMC->PWMC_CH[channel].PWMC_CPRDR = period;\r
162     }\r
163     // Otherwise use update register\r
164     else {\r
165 \r
166         AT91C_BASE_PWMC->PWMC_CH[channel].PWMC_CMR |= AT91C_PWMC_CPD;\r
167         AT91C_BASE_PWMC->PWMC_CH[channel].PWMC_CUPDR = period;\r
168     }\r
169 }\r
170 \r
171 //------------------------------------------------------------------------------\r
172 /// Sets the duty cycle used by a PWM channel. This function writes directly to\r
173 /// the CDTY register if the channel is disabled; otherwise it uses the\r
174 /// update register CUPD.\r
175 /// Note that the duty cycle must always be inferior or equal to the channel\r
176 /// period.\r
177 /// \param channel  Channel number.\r
178 /// \param duty  Duty cycle value.\r
179 //------------------------------------------------------------------------------\r
180 void PWMC_SetDutyCycle(unsigned char channel, unsigned short duty)\r
181 {\r
182     SANITY_CHECK(duty <= AT91C_BASE_PWMC->PWMC_CH[channel].PWMC_CPRDR);\r
183 \r
184     // SAM7S errata\r
185 #if defined(at91sam7s16) || defined(at91sam7s161) || defined(at91sam7s32) \\r
186     || defined(at91sam7s321) || defined(at91sam7s64) || defined(at91sam7s128) \\r
187     || defined(at91sam7s256) || defined(at91sam7s512)\r
188     ASSERT(duty > 0, "-F- Duty cycle value 0 is not permitted on SAM7S chips.\n\r");\r
189     ASSERT((duty > 1) || (AT91C_BASE_PWMC->PWMC_CH[channel].PWMC_CMR & AT91C_PWMC_CALG),\r
190            "-F- Duty cycle value 1 is not permitted in left-aligned mode on SAM7S chips.\n\r");\r
191 #endif\r
192 \r
193     // If channel is disabled, write to CDTY\r
194     if ((AT91C_BASE_PWMC->PWMC_SR & (1 << channel)) == 0) {\r
195 \r
196         AT91C_BASE_PWMC->PWMC_CH[channel].PWMC_CDTYR = duty;\r
197     }\r
198     // Otherwise use update register\r
199     else {\r
200 \r
201         AT91C_BASE_PWMC->PWMC_CH[channel].PWMC_CMR &= ~AT91C_PWMC_CPD;\r
202         AT91C_BASE_PWMC->PWMC_CH[channel].PWMC_CUPDR = duty;\r
203     }\r
204 }\r
205 \r
206 //------------------------------------------------------------------------------\r
207 /// Enables the given PWM channel. This does NOT enable the corresponding pin;\r
208 /// this must be done in the user code.\r
209 /// \param channel  Channel number.\r
210 //------------------------------------------------------------------------------\r
211 void PWMC_EnableChannel(unsigned char channel)\r
212 {\r
213     AT91C_BASE_PWMC->PWMC_ENA = 1 << channel;\r
214 }\r
215 \r
216 //------------------------------------------------------------------------------\r
217 /// Disables the given PWM channel.\r
218 /// Beware, channel will be effectively disabled at the end of the current period.\r
219 /// Application can check channel is disabled using the following wait loop:\r
220 /// while ((AT91C_BASE_PWMC->PWMC_SR & (1 << channel)) != 0);\r
221 /// \param channel  Channel number.\r
222 //------------------------------------------------------------------------------\r
223 void PWMC_DisableChannel(unsigned char channel)\r
224 {\r
225     AT91C_BASE_PWMC->PWMC_DIS = 1 << channel;\r
226 }\r
227 \r
228 //------------------------------------------------------------------------------\r
229 /// Enables the period interrupt for the given PWM channel.\r
230 /// \param channel  Channel number.\r
231 //------------------------------------------------------------------------------\r
232 void PWMC_EnableChannelIt(unsigned char channel)\r
233 {\r
234     AT91C_BASE_PWMC->PWMC_IER = 1 << channel;\r
235 }\r
236 \r
237 //------------------------------------------------------------------------------\r
238 /// Disables the period interrupt for the given PWM channel.\r
239 /// \param channel  Channel number.\r
240 //------------------------------------------------------------------------------\r
241 void PWMC_DisableChannelIt(unsigned char channel)\r
242 {\r
243     AT91C_BASE_PWMC->PWMC_IDR = 1 << channel;\r
244 }\r
245 \r