十六、定时器灯闪烁
1. 配置流程
一般使用定时器功能,都需要有以下几个步骤。、
- 解除寄存器写保护
- 开启时钟(定时器时钟)
- 配置定时器参数
- 配置中断和NVIC
- 使能中断事件和定时器
- 编写中断服务函数 这里就以定时器2_1为例进行介绍。
1.1 解除寄存器写保护
HC32F4A0中的很多寄存器是不可以直接修改写入的,所以要先解除相关的寄存器读保护。
// 解除保护
LL_PERIPH_WE(LL_PERIPH_GPIO | LL_PERIPH_FCG | LL_PERIPH_PWC_CLK_RMU);
2
1.2 开启时钟
/* 使能 Timer2_1 时钟 */
FCG_Fcg2PeriphClockCmd(FCG2_PERIPH_TMR2_1, ENABLE);
2
1.3 配置定时器参数
因为我们在board.c文件中已经对整个系统的时钟进行了设置:
所以我们选定的PCLK1的时钟频率为120MHz。
要初始化定时器就要先配置这个结构体,首先定义这个结构体
stc_tmr2_init_t stcTmr2Init;
关于定时器参数配置和初始化定时器代码如下:
/* 使用默认值配置缺省的参数 */
(void)TMR2_StructInit(&stcTmr2Init);
/* 结构体配置 */
/*
(120MHz / 1024) * 1000000 / 2 = 58593.75 Hz (0.5秒)
*/
stcTmr2Init.u32ClockSrc = TMR2_CLK_PCLK1; // 120MHz的时钟源
stcTmr2Init.u32ClockDiv = TMR2_CLK_DIV1024; // 分频1024
stcTmr2Init.u32Func = TMR2_FUNC_CMP; // 输出比较
stcTmr2Init.u32CompareValue = (uint32_t)(58593 - 1); // 比较值
(void)TMR2_Init(CM_TMR2_1, TMR2_CH_A, &stcTmr2Init);
2
3
4
5
6
7
8
9
10
11
12
通过 (120MHz / 1024) * 1000000 / 2 计算得到了 58593.75 Hz,相当于 0.5 秒。
这段代码:
(void)TMR2_StructInit(&stcTmr2Init);
:这行代码使用了一个函数TMR2_StructInit
,它的作用是将结构体 stcTmr2Init 中的所有成员都设置为默认值。这样做可以确保在设置具体参数之前,所有的参数都被正确初始化。stcTmr2Init.u32ClockSrc = TMR2_CLK_PCLK1;
:将时钟源设置为 PCLK1,表示使用 PCLK1 作为 Timer2 的时钟源。在这里,PCLK1 被设置为 120MHz。stcTmr2Init.u32ClockDiv = TMR2_CLK_DIV1024;
:将时钟分频设置为 1024,表示将时钟源分频为 1024。这意味着时钟源的频率会被分频为更低的频率,以便用于 Timer2 模块。stcTmr2Init.u32Func = TMR2_FUNC_CMP;
:将 Timer2 设置为比较模式。在比较模式下,Timer2 会将计数器值与比较值进行比较,并在匹配时产生中断或其他操作。stcTmr2Init.u32CompareValue = (uint32_t)(58593 - 1);
:设置比较值为 58593 - 1。这个值将用于与 Timer2 的计数器值进行比较,当两者相等时,可以触发比较匹配中断或执行其他操作。(void)TMR2_Init(CM_TMR2_1, TMR2_CH_A, &stcTmr2Init);
:最后一行代码调用了TMR2_Init
函数,将上述配置应用到 Timer2 模块的通道 A 上,从而完成 Timer2 模块的初始化设置。
1.4 配置中断和NVIC
定时器的参数配置好之后,定时器基本就配置好了。不过我们需要在中断函数中去执行对应的功能,就要对中断进行操作。前面介绍过,如果要使用中断功能,就需要配置中断优先级。中断分组还继续沿用之前的配置。
中断配置结构体:
stc_irq_signin_config_t stcIrq;
配置中断参数和NVIC参数:
stcIrq.enIntSrc = INT_SRC_TMR2_1_CMP_A; // 中断号
stcIrq.enIRQn = INT050_IRQn; // 中断号定义
stcIrq.pfnCallback = &TMR2_Cmp_IrqCallback; // 中断服务函数
(void)INTC_IrqSignIn(&stcIrq);
NVIC_ClearPendingIRQ(stcIrq.enIRQn);
NVIC_SetPriority(stcIrq.enIRQn, DDL_IRQ_PRIO_03); // 优先级
NVIC_EnableIRQ(stcIrq.enIRQn);
2
3
4
5
6
7
8
- 这段代码是在配置和启用一个特定的中断。首先,创建了一个
stc_irq_signin_config_t
类型的变量stcIrq
,用来存储中断相关的配置信息。 - 代码设置了三个参数:
enIntSrc
设置为INT_SRC_TMR2_1_CMP_A
,表示中断源为定时器2的比较匹配功能A。enIRQn
设置为INT050_IRQn
,这是一个中断号的定义。pfnCallback
设置为&TMR2_Cmp_IrqCallback
,这是中断服务函数的地址。
- 调用
INTC_IrqSignIn(&stcIrq)
函数来注册这个中断。这个函数会将配置好的中断信息传递给中断控制器,以便系统在中断事件发生时调用相应的中断服务函数。 - 代码使用
NVIC
系列函数来配置Cortex-M4
处理器的中断控制器:
NVIC_ClearPendingIRQ(stcIrq.enIRQn)
用来清除指定中断的挂起状态。NVIC_SetPriority(stcIrq.enIRQn, DDL_IRQ_PRIO_03)
设置中断优先级为3
。NVIC_EnableIRQ(stcIrq.enIRQn)
启用指定的中断。
通过这些步骤,代码成功配置了特定中断的相关参数,并启用了该中断,以便在中断事件发生时执行相应的中断服务函数。
1.5 能中断事件和定时器
启用 Timer2_1 指定 INT_SRC_TMR2_1_CMP_A 中断:
/* 启用 Timer2_1 指定 INT_SRC_TMR2_1_CMP_A 中断 */
TMR2_IntCmd(CM_TMR2_1, TMR2_INT_MATCH_CH_A, ENABLE);
2
使能Timer2_1 CH_A:
// 使能Timer2_1 CH_A
TMR2_Start(CM_TMR2_1, TMR2_CH_A);
2
1.6 编写中断服务函数
使能中断之后,如果定时时间到,就会跳转到中断处理函数里面执行。需要编写中断处理函数。
我们在IRQ中断配置的时候已经将TMR2_Cmp_IrqCallback函数名作为参数传入结构体进行初始化了,所以我们只需要实现这个函数的逻辑即可。
TMR2_Cmp_IrqCallback // 定时器中断服务函数
在中断处理函数里需要检测中断标志位是否被置位。
en_flag_status_t TMR2_GetStatus(const CM_TMR2_TypeDef *TMR2x, uint32_t u32Flag)
这个函数是获取中断标志位。有两个参数,第一个参数就是要检测的定时器外设,第二个参数就是触发的中断源。有一个返回值
en_flag_status_t ,返回值的状态为SET和RESET。需要注意的是每次中断执行完毕之后都需要清除一下中断标志位等待下一次中断发生。
中断服务函数编写代码如下:
void TMR2_Cmp_IrqCallback(void)
{
if( flag )
{
// 如果LED为灭灯,则开启LED
GPIO_ResetPins(GPIO_PORT_B,GPIO_PIN_02); // 低电平
printf("LED []\r\n");
flag = 0;
}
else
{
// 如果LED为亮灯,则关闭LED
GPIO_SetPins(GPIO_PORT_B,GPIO_PIN_02); // 高电平
printf("LED []\r\n");
flag = 1;
}
// 清除 TMR2_FLAG_MATCH_CH_A 标志位
TMR2_ClearStatus(CM_TMR2_1, TMR2_FLAG_MATCH_CH_A);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
到此有关定时器中断的配置就完成了。
2.实验现象
烧写我们的代码之后,每隔0.5秒钟在串口助手上打印一次LED的状态!!!,然后LED会每隔0.5秒钟时间闪烁一下。