亿迅智能制造网
工业4.0先进制造技术信息网站!
首页 | 制造技术 | 制造设备 | 工业物联网 | 工业材料 | 设备保养维修 | 工业编程 |
home  MfgRobots >> 亿迅智能制造网 >  >> Manufacturing Technology >> 制造工艺

迷你 LED 矩阵时钟

组件和用品

Arduino Nano R3
× 1
Maxim Integrated DS3231M - ±5ppm,I2C 实时时钟
× 1
按钮开关,瞬时
× 2
LED 矩阵模块 32x8
× 1

必要的工具和机器

烙铁(通用)

应用和在线服务

Arduino IDE

关于这个项目

在“尼克的 LED 项目”页面上,我发现时钟项目女巫用 8x8 的 LED 在 4 个矩阵上显示时间。他用销售矩阵模块面板DIY套件的“ICStation”商店的矩阵制作了时钟。

通过对代码的最小更改,我使用 MAX7219 点阵模块微控制器四合一显示器制作了我的时钟,该显示器完全折叠并且便宜得多。我是从速卖通购买的。

时钟有很多特点:

- 大数字的基本模式

- 数字滚动屏幕的滑动模式

- 带秒模式的小数字

- 用文字书写的时间,例如“十二点十”

- 日期显示

- 12/24 小时选项

- 亮度选项

- 随机时钟模式选项,每隔几个小时改变一次显示模式。

- 用于设置和显示选择的按钮驱动菜单。

正如你在电路上看到的,除了矩阵,我们需要一个 arduino 板、一个实时时钟模块和两个用于设置的按钮。您可以通过以下链接下载库和修改后的代码。

代码

  • 代码
  • 图书馆
