arduino按键计时系统
文章目录
实验结果视频展示
共阴极数码管
一,所需材料和电路连接图
1.材料
1个四位共阴数码管
1个10k 欧姆电阻
1个按键
1个面包版
一个arduino 板
条线若干
2.电路接线图

代码展示及其说明
1.代码部分
/*
//数码管a,b,c,d,e,f,g,dp对应引脚
#define SEG_A 7
#define SEG_B 8
#define SEG_C 9
#define SEG_D 10
#define SEG_E 11
#define SEG_F 12
#define SEG_G 13
#define SEG_H A0
#define COM1 2
#define COM2 3
#define COM3 5
#define COM4 6
*/
#define KEY_PIN 4
int ledCount = 8;
int segCount = 4;
// 数码管段码表(0-9)
const unsigned char DuanMa[10] = {
0x3f, // 0: 00111111
0x06, // 1: 00000110
0x5b, // 2: 01011011
0x4f, // 3: 01001111
0x66, // 4: 01100110
0x6d, // 5: 01101101
0x7d, // 6: 01111101
0x07, // 7: 00000111
0x7f, // 8: 01111111
0x6f // 9: 01101111
};
int ledPins[] = {7,8,9,10,11,12,13,A0};
int segPins[] = {2,3,5,6};
unsigned char displayTemp[4];
// 全局变量:计数启停标志、按键状态(消抖)、计数使能
bool countEnable = false;
bool lastKeyState = LOW;
bool currentKeyState = LOW;
void setup()
{
// 初始化数码管段引脚
for(int ledpin = 0;ledpin<ledCount;ledpin++)
{
pinMode(ledPins[ledpin], OUTPUT);
}
// 初始化数码管位引脚
for(int segpin = 0;segpin<segCount;segpin++)
{
pinMode(segPins[segpin], OUTPUT);
digitalWrite(segPins[segpin], HIGH); // 初始关闭所有位选
}
// 初始化按键引脚(假设外部上拉,按下为LOW)
pinMode(KEY_PIN, INPUT_PULLUP);
// 初始化显示缓冲区为0
displayTemp[0] = DuanMa[0];
displayTemp[1] = DuanMa[0];
displayTemp[2] = DuanMa[0];
displayTemp[3] = DuanMa[0];
}
// 数码管段码输出函数(消影处理)
void deal(unsigned char value){
// 先关闭所有段(消影)
for(int i=0; i<8; i++) {
digitalWrite(ledPins[i], LOW);
}
// 输出段码
for(int i=0; i<8; i++) {
digitalWrite(ledPins[i], bitRead(value, i));
}
}
// 按键处理函数:检测按键状态,控制计数启停(消抖处理)
void handleKeyPress()
{
// 读取当前按键状态(使用上拉,按下为LOW)
currentKeyState = digitalRead(KEY_PIN);
// 按键消抖:当前状态与上一次不同,且稳定为低电平(按下)
if(currentKeyState == LOW && lastKeyState == HIGH)
{
delay(20); // 消抖延迟20ms
// 再次确认按键按下
if(digitalRead(KEY_PIN) == LOW)
{
countEnable = !countEnable; // 翻转计数使能状态
// 等待按键释放(避免重复触发)
while(digitalRead(KEY_PIN) == LOW) {
delay(10);
}
}
}
// 更新上一次按键状态
lastKeyState = currentKeyState;
}
void loop()
{
static unsigned int num = 0;
static unsigned long lastUpdateTime = 0;
static unsigned long lastScanTime = 0;
static int currentDigit = 0;
// 1. 处理按键逻辑(每次循环检测)
handleKeyPress();
// 2. 仅当计数使能时,更新数字
if(countEnable && millis() - lastUpdateTime >= 1000)
{
lastUpdateTime = millis();
num = (num + 1) % 10000; // 四位数码管,计数0-9999循环
// 更新显示缓冲区
displayTemp[0] = DuanMa[num / 1000]; // 千位
displayTemp[1] = DuanMa[(num % 1000) / 100]; // 百位
displayTemp[2] = DuanMa[(num % 100) / 10]; // 十位
displayTemp[3] = DuanMa[num % 10]; // 个位
}
// 3. 数码管动态扫描(每3ms扫描一位,保证显示稳定)
if(millis() - lastScanTime >= 3) {
lastScanTime = millis();
// 关闭所有位选(消影)
for(int i = 0; i < segCount; i++) {
digitalWrite(segPins[i], HIGH);
}
// 打开当前位选(根据您的硬件,LOW可能是点亮)
digitalWrite(segPins[currentDigit], LOW);
// 输出当前位的数据
deal(displayTemp[currentDigit]);
// 移动到下一位
currentDigit = (currentDigit + 1) % segCount;
}
}
2.代码解释
1.前期定义:引脚、数组、全局变量
这部分是程序的 “基础配置”,定义了所有需要用到的硬件引脚、数据和状态变量
// 数码管段引脚(a-h对应7、8、9、10、11、12、13、A0)
int ledPins[] = {7,8,9,10,11,12,13,A0};
// 数码管位选引脚(四位数码管的COM1-COM4对应2、3、5、6)
int segPins[] = {2,3,5,6};
// 按键引脚(4号引脚)
#define KEY_PIN 4
// 数量常量:段数8个、位数4个
int ledCount = 8;
int segCount = 4;
ledPins:对应数码管的 8 个段(a,b,c,d,e,f,g,dp),控制 “显示哪个数字”;
segPins:对应四位数码管的 4 个位选端,控制 “哪一位数码管亮”;
KEY_PIN:按键接在 4 号引脚,用来控制计数启停。
2.数码管段码表(核心数据)
// 数码管段码表(0-9),每个值对应8位二进制,控制8个段的亮灭
const unsigned char DuanMa[10] = {
0x3f, // 0: 00111111 → 对应a,b,c,d,e,f亮,g灭 → 显示0
0x06, // 1: 00000110 → 对应b,c亮 → 显示1
0x5b, // 2: 01011011 → 对应a,b,g,e,d亮 → 显示2
// ... 后续3-9同理,都是二进制转十六进制的段码
0x6f // 9: 01101111 → 对应a,b,c,d,f,g亮 → 显示9
};
此段码是数码管显示的核心:每个数字对应一个 8 位的二进制值,某一位为 1 则对应段亮,0 则灭;
比如0x3f转二进制是00111111,对应数码管的 a-f 段亮、g 段灭,刚好显示数字 0。
3.全局变量(状态控制)
unsigned char displayTemp[4]; // 显示缓冲区:存四位数码管要显示的段码(千、百、十、个位)
bool countEnable = false; // 计数使能标志:false=暂停,true=开始计数
bool lastKeyState = LOW; // 上一次按键状态(消抖用)
bool currentKeyState = LOW; // 当前按键状态(消抖用)
displayTemp:相当于 “临时货架”,提前存好四位要显示的数字段码,避免动态计算时卡顿;
countEnable:控制计数的核心开关,按键按下时翻转(true↔false);
lastKeyState/currentKeyState:按键消抖的关键,避免按键机械抖动导致误触发
实验代码中所用函数说明
1.setup () 函数:初始化(只执行一次)
Arduino 的setup()函数在程序启动时执行一次,负责硬件初始化和初始状态设置
void setup()
{
// 1. 初始化数码管段引脚为输出模式(控制段亮灭)
for(int ledpin = 0;ledpin<ledCount;ledpin++)
{
pinMode(ledPins[ledpin], OUTPUT);
}
// 2. 初始化数码管位引脚为输出模式,且初始全部关闭(HIGH=灭)
for(int segpin = 0;segpin<segCount;segpin++)
{
pinMode(segPins[segpin], OUTPUT);
digitalWrite(segPins[segpin], HIGH);
}
// 3. 初始化按键引脚:INPUT_PULLUP=内部上拉模式(按下为LOW,松开为HIGH)
pinMode(KEY_PIN, INPUT_PULLUP);
// 4. 初始化显示缓冲区:四位都显示0
displayTemp[0] = DuanMa[0]; // 千位
displayTemp[1] = DuanMa[0]; // 百位
displayTemp[2] = DuanMa[0]; // 十位
displayTemp[3] = DuanMa[0]; // 个位
}
重点:INPUT_PULLUP是 Arduino 按键常用模式,不用外接上拉电阻,按键按下时引脚为低电平(LOW),松开为高电平(HIGH);
数码管位选初始设为 HIGH,是因为大多数共阴极数码管的位选端低电平有效(LOW = 点亮对应位)。
2.核心函数 1:deal ()(数码管段码输出 + 消影)
void deal(unsigned char value){
// 第一步:先关闭所有段(消影)
for(int i=0; i<8; i++) {
digitalWrite(ledPins[i], LOW);
}
// 第二步:输出当前数字的段码
for(int i=0; i<8; i++) {
digitalWrite(ledPins[i], bitRead(value, i));
}
}
功能:把传入的段码值(比如0x3f)转换成 8 个段引脚的高低电平,控制显示对应数字;
关键:先关闭所有段再输出 → 解决数码管 “串影” 问题(比如切换数字时前一个数字的残影);
bitRead(value, i):读取value的第i位二进制值(0 或 1),比如bitRead(0x3f,0)就是读最低位(1),对应第一个段引脚亮。
3.核心函数 2:handleKeyPress ()(按键处理 + 消抖)
void handleKeyPress()
{
// 1. 读取当前按键状态
currentKeyState = digitalRead(KEY_PIN);
// 2. 按键消抖逻辑:状态变化且当前为按下(LOW)
if(currentKeyState == LOW && lastKeyState == HIGH)
{
delay(20); // 等待20ms,跳过机械抖动期
// 再次确认按键确实按下(避免抖动误判)
if(digitalRead(KEY_PIN) == LOW)
{
countEnable = !countEnable; // 翻转计数状态(暂停→开始,开始→暂停)
// 等待按键释放(避免长按重复触发)
while(digitalRead(KEY_PIN) == LOW) {
delay(10);
}
}
}
// 3. 更新上一次按键状态,为下一次检测做准备
lastKeyState = currentKeyState;
}
消抖原理:按键按下 / 松开时会有 10-20ms 的机械抖动,导致引脚电平反复跳变,delay(20)跳过这段时间,再检测一次,确保是真实的按键操作;
countEnable = !countEnable:取反操作,比如原本是 false(暂停),按下后变成 true(计数),再按又变回 false;
while(…):等待按键释放,避免长按期间反复触发状态翻转。
4.loop () 函数:主循环(反复执行)
void loop()
{
static unsigned int num = 0; // 计数值(static:只初始化一次,循环中保留值)
static unsigned long lastUpdateTime = 0; // 上一次计数更新的时间(毫秒)
static unsigned long lastScanTime = 0; // 上一次数码管扫描的时间(毫秒)
static int currentDigit = 0; // 当前要显示的数码管位数(0-3)
// 1. 处理按键:每次循环都检测按键状态
handleKeyPress();
// 2. 计数逻辑:仅当countEnable=true,且间隔1秒(1000ms)才更新
if(countEnable && millis() - lastUpdateTime >= 1000)
{
lastUpdateTime = millis(); // 更新时间戳
num = (num + 1) % 10000; // 计数+1,超过9999则归零(%10000)
// 拆分计数值到四位显示缓冲区
displayTemp[0] = DuanMa[num / 1000]; // 千位:num÷1000取整(比如1234→1)
displayTemp[1] = DuanMa[(num % 1000) / 100]; // 百位:1234%1000=234 → 234÷100=2
displayTemp[2] = DuanMa[(num % 100) / 10]; // 十位:1234%100=34 → 34÷10=3
displayTemp[3] = DuanMa[num % 10]; // 个位:1234%10=4
}
// 3. 数码管动态扫描:每3ms切换一位,视觉暂留让四位都亮
if(millis() - lastScanTime >= 3) {
lastScanTime = millis(); // 更新扫描时间戳
// 第一步:关闭所有位选(消影)
for(int i = 0; i < segCount; i++) {
digitalWrite(segPins[i], HIGH);
}
// 第二步:点亮当前位
digitalWrite(segPins[currentDigit], LOW);
// 第三步:输出当前位的段码(显示对应数字)
deal(displayTemp[currentDigit]);
// 第四步:切换到下一位(0→1→2→3→0循环)
currentDigit = (currentDigit + 1) % segCount;
}
}
关键细节解释:
static变量:普通局部变量每次循环会重置,static让变量只初始化一次,循环中保留上一次的值(比如num计数不会每次循环都变回 0);
millis():Arduino 内置函数,返回程序运行的毫秒数,用来实现 “非阻塞延时”(比delay()好,不影响按键检测和扫描);
动态扫描原理:四位数码管其实是轮流点亮(每 3ms 亮一位),因为人眼视觉暂留(约 1/24 秒),看起来像是四位同时亮;
计数拆分:num / 1000取千位、num%1000/100取百位,是 C 语言拆分多位数的经典写法。
总结
硬件逻辑:数码管通过 “段选(控制数字)+ 位选(控制哪一位)” 配合显示,动态扫描实现四位同时亮的视觉效果;
稳定处理:按键消抖(delay + 二次检测)、数码管消影(先关再开)是保证程序稳定的核心细节;
非阻塞设计:用millis()实现延时,代替delay(),让按键检测和数码管扫描不被阻塞,响应更及时
更多推荐
所有评论(0)