下面是使用自定义shiftOut函数实现数码管数字显示和按键控制的Arduino代码。这个实现替代了标准库的shiftOut,并通过位操作直接控制移位寄存器。

/*
按键处理:使用digitalRead函数读取按键状态,按键未按下时为高电平,按下时为低电平。
通过延时消除按键抖动,每次按键松开后将显示数字加1,超过10时回到0。
*/

const byte disNum[11] = {
  // 数码管各段状态定义(共阴极:1=亮,0=灭)
  //  abcdefgdp  
  0b11111100, //0
  0b01100000, //1
  0b11011010, //2
  0b11110010, //3
  0b01100110, //4
  0b10110110, //5
  0b10111110, //6
  0b11100000, //7
  0b11111110, //8
  0b11110110, //9
  0b00000001  //.
};

const int keyPin = 3;      // 按键引脚
const int dataPin = 11;    // 数据引脚 (DS)
const int clockPin = 10;   // 时钟引脚 (SH_CP)
const int latchPin = 9;    // 锁存引脚 (ST_CP)

void setup() {
  pinMode(dataPin, OUTPUT);
  pinMode(clockPin, OUTPUT);
  pinMode(latchPin, OUTPUT);
  pinMode(keyPin, INPUT);
  digitalWrite(clockPin, LOW);
  digitalWrite(latchPin, LOW);
  displayNumber(0); // 初始显示0
}

int currentNumber = 0;

void loop() {
  if (digitalRead(keyPin) == LOW) { // 检测按键按下
    delay(20); // 消抖
    while (digitalRead(keyPin) == LOW); // 等待按键释放
    delay(20); // 消抖
    
    currentNumber = (currentNumber + 1) % 11; // 循环0-10
    displayNumber(currentNumber);
  }
}

// 自定义shiftOut函数 - 替代标准库函数
void myShiftOut(byte dataPin, byte clockPin, byte bitOrder, byte val) {
  for (int i = 0; i < 8; i++) {
    if (bitOrder == LSBFIRST) {
      digitalWrite(dataPin, !bitRead(val, i)); // 取反,适配数码管定义
    } else {
      digitalWrite(dataPin, !bitRead(val, 7 - i)); // 取反,适配数码管定义
    }
    digitalWrite(clockPin, LOW);
    digitalWrite(clockPin, HIGH);
    
  }
}

// 显示数字函数
void displayNumber(int number) {
 
  myShiftOut(dataPin, clockPin, LSBFIRST, disNum[number]); // 发送数据
   digitalWrite(latchPin, LOW);         // 开始传输
  digitalWrite(latchPin, HIGH);        // 锁存数据
}

代码说明:

  1. 自定义shiftOut函数

    • myShiftOut函数模拟了标准库的shiftOut功能
    • 通过位操作逐位发送数据到移位寄存器
    • 支持低位在前(LSBFIRST)的数据传输方式
    • 数据位取反以适配数码管段定义(0=亮,1=灭)
  2. 数码管控制

    • 使用74HC595移位寄存器控制数码管
    • 通过displayNumber函数将数字转换为段码并显示
    • 段码定义包含0-9和小数点的显示模式
  3. 按键处理

    • 按键连接到引脚3,使用内部上拉电阻
    • 检测到按键按下后进行消抖处理
    • 按键释放后更新显示数字

这个实现完全使用自定义的移位输出函数,通过位操作直接控制数码管显示,相比标准库函数提供了更灵活的控制方式。

