查看: 5147|回复: 4

二进制时钟(Matrix版)

[复制链接]
本帖最后由 nille 于 2014-11-11 11:06 编辑

nille程晨  顺手智造


之前的手工版binWatch被很多人吐槽说不好看,我这个理工男的审美真是伤不起呀。经受了多次精神刺激之后,本人决定再做一个版本的binWatch——Matrix版。

IMG_20141104_160708.jpg

模块就选用DFRobot可编程8x8点阵模块,如下图所示
图片1.jpg

该模块内部使用的是LeonardoATmega32U4,所以我们通过模块上的MicroUSB 端口就能够直接烧写程序,另外模块内置了3.7V/140mAH的锂电池,这样就省去了我们单独添加电源的问题。

显示方面,本人对于整个表面64LED的规划如下

binWatch.jpg

第一排从右往左的5个灯用来表示小时时间的二进制显示,5LED可表示的最大数是31,我们只需要显示到23就可以了;
第二排从右往左的6个灯用来表示分钟时间的二进制显示,6LED可表示的最大数是63,我们只需要显示到59就可以了;
第三行作为分割行是不现实任何东西的;
再往下的5行能够显示两位数字,本人就用这块区域来显示文字化的两位数,显示的内容要依据第一、二排的第一个LED。当第一排的第一个LED点亮时,下方的数字区就显示的是小时数;当第二排的第一个LED点亮时,下方的数字区就显示的是分钟数。第一、二排的LED同一时间只有一个点亮,而切换显示内容的功能我们交给了模块上的轻触按键。

DF的网站上找到模块相对应的原理图,根据矩阵LED、轻触按键和控制板的连接关系定义变量及数组如下。

  1. byte row[8] = {9, 8, 4, A3, 3, 10, 11, 6};
  2. byte col[8] = {2, 7, A5, 5, 13, A4, 12, A2};
  3. //触碰按键接在A1口
  4. int pushButton = A1;
复制代码

另外我们需要定义一个8*8的矩阵,用来保存显示在LED矩阵上的内容。

  1. byte picture[8][8]=
  2. {
  3.   {0,0,0,1,1,1,0,1},
  4.   {0,0,0,1,0,1,1,1},
  5.   {0,0,0,0,0,0,0,0},
  6.   {0,1,1,0,0,1,1,0},
  7.   {1,0,0,0,1,0,0,0},
  8.   {1,0,0,0,1,0,0,0},
  9.   {0,1,1,0,0,1,1,0},
  10.   {0,0,0,0,0,0,0,0}
  11. };
复制代码


接着再定义如下几个变量,其中oldTime用来和millis()配合实现一个定时的功能,而变量valueHvalueMvalueS则是用来保存小时、分钟、秒3个量。

  1. unsigned long oldTime;
  2. int valueH,valueM,valueS;
复制代码


程序的主体如下。

  1. void setup(void)
  2. {
  3.   //初始化连接LED矩阵的管脚
  4.   LED_Init();
  5.   //初始化连接轻触按键的管脚
  6.   pinMode(pushButton, INPUT);
  7.   oldTime=millis();
  8.   //串口波特率为9600,用于校对时间
  9.   Serial.begin(9600);
  10. }
  11. void loop(void)
  12. {
  13.   //控制显示区
  14.   Draw_Pic();
  15.   
  16.   //利用函数millis()和变量oldTime来定时,这里我的定时时间为1秒,即1000毫秒
  17.   if(millis()-oldTime>=1000)
  18.   {
  19.     oldTime = millis();
  20.     //秒的变量加1
  21.     valueS = valueS + 1;
  22.    
  23.     //判断秒的变量是否达到60秒,如果达到60秒就要进位,将分钟数加1
  24.     if(valueS >= 60)
  25.     {
  26.       valueS = 0;
  27.       valueM = valueM + 1;
  28.       //判断分钟的变量是否达到60,如果达到60就要进位,将小时数加1
  29.       if(valueM>=60)
  30.       {
  31.         valueM = 0;
  32.         valueH = valueH + 1;
  33.         //判断小时的变量是否达到24
  34.         if(valueH>=24)
  35.         {
  36.            valueH = 0;
  37.         }
  38.       }
  39.       //以二进制方式显示小时和分钟的值
  40.       Show_Bin(valueH,valueM);
  41.     }
  42.     //判断轻触按键的状态,以决定数字显示区是显示小时还是分钟
  43.     if(digitalRead(pushButton))
  44.     {
  45.       //如果按键抬起则显示分钟值,同时切换左上角的显示指示
  46.       picture[0][0]=0;
  47.       picture[1][0]=1;
  48.       Show_Num(valueM);
  49.     }
  50.     else
  51.     {
  52.     //如果按键按下则显示小时值,同时切换左上角的显示指示
  53.     picture[1][0]=0;
  54.     picture[0][0]=1;
  55.     Show_Num(valueH);
  56.     }   
  57.   }
  58.   //查询是否收到数据来设定时间
  59.   setTime();
  60. }
