]> git.sur5r.net Git - freertos/blob - Demo/Common/drivers/Atmel/at91lib/peripherals/pwmc/pwmc.c
Start to re-arrange files to include FreeRTOS+ in main download.
[freertos] / Demo / Common / drivers / Atmel / 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_LOG(trace_DEBUG, "-D- 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 /// \param channel  Channel number.\r
91 /// \param prescaler  Channel prescaler.\r
92 /// \param alignment  Channel alignment.\r
93 /// \param polarity  Channel polarity.\r
94 //------------------------------------------------------------------------------\r
95 void PWMC_ConfigureChannel(\r
96     unsigned char channel,\r
97     unsigned int prescaler,\r
98     unsigned int alignment,\r
99     unsigned int polarity)\r
100 {\r
101     SANITY_CHECK(prescaler < AT91C_PWMC_CPRE_MCKB);\r
102     SANITY_CHECK((alignment & ~AT91C_PWMC_CALG) == 0);\r
103     SANITY_CHECK((polarity & ~AT91C_PWMC_CPOL) == 0);\r
104 \r
105     // Disable channel\r
106     AT91C_BASE_PWMC->PWMC_DIS = 1 << channel;\r
107 \r
108     // Configure channel\r
109     AT91C_BASE_PWMC->PWMC_CH[channel].PWMC_CMR = prescaler | alignment | polarity;\r
110 }\r
111 \r
112 //------------------------------------------------------------------------------\r
113 /// Configures PWM clocks A & B to run at the given frequencies. This function\r
114 /// finds the best MCK divisor and prescaler values automatically.\r
115 /// \param clka  Desired clock A frequency (0 if not used).\r
116 /// \param clkb  Desired clock B frequency (0 if not used).\r
117 /// \param mck  Master clock frequency.\r
118 //------------------------------------------------------------------------------\r
119 void PWMC_ConfigureClocks(unsigned int clka, unsigned int clkb, unsigned int mck)\r
120 {\r
121     unsigned int mode = 0;\r
122     unsigned int result;\r
123 \r
124     // Clock A\r
125     if (clka != 0) {\r
126 \r
127         result = FindClockConfiguration(clka, mck);\r
128         ASSERT(result != 0, "-F- Could not generate the desired PWM frequency (%uHz)\n\r", clka);\r
129         mode |= result;\r
130     }\r
131 \r
132     // Clock B\r
133     if (clkb != 0) {\r
134 \r
135         result = FindClockConfiguration(clkb, mck);\r
136         ASSERT(result != 0, "-F- Could not generate the desired PWM frequency (%uHz)\n\r", clkb);\r
137         mode |= (result << 16);\r
138     }\r
139 \r
140     // Configure clocks\r
141     trace_LOG(trace_DEBUG, "-D- Setting PWMC_MR = 0x%08X\n\r", mode);\r
142     AT91C_BASE_PWMC->PWMC_MR = mode;\r
143 }\r
144 \r
145 //------------------------------------------------------------------------------\r
146 /// Sets the period value used by a PWM channel. This function writes directly\r
147 /// to the CPRD register if the channel is disabled; otherwise, it uses the\r
148 /// update register CUPD.\r
149 /// \param channel  Channel number.\r
150 /// \param period  Period value.\r
151 //------------------------------------------------------------------------------\r
152 void PWMC_SetPeriod(unsigned char channel, unsigned short period)\r
153 {\r
154     // If channel is disabled, write to CPRD\r
155     if ((AT91C_BASE_PWMC->PWMC_SR & (1 << channel)) == 0) {\r
156 \r
157         AT91C_BASE_PWMC->PWMC_CH[channel].PWMC_CPRDR = period;\r
158     }\r
159     // Otherwise use update register\r
160     else {\r
161 \r
162         AT91C_BASE_PWMC->PWMC_CH[channel].PWMC_CMR |= AT91C_PWMC_CPD;\r
163         AT91C_BASE_PWMC->PWMC_CH[channel].PWMC_CUPDR = period;\r
164     }\r
165 }\r
166 \r
167 //------------------------------------------------------------------------------\r
168 /// Sets the duty cycle used by a PWM channel. This function writes directly to\r
169 /// the CDTY register if the channel is disabled; otherwise it uses the\r
170 /// update register CUPD.\r
171 /// Note that the duty cycle must always be inferior or equal to the channel\r
172 /// period.\r
173 /// \param channel  Channel number.\r
174 /// \param duty  Duty cycle value.\r
175 //------------------------------------------------------------------------------\r
176 void PWMC_SetDutyCycle(unsigned char channel, unsigned short duty)\r
177 {\r
178     SANITY_CHECK(duty <= AT91C_BASE_PWMC->PWMC_CH[channel].PWMC_CPRDR);\r
179 \r
180     // SAM7S errata\r
181 #if defined(at91sam7s16) || defined(at91sam7s161) || defined(at91sam7s32) \\r
182     || defined(at91sam7s321) || defined(at91sam7s64) || defined(at91sam7s128) \\r
183     || defined(at91sam7s256) || defined(at91sam7s512)\r
184     ASSERT(duty > 0, "-F- Duty cycle value 0 is not permitted on SAM7S chips.\n\r");\r
185     ASSERT((duty > 1) || (AT91C_BASE_PWMC->PWMC_CH[channel].PWMC_CMR & AT91C_PWMC_CALG),\r
186            "-F- Duty cycle value 1 is not permitted in left-aligned mode on SAM7S chips.\n\r");\r
187 #endif\r
188 \r
189     // If channel is disabled, write to CDTY\r
190     if ((AT91C_BASE_PWMC->PWMC_SR & (1 << channel)) == 0) {\r
191 \r
192         AT91C_BASE_PWMC->PWMC_CH[channel].PWMC_CDTYR = duty;\r
193     }\r
194     // Otherwise use update register\r
195     else {\r
196 \r
197         AT91C_BASE_PWMC->PWMC_CH[channel].PWMC_CMR &= ~AT91C_PWMC_CPD;\r
198         AT91C_BASE_PWMC->PWMC_CH[channel].PWMC_CUPDR = duty;\r
199     }\r
200 }\r
201 \r
202 //------------------------------------------------------------------------------\r
203 /// Enables the given PWM channel. This does NOT enable the corresponding pin;\r
204 /// this must be done in the user code.\r
205 /// \param channel  Channel number.\r
206 //------------------------------------------------------------------------------\r
207 void PWMC_EnableChannel(unsigned char channel)\r
208 {\r
209     AT91C_BASE_PWMC->PWMC_ENA = 1 << channel;\r
210 }\r
211 \r
212 //------------------------------------------------------------------------------\r
213 /// Disables the given PWM channel.\r
214 /// \param channel  Channel number.\r
215 //------------------------------------------------------------------------------\r
216 void PWMC_DisableChannel(unsigned char channel)\r
217 {\r
218     AT91C_BASE_PWMC->PWMC_DIS = 1 << channel;\r
219 }\r
220 \r
221 //------------------------------------------------------------------------------\r
222 /// Enables the period interrupt for the given PWM channel.\r
223 /// \param channel  Channel number.\r
224 //------------------------------------------------------------------------------\r
225 void PWMC_EnableChannelIt(unsigned char channel)\r
226 {\r
227     AT91C_BASE_PWMC->PWMC_IER = 1 << channel;\r
228 }\r
229 \r
230 //------------------------------------------------------------------------------\r
231 /// Disables the period interrupt for the given PWM channel.\r
232 /// \param channel  Channel number.\r
233 //------------------------------------------------------------------------------\r
234 void PWMC_DisableChannelIt(unsigned char channel)\r
235 {\r
236     AT91C_BASE_PWMC->PWMC_IDR = 1 << channel;\r
237 }\r
238 \r