攀藤 G1 PM2.5传感器

本文有更新,请参考 拆了攀登 G1 PM 2.5 传感器 《拆了攀登 G1 PM 2.5 传感器》

最近帮朋友做东西,他要求测试当前环境PM2.5。我首先拿出了珍藏许久的神荣模块。记得当时是90多入手的,当初Taobao上也没几个卖这个模块的,90多是最低价格了。写这篇文章的时候顺手查了一下目前的价格,已经降到了60多,卖家也不少了。很可能是目前出货量大,整体价格都降低下来了。我发现除了线材或者连接器,几乎所有的电子模块都有这样的问题。比方说:MPU6050模块,刚出来的时候居然要五六十,现在只要五六元。

先说一下神荣模块,问题多多,首先是根据他的SPEC,要求连续采集30秒,采集的时候基本上什么也不能做,否则会严重影响精度;另外,他每次计算的数值偏差很大,委婉一点的说法是:能够精确反应当前空气颗粒趋势……对于采集偏差,有资料说可能是因为他是采用发热电阻加热空气来实现流动的,而这样的方式不能确保空气的流通速度,所以结果不是很准确。个人观点:如果你的传感器上没有风扇换气,都是靠不住的。

后来,看到论坛上有人推荐攀藤模块,我在上网考察了一下,感觉上微创联合【参考1】这家很专业,但是不知道为什么他邮费报价很高(25?);最后我在树莓派一号店【参考2】这家买的,155+5元邮费,型号是G1。

简单说一下型号,有 G1 G2 G3,这几个主要差别在于外形尺寸。另外,G1可以输出最近一段测试到的单位个数。

外壳上有一层蓝膜,撕下之后能露出金属外壳(有朋友看照片说这个模块很老,那是因为我照相技术的原因,这个模块应该是2014年10月之后才有的):

image002

可以看到,上面带有一个风扇进行空气交换的。

image004

引脚介绍【参考3】。特别注意,似乎他们他们家的模块G1 G3引脚顺序上有差别?根据你手上的实物进行选择

image006

入手之后就开始实验,连接上最小只需要三根线即可让他工作,分别是VCC (Pin1) GND(Pin2)和TXD(Pin5)。我直接将这个模块和蓝牙透传模块连接起来(9600),PC串口即可获得数据非常方便。

image007

因为手头没有能够进行对照测试的仪器,只是简单测试了一下:我用手堵住进气口,过一段PM2.5值会变为0. 可以看一下工作的视频:

http://www.tudou.com/programs/view/xexkePsAd_c/?resourceId=0_06_02_99

上述代码下载

PM2.5Fix

编译器是 Delphi 2010 + CPort VCL

上面说的都是优点,下面说点缺点,貌似我更喜欢负结果?
1. 可能是因为上面有电机的缘故,所以对于电流要求比较高。产品手册说最大电流为 120ma,官网说最大是200ma,实际启动时非常有可能比这个更大,我试图使用万用表测量,无法让其工作未果。因此,如果给Arduino使用,一定不能用Arduino取电,否则有不可预料的后果;我的解决方法是:USB充电宝先是接到一个 USB母头上取电,然后下来的电给G1,蓝牙供电,再直接进入Arduino Pro Micro的VCC;

2. 接口设计上有问题,我买到的那个接口不是很牢固,经常出现串口取不到数据反查一路才发现接口松动,如果能选用更粗大的排插效果会更好;

3. 整体上没有指示灯,无法判断当前工作状态。对于我来说出现问题时首先要把手放在模块上看看是否有震动来判断是否工作;、
4. 只在两个角落预留螺丝孔,如果整体多几个孔位,使用上会更方便;孔径应该是 M2,我手上的M2螺丝太短,没有办法插进去。

5. 手册提供的数据格式似乎有问题,刚开始我看的是卖家介绍网页,以为是卖家搞错了,后来翻了一下手册,手册也是这样写的:

image010

我实际获得的数据是这样的,每一笔串口数据以“BM” 开头,后面是数据,我实验发现 PM1.0 = Data[4]+(Data[5] shl 8) 才是正确值。如果用 (Data[4] shl 8) + (Data[5]) 计算结果明显不正确。【这个问题是Delphi数组下标从1开始导致的,如果你使用VC即可follow spec】刚开始实验我根据手册写成后面的这种形式,结果7000多,疑似身在帝都了;后来琢磨一下写成前面这种,结果为 32 (室内),感觉才正确;

