//Arduino智能交通灯演示程序 #include <SPI.h> // 引入SPI通信库,用于串行外设接口通信 #include <LedControl.h> // 引入MAX7219点阵屏控制库,用于驱动8x8点阵屏 /* ---------------------- 引脚定义 ---------------------- */ const int dataPin = A3; // 数码管数据引脚(连接到移位寄存器的数据输入) const int clockPin = A4; // 数码管时钟引脚(控制数据移位时钟) const int latchPin = A5; // 数码管锁存引脚(锁存显示数据到寄存器) int numToShow = 12; // 倒计时显示数值(初始为红灯倒计时12秒) int redLight = A0; // 红灯引脚(连接红色LED) int yellowLight = A1; // 黄灯引脚(连接黄色LED) int greenLight = A2; // 绿灯引脚(连接绿色LED) int buzzer = 2; // 喇叭引脚(连接蜂鸣器) // MAX7219点阵屏引脚定义(用于显示行人动画和禁止图标) const int maxDinPin = 11; // 点阵屏DIN引脚(串行数据输入) const int maxCsPin = 10; // 点阵屏CS引脚(片选信号) const int maxClkPin = 13; // 点阵屏CLK引脚(串行时钟) // 创建MAX7219控制器对象(参数:DIN, CLK, CS, 设备数量) LedControl lc = LedControl(maxDinPin, maxClkPin, maxCsPin, 1); /* ---------------------- 点阵屏显示数据定义 ---------------------- */ // 行人走路动画帧(2帧循环,每帧8行数据,16进制表示点阵列) const byte MAN_WALKING[2][8] = { {0x00, 0x1C, 0x1C, 0x1C, 0x28, 0x0A, 0x14, 0x22}, // 第1帧:左腿抬起(二进制0b00011100...) {0x00, 0x1C, 0x1C, 0x1C, 0x0A, 0x28, 0x08, 0x1C} // 第2帧:右腿抬起(交换左右腿位置) }; // 禁止通行图标(红色叉号,8行数据构成对角线交叉) const byte NO_ENTRY[8] = {0x00,0x42,0x24,0x18,0x18,0x24,0x42,0x00}; // 数码管0-9显示编码(共阴数码管,LSB对应最低位LED) const byte NUM[] = { // 每个元素对应7段数码管的段码(D0-D6分别对应a-g段) 0xFC, // 0b11111100 → a/b/c/d/e/f段亮,g段灭(显示数字0) 0x60, // 0b01100000 → b/c段亮(显示数字1) 0xDA, // 0b11011010 → a/b/d/e/g段亮(显示数字2) 0xF2, // 0b11110010 → a/b/c/d/g段亮(显示数字3) 0x66, // 0b01100110 → b/c/f/g段亮(显示数字4) 0xB6, // 0b10110110 → a/c/d/f/g段亮(显示数字5) 0xBE, // 0b10111110 → a/c/d/e/f/g段亮(显示数字6) 0xE0, // 0b11100000 → a/b/c段亮(显示数字7) 0xFE, // 0b11111110 → 所有段亮(显示数字8) 0xF6, // 0b11110110 → a/b/c/d/f/g段亮(显示数字9) 0x01 // 0b00000001 → 仅小数点亮(用于熄灭显示) }; /* ---------------------- 全局变量定义 ---------------------- */ // 行人动画控制变量 unsigned long manTime = 0; // 记录上一次动画更新时间 const unsigned long MAN_DELAY = 250; // 动画切换间隔(250毫秒) int currentFrame = 0; // 当前动画帧索引(0或1) // 数码管动态扫描控制变量 unsigned long digitRefreshTime = 0; // 记录上一次数码管刷新时间 const unsigned long DIGIT_REFRESH_INTERVAL = 2; // 刷新间隔(2毫秒,确保视觉无闪烁) void setup() { // 初始化数码管控制引脚为输出模式 pinMode(dataPin, OUTPUT); pinMode(clockPin, OUTPUT); pinMode(latchPin, OUTPUT); // 初始化交通灯引脚和蜂鸣器引脚为输出模式 pinMode(redLight, OUTPUT); pinMode(yellowLight, OUTPUT); pinMode(greenLight, OUTPUT); pinMode(buzzer, OUTPUT); // 初始化为低电平(确保移位寄存器初始状态稳定) digitalWrite(clockPin, LOW); digitalWrite(latchPin, LOW); // 初始化MAX7219点阵屏 lc.shutdown(0, false); // 唤醒第0号设备(禁止掉电模式) lc.setIntensity(0, 8); // 设置亮度等级(0-15,8为中等亮度) lc.clearDisplay(0); // 清除点阵屏显示 // 初始状态:启动蜂鸣器提示音,点亮红灯,显示禁止图标 tone(buzzer, 200, 80); // 发出200Hz声音,持续80毫秒 digitalWrite(redLight, HIGH); // 点亮红灯 showNoEntry(); // 显示禁止通行图标(点阵屏显示红色叉号) digitRefreshTime = millis(); // 初始化数码管刷新计时器 } // 状态控制变量(用于记录当前激活的交通灯状态) unsigned long digitTime = 0; // 倒计时数值更新时间戳 unsigned long lightTime = 0; // 灯光闪烁时间戳 unsigned long buzzerTime = 0; // 蜂鸣器频率切换时间戳 bool On_Off = false; // 灯光闪烁开关 bool buzzer_on_off = false; // 蜂鸣器声音开关 bool red = true; // 红灯激活状态(true表示当前为红灯模式) bool green = false; // 绿灯激活状态 bool yellow = false; // 黄灯激活状态 int currentDigit = 0; // 当前显示的数码管位(0为个位,1为十位) void loop() { // 核心功能:定期刷新数码管显示(动态扫描实现双位显示) if (millis() - digitRefreshTime >= DIGIT_REFRESH_INTERVAL) { digitRefreshTime = millis(); // 更新时间戳 refreshDisplay(); // 执行数码管刷新 } // 根据当前激活状态执行对应逻辑 if (red) { // 红灯状态 displayRedLight(); // 处理红灯倒计时和灯光控制 showNoEntry(); // 点阵屏显示禁止图标 } else if (green) { // 绿灯状态 displayGreenLight(); // 处理绿灯倒计时和行人动画 animateMan(); // 点阵屏显示行人走路动画 } else if (yellow) { // 黄灯状态 displayYellowLight(); // 处理黄灯倒计时和蜂鸣器提示 showNoEntry(); // 点阵屏显示禁止图标 } } /* ---------------------- 状态处理函数 ---------------------- */ // 红灯状态处理函数 void displayRedLight() { // 每秒更新一次倒计时数值 if (millis() - digitTime >= 1000) { digitTime = millis(); // 更新时间戳 numToShow--; // 倒计时减1 // 当剩余时间>6秒时,持续点亮红灯,蜂鸣器低频提示 if (numToShow > 6) { tone(buzzer, 200, 80); // 200Hz声音 digitalWrite(redLight, HIGH); // 红灯常亮 } else { // 剩余时间≤6秒时,蜂鸣器高频提示 tone(buzzer, 500, 80); // 500Hz声音 } } // 每200毫秒切换红灯闪烁状态(仅在剩余时间≤6秒时闪烁) if (millis() - lightTime >= 200) { lightTime = millis(); // 更新时间戳 On_Off = !On_Off; // 切换开关状态 if (numToShow <= 6) { // 剩余时间≤6秒时执行闪烁 digitalWrite(redLight, On_Off ? HIGH : LOW); // 红灯闪烁 } } // 倒计时结束后切换到绿灯状态 if (numToShow < 0) { red = false; // 关闭红灯状态 green = true; // 激活绿灯状态 numToShow = 10; // 绿灯倒计时初始值10秒 tone(buzzer, 160, 80); // 切换状态提示音 digitalWrite(greenLight, HIGH); // 点亮绿灯 digitalWrite(redLight, LOW); // 关闭红灯 manTime = millis(); // 重置行人动画计时器 } } // 绿灯状态处理函数 void displayGreenLight() { // 每秒更新一次倒计时数值 if (millis() - digitTime >= 1000) { digitTime = millis(); // 更新时间戳 numToShow--; // 倒计时减1 } // 根据剩余时间执行不同提示逻辑 if (numToShow > 6) { // 剩余时间>6秒时,绿灯常亮,蜂鸣器短音提示 digitalWrite(greenLight, HIGH); // 绿灯常亮 if (millis() - lightTime >= 50) { // 每50毫秒切换一次提示音 lightTime = millis(); // 更新时间戳 On_Off = !On_Off; // 切换开关状态 if (On_Off) tone(buzzer, 160, 30); // 短促提示音 } } else { // 剩余时间≤6秒时,绿灯开始闪烁,蜂鸣器长音提示 if (millis() - lightTime >= 200) { // 每200毫秒切换闪烁状态 lightTime = millis(); // 更新时间戳 On_Off = !On_Off; // 切换开关状态 digitalWrite(greenLight, On_Off ? HIGH : LOW); // 绿灯闪烁 if (On_Off) tone(buzzer, 160, 100); // 长提示音 } } // 倒计时结束后切换到黄灯状态 if (numToShow < 0) { yellow = true; // 激活黄灯状态 green = false; // 关闭绿灯状态 numToShow = 5; // 黄灯倒计时初始值5秒 tone(buzzer, 350, 80); // 切换状态提示音 digitalWrite(yellowLight, HIGH); // 点亮黄灯 digitalWrite(greenLight, LOW); // 关闭绿灯 lc.clearDisplay(0); // 清除点阵屏 showNoEntry(); // 显示禁止图标 } } // 黄灯状态处理函数 void displayYellowLight() { // 每秒更新一次倒计时数值,黄灯常亮 if (millis() - digitTime >= 1000) { digitTime = millis(); // 更新时间戳 numToShow--; // 倒计时减1 digitalWrite(yellowLight, HIGH); // 黄灯常亮 } // 每500毫秒切换蜂鸣器声音频率 if (millis() - buzzerTime >= 500) { buzzerTime = millis(); // 更新时间戳 buzzer_on_off = !buzzer_on_off; // 切换声音模式 tone(buzzer, buzzer_on_off ? 300 : 100, 100); // 高低频交替 } // 倒计时结束后切换回红灯状态 if (numToShow < 0) { yellow = false; // 关闭黄灯状态 red = true; // 激活红灯状态 numToShow = 12; // 红灯倒计时初始值12秒 tone(buzzer, 200, 80); // 切换状态提示音 digitalWrite(redLight, HIGH); // 点亮红灯 digitalWrite(yellowLight, LOW); // 关闭黄灯 showNoEntry(); // 显示禁止图标 } } /* ---------------------- 显示控制函数 ---------------------- */ // 数码管动态扫描刷新函数(实现双位数码管显示) void refreshDisplay() { // 分解数值为个位和十位(例如12→个位2,十位1) int digits[2] = {numToShow % 10, numToShow / 10}; // [个位, 十位] // 消隐处理:先关闭所有段显示,避免残影 shiftOut(dataPin, clockPin, MSBFIRST, 0x00); // 位选信号全灭(0x00表示不选中任何位) shiftOut(dataPin, clockPin, LSBFIRST, 0x00); // 段选信号全灭(0x00表示所有段熄灭) digitalWrite(latchPin, HIGH); // 锁存消隐信号 digitalWrite(latchPin, LOW); // 显示当前位(0为个位,1为十位) byte val = 0; bitSet(val, currentDigit); // 设置当前位选信号(例如currentDigit=0→0b00000001) // 发送位选信号(MSB优先,最高位对应高位数码管) shiftOut(dataPin, clockPin, MSBFIRST, val); // 选择显示位(个位或十位) // 发送段选信号(LSB优先,最低位对应数码管a段) shiftOut(dataPin, clockPin, LSBFIRST, NUM[digits[currentDigit]]); // 显示对应数字的段码 // 锁存显示数据 digitalWrite(latchPin, HIGH); // 上升沿锁存数据 digitalWrite(latchPin, LOW); // 切换到下一位(个位→十位→个位循环) currentDigit = (currentDigit + 1) % 2; } // 行人走路动画函数(在点阵屏循环显示两帧动画) void animateMan() { if (millis() - manTime >= MAN_DELAY) { // 达到动画切换间隔时更新 manTime = millis(); // 更新时间戳 lc.clearDisplay(0); // 清除点阵屏显示(避免残留上一帧) // 显示当前动画帧(逐行设置点阵数据) for (int row = 0; row < 8; row++) { lc.setRow(0, row, MAN_WALKING[currentFrame][row]); // 设置第0号设备的第row行数据 } // 切换到下一帧(0→1→0循环) currentFrame = (currentFrame + 1) % 2; } } // 显示禁止图标函数(在点阵屏显示红色叉号) void showNoEntry() { // 逐行设置禁止图标数据(8行组成对角线交叉图案) for (int row = 0; row < 8; row++) { lc.setRow(0, row, NO_ENTRY[row]); // 设置第0号设备的第row行数据为禁止图标对应行 } }