STM32定时器实战:高效多任务系统
本文介绍了利用STM32定时器实现多任务并发控制的方法。通过配置TIM2和TIM3定时器,分别实现5秒串口发送信息和2秒LED闪烁功能,避免了传统轮询方式占用CPU资源的问题。文章详细讲解了硬件准备、STM32CubeMX配置、Keil代码编写及实验验证步骤,并对比了定时器与轮询、状态机等传统方式的优势,指出定时器在精准计时和系统资源利用上的显著优越性。该实验为嵌入式开发提供了高效的多任务处理解决
在嵌入式开发的世界里,精准的时间控制是实现复杂功能的关键。传统的延时函数会让 CPU 陷入 “死循环”,无法高效处理其他任务。而 STM32 的定时器就像给 CPU 配备了智能闹钟,既能实现精确计时,又能让 CPU 并行处理多个任务。今天,我们就通过一个有趣的实验,用定时器实现每隔 5 秒串口发送信息,同时让 LED 每隔 2 秒闪烁,体验多任务并发的魅力!
一、实验前的准备:工欲善其事,必先利其器
1. 硬件装备
- STM32 开发板:选用经典的 STM32F103C8T6 开发板,它丰富的定时器资源和 GPIO 引脚,是本次实验的绝佳选择。
- USB 转串口模块:采用 CH340 芯片的模块,负责搭建开发板与电脑之间的通信桥梁,方便我们通过串口助手查看发送的数据。
- 杜邦线:若干,用于连接开发板、USB 转串口模块以及 LED 对应的 GPIO 引脚(这里假设 LED 连接到 PA5 引脚)。
- 电脑:安装 Windows 系统,准备好开发所需的软件。
2. 软件工具
- STM32CubeMX:图形化配置神器,帮我们轻松搞定 STM32 外设初始化,本次使用 [具体版本]。
- Keil MDK:专业的集成开发环境,用于编写、编译和调试代码,版本号为 [具体版本]。
- 串口助手:选择 sscom 5.13.1 版本,实时接收 STM32 发送的数据。
- CH340 驱动程序:确保 USB 转串口模块能在电脑上正常工作。
二、STM32CubeMX 配置:搭建实验的基石
1. 新建工程
打开 STM32CubeMX,点击 “File”→“New Project”,在芯片选择界面搜索 “STM32F103C8T6”,选中后点击 “Start Project”,一个崭新的工程就创建好啦!
2. 定时器配置:给 CPU 设定闹钟
- TIM2(5 秒定时器):
-
- 在 “Pinout & Configuration” 选项卡中,展开 “Timers” 找到 “TIM2”。
-
- 将 “Counter Mode” 设为 “Up Counter”(向上计数模式)。
-
- 在 “Parameter Settings” 里,把 “Prescaler (PSC)” 设为 7199,“Counter Period (ARR)” 设为 49999。这样计算下来,定时器计数到目标值刚好耗时 5 秒,就像设定好了一个 5 秒的闹钟。
-
- 切换到 “NVIC Settings”,勾选 “TIM2 global interrupt”,开启定时器 2 的全局中断,让 CPU 能收到闹钟提醒。
- TIM3(2 秒定时器):参照 TIM2 的配置方法,将 “Prescaler (PSC)” 设为 7199,“Counter Period (ARR)” 设为 19999,实现 2 秒定时,并在 “NVIC Settings” 中开启中断。
3. USART1 配置:建立通信通道
找到 “USART1”,配置为异步通信模式:波特率 115200bps,8 位数据位,1 位停止位,无校验位,不使用硬件流控制。这样,开发板就能和电脑顺畅 “对话” 了。
4. LED 引脚配置:点亮小灯
假设 LED 连接在 PA5 引脚,将其 “Mode” 设为 “推挽输出(Output Push Pull)”,“Speed” 设为 “中速(Medium Speed)”,为 LED 闪烁做好准备。
5. 时钟树配置:提供稳定动力
在 “Clock Configuration” 选项卡中,选择 8MHz 的外部高速时钟(HSE),通过 PLL 将时钟倍频到 72MHz,为整个系统提供稳定的 “心跳”。
6. 生成工程代码
在 “Project Manager” 中设置好工程信息,选择 “MDK-ARM” 作为工具链。在 “Code Generator” 里勾选生成初始化文件,点击 “Generate Code”,Keil MDK 工程代码就自动生成啦!
三、Keil MDK 代码编写:赋予系统灵魂
1. 打开工程
在 Keil MDK 中打开刚刚生成的工程文件(.uvprojx),准备编写代码。
2. 中断回调函数:响应闹钟提醒
在 “main.c” 文件中添加定时器中断回调函数:
// 定时器2中断回调函数(5秒定时)
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {
if (htim == &htim2) {
char tx_buffer[] = "hello windows!\r\n";
HAL_UART_Transmit(&huart1, (uint8_t*)tx_buffer, sizeof(tx_buffer), 1000);
}
}
// 定时器3中断回调函数(2秒定时)
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {
if (htim == &htim3) {
HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5); // 翻转LED引脚电平
}
}
这两个函数就像是闹钟响起后的 “处理员”,定时器 2 定时时间到,就通过串口发送 “hello windows!”;定时器 3 时间到,就翻转 LED 引脚电平,让 LED 闪烁。
3. 启动定时器:开启定时任务
在main函数中,初始化完外设后,启动定时器 2 和定时器 3:
int main(void) {
HAL_Init();
MX_GPIO_Init();
MX_USART1_UART_Init();
MX_TIM2_Init();
MX_TIM3_Init();
// 启动定时器2和定时器3
HAL_TIM_Base_Start_IT(&htim2);
HAL_TIM_Base_Start_IT(&htim3);
while (1) {
// CPU可在此期间处理其他任务,如传感器数据采集
}
}
4. 编译工程
点击 Keil MDK 工具栏的 “编译(Build)” 按钮,检查代码语法。如果编译通过,就会生成可烧录的.hex 文件。
四、实验验证:见证成果的时刻
1. 硬件连接
- 将 USB 转串口模块的 TX 引脚连到开发板的 PA9(USART1_TX),RX 引脚连到 PA10(USART1_RX),GND 与开发板 GND 相连。
- 确保 LED 对应的 PA5 引脚连接正确。
2. 烧录程序
用 ST-Link、J-Link 等工具,把生成的.hex 文件烧录到开发板中。
3. 运行与观察
打开串口助手,设置好波特率等参数,选择对应的串口号。给开发板上电,就能看到串口助手每隔 5 秒收到 “hello windows!”,开发板上的 LED 每隔 2 秒闪烁一次,多任务并发完美实现!
五、无定时器方案对比:凸显定时器优势
1. 轮询方式
修改main函数代码为轮询方式:
int main(void) {
HAL_Init();
MX_GPIO_Init();
MX_USART1_UART_Init();
uint32_t send_time = 0;
uint32_t led_time = 0;
while (1) {
uint32_t current_time = HAL_GetTick();
// 5秒串口发送任务
if (current_time - send_time >= 5000) {
char tx_buffer[] = "hello windows!\r\n";
HAL_UART_Transmit(&huart1, (uint8_t*)tx_buffer, sizeof(tx_buffer), 1000);
send_time = current_time;
}
// 2秒LED闪烁任务
if (current_time - led_time >= 2000) {
HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5);
led_time = current_time;
}
}
}
虽然功能实现了,但 CPU 一直在查询时间,资源被大量占用,时间精度也不好。
2. 状态机方式
通过状态变量和转移条件编写代码(代码略),比轮询灵活些,但代码复杂,时间精度同样不理想。
对比下来,定时器在精准计时和多任务并发上的优势一目了然!
结语
通过这次实验,我们掌握了 STM32 定时器实现多任务并发的方法,也看到了定时器相比传统方式的巨大优势。在实际项目中,合理运用定时器,能让我们的嵌入式系统更加高效、智能。如果你在实验过程中遇到问题,或者有新的想法,欢迎在评论区交流讨论,一起探索更多嵌入式开发的奇妙世界!
更多推荐



所有评论(0)