代码Arduino
/**************************************************** *************************Mini Clock v1.0,2014 年 7 月由 Nick Hall 根据 GPL 条款分发。有关如何构建时钟见我的博客:http://123led.wordpress.com/Tested on IDE v1.6.5 ***************************** ******************************************//包含库:#include "LedControl.h"#include  // 字体库#include  // DS1307 时钟#include "RTClib.h" // DS1307 时钟#include  // 按钮库 by Alexander Brevig // 设置 LED 矩阵 // 引脚 12 连接到显示器上的 DataIn // 引脚 11 连接到显示器上的 CLK // 引脚 10 连接到显示器上的 LOADLedControl lc =LedControl(12, 11, 10, 4); //将 3 个引脚设置为 12、11 和 10,然后设置 4 个显示器(最多 8 个显示器)//全局变量字节强度 =7; // 默认强度/亮度 (0-15)byte clock_mode =0; // 默认时钟模式。默认 =0 (basic_mode)bool random_mode =0; // 定义随机模式 - 每隔几个小时更改一次显示类型。默认 =0 (off)byte old_mode =clock_mode; // 存储之前的时钟模式,所以如果我们去日期或其他什么,我们知道回到什么模式 after.bool ampm =0; // 定义 12 或 24 小时时间。 0 =24 小时。 1 =12 小时字节 change_mode_time =0; // 如果处于随机模式,则在时钟模式下一次更改时保持小时。unsigned long delaytime =500; // 我们总是在 displayint rtc[7] 的更新之间等待一段时间; // 保存实时时钟 outputchar days[7][4] ={ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; //day 数组 - 用于幻灯片、basic_mode 和 jumble 模式(DS1307 输出 1-7 个星期几值)char daysfull[7][9] ={ "Sunday", "Monday", "Tuesday", "Wed ", "星期四", "星期五", "星期六"};字符后缀[4][3] ={ "st", "nd", "rd", "th"}; //日期后缀数组,用于slide、basic_mode和jumble模式。 e,g, 1st 2nd ...//define constants#define NUM_DISPLAY_MODES 3 // 数字显示模式(连续零作为第一个模式)#define NUM_SETTINGS_MODES 4 // 数字设置模式=6(连续零作为第一个模式)#定义 SLIDE_DELAY 20 // 滑动模式下每个字符的滑动效果的时间(以毫秒为单位)。使其更高以获得更慢的效果#define cls clear_display // 清除 displayRTC_DS1307 ds1307; // 创建 RTC 对象按钮 buttonA =Button(2, BUTTON_PULLUP); // 设置按钮 A​​(使用按钮库)Button buttonB =Button(3, BUTTON_PULLUP); // 设置按钮 B(使用按钮库)void setup() { digitalWrite(2, HIGH); // 打开引脚 2 上按钮的上拉电阻 digitalWrite(3, HIGH); // 打开引脚 3 上按钮的上拉电阻 digitalWrite(4, HIGH); // 打开引脚 4 上按钮的上拉电阻 Serial.begin(9600); //开始串行 //初始化4个矩阵面板 //我们在创建LedControl时已经设置了设备数量 int devices =lc.getDeviceCount(); //我们必须在循环中初始化所有设备 for (int address =0; address =0 &&x <=7) { 地址 =3; } if (x>=8 &&x <=15) { 地址 =2; x =x - 8; } if (x>=16 &&x <=23) { 地址 =1; x =x - 16; } if (x>=24 &&x <=31) { 地址 =0; x =x - 24; } if (val ==1) { lc.setLed(address, y, x, true); } else { lc.setLed(地址, y, x, false); }}//clear screenvoid clear_display() { for (byte address =0; address <4; address++) { lc.clearDisplay(address); }}//淡出屏幕向下无效fade_down() { //从全局强度淡入1 for (byte i =强度; i> 0; i--) { for (byte address =0; address <4; address++) { lc .setIntensity(地址,我); } 延迟(30); //改变它以改变淡出速度 } clear_display(); //完全清除显示(关闭)//将意图重置为全局值(字节地址=0;地址<4;地址++){ lc.setIntensity(地址,强度); }}//上电led测试并显示软件版本号void printver() { byte i =0; char ver_a[9] ="Vers 1.0"; char ver_b[9] ="你好!"; //测试所有LED。 for (byte x =0; x <=31; x++) { for (byte y =0; y <=7; y++) { plot(x, y, 1); } } 延迟(500);淡入淡出(); while (ver_a[i]) { puttinychar((i * 4), 1, ver_a[i]);延迟(35);我++; } 延迟(700);淡入淡出();我 =0; while (ver_b[i]) { puttinychar((i * 4), 1, ver_b[i]);延迟(35);我++; } 延迟(700); fake_down();}// puttinychar// 从 myfont 数据结构复制一个 3x5 字符字形到显示内存,其左上角位于给定的坐标处// 这是未优化的,只是使用 plot() 绘制每个点.void puttinychar (字节 x, 字节 y, 字符 c){ 字节点; if (c>='A' &&c <='Z' || (c>='a' &&c <='z') ) { c &=0x1F; // A-Z 映射到 1-26 } else if (c>='0' &&c <='9') { c =(c - '0') + 32; } else if (c ==' ') { c =0; // 空格 } else if (c =='.') { c =27; // 句号 } else if (c ==':') { c =28; // 冒号 } else if (c =='\'') { c =29; // 单引号 } else if (c =='!') { c =30; // 单引号 } else if (c =='?') { c =31; // 单引号 } for (byte col =0; col <3; col++) { dots =pgm_read_byte_near(&mytinyfont[c][col]); for (char row =0; row <5; row++) { if (dots &(16>> row)) plot(x + col, y + row, 1);否则 plot(x + col, y + row, 0); } }}void putnormalchar(byte x, byte y, char c){ 字节点; // if (c>='A' &&c <='Z' || (c>='a' &&c <='z') ) { // c &=0x1F; // A-Z 映射到 1-26 // } if (c>='A' &&c <='Z' ) { c &=0x1F; // A-Z 映射到 1-26 } else if (c>='a' &&c <='z') { c =(c - 'a') + 41; // A-Z 映射到 41-67 } else if (c>='0' &&c <='9') { c =(c - '0') + 31; } else if (c ==' ') { c =0; // 空格 } else if (c =='.') { c =27; // 句号 } else if (c =='\'') { c =28; // 单引号 } else if (c ==':') { c =29; // 时钟模式选择器箭头 } else if (c =='>') { c =30; // 时钟模式选择箭头 } else if (c>=-80 &&c <=-67) { c *=-1; } for (char col =0; col <5; col++) { dots =pgm_read_byte_near(&myfont[c][col]); for (char row =0; row <7; row++) { //在尝试绘图之前检查坐标是否在屏幕上 //if ((x>=0) &&(x <=31) &&(y>=0) &&(y <=7)){ if (dots &(64>> row)) { // 只有 7 行。绘图(x + col,y + 行,1); } else { plot(x + col, y + row, 0); } //} } }}//small_mode//以3x5的小字符显示时间,以秒为单位 displayvoid small_mode() { char textchar[8]; // 显示字节的 16 个字符 mins =100; //分钟字节秒 =rtc[0]; //秒字节 old_secs =secs; //保持旧秒值 - 从上次更新秒开始 o 显示 - 用于检查秒数是否已更改 cls(); //只要run_mode返回true,就运行时钟主循环 while (run_mode()) { get_time(); //检查按钮按下 if (buttonA.uniquePress()) { switch_mode();返回; } if (buttonB.uniquePress()) { display_date();返回; } //如果秒数改变了,则在显示器上更新它们 secs =rtc[0]; if (secs !=old_secs) { //secs char buffer[3]; itoa(秒,缓冲区,10); //修复 - 否则,如果 num 有前导零,例如“03”秒,itoa 将其转换为带有空格“3”的字符。 if (secs <10) { buffer[1] =buffer[0];缓冲区[0] ='0'; } puttinychar( 20, 1, ':'); //秒冒号 puttinychar( 24, 1, buffer[0]); //秒 puttinychar( 28, 1, buffer[1]); //秒 old_secs =secs; } //如果分钟改变改变时间 if (mins !=rtc[1]) { //重置这些以便下次比较 mins =rtc[1];字节小时 =rtc[2];如果(小时> 12){ 小时 =小时 - 安培 * 12; } if (hours <1) { hours =hours + ampm * 12; } //字节dow =rtc[3]; // DS1307 输出 0 - 6,其中 0 =星期日0 - 6,其中 0 =星期日。 //字节日期 =rtc[4]; //设置字符字符缓冲区[3]; itoa(小时,缓冲,10); //修复 - 否则,如果 num 有前导零,例如“03”小时,itoa 将其转换为带有空格“3”的字符。 if (hours <10) { buffer[1] =buffer[0]; //如果我们处于 12 小时模式,则空白前导零。如果(安培){缓冲区[0] =“”; } else { 缓冲区[0] ='0'; } } //设置小时字符 textchar[0] =buffer[0];文本字符[1] =缓冲区[1]; textchar[2] =':'; itoa(分钟,缓冲区,10); if (mins <10) { buffer[1] =buffer[0];缓冲区[0] ='0'; } //设置分钟字符 textchar[3] =buffer[0];文本字符[4] =缓冲区[1]; //做秒 textchar[5] =':';缓冲区[3];秒 =rtc[0]; itoa(秒,缓冲区,10); //修复 - 否则,如果 num 有前导零,例如“03”秒,itoa 将其转换为带有空格“3”的字符。 if (secs <10) { buffer[1] =buffer[0];缓冲区[0] ='0'; } //设置秒 textchar[6] =buffer[0];文本字符[7] =缓冲区[1];字节 x =0;字节 y =0; //打印每个字符 for (byte x =0; x <6; x++) { puttinychar( x * 4, 1, textchar[x]); } } 延迟(50); }fade_down();}// basic_mode()//以5x7个字符显示时间void basic_mode(){ cls();字符缓冲区[3]; //为了将 rtc 值转换为字符,我们可以在屏幕上打印字节偏移量 =0; //当我们处于12小时模式并且时钟仅显示3位数字时,用于偏移数字的x位置并使显示居中。例如3:21 字节 x, y; //当我们在 12 小时模式下从 12:59 -> 1:00 am 滚动时,用于在显示器的左手“1”上绘制一个清晰的框。 //如果ampm设置为1字节,则进行12/24小时转换 hours =rtc[2];如果(小时> 12){ 小时 =小时 - 安培 * 12; } if (hours <1) { hours =hours + ampm * 12; } //做偏移量转换 if (ampm &&hours <10) { offset =2; } //设置下一分钟,我们在 //set_next_date(); // 最初将 mins 设置为值 100 - 因此它在时钟的第一个循环中永远不会等于 rtc[1],这意味着我们在输入函数字节 secs =100 时绘制时钟显示;字节分钟 =100;整数计数 =0; //只要run_mode返回true就运行时钟主循环 while (run_mode()) { //从时钟芯片中获取时间 get_time(); //检查按钮按下 if (buttonA.uniquePress()) { switch_mode();返回; } if (buttonB.uniquePress()) { display_date();返回; } //检查是否到了自动显示日期的时间 ​​//check_show_date(); //绘制闪烁:就像秒已经改变一样。 if (secs !=rtc[0]) { //用新值更新秒数 secs =rtc[0]; //draw :plot (15 - offset, 2, 1); //顶点图(15 - 偏移量,5, 1); //底部点数 =400; } //如果计数已经用完,关闭: if (count ==0) { plot (15 - offset, 2, 0); //顶点图(15 - 偏移量,5, 0); //底点 } else { 计数--; } //如果按下按钮或如果 mins !=rtc[1] 重新绘制显示,即如果时间从我们存储在 mins 中的时间改变了,(当 mins 为 100 时也触发第一次进入函数) if (mins !=rtc[1]) { //用新值更新分钟和小时 mins =rtc[1];小时 =rtc[2]; //调整ampm的小时数设置为12小时模式 if (hours> 12) { hours =hours - ampm * 12; } if (hours <1) { hours =hours + ampm * 12; } itoa(小时,缓冲区,10); //如果小时 <10 数字例如“3”小时,itoa 将其转换为带有空格“3”的字符,如果 (hours <10) { buffer[1] =buffer[0];缓冲区[0] ='0'; } //print hours //如果我们在 12 小时模式下并且 hours <10,那么不要打印前导零,并设置偏移量,以便我们以 3 位数字居中显示。 if (ampm &&hours <10) { offset =2; //如果时间是凌晨 1:00 清除整个显示,因为此时偏移量发生变化,我们需要清除旧的 12:59 if ((hours ==1 &&mins ==0) ) { cls(); } } else { //else 无偏移并打印小时十位偏移 =0; //如果时间是上午 10:00 清除整个显示,因为此时偏移量发生变化,我们需要清除旧的 9:59 if (hours ==10 &&mins ==0) { cls(); } putnormalchar(1, 0, buffer[0]); } //打印小时个位数 putnormalchar(7 - offset, 0, buffer[1]); //print mins // 如果 mins <10 itoa (mins, buffer, 10),则添加前导零; if (mins <10) { buffer[1] =buffer[0];缓冲区[0] ='0'; } //打印分钟十位和个位 putnormalchar(19 - offset, 0, buffer[0]); putnormalchar(25 - offset, 0, buffer[1]); } }fade_down();}//类似于basic_mode,但有滑动效果void slide() { byte digits_old[4] ={99, 99, 99, 99}; //我们存储时间的旧值。设置为最初永远不会匹配时间的东西,所以当模式开始时所有数字都被绘制字节digits_new[4]; //新数字时间将滑动以显示字节digits_x_pos[4] ={25, 19, 7, 1}; //x pos 在 char old_char[2] 处绘制每个数字; //当我们使用itoa将当前数字(字节类型)转置成char传递给动画函数时使用 char new_char[2]; //当我们使用 itoa 将新数字(类型字节)转置为字符以传递给动画函数时使用 //old_chars - 在显示器上存储 5 天和日期后缀字符。例如“星期一”和“圣”。当这些字符更新时,我们将它们作为当前字符输入到幻灯片动画中。 //我们最初将它们作为 A 发送,当 clocl 进入模式并且没有存储最后一个字符时使用它们。 //char old_chars[6] ="AAAAA"; //在显示器上绘制时钟冒号 cls(); putnormalchar( 13, 0, ':');字节 old_secs =rtc[0]; // 在 old_secs 中存储秒数。我们比较秒和旧秒。当它们不同时,我们重绘显示 //run clock main loop 只要 run_mode 返回 true while (run_mode()) { get_time(); //检查按钮按下 if (buttonA.uniquePress()) { switch_mode();返回; } if (buttonB.uniquePress()) { display_date();返回; } //如果秒变了,则更新显示 if (rtc[0] !=old_secs) { old_secs =rtc[0]; //如果ampm设置为1字节,则进行12/24小时转换 hours =rtc[2];如果(小时> 12){ 小时 =小时 - 安培 * 12; } if (hours <1) { hours =hours + ampm * 12; } //将所有日期和时间拆分为单独的数字-坚持digits_new数组 //rtc[0] =secs //array pos和digit存储 //digits_new[0] =(rtc[0]%10); //0 - 秒数 //digits_new[1] =((rtc[0]/10)%10); //1 - secs tens //rtc[1] =mins digits_new[0] =(rtc[1] % 10); //2 - 分钟个位数digits_new[1] =((rtc[1] / 10) % 10); //3 - 十分钟 //rtc[2] =hours numbers_new[2] =(hours % 10); //4 - 小时个位数digits_new[3] =((hours / 10) % 10); //5 - 十小时 //rtc[4] =日期 //digits_new[6] =(rtc[4]%10); //6 - 日期的 //digits_new[7] =((rtc[4]/10)%10); //7 - date tens //绘制所有字符的初始屏幕。在此之后,我们只需绘制更改。 //比较数字 0 到 3(分钟和小时) for (byte i =0; i <=3; i++) { //查看数字是否改变... if (digits_old[i] !=digits_new[i]) { //依次为每个运行9步动画序列 for (byte seq =0; seq <=8; seq++) { //将数字转换为字符串 itoa(digits_old[i], old_char, 10); itoa(digits_new[i], new_char, 10); //如果设置为 12 小时模式并且我们处于数字 2(小时十进制模式)然后检查这是否为零。如果是,则将其空白,这样我们就会得到 2.00pm 而不是 02.00pm if (ampm &&i ==3) { if (digits_new[3] ==0) { new_char[0] =' '; } if (digits_old[3] ==0) { old_char[0] =' '; } } //为每个数字绘制动画帧 slideanim(digits_x_pos[i], 0, seq, old_char[0], new_char[0]);延迟(幻灯片延迟); } } } /* //比较日期数字6(个)和(7)个十位——如果其中任何一个发生变化,我们需要更新日期行。我们比较日期为 10 月 31 日 -> 2 月 1 日,如果 ((digits_old[6] !=digits_new[6]) || (digits_old[7] !=digits_new[7])) { //更改显示的日期。下面的循环依次遍历 3 个字符中的每一个,例如"MON" for (byte day_char =0; day_char <=2; day_char++){ //为每个字符运行动画序列 for (byte seq =0; seq <=8; seq++){ //第 (0 - 6 天) ) 将此数字读入 days 字符数组。数组 0-2 中的秒数获取日期名称的 3 个字符,例如m o n slideanim(6*day_char,8,seq,old_chars[day_char],days[rtc[3]][day_char]); //6 x day_char 为我们提供了 char delay(SLIDE_DELAY) 的 x pos; } //将旧的日期字符保存到数组位置 0-2 处的 old_chars 数组中。我们下次更改日期时使用它并将其作为当前字符提供给动画。更新后的字符作为新字符输入。 old_chars[day_char] =days[rtc[3]][day_char]; } //更改日期的十位数(如果需要)和个位数。 (日期的个位数总是会改变,但将它放在“if”循环中会使代码更简洁。) for (byte i =7; i>=6; i--){ if (digits_old[i] !=digits_new[i]) { for (byte seq =0; seq <=8; seq++){ itoa(digits_old[i],old_char,10); itoa(digits_new[i],new_char,10); Slideanim(digits_x_pos[i],8,seq,old_char[0],new_char[0]);延迟(幻灯片延迟); } } } //打印日期后缀“nd”“rd”“th”等。首先计算出日期2个字母后缀——例如st、nd、rd、th字节s =3; //从中读取后缀数组的位置。字节日期 =rtc[4]; if(date ==1 || date ==21 || date ==31) { s =0; } else if (date ==2 || date ==22) { s =1; } else if (date ==3 || date ==23) { s =2; } for (byte suffix_char =0; suffix_char <=1; suffix_char++){ for (byte seq =0; seq <=8; seq++){ slideanim((suffix_char*6)+36,8,seq,old_chars[suffix_char+3 ],suffix[s][suffix_char]); // 我们传入 old_char 数组 char 作为当前 char 和后缀数组作为新 char delay(SLIDE_DELAY); } //将suffic char保存在数组pos 3和5处的旧chars数组中。下次我们更改后缀并将其作为当前char提供给动画时,我们将使用这些char。更新后的字符作为新字符输入。 old_chars[suffix_char+3] =suffix[s][suffix_char]; } }//end do date line */ //保存数字数组 tol old 用于比较下一个循环 for (byte i =0; i <=3; i++) { digits_old[i] =digits_new[i]; } }//secs/oldsecs }//while循环fade_down();}//被slide调用//这会绘制一个char滑上另一个滑出的动画。动画有8个步骤,我们调用函数来绘制0-7的步骤之一//输入是char x和y,动画帧序列(0-7)以及正在绘制的当前和新的chars.void slideanim(byte x, byte y, byte sequence, char current_c, char new_c) { // 要滑动一个字符然后滑动另一个字符,我们需要按顺序 9 个步骤或帧... // seq# 0123456 <-行显示// | ||||||| // seq0 0123456 START - 显示器的所有行 0-6 显示当前字符行 0-6 // seq1 012345 当前字符在显示器上向下移动一行。我们只看到它的第 0-5 行。在显示位置 1-6 顶部插入了一个空白行 // seq2 6 01234 当前字符向下移动 2 行。我们现在只能在显示器的第 2-6 行看到第 0-4 行。显示屏的第 1 行是空白的。第 0 行显示新字符的第 6 行 // seq3 56 0123 // seq4 456 012 半旧/半新字符 // seq5 3456 01 // seq6 23456 0 // seq7 123456 // seq8 0123456 END - 所有行显示新char //从上面我们可以看到... //currentchar 运行 0-6 然后 0-5 然后 0-4 一直到 0。起始 Y 位置每次增加 1 行。 //新字符运行6然后5-6然后4-6然后3-6。起始 Y 位置每次增加 1 行。 //如果序列号小于7,我们需要绘制当前字符 if (sequence <7) { byte dots; // if (current_c>='A' &&|| (current_c>='a' &¤t_c <='z') ) { // current_c &=0x1F; // A-Z 映射到 1-26 // } if (current_c>='A' &¤t_c <='Z' ) { current_c &=0x1F; // A-Z 映射到 1-26 } else if (current_c>='a' &¤t_c <='z') { current_c =(current_c - 'a') + 41; // A-Z 映射到 41-67 } else if (current_c>='0' &¤t_c <='9') { current_c =(current_c - '0') + 31; } else if (current_c ==' ') { current_c =0; // 空格 } else if (current_c =='.') { current_c =27; // 句号 } else if (current_c =='\'') { current_c =28; // 单引号 } else if (current_c ==':') { current_c =29; //冒号 } else if (current_c =='>') { current_c =30; // 时钟模式选择箭头 } 字节 curr_char_row_max =7 - 序列; //要绘制的最大行数为 6 - 序列号字节 start_y =sequence; //y 开始的位置 - 与序列号相同。我们包含每个循环 //绘制每一行直到行最大值(从序列号计算) for (byte curr_char_row =0; curr_char_row <=curr_char_row_max; curr_char_row++) { for (byte col =0; col <5; col++) { dots =pgm_read_byte_near(&myfont[current_c][col]); if (dots &(64>> curr_char_row)) plot(x + col, y + start_y, 1); //在else上绘制 plot(x + col, y + start_y, 0); //else plot led off } start_y++;//向 y 添加一个,所以我们将下一行向下绘制 } } //如果序列在 1 和 7 之间,则在字符之间画一条空行。如果我们不这样做,我们得到当前字符的剩余部分留在显示器上的最后一个位置 if (sequence>=1 &&sequence <=8) { for (byte col =0; col <5; col++) { plot(x + col, y + (sequence - 1), 0); //画线的y位置等价于序列号 - 1 } } //如果序列大于2,我们还需要开始绘制新的char if (sequence>=2) { //计算出char字节点; //if (new_c>='A' &&new_c <='Z' || (new_c>='a' &&new_c <='z') ) { // new_c &=0x1F; // A-Z 映射到 1-26 //} if (new_c>='A' &&new_c <='Z' ) { new_c &=0x1F; // A-Z 映射到 1-26 } else if (new_c>='a' &&new_c <='z') { new_c =(new_c - 'a') + 41; // A-Z 映射到 41-67 } else if (new_c>='0' &&new_c <='9') { new_c =(new_c - '0') + 31; } else if (new_c ==' ') { new_c =0; // 空格 } else if (new_c =='.') { new_c =27; // 句号 } else if (new_c =='\'') { new_c =28; // 单引号 } else if (new_c ==':') { new_c =29; // 时钟模式选择箭头 } else if (new_c =='>') { new_c =30; // 时钟模式选择箭头 } byte newcharrowmin =6 - (sequence - 2); //为新字符绘制的最小行数 - 当输入序列号 2-8 时,这会生成 6 到 0 的输出。这是为新字符字节 start_y =0 绘制的最小行; //y 开始的位置 - 与序列号相同。 we inc it each row //绘制每一行从最小行(按序列号计算)到 6 for (byte newcharrow =newcharrowmin; newcharrow <=6; newcharrow++) { for (byte col =0; col <5; col++ ) { 点 =pgm_read_byte_near(&myfont[new_c][col]); if (dots &(64>> newcharrow)) plot(x + col, y + start_y, 1); //在else上绘制 plot(x + col, y + start_y, 0); //else plot led off } start_y++;//给y加一个,所以我们把下一行画下来 } }}//使用单词而不是数字打印时钟void word_clock() { cls();字符数字[19][10] ={“一”,“二”,“三”,“四”,“五”,“六”,“七”,“八”,“九”,“十”, “十一”、“十二”、“十三”、“十四”、“十五”、“十六”、“十七”、“十八”、“十九”}; char numberstens[5][7] ={ "十", "二十", "三十", "四十", "五十" }; //可能有 3 行来显示 char str_a[8];字符 str_b[8];字符 str_c[8]; //字节 hours_y, mins_y; //小时和分钟以及小时和分钟行的位置 byte hours =rtc[2];如果(小时> 12){ 小时 =小时 - 安培 * 12; } if (hours <1) { hours =hours + ampm * 12; } get_time(); //从时钟芯片字节中获取时间 old_mins =100; // 将分钟存储在 old_mins 中。我们比较分钟和旧分钟,当它们不同时,我们重新绘制显示。最初将其设置为 100,以便在模式启动时绘制显示。字节分钟; //运行时钟主循环,只要run_mode返回true while (run_mode()) { //检查按钮按下 if (buttonA.uniquePress()) { switch_mode();返回; } if (buttonB.uniquePress()) { display_date(); } get_time(); //从时钟芯片中获取时间 mins =rtc[1]; //get mins //如果 mins 与 old_mins 不同 - 重绘显示 if (mins !=old_mins) { //用当前的 mins 值更新 old_mins old_mins =mins; //重置这些以便下次比较 mins =rtc[1];小时 =rtc[2]; //将小时数转换为 12 小时格式 if (hours> 12) { hours =hours - 12; } 如果(小时 ==0){ 小时 =12; } //将分钟值拆分为两个单独的数字 int minsdigit =rtc[1] % 10;字节 minsdigitten =(rtc[1] / 10) % 10; //如果 mins <=10 ,那么顶行必须读取“minsdigti past”,底行读取小时 if (mins <10) { strcpy (str_a, numbers[minsdigit - 1]); strcpy (str_b, "PAST"); strcpy (str_c, numbers[hours - 1]); } //如果mins =10,不能像上面一样使用minsdigit,所以社会案例要打印过去/n小时的10。 if (mins ==10) { strcpy (str_a, numbers[9]); strcpy(str_b,“过去”); strcpy (str_c, numbers[hours - 1]); } //如果时间不是在小时 - 即两个分钟数字都不为零,//然后让第一行读取“小时”,第二行和第三行读取“分钟”“分钟”,例如“三 / n 二十 / n 一” else if (minsdigitten !=0 &&minsdigit !=0 ) { strcpy (str_a, numbers[hours - 1]); //如果 mins 为 teens,则将数字数组中的 teens 用于第二行,例如"fifteen" //if (mins>=11 &&mins <=19) { if (mins <=19) { strcpy (str_b, numbers[mins - 1]); } else { strcpy (str_b, numberstens[minsdigitten - 1]); strcpy (str_c, numbers[minsdigit - 1]); } } // 如果分钟数为零,则不打印它。阅读阅读“小时”“分钟”例如“三 / 二十” else if (minsdigitten !=0 &&minsdigit ==0 ) { strcpy (str_a, numbers[hours - 1]); strcpy (str_b, numberstens[minsdigitten - 1]); strcpy (str_c, ""); } //如果两个分钟都为零,即它是在小时,顶行读“小时”,底行读“点钟” else if (minsdigitten ==0 &&minsdigit ==0 ) { strcpy (str_a,数字[小时 - 1]); strcpy (str_b, "O'CLOCK"); strcpy (str_c, ""); } }//结束工作时间//循环运行//打印一行“十二”字节len =0;而 (str_a[len]) { len++; }; //获取消息字节的长度 offset_top =(31 - ((len - 1) * 4)) / 2; // //绘制小时线字节 i =0; while (str_a[i]) { puttinychar((i * 4) + offset_top, 1, str_a[i]);我++; } //保持显示但检查按钮是否按下 int counter =1000; while (counter> 0){ //检查按钮是否按下 if (buttonA.uniquePress()) { switch_mode();返回; } if (buttonB.uniquePress()) { display_date(); } 延迟(1);柜台 -;淡入淡出(); //打印行 b len =0;而 (str_b[len]) { len++; }; //获取消息的长度offset_top =(31 - ((len - 1) * 4)) / 2;我 =0; while (str_b[i]) { puttinychar((i * 4) + offset_top, 1, str_b[i]);我++; } //保持显示但检查按钮是否按下 counter =1000; while (counter> 0){ if (buttonA.uniquePress()) { switch_mode();返回; } if (buttonB.uniquePress()) { display_date(); } 延迟(1);柜台 -;淡入淡出(); //如果有就打印c行。长度 =0;而 (str_c[len]) { len++; }; //获取消息的长度offset_top =(31 - ((len - 1) * 4)) / 2;我 =0; while (str_c[i]) { puttinychar((i * 4) + offset_top, 1, str_c[i]);我++; } 计数器 =1000; while (counter> 0){ //检查按钮是否按下 if (buttonA.uniquePress()) { switch_mode();返回; } if (buttonB.uniquePress()) { display_date(); } 延迟(1);柜台 -;淡入淡出(); //保持显示空白,但在再次开始之前检查按钮是否按下。计数器 =1000; while (counter> 0){ //检查按钮是否按下 if (buttonA.uniquePress()) { switch_mode();返回; } if (buttonB.uniquePress()) { display_date(); } 延迟(1);柜台 -; } }fade_down();}//滚动消息 - 目前未使用 - 太慢了。void scroll() { char message[] ={"Hello There "}; cls();字节 p =6; //字符串中的当前位置 byte chara[] ={0, 1, 2, 3, 4, 5}; //字符串中的字符 int x[] ={0, 6, 12, 18, 24, 30}; //每个字符字节的xpos y =0; //y 位置 // clear_buffer(); while (message[p] !='\0') { //绘制所有 6 个字符 for (byte c =0; c <6; c++) { putnormalchar(x[c],y,message[ chara[c] ] ); //在每个字符后绘制一条关闭的像素线,否则字符之间的间隙会留下前一个字符的像素 for (byte yy =0; yy <8; yy ++) { plot(x[c] + 5, 年, 0); } //每个字符位置减去一个 x[c] =x[c] - 1; } //重置一个字符,如果它离开屏幕为 (byte i =0; i <=5; i++) { if (x[i] <-5 ) { x[i] =31;字符[i] =p; p++; } } }}//display_date - 用闪烁的光标效果打印星期、日期和月份void display_date(){ cls(); //从DS1307字节中读取日期 dow =rtc[3]; // 星期几 =星期日字节日期 =rtc[4];字节月 =rtc[5] - 1; //要在显示器上打印的月份名称数组。有些被缩短了,因为我们只有 8 个字符来玩 char 月份名称[12][9] ={ "January", "February", "March", "April", "May", "June", "July" , "八月", "九月", "十月", "十一月", "十二月" }; //打印日期名称 //获取以像素为单位的文本长度,这样我们就可以通过划分剩余的像素 b2 并将其用作偏移字节 len =0;而(天满[dow][len]){len++; };字节偏移量 =(31 - ((len-1)*4)) / 2; //我们的偏移量使文本居中 //打印名称 int i =0; while(daysfull[dow][i]) { puttinychar((i*4) + offset , 1, daysfull[dow][i]);我++; } 延迟(1000);淡入淡出(); cls(); // 打印日期数字 char buffer[3]; itoa(日期,缓冲区,10);偏移=10; //offset to centre text if 3 chars - e.g. 3rd // first work out date 2 letter suffix - eg st, nd, rd, th etc // char suffix[4][3]={"st", "nd", "rd", "th" }; is defined at top of code byte s =3; if(date ==1 || date ==21 || date ==31) { s =0; } else if (date ==2 || date ==22) { s =1; } else if (date ==3 || date ==23) { s =2; } //print the 1st date number puttinychar(0+offset, 1, buffer[0]); //if date is under 10 - then we only have 1 digit so set positions of sufix etc one character nearer byte suffixposx =4; //if date over 9 then print second number and set xpos of suffix to be 1 char further away if (date> 9){ suffixposx =8; puttinychar(4+offset, 1, buffer[1]); offset =8; //offset to centre text if 4 chars } //print the 2 suffix characters puttinychar(suffixposx+offset, 1, suffix[s][0]); puttinychar(suffixposx+4+offset, 1, suffix[s][1]);延迟(1000); fade_down(); //print the month name //get length of text in pixels, that way we can centre it on the display by divindin the remaining pixels b2 and using that as an offset len =0; while(monthnames[month][len]) { len++; }; offset =(31 - ((len-1)*4)) / 2; //our offset to centre up the text i =0; while(monthnames[month][i]) { puttinychar((i*4) +offset, 1, monthnames[month][i]);我++; } 延迟(1000); fade_down();}//dislpay menu to change the clock modevoid switch_mode() { //remember mode we are in. We use this value if we go into settings mode, so we can change back from settings mode (6) to whatever mode we were in. old_mode =clock_mode; char* modes[] ={ "Basic", "Small", "Slide", "Words", "Setup" }; byte next_clock_mode; byte firstrun =1; //loop waiting for button (timeout after 35 loops to return to mode X) for (int count =0; count <35; count++) { //if user hits button, change the clock_mode if (buttonA.uniquePress() || firstrun ==1) { count =0; cls(); if (firstrun ==0) { clock_mode++; } if (clock_mode> NUM_DISPLAY_MODES + 1 ) { clock_mode =0; } //print arrown and current clock_mode name on line one and print next clock_mode name on line two char str_top[9]; //strcpy (str_top, "-"); strcpy (str_top, modes[clock_mode]); next_clock_mode =clock_mode + 1; if (next_clock_mode> NUM_DISPLAY_MODES + 1 ) { next_clock_mode =0; } byte i =0; while (str_top[i]) { putnormalchar(i * 6, 0, str_top[i]);我++; } firstrun =0; } delay(50); }}//run clock main loop as long as run_mode returns truebyte run_mode() { //if random mode is on... check the hour when we change mode. if (random_mode) { //if hour value in change mode time =hours. then reurn false =i.e. exit mode. if (change_mode_time ==rtc[2]) { //set the next random clock mode and time to change it set_next_random(); //exit the current mode.返回0; } } //else return 1 - keep running in this mode return 1;}//set the next hour the clock will change mode when random mode is onvoid set_next_random() { //set the next hour the clock mode will change - current time plus 1 - 4 hours get_time(); change_mode_time =rtc[2] + random (1, 5); //if change_mode_time now happens to be over 23, then set it to between 1 and 3am if (change_mode_time> 23) { change_mode_time =random (1, 4); } //set the new clock mode clock_mode =random(0, NUM_DISPLAY_MODES + 1); //pick new random clock mode}//dislpay menu to change the clock settingsvoid setup_menu() { char* set_modes[] ={ "Rndom", "24 Hr","Set", "Brght", "Exit"}; if (ampm ==0) { set_modes[1] =("12 Hr"); } byte setting_mode =0; byte next_setting_mode; byte firstrun =1; //loop waiting for button (timeout after 35 loops to return to mode X) for(int count=0; count <35; count++) { //if user hits button, change the clock_mode if(buttonA.uniquePress() || firstrun ==1){ count =0; cls(); if (firstrun ==0) { setting_mode++; } if (setting_mode> NUM_SETTINGS_MODES) { setting_mode =0; } //print arrown and current clock_mode name on line one and print next clock_mode name on line two char str_top[9]; strcpy (str_top, set_modes[setting_mode]); next_setting_mode =setting_mode + 1; if (next_setting_mode> NUM_SETTINGS_MODES) { next_setting_mode =0; } byte i =0; while(str_top[i]) { putnormalchar(i*6, 0, str_top[i]);我++; } firstrun =0; } delay(50); } //pick the mode switch(setting_mode){ case 0:set_random();休息; case 1:set_ampm();休息; case 2:set_time();休息; case 3:set_intensity();休息; case 4://exit menu break; } //change the clock from mode 6 (settings) back to the one it was in before clock_mode=old_mode;}//toggle random mode - pick a different clock mode every few hoursvoid set_random(){ cls(); char text_a[9] ="Off"; char text_b[9] ="On"; byte i =0; //if random mode is on, turn it off if (random_mode){ //turn random mode off random_mode =0; //print a message on the display while(text_a[i]) { putnormalchar((i*6), 0, text_a[i]);我++; } } else { //turn randome mode on. random_mode =1; //set hour mode will change set_next_random(); //print a message on the display while(text_b[i]) { putnormalchar((i*6), 0, text_b[i]);我++; } } delay(1500); //leave the message up for a second or so}//set 12 or 24 hour clockvoid set_ampm() { // AM/PM or 24 hour clock mode - flip the bit (makes 0 into 1, or 1 into 0 for ampm mode) ampm =(ampm ^ 1); cls();}//change screen intensityintensityvoid set_intensity() { cls(); byte i =0; char text[7] ="Bright"; while(text[i]) { puttinychar((i*4)+4, 0, text[i]);我++; } //wait for button input while (!buttonA.uniquePress()) { levelbar (0,6,(intensity*2)+2,2); //display the intensity level as a bar while (buttonB.isPressed()) { if(intensity ==15) { intensity =0; cls (); } else { intensity++; } //print the new value i =0; while(text[i]) { puttinychar((i*4)+4, 0, text[i]);我++; } //display the intensity level as a bar levelbar (0,6,(intensity*2)+2,2); //change the brightness setting on the displays for (byte address =0; address <4; address++) { lc.setIntensity(address, intensity); } 延迟(150); } }}// display a horizontal bar on the screen at offset xposr by ypos with height and width of xbar, ybarvoid levelbar (byte xpos, byte ypos, byte xbar, byte ybar) { for (byte x =0; x  
LibrariesArduino
无预览(仅限下载)。

示意图


制造工艺

  1. LED 定序器
  2. 布谷鸟钟
  3. MATLAB - 矩阵
  4. 迷你 Boss 战
  5. 主时钟
  6. 柏林时钟
  7. 模拟式 LED POV 时钟
  8. LED Matrix + Motion Sensor Door Display [Arduino Holiday]
  9. 8x LED 声音照明
  10. Arduino Quadruped
  11. 热胶 LED 矩阵灯
  12. 无需驱动程序和库即可控制 8x8 LED 矩阵