码迷,mamicode.com
首页 > 其他好文 > 详细

LCP1549之SCT_PWM 输出功能分析

时间:2015-05-10 06:24:17      阅读:875      评论:0      收藏:0      [点我收藏+]

标签:pwm   lpc1549   sct_pwm   system_init   systick   

本文章以periph_sct_pwm.ewp为例,基于IAR工程,深入分析lpc1549的sct_pwm输出功能,首先给出工程main函数代码,对整个工程有个大概了解:

/* Example entry point */
int main(void)
{
	uint32_t cnt1 = 0, cnt2 = 0;
	int led_dp = 0, led_step = 1, out_dp = 0;

	/* Generic Initialization */
	SystemCoreClockUpdate(); // 更新全局系统时钟变量值
	Board_Init();<span style="white-space:pre">		</span> // 开关矩阵中初始化gpio默认m模式配置,初始化USART0,初始化板载RGB LED。

	/* Initialize the SCT as PWM and set frequency */
	Chip_SCTPWM_Init(SCT_PWM);<span style="white-space:pre">			</span>// 初始化sct(state-configurable timers)时钟,复位PWM00
	Chip_SCTPWM_SetRate(SCT_PWM, SCT_PWM_RATE); <span style="white-space:pre">	</span>// 初始化计数匹配寄存器0及PWM事件0

	/* Setup Board specific output pin */
	app_setup_pin();<span style="white-space:pre">	</span>// 配置PWM输出通道0、1所连接到的引脚

	/* Use SCT0_OUT1 pin */
	Chip_SCTPWM_SetOutPin(SCT_PWM, SCT_PWM_OUT, SCT_PWM_PIN_OUT);<span style="white-space:pre">	</span>// 配置PWM事件1
	Chip_SCTPWM_SetOutPin(SCT_PWM, SCT_PWM_LED, SCT_PWM_PIN_LED);<span style="white-space:pre">	</span>// 配置PWM事件2

	/* Start with 0% duty cycle */
	Chip_SCTPWM_SetDutyCycle(SCT_PWM, SCT_PWM_OUT, Chip_SCTPWM_GetTicksPerCycle(SCT_PWM)/2);<span style="white-space:pre">	</span>// 配置匹配捕获寄存器1
	Chip_SCTPWM_SetDutyCycle(SCT_PWM, SCT_PWM_LED, 0);<span style="white-space:pre">						</span>// 配置匹配捕获寄存器2
	Chip_SCTPWM_Start(SCT_PWM);<span style="white-space:pre">	</span>// 启动 SCT定时器


	/* Enable SysTick Timer */
	SysTick_Config(SystemCoreClock / TICKRATE_HZ);<span style="white-space:pre">	</span>// 配置使能SYSTICK定时器

	while (1) {
		cnt1 ++;
		cnt2 ++;
		if (cnt1 >= OUT_STEP_CNT) {
			out_dp += 10;
			if (out_dp > 100) {//13979097921
				out_dp = 0;
			}

			/* Increase dutycycle by 10% every second */
			Chip_SCTPWM_SetDutyCycle(SCT_PWM, SCT_PWM_OUT,
				Chip_SCTPWM_PercentageToTicks(SCT_PWM, out_dp));<span style="white-space:pre">	</span>// 该引脚占空比由小到大循环增大 
			cnt1 = 0;
		}

		if (cnt2 >= LED_STEP_CNT) {
			led_dp += led_step;
			if (led_dp < 0) {<span style="white-space:pre">	</span>//改变占空比为逐渐增加
				led_dp = 0;
				led_step = 1;
			}
			if (led_dp > 200) {<span style="white-space:pre">	</span>// 改变占空比为逐渐减小
				led_dp = 200;
				led_step = -1;
			}

			/* Increment or Decrement Dutycycle by 0.5% every 10ms */
			Chip_SCTPWM_SetDutyCycle(SCT_PWM, SCT_PWM_LED,
				Chip_SCTPWM_PercentageToTicks(SCT_PWM, led_dp)/2);<span style="white-space:pre">	</span>// 根据led_dp值更改占空比,实现呼吸灯的效果
			cnt2 = 0;
		}
		__WFI();<span style="white-space:pre">	</span>// 系统休眠
	}
	return 0;
}

