nille 发表于 2014-11-7 16:01:38

二进制时钟(Matrix版)

nille程晨顺手智造

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


模块就选用DFRobot的可编程8x8点阵模块,如下图所示

该模块内部使用的是Leonardo的ATmega32U4,所以我们通过模块上的MicroUSB 端口就能够直接烧写程序,另外模块内置了3.7V/140mAH的锂电池,这样就省去了我们单独添加电源的问题。
显示方面,本人对于整个表面64个LED的规划如下


第一排从右往左的5个灯用来表示小时时间的二进制显示,5个LED可表示的最大数是31,我们只需要显示到23就可以了;第二排从右往左的6个灯用来表示分钟时间的二进制显示,6个LED可表示的最大数是63,我们只需要显示到59就可以了;第三行作为分割行是不现实任何东西的;再往下的5行能够显示两位数字,本人就用这块区域来显示文字化的两位数,显示的内容要依据第一、二排的第一个LED。当第一排的第一个LED点亮时,下方的数字区就显示的是小时数;当第二排的第一个LED点亮时,下方的数字区就显示的是分钟数。第一、二排的LED同一时间只有一个点亮,而切换显示内容的功能我们交给了模块上的轻触按键。
在DF的网站上找到模块相对应的原理图,根据矩阵LED、轻触按键和控制板的连接关系定义变量及数组如下。
byte row = {9, 8, 4, A3, 3, 10, 11, 6};
byte col = {2, 7, A5, 5, 13, A4, 12, A2};
//触碰按键接在A1口
int pushButton = A1;
另外我们需要定义一个8*8的矩阵,用来保存显示在LED矩阵上的内容。
byte picture=
{
{0,0,0,1,1,1,0,1},
{0,0,0,1,0,1,1,1},
{0,0,0,0,0,0,0,0},
{0,1,1,0,0,1,1,0},
{1,0,0,0,1,0,0,0},
{1,0,0,0,1,0,0,0},
{0,1,1,0,0,1,1,0},
{0,0,0,0,0,0,0,0}
};

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

程序的主体如下。
void setup(void)
{
//初始化连接LED矩阵的管脚
LED_Init();
//初始化连接轻触按键的管脚
pinMode(pushButton, INPUT);
oldTime=millis();
//串口波特率为9600,用于校对时间
Serial.begin(9600);
}

void loop(void)
{
//控制显示区
Draw_Pic();

//利用函数millis()和变量oldTime来定时,这里我的定时时间为1秒,即1000毫秒
if(millis()-oldTime>=1000)
{
    oldTime = millis();

    //秒的变量加1
    valueS = valueS + 1;
   
    //判断秒的变量是否达到60秒,如果达到60秒就要进位,将分钟数加1
    if(valueS >= 60)
    {
      valueS = 0;
      valueM = valueM + 1;

      //判断分钟的变量是否达到60,如果达到60就要进位,将小时数加1
      if(valueM>=60)
      {
      valueM = 0;
      valueH = valueH + 1;

      //判断小时的变量是否达到24
      if(valueH>=24)
      {
         valueH = 0;
      }
      }

      //以二进制方式显示小时和分钟的值
      Show_Bin(valueH,valueM);
    }

    //判断轻触按键的状态,以决定数字显示区是显示小时还是分钟
    if(digitalRead(pushButton))
    {
      //如果按键抬起则显示分钟值,同时切换左上角的显示指示
      picture=0;
      picture=1;
      Show_Num(valueM);
    }
    else
    {
    //如果按键按下则显示小时值,同时切换左上角的显示指示
    picture=0;
    picture=1;
    Show_Num(valueH);
    }   
}

//查询是否收到数据来设定时间
setTime();
}

以下是各个函数的具体实现代码,都是一些基础内容,包括LED矩阵的显示,数字的二进制处理、串行数据的接收处理等,有些内容可以参考前两个版本的binWatch,有些内容可以参考Arduino的基础例程,这里就不详细介绍了。
这里要说明一点,在程序中,除了函数Draw_Pic,其他显示的操作都是对数组picture的修改,实际上程序的执行过程是修改了数组picture中的某些变量,然后在函数Draw_Pic中调用数组picture来实现显示。
/////////////////////////////////////
//设置时间,格式为HxxMxx,xx为两位数字
/////////////////////////////////////
void setTime(void)
{
if( Serial.available())
{
    if('H'== Serial.read())
    {
      while(!Serial.available());//hour
      int recData = Serial.read() ;

      while(!Serial.available());//min
      valueH = (recData - 0x30)*10+Serial.read()-0x30;
      
      Serial.println("Hour set ok");
    }
    if('M'== Serial.read())
    {
      while(!Serial.available());//hour
      int recData = Serial.read() ;

      while(!Serial.available());//min
      valueM = (recData - 0x30)*10+Serial.read()-0x30;
      
      Serial.println("Min set ok");
    }
    Show_Bin(valueH,valueM);
}
}

