标签: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