5iMX宗旨:分享遥控模型兴趣爱好

5iMX.com 我爱模型 玩家论坛 ——专业遥控模型和无人机玩家论坛(玩模型就上我爱模型,创始于2003年)
查看: 3921|回复: 20
打印 上一主题 下一主题

ppm信号合成求助

[复制链接]
跳转到指定楼层
楼主
发表于 2011-6-28 11:54 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
各位老师,想求助大家一个问题。
我有一个头部传感器,成品的,淘宝买的,买的时候不知道这个传感器在4VF上不适用,后来知道需要配合一个PPM混合器,可是混合器买不到,我又没钱买新遥控器。于是想做一个,基本原理很简单,就是把原遥控器的ppm信号和头部传感器的ppm信号合成,新的ppm信号送入高频部分发射,这样就能控制云台了。不过我不太清楚这个程序怎么编,还请有经验的老师指点一二,谢谢!

欢迎继续阅读楼主其他信息

沙发
发表于 2011-6-28 12:02 | 只看该作者
传感器PPM型号采样后暂存至内存,观察控发射的PPM编码长度,收完控的PPM,在上升下降沿出开始准确计时,到达计时点后发传感器PPM。
3
 楼主| 发表于 2011-6-28 12:10 | 只看该作者
回楼上老师,计时的话是否还要考虑两个脉冲串的间隔,对于单片机我纯新手,不知这个是用定时器做还是用中断做捏?
有空的老师能否用代码的形式给点提示,不要求完全能运行,给点启发我好学习学习。
谢谢老师!
4
 楼主| 发表于 2011-6-28 12:11 | 只看该作者
在网上找到如下链接,但是不开源,与作者联系还没得到回复。。。
http://fmtv.us/ppm_mixer.html
5
 楼主| 发表于 2011-6-28 12:12 | 只看该作者
还有一个帖子是我在本坛找到的
http://bbs.5imx.com/bbs/viewthre ... B7%B2%BF&page=1
6
发表于 2011-6-28 13:11 | 只看该作者
LZ,你要是能做好单片机对PPM的解码和编码的话,距离实现这个混合只是一步之遥。

楼下这位高手的帖子里,提到过他的算法,你找找看
http://bbs.5imx.com/bbs/viewthre ... id=90793&page=1
7
发表于 2011-6-28 13:23 | 只看该作者
先要看一下原遥控器信号有没有留时间间隙给你加额外通道(比如说,你的控是6通的,但是在每帧信号之间,有足够的时间间隙可以加其他通道信号)。

如果时间间隙够,那么完成这个任务就轻松多了,用单片机卡在原信号截止的地方,然后叠加上头部传感通道。

否则,你就要用2入1出的合成算法,就你提问的情况来看,对你而言鸭梨很大。。。

要开2个外部中断,分别处理遥控器PPM和头部传感器PPM;还需要一个定时器配合外部中断进行解析;接着用另外一个定时器依次生成全新的PPM信号(如果仅做这一件事情,可以不用定时器,在主循环生成PPM信号)。

用AVR单片机(比如M8)的话,INT0接PPM1 INT1接PPM2,把定时器1开16位通道8分频,MCU跑8M,就可以解析出两路PPM数据,主循环用简单的延时+翻转IO即可。

PS,这个任务TINY13就可以完成,体积会更小巧。
8
 楼主| 发表于 2011-6-28 13:46 | 只看该作者
完全不会,开始学习。。。。
9
 楼主| 发表于 2011-6-28 13:52 | 只看该作者

回复 7楼 gale 的帖子

gale大大您能帮忙写一些么?头传可以改变输出的通道,默认是7、8通道,但是可以改到4、5通道。我这方面是弱了些不过画板烧录还是没有问题的。
10
 楼主| 发表于 2011-6-28 13:53 | 只看该作者

回复 6楼 dongfang 的帖子

谢谢您的信息,我正在学习。
11
发表于 2011-6-28 13:54 | 只看该作者
看你这意思,头传是不是自己可以合成啊。。。
12
 楼主| 发表于 2011-6-28 13:56 | 只看该作者

回复 11楼 gale 的帖子

不成,头传的说明书上明确说,对于4VF,6EX之类的低端遥控器只能另加个PPM混合器。
13
 楼主| 发表于 2011-6-28 14:06 | 只看该作者

又找到一点信息

花费了将近3天时间,挂掉一片AVR Mega8,终于完成了一个PPM转发的代码。
想要完成舵量输出,非常简单。光是输出想做到没有舵机抖动也很容易。但要做到舵量采集后再输出没有抖动就复杂了。
一共2ms的舵量信息,M8内置的8M频率,代码上稍微多执行几行、少执行几行,采集回来的舵量就变了。

时间都花在这上面了,本来第一天就完成了代码,在我的辉盛5克上没有发现抖动的问题。挺开心,心想蛮简单的。可换到辉盛9克上,就开始抖。而且很频繁,基本没不抖的时候。

