1.前言
昨天晚上画完板子,还剩点时间就再翻了翻手册,大致清楚了时钟树的运行,顺带搞清楚了定时的使用,那就出一份教程吧。
如果各位在此之前没有接触过LPC单片机,还是建议先把程序直接贴进自己的项目,稍微改改先把功能实现了,定时器能动起来再说,因为LPC的定时器和其他单片机还是有很大不同的。
2.图形化配置时钟树
在开始前有些东西要先讲一下,首先是如何打开图形化配置时钟树,我们在项目资源管理器的右上角有一个绿色X的按钮,点击一下就会生成工程对应的mex文件,这个就是图形化配置文件。第一次使用时点击按钮我们就会进入图形配置界面,然后工程会自动生成一个mex文件,后续如果还想再修改配置就可以双击mex文件进入
然后我们就会进入下图的这个界面,这是管脚配置界面,大家有需要可以自行配置。
我们将右手的界面向下滑,找到一个像pwm波的图标,点击进去就是时钟树
然后我们就会进入如图的界面
如果各位直接创建的新工程,芯片的主频设置的是18Mhz
我们双击最左边fro_osc框进行主频修改
我们将芯片主频设定为最高(30Mhz)
设置完成后点击页面顶端的更新源代码
我们可以大致了解到哪些更改了,哪些没有改动,没有问题后点击确定完成设置。
退回到程序开发界面后点击编译就可以了。
我们这次使用的ctimer挂在这条时钟树下面
可以看到其中有一个2分频,因此我们的ctimer的默认频率是15Mhz
3.初始化
首先说一下在804芯片里只有一个ctimer0,还是有点蛋疼的,再加上一个systick就两个传统意义上的定时器,有些情况下还是不够用的,这点最开始选型的时候慎重一点,有复杂的定时器控制的项目上建议用其他芯片。
ctimer0初始化代码如下,这部分需要重点说说。
ctimer_config_t config;CTIMER_GetDefaultConfig(&config);CTIMER_Init(CTIMER0, &config);/* Configuration 0 */
matchConfig0.enableCounterReset = true;
matchConfig0.enableCounterStop = false;
matchConfig0.matchValue = CLOCK_GetFreq(kCLOCK_CoreSysClk) / 1000;
matchConfig0.outControl = kCTIMER_Output_NoAction;
matchConfig0.outPinInitState = false;
matchConfig0.enableInterrupt = true;CTIMER_RegisterCallBack(CTIMER0, &ctimer_callback_table[0], kCTIMER_MultipleCallback);
CTIMER_SetupMatch(CTIMER0, kCTIMER_Match_0, &matchConfig0);
CTIMER_StartTimer(CTIMER0);
首先,第一行是获取默认配置,函数原型如下。
这里默认配置下,设定是的模式是定时器模式,输入模式0,这两个没啥好说的,一般也不会需要改,之后prescale是分频系数的设置,默认是没有的,那么此时我们的频率就是15Mhz,这里如果大家需要其他频率就要改了,但是不建议直接在库里面改。我们可以在主函数里像下图这样改,我这里就不动了。
完成之后就可以将配置信息放到ctimer里面就是后面一句CTIMER_Init(CTIMER0, &config);
之后就是设置匹配
我们看到设置,其他内容相信大家可以看得懂我就不过多介绍了,大家根据需要来设置,一般不用动。这里的配置主要改的是匹配值就是第三行,这里的匹配值也STM32内所说重装值,这里就涉及到我们中断时间了。刚刚我们说到定时器的时钟频率是15Mhz。那么如果我们的匹配值是1,那么我们单次进中断的时间就是1/15000000 s。我们这里的匹配值是15000,这样一来进中断的时间就是1/15000000*15000=1ms
这里匹配是啥意思?LPC的定时器挺别致的,它没有中断,用的是回调函数,这个回调函数是更高级语言的一种用法,大家可以自行上网搜索,我也不是很了解,就不献丑了。其效果和中断差不多,就是没有中断优先级。
下一步就是设置回调函数了,但在此之前我们要先准备三个东西,如下图所示。从上到下依次是(1)函数声明(2)回调函数表(3)回调函数本体。一会我们初始化的时候要用到。
这里第二个ctimer_callback_t ctimer_callback_table[]就是回调函数表,我们在里面填写的内容就是回调函数名称。
等这些都写完后我们就可以继续初始化了,下图就是定时器回调函数的配置
这个函数原型如下
第一个参数是指定哪个定时器,这里都是ctimer0,第二个参数是回调函数表的地址,第三个是回调函数的个数(单个或多个)。这里要说一下建议回调函数表里的顺序按照0,1,2这样排,因为当有多个回调函数的时候,程序会按照表里的顺序依次和匹配上。这里先将一个回调函数的情况吧,一会再将多个回调函数。
等这些都初始化完毕,我们就可以把设置的参数给ctimer了
而初始化的最后一句话就是开启定时器
4.多重回调
因为芯片里只有一个定时器,那么我们怎么才能拓展定时器时间呢?
这里NXP芯片有一点好,那就是可以设置多个匹配数。这里我借用STM32的手册上一幅图来解释
在STM32里面定时器是只有一个重装值的,那么当计数器的值与设定值匹配时就会触发中断,但是在804里面ctimer最多支持4个匹配值,那么我们就可以,触发多个计数器溢出。我先把程序放在这具体解释我打算回头再写一篇文章来说。
void init_ctimer(void)
{ctimer_config_t config;CTIMER_GetDefaultConfig(&config);CTIMER_Init(CTIMER0, &config);/* Configuration 0 */matchConfig0.enableCounterReset = true;matchConfig0.enableCounterStop = false;matchConfig0.matchValue = CLOCK_GetFreq(kCLOCK_CoreSysClk) / 1000;matchConfig0.outControl = kCTIMER_Output_NoAction;matchConfig0.outPinInitState = false;matchConfig0.enableInterrupt = true;/* Configuration 1 */matchConfig1.enableCounterReset = false;matchConfig1.enableCounterStop = false;matchConfig1.matchValue = 0;matchConfig1.outControl = kCTIMER_Output_NoAction;matchConfig1.outPinInitState = false;matchConfig1.enableInterrupt = true;CTIMER_RegisterCallBack(CTIMER0, &ctimer_callback_table[0], kCTIMER_MultipleCallback);CTIMER_SetupMatch(CTIMER0, kCTIMER_Match_0, &matchConfig0);CTIMER_SetupMatch(CTIMER0, kCTIMER_Match_1, &matchConfig1);CTIMER_StartTimer(CTIMER0);
}
5.程序
我这里初始化了两个端口(13,22),在两个回调函数里让他们翻转,大家可以先下载程序,测试下效果。
#include "fsl_ctimer.h"
void ctimer_match0_callback(uint32_t flags);
void ctimer_match1_callback(uint32_t flags);/* Array of function pointers for callback for each channel */
ctimer_callback_t ctimer_callback_table[] = {ctimer_match0_callback, ctimer_match1_callback, NULL, NULL, NULL, NULL, NULL, NULL};/* Match Configuration for Channel 0 */
static ctimer_match_config_t matchConfig0;
/* Match Configuration for Channel 1 */
static ctimer_match_config_t matchConfig1;void ctimer_match0_callback(uint32_t flags)
{GPIO_PortToggle(GPIO,0,1<<22);matchConfig1.matchValue=0;CTIMER_SetupMatch(CTIMER0, kCTIMER_Match_1, &matchConfig1);
}void ctimer_match1_callback(uint32_t flags)
{LEDTOL;matchConfig1.matchValue+=CLOCK_GetFreq(kCLOCK_CoreSysClk) / 2000;CTIMER_SetupMatch(CTIMER0, kCTIMER_Match_1, &matchConfig1);
}void init_ctimer(void)
{ctimer_config_t config;CTIMER_GetDefaultConfig(&config);CTIMER_Init(CTIMER0, &config);/* Configuration 0 */matchConfig0.enableCounterReset = true;matchConfig0.enableCounterStop = false;matchConfig0.matchValue = CLOCK_GetFreq(kCLOCK_CoreSysClk) / 1000;matchConfig0.outControl = kCTIMER_Output_NoAction;matchConfig0.outPinInitState = false;matchConfig0.enableInterrupt = true;/* Configuration 1 */matchConfig1.enableCounterReset = false;matchConfig1.enableCounterStop = false;matchConfig1.matchValue = 0;matchConfig1.outControl = kCTIMER_Output_NoAction;matchConfig1.outPinInitState = false;matchConfig1.enableInterrupt = true;CTIMER_RegisterCallBack(CTIMER0, &ctimer_callback_table[0], kCTIMER_MultipleCallback);CTIMER_SetupMatch(CTIMER0, kCTIMER_Match_0, &matchConfig0);CTIMER_SetupMatch(CTIMER0, kCTIMER_Match_1, &matchConfig1);CTIMER_StartTimer(CTIMER0);
}int main(void)
{BOARD_InitBootPins();BOARD_InitBootClocks();BOARD_InitDebugConsole();init_ctimer();GPIO_PortInit(GPIO, 0);CLOCK_EnableClock(kCLOCK_Gpio0);gpio_pin_config_t LED_RED_config = {.pinDirection = kGPIO_DigitalOutput,.outputLogic = 0U,};GPIO_PinInit(GPIO, 0, 13, &LED_RED_config);GPIO_PinInit(GPIO, 0, 22, &LED_RED_config);while (1){}
}
对于初学者来说可以先修改下图的这个参数,这里除1000是10ms,官方除2是1s。大家可以直接改这个参数来进行定时器实际的设置。
6.实际效果
7.结语
总的来说,LPC系列的定时器还是很特殊的,用过那么多单片机很少是NXP这个思路,最开始我学LPC55S69,第一次接触到回调函数这个问题也是非常懵逼的,如果各位也有这个困扰,建议大家先把回调函数视作中断,等慢慢熟悉了再去深入了解。还是那句话,有问题评论区见,我会尽量解答,那么我们下篇文章见。