00001 /********************************************************************************* 00002 00003 Copyright 2006-2008 MakingThings 00004 00005 Licensed under the Apache License, 00006 Version 2.0 (the "License"); you may not use this file except in compliance 00007 with the License. You may obtain a copy of the License at 00008 00009 http://www.apache.org/licenses/LICENSE-2.0 00010 00011 Unless required by applicable law or agreed to in writing, software distributed 00012 under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 00013 CONDITIONS OF ANY KIND, either express or implied. See the License for 00014 the specific language governing permissions and limitations under the License. 00015 00016 *********************************************************************************/ 00017 00018 /** \file pwm.c 00019 PWM - Pulse Width Modulation. 00020 Library of functions for the Make Application Board's PwmOut Subsystem. 00021 */ 00022 00023 /* Library includes. */ 00024 #include <string.h> 00025 #include <stdio.h> 00026 00027 /* Scheduler includes. */ 00028 #include "FreeRTOS.h" 00029 #include "task.h" 00030 00031 /* Hardware specific headers. */ 00032 #include "Board.h" 00033 #include "AT91SAM7X256.h" 00034 00035 #include "io.h" 00036 #include "config.h" 00037 #include "pwm.h" 00038 00039 int Pwm_GetChannelIo( int channel ); 00040 00041 #define PWM_DUTY_MAX 1024 00042 00043 #define PWM_COUNT 4 00044 00045 #define PWM_CHANNEL_0_IO IO_PB19 00046 #define PWM_CHANNEL_1_IO IO_PB20 00047 #define PWM_CHANNEL_2_IO IO_PB21 00048 #define PWM_CHANNEL_3_IO IO_PB22 00049 00050 static int Pwm_Init( void ); 00051 static int Pwm_Deinit( void ); 00052 00053 struct Pwm_ 00054 { 00055 int users; 00056 int channels; 00057 int duty[ PWM_COUNT ]; 00058 } Pwm; 00059 00060 /** \defgroup Pwm PWM (Pulse Width Modulation) 00061 The PWM subsystem provides control of the 4 PWM outputs on the SAM7X. 00062 00063 The Make Controller has 4 PWM lines. These can each be configured separately and can control 00064 up to 2 output lines directly, the 2 lines running either parallel or inverted. For a very simple 00065 start, just see Pwm_Set( ) and Pwm_Get( ) as these will start driving your PWMs immediately with 00066 very little hassle. 00067 00068 \section padjust Period adjustment of the PWM unit 00069 Configuring and setting the clock for the PWM system can be quite a complicated matter. Here are 00070 some of the relevant issues. 00071 00072 Each of the 4 PWM channels is fed in a clock, as determined by its clock source: 00073 - Clock source 0-10 represent the Master clock divided by 2 to the power of the Clock Source Value. eg. a clock 00074 source value of 5 = a clock rate of MasterClock/(2^5) 00075 - A value of 11 sets the Clock source to be generated by clock Divider A (Default) 00076 - A value of 12 sets the Clock source to be generated by clock Divider B 00077 00078 If either Clock Divider A or Clock Divider B is used, you can adjust their values individually to 00079 allow the clock period to precisely match your needs. Each clock divider has two values, a \b Mux value 00080 and a \b Divider value. 00081 00082 The mux works just like the clock source values, and chooses Master clock divided by 2 to 00083 the power of the Clock Source Value, eg. a DividerXMux value of 5 == a clock rate of MasterClock/(2^5). 00084 The Divider value sets a linear divider, which is fed the clock value as selected by the Mux, and returns that 00085 clock value divided by the divider value. This output value is what is fed out of the divider unit. A output 00086 formula: 00087 \code output = MCLK / ( (2^DividerMux) * DividerValue ) \endcode 00088 00089 The PWM subsystem of the Controller Board can be used independently from the \ref PwmOut 00090 library, since the \ref PwmOut library relies on the core PWM. 00091 00092 \ingroup Core 00093 @{ 00094 */ 00095 00096 /** 00097 Set the duty of a PWM device. 00098 @param index An integer specifying which PWM device (0-3). 00099 @param duty The duty - (0 - 1023). 00100 @return 0 on success. 00101 */ 00102 int Pwm_Set( int index, int duty ) 00103 { 00104 if ( index < 0 || index >= PWM_COUNT ) 00105 return CONTROLLER_ERROR_ILLEGAL_INDEX; 00106 00107 // Set the duty 00108 Pwm.duty[ index ] = duty; 00109 AT91C_BASE_PWMC->PWMC_CH[ index ].PWMC_CUPDR = duty; 00110 00111 return CONTROLLER_OK; 00112 } 00113 00114 /** 00115 Read the current duty of a PWM device. 00116 @param index An integer specifying which PWM device (0-3). 00117 @return The duty - (0 - 1023). 00118 */ 00119 int Pwm_Get( int index ) 00120 { 00121 if ( index < 0 || index >= PWM_COUNT ) 00122 return CONTROLLER_ERROR_ILLEGAL_INDEX; 00123 00124 return Pwm.duty[ index ]; 00125 } 00126 00127 int Pwm_Start( int channel ) 00128 { 00129 int status; 00130 00131 if ( channel < 0 || channel >= PWM_COUNT ) 00132 return CONTROLLER_ERROR_ILLEGAL_INDEX; 00133 00134 // Make sure the channel isn't already being used 00135 int c = 1 << channel; 00136 if ( c & Pwm.channels ) 00137 return CONTROLLER_ERROR_CANT_LOCK; 00138 00139 // lock the correct select line 00140 int io = Pwm_GetChannelIo( channel ); 00141 // Try to lock the pin 00142 status = Io_Start( io, true ); 00143 if ( status != CONTROLLER_OK ) 00144 return status; 00145 00146 // mark the channel as being used 00147 Pwm.channels |= c; 00148 00149 // Disable the PIO for the IO Line 00150 Io_SetPio( io, false ); 00151 00152 Io_SetPeripheralA( io ); 00153 00154 if ( Pwm.users++ == 0 ) 00155 { 00156 status = Pwm_Init(); 00157 } 00158 00159 // Enable the current channel 00160 AT91C_BASE_PWMC->PWMC_ENA = 1 << channel; 00161 00162 return CONTROLLER_OK; 00163 } 00164 00165 int Pwm_Stop( int channel ) 00166 { 00167 if ( Pwm.users <= 0 ) 00168 return CONTROLLER_ERROR_TOO_MANY_STOPS; 00169 00170 if ( channel < 0 || channel >= PWM_COUNT ) 00171 return CONTROLLER_ERROR_ILLEGAL_INDEX; 00172 00173 int c = 1 << channel; 00174 if ( !( c & Pwm.channels ) ) 00175 return CONTROLLER_ERROR_NOT_LOCKED; 00176 00177 // Get the IO 00178 int io = Pwm_GetChannelIo( channel ); 00179 00180 // Set the pin to OFF 00181 Io_SetValue( io, false ); 00182 Io_SetPio( io, true ); 00183 00184 // release the pin 00185 Io_Stop( io ); 00186 00187 Pwm.channels &= ~c; 00188 00189 // Disable the PWM's 00190 AT91C_BASE_PWMC->PWMC_DIS = 1 << channel; 00191 00192 if ( --Pwm.users == 0 ) 00193 Pwm_Deinit(); 00194 00195 return CONTROLLER_OK; 00196 } 00197 00198 int Pwm_Init() 00199 { 00200 // Configure PMC by enabling PWM clock 00201 AT91C_BASE_PMC->PMC_PCER = 1 << AT91C_ID_PWMC; 00202 00203 // Initially they're stopped 00204 AT91C_BASE_PWMC->PWMC_DIS = AT91C_PWMC_CHID0 | AT91C_PWMC_CHID1 | AT91C_PWMC_CHID2 | AT91C_PWMC_CHID3; 00205 00206 // Set the Clock A divider 00207 AT91C_BASE_PWMC->PWMC_MR = (( 4 << 8 ) | 0x08 ); // MCK selection or'ed with Divider 00208 00209 // Set the Clock 00210 int i; 00211 for ( i = 0; i < 4; i++ ) 00212 { 00213 AT91S_PWMC_CH *pwm = &AT91C_BASE_PWMC->PWMC_CH[ i ]; 00214 00215 pwm->PWMC_CMR = 00216 // AT91C_PWMC_CPRE_MCK | // Divider Clock ? 00217 AT91C_PWMC_CPRE_MCKA | //Divider Clock A 00218 // AT91C_PWMC_CPRE_MCKB; //Divider Clock B 00219 // AT91C_PWMC_CPD; // Channel Update Period 00220 AT91C_PWMC_CPOL; // Channel Polarity Invert 00221 // AT91C_PWMC_CALG ; // Channel Alignment Center 00222 00223 // Set the Period register (sample size bit fied ) 00224 pwm->PWMC_CPRDR = PWM_DUTY_MAX; 00225 00226 // Set the duty cycle register (output value) 00227 pwm->PWMC_CDTYR = 0; 00228 00229 // Initialise the Update register write only 00230 pwm->PWMC_CUPDR = 0 ; 00231 } 00232 00233 return CONTROLLER_OK; 00234 } 00235 00236 int Pwm_Deinit() 00237 { 00238 // Deconfigure PMC by disabling the PWM clock 00239 AT91C_BASE_PMC->PMC_PCDR = 1 << AT91C_ID_PWMC; 00240 00241 return CONTROLLER_OK; 00242 } 00243 00244 int Pwm_GetChannelIo( int channel ) 00245 { 00246 int io; 00247 switch ( channel ) 00248 { 00249 case 0: 00250 io = PWM_CHANNEL_0_IO; 00251 break; 00252 case 1: 00253 io = PWM_CHANNEL_1_IO; 00254 break; 00255 case 2: 00256 io = PWM_CHANNEL_2_IO; 00257 break; 00258 case 3: 00259 io = PWM_CHANNEL_3_IO; 00260 break; 00261 default: 00262 io = 0; 00263 break; 00264 } 00265 return io; 00266 } 00267 00268 /** 00269 Adjust the clock period on Divider A. 00270 See AT91SAM7X manual for more information p.421 00271 00272 Contributed by TheStigg - http://www.makingthings.com/author/thestigg 00273 @param val An int between 0 and 4096. 00274 @return 0 on success. 00275 */ 00276 int Pwm_SetDividerA(int val) 00277 { 00278 if( val < 0 || val > ( 1 << 12 ) ) 00279 return CONTROLLER_ERROR_ILLEGAL_PARAMETER_VALUE; 00280 00281 // First disable PWM controller for all 4 channels. 00282 AT91C_BASE_PWMC->PWMC_DIS = 0x0000000f; 00283 00284 // Now Set the new Clock values 00285 AT91C_BASE_PWMC->PWMC_MR = (AT91C_BASE_PWMC->PWMC_MR & 0xffff0000) | val; 00286 00287 // Re-enable the active channels 00288 AT91C_BASE_PWMC->PWMC_ENA = Pwm.channels; 00289 00290 return CONTROLLER_OK; 00291 } 00292 00293 /** 00294 Read the clock period on Divider A. 00295 00296 Contributed by TheStigg - http://www.makingthings.com/author/thestigg 00297 @return The clock period on Divider A. 00298 */ 00299 int Pwm_GetDividerA() 00300 { 00301 return (AT91C_BASE_PWMC->PWMC_MR & 0x0000ffff); 00302 } 00303 00304 /** 00305 Adjust the clock period on Divider B. 00306 See AT91SAM7X manual for more information p.421 00307 00308 Contributed by TheStigg - http://www.makingthings.com/author/thestigg 00309 @param val An int between 0 and 4096. 00310 @return 0 on success. 00311 */ 00312 int Pwm_SetDividerB(int val) 00313 { 00314 if( val < 0 || val > ( 1 << 12 ) ) 00315 return CONTROLLER_ERROR_ILLEGAL_PARAMETER_VALUE; 00316 00317 //New function which adjusts the clock period on Divider A. 00318 //See AT91SAM7X manual for more information p.421 00319 00320 // First disable PWM controller for all 4 channels. 00321 AT91C_BASE_PWMC->PWMC_DIS = 0x0000000f; 00322 00323 // Now Set the new Clock values 00324 AT91C_BASE_PWMC->PWMC_MR = (AT91C_BASE_PWMC->PWMC_MR & 0x0000ffff) | ( val << 16 ); 00325 00326 // Re-enable the active channels 00327 AT91C_BASE_PWMC->PWMC_ENA = Pwm.channels; 00328 00329 return CONTROLLER_OK; 00330 } 00331 00332 /** 00333 Read the clock period on Divider B. 00334 00335 Contributed by TheStigg - http://www.makingthings.com/author/thestigg 00336 @return The clock period on Divider B. 00337 */ 00338 int Pwm_GetDividerB() 00339 { 00340 return (AT91C_BASE_PWMC->PWMC_MR >> 16); 00341 } 00342 00343 /** 00344 Set the clock divider for a particular channel. 00345 For values 0-10, the Master_Clock is divided by (2^val). For example, 00346 for 4 the resulting period will be Master Clock / 16, as 2 ^ 4 == 16. 00347 00348 When val is 11, Clock Divider A is used. When val is 12, Clock Divider B 00349 is used. Values other than 0 - 12 are not valid. 00350 00351 Contributed by TheStigg - http://www.makingthings.com/author/thestigg 00352 @param channel The PWM channel (0-3) you'd like to configure. 00353 @param val The new clock divider. 00354 @return 0 on success. 00355 */ 00356 int Pwm_SetClockSource(int channel, int val) 00357 { 00358 if ( channel < 0 || channel >= PWM_COUNT ) 00359 return CONTROLLER_ERROR_ILLEGAL_INDEX; 00360 00361 if ( val < 0 || val > 12 ) 00362 return CONTROLLER_ERROR_ILLEGAL_PARAMETER_VALUE; 00363 00364 AT91S_PWMC_CH *pwm = &AT91C_BASE_PWMC->PWMC_CH[ channel ]; 00365 int c = 1 << channel; 00366 00367 // Disable the Channel 00368 AT91C_BASE_PWMC->PWMC_DIS = c; 00369 00370 // Set the Channel Divider Value 00371 pwm->PWMC_CMR = (pwm->PWMC_CMR & 0x00000700) | val; 00372 00373 // Re-enable the Channel 00374 AT91C_BASE_PWMC->PWMC_ENA = c; 00375 00376 return CONTROLLER_OK; 00377 } 00378 00379 /** 00380 Read the clock source for a particular channel. 00381 Contributed by TheStigg - http://www.makingthings.com/author/thestigg 00382 @param channel The PWM channel (0-3) whose channel you'd like to read. 00383 @return The clock source. 00384 @see Pwm_SetClockSource( ) 00385 */ 00386 int Pwm_GetClockSource(int channel) 00387 { 00388 if ( channel < 0 || channel >= PWM_COUNT ) 00389 return CONTROLLER_ERROR_ILLEGAL_INDEX; 00390 00391 return (AT91C_BASE_PWMC->PWMC_CH[ channel ].PWMC_CMR & 0x0000000f); 00392 } 00393 00394 /** 00395 Set the wave form properties of the PWM contoller for a given channel. 00396 The waveform properties are controlled by bits 0, 1, and 2. 00397 - bit 0 sets whether the PWModule is Left aligned (0) or Center aligned (1) 00398 - bit 1 sets whether the PWMoudle's polarity starts out low (0) or high (1) 00399 - bit 2 is not supported at this time. 00400 00401 Contributed by TheStigg - http://www.makingthings.com/author/thestigg 00402 @param channel the PWM channel (0-3) that you want to configure 00403 @param val The mask of values as described above. 00404 @return 0 on success. 00405 */ 00406 int Pwm_SetWaveformProperties(int channel, int val) 00407 { 00408 if ( channel < 0 || channel >= PWM_COUNT ) 00409 return CONTROLLER_ERROR_ILLEGAL_INDEX; 00410 00411 if ( val < 0 || val > 3 ) 00412 return CONTROLLER_ERROR_ILLEGAL_PARAMETER_VALUE; 00413 00414 AT91S_PWMC_CH *pwm = &AT91C_BASE_PWMC->PWMC_CH[ channel ]; 00415 int c = 1 << channel; 00416 00417 // Disable the Channel 00418 AT91C_BASE_PWMC->PWMC_DIS = c; 00419 00420 // Set the Channel Divider Value 00421 pwm->PWMC_CMR = (pwm->PWMC_CMR & 0x0000040f) | ( val << 8 ); 00422 00423 // Re-enable the Channel 00424 AT91C_BASE_PWMC->PWMC_ENA = c; 00425 00426 return CONTROLLER_OK; 00427 } 00428 00429 /** 00430 Read the waveform configuration of a specified PWM channel 00431 00432 Contributed by TheStigg - http://www.makingthings.com/author/thestigg 00433 @param channel The PWM channel (0-3) to read from. 00434 @return A bitmask describing the waveform configuration. 00435 @see Pwm_SetWaveformProperties( ) 00436 */ 00437 int Pwm_GetWaveformProperties(int channel) 00438 { 00439 if ( channel < 0 || channel >= PWM_COUNT ) 00440 return CONTROLLER_ERROR_ILLEGAL_INDEX; 00441 00442 return ( (AT91C_BASE_PWMC->PWMC_CH[ channel ].PWMC_CMR >> 8) & 0x00000003 ); 00443 } 00444 00445 /** @} 00446 */
The Make Controller Kit is an open source project maintained by MakingThings.
MakingThings code is released under the Apache 2.0 license.
Bug tracker, development wiki and status can be found at http://dev.makingthings.com.
This document was last updated on 18 May 2009.