标签: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;
}<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函数中的函数实现:
该函数获得系统的核心时钟频率,内容很简单,只有一行代码:
/* 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)。函数调用比较多,需要仔细分析。
如果读者看懂了上述时钟的初始化及获得时钟的一系列过程,那再看此函数的调用应该是很简单,如下:
/* 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);
}
}
前面的的知识算是基础入门,从此函数开始,进入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
}
该函数的作用等同函数名,设置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。
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时钟
}此函数至关重要,设置两个事件的输出控制引脚,如下:
<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.
<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_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输出。
配图:
该函数启动SCT开启计数。
该函数开启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 */
}
该函数使系统进入休眠状态,由systick唤醒。
综合以上所有函数的调用,可以知道系统实现的功能是:每隔1ms,更改一次PWM占空比,使得LED的亮度发生渐变,呈现呼吸灯的效果。
标签:pwm lpc1549 sct_pwm system_init systick
原文地址:http://blog.csdn.net/linux_liulu/article/details/45604493