- Main Page
- Related Pages
- Modules
- Data Structures
- Files

- File List
- Globals

# pwm.c

Go to the documentation of this file.

    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.