Collect from 追梦人物的博客
Modified by ACool

树莓派3B 超声波测距传感器Python GPIO/WPI/BCM三种方式

说明

本文详细记一下树莓派3B中进行超声波测距的方法。

超声波测距模块:HC-SR04

一共四个引脚:

  • VCC:超声波模块电源脚,接5V电源即可
  • Trig:超声波发送脚,高电平时发送出40KHZ出超声波
  • Echo:超声波接收检测脚,当接收到返回的超声波时,输出高电平
  • GND:超声波模块GND

主板:树莓派3B

GPIO说明:

ID1Eq.jpg

利用树莓派的GPIO口进行超声波测距一共有三种方法,分别是Python GPIO,利用C语言的 wiringPi 库,以及C语言的 bcm2835 库,下面就这三种方式分别说明程序编写方法。

Python GPIO

介绍

Python GPIO 是一个小型的 python 库,可以帮助用户完成 raspberry 相关 IO 口操作,但是 python GPIO 库还没有支持 SPI、I2C 或者 1-wire 等总线接口,所以如果要使用这些总线接口的话需要自行编写类或者方法。

安装

2016-03-18-raspbian-jessie 以后的版本都预装了python GPIO,但是为了方便起见,本文仍然介绍一下安装方法,以便未安装这个库的系统使用。

安装命令如下:

#python2安装命令
sudo apt-get install python-dev
sudo apt-get install python-rpi.gpio

#python3安装命令
sudo apt-get install python3-dev
sudo apt-get install python3-rpi.gpio

验证一下:

在命令行中输入python(注意版本,如果写程序用python3则输入python3,否则则是python2)进入python交互式界面,然后再进行如下输入:

import RPi.GPIO as GPIO

如果不报错则说明安装成功。

超声波测距程序

程序如下所示:

import RPi.GPIO as GPIO
import time

Trig = 20 #Trig发送口连的是20口,对应GPIO口的28口
Echo = 21 #Echo接受口连的是21口,对应GPIO口的29口

GPIO.setmode(GPIO.BCM)#这里采用BCM编码方式
GPIO.setup(Trig, GPIO.OUT, initial = GPIO.LOW)
GPIO.setup(Echo, GPIO.IN)

time.sleep(2)

def checkdist():
    while True:
        #发出发信号
        GPIO.output(Trig, GPIO.HIGH)
        time.sleep(0.00015)#发出超声波脉冲,保持15us
        GPIO.output(Trig, GPIO.LOW)
        while not GPIO.input(Echo):
            pass
        t1 = time.time()#发现高电平时开始计时
        while GPIO.input(Echo):
            pass
        t2 = time.time()#高电平结束停止计时
        distance=(t2-t1)*340*100/2
        print 'Distance:%0.2f cm' % distance #打印出测出的距离,便于调试
        time.sleep(0.1)

try:
        checkdist()      
except KeyboardInterrupt:
    GPIO.cleanup()

C 语言 WPI库

说明

WiringPi 是应用于树莓派平台的 GPIO 控制库函数,WiringPi 遵守 GUN Lv3。wiringPi 使用 C 或者 C++ 开发并且可以被其他语言包转,例如 Python、ruby 或者 PHP 等。 wiringPi 包括一套 gpio 控制命令,使用 gpio 命令可以控制树莓派 GPIO 管脚。用户可以利用 gpio 命令通过 shell 脚本控制或查询 GPIO 管脚。wiringPi 是可以扩展的,可以利用 wiringPi 的内部模块扩展模拟量输入芯片,可以使用 MCP23x17/MCP23x08(I2C 或者 SPI)扩展 GPIO 接口。另外可通过树莓派上的串口和 Atmega(例如 arduino 等)扩展更多的 GPIO 功能。另外,用户可以自己编写扩展模块并把自定义的扩展模块集成到 wiringPi 中。WiringPi 支持模拟量的读取和设置功能,不过在树莓派上并没有模拟量设备。但是使用 WiringPi 中的软件模块却可以轻松地应用 AD 或 DA 芯片。

不过就笔者体验来看BCM的使用兼容性更好,某些拓展板不支持 WiringPi 编码方式,而仅仅支持 BCM编码。

安装

在这里推荐使用 git 来安装:

git clone git://git.drogon.net/wiringPi
cd wiringPi
./build

验证一下:

通过以下指令可以测试wiringPi 是否安装成功。

$gpio -v
$gpio readall

如果出现上面的 GPIO 图,则说明安装成功。

超声波测距程序

程序如下所示:

#include <wiringPi.h>    
#include <stdio.h>    
#include <sys/time.h>    
#define Trig 28 //发射口,对应于GPIO 28口
#define Echo 29 //接受口,对应于GPIO 29口