在分析main函数中调用的函数实现之前,首先分析系统上电后运行的一个函数,SystemInit(),该函数开发者很少关注,却是关系到系统时钟的最重要的一个函数,如下:

void SystemInit(void)

该函数为系统上电后执行的第一个初始化用户函数(除去.s及IAR封装的函数),其原型如下:
<span style="font-size:12px;font-weight: normal;">void SystemInit(void)
{
#if defined(NO_BOARD_LIB)// 是否使用板载库文件
	/* Chip specific SystemInit */
	Chip_SystemInit();// 基于LPC1549单芯片工程调用此函数
#else
	/* Board specific SystemInit */
	Board_SystemInit();// 基于LPC1549开发板工程调用此函数
#endif
}</span>

继续分析Board_SystemInit();

<span style="font-size:12px;font-weight: normal;">/* Set up and initialize hardware prior to call to main */
void Board_SystemInit(void)
{
	/* Setup system clocking and muxing */
	Board_SetupMuxing(); // 初始化GPIO复用,引脚的功能模式即外设引脚复用
	Board_SetupClocking();// 设置系统主时钟源即频率大小,USB及SYSTICK时钟

	/* Set SYSTICKDIV to 1 so CMSIS Systick functions work */
	LPC_SYSCTL->SYSTICKCLKDIV = 1;  // 上面API已设置了
}</span>
首先看第1个函数实现:

<span style="font-size:12px;font-weight: normal;">/* Sets up system pin muxing */
void Board_SetupMuxing(void)
{
	int i;

	/* Enable SWM and IOCON clocks */
	Chip_Clock_EnablePeriphClock(SYSCTL_CLOCK_IOCON); // 开启GIOCON控制器时钟
	Chip_Clock_EnablePeriphClock(SYSCTL_CLOCK_SWM); // 开启开关矩阵时钟
	Chip_SYSCTL_PeriphReset(RESET_IOCON); // 复位IOCON控制器

	/* IOCON setup */ // 初始化IO引脚的功能模式,比如设定为数字引脚还是模拟引脚,ioconsetup表中包含默认的io模式配置
	Chip_IOCON_SetPinMuxing(LPC_IOCON, ioconSetup, sizeof(ioconSetup) / sizeof(PINMUX_GRP_T));

	/* SWM assignable pin setup */// 开关矩阵设置,设置引脚的复用功能,比如,指定为某个外设的某些引脚,串口引脚及RGB三个引脚就包含在swmSetup表中
	for (i = 0; i < (sizeof(swmSetup) / sizeof(SWM_GRP_T)); i++) {
		Chip_SWM_MovablePortPinAssign((CHIP_SWM_PIN_MOVABLE_T) swmSetup[i].assignedpin,
									  swmSetup[i].port, swmSetup[i].pin);
	}

	/* SWM fixed pin setup */
	//	LPC_SWM->PINENABLE[0] = PINENABLE0_VAL;
	//	LPC_SWM->PINENABLE[1] = PINENABLE1_VAL;

	/* Note SWM and IOCON clocks are left on */
}</span>

第二个函数的实现

<span style="font-size:12px;font-weight: normal;">/* Set up and initialize clocking prior to call to main */
void Board_SetupClocking(void)
{
	Chip_SetupXtalClocking();// 设置使用晶振

	/* Set USB PLL input to main oscillator */
	Chip_Clock_SetUSBPLLSource(SYSCTL_PLLCLKSRC_MAINOSC);// 将主时钟源作为USB时钟
	/* Setup USB PLL  (FCLKIN = 12MHz) * 4 = 48MHz
	   MSEL = 3 (this is pre-decremented), PSEL = 1 (for P = 2)
	   FCLKOUT = FCLKIN * (MSEL + 1) = 12MHz * 4 = 48MHz
	   FCCO = FCLKOUT * 2 * P = 48MHz * 2 * 2 = 192MHz (within FCCO range) */
	Chip_Clock_SetupUSBPLL(3, 1);

	/* Powerup USB PLL */
	Chip_SYSCTL_PowerUp(SYSCTL_POWERDOWN_USBPLL_PD);// 启动USB时钟

	/* Wait for PLL to lock */
	while (!Chip_Clock_IsUSBPLLLocked()) {}// 等待PLL上锁

	/* Set default system tick divder to 1 */
	Chip_Clock_SetSysTickClockDiv(1);// systick不分频,使用系统72MHz时钟源
}</span>

