📄 pwm.c
字号:
/*
* LPC2106 PWM manipulation.
* Author: Bruce McKenney, BMC Consulting, Selkirk, NY
*/
#include <lpc210x.h>
#include "pwm.h"
/*
* Bit positions in PWM_TCR:
*/
#define PWM_TCR_COUNTER_ENABLE 0x01
#define PWM_TCR_RESET 0x02
// reserved: 0x04
#define PWM_TCR_PWM_ENABLE 0x08
/*
* Syntax macro to set the appropriate PWM MRx and latch, mainly
* so we don't forget to do both.
*
* It's unclear whether the latch is strictly needed for MR0.
*/
#define PWM_MRSET(_pwm,_val) do { \
*MRaddr[_pwm] = (_val); \
PWM_LER |= (1 << (_pwm)); \
} while(0)
/*
* This table is used by the macro above. It is a concession to the
* fact that the MRn registers aren't consecutive.
*/
static volatile unsigned long * const __attribute__((section(".text"))) MRaddr[7] = {
&PWM_MR0, &PWM_MR1, &PWM_MR2, &PWM_MR3, &PWM_MR4, &PWM_MR5, &PWM_MR6
};
/*
* pwm_check():
* Check for a valid channel.
* If you're Sure you'll never make a mistake, you can throw this out.
*/
static __inline__ unsigned int pwm_check(unsigned int chan)
{
switch(chan) {
case PWM2: case PWM4: case PWM6:
return(1);
default:
return(0);
}
}
/*
* pwm_pin():
* Return the P0.x pin associated with the given channel.
* The astute reader will observe that this is 6+(chan/2) for the
* even-numbered PWMs, but it's also only used in pwm_chan_init,
* so cost doesn't matter much.
*/
static __inline__ unsigned int pwm_pin(unsigned int chan)
{
switch(chan) {
case PWM2: return(7); /* P0.7 */
case PWM4: return(8); /* P0.8 */
case PWM6: return(9); /* P0.9 */
default: return((unsigned)-1); /* preposterous number */
}
}
/*
* pwm_set():
* Set duty output for channel
* The duty value is expected to be [0..nticks] (setting > nticks
* results in a 100% duty cycle, since no match ever happens).
* The way the prescaler is set, this value can be used without scaling.
*/
void
pwm_set(unsigned int chan, unsigned int duty)
{
if (!pwm_check(chan))
return; /* safety first */
PWM_MRSET(chan,duty); /* store MRx */
return;
}
/*
* pwm_chan_init():
* Start up a particular channel.
* initduty is the initial duty cycle setting (a la pwm_set). 0 is a
* fine value, but e.g. servos might like the center pulse instead,
* and doing it here avoids glitches.
*/
void
pwm_chan_init(unsigned int chan, unsigned int initduty)
{
unsigned int pin;
if (!pwm_check(chan))
return; /* safety first */
pin = pwm_pin(chan);
/*
* Set initial duty cycle
*/
PWM_MRSET(chan, initduty);
/*
* Set for alternate function: PWM.
* (We know all even-numbered PWMs are in PINSEL0.)
*/
PINSEL0 = (PINSEL0 & ~(3 << (2*pin))) /* all bits off */
| (2 << (2*pin)); /* value=2 */
/*
* PWMSELn = 0 (single edge); PWMENAn = 1 (enable driver)
*/
PWM_PCR = (PWM_PCR & ~(1 << chan)) | (1 << (8+chan));
return;
}
/*
* pwm_init()
* Starts the PWM timer
* nticks is number of divisions in the range
* tickcycles is the size of the division (pclk/CPU clocks)
* (The PWM period is thus nticks*tickcycles.)
*/
void
pwm_init(unsigned int nticks, unsigned int tickcycles)
{
PWM_TCR = 0; /* disable timer initially */
/*
* Safety first: Since these are decremented, 0 values are anomalous
*/
if (nticks == 0 || tickcycles == 0)
return;
/*
* Clear the Prescale Counter, for no real good reason.
*/
PWM_PC = 0;
/*
* Clear the Timer Counter, so we don't miss the first match
* and wait 60+ seconds for the TC to cycle.
*/
PWM_TC = 0;
/*
* The prescaler is set to the time (in pclk(=CPU) cycles)
* for one tick. This allows us to store the pwm_set argument
* directly in the MRx without any scaling.
*/
PWM_PR = tickcycles - 1;
/*
* Set PWM period (MR0) as the number of ticks
* Despite the diagram in the UM which suggests the match value
* needs to be decremented, experiment indicates this usage gives
* the correct result. Go figure.
*/
PWM_MRSET(0, nticks);
/*
* Set to clear TC on a PWMMR0 match (so it cycles as we want).
*/
PWM_MCR = (PWM_MCR & ~(7 << (3*0))) /* all bits off */
| (2 << (3*0)); /* clear-on-match option */
/*
* Two-step sequence to start the timer: First enables and resets,
* the second turns off the reset so it runs.
*/
PWM_TCR = PWM_TCR_PWM_ENABLE |
PWM_TCR_COUNTER_ENABLE |
PWM_TCR_RESET; /* PWM enable, reset, T/C enable */
PWM_TCR = PWM_TCR_PWM_ENABLE |
PWM_TCR_COUNTER_ENABLE ; /* PWM enable, T/C enable */
return;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -