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

发布于:2025-03-19 ⋅ 阅读:(14) ⋅ 点赞:(0)

【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 完整代码

#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 按键扫描与速度调节
// 按键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状态迁移
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:全局中断使能。

网站公告

今日签到

点亮在社区的每一天
去签到