Chip_SetupXtalClocking()的函数实现如下:

<span style="font-size:12px;font-weight: normal;">/* Clock and PLL initialization based on the external oscillator */
void Chip_SetupXtalClocking(void)
{
	volatile int i;

	/* Powerup main oscillator */
	Chip_SYSCTL_PowerUp(SYSCTL_POWERDOWN_SYSOSC_PD);// 系统振荡器上电

	/* Wait 200us for OSC to be stablized, no status
	   indication, dummy wait. */
	for (i = 0; i < 0x200; i++) {} // 等待稳定,仅仅软件延时

	/* Set system PLL input to main oscillator */ // 系统PLL时钟源选择为晶体时钟
	Chip_Clock_SetSystemPLLSource(SYSCTL_PLLCLKSRC_MAINOSC);

	/* Power down PLL to change the PLL divider ratio */
	Chip_SYSCTL_PowerDown(SYSCTL_POWERDOWN_SYSPLL_PD); // 系统PLL掉电

	/* Setup PLL for main oscillator rate (FCLKIN = 12MHz) * 6 = 72MHz
	   MSEL = 5 (this is pre-decremented), PSEL = 1 (for P = 2)
	   FCLKOUT = FCLKIN * (MSEL + 1) = 12MHz * 6 = 72MHz
	   FCCO = FCLKOUT * 2 * P = 72MHz * 2 * 2 = 288MHz (within FCCO range) */
	Chip_Clock_SetupSystemPLL(5, 2); // 设置PLL的参数值,得到PLL输出时钟为72MHz,具体公式不做介绍

	/* Powerup system PLL */
	Chip_SYSCTL_PowerUp(SYSCTL_POWERDOWN_SYSPLL_PD);// 等待PLL上电

	/* Wait for PLL to lock */
	while (!Chip_Clock_IsSystemPLLLocked()) {} // 等待PLL上锁

	/* Set system clock divider to 1 */
	Chip_Clock_SetSysClockDiv(1); // 设置系统时钟分频,即AHB时钟,同样为72MHz

	/* Setup FLASH access timing for 72MHz */
	Chip_FMC_SetFLASHAccess(SYSCTL_FLASHTIM_72MHZ_CPU);// 设置Flash 在72MHz下访问,一次执行使用三个系统周期

	/* Set main clock source to the system PLL. This will drive 72MHz
	   for the main clock */
	Chip_Clock_SetMainClockSource(SYSCTL_MAINCLKSRC_SYSPLLOUT); // 将PLL输出时钟作为系统主时钟,即72MHz
}</span>
经过上述初始化的配置,系统IO模式及我们要用到的USART、RGB引脚配置完成,同时系统时钟也已经配置好了。接下来分析main函数中的函数实现:

SystemCoreClockUpdate( )

该函数获得系统的核心时钟频率,内容很简单,只有一行代码:

/* Update system core clock rate, should be called if the system has
   a clock rate change */
void SystemCoreClockUpdate(void)
{
	/* CPU core speed */
	SystemCoreClock = Chip_Clock_GetSystemClockRate();
}


Chip_Clock_GetSystemClockRate( )实现如下:

/* Return system clock rate */
uint32_t Chip_Clock_GetSystemClockRate(void)
{
	/* No point in checking for divide by 0 */
	return Chip_Clock_GetMainClockRate() / LPC_SYSCTL->SYSAHBCLKDIV;<span style="white-space:pre">	</span>// 系统主时钟频率除以AHB分频,其实得到的就是AHB时钟,LPC_SYSCTL->SYSAHBCLKDIV由之前的设置可知为1分频
}
Chip_Clock_GetMainClockRate( ) 实现如下:

/* Return main clock rate */
uint32_t Chip_Clock_GetMainClockRate(void)
{
	uint32_t clkRate;

	if (Chip_Clock_GetMain_B_ClockSource() == SYSCTL_MAIN_B_CLKSRC_MAINCLKSELA) {<span style="white-space:pre">	</span>// 判断B时钟源,如果使用内部晶振,则B时钟源就是内部A时钟源,否则为外部B时钟源
		/* Return main A clock rate */
		clkRate = Chip_Clock_GetMain_A_ClockRate();
	}
	else {
		/* Return main B clock rate */
		clkRate = Chip_Clock_GetMain_B_ClockRate();<span style="white-space:pre">	</span>// 之前已提到,使用板载晶振,来到此处
	}

	return clkRate;
}
Chip_Clock_GetMain_B_ClockRate( ) 实现如下:

