ppm理解与代码实现

时间:2024-04-06 20:12:42

PWM指的是脉宽调制,狭义说就是舵机信号中正脉冲宽度,就是高电平宽度来表示控制量大小,PPM是脉冲和脉冲之间间隔调制,狭义的说,对于遥控器输出或者接收机输出而言,两个脉冲上升沿之间间隔表示控制量大小.在遥控设备的传统中PWM通常是单通道的控制量,而PPM则是以帧为单位,帧内每个脉冲间隔表示一个通道的控制量,然后两帧之间还有一个长一点的低电平,这个信号曾经用于同步,作为帧和帧之间的划分

脉宽和数值

PPM信号规定的脉宽变化范围是1000us~2000us
一般航模遥控器中:
方向、油门之类的双向控制,中间值是1500us,两个方向的极值分别是1000us和2000us;
模式切换按钮,分别在1000us和2000us之间切换。
注:
1、航模遥控器中,双向控制项目一般都会有个中间值调节旋钮,如果调节了中间值不是1500us,那么另外两个方向的极值也会随着变化。总之输出范围是围绕着中间值±500的范围。
2、PPM信号可直接接到舵机,而一般舵机的脉宽范围是500us~2500us,对应角度0°~180°。所以一般由PPM直接控制的舵机,旋转角度范围在45°~135°。

ppm时序图


ppm理解与代码实现


           这是因为航模遥控器不能同时传输多路通道,只能单路传输,然后分给各个通道。遥控器的无线信号类似于上面信号的第一行,解调后变成了下面各路通道信号。
注意,各个通道的高电平信号是一个紧挨着一个的,而不是每个通道固定分配2ms的时间。
在对接收机解码时发现,实际各个通道的正脉冲并不是一个紧挨着一个出现有的接收机各个通道输出的高电平连接比较近,而有的接收机在上一个通道正脉冲结束后要等较长的时间下一个通道的高电平才出现,在此期间所有的通道都是低电平。

由于单路信号最长是2000us,周期20ms,所以理论上可以容纳10路。而由于需要进行同步,实际上遥控器最多只能容纳9路信号
记住:接收机输出的每帧信号(20ms)里,理论上最后必然有至少2ms的时间里,所有的通道都输出低电平,单片机解码时就是利用这一点来判断一帧信号结束的。


精度

ppm理解与代码实现

PPM和电调

PPM信号可直接输出给舵机,而在航模中经常会用PPM信号控制电机(有刷和无刷),它们是不能直接用PPM信号控制的,此时需要电调。
电调具有接收PPM信号和驱动电机的功能,不同的电机驱动方式不同(有刷和无刷,有感也无感)。简单地说,电调的作用是接收PPM信号,转换成我们希望的电机驱动信号,并通过内部的电机驱动电路实现对电机的控制。


接下来是ppm在stm32中的代码:

 void PPM_Init()
{
GPIO_InitTypeDef GPIO_InitStructure;
EXTI_InitTypeDef EXTI_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_8 ;//GPIO_Pin_0
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;//输入下拉
//GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource8);
EXTI_InitStructure.EXTI_Line = EXTI_Line8;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel = EXTI9_5_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority =1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority =2;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);


}


uint16 PPM_Sample_Cnt=0;
uint16 PPM_Isr_Cnt=0;
u32 Last_PPM_Time=0;
u32 PPM_Time=0;
u16 PPM_Time_Delta=0;
u16 PPM_Time_Max=0;
uint16 PPM_Start_Time=0;
uint16 PPM_Finished_Time=0;
uint16 PPM_Is_Okay=0;
uint16 PPM_Databuf[8]={0};


