ppm理解与代码实现

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时序图

 

2018030119352898

 

 

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

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

 

 

 

精度

20180301195340935

 

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;


    }


  }


}

 

 

 

 

 

 

 

以下是一个简单的PPM数据压缩代码实现,基于霍夫曼编码算法: ```python import heapq import struct class Node: def __init__(self, freq, symbol=None, left=None, right=None): self.freq = freq self.symbol = symbol self.left = left self.right = right def __lt__(self, other): return self.freq < other.freq def build_huffman_tree(freq_table): heap = [] for symbol, freq in freq_table.items(): heapq.heappush(heap, Node(freq, symbol)) while len(heap) > 1: left = heapq.heappop(heap) right = heapq.heappop(heap) parent = Node(left.freq + right.freq, left=left, right=right) heapq.heappush(heap, parent) return heap[0] def build_freq_table(data): freq_table = {} for symbol in data: if symbol in freq_table: freq_table[symbol] += 1 else: freq_table[symbol] = 1 return freq_table def encode_huffman_tree(node, code_table, code=''): if node.symbol is not None: code_table[node.symbol] = code else: encode_huffman_tree(node.left, code_table, code+'0') encode_huffman_tree(node.right, code_table, code+'1') def compress_ppm_data(data): freq_table = build_freq_table(data) huffman_tree = build_huffman_tree(freq_table) code_table = {} encode_huffman_tree(huffman_tree, code_table) encoded_data = '' for symbol in data: encoded_data += code_table[symbol] # padding padding_len = 8 - len(encoded_data) % 8 encoded_data += '0' * padding_len # write encoded data to binary file with open('compressed.ppm', 'wb') as f: # write huffman tree write_huffman_tree(huffman_tree, f) # write padding length f.write(struct.pack('B', padding_len)) # write encoded data for i in range(0, len(encoded_data), 8): byte = encoded_data[i:i+8] f.write(struct.pack('B', int(byte, 2))) def write_huffman_tree(node, f): if node.symbol is not None: f.write(b'\x01') f.write(struct.pack('B', ord(node.symbol))) else: f.write(b'\x00') write_huffman_tree(node.left, f) write_huffman_tree(node.right, f) def main(): with open('image.ppm', 'rb') as f: data = f.read() # compress ppm data compress_ppm_data(data) if __name__ == '__main__': main() ``` 上述代码中,`compress_ppm_data`函数用于压缩PPM数据,首先通过`build_freq_table`函数构建数据中每个字符出现的频率表,然后使用`build_huffman_tree`函数构建霍夫曼树,再通过`encode_huffman_tree`函数将霍夫曼树编码为一个二进制字符串,最后将数据按照霍夫曼编码转换为字符串,并进行填充,将结果写入到二进制文件中。`write_huffman_tree`函数用于将霍夫曼树写入到二进制文件中。 需要注意的是,这只是一个简单的实现,实际情况中还需要考虑更多的细节和优化。
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Aaron-Suen

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值