/* Return main B clock rate */
uint32_t Chip_Clock_GetMain_B_ClockRate(void)
{
	uint32_t clkRate = 0;

	switch (Chip_Clock_GetMain_B_ClockSource()) {<span style="white-space:pre">	</span>// 该函数调用见下一个函数描述
	case SYSCTL_MAIN_B_CLKSRC_MAINCLKSELA:
		clkRate = Chip_Clock_GetMain_A_ClockRate();
		break;

	case SYSCTL_MAIN_B_CLKSRC_SYSPLLIN:
		clkRate = Chip_Clock_GetSystemPLLInClockRate();
		break;

	case SYSCTL_MAIN_B_CLKSRC_SYSPLLOUT:<span style="white-space:pre">		</span>// 来到此处,B时钟为PLL倍频输出
		clkRate = Chip_Clock_GetSystemPLLOutClockRate();<span style="white-space:pre">	</span>// 获取PLL倍频输出频率<span style="white-space:pre">	</span>
		break;

	case SYSCTL_MAIN_B_CLKSRC_RTC:
		clkRate = Chip_Clock_GetRTCOscRate();
		break;
	}

	return clkRate;
}
Chip_Clock_GetMain_B_ClockSource( ) 实现如下:

STATIC INLINE CHIP_SYSCTL_MAIN_B_CLKSRC_T Chip_Clock_GetMain_B_ClockSource(void)
{
	return (CHIP_SYSCTL_MAIN_B_CLKSRC_T) (LPC_SYSCTL->MAINCLKSELB);<span style="white-space:pre">	</span>// 由之前的设定可知,此处为PLL倍频输出
}
Chip_Clock_GetSystemPLLOutClockRate()实现如下:此处连续贴出4个函数,从下往上看

晶振时钟源:

STATIC INLINE uint32_t Chip_Clock_GetMainOscRate(void)
{
	return OscRateIn;<span style="white-space:pre">	</span>// 该变量为晶振时钟频率,为全局const变量,const uint32_t OscRateIn = 12000000; // 此处设定晶振的时钟频率
}

/* Return a PLL input (common) */
STATIC uint32_t Chip_Clock_GetPLLInClockRate(uint32_t reg)
{
	uint32_t clkRate;

	switch ((CHIP_SYSCTL_PLLCLKSRC_T) (reg & 0x3)) {<span style="white-space:pre">	</span>// 根据设置得到PLL输入时钟源
	case SYSCTL_PLLCLKSRC_IRC:
		clkRate = Chip_Clock_GetIntOscRate();
		break;

	case SYSCTL_PLLCLKSRC_MAINOSC:
		clkRate = Chip_Clock_GetMainOscRate();<span style="white-space:pre">		</span>// 来到此处,本例中为晶振时钟输入,见上个函数描述
		break;

	default:
		clkRate = 0;
	}

	return clkRate;
}

/*****************************************************************************
 * Public functions
 ****************************************************************************/

/* Return System PLL input clock rate */
uint32_t Chip_Clock_GetSystemPLLInClockRate(void)
{
	return Chip_Clock_GetPLLInClockRate(LPC_SYSCTL->SYSPLLCLKSEL);<span style="white-space:pre">	</span>// 根据PLL输入时钟源选择得到PLL输入时钟,见上一个函数
}

/* Return System PLL output clock rate */
uint32_t Chip_Clock_GetSystemPLLOutClockRate(void)
{
	return Chip_Clock_GetPLLFreq(LPC_SYSCTL->SYSPLLCTRL,Chip_Clock_GetSystemPLLInClockRate()); 
<span style="white-space:pre">	</span>// 该函数得到PLL输出时钟,函数实现此处未贴出,简单讲就是之前的PLL倍频公式的逆过程,参数2见上一个函数,为PLL输入时钟频率
}

经过上述一系列眼花缭乱的过程,SystemCoreClockUpdate()得到了系统的核心时钟频率(AHB)。函数调用比较多,需要仔细分析。

Board_Init();

