树莓派控制HC-SR04超声波模块测距(新手向+C语言向)

  因为作业要求使用c语言代码,这里先附上一段摘自网上的代码

  感谢KalaerSun的c语言代码,摘自https://blog.csdn.net/qq_25247589/article/details/62892140

 1 #include <wiringPi.h>  
 2 #include <stdio.h>  
 3 #include <sys/time.h>  
 4 #define Trig    4  
 5 #define Echo    5  
 6   
 7 void ultraInit(void)  
 8 {  
 9     pinMode(Echo, INPUT);  //设置端口为输入
10     pinMode(Trig, OUTPUT);  //设置端口为输出
11 }  
12   
13 float disMeasure(void)  
14 {  
15     struct timeval tv1;  //timeval是time.h中的预定义结构体 其中包含两个一个是秒,一个是微秒
16     /*
17     struct timeval
18     {
19         time_t tv_sec;  //Seconds. 
20         suseconds_t tv_usec;  //Microseconds. 
21     };
22     */
23     
24     struct timeval tv2;  
25     long start, stop;  
26     float dis;  
27   
28     digitalWrite(Trig, LOW);  
29     delayMicroseconds(2);  
30   
31     digitalWrite(Trig, HIGH);  
32     delayMicroseconds(10);      //发出超声波脉冲  
33     digitalWrite(Trig, LOW);  
34       
35     while(!(digitalRead(Echo) == 1));  
36     gettimeofday(&tv1, NULL);           //获取当前时间 开始接收到返回信号的时候 
37   
38     while(!(digitalRead(Echo) == 0));  
39     gettimeofday(&tv2, NULL);           //获取当前时间  最后接收到返回信号的时候
40     /*
41     int gettimeofday(struct timeval *tv, struct timezone *tz);
42     The functions gettimeofday() and settimeofday() can get and set the time as well as a timezone. 
43     The use of the timezone structure is obsolete; the tz argument should normally be specified as NULL.
44     */
45     start = tv1.tv_sec * 1000000 + tv1.tv_usec;   //微秒级的时间  
46     stop  = tv2.tv_sec * 1000000 + tv2.tv_usec;  
47   
48     dis = (float)(stop - start) / 1000000 * 34000 / 2;  //计算时间差求出距离  
49   
50     return dis;  
51 }  
52   
53 int main(void)  
54 {  
55     float dis;  
56   
57     if(wiringPiSetup() == -1){ //如果初始化失败,就输出错误信息 程序初始化时务必进行
58         printf("setup wiringPi failed !");  
59         return 1;   
60     }  
61   
62     ultraInit();  
63       
64     while(1){  
65         dis = disMeasure();  
66         printf("distance = %0.2f cm\n",dis);  
67         delay(1000);  
68     }  
69   
70     return 0;  
71 }  

  因为是刚开始接触树莓派开发,所以文章中可能会出现错误,希望大神们能多多指教,不胜感激。

  PS:以下内容出现顺序按照代码阅读顺序

  1.sys/time.h

    360搜索链接:https://baike.so.com/doc/1233815-1304991.html

  2.wiringPi.h

    wiringPi是一个很棒的树莓派IO控制库,使用C语言开发,提供了丰富的接口:GPIO控制,中断,多线程,等等。

    wiringPi.h详解:https://www.cnblogs.com/lulipro/p/5992172.html

  3.wiringPiSetup(void)

    在使用wiringPi.h库时,在执行任何操作前都必须初始化树莓派,否则程序便无法正常运行。当初始化操作未完成时,函数返回值为-1

    其他的树莓派初始化函数还有wiringPiSetupGpio(void),此函数使用方法与wiringPiSetup(void)类似,当函数无法正常运行时返回值也是-1.不同的地方在于,wiringPiSetup(void)初始化树莓派引脚时使用的是wiringPi 引脚编号表。引脚的编号为 0~16;wiringPiSetupGpio(void)初始化树莓派引脚时使用的是BCM GPIO 引脚编号表。

    其他两种函数还有wiringPiSetupPhys(void)和wiringPiSetupSys (void) ,因为不常用,所以在此处不做介绍。

  4.void pinMode(uint8 pin, WiringPinMode mode)

    这个函数是用来确定引脚的功能的,如果在使用某个引脚之前没有确定这个引脚的功能或者引脚设置模式不正确,就会出现一些不可捉摸的错误。

    这个函数有两个参数,第一个参数pin是一个正整数,用来指定引脚的编号(0-16),第二个参数是用来指定引脚的IO模式,可用的参数有INPUT , OUTPUT , OUTPUT_OPEN_DRAIN , INPUT_ANALOG , INPUT_PULLUP , INPUT_PULLDOWN , INPUT_FLOATING , PWMPWM_OPEN_DRAIN

    每个参数的具体意义请移步:https://book.2cto.com/201311/36087.html

  5.timeval

    在代码中已经告诉了我们整个结构体的来历和定义下的架构,这里不做详解,有兴趣的童鞋请移步https://blog.csdn.net/king16304/article/details/52273834

  6.digitalWrite(uint8 pin, uint8 value)

    这又是一个在wiringPi.h中已经定义好的函数,它的作用是对一个已近配置为输出模式(OUTPUT或者OUTPUT_OPEN_DRAIN)的 引脚  输出指定的电平信号,其中pin是一个正整数,用来指定一个已经初始化过的引脚,value可以是数字或者参数,数字表示下:1代表高电平,0代表低电平;参数表示下:LOW代表低电平,HIGH代表高电平。

  7.delayMicroseconds (unsigned int howLong)

    将线程暂停指定的微秒数(1000微妙=1毫秒=0.001s),因为Linux是多线程的,所以实际暂停的秒数可能比设置的更多一些

  8.digitalRead (int pin)

    读取一个引脚的电平值(LOW / HIGH),并且返回。其中pin是引脚的编号,该引脚的初始化类型必须为INPUT等输入类型。返回值也可以是1 / 0(当输入信号电压在0~1.16 V时该函数返回0,当输入信号在1.83~3.3 V时返回1。如果输入电压在1.16~1.83 V之间不确定会返回0还是1。)

  9.gettimeofday(struct timeval *, struct timezone *);

    则个函数返回的是1970年0:00:00到现在经过的秒数,函数的正常传入时需要用到两个参数。第一个已经介绍过了,第二个因为在这里没有用处,所以暂且不表,传入参数时用NULL即可,这里关于这个问题还有一个小故事:timeval中的tv_sec是time_t类型的,即long的类型。在32位下为4个字节,能够表示的最大正整数是2147483647,而这个表示的时间最大能到2038-01-19 03:14:07,超过了之后就变为-2147483648,这就是linux2038年的问题。而64位系统下的time_t类型即long类型长度为8个字节,可以用到几千亿年,这么长的时间完全不用担心溢出的问题。

  10.根据返回的秒数计算出微秒数

    start = tv1.tv_sec * 1000000 + tv1.tv_usec; 

    stop  = tv2.tv_sec * 1000000 + tv2.tv_usec;    

    我们知道 timeval结构体中含有两个变量,tv_sec表示的是秒数,1秒=1000000微妙,第二个参数tv_usec表示的就是微秒数,所以通过这两个式子我们就求出了开始和结束时的微秒数,然后做差即可得到超声波传递所使用的时间

  11.根据时间计算距离

    (stop - start) / 1000000 * 34000 / 2

    因为stop和start原本表示的微妙,所以做差之后处1000000换算回是多少秒。因为声音在物质中的传播受到物质材质的影响,这里我们暂且不考虑介质的种类,默认为声音是在空气中传播,所以取声音的速度为340m/s=34000cm/s,因为超声波测距的误差较小的范围200-300cm,所以我们这里计算速度时用cm表示。

  


 

  既然我们已经读懂了代码,接下来我们就是硬件方面的链接:

  我们先来了解一下各个硬件分别开来的属性。

  1.树莓派:

    树莓派的历史我们在此不做过多的讲解,其他硬件也暂且不讲,我们先来看一下树莓派上的引脚。

    拿起树莓派,将USB接口向下,面向你那起来,你会发现树莓派右边有两列一共40个引脚,下边这部分告诉了我们这些引脚各自的功能。

 +-----+-----+---------+------+---+---Pi 3---+---+------+---------+-----+-----+  
 | BCM | wPi |   Name  | Mode | V | Physical | V | Mode | Name    | wPi | BCM |  
 +-----+-----+---------+------+---+----++----+---+------+---------+-----+-----+  
 |     |     |    3.3v |      |   |  1 || 2  |   |      | 5v      |     |     |  
 |   2 |   8 |   SDA.1 | ALT0 | 1 |  3 || 4  |   |      | 5V      |     |     |  
 |   3 |   9 |   SCL.1 | ALT0 | 1 |  5 || 6  |   |      | 0v      |     |     |  
 |   4 |   7 | GPIO. 7 |   IN | 1 |  7 || 8  | 1 | ALT5 | TxD     | 15  | 14  |  
 |     |     |      0v |      |   |  9 || 10 | 1 | ALT5 | RxD     | 16  | 15  |  
 |  17 |   0 | GPIO. 0 |  OUT | 0 | 11 || 12 | 0 | IN   | GPIO. 1 | 1   | 18  |  
 |  27 |   2 | GPIO. 2 |   IN | 0 | 13 || 14 |   |      | 0v      |     |     |  
 |  22 |   3 | GPIO. 3 |   IN | 0 | 15 || 16 | 0 | IN   | GPIO. 4 | 4   | 23  |  
 |     |     |    3.3v |      |   | 17 || 18 | 0 | IN   | GPIO. 5 | 5   | 24  |  
 |  10 |  12 |    MOSI | ALT0 | 0 | 19 || 20 |   |      | 0v      |     |     |  
 |   9 |  13 |    MISO | ALT0 | 0 | 21 || 22 | 0 | IN   | GPIO. 6 | 6   | 25  |  
 |  11 |  14 |    SCLK | ALT0 | 0 | 23 || 24 | 1 | OUT  | CE0     | 10  | 8   |  
 |     |     |      0v |      |   | 25 || 26 | 1 | OUT  | CE1     | 11  | 7   |  
 |   0 |  30 |   SDA.0 |   IN | 1 | 27 || 28 | 1 | IN   | SCL.0   | 31  | 1   |  
 |   5 |  21 | GPIO.21 |   IN | 1 | 29 || 30 |   |      | 0v      |     |     |  
 |   6 |  22 | GPIO.22 |   IN | 1 | 31 || 32 | 0 | IN   | GPIO.26 | 26  | 12  |  
 |  13 |  23 | GPIO.23 |   IN | 0 | 33 || 34 |   |      | 0v      |     |     |  
 |  19 |  24 | GPIO.24 |   IN | 0 | 35 || 36 | 0 | IN   | GPIO.27 | 27  | 16  |  
 |  26 |  25 | GPIO.25 |   IN | 0 | 37 || 38 | 0 | IN   | GPIO.28 | 28  | 20  |  
 |     |     |      0v |      |   | 39 || 40 | 0 | IN   | GPIO.29 | 29  | 21  |  
 +-----+-----+---------+------+---+----++----+---+------+---------+-----+-----+  
 | BCM | wPi |   Name  | Mode | V | Physical | V | Mode | Name    | wPi | BCM |  
 +-----+-----+---------+------+---+---Pi 3---+---+------+---------+-----+-----+

    我们重点来介绍这次试验用到的两种引脚:

      ①.电源引脚:分为3.3V / 5V / 0V三种,其中0V代表接地

      ②.GPIO输入输出接口:这是非常重要的一类引脚,上图中标明了GPIO的就是这类接口,树莓派GPIO接口只能输入输出数字信号(0&1等)

  2.超声波模块

    超声波模块的种类有很多,我们此处选择的模块型号为HC-SR04,本模块的优点为性能稳定,测度距离精确,模块高精度,盲区小。探测距离为2cm-450cm。

    

    我们可以看到它一共有四个引脚:

      Vcc:接5V电源(接1号引脚)

      Trig:输出端口(接16号引脚)

      Echo:输入端口(接18号端口)

      Gnd:接地端(接6号端口)

   3.杜邦线

     杜邦线有三种类型,分别为公对母,母对母,公对公。我们这里需要的是四条母对母的杜邦线。

  

    接下来我们就用杜邦线将超声波模块与树莓派连接,连接时一定要注意引脚之间的对应关系。连接成功后如下

    


 

    连接成功后我们就开始测试程序了,先将程序编译成可执行的二进制文件

    

    之后输入ls 查看,我们发现有编译成功后的disMeasure文件

    

    之后我们输入sudo ./disMeasure   开始执行程序,程序运行效果如下。

    

    到此,我们的超声波模块测距部分就算完美收工了。


 

以上就是超声波模块测距的c的代码和解析的部分了,希望能对大家的树莓派学习有帮助。

 

posted @ 2018-04-20 09:31  月沫  阅读(13237)  评论(0编辑  收藏  举报