效果展示
<iframe allowfullscreen="true" data-mediaembed="csdn" frameborder="0" id="MJsKzV7C-1731566505079" src="https://live.csdn.net/v/embed/432499"></iframe>TMR-基本定时器定时
使用基本定时器进行定时,使LED按 1 Hz的频率闪烁
硬件原理图
我们这次只用到PA8引脚,其他几个引脚不看
源代码
关于LED的代码就不贴出了,LED相关代码可以在流水灯文章中找到。
定时器部分
#ifndef __BSP_BASE_TMR_H__
#define __BSP_BASE_TMR_H__
/* 包含头文件 ----------------------------------------------------------------*/
#include "apm32f10x.h"
#include "apm32f10x_tmr.h"
#include "apm32f10x_rcm.h"
#include "apm32f10x_misc.h"
// 如果使用TIM7,注释掉这个宏即可
#define USE_TMR6
#ifdef USE_TMR6 // 使用基本定时器TIM6
#define BASE_TMRX TMR6
#define BASE_TMRX_CLOCK RCM_APB1_PERIPH_TMR6
#define BASE_TMRX_IRQn TMR6_IRQn
#else // 使用基本定时器TIM7
#define BASE_TMRX TMR7
#define BASE_TMRX_CLOCK RCM_APB1_PERIPH_TMR7
#define BASE_TMRX_IRQn TMR7_IRQn
#endif
/* 函数声明 ------------------------------------------------------------------*/
void Base_TMRx_Config(void);
#endif
#include "bsp_base_tmr.h"
/**
* @brief 配置基本定时器
* @param 无
* @attention TMR_BaseConfig_T结构体里面有5个成员,TMR6和TMR7的寄存器里面只有
* division和period,所以使用TMR6和TMR7的时候只需初始化这两个成员即可,
* 另外三个成员是通用定时器和高级定时器才有.
*/
void Base_TMRx_Config(void)
{
TMR_BaseConfig_T TMR_BaseConfigStruct;
// 开始定时器时钟,基本定时器只有内部时钟
RCM_EnableAPB1PeriphClock(BASE_TMRX_CLOCK);
// 配置优先级分组
NVIC_ConfigPriorityGroup(NVIC_PRIORITY_GROUP_4);
// 中断周期为 = 1/(72MHZ /(71+1)) * 1000 = 1ms
TMR_BaseConfigStruct.division = 71;
TMR_BaseConfigStruct.period = 999;
TMR_ConfigTimeBase(BASE_TMRX, &TMR_BaseConfigStruct);
// 清除计数器中断标志位
TMR_ClearIntFlag(BASE_TMRX, TMR_INT_UPDATE);
// 开启定时器更新中断
TMR_EnableInterrupt(BASE_TMRX, TMR_INT_UPDATE);
// 配置定时器中断优先级
NVIC_EnableIRQRequest(BASE_TMRX_IRQn, 3, 0);
// 使能定时器
TMR_Enable(BASE_TMRX);
}
主程序部分
/* 包含头文件 ----------------------------------------------------------------*/
#include "bsp_led.h"
#include "bsp_base_tmr.h"
/* 私有类型定义 --------------------------------------------------------------*/
/* 私有宏定义 ----------------------------------------------------------------*/
/* 私有变量 ------------------------------------------------------------------*/
__IO uint16_t timer_count = 0;
/* 扩展变量 ------------------------------------------------------------------*/
/* 私有函数原形 --------------------------------------------------------------*/
/**
* @brief 主函数
* @return 无
*/
int main(void)
{
// 初始化板载LED灯
LED_GPIO_Config();
// 基本定时器初始化:1ms中断一次
Base_TMRx_Config();
while (1)
{
if (timer_count == 1000)
{
timer_count = 0;
LED1_TOGGLE;
}
}
}
中断部分
/* 包含头文件 ----------------------------------------------------------------*/
#include "bsp_led.h"
#include "bsp_base_tmr.h"
/* 私有类型定义 --------------------------------------------------------------*/
/* 私有宏定义 ----------------------------------------------------------------*/
/* 私有变量 ------------------------------------------------------------------*/
__IO uint16_t timer_count = 0;
/* 扩展变量 ------------------------------------------------------------------*/
/* 私有函数原形 --------------------------------------------------------------*/
/**
* @brief 主函数
* @return 无
*/
int main(void)
{
// 初始化板载LED灯
LED_GPIO_Config();
// 基本定时器初始化:1ms中断一次
Base_TMRx_Config();
while (1)
{
if (timer_count == 1000)
{
timer_count = 0;
LED1_TOGGLE;
}
}
}
代码分析
bsp_base_tmr.h该文件里都是各种宏定义我们就不看了,我们主要来看bsp_base_tmr.c文件中的内容
bsp_base_tmr.c
主要来讲下基本定时器的配置如何配置,之前说过标准库使用套路,套路如下:
- 声明定时器配置结构体
- 开启定时器对应时钟
- 配置结构体
- 将结构体填入初始化函数中
- 使能定时器
我们来具体看看结构体怎么配
/**
* @brief TMR Base Configure structure definition
*/
typedef struct
{
TMR_COUNTER_MODE_T countMode;
TMR_CLOCK_DIV_T clockDivision;
uint16_t period; /*!< This must between 0x0000 and 0xFFFF */
uint16_t division; /*!< This must between 0x0000 and 0xFFFF */
uint8_t repetitionCounter; /*!< This must between 0x00 and 0xFF, only for TMR1 and TMR8. */
} TMR_BaseConfig_T; ;
-
countMode
计数模式:TMR_COUNTER_MODE_UP
向上计数、TMR_COUNTER_MODE_DOWN
向下计数、TMR_COUNTER_MODE_CENTERALIGNED1
中心对齐模式1、TMR_COUNTER_MODE_CENTERALIGNED2
中心对齐模式2、TMR_COUNTER_MODE_CENTERALIGNED3
中心对齐模式3 -
clockDivision
时钟分频系数:TMR_CLOCK_DIV_1
1分频也就是不分频、TMR_CLOCK_DIV_2
二分频,TMR_CLOCK_DIV_4
四分频 -
period
周期:0-65535 -
division
预分频系数周期:0-65535 -
repetitionCounter
重复计数:0-255在讲这些参数怎么选之前我们先来看下基本定时器的结构框图,参考手册197页,结构框图如下
基本定时器的时钟源只有内部时钟,内部时钟经过
clockDivision
变成CK_INT,CK_INT经过控制器和division
(预分频器)变成CK_CNT。-
countMode
在基本定时器(TMR6和7)配置中只能选TMR_COUNTER_MODE_UP
,因为基本定时器只有向上计数模式 -
clockDivision
,我们内部时钟频率为72MHz,设置成TMR_CLOCK_DIV_1,分频后频率为72MHz -
division
,我们设置成71,实际分频(71+1),经过分频后,计数频率变为1MHz,至于这里为什么要加1?芯片就是这样设计的,可以看参考手册202页17.6.7 -
repetitionCounter
高级定时器中的,我们用不到 -
period
,我们设置成999,周期
T = 1 72 MHz 71 + 1 × 1000 = 1 ms T = \frac{1}{\frac{72 \text{ MHz}}{71+1}} \times 1000 = 1 \text{ ms} T=71+172 MHz1×1000=1 ms
这里设置成999,计数周期为什么是1000?可以这样理解,计数到999时还要再计数一次才会溢出所以是1000。我们的程序里还开启了定时器更新中断
TMR_EnableInterrupt(BASE_TMRX, TMR_INT_UPDATE);
,开启中断后还要配置下NVIC,NVIC_EnableIRQRequest(BASE_TMRX_IRQn, 3, 0);
,因为所有的中断都是由NVIC管理的。
apm32f10x_it.c
void TMR6_IRQHandler(void) { extern __IO uint16_t timer_count; if (TMR_ReadIntFlag(BASE_TMRX, TMR_INT_UPDATE) != RESET) { TMR_ClearIntFlag(BASE_TMRX, TMR_INT_UPDATE); // 清除更新中断标志位 timer_count++; } }
timer_count
在main.c文件中声明__IO uint16_t timer_count = 0;
,该中断服务函数主要功能就是计数,没进一次中断timer_count
加1。main.c
/** * @brief 主函数 * @return 无 */ int main(void) { // 初始化板载LED灯 LED_GPIO_Config(); // 基本定时器初始化:1ms中断一次 Base_TMRx_Config(); while (1) { if (timer_count == 1000) { timer_count = 0; LED1_TOGGLE; } } }
main程序中主要功能是翻转LED,当中断中
timer_count
到1000就翻转LED的状态。 -