如果读者看懂了上述时钟的初始化及获得时钟的一系列过程,那再看此函数的调用应该是很简单,如下:

/* Set up and initialize all required blocks and functions related to the
   board hardware */
void Board_Init(void)
{
	/* Sets up DEBUG UART */
	DEBUGINIT(); <span style="white-space:pre">		</span>  // USART0初始化

	/* Initialize GPIO */
	Chip_GPIO_Init(LPC_GPIO); // 参数无意义,使能了各个GPIO端口时钟及MUX,并将MUX复用控制器复位,此函数不展开介绍

	/* Initialize LEDs */
	Board_LED_Init();<span style="white-space:pre">	</span>// RGB三个引脚初始化
}
串口初始化函数:

/* Initialize debug output via UART for board */
void Board_Debug_Init(void)
{
#if defined(DEBUG_UART) <span style="white-space:pre">	</span>// 使用USART0
<span style="white-space:pre">	</span>// 引脚上拉下拉禁止,使用数字接口功能, #define IOCON_DIGMODE_EN        (0x1 << 7) 关于此位比较疑惑,因为文档中该位为保留位。
	/* Disables pullups/pulldowns and enable digitial mode */
	Chip_IOCON_PinMuxSet(LPC_IOCON, 0, 13, (IOCON_MODE_INACT | IOCON_DIGMODE_EN));
	Chip_IOCON_PinMuxSet(LPC_IOCON, 0, 18, (IOCON_MODE_INACT | IOCON_DIGMODE_EN));

	/* UART signal muxing via SWM */
	Chip_SWM_MovablePortPinAssign(SWM_UART0_RXD_I, 0, 13); // 配置usart0的引脚映射,SWM矩阵之前已提到
	Chip_SWM_MovablePortPinAssign(SWM_UART0_TXD_O, 0, 18);

	/* Use main clock rate as base for UART baud rate divider */ // 不分频,使用72MHz时钟
	Chip_Clock_SetUARTBaseClockRate(Chip_Clock_GetMainClockRate(), false);<span style="white-space:pre">	</span>// 对于参数2的作用暂未做了解,有熟知的读者可以一同探讨

	/* Setup UART */
	Chip_UART_Init(DEBUG_UART);<span style="white-space:pre">			</span> // uart 时钟使能,功能复位
	Chip_UART_ConfigData(DEBUG_UART, UART_CFG_DATALEN_8 | UART_CFG_PARITY_NONE | UART_CFG_STOPLEN_1);<span style="white-space:pre">	</span>// 8位,无校验,一位停止位
	Chip_UART_SetBaud(DEBUG_UART, 115200);<span style="white-space:pre">		</span>// 波特率设置
	Chip_UART_Enable(DEBUG_UART); <span style="white-space:pre">			</span>// 使能uart
	Chip_UART_TXEnable(DEBUG_UART); <span style="white-space:pre">		</span>// 使能发送
#endif
}

RGB灯的引脚初始化:

#define MAXLEDS 3
static const uint8_t ledpins[MAXLEDS]  = {25, 3, 1};
static const uint8_t ledports[MAXLEDS] = {0,  0, 1};

/* Initializes board LED(s) */
static void Board_LED_Init(void)
{
	int idx;

	for (idx = 0; idx < MAXLEDS; idx++) 
   <span style="white-space:pre">	</span> {   <span style="white-space:pre">	</span>// 设置RGB三个引脚的输出方向及状态
		/* Set the GPIO as output with initial state off (high) */
		Chip_GPIO_SetPinDIROutput(LPC_GPIO, ledports[idx], ledpins[idx]);
		Chip_GPIO_SetPinState(LPC_GPIO, ledports[idx], ledpins[idx], true);
	}
}

Chip_SCTPWM_Init(SCT_PWM)

前面的的知识算是基础入门,从此函数开始,进入SCT的PWM功能分析,如下:

/**
 * @brief	Initialize the SCT/PWM clock and reset
 * @param	pSCT	: The base of SCT peripheral on the chip
 * @return	None
 */
STATIC INLINE void Chip_SCTPWM_Init(LPC_SCT_T *pSCT)
{
	Chip_SCT_Init(pSCT);<span style="white-space:pre">	</span>// 只有一行代码,调用了此函数
}
/* Initialize SCT */
void Chip_SCT_Init(LPC_SCT_T *pSCT)
{
	uint32_t index = (uint32_t) pSCT;
	index = ((index >> 14) & 0xF) - 6;
	Chip_Clock_EnablePeriphClock((CHIP_SYSCTL_CLOCK_T)(SYSCTL_CLOCK_SCT0 + index));<span style="white-space:pre">	</span>// SCT_PWM宏定义为LPC_SCT0,初始化其时钟
	Chip_SYSCTL_PeriphReset((CHIP_SYSCTL_PERIPH_RESET_T)(RESET_SCT0 + index));<span style="white-space:pre">	</span>// 复位LPC_SCT0
}

Chip_SCTPWM_SetRate(SCT_PWM, SCT_PWM_RATE)

该函数的作用等同函数名,设置PWM频率为SCT_PWM_RATE = 1000Hz,但是其实现却不是这么直白,如下:

/* Set the PWM frequency */
void Chip_SCTPWM_SetRate(LPC_SCT_T *pSCT, uint32_t freq)<span style="white-space:pre">	</span>// 函数名告诉我们,它用来设置PWM的频率
{
	uint32_t rate;

	rate = Chip_Clock_GetSystemClockRate() / freq; <span style="white-space:pre">		</span>// rate = 72000000/1000 = 72000,为重载值

	/* Stop the SCT before configuration */
	Chip_SCTPWM_Stop(pSCT);<span style="white-space:pre">		</span>// 配置之前,先停止SCT

	/* Set MATCH0 for max limit */
	pSCT->REGMODE = 0;<span style="white-space:pre">					</span>// 所有的匹配/捕获寄存器配置为匹配寄存器模式
	Chip_SCT_SetMatchCount(pSCT, SCT_MATCH_0, 0); <span style="white-space:pre">		</span>// 匹配寄存器[0]的值置为0
	Chip_SCT_SetMatchReload(pSCT, SCT_MATCH_0, rate);<span style="white-space:pre">	</span>// 匹配寄存器[0]重载值为7200
	pSCT->EVENT[0].CTRL = 0 | 1 << 12; <span style="white-space:pre">			</span>// 事件0为匹配触发事件,事件来源于匹配寄存器0
	pSCT->EVENT[0].STATE = 1;   <span style="white-space:pre">				</span>// 启动事件0

	/* Set SCT Counter to count 32-bits and reset to 0 after reaching MATCH0 */
	Chip_SCT_Config(pSCT, SCT_CONFIG_32BIT_COUNTER | SCT_CONFIG_AUTOLIMIT_L);
    <span style="white-space:pre">	</span>// SCT工作在32位模式下,当匹配寄存器[0]匹配之后,计数值自动重载
}
通过以上分析可知,SCT只是一个定时器。在SCT基础上要想实现PWM的功能,此处做的事情有:开启了匹配寄存器[0],使得定时器每滴答7200下触发复位重新开始计数,同时触发事件0;根据时钟配置可知,SCT 在1S内触发1000匹配[0],同时匹配触发1000次事件0。

app_setup_pin()

既然SCT有PWM功能,那肯定得连接到MCU的指定引脚才有意义,SCT0_PWM有多路输出通道,此处指定了SCT0两路输出通道的输出引脚。
static void app_setup_pin( void)
{
	/* Enable SWM clock before altering SWM */
	Chip_Clock_EnablePeriphClock(SYSCTL_CLOCK_SWM); <span style="white-space:pre">	</span>// 启动了SWM时钟

#if defined(BOARD_NXP_LPCXPRESSO_1549)
	/* Connect SCT output 1 to PIO0_29 */ <span style="white-space:pre">			</span>// SCT0_PWM的输出通道0、1、2可以映射到0端口的任意引脚,剩下的3、4、5、6、7就只能是固定的引脚输出
	Chip_SWM_MovablePinAssign(SWM_SCT0_OUT1_O, 25); // 29);<span style="white-space:pre">	</span>// 配置SCT0的输出通道1所连接的引脚
	Chip_SWM_MovablePinAssign(SWM_SCT0_OUT0_O, 3); <span style="white-space:pre">		</span>// 配置SCT0的出书通道0所连接的引脚
#endif

	Chip_Clock_DisablePeriphClock(SYSCTL_CLOCK_SWM);<span style="white-space:pre">	</span>// 配置完毕,关闭了SWM时钟
}

