樹莓派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

     


作用:配置引腳的IO模式

注意:
只有wiringPi 引腳編號下的1腳(BCM下的18腳) 支持PWM輸出

只有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        不啟用任何拉電阻。關閉拉電阻。
             PUD_DOWN    啟用下拉電阻,引腳電平拉到GND
             PUD_UP         啟用上拉電阻,引腳電平拉到3.3v

對一個設置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:下降沿觸發
 INT_EDGE_RISING:上升沿觸發
 INT_EDGE_BOTH :上下降都會觸發
 INT_EDGE_SETUP:編程時用不到。       

    

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)
{
  pthread_mutex_lock (&piMutexes [keyNum]) ;
}

 

piUnlock(int keyNum) keyNum:0-3的值,每一個值代表一把鎖

解鎖,或者說讓出鎖。

源代碼:

void piUnlock (int key)
{
  pthread_mutex_unlock (&piMutexes [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 vt100 sudo 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種模式下,通過參數指定:
     PWM_MODE_BAL   :樹莓派默認的PWM模式
     PWM_MODE_MS    :傳統的pwm模式,

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

 為了獲得更好的閱讀體驗,請訪問原博客地址。

限於本人水平,如果文章和代碼有表述不當之處,還請不吝賜教。

代碼鋼琴家



 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM