![](https://i-blog.csdnimg.cn/blog_migrate/238ad239bb6d44a0543fec7fc239d48a.jpeg)
一、简单认识遥控器
接下来我们要了解的是摇控器,你如果对摇控器有一定了解的话可能听说过“日本手”和“美国手”这两种不同的摇控器。其实它们并没有什么本质差别。假设我们的四轴已经可以成功的飞行,我们需要摇控器上的一个控制杆可以控制四个电机的整体转数,也就是人们常说的“油门”。当然,油门这个词用在四轴上可能不太贴切,它的作用就是控制电机转数,可快,可慢,而所谓的“日本手”摇控器是油门在右侧,“美国手”摇控器的油门则在左侧:
![](https://i-blog.csdnimg.cn/blog_migrate/9199a5fba3eb13c41c86b19aafa30952.png)
关于选择日本手还是美国手,我们还建议是根据你自己的习惯(其实没有什么区别)。另外摇控器上还有很多其它的功能控制旋钮,你可能被说明书搞的头晕脑涨,不过你完全不用担心。我们的四轴只用摇控器的3个通道,也就是“前/后”、“左/右”、“上、下”。摇控器其实是两个设备,包括一个发射器和一个接收机,接收机就是我们要接在树莓派上的。发射器就是我们手里拿的摇控器。我们不管用的是什么日本手还是美国手,先来看看它的4个通道:
![](https://i-blog.csdnimg.cn/blog_migrate/20e8c954c46260c172c2e910c6d1a4da.jpeg)
也就是说,我们只用到3个通道来控制四轴
,“前/后(俯仰)”、“左/右(横滚)”、“上、下(油门)”,而对于“航向”通道我们暂时不做处理
。而用于这3个功能的通道我们可以自己定义,你想让哪个通道来控制油门都可以。我们将接收器连接到树莓派的GPIO接口上,用哪一个接口都可以。接收机上通常有4个通道的信号输出接口(你的接收机或许是6通道的,或者更多,但我们只使用其中的3个通道),每一个信号接口都有3根引脚,分别是5V、GROUND、SIGNAL,所有的5V都接在树莓派的5V引脚上;所有GROUND都接在GND引脚上。
注意:遥控器在第一次使用时需要进行遥控器与接收机的对码,如何给遥控器对码请参见购买时的使用说明书,这里我们不再赘述。
![](https://i-blog.csdnimg.cn/blog_migrate/c3714e5c621b108c441e416e769a98f3.jpeg)
这里,我们用的接线引脚是15、16、1。这只是个例子,在实际接线时一定要注意引脚的编号,别弄错了。当接线完成时,你就可以写一个程序来读取接收机的3个通道数据。
首先初始化引脚15为输入引脚(这与我们之前用的电调是一个引脚号,在实际接线时要把电调和接收机信号分开接到不用的GPIO引脚上)为输入模式:
wiringPiSetup();
pinMode(15, INPUT);
二、使用循环计时方式读取遥控器信号(不推荐)
再来看一下如何通过程序来读取树莓派的引脚,从而取得摇控器接收器的通道值:
void driver_ctl_LR()
{
//高电平开始
struct timeval start;
//高电平结束
struct timeval end;
int s = 0;
int f = 0;
//循环接收信号
while (1)
{
s = 0;
f = 0;
do
{
//读取引脚的值
int lr = digitalRead(15);
//如果是高电平则计时开始
if (lr == 1 && s == 0)
{
gettimeofday(&start, NULL);
s = 1;
}
//如果已经开始计时,并且读到低电平,说明信号读取结束
if (s == 1 && lr == 0)
{
//高电平结束(低电平)
gettimeofday(&end, NULL);
//计算高电平持续时间
long timer = (end.tv_sec - start.tv_sec) * 1000000 + (end.tv_usec - start.tv_usec);
//得到PWM信号做出相应动作
engine_lr_pwm(timer);
break;
}
//超时检测
if (s == 0 && lr == 0)
{
if (f == 0)
{
gettimeofday(&start, NULL);
f = 1;
}
//如果持续低电平
if (f == 1)
{
//如果低电平持续时间超过1秒,跳出
gettimeofday(&end, NULL);
long timer = (end.tv_sec - start.tv_sec) * 1000000 + (end.tv_usec - start.tv_usec);
if (timer > 1000000)
{
break;
}
}
}
}
while (1);
}
}
这样就可以得到引脚15的PWM信号了,顺便说一下:摇控器接收机的信号通常也是PWM信号,通常是在1.0ms到2.0ms之间。
这样的程序虽然可以得到遥控器的读数,但有一个非常大的问题就是占用CPU的时间非常占用率非常高。
三、使用中断方式读取遥控器信号(推荐)
为了让我们的程序把CPU的占用率降低下来我们可以使用GPIO中断的方式来读取乳制品遥控器的读数,中断方式是计算机处理外部设备时常用的一种方式,,在使用Wringpi处理中断时我们需要初始化一个GPIO引脚为输入,并为其指定中断处理函数,也就是说当中断发生之后weiringpi程序会自动回调我们的处理函数,例如:
//设置摇控器3个通道到GPIO为输入引脚
pinMode(15, INPUT);
pinMode(16, INPUT);
pinMode(1, INPUT);
//注册摇控器3个通道的引脚变化中断
wiringPiISR(15, INT_EDGE_BOTH, &controller_ctl_pwm_fb);
wiringPiISR(16, INT_EDGE_BOTH, &controller_ctl_pwm_fb);
wiringPiISR(1, INT_EDGE_BOTH, &controller_ctl_pwm_fb);
void controller_ctl_pwm_fb()
{
//处理功能
}
void controller_ctl_pwm_lr()
{
//处理功能
}
void controller_ctl_pwm_pw()
{
//处理功能
}
这样,我们就注册了3个回调函数函数当GPIO引脚的电平发生变化时WiringPi就会自己调用这3个函数,我们可以在处理函数中这样写:
void controller_ctl_pwm_fb()
{
//读取电平信号
s32 value = digitalRead(15);
//如果是高电平
if (value)
{
//计时开始
gettimeofday(&ctl_pwm->timer_start, NULL);
return;
}
//如果是低电平,计时结束
gettimeofday(&ctl_pwm->timer_end, NULL);
//计算高电平时长,即pwm占空比
timer = (ctl_pwm->timer_end.tv_sec - ctl_pwm->timer_start.tv_sec) * 1000000
+ (ctl_pwm->timer_end.tv_usec - ctl_pwm->timer_start.tv_usec);
}
当然我们在程序中写的GPIO引脚编号只是为了便于调试而设置的15、16、1。了解了遥控器信号和读入的原理之后我们就可以任意指定其编号了。
得到了一个通道的信号,那么再取其它通道的信号也就没有什么难度了,我们可以读取摇控器上4个通道的任意3个,并把它们做为控制四轴的“前/后”、“左/右”、“上/下”。所以我们说你使用摇控器时,选择日本手还是美国手其实并不太重要,你可以把任意一个通道变为你想要的功能。
欢迎关注公众号:编程外星人