⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 pwm.c

📁 LPC2129 pwm sample code
💻 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 + -