uavcan學習,libcanard c語言

1、下載源碼

git clone https://github.com/UAVCAN/libcanard.git
cd libcanard
git submodules update --init --recursive

2、自定義消息

  如創建消息文件 232.Rep.uavcan  ,放在目錄nhf1中,內容:

uint8 v

uint32[32] q

int13 zzz

  進入libcanard\dsdl_compiler目錄,調用生成c代碼指令:

python3 libcanard_dsdlc --outdir <outdir> <dsdl-definition-uavcan-folder>

  生成c文件nhf1_Rep.c, Rep.h,後續使用。

3、消息發送舉例

#include "uavcan/protocol/NodeStatus.h"

/* Reserve memory and struct for messages */
uint8_t packed_uavcan_msg_buf[UAVCAN_PROTOCOL_NODESTATUS_MAX_SIZE];
/* MAX_SIZE comes from module header as pre-calculated */
uavcan_protocol_NodeStatus msg;

msg.uptime_sec = getUptime();
msg.health = UAVCAN_PROTOCOL_NODESTATUS_HEALTH_OK;
msg.mode = UAVCAN_PROTOCOL_NODESTATUS_MODE_OPERATIONAL;
msg.sub_mode = sub_mode;
msg.vendor_specific_status_code = vendor_status_code;

/* Encode the filled struct into packed_uavcan_msg_buf, ready to be sent */
const uint32_t len_of_packed_msg = uavcan_protocol_NodeStatus_encode(&msg, packed_uavcan_msg_buf);

(void) canardBroadcast(&g_canard,
                       UAVCAN_PROTOCOL_NODESTATUS_SIGNATURE,
                       UAVCAN_PROTOCOL_NODESTATUS_ID,
                       &g_bc_node_status_transfer_id,
                       CANARD_TRANSFER_PRIORITY_MEDIUM,
                       packed_uavcan_msg_buf,
                       len_of_packed_msg);

4、消息解析舉例

/* include header */
#include "uavcan/protocol/param/GetSet.h"

#define GETSETREQ_NAME_MAX_SIZE 96 // max size needed for the dynamic arrays
/* Reserve some memory for the dynamic arrays from the stack */
uint8_t buff[GETSETREQ_NAME_MAX_SIZE];
uint8_t* dyn_buf_ptr = buff;

/* Reserve struct */
uavcan_protocol_param_GetSetRequest get_set_req;

/* NOTE get_set_req struct will be cleared in the Decode function first */
(void) uavcan_protocol_param_GetSetRequest_decode(transfer,
                                                  (uint16_t)transfer->payload_len,
                                                  &get_set_req,
                                                  &dyn_buf_ptr);

/* Now the struct get_set_req "object" is ready to be used */

5、代碼範例學習

  libcanard\tests\Demo.c
  涉及文件 :
    canard.c
    canard.h
    canard_internals.h

canardInit(&canard, canard_memory_pool, sizeof(canard_memory_pool), onTransferReceived, shouldAcceptTransfer, NULL);

canard:can庫實例化
onTransferReceived:CAN接收的消息處理函數
shouldAcceptTransfer:CAN接收的消息是否要處理函數,此函數決定上面函數執行

UAVCAN庫與底層CAN驅動發送與接收處理
/* CAN發送與接收處理含稅 */
static void processTxRxOnce(SocketCANInstance* socketcan, int32_t timeout_msec)
{
    // Transmitting
    /* 將CAN庫中緩存的消息全部發送到CAN到驅動上 */
    for (const CanardCANFrame* txf = NULL; (txf = canardPeekTxQueue(&canard)) != NULL;)
    {
    	/* 發送一包數據,會調用底層CAN接口 */
        const int16_t tx_res = socketcanTransmit(socketcan, txf, 0);
        if (tx_res < 0)         // Failure - drop the frame and report
        {
            canardPopTxQueue(&canard);
            (void)fprintf(stderr, "Transmit error %d, frame dropped, errno '%s'\n", tx_res, strerror(errno));
        }
        else if (tx_res > 0)    // Success - just drop the frame
        {
        	/* 釋放CAN庫中申請的緩存 */
            canardPopTxQueue(&canard);
        }
        else                    // Timeout - just exit and try again later
        {
            break;
        }
    }

    // Receiving
    CanardCANFrame rx_frame;
    const uint64_t timestamp = getMonotonicTimestampUSec();
    /* 從底層CAN驅動中接收的數據取出來進行解析 */
    const int16_t rx_res = socketcanReceive(socketcan, &rx_frame, timeout_msec);
    if (rx_res < 0)             // Failure - report
    {
        (void)fprintf(stderr, "Receive error %d, errno '%s'\n", rx_res, strerror(errno));
    }
    else if (rx_res > 0)        // Success - process the frame
    {
    	/* 調用CAN庫進行數據解析 */
        canardHandleRxFrame(&canard, &rx_frame, timestamp);
    }
    else
    {
        ;                       // Timeout - nothing to do
    }
}

CAN發送內容到UAVCAN庫

/**
 * This function is called at 1 Hz rate from the main loop.
 */
