OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 4483|回复: 5

6路接收机PWM信号融合为PPM信号

[复制链接]

1

主题

2

帖子

0

精华

初级会员

Rank: 2

积分
53
金钱
53
注册时间
2017-9-28
在线时间
4 小时
发表于 2017-10-3 20:34:53 | 显示全部楼层 |阅读模式
本帖最后由 D调的华丽 于 2017-10-3 20:37 编辑

    最近固定翼入魔,练模拟器的时候,有个很不爽的事(因为我是把电脑接到大电视上玩模拟器),遥控器上要拖一根教练机接口的线。其实这个线输出的是PPM六通道融合的信号(当然,如果遥控器是10通道的就是10通道融合的)。作为一个玩STM32的人,怎么能容忍这种拖着一根线玩模拟器呢,随便找个开发板,把接收机的6通道信号分别如下连接:通道1--->  PA0;
通道2--->  PA1;
通道3--->  PA2;
通道4--->  PA3;
通道5--->  PA0;
通道6--->  PA4;
凤凰模拟器--->  PB1和GND;
别忘了从开发板上供一个+5V的电源给接收机。


OK,先理理程序怎么设计。
接收机如图

第一排引脚输出的其实是一组舵机控制信号,20ms周期,高电平脉宽1000us~2000us对应油门0%~100%(具体对应关系可能略微差别)。而模拟器从遥控器教练输出端口输出来的其实是6通道融合后的PPM信号,具体看下图


所以现在要做的就是,把遥控器的6路PWM信号的每周期高电平脉宽测量出来,并融合成一个PPM信号,要测量的同时要保证其实时性,我们所有通道做最大油门和最小油门的情况分析,如下图
左边全部最小油门,右边最大油门


上图可以看出, 要想在最后一个通道数据测量完成之后再对第六通道的PPM进行融合处理的最佳时间为第3个通道下降沿发生时进行PPM融合处理,这样保证在接收到的信号与融合的信号只差半个周期(也就是10ms左右),如果本周期测量,下周期融合输出,会滞后一个周期。

六个通道的输入信号不会同时发生,是依次发生的(不信可以自己用逻辑分析仪或者示波器看~)

有了这个思路,程序基本上就清楚了
利用外部中断(上升下降触发模式)进中断记录systick定时器10us的计数获得每通道的高电平时长(单位10us),因为信号精度0.1ms,10us的systick定时器可以做到非常精准还原。
利用另一个系统定时器计时还原输出信号。具体程序如下:

1:端口配置GPIO.H中定义跳变宏

[mw_shl_code=c,true]
#define digitalHi(p,i)                                {p->BSRR=i;}                        //设置高电平        
#define digitalLo(p,i)                                {p->BRR        =i;}                                //设置低电平

#define PPM_ON                digitalHi(GPIOB,GPIO_Pin_1)
#define PPM_OFF                        digitalLo(GPIOB,GPIO_Pin_1)
[/mw_shl_code]

2:GPIO.C中初始化函数

[mw_shl_code=c,true]
void GPIO_Config(void)
{               
                /*定义一个GPIO_InitTypeDef类型的结构体*/
                GPIO_InitTypeDef GPIO_InitStructure;

                /*开启GPIOB和GPIOF的外设时钟*/
                RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);

                /*选择要控制的GPIOB引脚*/                                                                                                                           
                GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;        

                /*设置引脚模式为通用推挽输出*/
                GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;   

                /*设置引脚速率为50MHz */   
                GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

                /*调用库函数,初始化GPIOB0*/
                GPIO_Init(GPIOB, &GPIO_InitStructure);        
               
}[/mw_shl_code]


3:设置滴答定时器systick.h中

[mw_shl_code=c,true]#ifndef __SYSTICK_H
#define __SYSTICK_H

#include "stm32f10x.h"

void SysTick_Init(void);

#endif [/mw_shl_code]

4:systick.c中配置初始化

[mw_shl_code=c,true]void SysTick_Init(void)
{
        /* SystemFrequency / 1000    1ms中断一次
         * SystemFrequency / 100000         10us中断一次
         * SystemFrequency / 1000000 1us中断一次
         */
//        if (SysTick_Config(SystemFrequency / 100000))        // ST3.0.0库版本
        if (SysTick_Config(SystemCoreClock / 100000))        // ST3.5.0库版本
        {
                /* Capture error */
                while (1);
        }

//关闭滴答定时器,初始化完后再开启
        SysTick->CTRL &= ~ SysTick_CTRL_ENABLE_Msk;
}[/mw_shl_code]

5:中断初始化配置.h中的申明就不写了,直接配置函数如下:

[mw_shl_code=c,true]static void NVIC_Configuration(void)
{

        
  NVIC_InitTypeDef NVIC_InitStructure;
  
        
                EXTI_DeInit();
        
  /* Configure one bit for preemption priority */
  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
  
  /* 配置中断源 */
  NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;  //PA0的中断线
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 13;    //优先级,别设置太高
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 13;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);
        
  NVIC_InitStructure.NVIC_IRQChannel = EXTI1_IRQn;  //PA1的中断线
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 14;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 14;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);
        
  NVIC_InitStructure.NVIC_IRQChannel = EXTI2_IRQn;  //PA2的中断线
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 15;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 15;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);
        
  NVIC_InitStructure.NVIC_IRQChannel = EXTI3_IRQn;   //PA3的中断线
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 16;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 16;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);
        
  NVIC_InitStructure.NVIC_IRQChannel = EXTI4_IRQn;     //PA4的中断线
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 17;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 17;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);
        
  NVIC_InitStructure.NVIC_IRQChannel = EXTI9_5_IRQn;   //PA5的中断线
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 18;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 18;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);
        
}[/mw_shl_code]

[mw_shl_code=c,true]void EXTI_PA0_5_Config(void)
{
        GPIO_InitTypeDef GPIO_InitStructure;
        EXTI_InitTypeDef EXTI_InitStructure;

        /* config the extiline clock and AFIO clock */
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO,ENABLE);
                                                                                                
        /* config the NVIC */
        NVIC_Configuration();

        /* EXTI line gpio config*/        
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5;      
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;         // 下拉输入,因为是测高电平脉冲
  GPIO_Init(GPIOA, &GPIO_InitStructure);

        /* EXTI line mode config */
  GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0);
  EXTI_InitStructure.EXTI_Line = EXTI_Line0;
  EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
  EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising_Falling; //上升下降中断
  EXTI_InitStructure.EXTI_LineCmd = ENABLE;
  EXTI_Init(&EXTI_InitStructure);
        
                /* EXTI line mode config */
  GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource1);
  EXTI_InitStructure.EXTI_Line = EXTI_Line1;
  EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
  EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising_Falling;
  EXTI_InitStructure.EXTI_LineCmd = ENABLE;
  EXTI_Init(&EXTI_InitStructure);
        
        /* EXTI line mode config */
  GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource2);
  EXTI_InitStructure.EXTI_Line = EXTI_Line2;
  EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
  EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising_Falling;
  EXTI_InitStructure.EXTI_LineCmd = ENABLE;
  EXTI_Init(&EXTI_InitStructure);
        
        /* EXTI line mode config */
  GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource3);
  EXTI_InitStructure.EXTI_Line = EXTI_Line3;
  EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
  EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising_Falling;
  EXTI_InitStructure.EXTI_LineCmd = ENABLE;
  EXTI_Init(&EXTI_InitStructure);
        
        /* EXTI line mode config */
  GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource4);
  EXTI_InitStructure.EXTI_Line = EXTI_Line4;
  EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
  EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising_Falling;
  EXTI_InitStructure.EXTI_LineCmd = ENABLE;
  EXTI_Init(&EXTI_InitStructure);
        
        /* EXTI line mode config */
  GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource5);
  EXTI_InitStructure.EXTI_Line = EXTI_Line5;
  EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
  EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising_Falling;
  EXTI_InitStructure.EXTI_LineCmd = ENABLE;
  EXTI_Init(&EXTI_InitStructure);
}
[/mw_shl_code]


6:输出用的系统定时器设置

[mw_shl_code=c,true]/// TIM2中断优先级配置
void TIM2_NVIC_Configuration(void)
{
    NVIC_InitTypeDef NVIC_InitStructure;
   
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);                                                                                                         
    NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;         
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;        
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
}


