极客工坊

 找回密码
 注册

QQ登录

只需一步,快速开始

查看: 6490|回复: 1

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

[复制链接]
发表于 2017-10-3 21:15:28 | 显示全部楼层 |阅读模式
本帖最后由 D调的华丽 于 2017-10-3 21:15 编辑

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


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

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


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

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

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

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

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

[

  1. #define digitalHi(p,i)                                {p->BSRR=i;}                        //设置高电平        
  2. #define digitalLo(p,i)                                {p->BRR        =i;}                                //设置低电平

  3. #define PPM_ON                digitalHi(GPIOB,GPIO_Pin_1)
  4. #define PPM_OFF                        digitalLo(GPIOB,GPIO_Pin_1)
  5. [/mw_shl_code]

  6. 2:GPIO.C中初始化函数

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

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

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

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

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

  20.                 /*调用库函数,初始化GPIOB0*/
  21.                 GPIO_Init(GPIOB, &GPIO_InitStructure);        
  22.                
  23. }
复制代码
]


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

  1. ]#ifndef __SYSTICK_H
  2. #define __SYSTICK_H

  3. #include "stm32f10x.h"

  4. void SysTick_Init(void);

  5. #endif
复制代码
]

4:systick.c中配置初始化

  1. ]void SysTick_Init(void)
  2. {
  3.         /* SystemFrequency / 1000    1ms中断一次
  4.          * SystemFrequency / 100000         10us中断一次
  5.          * SystemFrequency / 1000000 1us中断一次
  6.          */
  7. //        if (SysTick_Config(SystemFrequency / 100000))        // ST3.0.0库版本
  8.         if (SysTick_Config(SystemCoreClock / 100000))        // ST3.5.0库版本
  9.         {
  10.                 /* Capture error */
  11.                 while (1);
  12.         }

  13. //关闭滴答定时器,初始化完后再开启
  14.         SysTick->CTRL &= ~ SysTick_CTRL_ENABLE_Msk;
  15. }
复制代码
]

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

  1. ]static void NVIC_Configuration(void)
  2. {

  3.         
  4.   NVIC_InitTypeDef NVIC_InitStructure;
  5.   
  6.         
  7.                 EXTI_DeInit();
  8.         
  9.   /* Configure one bit for preemption priority */
  10.   NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
  11.   
  12.   /* 配置中断源 */
  13.   NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;  //PA0的中断线
  14.   NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 13;    //优先级,别设置太高
  15.   NVIC_InitStructure.NVIC_IRQChannelSubPriority = 13;
  16.   NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  17.   NVIC_Init(&NVIC_InitStructure);
  18.         
  19.   NVIC_InitStructure.NVIC_IRQChannel = EXTI1_IRQn;  //PA1的中断线
  20.   NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 14;
  21.   NVIC_InitStructure.NVIC_IRQChannelSubPriority = 14;
  22.   NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  23.   NVIC_Init(&NVIC_InitStructure);
  24.         
  25.   NVIC_InitStructure.NVIC_IRQChannel = EXTI2_IRQn;  //PA2的中断线
  26.   NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 15;
  27.   NVIC_InitStructure.NVIC_IRQChannelSubPriority = 15;
  28.   NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  29.   NVIC_Init(&NVIC_InitStructure);
  30.         
  31.   NVIC_InitStructure.NVIC_IRQChannel = EXTI3_IRQn;   //PA3的中断线
  32.   NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 16;
  33.   NVIC_InitStructure.NVIC_IRQChannelSubPriority = 16;
  34.   NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  35.   NVIC_Init(&NVIC_InitStructure);
  36.         
  37.   NVIC_InitStructure.NVIC_IRQChannel = EXTI4_IRQn;     //PA4的中断线
  38.   NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 17;
  39.   NVIC_InitStructure.NVIC_IRQChannelSubPriority = 17;
  40.   NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  41.   NVIC_Init(&NVIC_InitStructure);
  42.         
  43.   NVIC_InitStructure.NVIC_IRQChannel = EXTI9_5_IRQn;   //PA5的中断线
  44.   NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 18;
  45.   NVIC_InitStructure.NVIC_IRQChannelSubPriority = 18;
  46.   NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  47.   NVIC_Init(&NVIC_InitStructure);
  48.         
  49. }