6. 如同文章开头所说,和其他相比价格还是偏贵;

7. 资料偏少,没有官方资料【参考4】缺少寿命方面的详细数据,让人很难信服。特别是带有机械部分的电子设备,寿命通常会远低于电子部分,缺少数据让人很不放心;万一选用的是国产电机,你更无法判断什么时候会悲剧。

最后的总结:如果你是为了简单的研发目的,或者毕业设计,相比其他PM2.5传感器,这款使用简单,结果看起来很准确,非常值得推荐。但是如果你是为了稳定长期的工作,我认为有待时间检验。

参考:
1. http://shop115958317.taobao.com/index.htm?spm=a1z10.1-c.w5002-9767628871.2.jTEb6C 微创联合
2. http://shop110224467.taobao.com/index.htm?spm=2013.1.w5002-6755541327.2.ZK3XMi 树莓派一号店
3. 攀藤 G1 说明书
4. http://www.plantower.com/ 攀藤科技官网

2015年9月5日 放上来说明书 PantengGx

Arduino 驱动74HC595 LCD模块

Arduino 驱动8位数码管模块

之前我们使用数码管都是直接使用【参考1】,缺点是电路复杂,占用引脚太多,稍微有点问题就会出现缺笔画的问题(测试的时候务必完整测试,否则很可能遇到你想显示8,结果出现6的状况)。最近入手了下面这个模块,上面通过74HC595芯片驱动LCD,接线很简单。我做了简单的实验,感觉挺方便的。

示例代码

    unsigned char LED_0F[] = 
    {// 0	 1	  2	   3	4	 5	  6	   7	8	 9	  A	   b	C    d	  E    F    -
    	0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90,0x8C,0xBF,0xC6,0xA1,0x86,0xFF,0xbf
    };
    unsigned char LED[8];	//用于LED的8位显示缓存
    int SCLK = 10;
    int RCLK = 9;
    int DIO = 8; //这里定义了那三个脚
    
    void setup ()
    {
      pinMode(SCLK,OUTPUT);
      pinMode(RCLK,OUTPUT);
      pinMode(DIO,OUTPUT); //让三个脚都是输出状态
    }
    void loop()
    {
        LED[0]=1;
    	LED[1]=2;
    	LED[2]=3;
    	LED[3]=4;
        LED[4]=5;
    	LED[5]=6;
    	LED[6]=7;
    	LED[7]=8;
    	while(1)
    	{
    		LED8_Display ();
    	} 
      
    }
    
    void LED8_Display (void)
    {
    	unsigned char *led_table;          // 查表指针
    	unsigned char i;
    	//显示第1位
    	led_table = LED_0F + LED[0];
    	i = *led_table;
    	LED_OUT(i);			
    	LED_OUT(0x01);		
        digitalWrite(RCLK,LOW);
        digitalWrite(RCLK,HIGH);
    	//显示第2位
    	led_table = LED_0F + LED[1];
    	i = *led_table;
    	LED_OUT(i);		
    	LED_OUT(0x02);		
        digitalWrite(RCLK,LOW);
        digitalWrite(RCLK,HIGH);
    	//显示第3位
    	led_table = LED_0F + LED[2];
    	i = *led_table;
    	LED_OUT(i);			
    	LED_OUT(0x04);	
        digitalWrite(RCLK,LOW);
        digitalWrite(RCLK,HIGH);
    	//显示第4位
    	led_table = LED_0F + LED[3];
    	i = *led_table;
    	LED_OUT(i);			
    	LED_OUT(0x08);		
        digitalWrite(RCLK,LOW);
        digitalWrite(RCLK,HIGH);

    	//显示第5位
    	led_table = LED_0F + LED[4];
    	i = *led_table;
    	LED_OUT(i);			
    	LED_OUT(0x10);		
        digitalWrite(RCLK,LOW);
        digitalWrite(RCLK,HIGH);
    	//显示第6位
    	led_table = LED_0F + LED[5];
    	i = *led_table;
    	LED_OUT(i);			
    	LED_OUT(0x20);		
        digitalWrite(RCLK,LOW);
        digitalWrite(RCLK,HIGH);
    	//显示第7位
    	led_table = LED_0F + LED[6];
    	i = *led_table;
    	LED_OUT(i);			
    	LED_OUT(0x40);		
        digitalWrite(RCLK,LOW);
        digitalWrite(RCLK,HIGH);        
    	//显示第8位
    	led_table = LED_0F + LED[7];
    	i = *led_table;
    	LED_OUT(i);			
    	LED_OUT(0x80);		
        digitalWrite(RCLK,LOW);
        digitalWrite(RCLK,HIGH);        
    }
    
    void LED_OUT(unsigned char X)
    {
    	unsigned char i;
    	for(i=8;i>=1;i--)
    	{
    		if (X&0x80) 
                {
                  digitalWrite(DIO,HIGH);
                 }  
                else 
                {
                  digitalWrite(DIO,LOW);
                }
    		X<<=1;
                digitalWrite(SCLK,LOW);
                digitalWrite(SCLK,HIGH);
    	}
    }

 

卖家没有提供相应的代码,上述代码是我根据从其他地方找到的4位LED的代码基础上修改出来的。

工作照片:

595LED

附件是卖家提供的8 LED的资料和其他地方找到的 4 LED 的资料。

4LED

8LEDDS

参考:

1. http://www.lab-z.com/4digitial/ Arduino 四位数码管实验
2. http://www.lab-z.com/usb7seg/ USB 控制七段数码管
3. http://www.lab-z.com/usb-%E6%8E%A7%E5%88%B6%E4%B8%83%E6%AE%B5%E6%95%B0%E7%A0%81%E7%AE%A1ii/ USB 控制七段数码管(II)

推荐绘制电路图的软件 TinyCAD

之前我尝试用 Fritzing 绘制短路图一段时间。这个软件有直观方便的特点。问题是大多数我用到的原件他的库中没有,如果自己绘制的话非常复杂,修改元件管脚也异常麻烦。

http://sourceforge.net/projects/tinycad/

tinycada

用这个很容易就绘制一个 Arduino Pro Micro的电路

tinycadb

对应文件可以在这里下载
Arduino-Pro-Micro

小水泵套件

最近从淘宝上买了一个小水泵和水管给Arduino 用,效果还不错

螃蟹王国 隔膜泵 抽水机 水泵 茶具功夫茶配件 烧水茶具 385水泵 9.9元
http://detail.tmall.com/item.htm?spm=a1z10.3-b.w4011-2934116009.41.OZz7kv&id=27069408842&;rn=19abc97d3a3f333b0532d77acf876c95&abbucket=10

poop
螃蟹王国 硅胶管 耐高温 食品级 水管 多规格 6元

http://detail.tmall.com/item.htm?spm=a220o.1000855.0.0.aUdENI&id=16273533392&;rn=5ae68cf7c122dcfc1738eafece3afc9c&abbucket=0

同样他家选购了一个6V的适配器。

螃蟹王国 电源适配器6V2A 高性能 路由器电源 适配器 充电器 9.9元
http://detail.tmall.com/item.htm?spm=a1z10.3-b.w4011-2934116009.58.24xZpI&id=20618420536&;rn=6257e27c013737241e82d5c120284aaa&abbucket=10

螃蟹王国 DC电源插座 5.5*2.1MM 直流电源接口 DC-005 铜脚 3.95元
http://detail.tmall.com/item.htm?spm=a1z10.3-b.w4011-2934116009.27.pZwIWV&id=20623496826&;rn=1bc29be0dbbabd7042db076efd6ce381&abbucket=10

需要注意的是,这种插座有2个参数,5.5是直径,2.1是长度。也有2.5mm长度的,如果座和插头不匹配的话,插入之后会有部分露在外面。

下面是它工作视频以便参考

http://www.tudou.com/programs/view/7diay5i7Z-c/?resourceId=0_06_02_99

Arduino Pro Micro 打造PPT遥控器

之前,我们介绍过如何用Arduino Uno打造一个PPT遥控器【参考1】,缺点是制作过程复杂,用到的器件较多导致整体稳定性不好。这里介绍使用 Arduino Pro Micro来做一个同样的PPT遥控器。

原理上和之前的并没有多少差别,都是通过模拟USB键盘的方式来进行控制。差别在于Pro Micro是缩小版的 Leonardo ,内部集成了USB Slave控制器,我们不需要再花费精力模拟自己为USB Keyboard.
硬件连接示意图:

ppt1

代码是直接修改自示例“KeyboardMessage”

ppt2

