第十二章 定时器编程
参考资料:
- 芯片手册《Chapter30 : General Purpose Timer (GPT)》。
- 芯片手册《Chapter24 : Enhanced Periodic Interrupt Timer (EPIT)》。
12.1 GPT定时器介绍
GPT具有32位递增计数器。可以将外部引脚上的事件通过定时器计数器捕获到寄存器中。触发事件可以为上升沿或下降沿。当定时器达到设定的值时,GPT还可以在输出引脚上产生事件,并产生中断。GPT具有12位预分频器,该分频器可以对多个时钟源的时钟进行分频。GPT框图如下:
特性
- 一个带有时钟源选择的32位递增计数器,时钟源包括外部时钟。
- 两个具有可编程触发沿的输入捕捉通道。
- 具有可编程输出模式的三个输出比较通道。还有一个“force compare”功能。
- 可以编程为在低功耗(low power)和调试(debug)模式下处于运行状态。
- 在捕获(capture),比较(compare)和翻转(rollover)事件时产生中断。
- 以重新启动(restart)或自由运行(free-run)模式进行计数器操作。
12.1.1 时钟源选择
Clock name | Clock Root | Description |
---|---|---|
ipg_clk | ipg_clk_root | Peripheral clock |
ipg_clk_32k | ckil_sync_clk_root | Low-frequency reference clock (32 kHz) |
ipg_clk_highfreq | perclk_clk_root | High-frequency reference clock |
ipg_clk_s | ipg_clk_root | Peripheral access clock |
上表是GPT模块会使用到的时钟,ipg_clk提供了Peripheral的时钟,ipg_clk_perclk提供了gpt控制器的时钟,ipg_clk_32k提供低频参考时钟,ipg_clk_highfreq提供了高频参考时钟,ipg_clk_s提供了访问控制器寄存器所需的时钟。
从上图可以看出,可以从4个时钟源中选择输入到预分频器的时钟。分别为:高频参考时钟(ipg_clk_highfreq),低频参考时钟(ipg_clk_32k),外围时钟(ipg_clk)和外部时钟(GPT_CLK)或者晶体振荡器时钟(ipg_clk_24M)。由于外部时钟(GPT_CLK)或者晶体振荡器时钟(ipg_clk_24M)只能选择一个,所以总共是四个。由于这里暂时不关注lower power mode的内容,实验里将ipg_clk作为预分频器的时钟源,其它的在这里不关注。ipg_clk怎么设置呢?需要查看时钟树。
如上图所示,PERCLK_CLK_ROOT可以来源于IPG_CLK_ROOT和OSC,后续实验选择了IPG_CLK_ROOT,经过CSCMR1[PERCLK_PODF](实验里设置为0,对应分频值1)分频,IPG_CLK_ROOT来源于AHB_CLK_ROOT分频CBCDR[IPG_PODF] (实验里设置为1,对应分频2),AHB_CLK_ROOT实验里设置来源于SYS PLL PFD2分频CBCDR[AHB_PODF](实验里设置为2,对应分频3),SYS PLL PFD2的大小为396M。所以PERCLK_CLK_ROOT的大小为66M。
PERCLK_CLK_ROOT = 396M / 3 / 2 = 66M
12.1.2 时钟源选择的操作流程
GPT_CR寄存器中的CLKSRC字段选择时钟源。CLKSRC字段仅在禁用GPT(EN = 0)后才能更改该值。
更改时钟源时要遵循的软件顺序为:
1.通过在GPT_CR寄存器中设置EN = 0来禁用GPT。
2.禁用GPT中断寄存器(GPT_IR)。
3.将输出模式配置为未连接/断开连接—往GPT_CR中的OM1,OM2,OM3写0。
4.禁用输入捕获模式-往GPT_CR的IM1和IM2中写入零
5.在GPT_CR寄存器中将时钟源CLKSRC更改为所需的值。
6.将GPT_CR寄存器中的SWR位置1。
7.清除GPT状态寄存器(GPT_SR)(该寄存器是往响应位写1清0)。
8.在GPT_CR寄存器中设置ENMOD = 1,以使GPT计数器为0x00000000。
9.在GPT_CR寄存器中启用GPT(EN = 1)。
10.启用GPT中断寄存器(GPT_IR)
12.1.3 GPT的计数模式
① 重新启动计数模式(Restart mode)
在重启模式下(可通过GPT控制寄存器GPT_CR选择),当计数器达到比较值时,计数器将复位并从0x00000000重新开始计数。重新启动功能仅与比较通道1相关联。对通道1的比较寄存器的任何写操作都将复位GPT计数器。这样做是为了避免在进行计数时将比较值从较高的值更改为较低的值时可能丢失比较事件。对于其他两个比较通道,当发生比较事件时,计数器不会复位。
② 自由运行模式(free-run mode)
在自由运行模式下,当所有三个通道发生比较事件时,计数器不会复位;而是,计数器继续计数直到0xffffffff,然后翻转(变为0x00000000)。
12.1.4 GPT的操作
通用定时器(GPT)具有一个计数器(GPT_CNT),该计数器是32位递增计数器,在由软件启用该计数器后(EN = 1)开始计数。
•如果禁用了GPT计时器(EN = 0),则主计数器和预分频器计数器将冻结其当前计数值。当EN位置1且计数器再次使能时,ENMOD位确定GPT counter的值。
•如果将ENMOD位置1,则当启用GPT(EN = 1)时,主计数器和预分频器计数器值将重置为0。
•如果将ENMOD位设置为0,则当再次启用GPT(EN = 1)时,主计数器和预分频器计数器将从其冻结值重新开始计数。
•如果将GPT编程为在低功耗模式(STOP / WAIT)下被禁用,则当GPT进入低功耗模式时,主计数器和预分频器计数器将冻结在其当前计数值。当GPT退出低功耗模式时,无论ENMOD位值如何,主计数器和预分频器计数器都将从其冻结值开始计数。请注意,处理器可以随时读取GPT_CNT,并且两个输入捕获通道都使用相同的计数器(GPT_CNT)。
•硬件复位将所有GPT寄存器复位为各自的复位值。除输出比较寄存器(OCR1,OCR2,OCR3)以外的所有寄存器的值均为0x0。比较寄存器复位为0xFFFF_FFFF。
•软件复位(GPT_CR控制寄存器中的SWR位)将复位所有寄存器位,除了EN,ENMOD,STOPEN,WAITEN和DBGEN位。这些位的状态不受软件复位的影响。请注意,禁用GPT时可以进行软件复位操作。
12.1.5 GPT的输入捕获
有两个输入捕获通道,每个输入捕获通道都有一个专用的捕获引脚,捕获寄存器和输入边沿检测/选择逻辑。每个输入捕获功能都有一个状态标记位,并且可以向处理器发出中断服务请求。当输入捕获引脚上发生选定的边沿转换时,GPT_CNT的内容被捕捉到相应的捕捉寄存器中,并设置适当的中断状态标志。如果检测到转换(如果在中断寄存器中)相应的使能位置1,则可以生成中断请求。可以将捕获设置为发生在输入引脚的上升沿,下降沿,上升沿和下降沿,或者禁用捕获。事件与选择运行计数器的时钟同步。只有那些在上一个记录的转换之后至少一个时钟周期(选择运行计数器的时钟源)发生的转换才能保证触发捕获事件。输入跳变的锁存最多可能有一个时钟周期的不确定性。可以随时读取输入捕获寄存器,而不会影响它们的值。具体时序图如下所示:
12.1.6 GPT的输出比较
三个输出比较通道使用与输入捕捉通道相同的计数器(GPT_CNT)。当输出比较寄存器的值与GPT_CNT中的值匹配时,将输出比较状态标志置1,并产生中断(如果在中断寄存器中设置了相应的位)。因此,根据模式位,输出比较定时器的引脚将被置位(set),清除(clear),翻转(toggle),没有影响,或在一个输入时钟周期内提供低电平有效脉冲(受焊盘允许的最大频率的限制)。
还有一个“强制比较(forced-compare)”功能,允许软件在需要时生成比较事件,不需要计数器值等于比较值的条件。强制比较的结果所采取的操作与发生输出比较匹配时的操作相同,不同之处在于不设置状态标记位并且不会产生中断。强制比较的通道在写入force-compare位后立即采取设置的措施。这些位是自动清除的,读的话一直零。下图是输出比较时的时序图:
12.1.7 GPT的中断
GPT可以产生6种不同的中断。如果选定的用于运行计数器的时钟可用,则可以在低功耗和调试模式下生成所有中断。
•翻转中断
当GPT计数器达到0xffffffff,然后重新设置为0x00000000并继续计数时,将产生翻转中断。翻转中断通过GPT_IR寄存器中的ROVIE位来使能。相关的状态位是GPT_SR寄存器中的ROV位。
•输入捕获中断1、2
捕获事件发生后,相应的输入捕获通道会产生一个中断。“捕获事件”中断通过IF2IE和IF1IE位(在GPT_IR寄存器中)使能;相应的状态位是IF2和IF1(在GPT_SR寄存器中)。由于捕获事件而导致的计数器值的捕获不受挂起的捕获中断的影响。当发生捕获事件时,无论是否已处理捕获通道的中断,捕获寄存器都会更新新的捕获到计数器值。
•输出比较中断1、2、3
比较事件发生后,相应的输出比较通道会产生一个中断。“比较事件”中断由OF3IE,OF2IE和OF1IE位(在GPT_IR寄存器中)使能;相应的状态位是OF3,OF2和OF1(在GPT_SR寄存器中)。 “强制比较(Force compare)”不会产生中断。
还存在一条cumulative中断线,每当上述任何中断发生时,它就会被置为有效。cumulative中断线没有相关的使能或状态位。
12.2 GPT寄存器介绍
12.2.1 GPT Control Register (GPTx_CR)
GPT控制寄存器
bit31-29 分别为FO3-1,写0没有影响,写1导致相应的输出引脚状态变化,OFn标记位不会设置
bit28-26,bit25-23,bit22-20 分别为OM3,OM2和OM1,为000时与输出引脚断开,001表示翻转引脚状态,010时表示引脚清0,011时引脚置位,1xx时产生一个低脉冲
bit19-18,bit17-16,分比为IM2和IM1,00时表示捕获功能关闭,01时捕获上升沿,10时捕获下降沿,11时同时捕获上升和下降沿
bit15 SWR软件复位
bit10 EN_24M,硬件复位时,复位 EN_24M位,软件复位时不影响EN_24M位
bit9 FRR 0时为restart模式,1时为free-run模式
bit8-6,BLKSRC,时钟源选择位,000时表示与时钟源断开,001时Peripheral Clock (ipg_clk),010时为High Frequency Reference Clock (ipg_clk_highfreq),011 时为External Clock,100 时为Low Frequency Reference Clock (ipg_clk_32k),101时为Crystal oscillator as Reference Clock (ipg_clk_24M)
bit5 STOPEN,stop mode时GPT是否使能
bit4 DOZEEN,doze mode时GPT是否使能
bit3 WAITEN,wait mode时GPT是否使能
bit2 DBGEN,debug mode时GPT是否使能
bit1 ENMOD,为0时关闭GPT时计数器时保持原有值,为1时关闭GPT时计数器值复位为0
bit0 EN,GPT使能位
12.2.2 GPT Prescaler Register (GPTx_PR)
GPT预分频寄存器
bit15-12,PRESCALER24M,选择24M crystal 时钟时的预分频值
bit12-0,选择其它时钟源时的预分频值
12.2.3 GPT Status Register (GPTx_SR)
GPT状态寄存器
状态寄存器包含状态位,指示计数器翻转,或者输入通道和输出通道产生相应的事件
bit5,ROV,翻转标记位
bit4-3,IF2和IF1,输入通道捕获事件标记位
bit2-0,OF3-1,输出通道比较事件标记位
12.2.4 GPT Interrupt Register (GPTx_IR)
GPT中断寄存器
翻转,输入和输出通道事件的中断使能位,与状态寄存器的位对应。
12.2.5 GPT Output Compare Register 1~3 (GPTx_OCR1~3)
GPT输出比较寄存器
GPTx_OCR1-3,总共3个输出比较寄存器,当计数器达到输出比较寄存器的值时,将在相应通道上产生事件,restart mode时,写入channael1 的比较寄存器会复位GPT计数器,写寄存器的值在一个时钟周期后生效,读寄存器的值会立即返回。
12.2.6 GPT Input Capture Register 1~2 (GPTx_ICR1~2)
GPT输入捕获寄存器
GPTx_ICR1-2,两个输入捕获寄存器,只读寄存器,用于保存相应输入捕获通道上一次捕获事件发生时计数器中的值。
12.2.7 GPT Counter Register (GPTx_CNT)
GPT计数器寄存器
只读寄存器,GPT计数器的值,读不影响计数过程
12.3 GPT查询方式延时代码详解与测试
12.3.1 代码分析
通过gpt_poll_init和gpt_poll_restart两个函数来实现,gpt_poll_init函数首先对GPT进行软件复位,设置gpt为restart模式,时钟源选择Peripheral Clock (ipg_clk)为66M,预分频值设置为0(即预分频值为1)。
gpt_poll_init代码在裸机Git仓库 NoosProgramProject/(12_定时器编程/009_timer_gpt_poll/gpt.c):
void gpt_poll_init(GPT_Type *base)
{
/* bit15 SWR, Software reset*/
base->CR |= (1 << 15);
/* Wait reset finished. */
while((base->CR >> 15) & 0x1) {
}
/*
*bit10: Enable 24 MHz clock input from crystal
*bit9: 0 restart mode, 1 free-run mode:set 0
*bit8-6: Clock Source select :001 Peripheral Clock (ipg_clk)
*bit5: GPT Stop Mode enable
*bit3: GPT Wait Mode enable.
*bit1: GPT Enable Mode
*/
base->CR = (1 << 6) | (1 << 5) | (1 << 3) | (1 << 1);
/*
*bit15-bit12:PRESCALER24M
*bit11-0:PRESCALER
*/
base->PR = 0;
}
gpt_poll_restart函数根据通道设置和延时时间设置输出比较寄存器,为了防止状态寄存器已经设置,先清除一下状态寄存器对应的位,再使能中断寄存器对应的位,然后使能GPT计数器,等待compare flag设置,设置后先关闭GPT计数器,禁止中断寄存器对应的位,最后往状态寄存器相应的位写1清除掉状态位。
代码在裸机Git仓库 NoosProgramProject/(12_定时器编程/009_timer_gpt_poll/gpt.c):
void gpt_poll_restart(GPT_Type *base, enum gpt_comp_channel chan, unsigned int us)
{
base->OCR[chan] = USEC_TO_COUNT(us);
/* write 1 to clear int status to avoid unexpected compare event*/
base->SR |= (1 << chan);
/* enable interrupt*/
base->IR |= (1 << chan);
/* gpt enable*/
base->CR |= (1 << 0);
/*wait for compare flag set*/
while(!((base->SR >> chan) & 0x1))
;
/* gpt disable*/
base->CR &= ~(1 << 0);
/* disable interrupt*/
base->IR &= ~(1 << chan);
/* write 1 to clear int status*/
base->SR |= (1 << chan);
}
主函数
使用gpt_poll_init初始化GPT1,不断调用gpt_poll_restart(GPT1, OUT_COMP1, 1000000);延时1s,并且依次点亮和关闭绿灯。
代码在裸机Git仓库 NoosProgramProject/(12_定时器编程/009_timer_gpt_poll/main.c):
gpt_poll_init(GPT1);
while(1) {
gpt_poll_restart(GPT1, OUT_COMP1, 1000000);
GPIO5->DR &= ~(1<<3);//led on
printf("led is on\r\n");
gpt_poll_restart(GPT1, OUT_COMP1, 1000000);
GPIO5->DR |= (1<<3);//led off
printf("led is off\r\n");
}
12.3.2 参考章节《4-1.4编译程序》编译程序
进入 裸机Git仓库 NoosProgramProject/(12_定时器编程/009_timer_gpt_poll) 源码目录
12.3.3 参考章节《4-1.4映像文件烧写、运行》烧写、运行程序
此时观察串口信息
绿灯不断闪烁,每隔1s翻转状态。串口输出如下:
12.4 GPT中断方式延时代码详解与测试
12.4.1 GPT1中断号的确定
查询数据手册chapter 3的Table3-1中断号表,gpt1如下所示:
gic中断号需要再这个序号基础上加上32,所以gpt1的gic中断号为55+32=87。
12.4.2 代码分析
12.4.2.1 通过gpt_init函数初始化gpt
首先对GPT进行软件复位,设置gpt为restart模式,时钟源选择Peripheral Clock (ipg_clk)为66M,预分频值设置为0(即预分频值为1),输出比较寄存器根据延时值设置。
gpt_init代码在裸机Git仓库 NoosProgramProject/(12_定时器编程/009_timer_gpt_int/gpt.c):
/* assume use ipc clk which is 66MHz, 1us against to 66 count */
#define USEC_TO_COUNT(us) (us * 66 - 1)
void gpt_init(GPT_Type *base, enum gpt_comp_channel chan, int us)
{
/* bit15 SWR, Software reset*/
base->CR |= (1 << 15);
/* Wait reset finished. */
while((base->CR >> 15) & 0x1) {
}
/*
*bit10: Enable 24 MHz clock input from crystal
*bit9: 0 restart mode, 1 free-run mode:set 0
*bit8-6: Clock Source select :001 Peripheral Clock (ipg_clk)
*bit5: GPT Stop Mode enable
*bit3: GPT Wait Mode enable.
*bit1: GPT Enable Mode
*/
base->CR = (1 << 6) | (1 << 5) | (1 << 3) | (1 << 1);
/*
*bit15-bit12:PRESCALER24M
*bit11-0:PRESCALER
*/
base->PR = 0;
/* GPTx_OCR1 bit31-0: Compare Value
* When the counter value equals the COMP bit field value,
* a compare event is generated on Output Compare Channel 1.
*/
base->OCR[chan] = USEC_TO_COUNT(us);
}
12.4.2.2 gpt中断使能函数
根据通道使能中断寄存器对应的位.
代码在裸机Git仓库 NoosProgramProject/(12_定时器编程/09_timer_gpt_int/gpt.c):
void gpt_enable_interrupt(GPT_Type *base, enum gpt_interrupt_bit bit, int on)
{
if (on)
base->IR |= (1 << bit);
else
base->IR &= ~(1 << bit);
}
12.4.2.3 gpt运行使能函数
设置控制寄存器的运行位。
代码在裸机Git仓库 NoosProgramProject/(12_定时器编程/009_timer_gpt_int/gpt.c):
void gpt_run(GPT_Type *base, int on)
{
/* bit0: GPT Enable */
if (on)
base->CR |= (1 << 0);
else
base->CR &= ~(1 << 0);
}
12.4.2.4 中断处理函数
中断处理函数里首先清除GPT1状态寄存器对应的位,然后每发生一次中断翻转绿灯的状态。
代码在在裸机Git仓库 NoosProgramProject/(12_定时器编程/009_timer_gpt_int/main.c):
void GPT1_COMP1_handle_irq(void)
{
static int on = 1;
printf("GPT1 comp0 interrupt happened\r\n");
/*
* bit0: OF1 Output Compare 1 Flag
* write 1 clear it */
GPT1->SR |= 1;
/* read GPIO5_DR to get GPIO5_IO01 status*/
if(on) {
/* led off, set GPIO5_DR to configure GPIO5_IO03 output 1 */
GPIO5->DR |= (1<<3); //led off
on = 0;
} else {
/* led on, set GPIO5_DR to configure GPIO5_IO03 output 0 */
GPIO5->DR &= ~(1<<3); //led on
on = 1;
}
12.4.2.5 主函数的设置
通过gpt_init初始化GPT1计数器,延时时间设置为1s,注册中断处理函数,使能gic的GPT1_IRQn的中断,使能GPT1的输出通道1的中断,最后运行GPT1计数器。
代码在裸机Git仓库 NoosProgramProject/(12_定时器编程/009_timer_gpt_int/main.c):
gpt_init(GPT1, OUT_COMP1, 1000000);// set 1s
request_irq(GPT1_IRQn, (irq_handler_t)GPT1_COMP1_handle_irq, NULL);
gic_enable_irq(GPT1_IRQn);
gpt_enable_interrupt(GPT1, IR_OF1IE, 1);
gpt_run(GPT1, 1);
12.4.3 参考章节《4-1.4编译程序》编译程序
进入裸机Git仓库 NoosProgramProject/(12_定时器编程/009_timer_gpt_int) 源码目录
12.4.4 参考章节《4-1.4映像文件烧写、运行》烧写、运行程序
此时观察串口及开发板led灯现象
绿灯不断闪烁,每隔1s翻转状态
串口输出:
12.5 EPIT定时器介绍
EPIT是一个32位的计时器,能够在处理器很少干预的情况下以固定的时间间隔提供精确的中断。软件使能后,EPIT就开始计数。框图如下:
12.5.1 EPIT特性
EPIT具有以下主要特性:
•具有时钟源选择的32位递减计数器
•12位预分频器,用于对输入时钟频率分频
•可即时编程的计数器值
•可以设置在低功耗和调试模式下处于计数状态
•计数器达到比较值时产生中断
时钟源的选择与GPT类似,后续实验时钟源也选择ipg_clk作为时钟源,ipg_clk设置的频率为66M。
12.5.2 操作模式
EPIT可以设置为set-and-forget或free-running模式。使用EPIT_CR [RLD]选择所需的模式。
① set-and-forget模式
要选择这种操作模式,将控制寄存器(EPIT_CR)中的RLD位置1。 在这种模式下,计数器从加载寄存器(EPIT_LR)获取值;它不能直接从数据总线写入。每当计数器达到零时,EPIT_LR中的值就会加载到计数器中,然后将此值减到零。初始化计数器的话,不是等待计数达到零才设置,需要设置EPIT计数器覆盖使能位(EPIT_CR [IOVW]),并将所需的初始化值写入EPIT_LR。
② free-runnning模式
选择此操作模式的话,需要清除RLD位。在这种模式下,计数器从0000 0000h翻转到FFFF FFFFh,而无需从加载寄存器重新加载。翻转后,计数器继续递减计数。初始化计数器的话,需要设置EPIT计数器覆盖使能位(EPIT_CR [IOVW]),并将所需的初始化值写入EPIT_LR。
12.5.3 操作过程
EPIT具有单个32位递减计数器,当软件使能该模块时,该计数器开始计数。计数器的起始值从EPIT加载寄存器中加载,处理器可以随时将其写入。比较寄存器中的值确定中断发生的时间。当禁用EPIT(EN = 0)时,主计数器和预分频器计数器会将其计数冻结为当前计数值。当重新启用EPIT(EN = 1)时,ENMOD位(可读可写位)决定计数器的值:
•如果设置了ENMOD,则主计数器从加载寄存器加载值(如果RLD = 1)或者 FFFF FFFFh(如果RLD = 0),并且预分频计数器复位(000h)。
•如果清除了ENMOD,则主计数器和预分频器计数器均从其冻结值重新开始计数。
如果将EPIT编程为在低功耗模式(STOP / WAIT)下被禁用,则当EPIT进入低功耗模式时,主计数器和预分频器计数器都冻结在其当前计数值。当EPIT退出低功耗模式时,无论ENMOD位如何,主计数器和预分频器计数器都将从其冻结值开始计数。硬件复位会将所有EPIT寄存器复位为各自的复位值。除控制寄存器中的EN,ENMOD,STOPEN和WAITEN位外,软件复位将其它位复位为各自的复位值。这些位的状态不受软件复位的影响。即使禁用了EPIT,也可以进行软件复位操作。
12.5.4 比较事件
当EPIT_EPITCMPR的编程值与EPIT_EPITCNR中的值匹配时,将设置比较状态标志,并且如果控制寄存器中的OCIEN位置1,则会产生中断。
根据控制寄存器中输出模式(OM)位的设置,比较输出引脚可以设置为置位,清除,切换或完全不受影响。如果在翻转时需要中断(当计数器值达到0x0000_0000并加载新值时),则应将比较寄存器值设置为等于set-and-forget模式中的加载寄存器值,或在free-running模式下等于0xFFFF_FFFF。下图显示了比较事件和中断的时序。
计数器值覆盖
可以在任何时候将EPIT计数器值设置为所需要的值。操作方法是,设置控制寄存器中的IOVW位,然后将所需要的值写入到加载寄存器。如果EPIT正在运行,则计数器将从覆盖值继续计数。
12.6 EPIT寄存器介绍
12.6.1 Control register (EPITx_CR)
EPIT控制寄存器
bit25-24,CLKSRC,选择时钟源,00与时钟源断开,01选择Peripheral clk,10选择Hign-frequency参考时钟,11选择low-frequency参考时钟
bit23-22,OM,输出引脚的模式,00与输出引脚断开,01翻转引脚状态,10清0引脚状态,11置位引脚状态
bit21,STOPEN,stop mode时是否关闭EPIT
bit19,WAITEN,wait mode时是否关闭EPIT
bit18,DBGEN,debug mode时是否使能EPIT
bit17,IOVW,写加载寄存器是否覆盖计数器的值
bit16,SWR,软件复位
bit15-4,PRESCALER,预分频计数器
bit3,RLD,计数器计数到0时是翻转到0xFFFF_FFFF,还是从加载寄存器加载
bit2,OCIEN,比较中断是否使能
bit1,ENMOD,0时从上次关闭时的计数值继续计数,为1时,如果RLD为1的话,从加载计数器开始计数,否则从0xFFFF_FFFF开始计数
bit0,EN,是否使能EPIT
12.6.2 Status register (EPITx_SR)
EPIT状态寄存器
状态寄存器只有一个状态位,用来表示输出比较事件是否发生,写1清除掉该位。
bit0,OCIF,比较事件是否发生
12.6.3 Load register (EPITx_LR)
EPIT加载寄存器
如果EPIT_CR的RLD置位的话,当EPIT计数器计数到0时,会将EPIT_LR值加载到计数器中。如果IOVW位设置的话,往该寄存器写值会同时覆盖掉计数器的值。这个覆盖特性与RLD是否设置无关。
加载计数的值
12.6.3 Compare register (EPITx_CMPR)
EPIT比较寄存器
比较寄存器用来决定什么时候产生比较事件。
比较寄存器,当计数器等于这个值时产生比较中断。
12.6.4 Counter register (EPITx_CNR)
EPIT计数器
EPIT计数器的值,可以在任何时候读,不会影响计数流程,这是个只读寄存器。如果控制寄存器的IOVW位设置的话,当往加载寄存器EPIT_IR写值时会覆盖当前的计数值。
12.7 EPIT查询实现延时代码详解
12.7.1 代码分析
12.7.1.1 epit_poll_init函数
通过epit_poll_init和epit_poll_restart两个函数来实现,epit_poll_init函数首先对EPIT进行软件复位,设置EPIT为set-and-forget模式,设置enable mode,时钟源选择Peripheral Clock (ipg_clk)为66M,预分频值设置为0(即预分频值为1),使能overwrite,比较寄存器CMPR设置为0,最后使能OCIEN,打开输出比较中断。
epit_poll_init代码在裸机Git仓库 NoosProgramProject/(12_定时器编程/009_timer_epit_poll/epit.c):
void epit_poll_init(EPIT_Type *base)
{
base->CR = 0;
/* software reset
* bit16
*/
base->CR |= (1 << 16);
/* wait for software reset self clear*/
while((base->CR) & (1 << 16))
;
/*
* EPIT_CR
* bit21 stopen; bit19 waiten; bit18 debugen
* bit17 overwrite enable; bit3 reload
* bit2 compare interrupt enable; bit1 enable mode
*/
base->CR |= (1 << 21) | (1 << 19) | (1 << 17) | (1 << 3) | (1 << 1);
/*
* EPIT_CR
* bit25-24: 00 off, 01 peripheral clock(ipg clk), 10 high, 11 low
* bit15-4: prescaler value, divide by n+1
*/
base->CR &= ~((0x3 << 24) | (0xFFF << 4));
base->CR |= (1 << 24);
/* EPIT_CMPR: compare register */
base->CMPR = 0;
/* EPIT_LR: load register , assue use ipc clk 66MHz*/
//base->LR = USEC_TO_COUNT(us);
/* EPIT_CR bit2 OCIEN compare interrupt enable */
base->CR |= (1 << 2);
}
12.7.1.2 epit_poll_restart函数
epit_poll_restart函数首先关闭首先关闭EPIT,然后根据延时时间设置加载寄存器LR,为了防止状态寄存器已经设置,先清除一下状态寄存器OCIF位,再使能EPIT计数器,等待状态寄存器OCIF位设置,设置后清除一下状态寄存器OCIF位。
代码在裸机Git仓库 NoosProgramProject/(12_定时器编程/009_timer_epit_poll/epit.c):
void epit_poll_restart(EPIT_Type *base, unsigned int us)
{
epit_run(base, 0);
/* EPIT_LR: load register , assue use ipc clk 66MHz*/
base->LR = USEC_TO_COUNT(us);
/* write 1 clear it, avoid it happened before */
EPIT1->SR |= (1 << 0);
epit_run(base, 1);
/* wait compare event happened*/
while(!(EPIT1->SR & 0x1))
/* write 1 clear it */
EPIT1->SR |= (1 << 0);
}
12.7.1.3 epit_run函数
epit_run函数用来使能和关闭EPIT。
代码在裸机Git仓库 NoosProgramProject/(12_定时器编程/009_timer_epit_poll/epit.c):
void epit_run(EPIT_Type *base, int on)
{
/* EPIT_CR bit0 EN */
if (on)
base->CR |= (1 << 0);
else
base->CR &= ~(1 << 0);
}
12.7.1.4 主函数
使用epit_poll_init初始化EPIT1,不断调用epit_poll_restart(EPIT1, 1000000)延时1s,并且依次点亮和关闭绿灯。
代码在裸机Git仓库 NoosProgramProject/(12_定时器编程/009_timer_epit_poll/main.c):
epit_poll_init(EPIT1);
while(1) {
epit_poll_restart(EPIT1, 1000000);
GPIO5->DR &= ~(1<<3); //led on
printf("led is on\r\n");
epit_poll_restart(EPIT1, 1000000);
GPIO5->DR |= (1<<3); //led off
printf("led is off\r\n");
}
12.7.2 参考章节《4-1.4编译程序》编译程序
进入 裸机Git仓库 NoosProgramProject/(12_定时器编程/009_timer_epit_poll) 源码目录
12.7.3 参考章节《4-1.4映像文件烧写、运行》烧写、运行程序
此时观察串口及开发板led灯现象
绿灯不断闪烁,每隔1s翻转状态
串口输出不断打印”led is on”和“led is off”,如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zTQdpYLB-1642060245774)(https://cdn.jsdelivr.net/gh/DongshanPI/HomeSite-Photos@main/IMX6ULL-BareMetal/12_timer_image023.png)]
12.8 EPIT中断实现延时代码详解
12.8.1 EPIT1中断号的确定
查询数据手册chapter 3的Table3-1中断号表,EPIT1如下所示:
gic中断号需要再这个序号基础上加上32,所以EPIT1的gic中断号为56+32=88。
12.8.1 代码分析
12.8.1.1 初始化EPIT1
通过epit_init函数初始化EPIT1,首先对EPIT进行软件复位,设置EPIT为set-and-forget模式,设置enable mode,时钟源选择Peripheral Clock (ipg_clk)为66M,预分频值设置为0(即预分频值为1),比较寄存器设置为0,加载寄存器根据延时值设置。
epit_init代码在在裸机Git仓库 NoosProgramProject/(12_定时器编程/009_timer_epit_int/epit.c):
/* assume use ipc clk which is 66MHz, 1us against to 66 count */
#define USEC_TO_COUNT(us) (us * 66 - 1)
void epit_init(EPIT_Type *base, unsigned int us)
{
base->CR = 0;
/* software reset
* bit16
*/
base->CR |= (1 << 16);
/* wait for software reset self clear*/
while((base->CR) & (1 << 16))
;
/*
* EPIT_CR
* bit21 stopen; bit19 waiten; bit18 debugen
* bit17 overwrite enable; bit3 reload
* bit2 compare interrupt enable; bit1 enable mode
*/
base->CR |= (1 << 21) | (1 << 19) | (1 << 3) | (1 << 1);
/*
* EPIT_CR
* bit25-24: 00 off, 01 peripheral clock(ipg clk), 10 high, 11 low
* bit15-4: prescaler value, divide by n+1
*/
base->CR &= ~((0x3 << 24) | (0xFFF << 4));
base->CR |= (1 << 24);
/* EPIT_CMPR: compare register */
base->CMPR = 0;
/* EPIT_LR: load register , assue use ipc clk 66MHz*/
base->LR = USEC_TO_COUNT(us);
}
12.8.1.2 打开或者关闭比较中断
epit_enable_interrupt函数用来打开或者关闭比较中断。
代码在在**裸机Git仓库 NoosProgramProject/(12_定时器编程/**009_timer_epit_int/epit.c):
void epit_enable_interrupt(EPIT_Type *base, int on)
{
/* EPIT_CR bit2 OCIEN compare interrupt enable */
if (on)
base->CR |= (1 << 2);
else
base->CR &= ~(1 << 2);
}
12.8.1.3 EPIT运行使能函数
设置或者关闭控制寄存器的运行位。
代码在裸机Git仓库 NoosProgramProject/(12_定时器编程/009_timer_epit_int/epit.c):
void epit_run(EPIT_Type *base, int on)
{
/* EPIT_CR bit0 EN */
if (on)
base->CR |= (1 << 0);
else
base->CR &= ~(1 << 0);
}
12.8.1.4 中断处理函数
EPIT1_handle_irq首先写1清除中断状态位,并且翻转绿灯的状态。
代码在裸机Git仓库 NoosProgramProject/(12_定时器编程/009_timer_epit_int/main.c):
void EPIT1_handle_irq(void)
{
static int on = 1;
printf("EPIT1 interrupt happened\r\n");
/* write 1 clear it */
EPIT1->SR |= (1 << 0);
/* read GPIO5_DR to get GPIO5_IO01 status*/
if(on) {
/* led off, set GPIO5_DR to configure GPIO5_IO03 output 1 */
GPIO5->DR |= (1<<3); //led on
on = 0;
} else {
/* led on, set GPIO5_DR to configure GPIO5_IO03 output 0 */
GPIO5->DR &= ~(1<<3); //led off
on = 1;
}
}
12.8.1.5 主函数
主函数里调用epit_init(EPIT1, 1000000)设置延时时间为1s,注册中断处理函数,设置中断处理函数为EPIT1_handle_irq,gic_enable_irq(EPIT1_IRQn)打开EPIT1对应的gic使能位,调用epit_enable_interrupt(EPIT1, 1)打开EPIT的比较中断使能位,最后调用epit_run(EPIT1, 1)使能EPIT开始计数。
代码在裸机Git仓库 NoosProgramProject/(12_定时器编程/009_timer_epit_int/main.c):
epit_init(EPIT1, 1000000);// set 1s
request_irq(EPIT1_IRQn, (irq_handler_t)EPIT1_handle_irq, NULL);
gic_enable_irq(EPIT1_IRQn);
epit_enable_interrupt(EPIT1, 1);
epit_run(EPIT1, 1);
12.8.1.6 参考章节《4-1.4编译程序》编译程序
进入裸机Git仓库 NoosProgramProject/(12_定时器编程/009_timer_epit_int) 源码目录
12.8.1.7 参考章节《3-1.4映像文件烧写、运行》烧写、运行程序
此时观察串口及开发板led灯现象
绿灯不断闪烁,每隔1s翻转状态
串口输出如下: