【33】单片机编程核心技巧:Switch驱动跑马灯速度控制

【33】单片机编程核心技巧:Switch驱动跑马灯速度控制

七律 · 速度

分支控速显神通,按键轻触改时钟。
计数器值随心变,LED流自从容。
状态迁移分快慢,标志联动定西东。
单片机中真王者,一招一式定乾坤。


摘要

本文以STC8H单片机为例,通过Switch语句结合按键输入实现跑马灯速度的动态调节。系统阐述按键扫描、计数器阈值管理及代码实现,重点解析Switch如何通过状态变量与速度标志位联动,实现LED流动速度的精准控制。实验验证表明,该方法可实时响应按键输入,实现速度的增减调节,展现了Switch语句在动态参数控制中的核心作用。


引言

Switch语句是单片机程序的核心控制结构,尤其适用于动态参数调节场景:

  1. 多状态管理:通过按键输入修改速度标志位,动态调整LED流动速度。
  2. 计数器联动:结合定时中断与计数器阈值,实现速度的量化控制。
  3. 代码清晰:通过Switch分支独立管理不同速度状态,提升可读性。

本文以单路8LED跑马灯为例,通过Switch语句驱动状态迁移,并结合按键输入实现速度调节,为开发者提供动态参数控制的标准化范式。


1. Switch语句与速度控制的核心逻辑

1.1 状态变量与速度标志位

  • 状态变量run_step(0~7),记录当前LED流动位置。
  • 速度标志位speed_level,控制计数器阈值(如LED_ON_TIME),决定LED点亮时长。

1.2 按键输入与速度调节

  • 按键1 :降低速度(增大LED_ON_TIME)。
  • 按键2 :提高速度(减小LED_ON_TIME)。
  • 去抖处理:通过延时计数器消除按键机械抖动。

2. 硬件与软件配置

2.1 硬件连接

  • LED组
    • 阳极通过 510Ω 电阻连接至 P1.0~P1.7 引脚。
    • 阴极接地。
  • 按键输入
    • 按键1接 P3.0 ,按键2接 P3.1,均配置为输入模式并接上拉电阻。

2.2 软件框架设计

2.2.1 状态机设计
  • 状态迁移 :通过run_step控制LED流动位置。
  • 速度控制 :通过修改LED_ON_TIME阈值调整流动速度。
2.2.2 按键扫描机制
  • 去抖逻辑 :按键按下后通过延时计数器(debounce_cnt)消除抖动。
  • 单次触发:按键按下后仅触发一次速度调节,避免重复响应。

3. 代码实现与解析

3.1 完整代码

c 复制代码
#include <stc8.h>  

// LED引脚定义  
#define LED_PORT   P1  
#define LED_PINS   8  

// 按键引脚定义  
#define KEY1_PORT  P3  
#define KEY1_PIN   0  
#define KEY2_PORT  P3  
#define KEY2_PIN   1  

// 定义LED亮灯时间(单位:中断次数)  
unsigned long LED_ON_TIME = 500; // 初始值:0.5秒  

// 状态变量与标志位  
unsigned char run_step = 0;  

// 按键去抖参数  
unsigned char debounce_cnt1 = 0;  
unsigned char debounce_cnt2 = 0;  
unsigned char key_pressed1 = 0;  
unsigned char key_pressed2 = 0;  

// 定时器1中断服务函数  
void timer1_isr() interrupt 3 {  
    TH1 = 0xFC;  // 重装定时器初值(1ms中断周期)  
    TL1 = 0x00;  

    // 按键1扫描(降低速度)  
    if (!KEY1_PORT & (1 << KEY1_PIN)) {  
        debounce_cnt1++;  
        if (debounce_cnt1 > 20 && !key_pressed1) {  
            LED_ON_TIME += 100; // 每次增加100ms  
            if (LED_ON_TIME > 2000) LED_ON_TIME = 2000; // 上限2秒  
            key_pressed1 = 1;  
        }  
    } else {  
        debounce_cnt1 = 0;  
        key_pressed1 = 0;  
    }  

    // 按键2扫描(提高速度)  
    if (!KEY2_PORT & (1 << KEY2_PIN)) {  
        debounce_cnt2++;  
        if (debounce_cnt2 > 20 && !key_pressed2) {  
            LED_ON_TIME -= 100; // 每次减少100ms  
            if (LED_ON_TIME < 100) LED_ON_TIME = 100; // 下限0.1秒  
            key_pressed2 = 1;  
        }  
    } else {  
        debounce_cnt2 = 0;  
        key_pressed2 = 0;  
    }  

    // 更新计数器  
    static unsigned long cnt = 0;  
    cnt++;  
}  

void main() {  
    // 初始化IO口  
    P1M0 = 0xFF; P1M1 = 0x00;  // P1为推挽输出  
    P3M0 &= ~((1 << KEY1_PIN) | (1 << KEY2_PIN)); // P3.0/1为输入模式  
    P3M1 &= ~((1 << KEY1_PIN) | (1 << KEY2_PIN));  

    // 定时器1配置(1ms中断周期)  
    TMOD |= 0x01;          // 设置定时器1为模式1(16位)  
    TH1 = 0xFC;            // 初值计算(系统时钟11.0592MHz)  
    TL1 = 0x00;  
    ET1 = 1;               // 使能定时器1中断  
    EA = 1;                // 全局中断使能  
    TR1 = 1;               // 启动定时器  

    // 初始状态:所有LED灭  
    LED_PORT = 0xFF;  

    while(1) {  
        switch(run_step) {  
            case 0:  
                if(cnt > LED_ON_TIME) {  
                    cnt = 0;  
                    LED_PORT = 0x01; // 点亮LED1(P1.0)  
                    run_step = 1;  
                }  
                break;  
            case 1:  
                if(cnt > LED_ON_TIME) {  
                    cnt = 0;  
                    LED_PORT = 0x02; // 点亮LED2(P1.1)  
                    run_step = 2;  
                }  
                break;  
            // 类似实现case 2~6  
            case 7:  
                if(cnt > LED_ON_TIME) {  
                    cnt = 0;  
                    LED_PORT = 0x80; // 点亮LED8(P1.7)  
                    run_step = 0;  
                }  
                break;  
        }  
    }  
}  

