Raspberry Pi 实现刷卡就亮灯

先说一下本文用到的硬件:Raspberry Pi(Model A或Model B均可以),来自SeeedStudio的NFC开发板(PN532)和彩色LED灯(P9813),可选配件为:单色LED光源,蜂鸣器。git

软件的话就是wiringPi的库(便于控制针脚),还有spi-dev, spi-bcm的内核模块须要被加载(不然设备没法被发现),gcc什么的确定是必备啦github

 

硬件链接

为了描述方便,这里使用wiringPi的针脚来进行说明,而图片当中则使用原始针脚来进行链接。ssh

PN532:将Raspberry Pi的SPI的MOSI和MISO,CLK三个连接到PN532的开发板对应针脚上面,5V和GND可使用外接电源或者使用Pi上面的针脚。ChipSelect则使用了wiringPi的11针(具体针脚是要和你代码当中的针脚一致的)这里不得不说SeeedStudio网页的例子了,感受彻底是照搬的,没有按照本身的板子更改。它的2*3是SPI端口,可是只有MOSI,MISO,CLK是须要链接树莓派对应SPI端口的;而5V,GND则是由下部的6Pin针脚来获取的。ChipSelect则是经过上面标号为10的针脚来进行的。函数

P9813:虽然它使用的是SPI通讯协议,但它仅有4根线,除去5V和GND以外,只有CLK和MOSI了。因为它没有ChipSelect针脚,它一直接受总线信号。当它也接入SPI总线的时候会致使随机闪烁,所以咱们须要解决方案:1.想办法让它只能“听到”针对本身的信号,别人的信号听不到;2.用GPIO模拟SPI的MOSI和CLK,那么就分离了它。本文使用方案2来进行。同时因为购买的板子不是2.54mm的杜邦线插座,可是幸亏NFC的板子上面有对应接口,它们能够经过下面6Pin的4,5号针脚从外部引入信号。测试

PN532connection

上图的地线(黑色的那个)在20140308的从新链接电路并测试当中,发现该位置安装后不能正常SPI通信,而切换到旁边的(上图黑色线与方块之间,也就是黑线右邻)以后则能够正常使用了。也许当初我没焊接好?字体

图错了啊!!!!!!上图的MISO(粉色)应该链接SCK上方的那个针,MOSI(紫色)应该链接SCK右侧的针。20140308从新搭建的时候发现该错误……网站

简单LED和蜂鸣器:GND链接通用的就能够,而后每一个LED占用一个GPIO针脚,Beeper也是同样。由于它们比较简单,GPIO口处于高电压时就亮/发声;反之不亮/没有声音。操做起来只需设置他们的电位便可。spa

LED

 

链接好以后,你能够经过wiringPi当中的gpio命令来测试LED和蜂鸣器是否可用:.net

gpio write {针脚编号,好比0236} {1表明开,0表明关}

 

软件部分

(这个草稿早就开始写了,前一部分还好,这一部分写的时候就不知道当时想写哪些了…………那就把github上面的说明一下好了……)线程

思路:

软件读取NFC的数据(目前只会读取ID,并且是4ByteID,7Byte的应该无法读取),根据数据决定是否亮灯/提示音。若是遇到特殊的卡,则退出程序。

因为我但愿的是在临睡前自动关灯,因此设定了个延迟来让灯逐渐变暗而后灭掉。

伪代码:

初始化硬件

获取卡ID

IF 卡ID符合规则1 THEN

    设置灯泡亮,蜂鸣器发音

ENDIF

IF 卡ID符合规则2 THEN

    关灯,提示音

    退出程序

ENDIF

----------------------------

// 控制灯亮的线程(在main文件当中)

一直循环{

    获取当前颜色

    与目标颜色比较获得增量(若是为0则这次循环等待事后跳过)

    设置彩灯为增量过的颜色

    更新当前颜色

}

// 可是我发现这个线程并非真正的并行运行了。好比在main当中有sleep的时候它也不执行了(具体表现为,若是颜色是一直递减变为0的状况下,会出现1s保持恒定,1s变暗的状态)