/*
* TIM_Period / Auto Reload Register(ARR) = 1000   TIM_Prescaler--71
* 中断周期为 = 1/(72MHZ /72) * 10 = 10us
*
* TIMxCLK/CK_PSC --> TIMxCNT --> TIM_Period(ARR) --> 中断 且TIMxCNT重置为0重新计数
*/
void TIM2_Configuration(void)
{
    TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
               
                /* 设置TIM2CLK 为 72MHZ */
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2 , ENABLE);
    //TIM_DeInit(TIM2);
        
        /* 自动重装载寄存器周期的值(计数值) */
    TIM_TimeBaseStructure.TIM_Period=10;
        
    /* 累计 TIM_Period个频率后产生一个更新或者中断 */
          /* 时钟预分频数为72 */
    TIM_TimeBaseStructure.TIM_Prescaler= 71;
        
                /* 对外部时钟进行采样的时钟分频,这里没有用到 */
    TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;
        
    TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;
    TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
        
    TIM_ClearFlag(TIM2, TIM_FLAG_Update);
        
    TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);
               
    TIM_Cmd(TIM2, ENABLE);                                                                                                                                                
   
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2 , DISABLE);                /*先关闭等待使用*/   
}[/mw_shl_code]


7:具体的中断函数写到main.c中,因为代码量不大-^-
[mw_shl_code=c,true]
uint8_t i;  //输出开关
volatile u32 time = 0;  // 输出计时变量
__IO u32 timenow;   //输入信号暂存测量值
__IO u32 timeCh1;   //通道测量值
__IO u32 timeCh2;
__IO u32 timeCh3;
__IO u32 timeCh4;
__IO u32 timeCh5;
__IO u32 timeCh6;

void EXTI0_IRQHandler(void)    //CH1通道中断处理
{
        if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0)){timenow = 0;SysTick->CTRL |=  SysTick_CTRL_ENABLE_Msk; }  // 判断进入后引脚是高电平,证明上升沿进入,暂存值清0,开启滴答定时器
        if(!GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0)){ timeCh1 = timenow;  }                                                    // 中断进入后引脚低电平,证明下跳沿进入,一个高电平测量结束,存入当前通道的滴答累加值
        EXTI_ClearITPendingBit(EXTI_Line0);
}

void EXTI1_IRQHandler(void)     
{
        if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_1)){timenow = 0;SysTick->CTRL |=  SysTick_CTRL_ENABLE_Msk; }
        if(!GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_1)){ timeCh2 = timenow;  }
        EXTI_ClearITPendingBit(EXTI_Line1);
}

void EXTI2_IRQHandler(void)
{
        if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_2)){timenow = 0;SysTick->CTRL |=  SysTick_CTRL_ENABLE_Msk; }
        if(!GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_2)){ timeCh3 = timenow; i=1; }                                                  // 本处多了i = 1;表示3通道结束时开启PPM融合输出开关
        EXTI_ClearITPendingBit(EXTI_Line2);
}

void EXTI3_IRQHandler(void)
{
        if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_3)){timenow = 0;SysTick->CTRL |=  SysTick_CTRL_ENABLE_Msk; }
        if(!GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_3)){ timeCh4 = timenow;  }
        EXTI_ClearITPendingBit(EXTI_Line3);
}

void EXTI4_IRQHandler(void)
{
        if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_4)){timenow = 0;SysTick->CTRL |=  SysTick_CTRL_ENABLE_Msk; }
        if(!GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_4)){ timeCh5 = timenow;  }
        EXTI_ClearITPendingBit(EXTI_Line4);
}

void EXTI9_5_IRQHandler(void)
{
        if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_5)){timenow = 0;SysTick->CTRL |=  SysTick_CTRL_ENABLE_Msk; }
        if(!GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_5)){ timeCh6 = timenow;  }
        EXTI_ClearITPendingBit(EXTI_Line5);
}


oid SysTick_Handler(void)
{
        timenow++;    //滴答定时器开启后进入中断,脉宽测量计数,单位10us
}


void TIM2_IRQHandler(void)    //输出定时器中断,用于输出时输出信号计时
{
        if ( TIM_GetITStatus(TIM2 , TIM_IT_Update) != RESET )
        {        
                time++;                                 
                TIM_ClearITPendingBit(TIM2 , TIM_FLAG_Update);                  
        }                        
}