3.2 关键代码解析

3.2.1 按键扫描与速度调节
c 复制代码
// 按键1(降低速度)  
if (!KEY1_PORT & (1 << KEY1_PIN)) {  
    debounce_cnt1++;  
    if (debounce_cnt1 > 20 && !key_pressed1) {  
        LED_ON_TIME += 100; // 每次增加100ms  
        key_pressed1 = 1;  
    }  
} else {  
    debounce_cnt1 = 0;  
    key_pressed1 = 0;  
}  
  • 去抖逻辑 :通过debounce_cnt1计数器消除按键抖动(约20ms)。
  • 速度调节 :按键按下后,LED_ON_TIME增加100(对应0.1秒),上限为2秒。
3.2.2 Switch状态迁移
c 复制代码
switch(run_step) {  
    case 0:  
        if(cnt > LED_ON_TIME) {  
            LED_PORT = 0x01; // 点亮LED1  
            run_step = 1;  
        }  
        break;  
    // 其他case类似  
}  
  • 动态阈值LED_ON_TIME的值由按键输入动态调整,控制LED切换速度。

4. 实验验证

4.1 硬件连接示意图

复制代码
+-------------------+  
| STC8H单片机       |  
| P1.0-P1.7 → LED阳极 |  
| P3.0 → 按键1(降速)|  
| P3.1 → 按键2(增速)|  
+-------------------+  

4.2 预期结果

  • 初始状态:LED以0.5秒/灯的速度正向流动。
  • 按下按键1:速度降低(如1秒/灯)。
  • 按下按键2:速度提高(如0.2秒/灯)。
  • 重复按键:速度在0.1~2秒范围内可调。

5. 扩展应用

Switch语句的"战斗机"特性可扩展至复杂场景:

  1. 多级速度控制:通过按键组合实现预设速度(如慢/中/快三档)。
  2. 渐变速度模式:结合ADC输入实现速度的平滑调节。
  3. 组合控制:与方向控制结合,实现速度与方向的独立调节。

6. 结论

Switch语句通过状态变量与速度标志位的联动,实现了LED流动速度的动态控制。本文通过按键调节速度的案例,验证了其在参数调节中的高效性与灵活性,为开发者提供了动态参数控制的标准化设计思路。


附录:定时器配置说明

  1. 定时器1配置
    • TMOD |= 0x01:设置定时器1为模式1(16位定时)。
    • TH1 = 0xFC; TL1 = 0x00:初值计算公式:
      初值 = 65536 − 系统时钟 定时周期 × 12 \text{初值} = 65536 - \frac{\text{系统时钟}}{\text{定时周期} \times 12} 初值=65536−定时周期×12系统时钟
      (此处系统时钟为11.0592MHz,定时1ms)。
  2. 中断使能
    • ET1 = 1:使能定时器1中断。
      OD |= 0x01`:设置定时器1为模式1(16位定时)。
    • TH1 = 0xFC; TL1 = 0x00:初值计算公式:
      初值 = 65536 − 系统时钟 定时周期 × 12 \text{初值} = 65536 - \frac{\text{系统时钟}}{\text{定时周期} \times 12} 初值=65536−定时周期×12系统时钟
      (此处系统时钟为11.0592MHz,定时1ms)。
  3. 中断使能
    • ET1 = 1:使能定时器1中断。
    • EA = 1:全局中断使能。
相关推荐
jz_ddk7 分钟前
实战:Windows环境下C语言串口通信与多线程编程
c语言·windows·单片机·测试用例
zhongvv4 小时前
单片机flash存储也做磨损均衡
单片机·数据存储·磨损均衡·单片机开发·flash读写
IDIOT___IDIOT9 小时前
第一次烧录51单片机的烧录不了的问题
单片机·嵌入式硬件·51单片机
小麦嵌入式11 小时前
Linux驱动开发实战(六):设备树升级!插件设备树点灯!
linux·c语言·驱动开发·单片机·嵌入式硬件·mcu·ubuntu
梁山1号11 小时前
【QT】】qcustomplot的初步使用二
c++·单片机·qt
weixin_5088216511 小时前
在 STM32F7 系列微控制器中,使用定时器(如 TIM10)实现 10ms 中断,并在中断服务函数中调用 ProRelay() 函数
stm32·单片机·嵌入式硬件
代码总长两年半13 小时前
STM32---FreeRTOS软件定时器
stm32·单片机·嵌入式硬件
电子艾号哲14 小时前
STC89C52单片机学习——第26节: [11-2]蜂鸣器播放音乐
单片机·嵌入式硬件·学习
KeLin&14 小时前
STM32:Default_Handler问题
stm32·单片机·嵌入式硬件