/////////////////////////////////////
//连接LED矩阵的管脚初始化
/////////////////////////////////////
void LED_Init(void)
{
for (byte thisPin = 0; thisPin < 8; thisPin++)
{
    pinMode(col, OUTPUT);
    pinMode(row, OUTPUT);
    digitalWrite(row, LOW);
    digitalWrite(col, HIGH);
}
}

/////////////////////////////////////
//根据变量数组picture绘制显示区
/////////////////////////////////////
void Draw_Pic()
{
    for (byte thisRow = 0; thisRow < 8; thisRow++)
    {
      pinMode(row, OUTPUT);
      digitalWrite(row, HIGH);
      for (byte thisCol = 0; thisCol < 8; thisCol++)
      {
             pinMode(col, OUTPUT);
             digitalWrite(col, !picture);
             if (picture == HIGH)
             {
                delayMicroseconds(80);
                digitalWrite(col, HIGH);
             }
      }
      digitalWrite(row, LOW);
    }
}

/////////////////////////////////////
//以二进制方式显示时间
/////////////////////////////////////
void Show_Bin(int _hour,int _min)
{
int _showTemp = _hour;
int i;
for(i=7;i>2;i--)
{
    if(_showTemp%2 == 1)
    {
      picture=1;
    }
    else
    {
      picture=0;
    }
    _showTemp = _showTemp/2;
}

_showTemp = _min;
for(i=7;i>1;i--)
{
    if(_showTemp%2 == 1)
    {
      picture=1;
    }
    else
    {
      picture=0;
    }
    _showTemp = _showTemp/2;
}
}

/////////////////////////////////////
//数字区显示
/////////////////////////////////////
void Show_Num(int _value)
{
for (int i = 0; i < 8; i++)
{
    picture = 0;
    picture = 0;
    picture = 0;
    picture = 0;
    picture = 0;
}
switch(_value/10)
{
    case 0:
      break;
    case 1:
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      break;
    case 2:
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      break;
    case 3:
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      break;
    case 4:
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      break;
    case 5:
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      break;
    case 6:
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      break;
    case 7:
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      break;
    case 8:
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      break;
    case 9:
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      break;
    default:
      break;
}
switch(_value%10)
{
    case 0:
      if(_value!=0)
      {
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      }   
      break;
    case 1:
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      break;
    case 2:
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      break;
    case 3:
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      break;
    case 4:
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      break;
    case 5:
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      break;
    case 6:
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      break;
    case 7:
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      break;
    case 8:
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      break;
    case 9:
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      picture=1;
      break;
    default:
      break;
}
}

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


当模块侧面的按键按下是数字显示区就显示的是小时,弹起的话就显示的是分钟。要是想锻炼一下算术的话那就自己数点算时间吧。
带着闪闪发光的手表,我心想要不要自己也尝试设计一下这个手表的外壳,之前在翻译《解析3D打印机》的书中还是学到一些3D建模的知识。
建模之前先要确定一下模块的大小,在DFRobot的网站上能够查到显示模块的大小是32mm×32mm×16mm,手表外壳壁厚2mm。有了这两个基本数据就可以开始建模了,建模软件方面本人选择了被Google收购的SketchUp。
打开SketchUp后,在模板中选择“产品设计与木器加工-毫米”这一项。


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


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


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


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


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


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


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


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


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


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


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


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


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


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


为了能够使用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选项。


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



打印完成后,在两个耳朵上缝上一段粘扣。


最后把两个粘扣粘在一个表带上,Matrix版的binWatch就完成了。一起来作一个吧






Ricky 发表于 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ㄒ)/~~只能说不明觉厉..
页: [1]
查看完整版本: 二进制时钟(Matrix版)