复制代码
]

  1. ]void EXTI_PA0_5_Config(void)
  2. {
  3.         GPIO_InitTypeDef GPIO_InitStructure;
  4.         EXTI_InitTypeDef EXTI_InitStructure;

  5.         /* config the extiline clock and AFIO clock */
  6.         RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO,ENABLE);
  7.                                                                                                 
  8.         /* config the NVIC */
  9.         NVIC_Configuration();

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

  14.         /* EXTI line mode config */
  15.   GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0);
  16.   EXTI_InitStructure.EXTI_Line = EXTI_Line0;
  17.   EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
  18.   EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising_Falling; //上升下降中断
  19.   EXTI_InitStructure.EXTI_LineCmd = ENABLE;
  20.   EXTI_Init(&EXTI_InitStructure);
  21.         
  22.                 /* EXTI line mode config */
  23.   GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource1);
  24.   EXTI_InitStructure.EXTI_Line = EXTI_Line1;
  25.   EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
  26.   EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising_Falling;
  27.   EXTI_InitStructure.EXTI_LineCmd = ENABLE;
  28.   EXTI_Init(&EXTI_InitStructure);
  29.         
  30.         /* EXTI line mode config */
  31.   GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource2);
  32.   EXTI_InitStructure.EXTI_Line = EXTI_Line2;
  33.   EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
  34.   EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising_Falling;
  35.   EXTI_InitStructure.EXTI_LineCmd = ENABLE;
  36.   EXTI_Init(&EXTI_InitStructure);
  37.         
  38.         /* EXTI line mode config */
  39.   GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource3);
  40.   EXTI_InitStructure.EXTI_Line = EXTI_Line3;
  41.   EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
  42.   EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising_Falling;
  43.   EXTI_InitStructure.EXTI_LineCmd = ENABLE;
  44.   EXTI_Init(&EXTI_InitStructure);
  45.         
  46.         /* EXTI line mode config */
  47.   GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource4);
  48.   EXTI_InitStructure.EXTI_Line = EXTI_Line4;
  49.   EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
  50.   EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising_Falling;
  51.   EXTI_InitStructure.EXTI_LineCmd = ENABLE;
  52.   EXTI_Init(&EXTI_InitStructure);
  53.         
  54.         /* EXTI line mode config */
  55.   GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource5);
  56.   EXTI_InitStructure.EXTI_Line = EXTI_Line5;
  57.   EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
  58.   EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising_Falling;
  59.   EXTI_InitStructure.EXTI_LineCmd = ENABLE;
  60.   EXTI_Init(&EXTI_InitStructure);
  61. }
复制代码
]


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

  1. ]/// TIM2中断优先级配置
  2. void TIM2_NVIC_Configuration(void)
  3. {
  4.     NVIC_InitTypeDef NVIC_InitStructure;
  5.    
  6.     NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);                                                                                                         
  7.     NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;         
  8.     NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
  9.     NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;        
  10.     NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  11.     NVIC_Init(&NVIC_InitStructure);
  12. }


  13. /*
  14. * TIM_Period / Auto Reload Register(ARR) = 1000   TIM_Prescaler--71
  15. * 中断周期为 = 1/(72MHZ /72) * 10 = 10us
  16. *
  17. * TIMxCLK/CK_PSC --> TIMxCNT --> TIM_Period(ARR) --> 中断 且TIMxCNT重置为0重新计数
  18. */
  19. void TIM2_Configuration(void)
  20. {
  21.     TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
  22.                
  23.                 /* 设置TIM2CLK 为 72MHZ */
  24.     RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2 , ENABLE);
  25.     //TIM_DeInit(TIM2);
  26.         
  27.         /* 自动重装载寄存器周期的值(计数值) */
  28.     TIM_TimeBaseStructure.TIM_Period=10;
  29.         
  30.     /* 累计 TIM_Period个频率后产生一个更新或者中断 */
  31.           /* 时钟预分频数为72 */
  32.     TIM_TimeBaseStructure.TIM_Prescaler= 71;
  33.         
  34.                 /* 对外部时钟进行采样的时钟分频,这里没有用到 */
  35.     TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;
  36.         
  37.     TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;
  38.     TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
  39.         
  40.     TIM_ClearFlag(TIM2, TIM_FLAG_Update);
  41.         
  42.     TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);
  43.                
  44.     TIM_Cmd(TIM2, ENABLE);                                                                                                                                                
  45.    
  46.     RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2 , DISABLE);                /*先关闭等待使用*/   
  47. }
