PX4飞控的PPM接收机

(一)原理图:

      PX4飞控的PPM输入捕获由协处理器完成,接在A8引脚,对应Timer1的通道1。

(二)PPM协议:

      PPM的每一帧数据间隔为20ms,用两个上升沿之间的时间间隔表示一个通道的值,如图所示是一个6通道信号。在每一帧的结束会有较长时间的持续低电平,两次上升沿时间间隔要大于5ms,可以用于辨别下一帧数据的到来。

(三)相关代码

定时器1配置:

/* timer1 config */
void Bsp_Timer1_Config(void)
{
    TIM_ICInitTypeDef       TIM_ICInitStructure;
    TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
    NVIC_InitTypeDef        NVIC_InitStructure;
    GPIO_InitTypeDef        GPIO_InitStructure;

    /* Clock Enable */
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

    /* Enable global Interrupt */
    NVIC_InitStructure.NVIC_IRQChannel = TIM1_CC_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);

    /* TIM1 channel 1 pin (PA.08) configuration */
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    /* Time Base configuration, 1us every tick */
    TIM_TimeBaseStructure.TIM_Prescaler = SystemCoreClock / 1000000;
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
    TIM_TimeBaseStructure.TIM_Period = 0xFFFF;
    TIM_TimeBaseStructure.TIM_ClockDivision = 0;
    TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;

    TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);

    TIM_ICInitStructure.TIM_Channel = TIM_Channel_1;
    TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;
    TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
    TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
    TIM_ICInitStructure.TIM_ICFilter = 0x0;

    TIM_ICInit(TIM1, &TIM_ICInitStructure);

    /* TIM enable counter */
    TIM_Cmd(TIM1, ENABLE);

    /* Enable the CC1 Interrupt Request */
    TIM_ITConfig(TIM1, TIM_IT_CC1, ENABLE);
}

PPM处理程序:

#define GET_GAP(x, y)	(x > y ? (x - y) : (0xFFFF - y + x))

ST_PPM machine_param;

static uint8_t PPM_Recv = 0;        /* ppm received flag */ 
static uint8_t PPM_Send_Freq = 0;   /* freq of sending ppm to fmu */ 

/* status turn to the next one */ 
static void ppm_next_status(ST_PPM *param)
{
    static uint8_t max_ch = STA_EDGE_9 + 1;

    param->status = (param->status + 1) % max_ch;
}

/* reset the ppm machine state */
void reset_machine_state(ST_PPM *param)
{
    param->status = STA_EDGE_1;
    param->bad_frame = 0;
    param->last_ic = 0;
}

/* the function will be called back by the timer1 interrupt */
void App_PPM_Status_Machine(uint16_t ic)
{
    static uint16_t temp_val[8];
    uint16_t gap;

    gap = GET_GAP(ic, machine_param.last_ic);

    /* each ppm frame has a 5.5ms start puls, each tick is 1us */
    if (gap > 5000)
    {
        reset_machine_state(&machine_param);
    }
    else if (gap < 800 || gap > 2200)
    {
        /* bad signal, drop frame */
        machine_param.bad_frame = 1;
    }

    if (machine_param.status == STA_EDGE_1)
    {
        /* do nothing */
    }
    else if (machine_param.status == STA_EDGE_9)
    {
        uint8_t i;

        temp_val[STA_EDGE_9 - 1] = gap;
        /* we know next we have a 5.5ms gap, so we can do some time-cost task here */
        /* If it's bad frame, we drop it */
        if (!machine_param.bad_frame)
        {
            for (i = 0; i < 8; i++)
            {
                machine_param.ppm_val[i] = temp_val[i];
            }
            PPM_Recv = 1;
        }
    }
    else
    {
        temp_val[machine_param.status - 1] = gap;
    }

    machine_param.last_ic = ic;
    ppm_next_status(&machine_param);
}

/* received a wholed ppm frame */
uint8_t App_PPM_Ready(void)
{
    static uint32_t prev_time = 0;
    uint32_t time = Sys_Tick_Time;

    if (!PPM_Send_Freq)
    {
        return 0;
    }

    if (time - prev_time > 1000 / PPM_Send_Freq && PPM_Recv)
    {
        prev_time = time;
        return 1;
    }

    return 0;
}

/* copy the ppm frame to the buff when the ppm frame will be used */
void App_Get_PPM_Value(uint16_t ppm[8])
{
    memcpy(ppm, machine_param.ppm_val, 8);
    PPM_Recv = 0;
}

/* set the frequency of sending ppm buff to fmu */
uint8_t App_Set_PPM_Send_Freq(uint8_t freq)
{
    if (freq <= 50)
    {
        PPM_Send_Freq = freq;
        return 1;
    }
    else
    {
        return 0;
    }
}	(x > y ? (x - y) : (0xFFFF - y + x))

ST_PPM machine_param;

static uint8_t PPM_Recv = 0;        /* ppm received flag */ 
static uint8_t PPM_Send_Freq = 0;   /* freq of sending ppm to fmu */ 

/* status turn to the next one */ 
static void ppm_next_status(ST_PPM *param)
{
    static uint8_t max_ch = STA_EDGE_9 + 1;

    param->status = (param->status + 1) % max_ch;
}

/* reset the ppm machine state */
void reset_machine_state(ST_PPM *param)
{
    param->status = STA_EDGE_1;
    param->bad_frame = 0;
    param->last_ic = 0;
}

/* the function will be called back by the timer1 interrupt */
void App_PPM_Status_Machine(uint16_t ic)
{
    static uint16_t temp_val[8];
    uint16_t gap;

    gap = GET_GAP(ic, machine_param.last_ic);

    /* each ppm frame has a 5.5ms start puls, each tick is 1us */
    if (gap > 5000)
    {
        reset_machine_state(&machine_param);
    }
    else if (gap < 800 || gap > 2200)
    {
        /* bad signal, drop frame */
        machine_param.bad_frame = 1;
    }

    if (machine_param.status == STA_EDGE_1)
    {
        /* do nothing */
    }
    else if (machine_param.status == STA_EDGE_9)
    {
        uint8_t i;

        temp_val[STA_EDGE_9 - 1] = gap;
        /* we know next we have a 5.5ms gap, so we can do some time-cost task here */
        /* If it's bad frame, we drop it */
        if (!machine_param.bad_frame)
        {
            for (i = 0; i < 8; i++)
            {
                machine_param.ppm_val[i] = temp_val[i];
            }
            PPM_Recv = 1;
        }
    }
    else
    {
        temp_val[machine_param.status - 1] = gap;
    }

    machine_param.last_ic = ic;
    ppm_next_status(&machine_param);
}

/* received a wholed ppm frame */
uint8_t App_PPM_Ready(void)
{
    static uint32_t prev_time = 0;
    uint32_t time = Sys_Tick_Time;

    if (!PPM_Send_Freq)
    {
        return 0;
    }

    if (time - prev_time > 1000 / PPM_Send_Freq && PPM_Recv)
    {
        prev_time = time;
        return 1;
    }

    return 0;
}

/* copy the ppm frame to the buff when the ppm frame will be used */
void App_Get_PPM_Value(uint16_t ppm[8])
{
    memcpy(ppm, machine_param.ppm_val, 8);
    PPM_Recv = 0;
}

/* set the frequency of sending ppm buff to fmu */
uint8_t App_Set_PPM_Send_Freq(uint8_t freq)
{
    if (freq <= 50)
    {
        PPM_Send_Freq = freq;
        return 1;
    }
    else
    {
        return 0;
    }
}

定时器1中断程序:

/* timer1 interrupt */
void TIM1_CC_IRQHandler(void)
{
    uint16_t ic = 0;

    if (TIM_GetITStatus(TIM1, TIM_IT_CC1) == SET) 
    {
        /* Clear TIM1 Capture compare interrupt pending bit */
        TIM_ClearITPendingBit(TIM1, TIM_IT_CC1);

        ic = TIM_GetCapture1(TIM1);
        App_PPM_Status_Machine(ic);
    }
}
  • 5
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
PPM编码器可以将8 个PWM(脉宽调制)信号编码成一个PPM(脉位调制)信号。3DR PPM编码器(v2)现在使用ArduPPM固件,取代以前用Paparazzi PPM编码器固件。这个新ArduPPM固件设计的初衷就是来提高性能和稳健性,同时更好的适应我们的产品现在和将来的需要。 飞控 3DR PPM编码器实物截图: PPM编码器的作用是把接收机输出的多个通道PWM信号编码成PPM复合信号输出,以单线方式连接所支持的飞控。有些飞控为了节约端口资源或者布局限制,只允许连接PPM信号,这就需要PPM编码器。市场上有些接收机可以直接输出PPM信号,例如Graupner。PPM信号也可以用于直接连接模拟器狗和接收机进行无线模拟训练。 请注意:PPM信号与S.BUS.、XBUS类似,都是单线传输所有通道信号,但几种编码方式完全不同,并不能相互兼容。此PPM编码器硬件源于PPZ的PPM编码器,默认固件为3DR所开发,如需自行修改功能,请自备ISP类型编程器。我们预先刷入的是v2.3.16 for arducopter版本,适合多轴飞行器使用。 飞控 3DR PPM编码器电路 PCB截图: 失效保护输出值 新的中断系统处理某些Futaba接收器时表现更好(在快速的时间间隔内对R/C通道组进行开放式并发控制)(在v2.3.13版本中已经存在)。 通道信号丢失情况下的适应行为:如果一个通道信号已经丢失,他将会根据下表进行相应设置。此时其他通道将继续工作。 同时注意,安全操作与发射器和接收器之间断开无线连接是木有关系的。当参数超出范围的时候,接收机的响应取决于发射器/接收机的硬件和设置。所以在飞行之前确保检查过所有场景和硬件设置。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值