Totol Phase I2C工具选型指南
还是上次那颗芯片的问题,还在Debug。有了新的需求,忽然发现没有合适的工具,只好研究一番
研究一下如何 RESETSYSTEM,直接使用 RUNTIMESERVICES 是很好的选择。首先是查看资料 UEFI 2.4 是不二的选择。
7.5.1 Reset System
具体参数解释:EFI_RESET_TYPE 有: EfiResetCold,EfiResetWarm, EfiResetShutdown,和EfiResetPlatformSpecific。Cold是系统级别的完全重启。 Warm也是系统级别的重启,但主要是CPU级别的重启。(具体的话,以前确实有Warm Reset和Cold Reset的差别,但是我感觉实际BIOS设计上,这两个并不是区分的特别清晰。传统上的CPU的Warm Reset有可能导致几百次重启之后系统挂掉,而测试人员根本不会听取你关于他们差别的解释,于是最好的办法仍然是都用Cold Reset来解决)。Shutdown的话,就是关机了。EfiResetPlatformSpecific从解释上来看好像是将一个EFI_GUID指定的Reset类型存放在ResetData中(类似记录重启原因吗?)
EfiResetPlatformSpecific 太复杂,这里只是研究一下简单的 Reset 最后编写程序如下
#include <Uefi.h> #include <Library/UefiLib.h> #include <Library/ShellCEntryLib.h> #include <stdio.h> #include <stdlib.h> #include <wchar.h> extern EFI_BOOT_SERVICES *gBS; extern EFI_SYSTEM_TABLE *gST; extern EFI_RUNTIME_SERVICES *gRT; /*** Demonstrates basic workings of the main() function by displaying a welcoming message. Note that the UEFI command line is composed of 16-bit UCS2 wide characters. The easiest way to access the command line parameters is to cast Argv as: wchar_t **wArgv = (wchar_t **)Argv; @retval 0 The application exited normally. @retval Other An error occurred. ***/ int EFIAPI main ( IN int Argc, IN char **Argv ) { gRT -> ResetSystem(EfiResetCold,EFI_SUCCESS,0,NULL); return EFI_SUCCESS; }
运行之后系统就重启了。
下载 MainRST
这是来自 http://playground.arduino.cc/Main/LibraryList 的Arduino MD5库【参考1】。我实验了一下挺好用的。
#include "MD5.h" /* This is en example of how to use my MD5 library. It provides two easy-to-use methods, one for generating the MD5 hash, and the second one to generate the hex encoding of the hash, which is frequently used. */ void setup() { //initialize serial Serial.begin(9600); //give it a second delay(1000); //generate the MD5 hash for our string unsigned char* hash=MD5::make_hash("hello world"); //generate the digest (hex encoding) of our hash char *md5str = MD5::make_digest(hash, 16); //print it on our serial monitor Serial.println(md5str); char* test="hello world"; unsigned char* hash2=MD5::make_hash(test); md5str=MD5::make_digest((unsigned char*)hash2, 16); Serial.println(md5str); } void loop() { }
运行结果
这个结果和我在一个在线MD5的网站【参考2】计算结果是一致的
例子下载
MD5_Hash
我只是简单的测试了一下对Char做MD5,头文件中的另外几个函数的用法没搞清楚。从经验上来看,应该是能够不断累积计算一系列MD5值(比如说刚开始有个字符串”ABC”后来又来了一个字符串“DEF”可以继续加入计算中)。试验了一下没搞清楚。
static const void *body(void *ctxBuf, const void *data, size_t size);
static void MD5Init(void *ctxBuf);
static void MD5Final(unsigned char *result, void *ctxBuf);
static void MD5Update(void *ctxBuf, const void *data, size_t size);
参考:
1.https://github.com/tzikis/ArduinoMD5/ 完整代码下载 ArduinoMD5-master
2.http://md5calculator.chromefans.org/ 一个MD5在线计算网站
在 https://www.electronicsblog.net/arduino-lcd-horizontal-progress-bar-using-custom-characters/ 这里发现比较有趣的代码:用1602LCD 实现一个进度条。根据文章指引,我也试验了一下。
弄明白了原理,程序非常简单:
//https://www.electronicsblog.net/ //Arduino LCD horizontal progress bar using custom characters #include <Wire.h> #include "LiquidCrystal_I2C.h" #define lenght 16.0 double percent=100.0; unsigned char b; unsigned int peace; int value=100; // custom charaters LiquidCrystal_I2C lcd(0x27,16,2); //定义进度块 byte p1[8] = { 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10}; byte p2[8] = { 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18}; byte p3[8] = { 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C}; byte p4[8] = { 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E}; byte p5[8] = { 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F}; void setup() { lcd.init(); //初始化LCD lcd.backlight(); //打开背光 //将自定义的字符块发送给LCD //P1 是第一个,P2 是第二个,以此类推 lcd.createChar(0, p1); lcd.createChar(1, p2); lcd.createChar(2, p3); lcd.createChar(3, p4); lcd.createChar(4, p5); } void loop() { //设置光标在左上角 lcd.setCursor(0, 0); percent = value/1024.0*100.0; //当超过100%的时候自动校正为 100% if (percent>100) {percent=1;value=0;} lcd.print(" "); lcd.print(percent); lcd.print(" % "); //移动光标到第二行 lcd.setCursor(0,1); double a=lenght/100*percent; // drawing black rectangles on LCD // 显示全黑块。 if (a>=1) { for (int i=1;i<a;i++) { lcd.write(4); b=i; } //for (int i=1;i<a;i++) a=a-b; } else {b=0;} peace=a*5; // drawing charater's colums // 显示除去全黑块之后的零头 switch (peace) { case 0: break; case 1: lcd.write(0); break; case 2: lcd.write(1); break; case 3: lcd.write(2); break; case 4: lcd.write(3); break; } //switch (peace) // clearing line // 用空格填充剩下的位置 for (int i =0;i<(lenght-b);i++) { lcd.print(" "); } //递增 value=value+10; delay(300); }
连接
大图
代码下载 pb1602
================================================================================
2015年3月13日 特别注意:自定义字符一次不能超过8个,如果需要自定义很多个,可以用动态的方法进行切换,参考 http://www.geek-workshop.com/thread-5190-1-1.html 1602自定义字符的另一种思路,实现超过8种自定义字符的显示
我的网站打开比较慢有一段时间了,刚开始检查过几次,直接Ping出去速度都还可以。加之大多数情况下我都是写好了再上传,打开页面慢点并不碍事。最近几次在网站上直接写文章,稍微留心了一下,发现缓慢的时候浏览器左下角显示在访问Google的什么。忽然灵光一闪:莫非是因为我的网站调用Google一些东西,而Goolge因为“众所不知”的原因无法访问因此会导致巨慢的问题?Baidu了一下发现真是这么回事。
http://www.gox.name/Article/2775.html 谷歌被墙!解决WordPress访问速度慢的问题
http://www.wind88.net/news/news-125.html WordPress最近速度很慢的解决方法
简单的解释:Wordpress调用了Google的一些服务,在打开的时候浏览器需要和服务器进行交互,而Google 肯定是Timeout因此会有问题。
按照文章中的说法,按转一个插件,绕开了Google的服务,再次页面速度马上快了起来。
这是“城门失火殃及池鱼”吗? 不是的,因为你生活在墙中,那是一个看不见摸不着,但是每时每刻你都可能撞上的“墙”。
隐约记得列宁有过一段关于拆墙的论述,查了一下,还真有,应该是出现在历史课本中的。
“当列宁因参加学生运动而被抓捕时,只有17岁。一个警察局长不解地问他:“我不明白,你为什么要起来造反?年轻人,要知道你面前是一堵墙。你不是在用脑袋往墙上撞吗?”列宁藐视地回答:“是的,一堵墙,不过已经腐朽了,一推就倒。我们可以从上面跨越过去。”文中的“一堵墙”被推倒应该是在
A.二月革命中 B.七月事件中
C.十月革命中 D.国内战争中”
唉,最后再次祝愿病魔早日战胜北邮前校长方滨兴先生吧。
这次的目标是要实现用 USB 口来控制七段数码管。
硬件上面选用《圈圈教你玩USB》配套的实验板。上面的单片机为STC89S52。实验板上P0接口用来和D12通讯,P1接口连接到一组开关按钮上,P2接口上有一组LED,LED另外一端通过10K电阻上拉。下面用色块对这些端口的用途做了简单标记:
最开始的设想是数码管(用的还是上次出场过的那一款数码管,共阳极,【参考1】)阳极连接到P1(可以理解为SEG选择 Pin接正极),阴极连接到P2(数码段 Pin接负极)。实验发现数码管根本不亮,检查很多次后尝试去掉六个数码段的连接终于能够点亮一段数字。这才恍然大悟,因为共阳极上还有板载的LED所以会使得电压很奇怪,不足以推动全部。同时显示,所以需要更改方案。
做到这里,我忽然明白极客工坊大神弘毅提到的:“驱动数码管限流电阻肯定是必不可少的,限流电阻有两种接法,一种是在d1-d4阳极接,总共接4颗。这种接法好处是需求电阻比较少,但是会产生每一位上显示不同数字亮度会不一样,1最亮,8最暗。另外一种接法就是在其他8个引脚上接,这种接法亮度显示均匀,但是用电阻较多。” 因为电压相同,数字1只需要点亮两根LED,8需要点亮七根。
修改之后,阳极连接到P2,阴极连接到P1上。实验发现即使4个数字同时显示仍然是可以清晰辨认的。连接如下图所示:
继续尝试在上位机程序中自动扫描点亮每个LED,前面一篇Arduino就是使用类似的方法点亮的。实做之后发现这样做会导致每个数码管亮灭之间的间隔太长,这样在人类看起来是逐个亮灭的而不是同时亮。猜测这是程序本身发送和USB传输Delay太长导致的。初步估计每个数字显示间隔至少在200ms以上。如果想做一个类似跑马灯的程序用这样的方案没问题,但是如果想持续稳定的显示数字这个方案不可行。
最后上位机程序直接发送显示的数值,由Firmware本身解析,同时利用单片机上的定时器(15ms一次),不断刷新每个显示位。开始时,Firmware中定时器产生中断后,ISR中将四位数值轮流显示一次,但是实验结果肉眼只能看到最后一个点亮的数值。估计这个可能是因为ISR中每个数码位显示过快,而他们点亮的时间远远小于不在ISR中的时间。
经过无数次的调试后,终于完成了。
拍了一个视频,PC来控制七段数码管
http://www.tudou.com/programs/view/-D_w29Pwavc/?resourceId=414535982_06_02_99
对应的Firmware关键点在于中断服务函数
void Timer0Isr(void) interrupt 1 { //LabzDebug_Start static i=0; //LabzDebug_End //定时器0重装,定时间隔为5ms,加15是为了修正重装所花费时间 //这个值可以通过软件仿真来确定,在这里设置断点,调整使两次运行 //时间差刚好为5ms即可。 TH0=(65536-Fclk/1000/12*5+15)/256; TL0=(65536-Fclk/1000/12*5+15)%256; // //LabZDebug_Start P2=1 << i; P1=zBuf[i]; i++; if (i==4) {i=0;} //LabZDebug_End }
Firmware下载 HID7Seg
PC端上位机主要代码
int n=0; while (kbhit()==0) { WriteReportBuffer[1]=Seven_Dig[n / 1000 % 10]; WriteReportBuffer[2]=Seven_Dig[n / 100 % 10]; WriteReportBuffer[3]=Seven_Dig[n / 10 % 10]; WriteReportBuffer[4]=Seven_Dig[n %10]; printf("%d \n",n); //调用WriteFile函数发送数据 Result=WriteFile(hUsb, WriteReportBuffer, 9, &lpNumberOfBytesWritten, NULL); //如果函数返回失败,则可能是真的失败,也可能是IO挂起 if(Result==FALSE) { //获取最后错误代码 LastError=GetLastError(); //看是否是真的IO挂 if((LastError==ERROR_IO_PENDING)||(LastError==ERROR_SUCCESS)) { return TRUE; } //否则,是函数调用时发生错误,显示错误代码 else { printf("Sending error:%d \n",LastError); //如果最后错误为1,说明该设备不支持该函数。 if(LastError==1) {printf("This device doesn't support WriteFile function \n");} } } n=(n++)%10000; }//while (kbhit()==0)
完整的代码下载 USB7Seg
参考:
1. http://www.lab-z.com/4digitial/ Arduino 四位数码管实验
2. http://www.geek-workshop.com/forum.php?mod=viewthread&tid=82&highlight=%CA%FD%C2%EB%B9%DC arduino学习笔记13 – 4位数码管实验
通常如果想让Arduino发出声音需要额外的配备,比如:Mp3解码器,Wav专用播放器或者语音合成的模块等等。
但是理论上因为Arduino具有模拟输出,所以应该可以直接输出波形给喇叭(这个还是必须的,我随便选了一个8欧1.5瓦的)。
随手搜索了一下,国外真有人这样做了。原理上来说就是先用工具将音频转化为WAV, Arduino 的存储空间有限,这里只能使用单声道 8000Hz 采样率,然后通过控制模拟端口将数据发送出来。从我的实验来看,Arduino Uno(Flash Memory 32 KB 【参考1】)可以存放大约4s左右的音频(编译之后在 31K左右)。
具体的做法是:
1.硬件方面,喇叭负极连接到GND,正极连接到Pin11
2.在Arduino程序中使用下面这个库
3.选择音频文件,然后转化为 WAV 8000Hz Mono 格式(我Switch Sound File coverter 感觉不错,免费的)
软件下载 switchsetup
4.最后用参考2提供工具,将wav转化为数组的定义(需要注意这个工具需要 javaw.exe 支持)
5.编译之后 Upload 即可
用Windows自带的录音机录下来的
工具转化为 wav 8K mono
最后生成的程序
做出来的样子
http://www.tudou.com/programs/view/SzuxR6-7oKI/?resourceId=414535982_06_02_99
参考:
1.http://kb.open.eefocus.com/index.php?title=Arduino_Uno Arduino Uno
2.http://highlowtech.org/?p=1963
===============================================================================
2015.2.23 补充:我在编译时发生“error: ‘OCR2A’ undeclared (first use in this function)” 这样一系列的错误,搜索了一下,是因为最近一直在玩 Arduino Pro Micro,它的芯片是 ATmega32u4 ,而这个芯片没有 Timer2 , 只有 0, 1, 3 和 4。IDE中重新切换回Uno再次编译即可。参考 http://forum.arduino.cc/index.php?topic=237770.0
再补充参考2给出来的那个转换工具
再给出一个完整的Arduino程序例子
playback1
写了一个小程序,获得摄像头输出之后直接在上面画图。
cvCircle(CvArr* img, CvPoint center, int radius, CvScalar color, int thickness=1, int lineType=8, int shift=0)
img为图像指针,单通道多通道都行,不需要特殊要求
center为画圆的圆心坐标
radius为圆的半径
color为设定圆的颜色,比如用CV_RGB(255, 0,0)设置为红色
thickness为设置圆线条的粗细,值越大则线条越粗,为负数则是填充效果 【参考1】
#include "stdafx.h" #include "highgui.h" #include "cv.h" #include "stdio.h" #include <ctype.h> int main(int argc,char ** argv) { int r=0; CvCapture *capture = 0; //从摄像头读取 capture = cvCaptureFromCAM(0); if(!capture) { fprintf(stderr,"Could not initialize capturing...\n"); return -1; } cvNamedWindow("Laplacian",1); //循环捕捉,直到用户按键跳出循环体 for(;;) { IplImage * frame =0; //抓起一祯 frame = cvQueryFrame(capture); if(!frame) break; r>20?r=0:r++; //画一个圈 cvCircle(frame,cvPoint(frame->width / 2,frame->height /2 ), 45+abs(10-r), CV_RGB(150, 100,100), 4, 8, 0); cvShowImage("Laplacian",frame); if(cvWaitKey(10)>0) break; } cvReleaseCapture(&capture); cvDestroyWindow("Laplacian"); return 0; }
参考:
1.http://blog.163.com/yuhua_kui/blog/static/9679964420141104385570/ OpenCV cvCircle函数
2.http://wiki.opencv.org.cn/index.php/Cxcore%E7%BB%98%E5%9B%BE%E5%87%BD%E6%95%B0 Cxcore绘图函数
起因:有网友抱怨,因为陪着孩子在家玩,漏接了公司的重要电话,被罚款500(如果真的是人民币的话,我相信他一定乐于看到这个产品)
看到这个需求,我忽然想是否可以用 Arduino来打造一个能够提醒的盒子呢? 当然,解决方法还有很多,严小姐听了我的目标之后第一个反应是为什么不换一个山寨机,大屏幕啊有没有,超长待机啊有没有,八个喇叭啊有没有,只要499啊有没有。我回答有很多人喜欢老物件有感情而已。她又在问为什么,终于被我意味深长的眼神说服了。
下面就开始动手实做了。从触发的角度来说最稳妥的办法还是安装APP,Android和iOS都是可行的,只是…….默默的轻抚了一下自己的 Nokia E72,放弃了这个方案。翻翻元件箱,里面有振动开关,那就凑合着用吧。提示方面,最近入手了一个具有录音放音功能的模块ISD1820,使用非常简单,GPIO足以搞定(多说一句,上面有麦克风和一个录音按钮,按下这个按钮对着它高歌一曲即记录在芯片中。时间比较短,只能记录10秒)。选用了 8×8 LED模块作为提示灯光,它在之前的《电子骰子系列》登场过。
原理上很简单,就是检测连续振动确定是振铃–>触发装置,每隔10秒提示一次–>如果有人按下Stop按钮就停止提示。
用到的配件
用面包板来验证电路
侧面有一个喇叭,一个按钮
正面俯视,有一个 8×8点阵
振动传感器
内部线路看起来有点复杂
另外一边做出一个洞,给 Arduino供电
#include "LedControl.h" const int ShockTestPin = 4; //Pin for vibration sensor const int AlartPin = 5; //LED Alert const int StopPin = 6; //Stop button int sta=0; //pin 12 is connected to the DataIn //pin 11 is connected to the CLK //pin 10 is connected to LOAD //We have only a single MAX72XX. LedControl lc=LedControl(12,11,10,1); void showface() { /* here is the data for the characters */ byte face[8]={ B00000100, B00100010, B01000110, B01000000, B01000000, B01000110, B00100010, B00000100}; lc.setRow(0,0,face[0]); lc.setRow(0,1,face[1]); lc.setRow(0,2,face[2]); lc.setRow(0,3,face[3]); lc.setRow(0,4,face[4]); lc.setRow(0,5,face[5]); lc.setRow(0,6,face[6]); lc.setRow(0,7,face[7]); } void setup() { Serial.begin(9600); pinMode(ShockTestPin, INPUT); pinMode(AlartPin,OUTPUT); digitalWrite(AlartPin,LOW), pinMode(StopPin,INPUT); //Stop pin for input //The MAX72XX is in power-saving mode on startup, //we have to do a wakeup call lc.shutdown(0,false); //Set the brightness to a medium values lc.setIntensity(0,8); //and clear the display lc.clearDisplay(0); } // End of Setup void loop() { long int start; if (0==sta) { //Status One: Test status boolean shock=false; start=millis(); while (millis()-start<=1000L) { //Test vibration for 1 second if (LOW==digitalRead(ShockTestPin)) { //If there is virbration signal shock=true; //Mark true break; } //if (LOW==digitalRead(ShockTestPin)) } if (false==shock) {sta=0; goto Sta1End;} //If in the one seconds there is no vibration, it should break delay(500); start=millis(); while (millis()-start<=1000L) { //Test vibration for 1 second if (LOW==digitalRead(ShockTestPin)) { //If there is virbration signal shock=true; //Mark true break; } //if (LOW==digitalRead(ShockTestPin)) } if (false==shock) {sta=0; goto Sta1End;} //If in the one seconds there is no vibration, it should break delay(500); start=millis(); while (millis()-start<=1000L) { //Test vibration for 1 second if (LOW==digitalRead(ShockTestPin)) { //If there is virbration signal shock=true; //Mark true break; } //if (LOW==digitalRead(ShockTestPin)) } if (false==shock) {sta=0; goto Sta1End;} //If in the one seconds there is no vibration, it should break delay(500); //It means we have test vibration in 3 seconds sta=1; Sta1End: ; } if (1==sta) { //Play the voice that we have record digitalWrite(AlartPin,LOW); digitalWrite(AlartPin,HIGH); delay(100); digitalWrite(AlartPin,LOW); //Delay 10 seconds start=millis(); while (millis()-start<=15000L) { if (LOW==digitalRead(StopPin)) { //We will stop the playing ,if we test the stop button is pressed sta=0; lc.clearDisplay(0); break; } if (0==(millis()-start)/1000L % 2) { showface(); } else { lc.clearDisplay(0); } }//while (millis()-start<=15000L) }//if (1==sta) }
具体的使用方法拍了一个简单的视频,本人倾力献身声
最后放两张靓照
这只是很粗糙的原型,如果真想做出来实用的还有很长的路要走。比如:让检测更灵敏更可靠,或者干脆使用手机的声音作为触发条件。提示音量再大一些。或者再加上充电功能就更加实用,也给需要一直插着让Arduino供电找到更好的借口。
然后严小姐的问题是:如果忽然有很强的振动,误触发了怎么办,如果家里确实没有人它又一直在叫怎么办,如果隔壁在装修,有振动有噪音怎么办?好吧,如果你肯再品准500+以上的预算,让我买块蓝牙开发板,我相信一定不是问题。哦,对了还要换一个支持Android的手机才好啊。