news 2026/7/10 10:18:17

STM32F103C8T6 定时器中断实战:4个独立按键+数码管实现交通灯3模式切换

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
STM32F103C8T6 定时器中断实战:4个独立按键+数码管实现交通灯3模式切换

STM32F103C8T6 定时器中断实战:4个独立按键+数码管实现交通灯3模式切换

1. 项目概述与设计思路

在嵌入式系统开发中,定时器中断和状态机设计是两个核心概念。本项目基于STM32F103C8T6最小系统板,通过三个定时器中断(显示刷新、秒计时、按键扫描)协同工作,配合四位数码管和四个独立按键,实现了一个具备三种工作模式的交通灯控制系统。

系统核心架构采用分层设计:

  • 硬件驱动层:处理GPIO、定时器、数码管等底层外设
  • 中断服务层:实现定时触发的关键任务
  • 业务逻辑层:管理状态转换和模式切换
  • 人机交互层:处理按键输入和显示输出

这种架构设计使得系统各模块耦合度低,便于后期功能扩展和维护。相比传统的51单片机方案,STM32的丰富外设资源和更高性能为复杂状态管理提供了硬件基础。

2. 硬件设计与关键外设配置

2.1 核心硬件选型

硬件模块型号/参数功能说明
主控MCUSTM32F103C8T672MHz主频,3个通用定时器
数码管显示4位共阴数码管显示倒计时,TM1650驱动
按键输入4路轻触开关模式切换/参数调整
交通指示灯红/黄/绿LED各2组模拟十字路口信号灯
电源管理AMS1117-3.3V提供稳定3.3V系统电压

2.2 定时器资源配置

// 定时器2配置 - 显示刷新(1ms) TIM_TimeBaseInitTypeDef TIM2_Init; TIM2_Init.TIM_Period = 1000-1; // 1kHz刷新率 TIM2_Init.TIM_Prescaler = 72-1; // 72MHz/72=1MHz TIM2_Init.TIM_ClockDivision = 0; TIM2_Init.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit(TIM2, &TIM2_Init); // 定时器3配置 - 秒计时(1s) TIM_TimeBaseInitTypeDef TIM3_Init; TIM3_Init.TIM_Period = 1000-1; // 1s中断 TIM3_Init.TIM_Prescaler = 72000-1; // 72MHz/72000=1kHz TIM_TimeBaseInit(TIM3, &TIM3_Init); // 定时器4配置 - 按键扫描(20ms消抖) TIM_TimeBaseInitTypeDef TIM4_Init; TIM4_Init.TIM_Period = 200-1; // 50Hz采样 TIM4_Init.TIM_Prescaler = 7200-1; // 72MHz/7200=10kHz TIM_TimeBaseInit(TIM4, &TIM4_Init);

2.3 GPIO分配方案

// 交通灯控制引脚 #define NS_RED GPIO_Pin_0 // PA0 #define NS_YELLOW GPIO_Pin_1 // PA1 #define NS_GREEN GPIO_Pin_2 // PA2 #define EW_RED GPIO_Pin_3 // PA3 #define EW_YELLOW GPIO_Pin_4 // PA4 #define EW_GREEN GPIO_Pin_5 // PA5 // 按键输入引脚 #define KEY_MODE GPIO_Pin_6 // PB6 #define KEY_UP GPIO_Pin_7 // PB7 #define KEY_DOWN GPIO_Pin_8 // PB8 #define KEY_EMER GPIO_Pin_9 // PB9

3. 软件架构与核心算法

3.1 状态机设计

系统采用三层状态机架构管理交通灯行为:

  1. 顶层模式状态机

    • 普通模式:固定时序循环
    • 设置模式:调整绿灯时长
    • 紧急模式:强制南北/东西通行
  2. 中层灯态状态机

    • 红灯状态
    • 绿灯状态
    • 黄灯状态
    • 全红过渡状态
  3. 底层显示状态机

    • 正常显示
    • 闪烁警告
    • 设置提示
stateDiagram-v2 [*] --> NormalMode NormalMode --> SettingMode: 长按MODE键 NormalMode --> EmergencyMode: 按下EMER键 SettingMode --> NormalMode: 再次按MODE键 EmergencyMode --> NormalMode: 再次按EMER键 state NormalMode { [*] --> RedState RedState --> GreenState: 计时结束 GreenState --> YellowState: 最后5秒闪烁 YellowState --> RedState: 计时结束 }

3.2 定时器中断服务例程

TIM3秒中断处理逻辑

