Page tree

Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

In this article we will extend the functionality of a the UAVCAN node from the previous tutorial. For pragmatic purposes reasons we will make something that can be used useful - a UAVCAN to RCPWM converter.

...

Our goal is to make Zubax Babel act as a UAVCAN node and receive RCPWM values via CAN and then convert them to RCPWM signal outputs.

Couple A couple of words about RCPWM. RCPWM is one of the most widespread way of interaction in hobby RC world. All servos and almost all popular low-level analog interfaces in robotics. Most of the low-cost servos and motor controllers support it (and there are lots and lots of other devices that communicate this way). Without a doubt, it is a very old, slow and limited interface, and today it must be replaced with something better (like UAVCAN). But this is legacy and we have to deal with it. From the physical standpoint, RCPWM (also known as RC PWM) is just a PWM signal with a period of 20mS and the infromation is coded 20 ms where the information is encoded in pulse widthm width which may vary from 1 mS ms to 2 mS ms with the neutral value at 1.5 mSms. To generate that, we will use timers that are available on Zubax Babel (according to to the datasheet where we can also find a detailed pinout specification).

We will use TIM12_CH1, TIM12_CH2, TIM3_CH1, TIM3_CH2, TIM3_CH3, TIM3_CH4, utilizing all available pins.

...

We will base our project on the code from the Basic tutorial end and add lacking the missing functions. Firstly we should add support of a new message type to shouldAcceptTransfer  and its appropriate handler to onTransferReceived:

Code Block
titleshouldAcceptTransfer
bool shouldAcceptTransfer(const CanardInstance* ins,
                          uint64_t* out_data_type_signature,
                          uint16_t data_type_id,
                          CanardTransferType transfer_type,
                          uint8_t source_node_id)
{
    if ((transfer_type == CanardTransferTypeRequest) &&
        (data_type_id == UAVCAN_GET_NODE_INFO_DATA_TYPE_ID))
    {
        *out_data_type_signature = UAVCAN_GET_NODE_INFO_DATA_TYPE_SIGNATURE;
        return true;
    }

    if(data_type_id == UAVCAN_EQUIPMENT_ESC_RAWCOMMAND_ID)
    {
        *out_data_type_signature = UAVCAN_EQUIPMENT_ESC_RAWCOMMAND_SIGNATURE;
        return true;
    }
   
    return false;
}

...

Code Block
titleonTransferReceived
void onTransferReceived(CanardInstance* ins, CanardRxTransfer* transfer)
{
    if ((transfer->transfer_type == CanardTransferTypeRequest) &&
    (transfer->data_type_id == UAVCAN_GET_NODE_INFO_DATA_TYPE_ID))
    {
        getNodeInfoHandleCanard(transfer);
    } 

    if (transfer->data_type_id == UAVCAN_EQUIPMENT_ESC_RAWCOMMAND_ID)
    { 
        rawcmdHandleCanard(transfer);
    }
}

We added support of UAVCAN_EQUIPMENT_ESC_RAWCOMMAND_ID, message that contains all values needed to generate RCPWM signal from ESC setpoint messages. The UAVCAN GUI Tool starts to broadcast these messages when the ESC Management panel is opened.  Now its Note that other messages can also be mapped to RCPWM outputs; for example, the actuator command messages from the namespace uavcan.equipment.actuator.

Now it's the handler's turn, where RCPWM values are passed to the MCU timers:

Code Block
titlecanard_rawcmd_handle
void rawcmdHandleCanard(CanardRxTransfer* transfer)
{
    int16_t ar[6] = {0,0,0,0,0,0};
    int offset = 0;
    for (int i = 0; i<6; i++)
    {
        if (canardDecodeScalar(transfer, offset, 14, true, &ar[i])<14) { break; }
        offset += 14;
    }
    rcpwmUpdate(ar);
}

const rcpwm_t rcpwm_outputs[] = 
{
    {TIM12, 1, "TIM12_CH1_PA4"},
    {TIM12, 2, "TIM12_CH1_PA5"},
    {TIM3,  1, "TIM3_CH1_PA6"},
    {TIM3,  2, "TIM3_CH2_PB0"},
    {TIM3,  3, "TIM3_CH3_PB6"},
    {TIM3,  4, "TIM3_CH4_PB7"}
};

void rcpwmUpdate(int16_t ar[6])
{
    for (char i = 0; i < 6; i++ )
    {
        uint32_t val = RCPWM_NEUTRAL_uS;
        ar[i] = ar[i] * RCPWM_MAGNITUDE_uS / UAVCAN_EQUIPMENT_ESC_RAWCOMMAND_MAX_VALUE;
        val += ar[i];
        TIM_SetCompare(rcpwm_outputs[i].timer,rcpwm_outputs[i].channel, uS_to_ticks(val));
    }
}


static void TIM_SetCompare(TIM_TypeDef* TIMx, uint8_t ch, uint32_t Compare)
{
    if (ch == 1)  
    {
        TIMx->CCR1 = Compare;
        return;
    }
    if (ch == 2)  
    {
        TIMx->CCR2 = Compare;
        return;
    }
    if (ch == 3)  
    {
        TIMx->CCR3 = Compare;
        return;
    }
    if (ch == 4)  
    {
        TIMx->CCR4 = Compare;
        return;
    }
}

And that's it. Now its time to open the UAVCAN GUI Tool and go to ESC the ESC Management panel:

Our device supports up to six channels, so increase the number of channels and try moving the sliders. 

If you connect an oscilloscope to Babel, you may should see something like this:

Or you may connect servos and see them moving move after the slider in sliders on the ESC panel.