int main(void)
{        
        GPIO_Config();/* 端口初始化 */

        //USART1_Config();  如果要调试,开启串口

        EXTI_PA0_5_Config();   //中断配置

        SysTick_Init();  /* 配置SysTick 为10us中断一次 */
        
        TIM2_NVIC_Configuration();  //输出计时定时器中断配置
        TIM2_Configuration();          //输出定时器配置
        
        RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2 , ENABLE);    //开启输出定时器
        
        PPM_ON;   //拉高输出(PPM信号默认高电平)
        while(1)
        {
                if(i == 1)    //第3通道测量完成开启输出
                {
                        time = 0;   //输出计时清0
                        PPM_OFF;  //拉低输出
                        while(time<=40){}  //等待0.4ms的信号头
                        
                        PPM_ON;       //开始第一通道信号
                        time = 0;      //同时清0计时器
                        while(time < timeCh1-30){};   //1通道高电平时间 = PWM测量脉宽-0.3ms信号分界
                        PPM_OFF;       //时间到拉低
                        time = 0;         //同事清0计时器
                                
                        while(time<=30){}   //0.3ms低电平
                        PPM_ON;                    //开始第2通道信号
                        while(time < timeCh2-30){};
                        PPM_OFF;
                        time = 0;        
                                
                        while(time<=30){}
                        PPM_ON;        
                        while(time < timeCh3-30){};
                        PPM_OFF;
                        time = 0;        
                                
                        while(time<=30){}
                        PPM_ON;        
                        while(time < timeCh4-30){};
                        PPM_OFF;
                        time = 0;
                                
                        while(time<=30){}
                        PPM_ON;        
                        while(time < timeCh5 -20){};        //本处理论应该是-30,但是根据实际波形微调成-20,
                        PPM_OFF;
                        time = 0;        
                                
                        while(time<=30){}
                        PPM_ON;
                        while(time < timeCh6-20){};
                        PPM_OFF;
                        time = 0;        
                        while(time<=30){}
                        PPM_ON;            //信号结束,从新拉高输出,一个信号完成

                                
//                        printf("--- %d --- %d --- %d --- %d --- %d --- %d  --- time: %d\r\n",timeCh1,timeCh2,timeCh3,timeCh4,timeCh5,timeCh6,time);      //如果要调试,开启
                        i = 0;  //一组输出完成,从新锁定
                }
               
        }     
}



[/mw_shl_code]


基本代码就完成了,实际最大最小值情况的输出波形如下,蓝色为PPM信号,黄色为第一通道的波形,可以看出,每个周期完结前PPM融合也完成了。以后玩模拟器再也不用拖一根线了。其实另外的用处就是,某宝上卖的PPM信号编码器,玩航模的基本都不陌生,有些飞控只有PPM信号输入,但是很多遥控器都没有PPM输出,之后多通道的PWM信号输出,所以就要一块PWM编码PPM的模块来实现。



00000.png
001aaa.gif
002.png
003.png
ACC.png
ACC1.png
正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

6

主题

34

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
204
金钱
204
注册时间
2017-9-28
在线时间
73 小时
发表于 2017-10-25 17:35:43 | 显示全部楼层
楼主,最近也在弄这个,你是怎么捕捉这6路pwm高电平脉冲宽度的
回复 支持 反对

使用道具 举报

0

主题

7

帖子

0

精华

初级会员

Rank: 2

积分
79
金钱
79
注册时间
2017-4-10
在线时间
13 小时
发表于 2017-11-6 16:36:46 | 显示全部楼层
收藏了,好东西。。
回复 支持 反对

使用道具 举报

2

主题

33

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
377
金钱
377
注册时间
2015-10-19
在线时间
173 小时
发表于 2017-11-8 18:51:15 | 显示全部楼层
圖文解說清楚先收藏待以後再消化
回复 支持 反对

使用道具 举报

1

主题

2

帖子

0

精华

初级会员

Rank: 2

积分
53
金钱
53
注册时间
2017-9-28
在线时间
4 小时
 楼主| 发表于 2017-11-8 19:03:05 | 显示全部楼层
PURSRE 发表于 2017-10-25 17:35
楼主,最近也在弄这个,你是怎么捕捉这6路pwm高电平脉冲宽度的

滴答定时器加中断,具体看代码就行了
回复 支持 反对

使用道具 举报

2

主题

5

帖子

0

精华

新手上路

积分
20
金钱
20
注册时间
2017-12-21
在线时间
4 小时
发表于 2017-12-22 21:19:08 | 显示全部楼层
我的定时器为啥进不去中断,time_now的值一直是0
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2024-11-26 14:01

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

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