说明
本文详细记一下树莓派3B中进行超声波测距的方法。
超声波测距模块:HC-SR04
一共四个引脚:
- VCC:超声波模块电源脚,接5V电源即可
- Trig:超声波发送脚,高电平时发送出40KHZ出超声波
- Echo:超声波接收检测脚,当接收到返回的超声波时,输出高电平
- GND:超声波模块GND
主板:树莓派3B
GPIO说明:
利用树莓派的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; }
至此,树莓派上面三种超声波测距方式介绍完成。
其实这一篇文章是为了下一篇做铺垫,下一篇文章涉及到多核多线程,语言混编等内容,内容较多,故提前写一篇介绍一下超声波测距这部分的代码,以便后文引用。