| 61 | | /** \defgroup Pwm PWM |
| 62 | | The Pwm subsystem provides control of the 4 PWM outputs on the SAM7X. |
| 63 | | The Pwm subsystem of the Controller Board can be used independently from the PWM_Out subsystem |
| 64 | | of the Application Board, and in fact the AppBoard PwmOut relies on the Controller Board PWM. |
| | 60 | /** \defgroup Pwm PWM (Pulse Width Modulation) |
| | 61 | The PWM subsystem provides control of the 4 PWM outputs on the SAM7X. |
| | 62 | |
| | 63 | The Make Controller has 4 PWM lines. These can each be configured separately and can control |
| | 64 | up to 2 output lines directly, the 2 lines running either parallel or inverted. For a very simple |
| | 65 | start, just see Pwm_Set( ) and Pwm_Get( ) as these will start driving your PWMs immediately with |
| | 66 | very little hassle. |
| | 67 | |
| | 68 | \section padjust Period adjustment of the PWM unit |
| | 69 | Configuring and setting the clock for the PWM system can be quite a complicated matter. Here are |
| | 70 | some of the relevant issues. |
| | 71 | |
| | 72 | Each of the 4 PWM channels is fed in a clock, as determined by its clock source: |
| | 73 | - Clock source 0-10 represent the Master clock divided by 2 to the power of the Clock Source Value. eg. a clock |
| | 74 | source value of 5 = a clock rate of MasterClock/(2^5) |
| | 75 | - A value of 11 sets the Clock source to be generated by clock Divider A (Default) |
| | 76 | - A value of 12 sets the Clock source to be generated by clock Divider B |
| | 77 | |
| | 78 | If either Clock Divider A or Clock Divider B is used, you can adjust their values individually to |
| | 79 | allow the clock period to precisely match your needs. Each clock divider has two values, a \b Mux value |
| | 80 | and a \b Divider value. |
| | 81 | |
| | 82 | The mux works just like the clock source values, and chooses Master clock divided by 2 to |
| | 83 | the power of the Clock Source Value, eg. a DividerXMux value of 5 == a clock rate of MasterClock/(2^5). |
| | 84 | The Divider value sets a linear divider, which is fed the clock value as selected by the Mux, and returns that |
| | 85 | clock value divided by the divider value. This output value is what is fed out of the divider unit. A output |
| | 86 | formula: |
| | 87 | \code output = MCLK / ( (2^DividerMux) * DividerValue ) \endcode |
| | 267 | |
| | 268 | /** |
| | 269 | Adjust the clock period on Divider A. |
| | 270 | See AT91SAM7X manual for more information p.421 |
| | 271 | |
| | 272 | Contributed by TheStigg - http://www.makingthings.com/author/thestigg |
| | 273 | @param val An int between 0 and 4096. |
| | 274 | @return 0 on success. |
| | 275 | */ |
| | 276 | int Pwm_SetDividerA(int val) |
| | 277 | { |
| | 278 | if( val < 0 || val > ( 1 << 12 ) ) |
| | 279 | return CONTROLLER_ERROR_ILLEGAL_PARAMETER_VALUE; |
| | 280 | |
| | 281 | // First disable PWM controller for all 4 channels. |
| | 282 | AT91C_BASE_PWMC->PWMC_DIS = 0x0000000f; |
| | 283 | |
| | 284 | // Now Set the new Clock values |
| | 285 | AT91C_BASE_PWMC->PWMC_MR = (AT91C_BASE_PWMC->PWMC_MR & 0xffff0000) | val; |
| | 286 | |
| | 287 | // Re-enable the active channels |
| | 288 | AT91C_BASE_PWMC->PWMC_ENA = Pwm.channels; |
| | 289 | |
| | 290 | return CONTROLLER_OK; |
| | 291 | } |
| | 292 | |
| | 293 | /** |
| | 294 | Read the clock period on Divider A. |
| | 295 | |
| | 296 | Contributed by TheStigg - http://www.makingthings.com/author/thestigg |
| | 297 | @return The clock period on Divider A. |
| | 298 | */ |
| | 299 | int Pwm_GetDividerA() |
| | 300 | { |
| | 301 | return (AT91C_BASE_PWMC->PWMC_MR & 0x0000ffff); |
| | 302 | } |
| | 303 | |
| | 304 | /** |
| | 305 | Adjust the clock period on Divider B. |
| | 306 | See AT91SAM7X manual for more information p.421 |
| | 307 | |
| | 308 | Contributed by TheStigg - http://www.makingthings.com/author/thestigg |
| | 309 | @param val An int between 0 and 4096. |
| | 310 | @return 0 on success. |
| | 311 | */ |
| | 312 | int Pwm_SetDividerB(int val) |
| | 313 | { |
| | 314 | if( val < 0 || val > ( 1 << 12 ) ) |
| | 315 | return CONTROLLER_ERROR_ILLEGAL_PARAMETER_VALUE; |
| | 316 | |
| | 317 | //New function which adjusts the clock period on Divider A. |
| | 318 | //See AT91SAM7X manual for more information p.421 |
| | 319 | |
| | 320 | // First disable PWM controller for all 4 channels. |
| | 321 | AT91C_BASE_PWMC->PWMC_DIS = 0x0000000f; |
| | 322 | |
| | 323 | // Now Set the new Clock values |
| | 324 | AT91C_BASE_PWMC->PWMC_MR = (AT91C_BASE_PWMC->PWMC_MR & 0x0000ffff) | ( val << 16 ); |
| | 325 | |
| | 326 | // Re-enable the active channels |
| | 327 | AT91C_BASE_PWMC->PWMC_ENA = Pwm.channels; |
| | 328 | |
| | 329 | return CONTROLLER_OK; |
| | 330 | } |
| | 331 | |
| | 332 | /** |
| | 333 | Read the clock period on Divider B. |
| | 334 | |
| | 335 | Contributed by TheStigg - http://www.makingthings.com/author/thestigg |
| | 336 | @return The clock period on Divider B. |
| | 337 | */ |
| | 338 | int Pwm_GetDividerB() |
| | 339 | { |
| | 340 | return (AT91C_BASE_PWMC->PWMC_MR >> 16); |
| | 341 | } |
| | 342 | |
| | 343 | /** |
| | 344 | Set the clock divider for a particular channel. |
| | 345 | For values 0-10, the Master_Clock is divided by (2^val). For example, |
| | 346 | for 4 the resulting period will be Master Clock / 16, as 2 ^ 4 == 16. |
| | 347 | |
| | 348 | When val is 11, Clock Divider A is used. When val is 12, Clock Divider B |
| | 349 | is used. Values other than 0 - 12 are not valid. |
| | 350 | |
| | 351 | Contributed by TheStigg - http://www.makingthings.com/author/thestigg |
| | 352 | @param channel The PWM channel (0-3) you'd like to configure. |
| | 353 | @param val The new clock divider. |
| | 354 | @return 0 on success. |
| | 355 | */ |
| | 356 | int Pwm_SetClockSource(int channel, int val) |
| | 357 | { |
| | 358 | if ( channel < 0 || channel > PWM_COUNT ) |
| | 359 | return CONTROLLER_ERROR_ILLEGAL_INDEX; |
| | 360 | |
| | 361 | if ( val < 0 || val > 12 ) |
| | 362 | return CONTROLLER_ERROR_ILLEGAL_PARAMETER_VALUE; |
| | 363 | |
| | 364 | AT91S_PWMC_CH *pwm = &AT91C_BASE_PWMC->PWMC_CH[ channel ]; |
| | 365 | int c = 1 << channel; |
| | 366 | |
| | 367 | // Disable the Channel |
| | 368 | AT91C_BASE_PWMC->PWMC_DIS = c; |
| | 369 | |
| | 370 | // Set the Channel Divider Value |
| | 371 | pwm->PWMC_CMR = (pwm->PWMC_CMR & 0x00000700) | val; |
| | 372 | |
| | 373 | // Re-enable the Channel |
| | 374 | AT91C_BASE_PWMC->PWMC_ENA = c; |
| | 375 | |
| | 376 | return CONTROLLER_OK; |
| | 377 | } |
| | 378 | |
| | 379 | /** |
| | 380 | Read the clock source for a particular channel. |
| | 381 | Contributed by TheStigg - http://www.makingthings.com/author/thestigg |
| | 382 | @param channel The PWM channel (0-3) whose channel you'd like to read. |
| | 383 | @return The clock source. |
| | 384 | @see Pwm_SetClockSource( ) |
| | 385 | */ |
| | 386 | int Pwm_GetClockSource(int channel) |
| | 387 | { |
| | 388 | if ( channel < 0 || channel > PWM_COUNT ) |
| | 389 | return CONTROLLER_ERROR_ILLEGAL_INDEX; |
| | 390 | |
| | 391 | return (AT91C_BASE_PWMC->PWMC_CH[ channel ].PWMC_CMR & 0x0000000f); |
| | 392 | } |
| | 393 | |
| | 394 | /** |
| | 395 | Set the wave form properties of the PWM contoller for a given channel. |
| | 396 | The waveform properties are controlled by bits 0, 1, and 2. |
| | 397 | - bit 0 sets whether the PWModule is Left aligned (0) or Center aligned (1) |
| | 398 | - bit 1 sets whether the PWMoudle's polarity starts out low (0) or high (1) |
| | 399 | - bit 2 is not supported at this time. |
| | 400 | |
| | 401 | Contributed by TheStigg - http://www.makingthings.com/author/thestigg |
| | 402 | @param channel the PWM channel (0-3) that you want to configure |
| | 403 | @param val The mask of values as described above. |
| | 404 | @return 0 on success. |
| | 405 | */ |
| | 406 | int Pwm_SetWaveformProperties(int channel, int val) |
| | 407 | { |
| | 408 | if ( channel < 0 || channel > PWM_COUNT ) |
| | 409 | return CONTROLLER_ERROR_ILLEGAL_INDEX; |
| | 410 | |
| | 411 | if ( val < 0 || val > 3 ) |
| | 412 | return CONTROLLER_ERROR_ILLEGAL_PARAMETER_VALUE; |
| | 413 | |
| | 414 | AT91S_PWMC_CH *pwm = &AT91C_BASE_PWMC->PWMC_CH[ channel ]; |
| | 415 | int c = 1 << channel; |
| | 416 | |
| | 417 | // Disable the Channel |
| | 418 | AT91C_BASE_PWMC->PWMC_DIS = c; |
| | 419 | |
| | 420 | // Set the Channel Divider Value |
| | 421 | pwm->PWMC_CMR = (pwm->PWMC_CMR & 0x0000040f) | ( val << 8 ); |
| | 422 | |
| | 423 | // Re-enable the Channel |
| | 424 | AT91C_BASE_PWMC->PWMC_ENA = c; |
| | 425 | |
| | 426 | return CONTROLLER_OK; |
| | 427 | } |
| | 428 | |
| | 429 | /** |
| | 430 | Read the waveform configuration of a specified PWM channel |
| | 431 | |
| | 432 | Contributed by TheStigg - http://www.makingthings.com/author/thestigg |
| | 433 | @param channel The PWM channel (0-3) to read from. |
| | 434 | @return A bitmask describing the waveform configuration. |
| | 435 | @see Pwm_SetWaveformProperties( ) |
| | 436 | */ |
| | 437 | int Pwm_GetWaveformProperties(int channel) |
| | 438 | { |
| | 439 | if ( channel < 0 || channel > PWM_COUNT ) |
| | 440 | return CONTROLLER_ERROR_ILLEGAL_INDEX; |
| | 441 | |
| | 442 | return ( (AT91C_BASE_PWMC->PWMC_CH[ channel ].PWMC_CMR >> 8) & 0x00000003 ); |
| | 443 | } |
| | 444 | |
| | 445 | /** @} |
| | 446 | */ |