今天拿到一块Arduino UNO R3板,迫不及待就开始试用了。相比Raspberry Pi是一个全能的电脑,Arduino则是个硬件开源的单片机,因为开源,资料和配件网上就很很多了,也就容易让初学者上手了。
Arduino特点:
开源,硬件标准化,配套传感器等模块很多; 结构简单 实时系统,稳定,启动只要0.5秒 Arduino IDE 下载Arduino IDE
上电测试 用USB线接在电脑USB口,然后在GND和PIN 13上插一个二极管,注意二极管正极插在PIN 13上, 如下图: (注:还应该串联一个300欧姆的限流电阻才保险!)
上传代码 在Arduino IDE编辑好下面的代码,然后点Upload后就会运行了,会看到LED一闪一闪。
/* Blink Turns on an LED on for one second, then off for one second, repeatedly. This example code is in the public domain. */ // Pin 13 has an LED connected on most Arduino boards. // give it a name: int led = 13; // the setup routine runs once when you press reset: void setup() { // initialize the digital pin as an output.
[Read More]
并发编程之内存屏障
原文地址:http://mechanical-sympathy.blogspot.com/2011/07/memory-barriersfences.html 或 http://ifeve.com/memory-barriersfences/
关键词:Load Barrier, Store Barrier, Full Barrier
本文我将和大家讨论并发编程中最基础的一项技术:内存屏障或内存栅栏,也就是让一个CPU处理单元中的内存状态对其它处理单元可见的一项技术。
CPU使用了很多优化技术来达成一个事实:CPU执行单元的速度要远超主存访问速度。在我上一篇文章 “Write Combing - 合并写"中我已经介绍了其中的一项技术。CPU避免内存访问延迟最常见的技术是将指令管道化,然后尽量重排这些管道的执行以最大利用缓存而把因为缓存未命中引起的延迟降到最小。
当一个程序执行时指令是否被重排并不重要,只要最终的结果是一样的。例如,在一个循环里,如果循环体内没用到这个计数器,循环的计数器什么时候更新(在循环开始,中间还是最后)并不重要。编译器和CPU可以自由的重排指令以最佳的利用CPU,只要下一次循环前更新该计数器即可。并且在循环执行中,这个变量可能一直存在寄存器上,并没有被推到缓存或主存,这样这个变量对其他CPU来说一直都是不可见的。
CPU核内部包含了多个执行单元。例如,现代Intel CPU包含了6个执行单元,可以组合进行算术运算,逻辑条件判断及内存操作。每个执行单元可以执行上述任务的某种组合。这些执行单元是并行执行的,这样指令也就是在并行执行。但如果站在另一个CPU角度看,这也就产生了程序顺序的另一种不确定性。
最后,当一个缓存失效发生时,现代CPU可以先假设一个内存载入的值并根据这个假设值继续执行,直到内存载入返回确切的值。
CPU核 | V 寄存器 | V 执行单元 -> Load/Store缓冲区->L1 Cache --->L3 Cache-->内存控制器-->主存 | | +-> Write Combine缓冲区->L2 Cache ---+ 代码顺序并不是真正的执行顺序,CPU和编译器可以各种优化只要有空间提高性能。缓存和主存的读取会利用load, store和write-combining缓冲区来缓冲和重排。这些缓冲区是查找速度很快的关联队列,当一个后来发生的load需要读取上一个store的值,而该值还没有到达缓存,查找是必需的,上图描绘的是一个简化的现代多核CPU,从上图可以看出执行单元可以利用本地寄存器和缓冲区来管理和缓存子系统的交互。
在多线程环境里需要使用技术来使得程序结果尽快可见。这篇文章里我不会涉及到 Cache Conherence 的概念。请先假定一个事实:一旦内存数据被推送到缓存,就会有消息协议来确保所有的缓存会对所有的共享数据同步并保持一致。这个使内存数据对CPU核可见的技术被称为内存屏障或内存栅栏。
内存屏障提供了两个功能。首先,它们通过确保从另一个CPU来看屏障的两边的所有指令都是正确的程序顺序,而保持程序顺序的外部可见性;其次它们可以实现内存数据可见性,确保内存数据会同步到CPU缓存子系统。
大多数的内存屏障都是复杂的话题。在不同的CPU架构上内存屏障的实现非常不一样。相对来说Intel CPU的强内存模型比DEC Alpha的弱复杂内存模型(缓存不仅分层了,还分区了)更简单。因为x86处理器是在多线程编程中最常见的,下面我尽量用x86的架构来阐述。
Store Barrier Store屏障,是x86的”sfence“指令,强制所有在store屏障指令之前的store指令,都在该store屏障指令执行之前被执行,并把store缓冲区的数据都刷到CPU缓存。这会使得程序状态对其它CPU可见,这样其它CPU可以根据需要介入。一个实际的好例子是Disruptor中的BatchEventProcessor。当序列Sequence被一个消费者更新时,其它消费者(Consumers)和生产者(Producers)知道该消费者的进度,因此可以采取合适的动作。所以屏障之前发生的内存更新都可见了。
private volatile long sequence = RingBuffer.INITIAL_CURSOR_VALUE; // from inside the run() method T event = null; long nextSequence = sequence.
[Read More]
个人网站实时在线人数接口
感觉把个人网站正在访问的在线人数显示在Nokia 5110液晶屏挺好玩,就稍微研究了一下如何提取实时在线人数。
实现方法
Google Analytics
Google Analytics具有很强大的实时流量分析功能,不过网站主必须登陆到后台才能看,但并没有提供Open API,所以就不能用这个服务了。
日志分析
不修改网站通过web服务器的日志分析,用一个脚本统计15分钟内日志的Unique IP可以粗略的获得一个在线人数。 但多个用户可能通过一个IP过来,这种做法肯定不精确。一般我们可以通过在页面上部署Javascript脚本,由Javascript为每一个浏览器产生一个独特的持久化Cookie,用这个Cookie代替IP来统计。但用Raspberry Pi来做这件事情会拖慢网站,于是一种方案是采用免费的Google App Engine来实现,打算有空来实现一个。
CNZZ
CNZZ也提供了15分钟内的在线人数统计功能。分析CNZZ的计数器代码后发现如下方法可以提取到在线人数:
curl -s "http://online.cnzz.com/online/online.php?id=[your_cnzz_id]&h=[your_cnzz_server_id].cnzz.com&on=1&s=line" | sed -e 's/.*当前在线\[\([0-9]\).*/\1/g'
于是先通过上面的脚本提取在线人数并上传到Cosm,Cosm有个触发器功能可以当在线人数超过某个值后发Twitter或HTTP Post到指定URL,并通过程序显示在液晶屏上。
升级版电子钟 - 如何使用Raspberry Pi驱动Nokia 5110液晶屏
Nokia 5110屏比前面介绍过的1602液晶屏功能好很多,淘宝上买价格相差不大(二手5110 12块左右, 全新1602 8块左右),Nokia 5110最少只需要占用4个GPIO引脚:
带蓝色背光 使用Philips PCD8544 LCD控制器(通过SPI接口) 84x48点阵,可显示100多个字符 硬件准备 树莓派 Nokia 5110 拆机屏 或 焊好的? 注意不要买裸屏,需要带电路板的 杜邦线 母对母8条 8P排针 用来焊接5110屏幕PCB板 电烙铁 电路 5110电路板有8个引脚,使用排针(如下图)将其焊上,方便后面用杜邦线连接,如果不会焊也可以买焊接好的。
RST —— 复位 接GPIO 0 CE —— 片选 接GPIO 1 或 不接 DC —— 数据/指令选择 接GPIO 2 DIN —— 串行数据线 接GPIO 3 CLK —— 串行时钟线 接GPIO 5 (因为我的GPIO 4已经接了一个DHT11传感器) VCC —— 电源输入 接3.3v BL —— 背光控制端 接3.3v GND —— 地线 接地 PS. 编号规范看这里 VCC, BK, GND可以接在面包板电源上
[Read More]
如何使用Raspberry Pi在1602液晶屏上显示当前时间--电子钟
硬件准备 需要以下硬件:
树莓派 面包板 1602液晶屏一块 10K电位器 杜邦线 排针 面包板电源 1602 LCD液晶屏 LCD1602液晶屏提供了16列x2行的ASCII字符显示能力,工作电压5V,提供4位数据与8位数据两种工作模式,因为Raspberry Pi的GPIO口数量很有限,所以使用4位数据模式。LCD1602液晶屏模块提供了16个引脚,我们只需接其中的12个即可–请参考GPIO命名规则:
VSS,接地,RPi PIN 6 VDD,接5V电源,PRi PIN 2 VO,液晶对比度调节,接电位器中间的引脚 RS,寄存器选择,接GPIO 14,RPi PIN 8 RW,读写选择,接地,表示写模式,PRi PIN 6 EN,使能信号,接GPIO 15,RPi PIN 10 D0,数据位0,4位工作模式下不用,不接 D1,数据位1,4位工作模式下不用,不接 D2,数据位2,4位工作模式下不用,不接 D3,数据位3,4位工作模式下不用,不接 D4,数据位4,接GPIO 17,RPi PIN 11 D5,数据位5,接GPIO 18,RPi PIN 12 D6,数据位6,接GPIO 27,RPi PIN 13 D7,数据位7,接GPIO 22,RPi PIN 15 A,液晶屏背光+,接5V,RPi PIN 2 K,液晶屏背光-,接地,RPi PIN 6 注意事项 电源VDD最后接上 排针焊接在液晶屏时注意不要虚焊,也可以用万用表测量一下 RW脚注意一定要接地 调节电位器可以调节液晶对比度 电路图 代码 #!/usr/bin/python # # based on code from lrvick and LiquidCrystal # lrvic - https://github.
[Read More]
Raspberry Pi GPIO的编号规范
树莓派和普通电脑不一样的地方在于它还带了17个可编程的GPIO(General Purpose Input/Output),可以用来驱动各种外设(如传感器,步进电机等)。但GPIO的编号方法有些混乱,不同的API(如wiringPi,RPi.GPIO等)对GPIO的端口号编号并不一样,下面则用图表标明了对应的叫法,这样在看程序例子的时候可以确定物理是哪个接口。
GPIO库 wiringPi C,有Perl, PHP, Ruby, Node.JS和**Golang**的扩展,支持wiringPi Pin和BCM GPIO两种编号 RPi.GPIO Python,支持Board Pin和BCM GPIO两种编号 Webiopi,Python, 使用BCM GPIO编号 WiringPi-Go, Go语言,支持以上三种编号 编号规范 第一列是wiringPi API中的缺省编号,wiringPiSetup()采用这列编号 第二列(Name)往往是转接板的编号 第三列是树莓派板子上的自然编号(左边引脚为1-15,右边引脚为2-26),RPi.GPIO.setmode(GPIO.BOARD)采用这列编号 树莓派主芯片提供商Broadcom的编号方法,相当于调用了WiringPiSetupGpio()或RPi.GPIO.setmode(GPIO.BCM)采用这列编号 wiringPi Pin Name Board Pin BCM GPIO 0 GPIO 0 11 17 1 GPIO 1 12 18 2 GPIO 2 13 21 3 GPIO 3 15 22 4 GPIO 4 16 23 5 GPIO 5 18 24 6 GPIO 6 22 25 7 GPIO 7 7 4 8 SDA 3 0 9 SCL 5 1 10 CE0 24 8 11 CE1 26 7 12 MOSI 19 10 13 MISO 21 9 14 SCLK 23 11 15 TXD 8 14 16 RXD 10 15 Rev.
[Read More]
如何使用Raspberry Pi测量室内温度和湿度并绘制曲线
硬件准备 需要以下硬件:
可以工作的树莓派一个 面包板和公对母杜邦线 10K 电位器一个 DHT11温度和湿度传感器一个或DHT22 传感器电路及原理 DHT11传感器外观 参数 湿度测量范围:20%~90%RH(0-50℃温度补偿); 温度测量范围:0~+50℃; 湿度测量精度:±5.0%RH 温度测量精度:±2.0℃ 响应时间:<5s; 电路图 DHT11一共4根引脚,左边第一根接电源5V (Pin 1),第二根为数据接口,接 Pin 7,第三根不接,第四根接地;在Pin 1和Pin7 之间还需要并联10K的电阻,以保持读数稳定。
安装好的样子: 读取温度和湿度的代码 数据读取流图:
#include <wiringPi.h> #include <stdio.h> #include <stdlib.h> #include <stdint.h> #define MAX_TIME 85 #define DHT11PIN 7 int dht11_val[5]={0,0,0,0,0}; int errors=0; void dht11_read_val() { uint8_t lststate=HIGH; uint8_t counter=0; uint8_t j=0,i; float farenheit; for(i=0;i<5;i++) dht11_val[i]=0; pinMode(DHT11PIN,OUTPUT); digitalWrite(DHT11PIN,LOW); delay(18); digitalWrite(DHT11PIN,HIGH); delayMicroseconds(40); pinMode(DHT11PIN,INPUT); for(i=0;i<MAX_TIME;i++) { counter=0; while(digitalRead(DHT11PIN)==lststate){ counter++; delayMicroseconds(1); if(counter==255) break; } lststate=digitalRead(DHT11PIN); if(counter==255) break; // top 3 transistions are ignored if((i>=4)&&(i%2==0)){ dht11_val[j/8]<<=1; if(counter>16) dht11_val[j/8]|=1; j++; } } // verify cheksum and print the verified data if((j>=40)&&(dht11_val[4]==((dht11_val[0]+dht11_val[1]+dht11_val[2]+dht11_val[3])& 0xFF))) { //farenheit=dht11_val[2]*9.
[Read More]
如何使用Raspberry Pi控制步进电机旋转高清摄像头并拍照
硬件准备 需要以下硬件:
可以工作的树莓派一个 母对母1P杜邦线6根 DC 5V4相28YBJ-48步进电机一个 UL2003芯片步进电机驱动板一块 安装 按下图将步进电机接到驱动板上,也就是白色的接口
步进电机电源 步进电机需要5V电压驱动,而树莓派的GPIO接口中已有5V输出,将图中的Pin 2(最右上角那个)5V,接到驱动板的5V正极,Pin 6接到5V负级,电源部分则搞定。
步进电机驱动线路 驱动板上有IN1, IN2, IN3, IN4四个接口,根据资料得知这四个接口依次设置为低电平就可以驱动,我们分别用杜邦线将GPIO 17(Pin 11),GPIO 18(Pin 12), GPIO 21(Pin 13), GPIO 22(Pin 15)和IN1,IN2,IN3,IN4一一相连。 注意不同的GPIO驱动程序对端口的编号不一定一样(至少有三种叫法:Board,Broadcom,GPIO)
驱动原理:(每次将四个GPIO端口按下表依次设置好电平后,可以sleep几十毫秒来控制转速)
序列 GPIO 17 GPIO 18 GPIO 21 GPIO 22 0 LOW HIGH HIGH HIGH 1 HIGH LOW HIGH HIGH 2 HIGH HIGH LOW HIGH 3 HIGH HIGH HIGH LOW 4 LOW HIGH HIGH HIGH … 安装摄像头 本来是希望用3D打印机来制作齿轮和支架来完成这部分工作的,但因为打印机还没到货,所以先用乐高积木来做了, 刚好乐高积木可以插在步进电机中轴上,而且很牢靠,还不用密封带了。
摄像头如下图用两根导线固定在乐高积木上:
然后用各种积木搭个底座把电机固定起来,并留两个洞口可以将驱动线和摄像头的USB线穿出,这样表面上比较整齐,USB线也不会因为牵扯影响转动。
[Read More]
如何在Raspberry Pi上用LED闪烁提示网站首页新访客
本方法只适合小网站,主要是好玩。Raspberry Pi不是很合适需要实时控制的系统(比如,飞行器,遥控小车),因为Linux内核要多任务,应用程序的优先级不能保持最高,会带来延时,但做些实时性要求不高的系统还是可以的。
硬件安装 需要以下硬件:
可以工作的树莓派一个 1P杜邦线2条 面包板一个 面包板跳线 或 单排针 两根 发光二极管一个 300欧姆的电阻一个 GPIO接口 用杜邦线将上图的3.3V输出和GPIO 23引出(板子正面朝上,GPIO引脚在左上角),将电阻和LED串联起来(电阻防止LED电流过大烧掉),注意二极管的两根脚不一样长,长脚的接正级,这样GPIO 23如果输出高电平,二极管就不发光了,输出低电平就亮啦!
都接好了后的样子如下:
GPIO接口编程 WiringPi An implementation of most of the Arduino Wiring functions for the Raspberry Pi。 代码地址在: https://github.com/wiringPi
安装:
git clone https://github.com/WiringPi/WiringPi cd WiringPi/wiringPi sudo make install 让二极管闪一下的示例代码:
#include <wiringPi.h> #include <stdio.h> #include <stdlib.h> int main (int argc, char* argv[]) { int pinNumber = 4; if (-1 == wiringPiSetup()) { printf("failed to setup wiringPi"); return 1; } pinMode(pinNumber, OUTPUT); digitalWrite(pinNumber, 1); delay(200); digitalWrite(pinNumber, 0); delay(200); return 0; } WiringPi也有Python, Perl, PHP, Ruby的接口包装,按这里,怎么没有Go的呢。。。
[Read More]
在Raspberry Pi上安装ArchLinux
介绍 之前买的Raspberry Pi因为要跑这个网站,不能经常拔下来玩别的,所以又买了一个,这次安装的是Arch Linux。这个发行版安装好后非常基础,占用的空间也只有600M不到,比较合适已有Linux基础的同学玩。初学者可以玩官方推荐的Raspbian。
Arch Linux特点:
启动快,上电后只要3s完成启动 安装完没有图形界面,干净 面向开发者的系统 包管理系统pacman很好用,一个命令就可以完成各种操作 ArchLinux缺省账号和密码是root/root,弄好了后要记得修改root密码 从中国用下载包很快,比Raspbian的源快多了 增加sudo用户 useradd hugo passwd hugo mkdir /home/hugo chown hugo:hugo /home/hugo pacman -S sudo visudo 执行visudo把新用户设置成管理员(增加sudo权限),最后面增加下面一行:
hugo ALL=(ALL) NOPASSWD: ALL USB盘 插上USB盘后,ArchLinux并不会自动mount,手动mount的过程如下: 插上USB前后执行两次 lsblk -o name,kname,uuid,那么输出上多出的那行就是该USB的设备名,或UUID,找到该行后就可以执行mount命令了(注意sda这个符号不同机器可能不一样)
[root@raspberrypi2 ~]# lsblk -o name,kname,uuid NAME KNAME UUID sda sda 001B-9622 mmcblk0 mmcblk0 ├─mmcblk0p1 mmcblk0p1 44C8-CEF1 └─mmcblk0p2 mmcblk0p2 fcee8534-f5f0-42ee-83ac-f943f878ee67 mkdir /mnt/usb mount /dev/sda /mnt/usb 或 mount -U 001B-9622 /mnt/usb 格式化整个USB盘可以用mkfs.ext4 /dev/sda 然后在/etc/fstab里增加一行,以后重启就会自动mount了:
/dev/sda /mnt/usb ext4 defaults,noatime 0 0 还可以测试一下SD卡和USB盘的读写性能:
[Read More]