反复修改软件,调整计时顺序,舵量输出顺序,甚至想用外部12M晶振提高速度。我的那片M8就是为这个挂掉的。熔丝位设置了外部晶振,但是没起来,ISP也进不去了。按照网上说的用外部有源晶振的方法倒是ISP能认了,但认的芯片型号不对。当时也没在意,直接修改熔丝位到内部晶振。结果可能由于数据M8认的有误,RESET脚给关掉了,而且内部频率也不是8M,倒像是1M的。这下彻底没着....:em25: 我的第一片M8就这样交待了。

反复试验了N种方法,最后决定必须使用硬件的定时器来确定舵量。然后修改代码,将定时器0作为舵量输出用,利用定时器2进行舵量采集。
流程是,在main里先完成一次舵量采集,完全根据定时器的计时信息决定舵量,没有使用变量累加计数。然后定时器0启动,输出一次完整舵量。
输出完成后,定时器0暂停计时,再交由main继续下一次采集舵量。就这样一直循环下去。

舵量的输出也采用了新方法,定时器在输出完一部分后,直接修改TIME0的计时器到下一个状态的触发时间点上。都取消了软件变量累加的计时方法。

效果还是不错的,舵机已经基本不抖了。为了做到更好,稍微牺牲了一点灵敏度,代码里将舵量和上次的采集结果进行比较,发现差异超过1时再更新。这样最后的效果已经和直接接到接收机上没什么差别了。

下面是代码,希望对想做同样东西的人能有些帮助:
//-------------------------------------------
//
// PPM 解码/转发代码
// Version 1.0
// cnmusic@163.net
// 使用AVR Mega8,使用内部8M晶振
//
//-------------------------------------------
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <stdlib.h>


#define FEED_DOG                        asm("wdr");
#define NOP                                        asm("nop");
#define INT_ON                                sei();
#define INT_OFF                                cli();

typedef unsigned char                BOOL;
typedef unsigned char                BYTE;
typedef unsigned char                CHAR;

#define TRUE                                1
#define FALSE                                0

#define CHANNELCOUNT                1

#define TICK_COUNT_LEAD                        70                        // 前0.5ms对应的定时器0的64分频的计数数值,虽然不是实际的0.5ms,但这是我这里舵机能
                                                                                        // 承受的最小数值了


typedef struct tagChannelData
{
        BYTE                nChannelOutValue;        // 舵量数据
        BYTE                nChannelOutStep;        // 舵量的输出状态
}CHANNELDATA;

volatile CHANNELDATA                g_ChannelData[CHANNELCOUNT] = {{0}};
volatile BOOL                                g_bOutAllComplete = TRUE;
volatile BYTE                                g_nOutIndex = 0;

// TIME0定时器中断
// 这个中断用来切换各个舵机端口的状态,包含3个部分:
// 前0.5ms的引导,中间最大2ms的舵量,以及后面的低电平部分
// 为了精确计时,使用TIME0的计数器,而没用软件进行变量累加计数
ISR(TIMER0_OVF_vect)
{
        if (g_ChannelData[g_nOutIndex].nChannelOutStep == 0)
        {
                PORTD |= (1<<PD7);
                TCNT0 = 0xFF - TICK_COUNT_LEAD;
                g_ChannelData[g_nOutIndex].nChannelOutStep = 1;
        }
        else if (g_ChannelData[g_nOutIndex].nChannelOutStep == 1)
        {
                TCNT0 = 0xFF - g_ChannelData[g_nOutIndex].nChannelOutValue;
                g_ChannelData[g_nOutIndex].nChannelOutStep = 2;
        }
        else
        {
                PORTD &= ~(1<<PD7);
                g_nOutIndex++;

                if (g_nOutIndex >= CHANNELCOUNT)
                {
                        g_bOutAllComplete = TRUE;
                        TCCR0 = 0;
                }
                else
                {
                        TCNT0 = 0xFF - 1;
                }
        }
}