复制代码
]


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

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

  17. void EXTI1_IRQHandler(void)     
  18. {
  19.         if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_1)){timenow = 0;SysTick->CTRL |=  SysTick_CTRL_ENABLE_Msk; }
  20.         if(!GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_1)){ timeCh2 = timenow;  }
  21.         EXTI_ClearITPendingBit(EXTI_Line1);
  22. }

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

  29. void EXTI3_IRQHandler(void)
  30. {
  31.         if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_3)){timenow = 0;SysTick->CTRL |=  SysTick_CTRL_ENABLE_Msk; }
  32.         if(!GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_3)){ timeCh4 = timenow;  }
  33.         EXTI_ClearITPendingBit(EXTI_Line3);
  34. }

  35. void EXTI4_IRQHandler(void)
  36. {
  37.         if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_4)){timenow = 0;SysTick->CTRL |=  SysTick_CTRL_ENABLE_Msk; }
  38.         if(!GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_4)){ timeCh5 = timenow;  }
  39.         EXTI_ClearITPendingBit(EXTI_Line4);
  40. }

  41. void EXTI9_5_IRQHandler(void)
  42. {
  43.         if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_5)){timenow = 0;SysTick->CTRL |=  SysTick_CTRL_ENABLE_Msk; }
  44.         if(!GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_5)){ timeCh6 = timenow;  }
  45.         EXTI_ClearITPendingBit(EXTI_Line5);
  46. }


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


  51. void TIM2_IRQHandler(void)    //输出定时器中断,用于输出时输出信号计时
  52. {
  53.         if ( TIM_GetITStatus(TIM2 , TIM_IT_Update) != RESET )
  54.         {        
  55.                 time++;                                 
  56.                 TIM_ClearITPendingBit(TIM2 , TIM_FLAG_Update);                  
  57.         }                        
  58. }




  59. int main(void)
  60. {        
  61.         GPIO_Config();/* 端口初始化 */

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

  63.         EXTI_PA0_5_Config();   //中断配置

  64.         SysTick_Init();  /* 配置SysTick 为10us中断一次 */
  65.         
  66.         TIM2_NVIC_Configuration();  //输出计时定时器中断配置
  67.         TIM2_Configuration();          //输出定时器配置
  68.         
  69.         RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2 , ENABLE);    //开启输出定时器
  70.         
  71.         PPM_ON;   //拉高输出(PPM信号默认高电平)
  72.         while(1)
  73.         {
  74.                 if(i == 1)    //第3通道测量完成开启输出
  75.                 {
  76.                         time = 0;   //输出计时清0
  77.                         PPM_OFF;  //拉低输出
  78.                         while(time<=40){}  //等待0.4ms的信号头
  79.                         
  80.                         PPM_ON;       //开始第一通道信号
  81.                         time = 0;      //同时清0计时器
  82.                         while(time < timeCh1-30){};   //1通道高电平时间 = PWM测量脉宽-0.3ms信号分界
  83.                         PPM_OFF;       //时间到拉低
  84.                         time = 0;         //同事清0计时器
  85.                                 
  86.                         while(time<=30){}   //0.3ms低电平
  87.                         PPM_ON;                    //开始第2通道信号
  88.                         while(time < timeCh2-30){};
  89.                         PPM_OFF;
  90.                         time = 0;        
  91.                                 
  92.                         while(time<=30){}
  93.                         PPM_ON;        
  94.                         while(time < timeCh3-30){};
  95.                         PPM_OFF;
  96.                         time = 0;        
  97.                                 
  98.                         while(time<=30){}
  99.                         PPM_ON;        
  100.                         while(time < timeCh4-30){};
  101.                         PPM_OFF;
  102.                         time = 0;
  103.                                 
  104.                         while(time<=30){}
  105.                         PPM_ON;        
  106.                         while(time < timeCh5 -20){};        //本处理论应该是-30,但是根据实际波形微调成-20,
  107.                         PPM_OFF;
  108.                         time = 0;        
  109.                                 
  110.                         while(time<=30){}
  111.                         PPM_ON;
  112.                         while(time < timeCh6-20){};
  113.                         PPM_OFF;
  114.                         time = 0;        
  115.                         while(time<=30){}
  116.                         PPM_ON;            //信号结束,从新拉高输出,一个信号完成

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



复制代码
]


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

KK1.jpg KK2.jpg
回复

使用道具 举报

发表于 2018-5-4 10:33:10 | 显示全部楼层
刚看到,不错! 好文
回复 支持 反对

使用道具 举报

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

本版积分规则

Archiver|联系我们|极客工坊 ( 浙ICP备09023225号 )

GMT+8, 2019-6-21 03:46 , Processed in 0.075715 second(s), 27 queries .

Powered by Discuz! X3.4 Licensed

© 2001-2017 Comsenz Inc.

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