/***************************************************
函数名: void EXTI9_5_IRQHandler(void)
说明: PPM接收中断函数
入口:
出口:
备注: 程序初始化后、始终运行
****************************************************/
extern u32 TIME_ISR_CNT;
void EXTI9_5_IRQHandler(void)
{
  if(EXTI_GetITStatus(EXTI_Line8) != RESET)
  {
//系统运行时间获取,单位us
    Last_PPM_Time=PPM_Time;
    PPM_Time=1000*10*TIME_ISR_CNT
                   +TIM2->CNT/2;//us
    PPM_Time_Delta=PPM_Time-Last_PPM_Time;
    //PPM中断进入判断
    if(PPM_Isr_Cnt<100)  PPM_Isr_Cnt++;
   //PPM解析开始
    if(PPM_Is_Okay==1)
    {
    PPM_Sample_Cnt++;
    //对应通道写入缓冲区
    PPM_Databuf[PPM_Sample_Cnt-1]=PPM_Time_Delta;
    //单次解析结束
      if(PPM_Sample_Cnt>=8)
        PPM_Is_Okay=0;
    }
    if(PPM_Time_Delta>=2050)//帧结束电平至少2ms=2000us
    {
      PPM_Is_Okay=1;
      PPM_Sample_Cnt=0;
    }
  }
  EXTI_ClearITPendingBit(EXTI_Line8);
}


这里的中位死区是50;      

void PPM_RC()
{
#ifdef RC_PPM
 if(PPM_Databuf[0]<=1480)  Roll_Control=(1450-PPM_Databuf[0])*50/400;
 else if(PPM_Databuf[0]>=1520)  Roll_Control=(1550-PPM_Databuf[0])*50/400;
 else Roll_Control=0;


 if(PPM_Databuf[1]<=1450)  Pitch_Control=(1450-PPM_Databuf[1])*50/400;
 else if(PPM_Databuf[1]>=1550)  Pitch_Control=(1550-PPM_Databuf[1])*50/400;
 else Pitch_Control=0;


 Target_Angle[0]=-Pitch_Control;
 Target_Angle[1]=-Roll_Control;


 Temp_RC=(PPM_Databuf[2]-1100);
 if(Temp_RC<=5)     Throttle_Control=0;
 else if(Temp_RC>=1000)  Throttle_Control=1000;
 else Throttle_Control=Temp_RC;


 if(PPM_Databuf[3]<=1450)  Yaw_Control=-(PPM_Databuf[3]-1450)*150/400;
 else if(PPM_Databuf[3]>=1550)  Yaw_Control=-(PPM_Databuf[3]-1550)*150/400;
 else Yaw_Control=0;
 if(Yaw_Control>=150) Yaw_Control=150;
 else if(Yaw_Control<=-150) Yaw_Control=-150;


 RC_NewData[0]=Throttle_Control;//遥感油门原始行程量
 Throttle_Rate=Get_Thr_Rate(Throttle_Control);
 Throttle_Control=Throttle_Base+Throttle_Control;
#endif




  if(Throttle_Control==1000
       &&Yaw_Control>=80
         &&Roll_Control<=-40
           &&Pitch_Control<=-40)
    Unlock_Makesure_Cnt++;






  if(Throttle_Control==1000
      &&Yaw_Control<=-70
        &&Roll_Control>=40
          &&Pitch_Control<=-40)
       Lock_Makesure_Cnt++;


   if(Throttle_Control==1000
      &&Yaw_Control<=-70
        &&Roll_Control>=40
          &&Pitch_Control<=-40
            &&Lock_Makesure_Cnt>250*1)//持续2S
  {
      Controler_State=Unlock_Controler;
      Lock_Makesure_Cnt=0;


      Auto_ReLock_Cnt=250*6;//持续10S
      Auto_Relock_Flag_Set=0;
  }


  if(Controler_State==Unlock_Controler
     &&Auto_Relock_Flag_Set==0//自动上锁位未设置
      &&Throttle_Control==1000
       &&Pitch_Control==0
        &&Roll_Control==0
         &&Yaw_Control==0)//解锁后遥感回中
  {
      Auto_Relock_Flag=1;//解锁初始时开启自动上锁标志位
      Auto_Relock_Flag_Set=1;//单次解锁后,只置位一次
  }




  if(Auto_Relock_Flag==1)
  {
   if(Throttle_Control==1000
     &&Pitch_Control==0
       &&Roll_Control==0
         &&Yaw_Control==0
          &&Controler_State==Unlock_Controler)//自动上锁计数器
    {
     Auto_ReLock_Cnt--;
     if(Auto_ReLock_Cnt<=0)  Auto_ReLock_Cnt=0;


    }


  }


}