/* 
 Keyboard Button test
 
 For the Arduino Leonardo and Micro.
 
 Sends a text string when a button is pressed.
 
 The circuit:
 * pushbutton attached from pin 2 to +5V
 * 10-kilohm resistor attached from pin 4 to ground
 
 created 24 Oct 2011
 modified 27 Mar 2012
 by Tom Igoe
 
 This example code is in the public domain.
 
 http://www.arduino.cc/en/Tutorial/KeyboardButton
 */

const int buttonPinA = A1;          // input pin for A
const int buttonPinB = A2;          // input pin for B
int previousButtonStateA = HIGH;   // for checking the state of a pushButton
int previousButtonStateB = HIGH;   // for checking the state of a pushButton

void setup() {
  // make the pushButtonA pin an input:
  pinMode(buttonPinA, INPUT);
  // make the pushButtonB pin an input:
  pinMode(buttonPinB, INPUT);
  // initialize control over the keyboard:
  Keyboard.begin();
  delay(10000);
}

void loop() {
  // read the pushbutton:
  int buttonStateA = digitalRead(buttonPinA);
  int buttonStateB = digitalRead(buttonPinB);  
  // if the button state has changed, 
  if ((buttonStateA != previousButtonStateA) 
    // and it's currently pressed:
  && (buttonStateA == HIGH)) {
    // type out a message
    //Keyboard.print("A");
    Keyboard.press(KEY_LEFT_ARROW);
    Keyboard.releaseAll();
  }
  // if the button state has changed, 
  if ((buttonStateB != previousButtonStateB) 
    // and it's currently pressed:
  && (buttonStateB == HIGH)) {
    // type out a message
    //Keyboard.print("B");
    Keyboard.press(KEY_RIGHT_ARROW);
    Keyboard.releaseAll();
  }  
  // save the current button state for comparison next time:
  previousButtonStateA = buttonStateA; 
  previousButtonStateB = buttonStateB;   
}

 

实物图

ppt3

工作的视频

http://www.tudou.com/listplay/mAjgE5Ac_Sg/Fz0piTvQ9Cs.html?resourceId=414535982_06_02_99

参考:

1. http://www.lab-z.com/%E7%94%A8-arduino-%E6%89%93%E9%80%A0ppt%E9%81%A5%E6%8E%A7%E5%99%A8/ 用 Arduino 打造PPT遥控器
2. http://arduino.cc/en/Reference/KeyboardModifiers 键值定义

Arduino 打造一个音量计

根据上一次的LM386的设计【参考1】,以及网上的设计【参考2】,用Arduino做一个音量计。首先,音频从MIC进入,经过LM386的放大后接入到Arduino的模拟输入上,经过 DAC 量化之后显示在 1602上。

电路方面,和【参考1】差别在于我们不再使用喇叭而是直接将放大后的OUT信号接入到 Arduino的A5上。

显示方面,我们使用【参考3】提到的方法来自定义字符充当强度指示。

下面的图片与其说是原理图不如说是连接图更合适

vu1

//www.lab-z.com 
//在 1602 上显示音量的小程序

#include <Wire.h> 
#include "LiquidCrystal_I2C.h"

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);
            //MIC输入放大之后在 A0 输入Arduino    
                pinMode(A0, INPUT);
}

//显示音量强度
//从左到右一共有 5 * 16 =80 点,一共是 80+1=81 个状态
void showprg(int value)
{
        //第一行显示当前VU值
                lcd.setCursor(0,0);
                lcd.print("     VU=");
                lcd.print(value);

                
		//移动光标到第二行
                lcd.setCursor(0,1);

        //显示全黑的块
                for (int i=1;i<value / 5;i++) {
                                lcd.write(4);
                        } //for (int i=1;i<a;i++) 


                // drawing charater's colums
		// 显示除去全黑块之后的零头
                switch (value % 5) {
                  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)

	       // 用空格填充剩下的位置
              for (int i =0;i<(16-value / 5);i++) {
                lcd.print(" ");   }
}

void loop()
{
     //输入的是0-1023,用函数将这个值对应到[0,80]上 
        showprg(map(analogRead(A0),0,1023,0,80));
		delay(100);
              
}

 

测试方法,在右边用一个手机播放声音,MIC将音频信号转化为电平信号,经过LM386放大后,通过Arduino A5进行量化,最终显示在LCD1602上。

vu2

工作视频:

http://www.tudou.com/programs/view/oW_isUBnIEU/?resourceId=414535982_06_02_99

