7 段阵列时钟
组件和用品
| × | 1 | ||||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 | ||||
| × | 4 | ||||
| × | 1 | ||||
| × | 36 | ||||
| × | 18 | ||||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 |
必要的工具和机器
| ||||
|
应用和在线服务
|
关于这个项目
在浏览 Hackaday.io 时,我遇到了一个漂亮的时钟,它由 Frugha 的一系列 7 段显示器制成。我真的很想构建这个,但 Frugha 的版本非常大,因为它使用 0.56" 7 段显示器。我的设计使用 0.28" 7 段显示器,使它只有四分之一的尺寸。
软件经过重新编写,包含闹钟和用于设置时间、日期和闹钟的配置屏幕。
视频
配置画面
时钟由四个按钮控制。 SELECT 按钮(最顶部)将显示配置屏幕。当前选择将闪烁。再次按下 SELECT 按钮将在不同的配置部分(即时间、日期、闹钟、调谐、亮度、日期格式和时钟)之间循环。
在选择菜单项之一时按 ENTER 按钮(从顶部向下第二个)将允许您更改其设置。如果菜单项有多个设置,则 ENTER 按钮将在它们之间循环。活动设置将闪烁。
当设置闪烁时,UP 和 DOWN 按钮(底部两个按钮)将更改设置。
日期画面
在显示时钟时按 ENTER 按钮(从上数第二个)将显示日期屏幕 5 秒钟。
配置屏幕上的日期格式部分允许您设置日期格式(DD-MM 或 MM-DD)和使用的字体。
设计注意事项
为了简化电路板设计,我的版本使用 4 位显示而不是原始版本中使用的个位显示。由于 0.28" 显示器很小,我不得不将 MAX7219 芯片放在单独的板上。为了简单接线,使用机械加工的公母排针连接板,
如果您希望将电路板商业制造或像我一样自己制作电路板,则已包含 Eagle 文件。我用碳粉方法制作我的。
柱板
需要制作六个容纳所有显示器的柱板。焊接公机头时,从中间组开始,向外边缘工作。使用火柴棒或类似的东西来充分抬起插座,以便您可以轻松焊接引脚。使用尖端细的小烙铁和 0.5mm 焊锡。
创建列模块后,我建议您使用哑光黑色涂料绘制显示边框。如果显示器与其邻居不完全对齐,这将停止显示白色边缘。同样使用记号笔,为每个列模块编号。这有助于将它们插入 MAX7219 主板。
MAX7219主板
在设计主板和列板时,我没有将正确的 MAX7219 段或数字映射到相应的显示引脚。我想让 PCB 布局尽可能简单,并根据软件的任何差异进行调整。
我建议当涉及到母机加工插座时,您首先将它们放在柱板的公针上,然后在连接时将组件焊接到位。这意味着柱销将与其对应的插座完全对齐。如果您需要移除它们,对色谱柱模块进行编号将确保它们回到相同的位置。
MPU板
微处理器板包含所有其他组件。四个按钮和两个 SMD 电容器安装在板的背面,所有其他组件都安装在板的正面。为了保持低调,通常焊接到 Arduino Pro Mini 的 FTDI 排针现在直接焊接到 PCB 上。两条线从 DTR 和 VCC 引脚直接向下连接到 PCB。
制作案例
包括正面和背面的 STL 文件。两者都使用 0.2 层高和边缘打印。正面还启用了仅接触构建板的支撑。
MAX7219 主板及其列模块从正面滑入。如果你觉得它有点松,用一两层美纹纸胶带包装。
MPU板热粘在背面。
软件
该软件要求 MD_MAX72XX 库已安装在您的 Arduino IDE 中。我已经提供了我的 Hardware_Test_V1 草图进行测试。它将点亮每个段,以便您检查短路或断线。一旦显示工作,上传Clock_V4草图。
代码
- Clock_V4.ino
- 数字.h
- Tunes.h
- 按钮.h
- Button.cpp
- Hardware_Test_V1.zip
Clock_V4.inoC/C++
/*----------------------------------------- ------ * 7 段阵列时钟 * * 基于 Frugha 的时钟 (https://hackaday.io/project/169632-7-segment-display-array-clock) * * John Bradnam (jbrad2089@) 的修改gmail.com) * - 更改硬件以使用 0.28" 7 段显示器。* - MAX7219 设备、数字和段顺序已更改为简单的 * 复杂的 PCB 板布线。* - 硬件使用 Arduino Mini Pro、DS1302 RTC 和 4按钮 * - 添加了带有可选闹钟功能的闹钟功能 * - 添加了亮度设置 * - 添加了用于设置时间、日期、闹钟、音乐和亮度的设置屏幕 * - 添加了日期屏幕(按下 ENTER 按钮时显示两秒钟 * 更新 2020 -06-16 * - 在配置屏幕上添加了日期格式选项 * - 将日期屏幕超时从 2 秒增加到 5 秒 * - 添加了用于日期显示的可配置字体选择 *-------------- ------------------------------------*/ #include#include #include # include #include #include "button.h"#include "Tunes.h"#include "Digits.h"#define HARDWARE_TYPE MD_MAX72XX::GENERIC_HW#define MAX_DEVICES 18#define LED_CLK 13 / / 或 SCK (WHI)#define LED_DATA 11 // 或 MOSI (BRN)#define LED_CS 10 // 或 SS (YEL)#define SPEAKER 2#define SW_UP 3#define SW_DOWN 4#define RTC_CLK 5#define RTC_IO 6#define SW_ENTER 7#define RTC_CE 8#define SW_SELECT 9DS1302RTC rtc(RTC_CE, RTC_IO, RTC_CLK);// SPI 硬件接口MD_MAX72XX mx =MD_MAX72XX(HARDWARE_TYPE, LED_CS, MAX_DEVICES);// 任意管脚,LEDHXX_MAX_TYPE =ARDXXM_MAX XX 72,MD_MD_MAX_XX LED_CLK, LED_CS, MAX_DEVICES);//EEPROM处理#define EEPROM_ADDRESS 0#define EEPROM_MAGIC 0x0BAD0DADtypedef struct { uint32_t magic;布尔警报; uint8_t 分钟; uint8_t 小时;布尔格式12小时; uint8_t 亮度; uint8_t 调;布尔格式Dmy; bool squareFont;} EEPROM_DATA;EEPROM_DATA eepromData; //当前的EEPROM设置void clockButtonPressed(void);void enterButtonPressed(void);void downButtonPressed(void);void upButtonPressed(void);Button* clockButton;Button* enterButton;Button* downButton;Button* upButton;//操作模式bool inSubMenu =false;#define SETUP_FLASH_RATE 200;unsigned long setupTimeout;bool setupDisplayState =false;enum ClockButtonModesEnum { CLOCK, TIME_SET, DATE_SET, ALARM_SET, TUNE_SET, BRIGHT_SET, FORMAT_SET };ClockButtonModesEnum clockMode =CLOCK, {TIMETIME_FORMEnumENUMEnumEnumEnumEnumTimeMenuEnum timeSetMode =TIME_HOUR;enum DateSetMenuEnum { DATE_YEAR, DATE_MONTH, DATE_DAY };DateSetMenuEnum dateSetMode =DATE_YEAR;enum AlarmSetMenuEnum { ALARM_HOUR, ALARM_MIN, ALARM_STATE };AlarmSetMenuEnum alarmSetMode =ALARM_DAY,{FONT_HOUR_HOUR_DATA_NAME;FALARM_HOUR -1;bool alarmRinging =false; //true 当警报是 onbool alarmCancelled =false; // 闹钟被 userbool musicPlaying =false 取消; //true 如果播放 Songbool clockColon =false; //显示/隐藏colonint8_t dom[] ={31,28,31,30,31,30,31,31,30,31,30,31};tmElements_t newTime; //用于存储新的timevoid setup(){ Serial.begin(115200); //Eprom readEepromData(); //初始化按钮clockButton =new Button(SW_SELECT);输入按钮 =新按钮(SW_ENTER); downButton =新按钮(SW_DOWN); downButton->Repeat(downButtonPressed); upButton =新按钮(SW_UP);向上按钮->重复(向上按钮按下); //初始化声音 pinMode(SPEAKER,OUTPUT); mx.begin(); mx.control(MD_MAX72XX::INTENSITY, EepromData.brightness); //0..MAX_INTENSITY mx.update(MD_MAX72XX::OFF); //没有自动更新 setSyncProvider(rtc.get); // 从 RTC 获取时间的函数 if(timeStatus() !=timeSet) { Serial.println("RTC Sync Bad"); newTime.Year =CalendarYrToTm(2020); newTime.Month =5; newTime.Day =30; newTime.Hour =14;新时间.分钟 =53; newTime.Second =0; time_t t =makeTime(newTime);设置时间(t); rtc.set(t); if (rtc.set(t) !=0) { Serial.println("设置时间失败"); } } newTime.Year =CalendarYrToTm(year()); newTime.Month =月(); newTime.Day =day(); newTime.Hour =小时(); newTime.Minute =分钟(); newTime.Second =second(); Serial.println("时间:" + String(newTime.Hour) + ":" + String(newTime.Minute) + ":" + String(newTime.Second)); Serial.println("日期:" + String(newTime.Day) + "/" + String(newTime.Month) + "/" + String(tmYearToCalendar(newTime.Year)));时钟模式 =时钟; showTime(true);}void loop(){ testButtons(); if (clockMode ==CLOCK) { showTime(false); if (EepromData.alarm &&EepromData.hours ==hours() &&EepromData.minutes ==minute()) { if (!alarmCancelled) { alarmRinging =true; playSong(旋律[EepromData.tune]); } } else { alarmCancelled =false;警报响铃 =假; } } else { showSetup(false); } delay(100);}//---------------------------------------- ----------------------//测试是否有任何按钮被按下void testButtons(){ //单次按下按钮 if (clockButton->Pressed()) {时钟按钮按下(); } if (enterButton->Pressed()) { enterButtonPressed(); } //不需要检查按下的结果,因为按钮处理程序会调用它的repeat函数upButton->Pressed(); downButton->Pressed();}//---------------------------------------- -----------------------//处理时钟 bttonvoid clockButtonPressed(){ if (cancelAlarm()) return; inSubMenu =false; clockMode =(clockMode ==FORMAT_SET) ?时钟:(ClockButtonModesEnum)((int)clockMode + 1); if (clockMode ==CLOCK) { Serial.println("保存时间:" + String(newTime.Hour) + ":" + String(newTime.Minute)); Serial.println(" From:" + String(hour()) + ":" + String(minute())); if (newTime.Year !=CalendarYrToTm(year()) || newTime.Month !=month() || newTime.Day !=day() || newTime.Hour !=hour() || newTime.Minute !=minute()) { //更新时间Serial.println("更新RTC"); newTime.Second =second(); time_t t =makeTime(newTime);设置时间(t); rtc.set(t); if (rtc.set(t) !=0) { Serial.println("设置时间失败"); } } writeEepromData();显示时间(真); } else { if (clockMode ==TIME_SET) { newTime.Year =CalendarYrToTm(year()); newTime.Month =月(); newTime.Day =day(); newTime.Hour =小时(); newTime.Minute =分钟(); newTime.Second =second(); Serial.println("加载时间:" + String(hour()) + ":" + String(minute()));显示设置(真); }}//---------------------------------------------- -----------------//处理回车 bttonvoid enterButtonPressed(){ if (cancelAlarm()) return; if (clockMode !=CLOCK) { if (!inSubMenu) { timeSetMode =TIME_HOUR; dateSetMode =DATE_YEAR;警报设置模式 =ALARM_HOUR; formatSetMode =DAY_MONTH; inSubMenu =true; } else { switch (clockMode) { case TIME_SET:timeSetMode =(timeSetMode ==TIME_FORMAT) ? TIME_HOUR :(TimeSetMenuEnum)((int)timeSetMode + 1);休息;案例 DATE_SET:dateSetMode =(dateSetMode ==DATE_DAY) ? DATE_YEAR :(DateSetMenuEnum)((int)dateSetMode + 1);休息; case ALARM_SET:alarmSetMode =(alarmSetMode ==ALARM_STATE) ? ALARM_HOUR :(AlarmSetMenuEnum)((int)alarmSetMode + 1);休息; case FORMAT_SET:formatSetMode =(formatSetMode ==FONT_STYLE) ? DAY_MONTH :(FormatSetMenuEnum)((int)formatSetMode + 1);休息;显示设置(真); } else { showDate(day(), month(), year());延迟(5000); }}//---------------------------------------------- -----------------//处理DOWN bttonvoid downButtonPressed(){ if (cancelAlarm()) return; switch (clockMode) { case TIME_SET:if (inSubMenu) { switch(timeSetMode) { case TIME_HOUR:newTime.Hour =(newTime.Hour + 24 - 1) % 24;休息; case TIME_MIN:newTime.Minute =(newTime.Minute + 60 - 1) % 60;休息; case TIME_FORMAT:EepromData.format12hr =!EepromData.format12hr;休息;显示设置(真); } 休息; case DATE_SET:if (inSubMenu) { switch(dateSetMode) { case DATE_YEAR:newTime.Year =((newTime.Year - 30 + 100) - 1) % 100 + 30;休息; case DATE_MONTH:newTime.Month =((newTime.Month - 1 + 12) - 1) % 12 + 1;休息; case DATE_DAY:uint8_t md =daysInMonth(newTime.Year, newTime.Month); newTime.Day =((newTime.Day - 1 + md) - 1) % md + 1;休息;显示设置(真); } 休息; case ALARM_SET:if (inSubMenu) { switch(alarmSetMode) { case ALARM_HOUR:EepromData.hours =(EepromData.hours + 24 - 1) % 24;休息; case ALARM_MIN:EepromData.minutes =(EepromData.minutes + 60 - 1) % 60;休息; case ALARM_STATE:EepromData.alarm =!EepromData.alarm;休息;显示设置(真); } 休息; case TUNE_SET:EepromData.tune =(EepromData.tune + NUM_OF_MELODIES - 1) % NUM_OF_MELODIES;显示设置(真);休息; case BRIGHT_SET:EepromData.brightness =(EepromData.brightness + MAX_INTENSITY - 1) % MAX_INTENSITY; mx.control(MD_MAX72XX::INTENSITY, EepromData.brightness); //0..MAX_INTENSITY showSetup(true);休息; case FORMAT_SET:if (inSubMenu) { switch (formatSetMode) { case DAY_MONTH:EepromData.formatDmy =!EepromData.formatDmy;休息; case FONT_STYLE:EepromData.squareFont =!EepromData.squareFont;休息;显示设置(真);休息; }}//---------------------------------------------- -----------------//处理 UP bttonvoid upButtonPressed(){ if (cancelAlarm()) return; switch (clockMode) { case TIME_SET:if (inSubMenu) { switch(timeSetMode) { case TIME_HOUR:newTime.Hour =(newTime.Hour + 1) % 24;休息; case TIME_MIN:newTime.Minute =(newTime.Minute + 1) % 60;休息; case TIME_FORMAT:EepromData.format12hr =!EepromData.format12hr;休息;显示设置(真); } 休息; case DATE_SET:if (inSubMenu) { switch(dateSetMode) { case DATE_YEAR:newTime.Year =((newTime.Year - 30) + 1) % 100 + 30;休息; case DATE_MONTH:newTime.Month =((newTime.Month - 1) + 1) % 12 + 1;休息; case DATE_DAY:uint8_t md =daysInMonth(newTime.Year, newTime.Month); newTime.Day =(newTime.Day % md) + 1;休息;显示设置(真); } 休息; case ALARM_SET:if (inSubMenu) { switch(alarmSetMode) { case ALARM_HOUR:EepromData.hours =(EepromData.hours + 1) % 24;休息; case ALARM_MIN:EepromData.minutes =(EepromData.minutes + 1) % 60;休息; case ALARM_STATE:EepromData.alarm =!EepromData.alarm;休息;显示设置(真); } 休息; case TUNE_SET:EepromData.tune =(EepromData.tune + 1) % NUM_OF_MELODIES;显示设置(真);休息; case BRIGHT_SET:EepromData.brightness =(EepromData.brightness + 1) % MAX_INTENSITY; mx.control(MD_MAX72XX::INTENSITY, EepromData.brightness); //0..MAX_INTENSITY showSetup(true);休息; case FORMAT_SET:if (inSubMenu) { switch (formatSetMode) { case DAY_MONTH:EepromData.formatDmy =!EepromData.formatDmy;休息; case FONT_STYLE:EepromData.squareFont =!EepromData.squareFont;休息;显示设置(真);休息; }}//---------------------------------------------- -----------------//如果正在播放tunebool则关闭闹钟 cancelAlarm(){ if (musicPlaying) { musicPlaying =false;警报取消 =警报响铃;返回真; } 屈服(); return false;}//-------------------------------------------- -------------------//显示Setup菜单和flash current item selectedvoid showSetup(bool force){ setupDisplayState =setupDisplayState |力量;力 =力 || (millis()> setupTimeout); if (force) { setupTimeout =millis() + SETUP_FLASH_RATE; bool on =setupDisplayState; setupDisplayState =!setupDisplayState; mx.clear(); if (on || !(clockMode ==TIME_SET &&!inSubMenu)) displayString(0,7,"TINE"); if (on || !(clockMode ==TIME_SET &&inSubMenu &&timeSetMode ==TIME_HOUR)) displayNumber(0,13,newTime.Hour,2,true); if (on || !(clockMode ==TIME_SET &&inSubMenu &&timeSetMode ==TIME_MIN)) displayNumber(0,16,newTime.Minute,2,true); if (on || !(clockMode ==TIME_SET &&inSubMenu &&timeSetMode ==TIME_FORMAT)) displayString(0,19,(EepromData.format12hr) ? "12HR" :"24HR"); if (on || !(clockMode ==DATE_SET &&!inSubMenu)) displayString(1,7,"DATE"); if (on || !(clockMode ==DATE_SET &&inSubMenu &&dateSetMode ==DATE_YEAR)) displayNumber(1,13,tmYearToCalendar(newTime.Year),4,true); if (on || !(clockMode ==DATE_SET &&inSubMenu &&dateSetMode ==DATE_MONTH)) displayNumber(1,18,newTime.Month,2,true); if (on || !(clockMode ==DATE_SET &&inSubMenu &&dateSetMode ==DATE_DAY)) displayNumber(1,21,newTime.Day,2,true); if (on || !(clockMode ==ALARM_SET &&!inSubMenu)) displayString(2,6,"ALARN"); if (on || !(clockMode ==ALARM_SET &&inSubMenu &&alarmSetMode ==ALARM_HOUR)) displayNumber(2,13,EepromData.hours,2,true); if (on || !(clockMode ==ALARM_SET &&inSubMenu &&alarmSetMode ==ALARM_MIN)) displayNumber(2,16,EepromData.minutes,2,true); if (on || !(clockMode ==ALARM_SET &&inSubMenu &&alarmSetMode ==ALARM_STATE)) displayString(2,19,(EepromData.alarm) ? "ON" :"OFF"); if (on || !(clockMode ==TUNE_SET &&!inSubMenu)) displayString(3,7,"TUNE"); if (on || !(clockMode ==TUNE_SET &&inSubMenu)) { switch(EepromData.tune) { case 0:displayString(3,13,"ELISE");休息;情况 1:displayString(3,13,"RANGE");休息;情况 2:displayString(3,13,"SUNSHINE");休息; } } if (on || !(clockMode ==BRIGHT_SET &&!inSubMenu)) displayString(4,1,"BRIGHTNESS"); if (on || !(clockMode ==BRIGHT_SET &&inSubMenu)) displayNumber(4,13,EepromData.brightness,0,false); if (on || !(clockMode ==FORMAT_SET &&!inSubMenu)) displayString(5,0,"DATE FORNAT"); if (on || !(clockMode ==FORMAT_SET &&inSubMenu &&formatSetMode ==DAY_MONTH)) displayString(5,13,(EepromData.formatDmy) ? "DD-NN" :"NN-DD"); if (on || !(clockMode ==FORMAT_SET &&inSubMenu &&formatSetMode ==FONT_STYLE)) displayString(5,19,(EepromData.squareFont) ? "FONT1" :"FONT2"); mx.update(); }}//---------------------------------------------- -----------------//如果更改则在显示器上显示当前时间// force - 即使未更改也始终显示时间void showTime(bool force){ force =force || (lastSeconds !=second()); if (force) { lastSeconds =second(); showTime(hour(), minute(), true, true, (second() &0x01), (clockMode !=CLOCK || !EepromData.format12hr)); }}//---------------------------------------------- -----------------//在显示屏上显示时间// h - 小时// m - 分钟// he - 小时启用// me - 分钟启用// ce - 冒号启用 void showTime(int h, int m, bool he, bool me, bool ce, bool f24){ mx.clear(); if (he) { if (!f24 &&h> 12) { h =h - 12; } if (h> 9 || f24) { displayLargeDigit(0, h / 10); } displayLargeDigit(5, h % 10); } if (me) { displayLargeDigit(13, m / 10); displayLargeDigit(18, m % 10); } if (ce) { displayLargeDigit(11, 10); } mx.update();}//---------------------------------------- -----------------------void showDate(int d, int m, int y){ #define XOFS 3 mx.clear(); if (EepromData.formatDmy) { displayDateDigit(XOFS+0, d / 10); displayDateDigit(XOFS+4, d % 10); displayDateDigit(XOFS+8, 10); //冒号 displayDateDigit(XOFS+12, m / 10); displayDateDigit(XOFS+16, m % 10); } else { displayDateDigit(XOFS+0, m / 10); displayDateDigit(XOFS+4, m % 10); displayDateDigit(XOFS+8, 10); //冒号 displayDateDigit(XOFS+12, d / 10); displayDateDigit(XOFS+16, d % 10); } displayNumber(5, 10, y, 0, false); mx.update();}//----------------------------------------- ----------------------// 根据配置设置写入一个正方形或小数位// x =0 到 23 // v =大号或平方数到display (0..10)void displayDateDigit(uint8_t x, uint8_t v){ if (EepromData.squareFont) { displaySquareDigit(x, v); } else { displaySmallDigit(x, v); }}//---------------------------------------------- -----------------// 写段位掩码// x =0 to 23// v =large digit to display (0..10)void displayLargeDigit(uint8_t x , uint8_t v){ if (x <24 &&v <11) { for (uint8_t row =0; row <6; row++) { for (uint8_t col =0; col <6; col++) { writePhysicalDigit(row, col + x, pgm_read_byte(&largeDigits[v][row][col]), false); } } }}//-------------------------------------------- -------------------// 写入段位掩码// x =0 到 23// v =要显示的大数字 (0..10)void displaySmallDigit( uint8_t x, uint8_t v){ if (x <24 &&v <11) { for (uint8_t row =0; row <5; row++) { for (uint8_t col =0; col <3; col++) { writePhysicalDigit(row, col + x, pgm_read_byte(&smallDigits[v][row][col]), false); } } }}//-------------------------------------------- -------------------// 写入段位掩码// x =0 到 23// v =要显示的大数字 (0..10)void displaySquareDigit( uint8_t col, uint8_t v){ //分段顺序 defbca _ g uint8_t mask =ascii[v]; if (mask ==B00000000) { //连字符掩码 =B00000001; } if (mask &B00000100) { //seg A writePhysicalDigit(0, col + 0, 0xff, false); writePhysicalDigit(0, col + 1, 0xff, false); writePhysicalDigit(0, col + 2, 0xff, false); } if (mask &B00010000) { //seg B writePhysicalDigit(0, col + 2, 0xff, false); writePhysicalDigit(1, col + 2, 0xff, false); writePhysicalDigit(2, col + 2, 0xff, false); } if (mask &B00001000) { //seg C writePhysicalDigit(2, col + 2, 0xff, false); writePhysicalDigit(3, col + 2, 0xff, false); writePhysicalDigit(4, col + 2, 0xff, false); } if (mask &B10000000) { //seg D writePhysicalDigit(4, col + 0, 0xff, false); writePhysicalDigit(4, col + 1, 0xff, false); writePhysicalDigit(4, col + 2, 0xff, false); } if (mask &B01000000) { //seg C writePhysicalDigit(2, col, 0xff, false); writePhysicalDigit(3, col, 0xff, false); writePhysicalDigit(4, col, 0xff, false); } if (mask &B00100000) { //seg E writePhysicalDigit(0, col, 0xff, false); writePhysicalDigit(1, col, 0xff, false); writePhysicalDigit(2, col, 0xff, false); } if (mask &B00000001) { //seg D writePhysicalDigit(2, col + 0, 0xff, false); writePhysicalDigit(2, col + 1, 0xff, false); writePhysicalDigit(2, col + 2, 0xff, false); }}//---------------------------------------------- -----------------// 写字符串uint8_t displayString(uint8_t row, uint8_t col, String s){ for (int i =0; i 0x5F) { c =0x3F; //用作空格字符 } c =c - 0x30; writePhysicalDigit(row, col, ascii[c], true);列 =列 + 1; }}//---------------------------------------------- -----------------// 写一个数字uint8_t displayNumber(uint8_t row, uint8_t col, int number, int padding, boolleadingZeros){ if (padding ==0) { padding =(数字> 0) ? floor(log10(number)) + 1 :1; } col =col + 填充;布尔第一 =真; for (int i =0; i > 2) * 3 + (row>> 1); uint16_t c =((uint16_t)dev <<3) | digitMap[dig];如果(!擦除){ v =v | mx.getColumn(c); } mx.setColumn(c, v); }}//---------------------------------------------- -----------------//返回给定年份和月份中的天数//Feb 有 28 除非闰年或世纪之交uint8_t daysInMonth(int y, int m) { return dom[m - 1] + ((m ==2 &&(y % 4) ==0 &&(y % 100) !=0) ? 1 :0);}//------- -------------------------------------------------- ------//将 EepromData 结构体写入 EEPROMvoid writeEepromData(){ //该函数使用 EEPROM.update() 来执行写入,所以如果值没有改变就不会重写。 EEPROM.put(EEPROM_ADDRESS,EepromData);}//-------------------------------------- -------------------------//从EEPROM读取EepromData结构体,必要时初始化void readEepromData(){ //Eprom EEPROM.get(EEPROM_ADDRESS, eeprom数据); //Serial.println("magic:" + String(EepromData.magic, 16) + ", alarm:" + String(EepromData.alarm) + ", time:" + String(EepromData.hours) + ":" + String(EepromData.minutes) + ", 12hr:" + String(EepromData.format12hr) + ", 亮度:" + String(EepromData.brightness)); if (EepromData.magic !=EEPROM_MAGIC) { Serial.println("初始化 EEPROM ..."); EepromData.magic =EEPROM_MAGIC; EepromData.alarm =false; eepromdata.minutes =30; EEpromData.hours =5; eepromData.format12hr =false; eepromdata.brightness =8; eepromdata.tune =0; EepromData.formatDmy =false; writeEepromData(); } Serial.println("alarm:" + String(EepromData.alarm) + ", time:" + String(EepromData.hours) + ":" + String(EepromData.minutes) + ", 12hr:" + String(EepromData) .format12hr) + ", 亮度:" + String(EepromData.brightness));}//---------------------------- ----------------------------------//播放一段旋律playSong(const uint16_t* melody){ //播放旋律中的每个音符,直到遇到 END_OF_TUNE 音符为止 musicPlaying =true; int thisNote =0; uint16_t noteRaw =pgm_read_word(&melody[thisNote++]); while (musicPlaying &¬eRaw !=END_OF_TUNE) { testButtons();屈服(); playNote(noteRaw); noteRaw =pgm_read_word(&melody[thisNote++]); } //while delay(50);}//-------------------------------------- -------------------------//播放单个notevoid playNote(uint16_t noteRaw){ // 计算音符时长,用一秒除以笔记类型。 // 例如四分音符 =1000 / 4,八分音符 =1000/8,等等。 uint16_t 频率 =noteRaw &0x1FFF; uint16_t 持续时间 =(noteRaw &0xE000)>> 13;如果(持续时间==7)持续时间=8; uint16_t noteDuration =1800 / 持续时间;如果(频率!=休息){ 音调(扬声器,频率,noteDuration); } // 为了区分音符,设置它们之间的最短时间。 // 音符的持续时间 + 30% 似乎效果很好: uint16_t pauseBetweenNotes =(noteDuration * 13) / 10;延迟(pauseBetweenNotes); if (frequency !=REST) { // 停止播放音调:noTone(SPEAKER); }}
Digits.hC/C++
/*--------------------------------------------------- * 7 Segment array clock * define each digit in a 6x6 byte array *--------------------------------------------------*/ #pragma once//---------------------------------------------------// Standard MAX7219 wiring// Digit order 0,1,2,3,4,5/*// Segment order _ a b c d e f g#define _______ B00000000#define __cd___ B00011000#define _bcde_g B00111101#define abcdefg B01111111#define __cdefg B00011111#define __c____ B00010000#define ab__efg B01100111#define abcd_fg B01111011#define abcde_g B01111101#define ab_defg B01101111#define _bc____ B00110000#define a__defg B01001111#define ____ef_ B00000110#define ___defg B00001111#define abc__fg B01110011#define a___ef_ B01000110#define _b_____ B00100000#define ab___f_ B01100010#define _bcd___ B00111000#define a___ef_ B01000110#define ab_____ B01100000#define ____e__ B00000100#define __cde_g B00011101#define a____f_ B01000010#define a_cdefg B01011111#define ___de__ B00001100#define _____f_ B00000010#define ab___fg B01100011#define a__def_ B01001110#define __cde__ B00011100#define a___efg B01000111#define a__d___ B01001000#define abc____ B01110000#define _bc_ef_ B00110110#define ___def_ B00001110#define abc_ef_ B01110110#define _bcdef_ B00111110#define a__def_ B01001110#define abcd___ B01111000*///Segment order d e f b c a _ g#define _______ B00000000#define __cd___ B10001000#define _bcde_g B11011001#define abcdefg B11111101#define __cdefg B11101001#define __c____ B00001000#define ab__efg B01110101#define abcd_fg B10111101#define abcde_g B11011101#define ab_defg B11110101#define _bc____ B00011000#define a__defg B11100101#define ____ef_ B01100000#define ___defg B11100001#define abc__fg B00111101#define a___ef_ B01100100#define _b_____ B00010000#define ab___f_ B00110100#define _bcd___ B10011000#define a___ef_ B01100100#define ab_____ B00010100#define ____e__ B01000000#define __cde_g B11001001#define a____f_ B00100100#define a_cdefg B11101101#define ___de__ B11000000#define _____f_ B00100000#define ab___fg B00110101#define a__def_ B11100100#define __cde__ B11001000#define a___efg B01100101#define a__d___ B10000100#define abc____ B00011100#define _bc_ef_ B01111000#define ___def_ B11100000#define abc_ef_ B01111100#define _bcdef_ B11111000#define a__def_ B11100100#define abcd___ B10011100//Square Numbers//ASCII Character Set//Numbers 0 - 9//Letters A - Z//Segment order d e f b c a _ guint8_t ascii[] ={ B11111100, B00011000, B11010101, B10011101, B00111001, B10101101, B11101101, B00011100, B11111101, B10111101, B00000000, B00000000, B00000000, B00000001, B00000000, B00000000, B00000000, B01111101, B11101001, B11100100, B11011001, B11100101, B01100101, B10111101, B01111001, B00011000, B11011000, B00000000, B11100000, B00000000, B01001001, B11001001, B01110101, B00000000, B01000001, B10101101, B11100001, B11001000, B00000000, B00000000, B00000000, B10111001, B00000000, B11001100, B01010001, B10011100, B00000000, B10000000};//Digit sequence for each device (MAX7219)uint8_t digitMap[] ={5, 2, 6, 4, 1, 7, 3, 0};//------------------------------------------------------------// Digits using logical coordinates//------------------------------------------------------------const int8_t largeDigits[11][6][6] PROGMEM ={ { //0 { _______, _______, __cd___, _bcde_g, abcdefg, __cdefg }, { _______, __c____, abcdefg, ab__efg, abcd_fg, abcdefg }, { _______, abcde_g, ab_defg, _______, _bc____, a__defg }, { _______, abcdefg, ____ef_, __c____, abcdefg, ____ef_ }, { _______, abcdefg, __cdefg, abcdefg, a___efg, _______ }, { _______, abc__fg, abcdefg, ab__efg, _______, _______ } }, { //1 { _______, _______, _______, __c____, abcdefg, __cdefg }, { _______, _______, _______, abcdefg, abcdefg, ____ef_ }, { _______, _______, _bcde_g, abcdefg, a__defg, _______ }, { _______, _bc____, abcdefg, abcdefg, ____ef_, _______ }, { _______, abcdefg, abcdefg, a___ef_, _______, _______ }, { _b_____, abcdefg, abcdefg, _______, _______, _______ } }, { //2 { _______, _______, __cd___, _bcde_g, abcdefg, __cdefg }, { _______, _bc____, abcdefg, ab___f_, abcd_fg, abcdefg }, { _______, _______, _______, _bcd___, abcdefg, a___ef_ }, { _______, _______, _bcde_g, abcdefg, a___ef_, _______ }, { __c____, abcdefg, abcdefg, a___ef_, _______, _______ }, { _b_____, abc__fg, abcdefg, abcdefg, ab__efg, _______ } }, { //3 { _______, _______, __cd___, abcdefg, abcdefg, __cdefg }, { _______, _b_____, abcdefg, ab___f_, abcd_fg, abcdefg }, { _______, _______, __cd___, _bcde_g, abcdefg, a___ef_ }, { _______, _______, ab_____, abc__fg, abcdefg, ____e__ }, { _______, __cd___, __cde_g, _bcde_g, abcdefg, ____ef_ }, { _b_____, abc__fg, abcdefg, abcdefg, a____f_, _______ } }, { //4 { _______, _______, _bcd___, abcdefg, __c____, abcdefg }, { _______, __c____, abcdefg, a___ef_, abcde_g, a__defg }, { _______, abcde_g, abcdefg, _bcd___, abcdefg, ____ef_ }, { __c____, abcdefg, abcdefg, abcdefg, abcdefg, _______ }, { _______, _______, __c____, abcdefg, ____ef_, _______ }, { _______, _______, abcdefg, ab__efg, _______, _______ } }, { //5 { _______, _______, _bcde_g, abcdefg, abcdefg, abcdefg }, { _______, _bc____, abcdefg, a_cdefg, ___de__, _______ }, { _______, _______, abc__fg, abcdefg, abcdefg, ____ef_ }, { _______, _______, _______, _bc____, abcdefg, ____ef_ }, { _______, __cde_g, __cde_g, _bcde_g, abcdefg, _____f_ }, { _b_____, abcdefg, abcdefg, ab__efg, a____f_, _______ } }, { //6 { _______, _______, _______, __cd___, abcdefg, ____ef_ }, { _______, _______, _bcde_g, abcdefg, a____f_, _______ }, { _______, _bcd___, abcdefg, abcdefg, abcdefg, ____e__ }, { __c____, abcdefg, a___ef_, ab_____, abcdefg, ____ef_ }, { _bc____, abcdefg, __cdefg, _bcde_g, abcdefg, _______ }, { _______, abc__fg, abcdefg, ab__efg, _______, _______ } }, { //7 { _______, _bc____, abcdefg, abcdefg, abcdefg, __cdefg }, { _______, _b_____, ab___fg, ab___fg, abcdefg, abcdefg }, { _______, _______, _______, _bcde_g, abcdefg, a___ef_ }, { _______, _______, _bcde_g, abcdefg, ab__efg, _______ }, { _______, _bcde_g, abcdefg, a___ef_, _______, _______ }, { _b_____, abcdefg, abcdefg, _______, _______, _______ } }, { //8 { _______, _______, __cd___, abcdefg, abcdefg, __cdefg }, { _______, _bc____, abcdefg, ab___f_, abcd_fg, abcdefg }, { _______, _b_____, abcdefg, _bcde_g, abcdefg, a___ef_ }, { _______, _bcde_g, ab__efg, abc__fg, abcdefg, ____e__ }, { _bc____, abcdefg, __cde_g, _bcde_g, abcdefg, ____ef_ }, { _______, abc__fg, abcdefg, abcdefg, a____f_, _______ } }, { //9 { _______, _______, __cde_g, abcdefg, __cdefg, ___de__ }, { _______, _bcd___, abcdefg, ab___fg, abcd_fg, abcdefg }, { _______, abcdefg, a_cdefg, __cde__, abcdefg, a__defg }, { _______, ab_____, abcd_fg, abcdefg, abcdefg, _____f_ }, { _______, __cd___, abcdefg, ab__efg, _______, _______ }, { _b_____, abcdefg, a___ef_, _______, _______, _______ } }, { //Colon { _______, _______, _______, _______, _______, _______ }, { _______, __cde_g, _______, _______, _______, _______ }, { _______, _______, _______, _______, _______, _______ }, { _______, _______, _______, _______, _______, _______ }, { __cde_g, _______, _______, _______, _______, _______ }, { _______, _______, _______, _______, _______, _______ } }};const int8_t smallDigits[11][5][3] PROGMEM ={ { //0 { a___ef_, a__d___, abc____ }, { _bc_ef_, _______, _bc_ef_ }, { _bc_ef_, _______, _bc_ef_ }, { _bc_ef_, _______, _bc_ef_ }, { ___def_, a__d___, _bcd___ } }, { //1 { _______, abc_ef_, _______ }, { _______, _bc_ef_, _______ }, { _______, _bc_ef_, _______ }, { _______, _bc_ef_, _______ }, { _______, _bcdef_, _______ } }, { //2 { a__def_, a__d___, abc____ }, { _______, _______, _bc_ef_ }, { a___ef_, a__d___, _bcd___ }, { _bc_ef_, _______, _______ }, { ___def_, a__d___, abcd___ } }, { //3 { a__def_, a__d___, abc____ }, { _______, _______, _bc_ef_ }, { _______, a__def_, _bc____ }, { _______, _______, _bc_ef_ }, { a__def_, a__d___, _bcd___ } }, { //4 { abc_ef_, _______, abc_ef_ }, { _bc_ef_, _______, _bc_ef_ }, { ___def_, a__d___, _bc____ }, { _______, _______, _bc_ef_ }, { _______, _______, _bcdef_ } }, { //5 { a___ef_, a__d___, abcd___ }, { _bc_ef_, _______, _______ }, { ___def_, a__d___, abc____ }, { _______, _______, _bc_ef_ }, { a__def_, a__d___, _bcd___ } }, { //6 { a___ef_, a__d___, abcd___ }, { _bc_ef_, _______, _______ }, { ____ef_, a__d___, abc____ }, { _bc_ef_, _______, _bc_ef_ }, { ___def_, a__d___, _bcd___ } }, { //7 { a__def_, a__d___, abc____ }, { _______, _______, _bc_ef_ }, { _______, _bc_ef_, _______ }, { _______, _bc_ef_, _______ }, { _______, _bcdef_, _______ }, }, { //8 { a___ef_, a__d___, abc____ }, { _bc_ef_, _______, _bc_ef_ }, { ____ef_, a__d___, _bc____ }, { _bc_ef_, _______, _bc_ef_ }, { ___def_, a__d___, _bcd___ } }, { //9 { a___ef_, a__d___, abc____ }, { _bc_ef_, _______, _bc_ef_ }, { ___def_, a__d___, _bc____ }, { _______, _______, _bc_ef_ }, { a__def_, a__d___, _bcd___ } }, { //Hyphen { _______, _______, _______ }, { _______, _______, _______ }, { a__def_, a__d___, abcd___ }, { _______, _______, _______ }, { _______, _______, _______ } }};
Tunes.hC/C++
无预览(仅限下载)。
Button.hC/C++
/*Class:ButtonAuthor:John Bradnam ([email protected])Purpose:Arduino library to handle buttons*/#pragma once#include "Arduino.h"#define DEBOUNCE_DELAY 10//Repeat speed#define REPEAT_START_SPEED 500#define REPEAT_INCREASE_SPEED 50#define REPEAT_MAX_SPEED 50class Button{ public://Simple constructor Button(int pin); Button(int name, int pin); Button(int name, int pin, int analogLow, int analogHigh, bool activeLow =true); //Background function called when in a wait or repeat loop void Background(void (*pBackgroundFunction)()); //Repeat function called when button is pressed void Repeat(void (*pRepeatFunction)()); //Test if button is pressed bool IsDown(void); //Test whether button is pressed and released //Will call repeat function if one is provided bool Pressed(); //Return button state (HIGH or LOW) - LOW =Pressed int State(); //Return button name int Name(); private:int _name; int _pin; bool _range; int _low; int _high; bool _activeLow; void (*_repeatCallback)(void); void (*_backgroundCallback)(void);};
Button.cppC/C++
/*Class:ButtonAuthor:John Bradnam ([email protected])Purpose:Arduino library to handle buttons*/#include "Button.h"Button::Button(int pin){ _name =pin; _pin =pin; _range =false; _low =0; _high =0; _backgroundCallback =NULL; _repeatCallback =NULL; pinMode(_pin, INPUT_PULLUP);}Button::Button(int name, int pin){ _name =name; _pin =pin; _range =false; _low =0; _high =0; _backgroundCallback =NULL; _repeatCallback =NULL; pinMode(_pin, INPUT_PULLUP);}Button::Button(int name, int pin, int analogLow, int analogHigh, bool activeLow){ _name =name; _pin =pin; _range =true; _low =analogLow; _high =analogHigh; _activeLow =activeLow; _backgroundCallback =NULL; _repeatCallback =NULL; pinMode(_pin, INPUT);}//Set function to invoke in a delay or repeat loopvoid Button::Background(void (*pBackgroundFunction)()){ _backgroundCallback =pBackgroundFunction;}//Set function to invoke if repeat system requiredvoid Button::Repeat(void (*pRepeatFunction)()){ _repeatCallback =pRepeatFunction;} bool Button::IsDown(){ if (_range) { int value =analogRead(_pin); return (value>=_low &&value <_high); } else { return (digitalRead(_pin) ==LOW); }}//Tests if a button is pressed and released// returns true if the button was pressed and released// if repeat callback supplied, the callback is called while the key is pressedbool Button::Pressed(){ bool pressed =false; if (IsDown()) { unsigned long wait =millis() + DEBOUNCE_DELAY; while (millis()=time) { _repeatCallback(); unsigned long faster =speed - REPEAT_INCREASE_SPEED; if (faster>=REPEAT_MAX_SPEED) { speed =faster; } time =millis() + speed; } } pressed =true; } } return pressed;}//Return current button stateint Button::State(){ if (_range) { int value =analogRead(_pin); if (_activeLow) { return (value>=_low &&value <_high) ? LOW :HIGH; } else { return (value>=_low &&value <_high) ? HIGH :LOW; } } else { return digitalRead(_pin); }}//Return current button nameint Button::Name(){ return _name;}
Hardware_Test_V1.zipC/C++
Hardware test sketch - Turns on all segments and tests speakerNo preview (download only).
定制零件和外壳
示意图
Schematic for each MAX7219 (repeated 18 times) Holds six 7 segment 4 digit displays (6 boards required) PCB files in Eagle format eagle_files_KPhUOj6Ezv.zip制造工艺