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()進行全部發出,如此循環。