void TIM3_IRQHandler(void) { if(TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET) { TIM_ClearITPendingBit(TIM3, TIM_IT_Update); // 全局秒计数器递增 sysTick++; // 根据当前模式处理计时逻辑 switch(currentMode) { case MODE_NORMAL: handleNormalMode(); break; case MODE_SETTING: handleSettingMode(); break; case MODE_EMERGENCY: handleEmergencyMode(); break; } } }

按键消抖算法

void TIM4_IRQHandler(void) { static uint8_t keyState[4] = {0}; static uint16_t keyCnt[4] = {0}; if(TIM_GetITStatus(TIM4, TIM_IT_Update) != RESET) { TIM_ClearITPendingBit(TIM4, TIM_IT_Update); // 扫描4个按键 for(int i=0; i<4; i++) { uint8_t currState = GPIO_ReadInputDataBit(KEY_PORT, KEY_PINS[i]); // 状态机消抖 if(currState != (keyState[i] & 0x01)) { keyCnt[i]++; if(keyCnt[i] >= DEBOUNCE_TIME) { keyState[i] = (keyState[i] << 1) | currState; keyCnt[i] = 0; // 检测下降沿(按键按下) if((keyState[i] & 0x03) == 0x02) { handleKeyPress(i); } } } else { keyCnt[i] = 0; } } } }

4. 三种工作模式实现细节

4.1 普通模式

标准交通灯时序

  • 南北绿灯:30秒(最后5秒闪烁)
  • 南北黄灯:3秒
  • 东西绿灯:20秒(最后5秒闪烁)
  • 东西黄灯:3秒
void handleNormalMode(void) { static uint8_t phase = 0; static uint16_t count = 0; count++; switch(phase) { case 0: // 南北绿灯,东西红灯 if(count >= NS_GREEN_TIME) { phase = 1; count = 0; setLights(NS_YELLOW | EW_RED); } else if(count >= (NS_GREEN_TIME-5)) { // 最后5秒绿灯闪烁 toggleLight(NS_GREEN); } break; case 1: // 南北黄灯,东西红灯 if(count >= YELLOW_TIME) { phase = 2; count = 0; setLights(NS_RED | EW_GREEN); } break; // 其他相位类似... } // 更新数码管显示 updateDisplay(count); }

4.2 设置模式

进入设置模式后:

  1. 数码管显示当前设置的绿灯时长
  2. 通过UP/DOWN键以5秒为步长调整(10-60秒范围)
  3. 再次按MODE键保存并退出

关键代码片段

void handleSettingMode(void) { static uint8_t blink = 0; // 数码管闪烁提示 if((sysTick % 10) == 0) { blink = !blink; setDisplayBlink(blink); } // 处理按键调整 if(keyEvent == KEY_UP) { greenTime += 5; if(greenTime > 60) greenTime = 10; } else if(keyEvent == KEY_DOWN) { greenTime -= 5; if(greenTime < 10) greenTime = 60; } // 显示当前设置值 showNumber(greenTime); }

4.3 紧急模式

特殊处理逻辑

  • 强制所有方向红灯(紧急停车)
  • 或强制特定方向绿灯(应急通行)
  • 倒计时暂停
  • 蜂鸣器报警提示
void handleEmergencyMode(void) { // 根据紧急类型设置灯态 if(emergencyType == EMERGENCY_STOP) { setLights(NS_RED | EW_RED); } else if(emergencyType == EMERGENCY_NS) { setLights(NS_GREEN | EW_RED); } else { setLights(NS_RED | EW_GREEN); } // 蜂鸣器报警 BUZZER_ON; delay_ms(100); BUZZER_OFF; }

5. 关键问题与优化技巧

5.1 定时器中断冲突处理

当多个定时器中断同时发生时,采用以下策略:

  1. 设置不同的中断优先级
    NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; NVIC_Init(&NVIC_InitStructure);
  2. 中断服务函数尽量简短
  3. 使用标志位在main循环中处理耗时任务

5.2 数码管显示刷新优化

采用动态扫描+缓冲区机制:

uint8_t digitBuffer[4]; // 显示缓冲区 void TIM2_IRQHandler(void) { static uint8_t pos = 0; if(TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET) { TIM_ClearITPendingBit(TIM2, TIM_IT_Update); // 关闭所有位选 GPIO_ResetBits(DIGIT_PORT, DIGIT_PINS); // 设置段选数据 GPIO_Write(SEGMENT_PORT, digitData[digitBuffer[pos]]); // 打开当前位选 GPIO_SetBits(DIGIT_PORT, digitPins[pos]); // 更新扫描位置 pos = (pos + 1) % 4; } }

5.3 低功耗设计考虑

  1. 在无车辆时段自动进入节能模式
  2. 动态调整显示亮度
  3. 使用停机模式+外部中断唤醒
void enterLowPowerMode(void) { // 关闭非必要外设时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, DISABLE); // 配置唤醒源 EXTI_InitStructure.EXTI_Line = EXTI_Line9; EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising; EXTI_Init(&EXTI_InitStructure); // 进入停机模式 PWR_EnterSTOPMode(PWR_Regulator_LowPower, PWR_STOPEntry_WFI); // 唤醒后恢复时钟 SystemInit(); }

6. 扩展功能与进阶设计

6.1 车流量检测扩展

通过红外对管或地磁传感器检测车辆:

void TIM5_IRQHandler(void) { // 输入捕获定时器 if(TIM_GetITStatus(TIM5, TIM_IT_CC1) != RESET) { vehicleCount++; TIM_ClearITPendingBit(TIM5, TIM_IT_CC1); } }

6.2 无线通信模块集成

添加ESP8266实现远程监控:

void sendTrafficStatus(void) { char buffer[128]; sprintf(buffer, "NS:%d/%d EW:%d/%d", nsGreenTime, nsCount, ewGreenTime, ewCount); ESP8266_Send(buffer); }

6.3 上位机配置界面

使用Qt开发配置工具:

# Python示例 - 串口配置工具 import serial import tkinter as tk def send_config(): ser = serial.Serial(port_entry.get(), 115200) ser.write(f"CONFIG,{ns_time.get()},{ew_time.get()}\r\n") ser.close() root = tk.Tk() tk.Label(root, text="NS绿灯时间:").pack() ns_time = tk.Scale(root, from_=10, to=60) ns_time.pack() tk.Label(root, text="EW绿灯时间:").pack() ew_time = tk.Scale(root, from_=10, to=60) ew_time.pack() tk.Button(root, text="发送配置", command=send_config).pack() root.mainloop()

7. 项目验证与测试方法

7.1 单元测试策略

  1. 定时器精度测试

    void testTimerAccuracy(void) { uint32_t start = sysTick; delay_ms(1000); uint32_t end = sysTick; printf("实际计时:%dms, 误差:%dms\n", end-start, (end-start)-1000); }
  2. 状态机转换测试

    void testStateTransition(void) { setMode(MODE_NORMAL); simulateKeyPress(KEY_MODE); assert(currentMode == MODE_SETTING); }

7.2 系统集成测试方案

  1. 正常模式压力测试

    • 连续运行24小时验证稳定性
    • 记录状态切换次数和时序准确性
  2. 紧急模式响应测试

    • 随机触发紧急事件
    • 测量从触发到完全响应的延迟
  3. 边界条件测试

    // 测试最小/最大绿灯时间设置 setGreenTime(10); // 最小值 setGreenTime(60); // 最大值

7.3 性能优化建议

  1. 使用DMA传输数码管数据
  2. 将频繁访问的变量定义为register类型
  3. 关键代码段使用内联汇编优化
// 使用DMA更新数码管显示 void initDisplayDMA(void) { DMA_InitTypeDef DMA_InitStructure; DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&GPIOB->ODR; DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)digitBuffer; DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; DMA_InitStructure.DMA_BufferSize = 4; DMA_Init(DMA1_Channel1, &DMA_InitStructure); DMA_Cmd(DMA1_Channel1, ENABLE); }
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/10 10:16:31

如何用BackgroundRemover快速实现专业级背景去除:完整入门指南

如何用BackgroundRemover快速实现专业级背景去除&#xff1a;完整入门指南 【免费下载链接】backgroundremover Background Remover lets you Remove Background from images and video using AI with a simple command line interface that is free and open source. 项目地…

作者头像 李华
网站建设 2026/7/10 10:16:19

23 组件生命周期:aboutToAppear、aboutToDisappear、onPageShow

23 组件生命周期&#xff1a;aboutToAppear、aboutToDisappear、onPageShow 前言 图&#xff1a;23 组件生命周期&#xff1a;aboutToAppear、aboutToDisappear、onPageShow 运行效果截图&#xff08;HarmonyOS NEXT&#xff09; 每个 ArkUI 组件从创建到销毁都有一个明确的生…

作者头像 李华
网站建设 2026/7/10 10:12:01

如何构建企业级Python DDD架构:5个领域驱动设计实战策略

如何构建企业级Python DDD架构&#xff1a;5个领域驱动设计实战策略 【免费下载链接】python-ddd Python DDD example 项目地址: https://gitcode.com/gh_mirrors/py/python-ddd 在复杂业务系统开发中&#xff0c;Python领域驱动设计&#xff08;DDD&#xff09;架构为…

作者头像 李华
网站建设 2026/7/10 10:07:35

香港公司注册地变更一般会选择哪些服务机构?

香港公司注册地变更&#xff0c;首先要判断企业说的是哪一种情况&#xff1a;如果只是香港本地 registered office change&#xff0c;通常由香港 company secretary 处理&#xff1b;如果是 BVI、Cayman、Bermuda 等海外公司迁入香港&#xff0c;本质上就是 cross-border re-d…

作者头像 李华