Chip_SCTPWM_SetOutPin(SCT_PWM, SCT_PWM_OUT, SCT_PWM_PIN_OUT)

Chip_SCTPWM_SetOutPin(SCT_PWM, SCT_PWM_LED, SCT_PWM_PIN_LED)

此函数至关重要,设置两个事件的输出控制引脚,如下:

<span style="font-size:12px;">/* Setup the OUTPUT pin corresponding to the PWM index */
void Chip_SCTPWM_SetOutPin(LPC_SCT_T *pSCT, uint8_t index, uint8_t pin)
{
	int ix = (int) index;
	pSCT->EVENT[ix].CTRL = index | (1 << 12); <span style="white-space:pre">	</span>// 事件ix为匹配触发事件,触发来源于匹配寄存器index,此处index就是ix.
	pSCT->EVENT[ix].STATE = 1; <span style="white-space:pre">			</span>// 启动事件ix
	pSCT->OUT[pin].SET = 1 << 0 ;       <span style="white-space:pre">		</span>// 事件0触发引脚pin的输出
	pSCT->OUT[pin].CLR = 1 << ix;      <span style="white-space:pre">		</span>// 事件ix清除引脚pin的输出

	/* Clear the output in-case of conflict */
	pSCT->RES = (pSCT->RES & ~(3 << (pin << 1))) | (0x01 << (pin << 1));

	/* Set and Clear do not depend on direction */
	pSCT->OUTPUTDIRCTRL = (pSCT->OUTPUTDIRCTRL & ~(3 << (pin << 1)));
}</span>
可能看上面的4行注释有点晕,直白的讲就是:

1. pin为0和1,分别代表上面设置的输出通道0及输出通道1的引脚。

2. index为1和2,其实就是事件1和事件2,之前已经定义了事件0。

3. 之前已经配置了匹配寄存器0,它会触发事件0,此处的pSCT->OUT[pin].SET = 1 << 0 ; 的意思就是:事件0触发引脚pin输出高电平,函数中两次调用Chip_SCTPWM_SetOutPin使得事件0触发后会分别将通道0和通道1的引脚拉高输出。

4. 现在增加了事件1和事件2,它们也是匹配触发,分别来源于匹配寄存器1和匹配寄存器2,pSCT->OUT[pin].CLR = 1 << ix;的意思就是:事件1触发引脚通道1输出清零,拉低引脚1;事件2触发通道0输出轻量,拉低引脚0.

Chip_SCTPWM_SetDutyCycle(SCT_PWM, SCT_PWM_OUT, Chip_SCTPWM_GetTicksPerCycle(SCT_PWM)/2)

Chip_SCTPWM_SetDutyCycle(SCT_PWM, SCT_PWM_LED, 0)

上一个函数配置了事件1和事件2分别来源于匹配寄存器1及2,此函数名为设置事件1和事件2的输出占空比,实际上是设置匹配寄存器1和2。

<span style="font-size:12px;">/**
 * @brief	Get number of ticks on per PWM cycle
 * @param	pSCT	: The base of SCT peripheral on the chip
 * @param	index	: Index of the PWM 1 to N (see notes)
 * @param	ticks	: Number of ticks the output should say ON
 * @return	None
 * @note	@a index will be 1 to N where N is the "Number of
 *          match registers available in the SCT - 1" or
 *          "Number of OUTPUT pins available in the SCT" whichever
 *          is minimum. The new duty cycle will be effective only
 *          after completion of current PWM cycle.
 */
STATIC INLINE void Chip_SCTPWM_SetDutyCycle(LPC_SCT_T *pSCT, uint8_t index, uint32_t ticks)
{
	Chip_SCT_SetMatchReload(pSCT, (CHIP_SCT_MATCH_REG_T)index, ticks); // 其实就是设置了匹配寄存器1和匹配寄存器2的计数重载值
}</span>
需注意的是:匹配寄存器0、1、2计数匹配重载值后都会清零重新开始计数,但是只有匹配寄存器0会引发SCT计数值清零,而匹配寄存器2、3不影响SCT计数值。

Chip_SCTPWM_PercentageToTicks(SCT_PWM, led_dp)/2)