-----------------------------

 

这里存在两个问题:1. 如何获取卡ID;2. 如何设置灯

获取卡ID(PN532.c)

这个直接使用http://blog.iteadstudio.com/to-drive-itead-pn532-nfc-module-with-raspberry-pi/的源代码做为参考。由于实际使用过程中不须要NFC直接进行P2P传输消息,因此我把P2P的部分给忽略掉了。

原始文件当中使用了部分wiringPi的库,可是本身重写了很多wiringPi原本的东西,通过个人对比,发如今wiringPiSPI当中的wiringPiSPISetup函数有不一样。我对仅使用wiringPi的和使用它给出的库运行了一下,对比发现wiringPi不能写入东西,而它给出的库能够读出来数据。这是由于wiringPiSPISetup当中他加入了如下函数来达到增长读取的功能:

if (ioctl (fd, SPI_IOC_RD_MODE, &spiMode)         < 0) return -1 ; 
if (ioctl (fd, SPI_IOC_RD_BITS_PER_WORD, &spiBPW) < 0) return -1 ; 
if (ioctl (fd, SPI_IOC_RD_MAX_SPEED_HZ, &speed)   < 0) return -1 ;
i = ioctl(fd, SPI_IOC_RD_LSB_FIRST,&spiLSB); 

 

(绿色字体的内容能够跳过)
难道原来的wiringPi不能这么作么?

究其缘由,我又去projects.drogon.net的网站看了看,若是你是从git.drogon.net下载的话,这个站点当中的SPI源文件是没有RD相关的函数的,也就是没有给SPI读取的权限,而另一个站点 github.com/WiringPi/ 下载的话则是有相关函数的。因此若是你遇到SPI相关问题,从github直接拖源码或许是个不错的选择。
由于咱们要抛弃掉附加的SPI库,转向使用wiringPi里边带的库,所以须要把上面连接当中的nfc代码略做修改。主要集中在wiringPiSPIDataRW函数上面。原来的代码当中感受很莫名其妙,设置了多个变量可是用到的只有一个……查看github上面的wiringPi源代码:

int wiringPiSPIDataRW (int channel, unsigned char *data, int len)

{

struct spi_ioc_transfer spi ;

channel &= 1 ;

spi.tx_buf = (unsigned long)data ;

spi.rx_buf = (unsigned long)data ;

spi.len = len ;

spi.delay_usecs = spiDelay ;

spi.speed_hz = spiSpeeds [channel] ;

spi.bits_per_word = spiBPW ;

return ioctl (spiFds [channel], SPI_IOC_MESSAGE(1), &spi) ;

}

与原来的对比,就能够知道在low level SPI当中,传入的字符指针只须要一个便可,它既充当tx的buf又充当rx的buf,运行完以后data当中的数据就是读取到的数据。

小小疑问:难道咱们不能直接传输整个字符串么?非得一个字符一个字符的传递?

其余的话都是小修小改,好比:

将delay改成usleep,由于我发现delay运行的状况下CPU负载20%以上,换作usleep则好不少。

添加了_chipSelect,而且初始化以后将chipSelect置于高位,也就是此时若是没有操做的话PN532默认是忽略信号的。

重命名了write,read,begin。感受这些函数起名容易让人误解,尤为是在初始化当中蹦出来个begin();让人以为莫名其妙。

添加了PN532DEBUG的宏,若是它被定义了,那么编译结果就会输出好多信息。

 

设置灯(P9813GPIO.c)

因为普通单色LED和蜂鸣器都是能够直接经过wiringPi的digitalWrite来操做的,一行程序便可解决问题,因此这些内容再也不赘述。不要忘了在初始化的时候将这些端口设置为OUT就能够了。