这个还只是一个模型,很粗糙,对于VU的动态显示范围不大,如果想让人类更容易理解需要考虑将现在的线性显示改为非线性的。

另外,工作时我发现比较奇怪的事情,就是如果静音的时候,会显示UV 大约 35左右,但是如果有动态声音播放的时候反而会出现 16 这样值,不清楚原因。

==============================================================================
另外的另外,研究了一下前面那个口哨开关的模拟输出。下面是电路图,右下角红色框起来的是模拟输出电路部分

vu3

查了一下,发现这种用法是电压跟随器,下图来自【参考4】。

vu4

电压跟随器,顾名思义,是实现输出电压跟随输入电压的变化的一类电子元件。也就是说,电压跟随器的电压放大倍数恒小于且接近1【参考5】。
多少输入就是多少输出…….这也就是为什么这个模块驱动能力很差带不动喇叭的原因。
参考:

1. http://www.lab-z.com/lm386-with-mic/ LM386 with MIC
2.http://www.arduino-hacks.com/arduino-vu-meter-lm386electret-microphone-condenser/ Arduino VU meter – LM386+electret microphone condenser
3. http://www.lab-z.com/1602progressbar/ 用 1602实现进度条
4. http://www.geek-workshop.com/thread-551-1-1.html LM358双运算放大器
5. http://baike.baidu.com/link?url=85VzUTT6n3IrCaAATZSg5jsg_A-zQYFrizj6jqGP7PzRg1aJe2yTddMihqJ7_UgRMa_GxnAYakSmmjM-9nhsz_ 电压跟随器

LM386 with MIC

最近有一个获取环境声音送入Arduino处理的需求。想起来之前入手过带有microphone模拟输出的模块。找出来之后发现他有模拟输出,但是应该是因为放大后的信号太小(没有功率放大),无法达到Arduino的量化要求(可惜没有示波器否则可以定量分析一下)。
image002

后来又想起之前入手过一个音频放大模块,主芯片是 LM386,于是拿出来实验。

image004

我的第一个错误是:没搞清楚输入和输出。一端是 GND GND IN VCC (两个GND是连通的),一端是 OUT 和 GND。刚开始我错误的认为OUT和GND端应该接MIC,IN 用来驱动喇叭。后来认真看了卖家商品介绍,上面提到“板载喇叭接线座”方才恍然大悟。IN 应该是输入的音频信号,另外一端是对喇叭的直接驱动。

第二个错误是:我将MIC直接接入IN。试验了很久都没有反应。隐约觉得什么地方搞错了。后来在网上搜索了一下LM386的典型应用【参考1】,又恍然大悟,要输入的是不断变化的信号,而非直流。

image005

咪头的接法,来自【参考1】.实际电路中,我用的是24K的电阻。
image006

图片来自【参考1】

下面这部分电路不用管,LM386模块完全负责了。

lmsch

此外还需要特别注意的是电路中的MIC有正负极的差别,100uF的电容也有正负极的区别,接反了不工作。

最终,实物是这样的:

lm3861

模块上有一个可以调节放大倍数的可变电阻。放大倍数太大会出现噪音很大失真严重的问题。

工作视频(只是演示工作情况,电路方面和上面的介绍有差别):

http://www.tudou.com/programs/view/KiIwulH_p2s/?resourceId=0_06_02_99

额外的话

1.麦克风的英文是Microphone ,缩写是 MIC(又叫咪头,如果你想在淘宝上买的话,直接搜咪头就好)。我以为英文应该是 micphone,上网搜了一下发现犯这样错的人真不少……

2.供电对功放的影响很大。开始我用USB适配器(220V-5V)作为电源,噪音挺大的;后来换了一个USB充电宝效果就好了很多,估计如果有可能直接用干电池效果会更好吧?

3.时代在变化,上一次买的语音放大套件还都是独立元件,焊接调试都是蛮麻烦的事情,转眼间集成度就高了很多。

参考:

1. http://www.arduino-hacks.com/arduino-vu-meter-lm386electret-microphone-condenser/ Arduino VU meter – LM386+electret microphone condenser

2. http://www.learningaboutelectronics.com/Articles/How-to-connect-a-LM386-audio-amplifier-chip How to Connect a LM386 Audio Amplifier Chip to a Circuit

3. https://lowvoltage.wordpress.com/2011/05/15/lm386-mic-amp/ LM386 microphone amplifier

