【回眸】香橙派Zero2 超声波模块测距控制SG90舵机转动
前言
知识准备
超声波模块时序图
gettimeofday()函数作用
gettimeofday()函数原型
tv结构体
获取当前系统时间与格林威治时间的时间差
获取香橙派数10万秒花费的时间
使用超声波模块获取到障碍物距离
SG90舵机模块
舵机模块的作用
舵机模块方波时序图
舵机模块工作原理
知识准备
setitimer时间函数原型
使用setitimer函数实现1秒钟打印一次hello
连接舵机和香橙派
舵机控制与PWM方波关系
后记
前言
这个系列的上一篇文章介绍了香橙派的基础功能和作用,还用通过代码让蜂鸣器响起来。
本篇博文介绍如何使用香橙派使超声波模块完成测距功能。
知识准备
超声波模块时序图
通过 距离 = 速度 * 时间这个公式能够得到与障碍物的距离,注意障碍物不能太小,也最好比较平整,避免有误差。
在写测距代码之前,先了解一下Linux下获取时间的函数 gettimeofday()
gettimeofday()函数作用
作用:获取当前系统时间与格林威治(GTM/UTC)的时间差
优点:可以精确到微秒级别且系统调用小
gettimeofday()函数原型
#include<sys/time.h>
int gettimeofday(struct timeval* tv, struct timezone* tz);
tv结构体
struct timeval
{time_t tv_sec;suseconds_t tv_usec;
}
tz在早期是用来获取时区,目前已经废弃,设置为NULL即可。
获取当前系统时间与格林威治时间的时间差
新建一个c文件命名为time,使用该函数获取当前系统时间与格林威治时间的时间差,用秒和微妙分别表示。
time.c
#include <stdio.h>
#include <sys/time.h>
int main(){struct timeval tv;gettimeofday(&tv,NULL);printf("sec = %ld,usec = %ld\n",tv.tv_sec,tv.tv_usec);return 0;
}
获取香橙派数10万秒花费的时间
再新建一个c文件命名为timeCnt10.c,预期效果是返回香橙派数10万秒花费的时间,单位是微秒
#include <stdio.h>
#include <sys/time.h>
void cntTest(){int i ,j;for(i=0;i<100;i++){for(j=0;j<1000;j++);}
}int main(){struct timeval timestop;struct timeval timestart;long diffTime;gettimeofday(×tart,NULL);cntTest();gettimeofday(×top,NULL);diffTime = 1000000*(timestop.tv_sec - timestart.tv_sec) + (timestop.tv_usec - timestart.tv_usec);printf("H616 count 10w S use time = %ld us\n",diffTime);return 0;
}
可以看到每次运行的结果都不一样,基本上稳定在1460左右,说明香橙派的资源每次需要1460毫秒左右 。
使用超声波模块获取到障碍物距离
echo.c
注意这里的diffTime 单位是微秒
dis的单位是厘米
#include <stdio.h>
#include <sys/time.h>
#include <wiringPi.h>
#include <unistd.h>
#include <stdlib.h>#define Trig 0
#define Echo 1double getDistance()
{struct timeval start;struct timeval stop;long diffTime;double dis;pinMode(Trig, OUTPUT);pinMode(Echo, INPUT);digitalWrite(Trig, LOW);usleep(5);digitalWrite(Trig, HIGH);usleep(10);digitalWrite(Trig, LOW);while (!(digitalRead(Echo)));gettimeofday(&start, NULL);while (digitalRead(Echo));gettimeofday(&stop, NULL);diffTime = 1000000 * (stop.tv_sec - start.tv_sec) + (stop.tv_usec - start.tv_usec);dis = ((double)diffTime / 1000000 * 34000 / 2);return dis;
}int main()
{double dis;if (wiringPiSetup() == -1){fprintf(stderr, "error:%s", "initWiringPi error");exit(-1);}while (1){dis = getDistance();printf("dis = %lf\n", dis);usleep(500000);}return 0;
}
触发的引脚从超声波模块连接到物理3PIN脚(靠左一列的第二行针脚)
echo的引脚从超声波模块连接到物理5PIN脚(靠左一列的第三行针脚)
VCC的线需要连接到5V的物理口,如果接到3.3V的物理口则无法正常驱动。
GND的线需要接到任意GND的物理口。
SG90舵机模块
舵机模块的作用
舵机模块的作用是通过编程可以将塑料齿轮转不同的角度,用于精确控制角度位置。
舵机模块方波时序图
舵机模块工作原理
向SG90舵机模块的黄色线输入PWM方波,不同占空比对应着不同角度。
SG90 黄色线输入频率是50HZ,周期是20ms的方波。
0.5ms-------------0度; 2.5% 对应函数中CCRx为5
1.0ms------------45度; 5.0% 对应函数中CCRx为10
1.5ms------------90度; 7.5% 对应函数中CCRx为15
2.0ms-----------135度; 10.0% 对应函数中CCRx为20
2.5ms-----------180度; 12.5% 对应函数中CCRx为25
这里舵机是否能够转动180度还是要具体看硬件是否支持。
知识准备
在实现SG90功能开发之前,还需要了解一个时间函数。
setitimer时间函数原型
int setitimer(int which,const struct itimerval *new_value,struct itimerval *old_value);
new_value和old_value的数据结构为:
struct itimerval {struct timeval it_interval; /* Interval for periodic timer */struct timeval it_value; /* Time until next expiration */
};struct timeval {time_t tv_sec; /* seconds */suseconds_t tv_usec; /* microseconds */
};
使用setitimer函数实现1秒钟打印一次hello
设置的时间单位是500微秒,需要2000个500微秒才是1秒
clock.c
#include <stdio.h>
#include <sys/time.h>
#include <stdlib.h>
#include <signal.h>static int i;void signal_handler(int signum)
{i++;if (i == 2000){printf("hello\n");i = 0;}
}int main()
{int res = 0;struct itimerval itv;// 设定定时时间itv.it_interval.tv_sec = 0;itv.it_interval.tv_usec = 500;// 设定开始生效,启动定时器的时间itv.it_value.tv_sec = 1;itv.it_value.tv_usec = 0;// 设定定时方式res = setitimer(ITIMER_REAL, &itv, NULL);if (res == -1){perror("error\n");exit(-1);}// 信号处理signal(SIGALRM, signal_handler);while (1);return 0;
}
连接舵机和香橙派
按照上图连接舵机和香橙派
注意SG90模块也和超声波模块一样,需要一个5V的电压,所以VCC需要连接5V的IO
GND也要选GND的IO
SG90Pin在代码里是5,对应物理PIN口是11
像上图示例这样连接舵机和树莓派,注意接地和VCC如果接反了会烧掉舵机。另外舵机的线是连在一起的,可以剪开杜邦线嫁接一下再用电工胶带绑一下连接处。
或者使用公对母杜邦线衔接一下也可以。
舵机控制与PWM方波关系
舵机的控制一般需要一个20ms 左右的时基脉冲,该脉冲的高电平部分一般为0.5ms~2.5ms 范围内的角度控制脉冲部分。以180 度角度伺服为例,那么对应的控制关系是这样的:
0.5ms ---------- 0 °;
1.0ms ---------- 45 °;
1.5ms ---------- 90 °;
2.0ms ---------- 135 °;
2.5ms ---------- 180 °;
方波的总周期是20ms,设置的时间单位是500微秒,就是40个单位为20ms,所以当i等于1时,舵机角度为0°,当i等于2时,舵机角度为45°,当i等于3时,舵机角度为90°,当i等于4时,舵机角度为135°,当i等于5时,舵机角度为180°。
SG90.c
#include <stdio.h>
#include <sys/time.h>
#include <stdlib.h>
#include <signal.h>
#include <wiringPi.h>#define SG90Pin 5static int i = 0;
static int angle = 0;void signal_handler(int signum)
{if (i <= angle){digitalWrite(SG90Pin, HIGH);}else{digitalWrite(SG90Pin, LOW);}if (i == 40){i = 0;}i++;
}int main()
{int res;struct itimerval itv;wiringPiSetup();pinMode(SG90Pin, OUTPUT);// 设定定时时间itv.it_interval.tv_sec = 0;itv.it_interval.tv_usec = 500;// 设定开始生效,启动定时器的时间itv.it_value.tv_sec = 1;itv.it_value.tv_usec = 0;// 设定定时方式res = setitimer(ITIMER_REAL, &itv, NULL);if (res == -1){perror("error\n");exit(-1);}// 信号处理signal(SIGALRM, signal_handler);while (1){printf("Please input angle: 1-0 2-45 3-90 4-135 5-180\n");scanf("%d", &angle);}return 0;
}
预期看到的效果是输入1---5,可以让舵机转向不同的方向,最终结果也是符合预期的。
后记
要是在上大学的时候搞这个还有点小困难,工作几年以后发现简单不少,时间是最好的老师??