树莓派wiringPi库详解
wiringPi是一個很棒的樹莓派IO控制庫,使用C語言開發,提供了豐富的接口:GPIO控制,中斷,多線程,等等。java 的pi4j項目也是基于wiringPi的,我最近也在看源代碼,到時候整理好了會放出來的。
下面開始wiringPi之旅吧!
?
安裝
進入? wiringPi的github (https://git.drogon.net/?p=wiringPi;a=summary) 下載安裝包。點擊頁面的第一個鏈接的右邊的snapshot,下載安裝壓縮包。
然后進入安裝包所在的目錄執行以下命令:
>tar xfz wiringPi-98bcb20.tar.gz //98bcb20為版本標號,可能不同 >cd wiringPi-98bcb20 >./build?
驗證wiringPi的是否安裝成功,輸入gpio -v,會在終端中輸出相關wiringPi的信息。否則安裝失敗。
?
?
?
?
?
編譯 和運行
假如你寫了一個LEDtest.c 的項目,則如下。
編譯:g++ -Wall -o LEDtest LEDtest.cpp -lwiringPi //使用C++編程 , -Wall 是為了使能所有警告,以便發現程序中的問題 gcc -Wall -o LEDtest LEDtest.c -lwiringPi //使用C語言編程 運行:sudo ./LEDtest?
?
查看引腳編號表格
使用如下控制臺下命令
> gpio readall?也可以查看下面的圖。
注意:查看時,將樹莓派的USB接口面對自己,這樣看才是正確的。
?
?
?
wiringPi庫API大全
在使用wiringPi庫時,你需要包含頭文件 #include<wiringPi.h>。凡是寫wiringPi的程序,都包含這個頭文件。
?
硬件初始化函數
使用wiringPi時,你必須在執行任何操作前初始化樹莓派,否則程序不能正常工作。
可以調用下表函數之一進行初始化,它們都會返回一個int , 返回 -1 表示初始化失敗。
?
| int wiringPiSetup (void) | 返回:執行狀態,-1表示失敗 | 當使用這個函數初始化樹莓派引腳時,程序使用的是wiringPi 引腳編號表。引腳的編號為 0~16 需要root權限 |
| int wiringPiSetupGpio (void) | 返回執行狀態,-1表示失敗 | 當使用這個函數初始化樹莓派引腳時,程序中使用的是BCM GPIO 引腳編號表。 需要root權限 |
| wiringPiSetupPhys(void) | ?不常用,不做介紹 | ?/ |
| wiringPiSetupSys (void) ; | ?不常用,不做介紹 | ?/ |
?
?
?
?
通用GPIO控制函數
| void pinMode (int pin, int mode) | pin:配置的引腳 mode:指定引腳的IO模式 可取的值:INPUT、OUTPUT、PWM_OUTPUT,GPIO_CLOCK ????? |
只有wiringPi編號下的7(BCM下的4號)支持GPIO_CLOCK輸出 |
| void digitalWrite (int pin, int value) | pin:控制的引腳 value:引腳輸出的電平值。 ?可取的值:HIGH,LOW分別代表高低電平 | 讓對一個已近配置為輸出模式的 引腳? 輸出指定的電平信號 |
| int digitalRead (int pin) | pin:讀取的引腳 返回:引腳上的電平,可以是LOW HIGH 之一 | 讀取一個引腳的電平值? LOW? HIGH ,返回 |
| void analogWrite(int pin, int value) | pin:引腳 value:輸出的模擬量 | 模擬量輸出 樹莓派的引腳本身是不支持AD轉換的,也就是不能使用模擬量的API, 需要增加另外的模塊 |
| int analogRead (int pin) | pin:引腳 返回:引腳上讀取的模擬量 | 模擬量輸入 樹莓派的引腳本身是不支持AD轉換的,也就是不能使用模擬量的API, 需要增加另外的模塊 |
| void pwmWrite (int pin, int value) | pin:引腳 value:寫入到PWM寄存器的值,范圍在0~1024之間。 | 輸出一個值到PWM寄存器,控制PWM輸出。 pin只能是wiringPi 引腳編號下的1腳(BCM下的18腳) |
| void pullUpDnControl (int pin, int pud) | ?pin:引腳 pud:拉電阻模式 可取的值:PUD-OFF ? ???? 關閉拉電阻 | 對一個設置IO模式為 INPUT 的輸入引腳設置拉電阻模式。 與Arduino不同的是,樹莓派支持的拉電阻模式更豐富。 樹莓派內部的拉電阻達50K歐姆 |
?
?
?LED閃爍程序
#include<iostream> #include<cstdlib> #include<wiringPi.h> const int LEDpin = 1;int main() {if(-1==wiringPiSetup()){cerr<<"setup error\n";exit(-1);}pinMode(LEDpin,OUTPUT); for(size_t i=0;i<10;++i){digitalWrite(LEDpin,HIGH); delay(600);digitalWrite(LEDpin,LOW);delay(600);}cout<<"------------bye-------------"<<endl;return 0; } View Code?
?
?PWM輸出控制LED呼吸燈的例子
#include<iostream> #include<wiringPi.h> #include<cstdlib> using namespace std;const int PWMpin = 1; //只有wiringPi編號下的1腳(BCM標號下的18腳)支持 void setup();int main() {setup();int val = 0;int step = 2;while(true){if(val>1024){step = -step; val = 1024;}else if(val<0){step = -step;val = 0;}pwmWrite(PWMpin,val);val+=step;delay(10);}return 0; }void setup() {if(-1==wiringPiSetup()){cerr<<"setup error\n";exit(-1);}pinMode(PWMpin,PWM_OUTPUT); } View Code?
時間控制函數
| unsigned int millis (void) | 這個函數返回 一個 從你的程序執行 wiringPiSetup? 初始化函數(或者wiringPiSetupGpio ) 到 當前時間 經過的 毫秒數。 返回類型是unsigned int,最大可記錄 大約49天的毫秒時長。 |
| unsigned int micros (void) | 這個函數返回 一個 從你的程序執行 wiringPiSetup? 初始化函數(或者wiringPiSetupGpio ) 到 當前時間 經過的 微秒數。 返回類型是unsigned int,最大可記錄 大約71分鐘的時長。 |
| void delay (unsigned int howLong) | 將當前執行流暫停 指定的毫秒數。因為Linux本身是多線程的,所以實際暫停時間可能會長一些。參數是unsigned int 類型,最大延時時間可達49天 |
| void delayMicroseconds (unsigned int howLong) | 將執行流暫停 指定的微秒數(1000微秒 = 1毫秒 = 0.001秒)。 因為Linux本身是多線程的,所以實際暫停時間可能會長一些。參數是unsigned int 類型,最大延時時間可達71分鐘 |
?
?
?
?
中斷
wiringPi提供了一個中斷處理注冊函數,它只是一個注冊函數,并不處理中斷。他無需root權限。
?
| int wiringPiISR (int pin, int edgeType,? void (*function)(void)) | 返回值:返回負數則代表注冊失敗 pin:接受中斷信號的引腳 edgeType:觸發的方式。 ?INT_EDGE_FALLING:下降沿觸發 function:中斷處理函數的指針,它是一個無返回值,無參數的函數。 | 注冊的函數會在中斷發生時執行 和51單片機不同的是:這個注冊的中斷處理函數會和main函數并發執行(同時執行,誰也不耽誤誰) 當本次中斷函數還未執行完畢,這個時候樹莓派又觸發了一個中斷,那么這個后來的中斷不會被丟棄,它仍然可以被執行。但是wiringPi最多可以跟蹤并記錄后來的僅僅1個中斷,如果不止1個,則他們會被忽略,得不到執行。 |
?
?
?通過1腳檢測 因為按鍵按下引發的 下降沿,觸發中斷,反轉11控制的LED
?
#include<iostream> #include<wiringPi.h> #include<cstdlib> using namespace std;void ButtonPressed(void); void setup();/********************************/ const int LEDPin = 11; const int ButtonPin = 1; /*******************************/int main() {setup();//注冊中斷處理函數if(0>wiringPiISR(ButtonPin,INT_EDGE_FALLING,ButtonPressed)){cerr<<"interrupt function register failure"<<endl;exit(-1);}while(1);return 0; }void setup() {if(-1==wiringPiSetup()){cerr<<"wiringPi setup error"<<endl;exit(-1);}pinMode(LEDPin,OUTPUT); //配置11腳為控制LED的輸出模式digitalWrite(LEDPin,LOW); //初始化為低電平 pinMode(ButtonPin,INPUT); //配置1腳為輸入pullUpDnControl(ButtonPin,PUD_UP); //將1腳上拉到3.3v }//中斷處理函數:反轉LED的電平 void ButtonPressed(void) {digitalWrite(LEDPin, (HIGH==digitalRead(LEDPin))?LOW:HIGH );} View Code?
?
?
多線程
wiringPi提供了簡單的Linux系統下的通用的 Posix threads線程庫接口來支持并發。
?
| int piThreadCreate(name) | name:被包裝的線程執行函數 返回:狀態碼。返回0表示成功啟動,反之失敗。 源代碼: int piThreadCreate (void *(*fn)(void *)) {pthread_t myThread ;return pthread_create (&myThread, NULL, fn, NULL) ; } | 包裝一個用PI_THEEAD定義的函數為一個線程,并啟動這個線程。 首先你需要通過以下方式創建一個特特殊的函數,這個函數中的代碼就是在新的線程中將執行的代碼。,myTread是你自己線程的名字,可自定義。 PI_THREAD (myThread) {//在這里面寫上的代碼會和主線程并發執行。 }在wiringPi.h中,我發現這樣一個宏定義:#define PI_THREAD(X) void *X (void *dummy) 那么,被預處理后我們寫的線程函數會變成下面這個樣子,請注意返回值,難怪我每次寫都會警告,因為沒有返回一個指針, 那么,以后注意返回NULL,或者 (void*)0 void *myThread (void *dummy) {//在這里面寫上的代碼會和主線程并發執行。 } |
| piLock(int keyNum) | keyNum:0-3的值,每一個值代表一把鎖 | 使能同步鎖。wiringPi只提供了4把鎖,也就是keyNum只能取0~3的值,官方認為有這4把鎖就夠了。 keyNum:0,1,2,3 每一個數字就代表一把鎖。 源代碼: void piLock (int keyNum) ? |
| piUnlock(int keyNum) | keyNum:0-3的值,每一個值代表一把鎖 | 解鎖,或者說讓出鎖。 源代碼: void piUnlock (int key) |
| int piHiPri (int priority) | priority:優先級指數,0~99 返回值:0,成功 ???????? -1:,失敗 | 設定線程的優先級,設定線程的優先級變高,不會使程序運行加快,但會使這個線程獲得相當更多的時間片。priority是相對的。比如你的程序只用到了主線程, 和另一個線程A,主線程設定優先級為1,A線程設定為2,那也代表A比main線程優先級高。 |
?
?
凡是涉及到多線程編程,就會涉及到線程安全的問題,多線程訪問同一個數據,需要使用同步鎖來保障數據操作正確性和符合預期。
當A線程鎖上 鎖S 后,其他共用這個鎖的競爭線程,只能等到鎖被釋放,才能繼續執行。
成功執行了piLock 函數的線程將擁有這把鎖。其他線程想要擁有這把鎖必須等到這個線程釋放鎖,也就是這個線程執行piUnlock后。
同時要擴展的知識是:volatile 這個C/C++中的關鍵字,它請求編譯器不緩存這個變量的數據,而是每次都從內存中讀取。特別是在多線程下共享放變量,必須使用volatile關鍵字聲明才是保險的。
?
?
?
?
?
?
softPwm,軟件實現的PWM
樹莓派硬件上支持的PWM輸出的引腳有限,為了突破這個限制,wiringPi提供了軟件實現的PWM輸出API。
需要包含頭文件:#include <softPwm.h>
編譯時需要添pthread庫鏈接? -lpthread
?
| int softPwmCreate (int pin, int initialValue, int pwmRange) | pin:用來作為軟件PWM輸出的引腳 initalValue:引腳輸出的初始值 pwmRange:PWM值的范圍上限 建議使用100. 返回:0表示成功。 | 使用一個指定的pin引腳創建一個模擬的PWM輸出引腳 |
| void softPwmWrite (int pin, int value) | pin:通過softPwmCreate創建的引腳 value:PWM引腳輸出的值 | 更新引腳輸出的PWM值 |
?
?
串口通信
使用時需要包含頭文件:#include <wiringSerial.h>
| int serialOpen (char *device, int baud) | device:串口的地址,在Linux中就是設備所在的目錄。 默認一般是"/dev/ttyAMA0",我的是這樣的。 ?baud:波特率 返回:正常返回文件描述符,否則返回-1失敗。 | 打開并初始串口 |
void serialClose (int fd) | fd:文件描述符 | 關閉fd關聯的串口 |
| void? serialPutchar (int fd, unsigned char c) | fd:文件描述符 c:要發送的數據 | 發送一個字節的數據到串口 |
| void? serialPuts (int fd, char *s) | fd:文件描述符 s:發送的字符串,字符串要以'\0'結尾 | 發送一個字符串到串口 |
| void? serialPrintf (int fd, char *message, …) | fd:文件描述符 message:格式化的字符串 | 像使用C語言中的printf一樣發送數據到串口 |
| int?? serialDataAvail (int fd) | fd:文件描述符 返回:串口緩存中已經接收的,可讀取的字節數,-1代表錯誤 | ?獲取串口緩存中可用的字節數。 |
| int serialGetchar (int fd) | fd:文件描述符 返回:讀取到的字符 | 從串口讀取一個字節數據返回。 如果串口緩存中沒有可用的數據,則會等待10秒,如果10后還有沒,返回-1 所以,在讀取前,做好通過serialDataAvail判斷下。 |
| void serialFlush (int fd) | fd:文件描述符 | 刷新,清空串口緩沖中的所有可用的數據。 |
| *size_t write (int fd,const void * buf,size_t count) | fd:文件描述符 buf:需要發送的數據緩存數組 count:發送buf中的前count個字節數據 返回:實際寫入的字符數,錯誤返回-1 | 這個是Linux下的標準IO庫函數,需要包含頭文件#include <unistd.h> 當要發送到的數據量過大時,wiringPi建議使用這個函數。 |
| *size_t read(int fd,void * buf ,size_t count); | fd:文件描述符 buf:接受的數據緩存的數組 count:接收的字節數. 返回:實際讀取的字符數。 | 這個是Linux下的標準IO庫函數,需要包含頭文件#include <unistd.h> 當要接收的數據量過大時,wiringPi建議使用這個函數。 |
?
初次使用樹莓派串口編程,需要配置。我開始搞了很久,以為是程序寫錯了 還一直在調試。。。(~ ̄— ̄)~
/* 修改 cmdline.txt文件 */ >cd /boot/ >sudo vim cmdline.txt刪除【】之間的部分 dwc_otg.lpm_enable=0 【console=ttyAMA0,115200】 kgdboc=ttyAMA0,115200 console=tty1 root=/dev/mmcblk0p2 rootfstype=ext4 elevator=deadline rootwait/*修改 inittab文件 */ >cd /etc/ >sudo vim inittab注釋掉最后一行內容:,在前面加上 # 號 #T0:23:respawn:/sbin/getty -L ttyAMA0 115200 vt100sudo reboot 重啟?
?
下面是雙機通信的一個例子
C51代碼,作為串口通信的接發送。serial庫請看另一篇文章
#include<reg52.h> #include"serial.h"/**********function****************/ bit isOpenPressed(void); bit isClosePressed(void); void delay(unsigned int t); /*********************************/sbit closeButton = P2^0; //與關閉按鍵相連的引腳 sbit openButton = P2^1; //與打開按鍵相連的引腳void main(void) {closeButton = 1; //拉高openButton = 1; //拉高 EA =1; //打開總中斷serial_init(9600); //初始化51串口while(1){if(isClosePressed()) //如果關閉按鈕按下 {serial_write(0); //發送數據 0給樹莓派delay(10);}else if(isOpenPressed()) //如果打開按鈕按下 {serial_write(1); //發送數據 1給樹莓派delay(10);}} }bit isOpenPressed(void) {bit press =0;if(0==openButton){delay(5);if(0==openButton){while(!openButton);press = 1;}}return press; }bit isClosePressed(void) {bit press =0;if(0==closeButton){delay(5);if(0==closeButton){while(!closeButton);press = 1;}}return press; }void delay(unsigned int t) {unsigned int i ;unsigned char j;for(i = t;i>0;i--)for(j=120;j>0;j--); } View Code?
樹莓派代碼,作為串口通信的接收方
#include<iostream> #include<cstdlib> #include<wiringPi.h> #include<wiringSerial.h> using namespace std;void setup(); const int LEDPin = 11;int main() {setup();int fd; //Linux 的思想是:將一切IO設備,都看做 文件,fd就是代表串口抽象出來的文件if((fd = serialOpen("/dev/ttyAMA0",9600))==-1) //初始化串口,波特率9600 {cerr<<"serial open error"<<endl;exit(-1);}while(true){if(serialDataAvail(fd) >= 1) //如果串口緩存中有數據 {int data = serialGetchar(fd);if(data==0) //接受到51發送的 數據 0 {// close led digitalWrite(LEDPin,LOW);}else if(data==1) //接受到51發送的 數據 1 {//open led digitalWrite(LEDPin,HIGH);}}}return 0; }void setup() {if(-1==wiringPiSetup()){cerr<<"set up error"<<endl;exit(-1);}pinMode(LEDPin,OUTPUT);digitalWrite(LEDPin,HIGH);} View Code?
?
shift移位寄存器芯片API
需要包含頭文件? #include <wiringShift.h>
| void shiftOut (uint8_t dPin, uint8_t cPin, uint8_t order, uint8_t val) | dPin:移位芯片的串行數據入口引腳,比如74HC595的SER腳 cPin:移位芯片的時鐘引腳。如74HC595的11腳 order: ?? LSBFIRST 先發送數據的低位 ?? MSBFIRST先發送數據的高位 ????? val:要發送的8位數據 | 將val串化,通過芯片轉化為并行輸出 如常見的74HC595 |
| ?uint8_t shiftIn (uint8_t dPin, uint8_t cPin, uint8_t order) | 同上。 | ? 將并行數據,通過芯片轉化為串行輸出。 |
?
用過595的都知道還有一個引腳:12腳,Rpin,用于把移位寄存器中的數據更新到存儲寄存器中,然后wiringPi的API中沒有使用這個引腳。我建議使用的時候自己加上。
?
#include<iostream> #include<wiringPi.h> #include <wiringShift.h> #include<cstdlib> using namespace std;const int SERpin = 1; //serial data input const int SCKpin = 2; //shift register clock const int RCKpin = 3; // storage register clock /************************/ void setup();/*************************/int main() {setup();for(int i=0;i<8;++i){digitalWrite(RCKpin,LOW);shiftOut(SERpin,SCKpin,LSBFIRST,1<<i);digitalWrite(RCKpin,HIGH);delay(800);}return 0;}void setup() {if(-1==wiringPiSetup()){cerr<<"setup error\n";exit(-1);}pinMode(SERpin,OUTPUT);pinMode(RCKpin,OUTPUT);pinMode(SCKpin,OUTPUT);} View Code?
?
?
樹莓派硬件平臺特有的API
并沒有列全,我只是列出了相對來說有用的,其他的,都基本不會用到。
| pwmSetMode (int mode) | mode:PWM運行模式 | 設置PWM的運行模式。 pwm發生器可以運行在2種模式下,通過參數指定: |
| pwmSetRange (unsigned int range) | range,范圍的最大值 0~range | 設置pwm發生器的數值范圍,默認是1024 |
| pwmSetClock (int divisor) | ? | This sets the divisor for the PWM clock. To understand more about the PWM system, you’ll need to read the Broadcom ARM peripherals manual. |
| piBoardRev (void) | 返回:樹莓派板子的版本編號 1或者2 | / |
?
?
就這樣,以后會更新。
?
?
?
?歡迎轉載,請注明出處:www.cnblogs.com/lulipro
?為了獲得更好的閱讀體驗,請訪問原博客地址。
限于本人水平,如果文章和代碼有表述不當之處,還請不吝賜教。
代碼鋼琴家
?
總結
以上是生活随笔為你收集整理的树莓派wiringPi库详解的全部內容,希望文章能夠幫你解決所遇到的問題。