复制代码


以下是各个函数的具体实现代码,都是一些基础内容,包括LED矩阵的显示,数字的二进制处理、串行数据的接收处理等,有些内容可以参考前两个版本的binWatch,有些内容可以参考Arduino的基础例程,这里就不详细介绍了。

这里要说明一点,在程序中,除了函数Draw_Pic,其他显示的操作都是对数组picture的修改,实际上程序的执行过程是修改了数组picture中的某些变量,然后在函数Draw_Pic中调用数组picture来实现显示。

  1. /////////////////////////////////////
  2. //设置时间,格式为HxxMxx,xx为两位数字
  3. /////////////////////////////////////
  4. void setTime(void)
  5. {
  6.   if( Serial.available())
  7.   {
  8.     if('H'== Serial.read())
  9.     {
  10.       while(!Serial.available());  //hour
  11.       int recData = Serial.read() ;
  12.       while(!Serial.available());  //min
  13.       valueH = (recData - 0x30)*10+Serial.read()-0x30;
  14.       
  15.       Serial.println("Hour set ok");
  16.     }
  17.     if('M'== Serial.read())
  18.     {
  19.       while(!Serial.available());  //hour
  20.       int recData = Serial.read() ;
  21.       while(!Serial.available());  //min
  22.       valueM = (recData - 0x30)*10+Serial.read()-0x30;
  23.       
  24.       Serial.println("Min set ok");
  25.     }
  26.     Show_Bin(valueH,valueM);
  27.   }
  28. }
  29. /////////////////////////////////////
  30. //连接LED矩阵的管脚初始化
  31. /////////////////////////////////////
  32. void LED_Init(void)
  33. {
  34.   for (byte thisPin = 0; thisPin < 8; thisPin++)
  35.   {
  36.     pinMode(col[thisPin], OUTPUT);
  37.     pinMode(row[thisPin], OUTPUT);
  38.     digitalWrite(row[thisPin], LOW);
  39.     digitalWrite(col[thisPin], HIGH);  
  40.   }
  41. }
  42. /////////////////////////////////////
  43. //根据变量数组picture绘制显示区
  44. /////////////////////////////////////
  45. void Draw_Pic()
  46. {
  47.     for (byte thisRow = 0; thisRow < 8; thisRow++)
  48.     {
  49.         pinMode(row[thisRow], OUTPUT);
  50.         digitalWrite(row[thisRow], HIGH);
  51.         for (byte thisCol = 0; thisCol < 8; thisCol++)
  52.         {
  53.              pinMode(col[thisCol], OUTPUT);
  54.              digitalWrite(col[thisCol], !picture[thisRow][thisCol]);
  55.              if (picture[thisRow][thisCol] == HIGH)
  56.              {
  57.                 delayMicroseconds(80);
  58.                 digitalWrite(col[thisCol], HIGH);
  59.              }
  60.         }
  61.         digitalWrite(row[thisRow], LOW);
  62.     }
  63. }
  64. /////////////////////////////////////
  65. //以二进制方式显示时间
  66. /////////////////////////////////////
  67. void Show_Bin(int _hour,int _min)
  68. {
  69.   int _showTemp = _hour;
  70.   int i;
  71.   for(i=7;i>2;i--)
  72.   {
  73.     if(_showTemp%2 == 1)
  74.     {
  75.       picture[0][i]=1;
  76.     }
  77.     else
  78.     {
  79.       picture[0][i]=0;
  80.     }
  81.     _showTemp = _showTemp/2;
  82.   }
  83.   
  84.   _showTemp = _min;
  85.   for(i=7;i>1;i--)
  86.   {
  87.     if(_showTemp%2 == 1)
  88.     {
  89.       picture[1][i]=1;
  90.     }
  91.     else
  92.     {
  93.       picture[1][i]=0;
  94.     }
  95.     _showTemp = _showTemp/2;
  96.   }
  97. }
  98. /////////////////////////////////////
  99. //数字区显示
  100. /////////////////////////////////////
  101. void Show_Num(int _value)
  102. {
  103.   for (int i = 0; i < 8; i++)
  104.   {
  105.     picture[3][i] = 0;
  106.     picture[4][i] = 0;
  107.     picture[5][i] = 0;
  108.     picture[6][i] = 0;
  109.     picture[7][i] = 0;
  110.   }
  111.   switch(_value/10)
  112.   {
  113.     case 0:
  114.       break;
  115.     case 1:
  116.       picture[3][2]=1;
  117.       picture[4][2]=1;
  118.       picture[5][2]=1;
  119.       picture[6][2]=1;
  120.       picture[7][2]=1;
  121.       picture[4][1]=1;
  122.       picture[7][1]=1;
  123.       picture[7][3]=1;
  124.       break;
  125.     case 2:
  126.       picture[3][1]=1;
  127.       picture[3][2]=1;
  128.       picture[3][3]=1;
  129.       picture[4][3]=1;
  130.       picture[5][1]=1;
  131.       picture[5][2]=1;
  132.       picture[5][3]=1;
  133.       picture[6][1]=1;
  134.       picture[7][1]=1;
  135.       picture[7][2]=1;
  136.       picture[7][3]=1;
  137.       break;
  138.     case 3:
  139.       picture[3][1]=1;
  140.       picture[3][2]=1;
  141.       picture[3][3]=1;
  142.       picture[4][3]=1;
  143.       picture[5][1]=1;
  144.       picture[5][2]=1;
  145.       picture[5][3]=1;
  146.       picture[6][3]=1;
  147.       picture[7][1]=1;
  148.       picture[7][2]=1;
  149.       picture[7][3]=1;
  150.       break;
  151.     case 4:
  152.       picture[3][1]=1;
  153.       picture[4][1]=1;
  154.       picture[3][3]=1;
  155.       picture[4][3]=1;
  156.       picture[5][1]=1;
  157.       picture[5][2]=1;
  158.       picture[5][3]=1;
  159.       picture[6][3]=1;
  160.       picture[7][3]=1;
  161.       break;
  162.     case 5:
  163.       picture[3][1]=1;
  164.       picture[3][2]=1;
  165.       picture[3][3]=1;
  166.       picture[4][1]=1;
  167.       picture[5][1]=1;
  168.       picture[5][2]=1;
  169.       picture[5][3]=1;
  170.       picture[6][3]=1;
  171.       picture[7][1]=1;
  172.       picture[7][2]=1;
  173.       picture[7][3]=1;
  174.       break;
  175.     case 6:
  176.       picture[3][1]=1;
  177.       picture[3][2]=1;
  178.       picture[3][3]=1;
  179.       picture[4][1]=1;
  180.       picture[5][1]=1;
  181.       picture[5][2]=1;
  182.       picture[5][3]=1;
  183.       picture[6][1]=1;
  184.       picture[6][3]=1;
  185.       picture[7][1]=1;
  186.       picture[7][2]=1;
  187.       picture[7][3]=1;
  188.       break;
  189.     case 7:
  190.       picture[3][1]=1;
  191.       picture[3][2]=1;
  192.       picture[3][3]=1;
  193.       picture[4][3]=1;
  194.       picture[4][1]=1;
  195.       picture[5][3]=1;
  196.       picture[6][3]=1;
  197.       picture[7][3]=1;
  198.       break;
  199.     case 8:
  200.       picture[3][1]=1;
  201.       picture[3][2]=1;
  202.       picture[3][3]=1;
  203.       picture[4][1]=1;
  204.       picture[4][3]=1;
  205.       picture[5][1]=1;
  206.       picture[5][2]=1;
  207.       picture[5][3]=1;
  208.       picture[6][1]=1;
  209.       picture[6][3]=1;
  210.       picture[7][1]=1;
  211.       picture[7][2]=1;
  212.       picture[7][3]=1;
  213.       break;
  214.     case 9:
  215.       picture[3][1]=1;
  216.       picture[3][2]=1;
  217.       picture[3][3]=1;
  218.       picture[4][3]=1;
  219.       picture[4][1]=1;
  220.       picture[5][1]=1;
  221.       picture[5][2]=1;
  222.       picture[5][3]=1;
  223.       picture[6][3]=1;
  224.       picture[7][1]=1;
  225.       picture[7][2]=1;
  226.       picture[7][3]=1;
  227.       break;
  228.     default:
  229.       break;
  230.   }
  231.   switch(_value%10)
  232.   {
  233.     case 0:
  234.       if(_value!=0)
  235.       {
  236.         picture[3][5]=1;
  237.         picture[3][6]=1;
  238.         picture[3][7]=1;
  239.         picture[4][5]=1;
  240.         picture[4][7]=1;
  241.         picture[5][5]=1;
  242.         picture[5][7]=1;
  243.         picture[6][5]=1;
  244.         picture[6][7]=1;
  245.         picture[7][5]=1;
  246.         picture[7][6]=1;
  247.         picture[7][7]=1;
  248.       }   
  249.       break;
  250.     case 1:
  251.       picture[3][6]=1;
  252.       picture[4][6]=1;
  253.       picture[5][6]=1;
  254.       picture[6][6]=1;
  255.       picture[7][6]=1;
  256.       picture[4][5]=1;
  257.       picture[7][5]=1;
  258.       picture[7][7]=1;
  259.       break;
  260.     case 2:
  261.       picture[3][5]=1;
  262.       picture[3][6]=1;
  263.       picture[3][7]=1;
  264.       picture[4][7]=1;
  265.       picture[5][5]=1;
  266.       picture[5][6]=1;
  267.       picture[5][7]=1;
  268.       picture[6][5]=1;
  269.       picture[7][5]=1;
  270.       picture[7][6]=1;
  271.       picture[7][7]=1;
  272.       break;
  273.     case 3:
  274.       picture[3][5]=1;
  275.       picture[3][6]=1;
  276.       picture[3][7]=1;
  277.       picture[4][7]=1;
  278.       picture[5][5]=1;
  279.       picture[5][6]=1;
  280.       picture[5][7]=1;
  281.       picture[6][7]=1;
  282.       picture[7][5]=1;
  283.       picture[7][6]=1;
  284.       picture[7][7]=1;
  285.       break;
  286.     case 4:
  287.       picture[3][5]=1;
  288.       picture[4][5]=1;
  289.       picture[3][7]=1;
  290.       picture[4][7]=1;
  291.       picture[5][5]=1;
  292.       picture[5][6]=1;
  293.       picture[5][7]=1;
  294.       picture[6][7]=1;
  295.       picture[7][7]=1;
  296.       break;
  297.     case 5:
  298.       picture[3][5]=1;
  299.       picture[3][6]=1;
  300.       picture[3][7]=1;
  301.       picture[4][5]=1;
  302.       picture[5][5]=1;
  303.       picture[5][6]=1;
  304.       picture[5][7]=1;
  305.       picture[6][7]=1;
  306.       picture[7][5]=1;
  307.       picture[7][6]=1;
  308.       picture[7][7]=1;
  309.       break;
  310.     case 6:
  311.       picture[3][5]=1;
  312.       picture[3][6]=1;
  313.       picture[3][7]=1;
  314.       picture[4][5]=1;
  315.       picture[5][5]=1;
  316.       picture[5][6]=1;
  317.       picture[5][7]=1;
  318.       picture[6][5]=1;
  319.       picture[6][7]=1;
  320.       picture[7][5]=1;
  321.       picture[7][6]=1;
  322.       picture[7][7]=1;
  323.       break;
  324.     case 7:
  325.       picture[3][5]=1;
  326.       picture[3][6]=1;
  327.       picture[3][7]=1;
  328.       picture[4][7]=1;
  329.       picture[4][5]=1;
  330.       picture[5][7]=1;
  331.       picture[6][7]=1;
  332.       picture[7][7]=1;
  333.       break;
  334.     case 8:
  335.       picture[3][5]=1;
  336.       picture[3][6]=1;
  337.       picture[3][7]=1;
  338.       picture[4][5]=1;
  339.       picture[4][7]=1;
  340.       picture[5][5]=1;
  341.       picture[5][6]=1;
  342.       picture[5][7]=1;
  343.       picture[6][5]=1;
  344.       picture[6][7]=1;
  345.       picture[7][5]=1;
  346.       picture[7][6]=1;
  347.       picture[7][7]=1;
  348.       break;
  349.     case 9:
  350.       picture[3][5]=1;
  351.       picture[3][6]=1;
  352.       picture[3][7]=1;
  353.       picture[4][7]=1;
  354.       picture[4][5]=1;
  355.       picture[5][5]=1;
  356.       picture[5][6]=1;
  357.       picture[5][7]=1;
  358.       picture[6][7]=1;
  359.       picture[7][5]=1;
  360.       picture[7][6]=1;
  361.       picture[7][7]=1;
  362.       break;
  363.     default:
  364.       break;
  365.   }
  366. }
复制代码


程序完成后,还需要想个办法把这个8x8点阵模块固定在手腕上,makerpapa的建伟同学帮我设计了一个类似于手表的外壳,整体完成之后如下图所示。图中所显示的时间是1249,第一排LED从右往左,点亮的LED是第3个和第4个,所以就是23-1+2(4-1=12。分钟的量我们直接看数字显示区就好。而binWatch表带是用本人用粘扣手工缝纫而成的。



当模块侧面的按键按下是数字显示区就显示的是小时,弹起的话就显示的是分钟。要是想锻炼一下算术的话那就自己数点算时间吧。

带着闪闪发光的手表,我心想要不要自己也尝试设计一下这个手表的外壳,之前在翻译《解析3D打印机》的书中还是学到一些3D建模的知识。

建模之前先要确定一下模块的大小,在DFRobot的网站上能够查到显示模块的大小是32mm×32mm×16mm,手表外壳壁厚2mm。有了这两个基本数据就可以开始建模了,建模软件方面本人选择了被Google收购的SketchUp

打开SketchUp后,在模板中选择“产品设计与木器加工-毫米”这一项。

图片2.jpg

在软件界面中打开“大工具集”,我们在使用时直接用鼠标选择工具集中的相应工具就可以了,比如绘制方形、圆形,拉伸、移动等等。

图片3.jpg

首先选择绘制方形的工具,因为模块的尺寸是32mm×32mm,而壁厚是2mm,所以这个方形的大小应该是36mm×36mm32+2+2),软件中可以直接在右下角的尺寸框中输入相应的尺寸,这里我们输入3636就得到了一个36mm×36mm的正方形

图片4.jpg

接着使用推/拉工具将正方形拉高,模块的高度是16mm,我们留2mm的富余,拉伸高度定位18mm,同样的我们直接在右下角的尺寸框中输入18就可以了。此时就得到了一个36mm×36mm×18mm的立方体

图片5.jpg

然后使用偏移工具将上表面的正方形边沿向内偏移2mm,作为外壳的厚度。

图片6.jpg

还是使用推/拉工具将刚才向内偏移2mm的正方形向下推18mm,这样就得到了一个只有外壳的方框。我们的显示模块到时就放在这个方框中。

图片7.jpg

第二步就是要绘制外壳表面两侧用来连接表带的耳朵。在方框一侧的底部绘制一个36mm×4mm×2mm的长方体。

图片8.jpg

使用偏移工具将长方体的顶面边沿向内偏移2mm,因为顶面的宽度本身只有4mm,所以偏移之后会得到一条线段,线段的两个端点距离两边的垂直距离为2mm

图片9.jpg

使用线条工具将线段的两个端点用垂直线段连接到外壳的外表面上,咱耳朵的上表面形成一个小的长方形。

图片10.jpg

同样使用推/拉工具将这个面向下推2mm,形成一个开口。这样一侧的耳朵就做好了。

图片11.jpg

用同样的方式在另外一次也制作一个耳朵。

图片12.jpg

此时我们的外壳整体基本上就算完成了,最后一步是要在外壳没有耳朵的一侧留一个开口。大家注意的话会发现,显示模块上的开关和轻触按键是在32mm×32mm的范围之外的,同时我们希望模块的MicroUSB 端口能漏在外面,方便下载程序和设定时间。外壳上的开口就是要把这些东西漏出来。我们用卡尺测量一下MicroUSB 端口的厚度,不到4mm,我们就按照4mm来设计。

IMG_20141104_143257.jpg

在外壳方框的内壁上,在距离底边4mm位置画一条直线。

图片13.jpg

使用推/拉工具将下方形成的小的长方体向外推2mm形成一个开口。

图片14.jpg

最后用擦除工具将方框和耳朵连接处之间的线段擦除掉,这一点非常重要,这样耳朵和方框才能连接成统一的整体,完成后如下图所示。此时我们的binWatch外壳建模就完成了。

图片15.jpg

为了能够使用3D打印机打印我们的手表外壳,需要将这个模型导成STL格式。在SketchUp中的文件格式是不适用于3D打印的,所以我们需要给SketchUp安装一个插件进行格式转换。这是由Nathan Bromham写的格式转换插件,可以从Guitar-List的网站www.guitar-list.com/download-software/convert-sketchup-skp-files-dxf-or-stl上下载,下载安装完成后,SketchUp中在Tool菜单下应该能够找到Export to DXF or STL选项。

图片16.jpg

我们在Cura中打开导出后的stl文件,如下图所示。然后再将这个stl文件转换成3D打印机可用的g-code文件,就能够使用3D打印机打印了。

本人制作的stl文件 binWatchSTL.rar (1002 Bytes, 下载次数: 752)



Ricky  NPC

发表于 2014-11-7 22:24:17

这货我喜欢。。。。做个玩 。
回复

使用道具 举报

heinau  高级技匠

发表于 2014-11-21 10:52:48

教程好专业!
哈哈哈这次的手表比上次的好看多啦~~理工男的审美还是可以抢救一下的嗯
回复

使用道具 举报

nille  高级技师
 楼主|

发表于 2014-11-21 12:49:47

heinau 发表于 2014-11-21 10:52
教程好专业!
哈哈哈这次的手表比上次的好看多啦~~理工男的审美还是可以抢救一下的嗯 ...

提供模型下载,也可以自己画一个。
回复

使用道具 举报

Ash  管理员

发表于 2014-11-21 16:59:53

对于二进制这个东西 /(ㄒoㄒ)/~~  只能说不明觉厉..
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

为本项目制作心愿单
购买心愿单
心愿单 编辑
[[wsData.name]]

硬件清单

  • [[d.name]]
btnicon
我也要做!
点击进入购买页面
上海智位机器人股份有限公司 沪ICP备09038501号-4

© 2013-2021 Comsenz Inc. Powered by Discuz! X3.4 Licensed

mail