下面说说P9813这个三色LED。根据文档的描述,它是用SPI通信的,正巧PN532的板子上面有4pin的接口,接上去发现4pin对着的是SDA/SCL,查了一下这两个端口,它俩的意思是须要在写完8bit以后再写入一个bit;而P9813则是不接受冗余bit的,因此我以为不可行就没有测试了。而是用SPI的话是能够的。可是问题在于若是将PN532和P9813同时接入的话,P9813会不时的闪一下。查了P9813的文档,猜测到是PN532通信的时候,P9813没有忽略这些通信信号(SPI共享总线的)所以误操做了。可是树莓派没有第二个SPI引出,并且添加一个chipSelect给P9813又不知道如何下手,因此索性就用GPIO bit-bang了一个SPI。

你也能够从第一个图看到,我把SCL/SDA接入到了GPIO23/24当中。在wiringPi对应的是4,5两个。以后就须要进行逐bit的发信号了。这里涉及到如下5个函数:

void sendByte(unsigned char b);
//这个是逐个bit的发送b,也就是发送8次。根据datasheet,每一次发送都得让CLK升降一次。


void sendColor(unsigned char r, unsigned char g, unsigned char b);
//调用sendByte发送RGB颜色,注意到datasheet当中提示发送每一个RGB都须要带校验。


void setColorRGB(unsigned char r, unsigned char g, unsigned char b);
//调用sendColor来发送,这里须要添加前缀。


void setColorRGBs(unsigned char* r, unsigned char* g, unsigned char* b, int count);
//调用setColorRGB来设置多个彩灯的RGB,由于条件限制没有作测试,理论上是对的。datasheet说最多能够1024个。


void setColorRGBbuffered(unsigned char r, unsigned char g, unsigned char b);
//因为发现调用颜色设置函数过于频繁的时候CPU可能占用超过15%,是因为大量digitalWrite形成的,因此决定使用缓冲来减小这种状况。代码当中设计了上一次设置的时候的颜色,若是这次设置颜色与上一次相符则什么都不操做,不然就写入颜色。为了防止不一样步,设置了每隔一段时间强制写入(即便与上一次相符)


另外谁能告诉我怎么在C当中设置函数的访问权限?我不想让sendByte和sendColor从外部访问。别的都应只调用setColorRGB系列函数。

 

总结

按照上面的思路,你就可使用NFC来操控灯了。我这里只是一个极其简单的实现,NFC怎么读卡内容,P9813设置不一样颜色我都没有实现。不过如今就能够天天晚上刷卡亮灯,睡觉前把卡拿走,灯就会慢慢暗下来(最开始感受不到,最后快要灭掉的时候才能以为亮度明显下降了),免得以前躺被窝里以后还得拿手机登陆ssh设置gpio write 0 0来灭普通LED了……

若是你有兴趣,我把代码放在了github上面,你能够去看看:https://github.com/DC-Shi/PN532SPI-P9813GPIO 由于我首次使用 github,许多地方都不了解,尽请谅解。

 

(2014038)为何用GPIO去bit-bang一个SPI去控制P9813?

最开始是由于,直接把SCL,SDA接入到SPI上的话会致使在与PN532的交互过程当中,P9813也接受信号从而致使出现莫名其妙的颜色。

而如今我有了一个4053的模拟开关,能够作到控制是否接通,也就是把SPI引出来,接到模拟开关的输入,其输出再接P9813。通过测试,通路状态下一切正常,可是在将通路切换为断路的时候,灯会略微延迟(保持最后的颜色),以后自动切换到白色。这一点很是不爽。因此我如今仍是得用bitbang……

话说Pi有三条SPI的,可是只引出来一个,太惋惜了……

 

题外话

这其实也是个最简单的应用,我在看完RPi的摄像头模块能够红外摄像以后,以为能不能经过摄像头来捕捉红外信息,若是获取的图像是我躺着的图像,肯定事后就灭灯,若是看见我不是睡觉的话就亮灯?或者进行其余的设置?

仍是预告一下吧,我很久没写东西了(上篇仍是在10月份的……)。一个是年底总结,可是这个是技术博客,所以发发技术上今年都干了啥;一个是DS18B20的,我竟然以前没有写过!这个东西我9月14号就开始用了;还有一个是Surface的TF卡装Fedora20,上次装18的时候是安装到硬盘了,也尝试过TF卡,但一直没成功过,此次搞明白原理了弄成功了可是又遇到了新问题。