float checkdist(void)    
{    
    struct timeval tv1;  //timeval是time.h中的预定义结构体 其中包含两个一个是秒,一个是微秒  
    struct timeval tv2;
    float dis;    
    long start, stop;    
    if(wiringPiSetup() == -1){ //如果初始化失败,就输出错误信息 程序初始化时务必进行  
        printf("setup wiringPi failed !");    
        return 1;     
    }    
    pinMode(Echo, INPUT);  //设置端口为输入  
    pinMode(Trig, OUTPUT);  //设置端口为输出  
    while(1){
        digitalWrite(Trig, LOW);    
        delayMicroseconds(2);    

        digitalWrite(Trig, HIGH);    
        delayMicroseconds(15);      //发出超声波脉冲,保持15us
        digitalWrite(Trig, LOW);    

        while(!(digitalRead(Echo) == 1));    
        gettimeofday(&tv1, NULL);           //获取当前时间,发现高电平时开始计时   

        while(!(digitalRead(Echo) == 0));    
        gettimeofday(&tv2, NULL);           //获取当前时间 ,高电平结束停止计时 
        /*  
        int gettimeofday(struct timeval *tv, struct timezone *tz);  
        The functions gettimeofday() and settimeofday() can get and set the time as well as a timezone.   
        The use of the timezone structure is obsolete; the tz argument should normally be specified as NULL.  
    */  
        start = tv1.tv_sec * 1000000 + tv1.tv_usec;   //微秒级的时间    
        stop  = tv2.tv_sec * 1000000 + tv2.tv_usec;    

        dis = (float)(stop - start) / 1000000 * 34000 / 2;  //计算时间差求出距离    
        printf("distance = %0.2f cm\n",dis);
        delay(15); //延时15ms
    }
}    

int main(void)    
{    
    checkdist();
    return 0;    
}    

C语言 BCM2835 库

说明

BCM2835 C Library 可以理解为使用C语言实现的相关底层驱动,BCM2835 C Library 的驱动库包括 GPIO、SPI 和 UART 等,可以通过学习 BCM2835 C Library 熟悉 BCM2835 相关的寄存器操作。如果有机会开发树莓派上的 linux 驱动,或自主开发 python 或 PHP 扩展驱动,可以从 BCM2835 C Library 找到不少的“灵感”。

安装

可以从http://www.airspayce.com/mikem/bcm2835/获取最新版本,目前最新版本为bcm2835-1.55.tar.gz

安装并编译:

wget http://www.airspayce.com/mikem/bcm2835/bcm2835-1.55.tar.gz #下载
tar xvzf bcm2835-1.55.tar.gz #解压缩到当前目录
cd bcm2835-1.55
./configure #配置
make #从源代码生成安装包
sudo make check #检查
sudo make install #安装 bcm2835库

bcm2835-1.55 目录下有 example/gpio/gpio.c,这个 gpio.c 为实例程序,编译:

gcc -o gpio gpio.c -lbcm2835

如果没有报错则说明安装成功。

超声波测距程序

程序如下所示:

#include<stdio.h>
#include <bcm2835.h>
#include<termio.h>
#include<sys/time.h>
#include<stdlib.h>

#define GPIO_S 28 //发射口,对应于GPIO 28口
#define GPIO_R 29 //接受口,对应于GPIO 29口

void checkdist(){
    struct timeval tv1;  
        struct timeval tv2; 
    long start, stop; 
    double dis;
    if(!bcm2835_init()){  
        printf("setup bcm2835 failed !");  
        return;   
    } 
    bcm2835_gpio_fsel(GPIO_S, BCM2835_GPIO_FSEL_OUTP); //设置端口为输出  
    bcm2835_gpio_fsel(GPIO_R, BCM2835_GPIO_FSEL_INPT); //设置端口为输入 
    while(1){
        bcm2835_gpio_write(GPIO_S,HIGH);
        bcm2835_delayMicroseconds(15);//延时15us
        bcm2835_gpio_write(GPIO_S,LOW);
        while(!bcm2835_gpio_lev(GPIO_R));

        gettimeofday(&tv1, NULL);
        while(bcm2835_gpio_lev(GPIO_R));

        gettimeofday(&tv2, NULL);
        start = tv1.tv_sec * 1000000 + tv1.tv_usec;   //微秒级的时间  
            stop  = tv2.tv_sec * 1000000 + tv2.tv_usec;
        dis = ((double)(stop - start) * 34000 / 2)/1000000;  //求出距离 
        printf("dist:%lfcm\n",dis);
        bcm2835_delay(15);//延时15ms

    }

}   

int main(void)    
{    
    checkdist();
    return 0;    
}    

至此,树莓派上面三种超声波测距方式介绍完成。

其实这一篇文章是为了下一篇做铺垫,下一篇文章涉及到多核多线程,语言混编等内容,内容较多,故提前写一篇介绍一下超声波测距这部分的代码,以便后文引用。