STM32单片机的基本定时器的功能比较简单,但提供精确的定时是其基本功能,这个是能轻松做到的。
应用程序中可以利用基本定时器为系统提供精确的定时,为某些需要定时完成的一些任务提供时间基准。
比如应用程序中需要每隔200ms刷新一下显示,每隔50ms刷新一下某个开关量输出,那么可以设计定时器的中断时间为10ms。再针对每个需要定时执行的任务设置一个计数器,比如针对刷新显示定义一个刷新显示计数器 ,针对开关量输出定义一个开关量输出计数器。
当每次进中断时,刷新显示计数器+1,开关量输出计数器+1,当刷新显示计数器=20时(20*10ms = 200ms),置位**(刷新显示的标志)** ,当开关量输出计数器=5时(5*10ms = 50ms),置位(开关量输出的标志)。
主程序循环检查这些标志位是否置位,如检测到标志置位则调用相关程序:如刷新显示,开关量输出,同时清除标志位,等待下次标志再次置位。从而完成这些定时执行的任务。
实例演示:TIM6每隔10ms产生一次中断,主程序每隔200ms刷新一次流水灯。
实施步骤:
1、打开TIM6的时钟使能:
参考本博主文章**《STM32单片机:外设时钟(STM32L4xx)》**

查阅芯片手册,TIM6的时钟由 APB1-PCLK1提供,所以将寄存器RCC_APB1ENR1.TIM6EN 置位即可使能TIM6的时钟:

2、设定合适的输入时钟频率:
本实验中TIM6的时钟源频率是36.864MHz,将其10分频**(TIM_PSC=9)**得到3.6864MHz,作为定时器的时钟频率,将定时器的溢出值设置为36864,溢出时间 = (36864 / 3.6864)us = 10000us = 10ms,当定时器计数到此值时溢出,产生中断。
实现的代码如下:
STM32L4xx_timer.h:定时器的寄存器定义
cpp
#define TIM6 ((uint32_t)0x40001000) // Basic timer 6
#define TIM7 ((uint32_t)0x40001400) // Basic timer 7
#define TIM_CR1(timx) REG32(timx + 0x00000000) // TIMx control register 1
#define TIM_CR2(timx) REG32(timx + 0x00000004) // TIMx control register 2
#define TIM_DIER(timx) REG32(timx + 0x0000000C) // TIMx DMA/Interrupt enable register
#define TIM_SR(timx) REG32(timx + 0x00000010) // TIMx status register
#define TIM_EGR(timx) REG32(timx + 0x00000014) // TIMx event generation register
#define TIM_CNT(timx) REG32(timx + 0x00000024) // TIMx counter
#define TIM_PSC(timx) REG32(timx + 0x00000028) // TIMx prescaler
#define TIM_ARR(timx) REG32(timx + 0x0000002C) // TIMx auto-reload register
dev_tim6.c: 定时器TIM6初始化,以及TIM6中断服务程序
cpp
uchar uc_200ms_Flag;
uchar uc_10ms_counter;
//---------------------------------------------------------------------------------------
// 初始化TIM6: 基本定时器(10ms中断一次)
//
// 时钟源: APB1-PCLK1 = 18.432MHz【80 MHz Max】
//
// RCC_CFGR_PPRE1 的分频系数 = 1 时 CLK = APB1-PCLK1
// RCC_CFGR_PPRE1 的分频系数 != 1 时 CLK = APB1-PCLK1 * 2
// 因为: 本系统 RCC_CFGR_PPRE1 的分频系数 = RCC_CFGR_PPRE1_DIV4 , 不等于1
// 所以: CLK = APB1-PCLK1 * 2 = 18.432MHz * 2 = 36.864MHz
//---------------------------------------------------------------------------------------
void DEV_TIM6_Init(void)
{
// 允许TIM6时钟
RCC_APB1ENR1 |= RCC_APB1ENR1_TIM6EN;
TIM_CR1(TIM6) = 0;
// 定时器时钟 = 输入时钟 / (PSC+1) = 36.864MHz / 10 = 3.6864 MHz
TIM_PSC(TIM6) = 9;
// 定时10ms的计数值 = 10000 us * 3.6864 MHz = 36864
TIM_ARR(TIM6) = 36864;
// 清除更新中断标志
TIM_SR(TIM6) = 0;
// 允许更新中断
TIM_DIER(TIM6) = 0;
TIM_DIER(TIM6) |= TIM_DIER_UIE;
// 启动TIM6计数器
TIM_CR1(TIM6) |= TIM_CR1_CEN;
// 开TIM6中断
NVIC_EnableIRQ(TIM6_DAC_IRQn);
}
//-----------------------------------------------------------------------------
// TIM6中断服务程序
//
// TIM6中断时间 = 36864 / 3.6864 MHz = 10ms
//-----------------------------------------------------------------------------
void TIM6_DAC_IRQHandler(void)
{
// 清除更新中断标志
TIM_SR(TIM6) = 0;
// 每10ms, 计数器+1
uc_10ms_counter++;
// 计数值达到20, 即每200ms, 标志uc_200ms_Flag置位, 主程序里处理uc_200ms_Flag标志
if (uc_10ms_counter >= 20)
{
uc_10ms_counter = 0;
uc_200ms_Flag = 1;
}
}
main.c: 主程序
cpp
extern uchar uc_200ms_Flag;
//-------------------------------------------------------------------------------------
// 本工程实现功能:
//
// 1. 启动外部高频晶体HSE(24.576MHz)
//
// 2. HSE作为PLL的输入时钟, 启动PLL模块(主PLL模块, PLLSAI1模块, PLLSAI2模块)
//
// 3. 将PA8配置成MCO, 用于将主PLL模块的输出时钟PLLCLK/16通过此引脚输出,
// 用于测试PLL时钟是否正常工作
//
// 4. 选择主PLL模块的输出时钟PLLCLK作为系统时钟SYSCLK
//
// 5. 基本定时器TIM6每10ms中断一次, 设置一个计数器, 每次中断+1,
// 计数到20时(即200ms), 刷新一次流水灯输出
//
// 6. 将PC6, PC7, PC8, PC9配置成普通IO输出, 并分别连接LED发光二极管, 显示流水灯,
// 指示程序正常运行
//
// 本工程运行环境: IAR for ARM 8.32.1
//
// 本工程运行MCU: STM32L431RCT6
//
// 注: 上电后程序从DEV_RCC_Init()这个函数开始运行, 然后再运行main()
//
//-------------------------------------------------------------------------------------
void main(void)
{
ulong ul_LedData;
DEV_GPIO_Init();
DEV_TIM6_Init();
ul_LedData = BIT(6);
GPIO_ODR(GPIOC) = ~ul_LedData;
while(1)
{
if (uc_200ms_Flag)
{
uc_200ms_Flag = 0;
GPIO_ODR(GPIOC) = ~ul_LedData;
ul_LedData <<= 1;
if (ul_LedData & BIT(10)) ul_LedData = BIT(6);
}
}
}