今天学习了Linux的定时器功能,并复刻了学习51单片机时玩的智能感应垃圾桶项目。总体感觉难度不大。
先捏软柿子,sg90舵机,应该是市面上能能买到的最便宜的舵机了吧(我瞎说的,如果不是可以发链接给我)。我手上的sg90有3根线,红、褐线分别是Vcc和GND,橙黄线输入PWM波。通过改变PWM波的占空比可以改变sg90舵机的旋转角度。
sg90舵机
输入的PWM波频率为50Hz,一个周期即为20ms。当占空比为1 : 40时,舵机角度转向0°(初始角度或参考角度),占空比为1 :20时,舵机转角为45°,为3:40转90°,为1:10转135°,为1:8时转180°。
所以这里就需要定出一个20ms的总时长,每次计数0.5ms的定时器来输出PWM波。
接下来就说一说Linux下的定时器功能。
定时器功能,是通过itimerval结构体以及函数setitimer产生的信号,系统随之使用signal信号处理
函数来处理产生的定时信号而实现的。
struct itimerval
{
/* Value to put into `it_value' when the timer expires. */
struct timeval it_interval;
/* Time to the next timer expiration. */
struct timeval it_value;
};
/*it_interval:定时器的单次计数时间间隔*/
/*it_value:定时器的启动时间*/
struct timeval
{
__time_t tv_sec; /* Seconds. */
__suseconds_t tv_usec; /* Microseconds. */
};
int setitimer (__itimer_which_t __which,
const struct itimerval *__restrict __new,
struct itimerval *__restrict __old)
setitimer()将value指向的结构体设为计时器的当前值,如果ovalue不是NULL,将返回计时器原有值。
函数形参的which有3种:
ITIMER_REAL //数值为0,计时器的值实时递减,发送的信号是SIGALRM。
ITIMER_VIRTUAL //数值为1,进程执行时递减计时器的值,发送的信号是SIGVTALRM。
ITIMER_PROF //数值为2,进程和系统执行时都递减计时器的值,发送的信号是SIGPROF。
我们这里使用第一种:
signal(SIGALRM,signal_handler);
返回说明:
成功执行时,返回0。失败返回-1
signal_handler函数很像51,32单片机的中断处理函数,可以类比理解。
有了上面两个知识,稍微结合一下,就可以实现键盘输入数字,舵机对应旋转到指定角度了。
代码如下:
#include "stdio.h"
#include "sys/time.h"
#include "stdlib.h"
#include "signal.h"
#include "wiringPi.h"
#define SG90_PIN 5
int angle;
void signal_handler(int signum)
{
static int i = 0;
if(i <= angle)
{
digitalWrite(SG90_PIN, HIGH);
}
else
{
digitalWrite(SG90_PIN, LOW);
}
if(i == 40)
{
i = 0;
}
i++;
}
int main(void)
{
struct itimerval itv;
wiringPiSetup();
pinMode(SG90_PIN, OUTPUT);
angle = 0;
/*setting the interval*/
itv.it_interval.tv_sec = 0;
itv.it_interval.tv_usec = 500;
/*setting the beginning time*/
itv.it_value.tv_sec = 1;
itv.it_value.tv_usec = 0;
/*setting the timer_mode*/
if(setitimer(ITIMER_REAL, &itv, NULL) == -1)
{
perror("error");
exit(-1);
}
/*handle the signal*/
signal(SIGALRM, signal_handler);
while(1)
{
printf("please input the angle: 1(0), 2(45), 3(90), 4(135)");
scanf("%d", &angle);
}
return 0;
}
对应现象就是键盘输入1时,舵机转向0度,2->45度,3->90度,4->135度,5->180度。
和上一篇日志内容相结合就可以实现感应垃圾的主体结构了
代码如下:
#include "stdio.h"
#include "sys/time.h"
#include "stdlib.h"
#include "signal.h"
#include "wiringPi.h"
#include "unistd.h"
#define Trig 0
#define Echo 1
#define SG90_PIN 5
int angle;
double getDistance(void)
{
struct timeval start, stop;
long diffTime;
double dist;
pinMode(Trig, OUTPUT);
pinMode(Echo, INPUT);
digitalWrite(Trig, LOW);
usleep(5);
digitalWrite(Trig, HIGH);
usleep(10);
digitalWrite(Trig, LOW);//HC-04 Trig signal
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);
printf("diffTime = %ld\n", diffTime);
dist = (double)diffTime/1000000*340/2*100;//(unit:cm)
return dist;
}
void signal_handler(int signum)
{
static int i = 0;
if(i <= angle)
{
digitalWrite(SG90_PIN, HIGH);
}
else
{
digitalWrite(SG90_PIN, LOW);
}
if(i == 40)
{
i = 0;
}
i++;
}
void init_SG90()
{
angle = 0; //init angle
pinMode(SG90_PIN, OUTPUT);
/*setting the interval*/
itv.it_interval.tv_sec = 0;
itv.it_interval.tv_usec = 500;
/*setting the beginning time*/
itv.it_value.tv_sec = 1;
itv.it_value.tv_usec = 0;
/*setting the timer_mode*/
if(setitimer(ITIMER_REAL, &itv, NULL) == -1)
{
perror("error");
exit(-1);
}
/*handle the signal*/
signal(SIGALRM, signal_handler);
}
int main(void)
{
double distance;
if(wiringPiSetup() == -1)
{
fprintf(stderr, "%s", "initWiringPi error");
exit(-1);
}
init_SG90();
while(1)
{
distance = getDistance();
if(distance < 10)
{
angle = 5;
}
else
{
angle = 2;
}
sleep(2);
}
return 0;
}
现象就是当物体距离超声波传感器小于10cm时,舵机转动,大于10cm时恢复角度。