=================================================================
2015年4月6日 又研究了一下上面提到的带有麦克风的模块,是通过 LM358D 来进行放大的。我在网上查到有LM358和LM386一同工作来做一个小功放的。我试验了一下,结果比我直接MIC+LM368差多了,根本出不来什么声音。可能是电路设计上的缘故吧,毕竟那个模块的主要作用是输出当前是否出现了一个超过设定音强的声音(有个好听的名字叫“口哨开关”)。

Arduino Uno 采样速度

首先测试一下普通的 Arduino UNO 的采样速度

// the setup routine runs once when you press reset:
void setup() {
  // initialize serial communication at 9600 bits per second:
  Serial.begin(9600);
  analogReference(INTERNAL); //调用板载1.1V基准源
}


void loop() {
  int i;
  float voltage;
  int sensorValue;
  unsigned long elsp=millis();
  for (i=0;i<10000;i++)
    {
      // read the input on analog pin 0:
      sensorValue = analogRead(A0);
    }  
  Serial.println(millis()-elsp);
  delay(10000);
}

 

运行结果是 1120 左右,就是说采样10000次用时1.12s,采样频率大约是 8928.57Hz (次/秒)。

找了一段代码【参考6】能够提高采样速度

// defines for setting and clearing register bits
#ifndef cbi
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#endif
#ifndef sbi
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#endif

 

写个程序测试一下

// the setup routine runs once when you press reset:
void setup() {
  // initialize serial communication at 9600 bits per second:
  Serial.begin(9600);
  analogReference(INTERNAL); //调用板载1.1V基准源

// set prescale to 16
sbi(ADCSRA,ADPS2) ;
cbi(ADCSRA,ADPS1) ;
cbi(ADCSRA,ADPS0) ;  
}


void loop() {
  int i;
  float voltage;
  int sensorValue;
  unsigned long elsp=millis();
  for (i=0;i<10000;i++)
    {
      // read the input on analog pin 0:
      sensorValue = analogRead(A0);
      
    }  
  Serial.println(millis()-elsp);

  delay(10000);
}

 

运行输出结果在 170 左右,就是说采样10000次用时0.170s,采样频率大约是 58823.53Hz (次/秒)。

原理上来说Arduino 的 ADC 是用一种逐次逼近比较的方法来进行ADC转换的。通俗的说就是内部有一个比较器,每次内部升高一个电压和外部指定的Pin进行比较,根据大于小于来判定外部指定脚上的电压。资料说做这样一个比较大约需要10次,13个机器周期。而比较的频率是主控频率分频给出来的,有一个分频因子,默认是128,分频因子越小,比较的速度也就越快。

adcfreq

这里只是简单说一下,如果想了解ADC具体的工作原理,请阅读参考中提到的文章。

多说两句批评一下《Arduino技术内幕》这本书,刚开始我是阅读这本书来学习Arduino ADC的,但是阅读的时候感觉一直在外面游荡,不知道是原作者不懂还是翻译的人不明白,反正这一章的介绍让我一头雾水。

ainside

参考:

1. http://www.geek-workshop.com/thread-1848-1-1.html Arduino入门教程–第十七课–使用内部基准源进行ADC采样
2. 冷知识-这次讲ADC采样率-11月8号更新-上传高速率采样库-后续加入示波器库
http://www.geek-workshop.com/thread-11511-1-1.html
3. http://apcmag.com/arduino-analog-to-digital-converter-how-it-works.htm/ 介绍 Arduino ADC 如何工作
4. http://www.microsmart.co.za/technical/2014/03/01/advanced-arduino-adc/
5. http://wenku.baidu.com/link?url=I3DA7sWeIOxPntLf89u_MO8V30InxZlEWzDu7BxsXPQlJtprgkzUsdQmIEiBPSOBrdq1_iccg-qxxkOh1ROvfz3C9vbt55Axy_f1JAFZJTq 基于Arduino的音乐频谱显示器方案概述
6. http://forum.arduino.cc/index.php?topic=6549.0
7. http://meettechniek.info/embedded/arduino-analog.html Arduino: Analog measurements

Arduino 遥控小灯的开关

目标:用 Arduino 遥控小灯的开关
材料:
1. Arduino Uno 一块
2. 2262/2272四路无线遥控套件M4非锁接收板 配四键无线遥控器(收发一套)
3. 继电器 (因为目前的Demo控制电压很小,所以最基础的型号就可以)
4. 5V充电宝一个
5. 面包板一块
6. 面包板电源一块
7. 小恐龙变色灯一个