static void process1HzTasks(uint64_t timestamp_usec)
{
    /*
     * Purging transfers that are no longer transmitted. This will occasionally free up some memory.
     */
    canardCleanupStaleTransfers(&canard, timestamp_usec);

    /*
     * Printing the memory usage statistics.
     */
    {
        const CanardPoolAllocatorStatistics stats = canardGetPoolAllocatorStatistics(&canard);
        const uint16_t peak_percent = (uint16_t)(100U * stats.peak_usage_blocks / stats.capacity_blocks);

        printf("Memory pool stats: capacity %u blocks, usage %u blocks, peak usage %u blocks (%u%%)\n",
               stats.capacity_blocks, stats.current_usage_blocks, stats.peak_usage_blocks, peak_percent);

        /*
         * The recommended way to establish the minimal size of the memory pool is to stress-test the application and
         * record the worst case memory usage.
         */
        if (peak_percent > 70)
        {
            puts("WARNING: ENLARGE MEMORY POOL");
        }
    }

    /*
     * Transmitting the node status message periodically.
     */
    {
        uint8_t buffer[UAVCAN_NODE_STATUS_MESSAGE_SIZE];
        makeNodeStatusMessage(buffer);

        static uint8_t transfer_id;  // Note that the transfer ID variable MUST BE STATIC (or heap-allocated)!
		/* 數據廣播到UAVCAN網絡 */
        const int16_t bc_res = canardBroadcast(&canard,
                                               UAVCAN_NODE_STATUS_DATA_TYPE_SIGNATURE,
                                               UAVCAN_NODE_STATUS_DATA_TYPE_ID,
                                               &transfer_id,
                                               CANARD_TRANSFER_PRIORITY_LOW,
                                               buffer,
                                               UAVCAN_NODE_STATUS_MESSAGE_SIZE);
        if (bc_res <= 0)
        {
            (void)fprintf(stderr, "Could not broadcast node status; error %d\n", bc_res);
        }
    }

    node_mode = UAVCAN_NODE_MODE_OPERATIONAL;
}

UAVCAN庫接收到的消息進行應答或者decode數據

/**
 * This callback is invoked by the library when a new message or request or response is received.
 */
static void onTransferReceived(CanardInstance* ins,
                               CanardRxTransfer* transfer)
{
    if ((transfer->transfer_type == CanardTransferTypeRequest) &&
        (transfer->data_type_id == UAVCAN_GET_NODE_INFO_DATA_TYPE_ID))
    {
        printf("GetNodeInfo request from %d\n", transfer->source_node_id);

        uint8_t buffer[UAVCAN_GET_NODE_INFO_RESPONSE_MAX_SIZE];
        memset(buffer, 0, UAVCAN_GET_NODE_INFO_RESPONSE_MAX_SIZE);

        // NodeStatus
        makeNodeStatusMessage(buffer);

        // SoftwareVersion
        buffer[7] = APP_VERSION_MAJOR;
        buffer[8] = APP_VERSION_MINOR;
        buffer[9] = 1;                          // Optional field flags, VCS commit is set
        uint32_t u32 = GIT_HASH;
        canardEncodeScalar(buffer, 80, 32, &u32);
        // Image CRC skipped

        // HardwareVersion
        // Major skipped
        // Minor skipped
        readUniqueID(&buffer[24]);
        // Certificate of authenticity skipped

        // Name
        const size_t name_len = strlen(APP_NODE_NAME);
        memcpy(&buffer[41], APP_NODE_NAME, name_len);

        const size_t total_size = 41 + name_len;

        /*
         * Transmitting; in this case we don't have to release the payload because it's empty anyway.
         */
        const int16_t resp_res = canardRequestOrRespond(ins,
                                                        transfer->source_node_id,
                                                        UAVCAN_GET_NODE_INFO_DATA_TYPE_SIGNATURE,
                                                        UAVCAN_GET_NODE_INFO_DATA_TYPE_ID,
                                                        &transfer->transfer_id,
                                                        transfer->priority,
                                                        CanardResponse,
                                                        &buffer[0],
                                                        (uint16_t)total_size);
        if (resp_res <= 0)
        {
            (void)fprintf(stderr, "Could not respond to GetNodeInfo; error %d\n", resp_res);
        }
    }
}

UAVCAN庫對接收的消息是否需要進行處理

/**
 * This callback is invoked by the library when it detects beginning of a new transfer on the bus that can be received
 * by the local node.
 * If the callback returns true, the library will receive the transfer.
 * If the callback returns false, the library will ignore the transfer.
 * All transfers that are addressed to other nodes are always ignored.
 */
static bool shouldAcceptTransfer(const CanardInstance* ins,
                                 uint64_t* out_data_type_signature,
                                 uint16_t data_type_id,
                                 CanardTransferType transfer_type,
                                 uint8_t source_node_id)
{
    (void)source_node_id;

    if (canardGetLocalNodeID(ins) == CANARD_BROADCAST_NODE_ID)
    {
        /*
         * If we're in the process of allocation of dynamic node ID, accept only relevant transfers.
         */
        if ((transfer_type == CanardTransferTypeBroadcast) &&
            (data_type_id == UAVCAN_NODE_ID_ALLOCATION_DATA_TYPE_ID))
        {
            *out_data_type_signature = UAVCAN_NODE_ID_ALLOCATION_DATA_TYPE_SIGNATURE;
            return true;
        }
    }
    else
    {
        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;
        }
    }

    return false;
}

6、總之

   processTxRxOnce()會將UAVCAN庫中緩存的消息通過底層CAN接口發送出去,同時會從底層CAN接口取出數據,放到 canardHandleRxFrame()進行數據解析,然後會通過 shouldAcceptTransfer()判斷該消息是否需要,若需要則 onTransferReceived()進行decode,根據消息類型ID調用對應的自定義decode函數進行數據映射。
   canardBroadcast()會將數據廣播到UAVCAN庫中,進行緩存,再通過 processTxRxOnce()進行全部發出,如此循環。

參考:https://github.com/UAVCAN/libcanard/tree/master

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章