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卡,但一直沒成功過,此次搞明白原理了弄成功了可是又遇到了新問題。

相關文章
相關標籤/搜索