int main()
{
        BYTE                nChannelValueFinal = 0;

        DDRD = (1<<DDD2) | (1<<DDD3) | (1<<DDD7);                // PD2,PD3是指示灯,PD7是舵机输出端口

        PORTD &= ~((1<<PD6) | (1<<PD7));
        PORTD |= (1<<PD3);                        // 指示灯亮

        INT_ON;                                                // 开总中断

        // 定时器0设置
        TIMSK |= (1<<TOIE0);                // 允许中断


        while (1)
        {
                // 下面的代码循环扫描所有舵机的输入端口,
                // 由于是测试板,所以这里只有一个PD6端口用作输入

                asm("nop");
               
                // TIME2用来计数,看输入舵量是多大。
                TCCR2 = (1<<CS22);                        // 64分频

                // 一直等待直到高电平来临
                while (!(PIND & (1<<PIND6)))
                {
                        //asm("nop");                                                // 如果使用更高级别的优化,要保留这个语句,否则整个while会被优化掉
                }
               
                // 重新设置计数器,开始计时
                TCNT2 = 0;

                // 等待前引导的0.5ms结束
                while (PIND & (1<<PIND6))
                {
                        if (TCNT2 == TICK_COUNT_LEAD)                // 计数数值达到0.5ms,计时器清零
                        {
                                TCNT2 = 0;
                                break;
                        }
                }
               
                // 一直等到高电平结束
                while (PIND & (1<<PIND6))
                {
                        //asm("nop");
                }
        
                // 高电平结束,根据计时信息将舵量换成数字信息。
                TCCR2 = 0;
                nChannelValueFinal = TCNT2 - 2;                        // 示波器显示,这么测量后的舵量信息和实际的有16uS的差异,所以这里减去一点

                if ((abs((int)g_ChannelData[0].nChannelOutValue - (int)nChannelValueFinal)) > 1)               
                {
                        g_ChannelData[0].nChannelOutValue = nChannelValueFinal;                // 保存最后的舵量信息
                }

                g_ChannelData[0].nChannelOutStep = 0;                        // 设置舵量输出标志
               
               
                // 重起定时器0
                g_nOutIndex = 0;                                                                // 从0通道开始输出舵量
                TCNT0 = 0xFF - 1;                                                                // 延迟1,基本是立即触发中断
                TCCR0 = (1<<CS00)|(1<<CS01);                                        // 64分频,定时器开始工作

                // 继续等待下一次定时器0完成所有通道的舵量输出
                g_bOutAllComplete = FALSE;

                while (!g_bOutAllComplete);
        }
}

这种方法没有去管那个总脉冲时长,完全根据输入脉冲的长度决定。
缺点就是如果某个IO口没接接收器,那么主循环就会一直等待下去,而舵量就不会输出了。
所以在进行扫描前要先确定哪个端口有没有悬空。

这种方式不会造成漏采集,因为整个脉冲里,真正有用的部分非常短,都算上才2.5ms,剩下的17.5ms都是低电平。
所以我们就利用这个时间差完成采集过程。

示波器显示,我们输出的舵机控制信号紧接着接收机发出的信号,频率和正脉宽也和接收机给出的一致。舵机也基本认可,基本上没有抖动现象发生。

测试过天地飞的发射/接收机和ESKY的发射/接收机,OK。

舵机:
辉盛5g,9g
ESKY0500,0508
ELE ES03

效果还是不错的。:loveliness:

[ 本帖最后由 c_nmusic 于 2008-9-29 15:30 编辑 ]
14
发表于 2011-6-28 14:25 | 只看该作者
卧槽,你这编码水平离新手的差距也太大了吧.简直让老手汗颜啊.

[ 本帖最后由 riversfox 于 2011-6-28 14:26 编辑 ]
15
 楼主| 发表于 2011-6-28 16:30 | 只看该作者

回复 14楼 riversfox 的帖子

楼上大哥,这是我找的以前一个高手的帖子,基本能看明白,但是自己的这个就完全没有头绪了,不知从何做起啊!
16
发表于 2011-6-28 19:10 | 只看该作者
膜拜牛人~ :em24:
17
 楼主| 发表于 2011-6-28 19:13 | 只看该作者
期待牛人出现给小弟指点迷津啊!!!
18
发表于 2011-6-30 10:33 | 只看该作者
M8资源有限,用一片M8来采集两路PPM并发生一路PPM,都用中断的话,压力太大,误差会比较大。。。注意,仅仅是比较大(如果用心优化代码,即使用C也能做到不错的效果)。

你最好还是用两个中断来采集两路PPM信号,在主循环中进行PPM发生(用定时器查 询方式),有个1~3us误差是不可避免的,毕竟主频太低了,实际上你做到5us,一般的模拟舵机也都不会有问题了。

另外,M8-16PU可以上到12M晶振的,事实上,M8-8PU也能,只不过属于超频。
19
发表于 2011-7-29 13:15 | 只看该作者
原帖由 sorrowboy 于 2011-6-28 14:06 发表
花费了将近3天时间,挂掉一片AVR Mega8,终于完成了一个PPM转发的代码。
想要完成舵量输出,非常简单。光是输出想做到没有舵机抖动也很容易。但要做到舵量采集后再输出没有抖动就复杂了。
一共2ms的舵量信息,M8内 ...

你的M8其实没有挂,还能用的,只要做个熔丝恢复就可以了。
我最近也在研究,解码没有问题,就是再输出就会有抖动。换了MEGA168 20m 也是一样,就是幅度小了点。
20
发表于 2011-7-30 00:24 | 只看该作者
你还不如买个高频头和接收机,双接受么好了。。
您需要登录后才可以回帖 登录 | 我要加入

本版积分规则

关闭

【站内推荐】上一条 /1 下一条

快速回复 返回顶部 返回列表