4. 定时器中断实现秒表
任务目标
使用STM32定时器产生1ms中断,在中断服务函数中累加计数实现秒表计时,并通过串口打印当前时间。本案例以STM32F103系列为例,使用标准外设库完成定时器中断配置。
硬件环境
- STM32F103C8T6最小系统板(或兼容开发板)
- USB转TTL串口模块(连接PA9/PA10)
- ST-Link或J-Link调试器(用于程序下载)
关键代码
#include "stm32f10x.h"
#include <stdio.h>
volatile uint32_t tick_count = 0; // 1ms递增计数
volatile uint32_t seconds = 0; // 秒
volatile uint32_t minutes = 0; // 分
// 重定向fputc到串口1
int fputc(int ch, FILE *f) {
while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
USART_SendData(USART1, (uint8_t)ch);
return ch;
}
void TIM2_IRQHandler(void) {
if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET) {
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
tick_count++;
if (tick_count >= 1000) { // 1秒到达
tick_count = 0;
seconds++;
if (seconds >= 60) {
seconds = 0;
minutes++;
if (minutes >= 60) minutes = 0;
}
}
}
}
void TIM2_Init(void) {
TIM_TimeBaseInitTypeDef TIM_InitStruct;
NVIC_InitTypeDef NVIC_InitStruct;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
TIM_InitStruct.TIM_Prescaler = 7199; // 72MHz/7200 = 10kHz
TIM_InitStruct.TIM_CounterMode = TIM_CounterMode_Up;
TIM_InitStruct.TIM_Period = 9; // 10kHz/10 = 1kHz (1ms)
TIM_InitStruct.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInit(TIM2, &TIM_InitStruct);
NVIC_InitStruct.NVIC_IRQChannel = TIM2_IRQn;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStruct);
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
TIM_Cmd(TIM2, ENABLE);
}
void USART1_Init(void) {
GPIO_InitTypeDef GPIO_InitStruct;
USART_InitTypeDef USART_InitStruct;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_USART1, ENABLE);
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStruct);
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStruct);
USART_InitStruct.USART_BaudRate = 115200;
USART_InitStruct.USART_WordLength = USART_WordLength_8b;
USART_InitStruct.USART_StopBits = USART_StopBits_1;
USART_InitStruct.USART_Parity = USART_Parity_No;
USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStruct.USART_Mode = USART_Mode_RX | USART_Mode_TX;
USART_Init(USART1, &USART_InitStruct);
USART_Cmd(USART1, ENABLE);
}
int main(void) {
USART1_Init();
TIM2_Init();
printf("秒表启动\r\n");
while (1) {
printf("%02d:%02d\r", minutes, seconds);
delay_ms(100); // 每100ms刷新一次显示
}
}
// 简单延时函数(非精确,仅用于主循环刷新间隔)
void delay_ms(uint32_t ms) {
volatile uint32_t i;
for (; ms > 0; ms--)
for (i = 0; i < 8000; i++);
}
编译与下载
- 使用Keil MDK5创建工程,选择芯片型号STM32F103C8
- 添加标准外设库文件:stm32f10x_gpio.c、stm32f10x_tim.c、stm32f10x_usart.c、stm32f10x_rcc.c、misc.c
- 在工程选项中定义USE_STDPERIPH_DRIVER
- 编译后将程序通过ST-Link下载到开发板
验证方法
将USB转TTL模块的RX连接到PA9(USART1_TX),GND共地。打开串口调试助手,设置波特率115200、8位数据、1位停止位、无校验。复位开发板后,串口助手应持续收到形如00:00、00:01的时间显示,每秒钟数值递增一次,分钟数每60秒递增一次。若观察到时间按预期规律变化,则说明定时器中断配置正确,秒表功能实现成功。
常见问题
- 串口无输出:检查PA9是否正确连接串口模块RX,确认波特率设置一致,检查USART1时钟是否使能。
- 时间跳动异常:确认系统主频为72MHz。若使用8MHz外部晶振,需在SystemInit中正确配置PLL倍频。
- 编译报错:确认已添加全部所需库文件,并在C/C++选项卡中定义USE_STDPERIPH_DRIVER。