可以看到程序定时的调用了Chip_SCTPWM_SetDutyCycle改变占空比值,而占空比的值由Chip_SCTPWM_PercentageToTicks获取,描述如下:

/**
 * @brief	Converts a percentage to ticks
 * @param	pSCT	: The base of SCT peripheral on the chip
 * @param	percent	: Percentage to convert (0 - 100)
 * @return	Number ot ticks corresponding to given percentage
 * @note	Do not use this function when using very low
 *          pwm rate (like 100Hz or less), on a chip that has
 *          very high frequency as the calculation might
 *          cause integer overflow
 */
STATIC INLINE uint32_t Chip_SCTPWM_PercentageToTicks(LPC_SCT_T *pSCT, uint8_t percent)
{
	return (Chip_SCTPWM_GetTicksPerCycle(pSCT) * percent) / 100;<span style="white-space:pre">	</span>// 循环一次的计数乘以百分比,函数描述如下
}
/**
 * @brief	Get number of ticks per PWM cycle
 * @param	pSCT	: The base of SCT peripheral on the chip
 * @return	Number ot ticks that will be counted per cycle
 * @note	Return value of this function will be vaild only
 *          after calling Chip_SCTPWM_SetRate()
 */
STATIC INLINE uint32_t Chip_SCTPWM_GetTicksPerCycle(LPC_SCT_T *pSCT)
{
	return pSCT->MATCHREL[0].U;<span style="white-space:pre">	</span>// 一次循环的计数竟然是匹配寄存器0!看到此处是否恍然大悟。
}
通过此函数的设置,PWM的输出原理已经“水落石出”了,定时器在1s内通过匹配寄存器【0】产生1000次匹配,触发两个引脚同时输出高电平,也就是说:每一次的匹配输出高电平时间维持1ms。

同时,匹配寄存器【2】和【3】的匹配值大小分别为匹配寄存器【0】的X%及Y%(0<=X<=100, 0 <=Y<=100),也就是说:在1ms的时间内,经历了1*X% ms后引脚pin1拉低,经历了时间1*Y% ms后,引脚pin2拉低。

总结就是:通过在单位时间内(比如当前设置的1ms)内控制引脚的高电平比,也就是占空比,达到不同频率不同占空比的PWM输出。

配图:



Chip_SCTPWM_Start(SCT_PWM)

该函数启动SCT开启计数。


SysTick_Config(SystemCoreClock / TICKRATE_HZ)

该函数开启systick定时器,计数重载值为:SystemCoreClock / TICKRATE_HZ,TICKRATE_HZ = 1000,即1ms滴答中断一次,如下:

/** \brief  System Tick Configuration

    The function initializes the System Timer and its interrupt, and starts the System Tick Timer.
    Counter is in free running mode to generate periodic interrupts.

    \param [in]  ticks  Number of ticks between two interrupts.

    \return          0  Function succeeded.
    \return          1  Function failed.

    \note     When the variable <b>__Vendor_SysTickConfig</b> is set to 1, then the
    function <b>SysTick_Config</b> is not included. In this case, the file <b><i>device</i>.h</b>
    must contain a vendor-specific implementation of this function.

 */
__STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks)
{
  if ((ticks - 1) > SysTick_LOAD_RELOAD_Msk)  return (1);      /* Reload value impossible */

  SysTick->LOAD  = ticks - 1;                                  /* set reload register */
  NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1);  /* set Priority for Systick Interrupt */
  SysTick->VAL   = 0;                                          /* Load the SysTick Counter Value */
  SysTick->CTRL  = SysTick_CTRL_CLKSOURCE_Msk |<span style="white-space:pre">		</span>       /* 选择系统时钟源,使能中断,使能systick */
                   SysTick_CTRL_TICKINT_Msk   |
                   SysTick_CTRL_ENABLE_Msk;                    /* Enable SysTick IRQ and SysTick Timer */
  return (0);                                                  /* Function successful */
}

__WFI()

该函数使系统进入休眠状态,由systick唤醒。


综合以上所有函数的调用,可以知道系统实现的功能是:每隔1ms,更改一次PWM占空比,使得LED的亮度发生渐变,呈现呼吸灯的效果。


LCP1549之SCT_PWM 输出功能分析

标签:pwm   lpc1549   sct_pwm   system_init   systick   

原文地址:http://blog.csdn.net/linux_liulu/article/details/45604493

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!