git clone https://github.com/UAVCAN/libcanard.git
cd libcanard
git submodules update --init --recursive
如创建消息文件 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,后续使用。
#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);
/* 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 */
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接收的消息是否要处理函数,此函数决定上面函数执行
/* 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
}
}
/**
* 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;
}
/**
* 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);
}
}
}
/**
* 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;
}
processTxRxOnce()会将UAVCAN库中缓存的消息通过底层CAN接口发送出去,同时会从底层CAN接口取出数据,放到 canardHandleRxFrame()进行数据解析,然后会通过 shouldAcceptTransfer()判断该消息是否需要,若需要则 onTransferReceived()进行decode,根据消息类型ID调用对应的自定义decode函数进行数据映射。
canardBroadcast()会将数据广播到UAVCAN库中,进行缓存,再通过 processTxRxOnce()进行全部发出,如此循环。
参考:https://github.com/UAVCAN/libcanard/tree/master