3 条评论

  • @ 2025-7-17 16:29:42

    • @ 2025-7-17 16:11:21
      /*
      按键处理:使用digitalRead函数读取按键状态,按键未按下时为高电平,按下时为低电平。
      通过延时消除按键抖动,每次按键松开后将显示数字加1,超过10时回到0。
      */
      
      const byte disNum[11] = {
          // 数码管各段状态定义(共阴极:1=亮,0=灭)
          //  abcdefgdp  
          0b11111100, //0
          0b01100000, //1
          0b11011010, //2
          0b11110010, //3
          0b01100110, //4
          0b10110110, //5
          0b10111110, //6
          0b11100000, //7
          0b11111110, //8
          0b11110110, //9
          0b00000001  //.
      };
      
      const int keyPin = 3;      // 按键引脚
      const int dataPin = 9;    // 数据引脚 (DS)
      const int clockPin = 10;   // 时钟引脚 (SH_CP)
      const int latchPin = 11;    // 锁存引脚 (ST_CP)
      
      void setup() {
          pinMode(dataPin, OUTPUT);
          pinMode(clockPin, OUTPUT);
          pinMode(latchPin, OUTPUT);
          pinMode(keyPin, INPUT);
      
      	digitalWrite(clockPin, LOW);
          digitalWrite(latchPin, LOW);
          displayNumber(0); // 初始显示0
      }
      
      int currentNumber = 0;
      
      void loop() {
          if (digitalRead(keyPin) == LOW) { // 检测按键按下
              delay(20); // 消抖
              while (digitalRead(keyPin) == LOW); // 等待按键释放
              delay(20); // 消抖
      
              currentNumber = (currentNumber + 1) % 11; // 循环0-10
              displayNumber(currentNumber);
          }
      }
      
      // 自定义shiftOut函数 - 替代标准库函数  //myShiftOut每次发送8位数据
      void myShiftOut(byte dataPin, byte clockPin, byte bitOrder, byte val) {//byte类型就是无符号的8位整数
          for (int i = 0; i < 8; i++) {
      		if (bitOrder == LSBFIRST) {// 如果是低位优先 数据val的顺序和数码管的段的顺序一致
                  //LSBFIRST相当于0
                  digitalWrite(dataPin, !bitRead(val, i)); // 取反,适配数码管定义
              }
      		else {// 如果是高位优先 数据val的顺序和数码管的段的顺序相反 MSBFIRST相当于1
                  digitalWrite(dataPin, !bitRead(val, 7 - i)); // 取反,适配数码管定义
              }
              digitalWrite(clockPin, LOW);
      		digitalWrite(clockPin, HIGH);//发送上升沿脉冲信号,数据移位
          }
      }
      
      // 显示数字函数
      void displayNumber(int number) {
          //myShiftOut(dataPin, clockPin, LSBFIRST, disNum[number]); // 发送数据
      	shiftOut(dataPin, clockPin, LSBFIRST, ~disNum[number]);// 发送数据,取反适配数码管定义
          digitalWrite(latchPin, LOW);         // 开始传输
          digitalWrite(latchPin, HIGH);        // 锁存数据
      }
      

      程序功能解释

      这是一个基于Arduino的数码管显示控制系统,通过按键控制数字显示。主要功能包括:

      • 实时检测按键状态,通过消抖处理确保信号稳定
      • 每次按下按键后,数码管显示的数字会递增(0-10循环)
      • 使用自定义的移位输出函数控制数码管显示

      硬件连接说明

      • 按键连接到引脚3,采用外部上拉(需外部上拉电阻)
      • 数码管通过74HC595移位寄存器控制:
        • 数据引脚(DS)连接到Arduino引脚9
        • 时钟引脚(SH_CP)连接到Arduino引脚10
        • 锁存引脚(ST_CP)连接到Arduino引脚11

      关键代码解析

      1. 数码管段码定义

      const byte disNum[11] = {
          0b11111100, //0
          0b01100000, //1
          ...
          0b00000001  //.
      };
      
      • 定义了0-9和小数点的段码(共阴极数码管)
      • 每个字节的8位分别对应数码管的a-g和dp段(低位在前)

      2. 自定义移位输出函数

      void myShiftOut(byte dataPin, byte clockPin, byte bitOrder, byte val) {
          for (int i = 0; i < 8; i++) {
              if (bitOrder == LSBFIRST) {
                  digitalWrite(dataPin, !bitRead(val, i));
              } else {
                  digitalWrite(dataPin, !bitRead(val, 7 - i));
              }
              digitalWrite(clockPin, LOW);
              digitalWrite(clockPin, HIGH);
          }
      }
      
      • 模拟标准库的shiftOut函数,将8位数据逐位移出
      • 通过bitRead函数读取数据位,并取反后输出(适配数码管定义)
      • 时钟信号采用上升沿触发(LOW->HIGH)

      3. 显示控制函数

      void displayNumber(int number) {
          shiftOut(dataPin, clockPin, LSBFIRST, ~disNum[number]);
          digitalWrite(latchPin, LOW);
          digitalWrite(latchPin, HIGH);
      }
      
      • 实际使用了标准库的shiftOut函数(与自定义函数功能类似)
      • 通过取反操作(~disNum[number])适配数码管段码定义
      • 锁存信号控制数据何时更新到输出端

      4. 按键处理逻辑

      if (digitalRead(keyPin) == LOW) {
          delay(20); // 消抖
          while (digitalRead(keyPin) == LOW); // 等待释放
          delay(20); // 消抖
          currentNumber = (currentNumber + 1) % 11;
          displayNumber(currentNumber);
      }
      
      • 检测到按键按下(低电平)后进行消抖处理
      • 按键释放后更新显示数字(0-10循环)

      代码中的注意事项

      1. 引脚配置与取反操作

        • 数码管段码定义中使用的是共阴极逻辑(1=亮,0=灭)
        • 但在displayNumber函数中使用了~disNum[number]取反操作
        • 这意味着代码中可能存在逻辑冲突,需要根据实际硬件连接调整
      2. 自定义函数与标准函数混用

        • 代码中定义了myShiftOut函数,但在displayNumber中使用了标准库的shiftOut
        • 建议保持一致,避免混淆
      3. 按键配置

        • 代码中使用INPUT模式但未启用内部上拉
        • 需要确保外部有上拉电阻,否则可能导致按键检测不稳定

      这个程序通过移位寄存器实现了数码管的控制,并通过按键实现了数字的循环切换,是一个基础的Arduino输入输出控制示例。

      • @ 2025-7-17 15:50:30

        四、按键控制数码管

        1. 电路连接

        在之前的电路基础上,增加一个按键:

        • 按键一端连接ESP32的3号引脚(keyPin)
        • 按键另一端接地
        • 可以在按键两端并联一个10kΩ的上拉电阻,或者使用ESP32内部的上拉电阻(在代码中设置)

        2. 代码实现

        const int keyPin = 3; // 按键引脚
        const int DS = 9; // 数码管DS引脚
        const int SH_CP = 10; // 数码管SH_CP引脚
        const int ST_CP = 11; // 数码管ST_CP引脚  
        
        // 定义二维数组:存储10个数字+1个小数点的各段状态(共阴极:1=亮,0=灭)
        const byte disNum[11][8] = {
        {1,1,1,1,1,1,0,0}, //0
        {0,1,1,0,0,0,0,0}, //1
        {1,1,0,1,1,0,1,0}, //2
        {1,1,1,1,0,0,1,0}, //3
        {0,1,1,0,0,1,1,0}, //4
        {1,0,1,1,0,1,1,0}, //5
        {1,0,1,1,1,1,1,0}, //6
        {1,1,1,0,0,0,0,0}, //7
        {1,1,1,1,1,1,1,0}, //8
        {1,1,1,1,0,1,1,0}, //9
        {0,0,0,0,0,0,0,1}  //.
        };  
        
        int currentNum = 0; // 当前显示的数字
        
        void setup() {
          pinMode(keyPin, INPUT); // 设置按键引脚为输入模式,并启用上拉电阻
          Serial.begin(9600); // 初始化串口通信,波特率为9600
          pinMode(DS, OUTPUT); // 数码管的DS引脚
          pinMode(SH_CP, OUTPUT); // 数码管的SH_CP引脚
          pinMode(ST_CP, OUTPUT); // 数码管的ST_CP引脚
          // 为了避免上电干扰
          digitalWrite(SH_CP, LOW); 
          digitalWrite(ST_CP, LOW); 
          displayNumber(currentNum); // 初始显示0
        }  
        
        void displayNumber(int num) { // 数码管显示数字函数
          for (int i = 7; i >= 0; i--) {
            digitalWrite(DS, !disNum[num][i]); // 共阳数码管需要取反
            digitalWrite(SH_CP, LOW); // 拉低移位时钟
            digitalWrite(SH_CP, HIGH); // 拉高移位时钟,产生上升沿,数据移位
          }
          digitalWrite(ST_CP, LOW); // 拉低存储时钟
          digitalWrite(ST_CP, HIGH); // 拉高存储时钟,产生上升沿,数据从移位寄存器传输到存储寄存器,数码管显示
        }  
        
        int buttonValue;
        void loop() {
          buttonValue = digitalRead(keyPin);
          if (buttonValue == 0) { // 按键按下(由于启用了上拉电阻,按下时为低电平)
            Serial.println("按键按下");
            delay(20); // 跳过按键前沿抖动区域
            // 按键一直按着就会在这里一直执行死循环,等待按键松开
            while (digitalRead(keyPin) == 0);
            delay(20); // 跳过后沿抖动
            Serial.println("按键松开");
            
            // 每次按键后显示下一个数字,循环显示0-10
            currentNum++;
            if (currentNum > 10) {
              currentNum = 0;
            }
            displayNumber(currentNum); // 显示当前数字
          }
          delay(20);
        }
        

        3. 代码解析

        • 按键处理:使用digitalRead函数读取按键状态,由于启用了上拉电阻,按键未按下时为高电平,按下时为低电平。通过延时来消除按键的抖动。
        • 数字切换:每次按键松开后,将当前显示的数字加1,当超过10时回到0,然后调用displayNumber函数显示新的数字。
        • 这种按键控制方式可以实现按一次按键切换一个数字的功能,你也可以根据需要修改代码,实现其他功能,比如长按切换模式等。
        • 1