本帖最后由 gangguo 于 2021-6-30 15:40 编辑
PPM信号是航模遥控器输出的一种标准信号,从PPM信号中可以获取7-9个通道的遥控指令数据。PPM看起来很像PWM,很多模型爱好者对于它们产生了误解,有些朋友认为PPM和PWM就是一回事,其实不然,下面我们先说明一下它们之间的区别和关系。 PWM,是英文Pulse Width Modulation的缩写,意思就是脉冲宽度调制。脉冲就是由高、低电平组成的信号序列,其中高电平的时间就是这里所说的脉冲宽度,也就是高电平维持的时间,单位为微秒,范围在500-2500微秒(us)内,或0.5毫秒-2.5毫秒(ms)。如下图所示: 这样的一个脉冲通常用来控制一个通道,即一个舵机,如要控制多个舵机,则需要多个这样的通道,例如:我们要控制一架固定翼模型飞机,则我们至少需要四个通道来分别控制油门、副翼、升降舵、方向舵,各个通道的脉冲宽度控制各个舵机转动。 图中第一个波形为PPM信号,第二个波形为一通道的PWM,它对应到PPM信号的“K1”,第三个波形为二通道的PWM,它对应到PPM信号的“K2”,依次类推,“K8”对应到第八通道的PWM。K1的前面及K8后面还有一个比较“宽”的脉冲,它的宽度大于所有通道的脉冲宽度,这个也称为“同步脉冲”,在这样的一帧信号中,找出信号的“头”很关键,就如同在SBUS信号解析的过程中,要找到数据的开头,才能正确的解算出各通道的数据。PPM信号“同步脉冲”就可以作为“帧头”来使用,只要判断一个脉冲大于通道的“正常值”,那么接下来的一个脉冲就是1通道的数据。这里要注意的是:PPM中的通道脉宽比实际的PWM脉宽要“窄一点”,这里是由于在PPM信号中需要接入脉冲间隔,以区分通道,而PPM信号帧的总长又不宜过长,因此把每个通道的脉宽“砍掉”一个同样的宽度作为间隔,我们在计算通道PWM脉宽时还应该把这个被砍掉的部分加上。 下面开始介绍我的解算思路。很显然,PPM信号不能像SBUS解算那样使用串口,因为PPM就没有“波特率”,它的实质就是一序列串在一起的脉冲,要解算它实质就是要把这些脉冲一个一个地采集进来。在Arduino中有一个专门用于采集脉冲宽度的函数:pulseIn(),这个函数可以用来完成PPM解析,但是用这个函数有一些弊端:1、它会“死等”脉冲的到来,也就是脉冲不来,它就会在那里永远等待;2、当在执行一个脉冲的采集时,程序依然会停在那里等待采集完毕,这样的话,整个解算过程即要等待8个通道及一个同步脉冲的总时间,加在一起是20毫秒,如果这个解算过程只是用于演示,那么我们可以接受,但如果是用于实时控制,比如四轴飞行器,这么长的采集周期势必会让整个控制崩溃,因此,我们必须寻找其他的解算方法。(这里举个例子,玩过APM飞控的朋友应该知道,APM的遥控信号输入是使用的是PWM通道独立输入,而采集这些信号的任务都不是有主控芯片mega2560来完成的,完成这个任务是由协处理芯片:mega32,它同时也是USB转TTL芯片,这说明采集多路PWM确实是一个比较“繁琐”的过程)。那么采用什么办法来做呢? 单片机系统都有外部中断,可以用中断来处理这些“粘在一起”的脉冲。这次试验用的Arduino板为:NANO板,这个板(MEGA328)使用Arduino官方库时有两个外部中断:D2及D3口,试验中使用了D2,即外部中断0,触发方式设置为“跳变”,即脉冲的上升沿及下降沿均触发中断,并在中断处理函数中判断触发方式(上升沿或下降沿),然后分别记录进入中断的时刻(使用Arduino的时间函数:micros()),然后下降沿时刻减去上升沿时刻,即可得到一个脉冲的宽度(这个方法同样可以用于单通道PWM的采集,或者超声波测距)。 那么如何处理这一串脉冲呢?如何正确获得通道PWM呢?我的方法是:连续采集20个脉冲宽度放在一个数组中,然后去数组中寻找“同步脉冲”,找到它之后,紧随其后的8个数组元素就是我们需要的通道PWM数据。为什么一定要采集20个呢?因为我们无法确定第一次采集到脉冲是哪个通道的,除非“运气好”,一开始就获取到了“同步脉冲”,因此,如果我们非常“小气”地只采集9个,几乎不可能容易地从中找到正确完整的数据,当然也可以做一个比较细致、复杂的解算程序来完成(如果你是一个拼图高手),而每次采集20个,则保证了每一次采集到的脉冲序列中至少包含一帧完整的数据,这样就可以简化解算的过程,只需要找到“同步脉冲”,然后从它之后顺序取8个脉冲,其余的数据丢弃,然后进入下一轮采集。
本次试验中使用的遥控器仍为FUTABA T10CHG,试验中将发射模式设置为“2.4G 7CH”,就是飞模拟器使用的模式,然后用一个音频接头改装连线,连接遥控器背面的PPM信号线及地线。PPM信号线连接到NANO板的D2口,地线就在NANO上找一个GND接上。
- #include "Timer.h"
- Timer t;
- int ppm_flag=0;//为0时表示ppm帧未采集完成,每次低电平出发中断完成一个PPM_DATE的采集,此时标志位加1,当完成一帧采集后,标志位清0.
- char flag_in=0,flag_out=0;
- int ppm_i=0;
- long int h_now,l_now;
- int ppm_date[20];
- int channels[8];
- void setup() {
- Serial.begin(115200);
- pinMode(2,INPUT);
- attachInterrupt(0,ppm_in,CHANGE);// put your setup code here, to run once:
- t.every(20,channels_update);
- t.every(25,date_print);
- }
- void ppm_in()
- {
- if(digitalRead(2)==1&&flag_out==0)
- {
- h_now=micros();
- flag_in=1;
- }
- if(digitalRead(2)==0&&flag_in==1&&flag_out==0)
- {
- l_now=micros();
- ppm_date[ppm_flag]=l_now-h_now;
- ppm_flag+=1;
- flag_in=0;
- if(ppm_flag==20)
- {
- ppm_flag=0;
-
- flag_out=1;
- }
- }
- }
- void channels_update()
- {
- if(flag_out==1)
- {
- ppm_i=0;
- while(ppm_date[ppm_i]<3000)
- {ppm_i++;}
- if(ppm_i<=12)
- {
- for(int j=0;j<8;j++)
- {
- channels[j]=ppm_date[(ppm_i+1)+j]+400;
- }
- }
- flag_out=0;
- }
- }
- void date_print()
- {
- /**********打印输出原始数据**************/
- /* for(int i=0;i<20;i++)
- {
- if(i<19)
- {
- Serial.print(ppm_date[i]);
- Serial.print(" ");
- }
- if(i>18)
- {
- Serial.println(ppm_date[i]);
- }
- }*/
- /****************打印输出通道数据**********************/
- for(int i=0;i<8;i++)
- {
- if(i<7)
- {
- Serial.print(channels[i]);
- Serial.print(" ");
- }
- if(i>6)
- {
- Serial.println(channels[i]);
- }
- }
- }
- void loop() {
- t.update();
- }
复制代码程序说明:在初始化中还必须将D2端口设置为输入模式,并且设置中断0:attachInterrupt(0,ppm_in,CHANGE)。其中ppm_flag用于控制采集脉冲的个数,并且将这些脉冲序列按顺序存放到数组ppm_date[]中;flag_in用于确保采集脉冲是从上升沿开始(因为上升沿和下降沿都会触发中断),它在上升沿处理中被置1,在下降沿处理中被置0,达到的目的就是在没有采集到上升沿时不对下降沿进行处理,因为我们要采集的是高电平的时间;flag_out用于判断是否完成了脉冲采集和是否完成了通道解算,当完成采集时它被置1,这个时候中断函数停止数据采集,只有等通道更新完毕后,它才会被置0,中断函数才会进行新一轮的数据采集,而在它为0的期间,也就是在数据采集的期间,不会进行通道更新。接下来从串口监视器观察原始数据,即采集到的ppm_date[]: 上图中,每一行数字即是完整ppm_date[]帧,从数据中可以看到,每一行都有两个比较大的数字:大于3000,这个就是我们要找的“同步脉冲”,找到一个之后,后面跟的8个数,就是要提取的通道数据。当然,从数据中能够看到,有时每两个“同步脉冲”之间偶尔会出现不足8个有效数据的情况,但这个影响暂且可以接受,我们可以设计更严密的通道更新程序将这样的数据帧丢弃。下面来看看1通道的数据,在试验过程中可以保持一通道(副翼)摇杆在中立位置(所有微调归0),此时可以看到得到的数据为1120左右,在这种情况下,该通道的实际输出脉宽(PWM)应为1520,这个我们可以对FUTABA接收机的一通道输出PWM进行采样验证,因此从ppm中获取的数据还应该加上400,这个就是低电平的持续时间即上面提到的“被砍掉的那一部分”,当然这个时间也可以用中断去采集ppm的低电平间隔得到。在程序中我直接给每个通道加了400,通过与之前PWM采集的数据进行对比,得出的结果是一致的。下面从串口监视器观察解算出来的通道数据channels[],channels[0]代表1通道,副翼通道: 至此,PPM信号解析完成,从实验中可以看出,FUTABA T10CHG的ppm输出确实为7个比例通道,第8个通道保持在中立位。这个试验主要是针对FUTABA T10CHG,对于其他品牌的遥控器我将进行进一步的试验,比如6通道的SPEKTRUM DX6I遥控器的PPM输出,也许还是有差别的吧. |