红红纪念日记录器(原创)
由 王宇 原创并发布 :
首先解释一下红红的由来:红红是源自美国动画片《急速蜗牛》中花园里的西红柿,由此寄托着一个IT匠人的希望和愿景。
红红纪念日记录器献给我即将六周岁的儿子乔乔。
一 功能说明
1、倒计时最近纪念日的天数。
2、显示最近四个纪念日列表。
3、显示当前年、月、日、星期和时间。
4、人体感应控制背光和切换屏幕。
5、通过串口(USB模拟)通讯,在PC上设置日期和时间。
6、显示红红Logo。
图片处理的不好,LCD12864实际显示的效果要比这个好的多。
二 硬件组成
1、Arduino主控板:Arduino Mega2560
2、DS1307时钟模块
3、LCD12864显示器
4、人体感应传感器HC-SR501
电路连线:
Mega2560 LCD12864 Mega2560 DS1307 Mega2560 HC-SR501
GND --------- PIN1 GND --------- GND GND --------- GND
5V ----------PIN2 5V ----------VCC 5V ----------VCC
8 ----------RS(CS) 20 ----------SDA 7 ----------OUT
9 ----------RW(SID) 21 ----------SCL
3 ----------EN(CLK)
GND-----------PSB
6 -----------PIN19 (背光)
调试飞线状态:
三 软件开发环境
Arduino 1.5.4
Vim 7.3 + c-support + ctags
我比较习惯在Linux(这个项目是在Win7下开发的)环境下开发软件,所以使用了几个小时的Arduino编辑器就开始不耐烦了,目前使用vim来编写Arduino的代码。先展示一下vim的开发界面:
哎!,怎么看怎么感觉舒服。 Vim plugin的配置和使用方法,我不在这里啰嗦了,网上多的是。这里我要多说一下的是,vim会识别文件的扩展名".c" ,如果你直接打开Arduino的".ino"文件,vim会把这个文件当成一个普通txt文件处理,所有的c-support Tlist 等功能则会失效。我的做法是使用vim 编辑一个".c"的文件,当需要编译的时候,通过一个批处理文件(.bat),将".c"文件复制成".ino"文件。 再用Arduino编辑器打开".ino"文件,然后Ctrl + R开始编译,Ctrl + U将程序下载到板卡上。习惯了以后还是蛮方便的。
四 程序逻辑介绍
1、初始化
initRTC(); /* Initial RTC. */
LCDA.Initialise(); /* Initial screen. */
LCDA.Initialise(); /* Initial screen. */
2、设置日期和时间
日期和时间的设置是通过串口来实现的。例如设置 2014-03-18 12:10:00 在Arduino编辑器的串口输入框中分别输入字符串: "sd 2014-03-18" 和 "st 12:10:00" 程序会解析这两个字符串,并分别设置DS1307的日期和时间。要完成这个功能,首先是要收集串口传送的字符串,然后通过'\n'和'r'来判断输入的结束。这里需要注意的是需要将Arduino串口设置成"Both NL & CR" 模式,采用默认模式无法获得'\n'和'\r' 。 如下图:
代码如下:
/* Handle command from serial. */ if ( Serial.available() ) { char c; c = Serial.read(); if ( bufferSerialCount >= BUFFER_LENGTH-1 || c == '\n' || c == '\r' ) { executeCommand(); bufferSerialCount = 0; bufferSerial[bufferSerialCount] = '\0'; } else { bufferSerial[bufferSerialCount] = c; bufferSerialCount++; bufferSerial[bufferSerialCount] = '\0'; } }
解析设置字符串和设置时间:
/* * === FUNCTION ====================================================================== * Name: executeCommand * Description: Parse command, then set data and time. * "sd" means set data. format: "sd 2014-03-18" * "st" means set time. format: "st 12:10:00" * ===================================================================================== */ int executeCommand(void) { String inputString; String commandString; String year, month, day; String hour, minute, second; int indexOfcommand; Serial.println(bufferSerial); inputString = String(bufferSerial); if(inputString.length() < 1) { return 0; } /* Get command */ indexOfcommand = inputString.indexOf(' '); commandString = inputString.substring(0, indexOfcommand); if(commandString == "sd") // Set date { year = inputString.substring(indexOfcommand + 1, indexOfcommand + 5); if(inputString.charAt(indexOfcommand + 5) != '-') { Serial.println(year); Serial.println("error: Get year."); return 0; } month = inputString.substring(indexOfcommand + 6, indexOfcommand + 8); if(inputString.charAt(indexOfcommand + 8) != '-') { Serial.println(month); Serial.println("error: Get month."); return 0; } day = inputString.substring(indexOfcommand + 9, indexOfcommand + 11); Serial.println(year + "-" + month + "-" + day); RTC.stopClock(); RTC.fillByYMD((uint16_t)year.toInt(), (uint8_t)month.toInt(), (uint8_t)day.toInt()); RTC.setTime(); RTC.startClock(); return 1; } else if(commandString == "st") // Set time { hour = inputString.substring(indexOfcommand + 1, indexOfcommand + 3); if(inputString.charAt(indexOfcommand + 3) != ':') { Serial.println(hour); Serial.println("error: Get hour."); return 0; } minute = inputString.substring(indexOfcommand + 4, indexOfcommand + 6); if(inputString.charAt(indexOfcommand + 6) != ':') { Serial.println(minute); Serial.println("error: Get minute."); return 0; } second = inputString.substring(indexOfcommand + 7, indexOfcommand + 9); Serial.println(hour + ":" + minute + ":" + second); RTC.stopClock(); RTC.fillByHMS((uint8_t)hour.toInt(), (uint8_t)minute.toInt(), (uint8_t)second.toInt()); RTC.setTime(); RTC.startClock(); return 1; } else if(commandString == "i") // Display currently date and time { displayInfo(); } else if(commandString == "h") { displayHelpInfo(); } Serial.print("Command is error, please check it!"); return 0; }
3、显示内容
(1)数据结构:
注意一下ScreenProperty 成员中,关联了DisplayContent。
typedef struct DisplayContent { int row; int column; unsigned char* content; int contentLength; }DisplayContent; typedef struct ScreenProperty { int type; // type: 1: picture; 2: time and word DisplayContent* content; int contentCount; int timerChange; }ScreenProperty; typedef struct MemorialDay { unsigned char* content; int contentLength; char* date; }MemorialDay;
(2)分屏显示:
分屏显示控制是由一个全局变量currentScreenNo和switch-case 在loop()中实现的。每屏显示时间的长短是由timerChangeScreen来控制的,这个变量是由每屏幕属性的timerChange来赋值的。
(3)显示一屏内容:
通过遍历以下数据结构定义,来显示单独一屏幕中的内容:
MemorialDay memorialDayList[MEMORIALDAY_COUNT] = { {wordGrandFather, AR_SIZE(wordGrandFather), "1945-02-04"}, {wordMonther, AR_SIZE(wordGrandMonther), "1982-03-21"}, {wordDavidBirthday, AR_SIZE(wordDavidBirthday), "2008-04-14"}, {wordGrandMonther, AR_SIZE(wordGrandMonther), "1945-06-21"}, {wordGrandMonther2, AR_SIZE(wordGrandMonther2), "1953-08-04"}, {wordFather, AR_SIZE(wordFather), "1976-09-10"}, {wordGrandFather2, AR_SIZE(wordGrandFather2), "1953-09-27"} }; DisplayContent contentScreen1[] = { {1, 0, wordRedRed, AR_SIZE(wordRedRed)}, {2, 0, wordMemorialDayRecord, AR_SIZE(wordMemorialDayRecord)} }; DisplayContent contentScreen2[] = { {0, 0, (unsigned char*)dateCharString, AR_SIZE(dateCharString)}, {0, 6, (unsigned char*)weekCharString, AR_SIZE(weekCharString)}, {1, 2, (unsigned char*)timeCharString, AR_SIZE(timeCharString)}, {2, 0, wordNextMemorialDay, AR_SIZE(wordNextMemorialDay)}, {3, 3, wordResidue, AR_SIZE(wordResidue)}, {3, 7, wordDay, AR_SIZE(wordDay)} }; DisplayContent contentScreen3[] = { {0, 0, memorialDayList[2].content, memorialDayList[2].contentLength}, {0, 5, (unsigned char*)(memorialDayList[2].date + 5), 5}, {1, 0, memorialDayList[3].content, memorialDayList[3].contentLength}, {1, 5, (unsigned char*)(memorialDayList[3].date + 5), 5}, {2, 0, memorialDayList[4].content, memorialDayList[4].contentLength}, :tabf D:\1_WorkSpace\2_Develop\10_Arduino_Project\MemorialDay\MemorialDay\ {2, 5, (unsigned char*)(memorialDayList[4].date + 5), 5}, {3, 0, memorialDayList[5].content, memorialDayList[5].contentLength}, {3, 5, (unsigned char*)(memorialDayList[5].date + 5), 5} }; ScreenProperty screenList[] = { {1, contentScreen1, 2, 5}, {2, contentScreen2, 6, 5}, {2, contentScreen3, 8, 5} };
4、计算纪念日的剩余时间
(1)通过函数getNextMemorialDay()获得离当前日期最近的纪念日日期。
(2)通过函数calculateDay()来计算倒计时剩余天数。这里考虑了同年,跨年,闰年等情况。
5、控制屏幕背光
控制背光的关键问题,是延时读取人体传感器。如果实时读取,屏幕会异常闪烁。虽然传感器具有延时功能,但是我还是在程序中处理的延时。
/* Detecting HC_SR501 and close or open back light. */ if(backLightDelay != LIGHTDELAY) { backLightDelay--; if(backLightDelay == 0) { backLightDelay = LIGHTDELAY; } } else if(digitalRead(pin_HCSR501) && backLightDelay == LIGHTDELAY) // It is 1 about having somebody. It is 0 about nobody. { digitalWrite(pin_Lcd12864BackLight, HIGH); backLightDelay--; } else if( !digitalRead(pin_HCSR501) && backLightDelay == LIGHTDELAY ) { digitalWrite(pin_Lcd12864BackLight, LOW); }
五 制作过程中遇到的问题
1、Arduino 主控板的选择:
我除了使用Mega 2650外,还使用过其他两块主控板。分别是Arduino Leonardo 和Arduino nano。 Leonardo的主要问题是:DS1307和LCD12864连接到此板后,不能同时工作,每个外设单独连接时是没有问题的,目前没有找到是什么原因。 Nano的主要问题是SRAM太小了,只有2K, 而我的程序中一个logo图片就需要1.006K,程序其他部分还需要1.2K左右的SRAM,这样就超出2K的限制。我曾经尝试将图片数据保存到flash memory 和 eeprom中,但都没有成功。
2、LCD12864:
这个显示屏的出厂设置是不能够直接用在Arduino上的,需要将标签为R9(0欧姆)的一个电阻去掉,否则连接上Arduino主控板就会短路。这个问题困扰了我一段时间。
期初,我选择屏幕时,采用的是诺基亚Nokia5110,这块屏幕是昔日的经典,但是目前都是拆卸或翻新的,最大的问题是残影。通过软件调节屏幕的亮度可以缓解,但是稳定性不好。我测试的那块屏,上电后显示的非常暗淡,过了几分钟后才逐渐显示清晰。最终放弃了使用这个屏幕。
3、DS1307时钟模块
这个模块的制作工艺比较低劣,问题主要有如下几个:
(1)备用电池电压低于3V,时钟模块不能正常工作。
(2)晶振质量不过关,导致时间不准确,过快或过慢。
4、人体感应器的输入电压是5V, 输出电压是3.3V,曾经误认为输入的工作电压是3.3V,导致感应器不能稳定工作。 此外这个模块的探头有所谓的进口和国产之分,进口的探头窗口略大些,感应的效果比所谓国产的要好一些。
在此感叹一下,仅仅是DIY如此简单的一个东西,却遇到了这么多的困难和问题。如果是开发一个产品将会如何呀?
六 一点点体会
制作这个小东西是需要一些基本知识和技能的。我总结了以下几点供喜欢DIY的朋友参考:
1、较好的英文阅读能力。Arduino目前没有什么好的中文资料,我所使用的资料都是英文的。
2、能够科学上网。习惯从Github 和 Strack Overfolow等国外网站查找资料。
3、具有一定的单片机知识,理解I2C、SPI和UART等通讯协议。
4、具有较好的C C++语言基础。
5、具有较好的动手能力。
6、克服困难的决心和毅力
相关推荐
《红红播放器1.0版:打造流畅的多媒体体验》 红红播放器1.0版是一款专为用户打造的高效、稳定的多媒体播放软件,致力于提供流畅的视频和音频播放体验。作为初代版本,它标志着红红播放器在数字娱乐领域的初次亮相,...
小班语言红红的小东西PPT学习教案.pptx
这篇文档主要描述了一个润州区机关幼儿园大三班的教学活动设计,主题为“中国娃”,活动名为“国旗红红的哩”。活动目标旨在培养幼儿对国旗的尊重和对祖国的热爱,让他们了解国旗的形状、图案、颜色以及国歌的意义,...
《国旗国旗红红的哩》是一首充满爱国主义情感的儿歌,适合幼儿园中班的孩子们学习。这首歌曲的教学目标不仅仅是让孩子们学会唱这首歌,更重要的是通过歌曲传递对国家的热爱和尊重,培养孩子们的爱国情怀。 活动设计...
这篇文档是一个幼儿园大班的音乐教学教案,主题是教授孩子们歌曲《国旗国旗红红的哩》。教案的主要目的是通过音乐教育,帮助孩子们熟悉旋律,学习唱准歌曲中的连音和休止符,同时激发他们对国旗的尊敬和热爱之情。 ...
电子日志 简单的记录器模块,用于处理前缀和彩色输出。依存关系安装npm install ezlog 原料药prefix_options = [text, [color], [style]] 其中text是要用作前缀的字符串或对象, color是可选颜色, style是可选样式...
红红的樱桃.doc
这篇文档是关于大班幼儿音乐活动的教案,主要内容围绕着教授孩子们歌曲《国旗红红的哩》展开。教案包括活动目标、重点难点、准备工作、活动流程以及教学反思,旨在通过音乐活动培养孩子们对国旗的热爱,提升他们的...
国旗红红的哩.ppt
1-5红红人经济模式.docx1-5红红人经济模式.docx
唱歌《国旗红红的里》.doc
红红草莓魔镜软件是一款全新打造而来的视频图像处理的软件,红红草莓魔镜可以帮助用户让视频及图像变形,扭曲,缩放等等,就像在哈哈镜面前一样,将身体躯干、四肢、脑袋、脖子等等拉长压扁,形成很搞笑的效果。...
【音乐教育活动:国旗红红的哩】 音乐教育在儿童成长过程中扮演着至关重要的角色,它不仅能够激发孩子们的艺术潜能,还能培养他们的感知力、想象力和团队协作能力。本活动“国旗红红的哩”旨在通过音乐教学,让孩子...
《大班音乐:国旗红红的哩》是一个针对幼儿园大班儿童设计的音乐教育活动,旨在通过学习歌曲《国旗红红的哩》,让孩子们在歌唱的过程中理解和表达对国旗的热爱和尊重。活动不仅注重音乐技能的培养,如歌唱和节奏感,...
大班主题教案《红红中国结》润新教育.txt
【标题】"红红的小东西flash动画"是一个适合幼儿园小班使用的语言课件,通过生动的动画形式,帮助孩子们学习和理解语言。该课件以其鲜艳的色彩和趣味性吸引了幼儿的注意力,使得学习过程更加有趣。 【描述】中提到...
《国旗国旗红红的哩》是一首以教育为主题的音乐课程,旨在通过音乐活动培养孩子们对国旗的热爱和对祖国的深情。课程分为多个教学环节,包括教学目标、教学设计、教学过程以及后续的复习和练习。 教学目标主要分为...
这篇文档是关于大班主题教学活动“红红中国结”的教案和反思,旨在教育幼儿认识中国结这一传统艺术形式,了解其蕴含的文化意义,并通过活动提升幼儿的语言表达、观察分析和动手能力,培养他们对祖国文化的热爱。...
2021年《红红的小东西》幼儿园小班教案.pdf