选择无线遥控器的原因是:价格便宜,不涉及到解码输出简单稳定,易于使用。

原理上介绍是这样的:Arduino循环判断遥控接收模块输出管脚电压,如果出现HIGH,表示对应的按钮被按下。遥控器上有4个按钮,对应着接收器输入上面的D0-D3。实验使用D0 和 D1 两个脚分别控制继电器的开关。

电路图如下,左侧因为没有面包板电源的元件图样,所以用电池盒表示,实际电路中为一个3.3V电源。右侧因为没有接收模块的元件图样,所以用一个其他模块替代。无线模块在之前的实验中也使用过,可以在【参考1】上看到。

const int InputPinD0=8; //接收模块D0 Pin
const int InputPinD1=9; //接收模块D1 Pin
const int ControlPin=3; //控制继电器
void setup()
{
    pinMode(ControlPin,OUTPUT);     
    pinMode(InputPinD0,INPUT_PULLUP); //输入,上拉10K
    pinMode(InputPinD1,INPUT_PULLUP);     
}

void loop()
{
  int n =digitalRead(InputPinD0);  //检查引脚是否有遥控输入
  if (n==HIGH) {
    digitalWrite(ControlPin,HIGH);
  }

  int m =digitalRead(InputPinD1);  
  if (m==HIGH) {
    digitalWrite(ControlPin,LOW);
  }
     
}

 

对于上面提到的10K上拉,可以在【参考2】中查到。

原理图

315_bb

最后的样子

image (1)

工作视频

参考:

1. http://www.lab-z.com/%E7%94%A8-arduino-%E6%89%93%E9%80%A0ppt%E9%81%A5%E6%8E%A7%E5%99%A8/ 用 Arduino 打造PPT遥控器
2. http://geek-workshop.com/forum.php?mod=viewthread&tid=2874&highlight=%C9%CF%C0%AD Arduino入门教程–第二十三课–使用IO口内部上拉功能

Step to UEFI (39) —– 编写一个GetCursorPosition函数

我们知道,可以使用 ConOut 下面的 SetCursorPosition 来设置光标输出的位置【参考1】。

setcursorposition

但是,找了一圈也没有找到 GetCursorPosition ,我怎么知道当前光标在哪呢?查查C手册,通常使用conio.h 中的 wherex,wherey 来完成这个功能。问题是, clib 连这个头文件都没有……..

经过一番查找,在【参考2】中有人提到可以使用 Mode 中的 CursorColumn 和 CursorRow 来完成这个功能。于是,编写简单的程序验证一下,程序非常简单,随机生成一个位置显示X,然后在它后面输出当前这个X的位置:

#include  <Uefi.h>
#include  <Library/UefiLib.h>
#include  <Library/ShellCEntryLib.h>

#include  <stdio.h>
#include  <stdlib.h>
#include  <wchar.h>
#include <Protocol/EfiShell.h>
#include <Library/ShellLib.h>

#include <Protocol/SimpleFileSystem.h>
#include <Protocol/BlockIo.h>
#include <Library/DevicePathLib.h>
#include <Library/HandleParsingLib.h>
#include <Library/SortLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/BaseMemoryLib.h>

extern EFI_SYSTEM_TABLE			 *gST;

/**
  Get the position of cursor
**/
VOID GetCursorPosition(
	IN	UINTN	*x,
	IN	UINTN	*y
)
{
	*x = gST->ConOut->Mode->CursorColumn;
	*y = gST->ConOut->Mode->CursorRow;
	
	return;
}

int
EFIAPI
main (                                         
  IN int Argc,
  IN char **Argv
  )
{
	UINTN wherex;
	UINTN wherey;
	UINTN i;
	
	for (i=0; i<5; i++)
	  {
		gST -> ConOut -> SetCursorPosition(gST -> ConOut,rand()%70,rand() % 20);
		GetCursorPosition(&wherex,&wherey);
		Print(L"x[%02d]:[%02d] ",wherex, wherey);
		
	  }
  
  return EFI_SUCCESS;
}

 

运行结果:

Getcur

完整代码下载:

GetCursor

参考:

1.UEFI Spec 2.4 P460
2.http://biosren.com/viewthread.php?tid=4164&highlight=%B9%E2%B1%EA EFI下对应wherex()作用的FUN是哪个?该怎么用?