1. ソースコードのダウンロード
1.gitのダウンロード
2.CSDNダウンロード
2. ソースコードの移植
私自身、rt-thread オペレーティング システムを使用して移植しました。ただし、OSに限定されるものではなく、ベアメタルも利用可能です。
1. まずソースコードをプロジェクトに追加します
2. メモリ割り当て関数と解放関数をそれぞれ実装します。これはポインタ関数であり、そのプロトタイプは次のとおりです。typedef void* (*CanardMemoryAllocate)(CanardInstance* ins, size_t amount);
static void* mem_allocate(CanardInstance* const canard, const size_t amount)
{
(void) canard;
return rt_malloc(amount);
}
static void mem_free(CanardInstance* const canard, void* const pointer)
{
(void) canard;
rt_free(pointer);
}
3.カナードの初期化
void slave_comm_init()
{
canard = canardInit(&mem_allocate, &mem_free);
canard.node_id = savePara.id;
txQueue = canardTxInit(1536, CANARD_MTU_CAN_CLASSIC); // Set MTU = 64 bytes. There is also CANARD_MTU_CAN_CLASSIC.
}
canard.node_id
送信キューを初期化するためにローカル マシン ID を設定します
canardTxInit(1536, CANARD_MTU_CAN_CLASSIC);
。サイズは 1536 です。CANARD_MTU_CAN_CLASSIC
通常のcanを使用し、最大データサイズは8バイトであるCANARD_MTU_CAN_FD
can fdを使用することを意味します。
3.送信機能の実現
void slave_comm_tx_process()
{
for (const CanardTxQueueItem* ti = NULL; (ti = canardTxPeek(&txQueue)) != NULL;) // Peek at the top of the queue.
{
if ((0U == ti->tx_deadline_usec) || (ti->tx_deadline_usec > rt_tick_get_millisecond()*1000)) // Check the deadline.
{
if (!slave_send_ext(ti->frame.extended_can_id,(void *)ti->frame.payload,ti->frame.payload_size)) // Send the frame over this redundant CAN iface.
{
break; // If the driver is busy, break and retry later.
}
}
// After the frame is transmitted or if it has timed out while waiting, pop it from the queue and deallocate:
canard.memory_free(&canard, canardTxPop(&txQueue, ti));
}
}
canard プロトコルは、送信されたパケットを処理後にキューに書き込み、canardTxPeek(&txQueue))
キューからデータを取り出し、slave_send_ext(ti->frame.extended_can_id,(void *)ti->frame.payload,ti->frame.payload_size))
ハードウェア用の関数を送信し、can を呼び出して送信します。注意:UAVCAN使用的是扩展帧id
。
ハードウェア送信機能は次のとおりです。
rt_inline uint8_t slave_send_ext(uint32_t id,uint8_t *sendBuf,uint8_t len)
{
struct rt_can_msg txMsg = {
0};
txMsg.id = id;
txMsg.ide = RT_CAN_EXTID;
txMsg.rtr = RT_CAN_DTR;
txMsg.len = len;
for(rt_uint8_t i=0;i<len;i++)
{
txMsg.data[i] = sendBuf[i];
}
return rt_device_write(slaveDev,0,&txMsg,sizeof(txMsg));
}
RT_CAN_EXTID
拡張フレームの使用を示します
4. ハードウェアの受信および処理機能を実装する
void slave_comm_rx_process()
{
CanardRxTransfer transfer;
CanardFrame receivedFrame;
struct rt_can_msg canRxMsg = {
0};
uint32_t rxTimestampUsec;
int8_t result;
while(rt_mq_recv(&slave_rec_msgq,&canRxMsg,sizeof(canRxMsg),RT_WAITING_NO) == RT_EOK)
{
receivedFrame.extended_can_id = canRxMsg.id;
receivedFrame.payload_size = canRxMsg.len;
receivedFrame.payload = canRxMsg.data;
rxTimestampUsec = rt_tick_get_millisecond()*1000;
result = canardRxAccept(&canard,
rxTimestampUsec, // When the frame was received, in microseconds.
&receivedFrame, // The CAN frame received from the bus.
0, // If the transport is not redundant, use 0.
&transfer,
NULL);
if (result < 0)
{
// An error has occurred: either an argument is invalid or we've ran out of memory.
// It is possible to statically prove that an out-of-memory will never occur for a given application if
// the heap is sized correctly; for background, refer to the Robson's Proof and the documentation for O1Heap.
// Reception of an invalid frame is NOT an error.
}
else if (result == 1)
{
void process_received_transfer(const uint8_t index,CanardRxTransfer* const transfer);
process_received_transfer(0, &transfer); // A transfer has been received, process it.
canard.memory_free(&canard, transfer.payload); // Deallocate the dynamic memory afterwards.
}
else
{
// Nothing to do.
// The received frame is either invalid or it's a non-last frame of a multi-frame transfer.
// Reception of an invalid frame is NOT reported as an error because it is not an error.
}
}
}
slave_rec_msgq
実装方法はcanの割り込みでデータを受け取り、キューに入れるだけです。
struct rt_can_msg rxMsg = {
0};
rt_device_read(slaveDev,0,&rxMsg,sizeof(rxMsg));
rt_mq_send(&slave_rec_msgq, &rxMsg, sizeof(rxMsg));
次に、プロトコルは処理のためにキューからデータを読み取ります。
can データを canard でサポートされているデータ型に変換します。
receivedFrame.extended_can_id = canRxMsg.id;
receivedFrame.payload_size = canRxMsg.len;
receivedFrame.payload = canRxMsg.data;
プロトコルがデータの完全なフレームを受信すると、result
1 を返し、受信したデータを独自に処理します。
process_received_transfer(0, &transfer); // A transfer has been received, process it.
次のように実装されます:
void process_received_transfer(const uint8_t index,CanardRxTransfer* const transfer)
{
LOG_D("slave rec id:%d size:%d",transfer->metadata.remote_node_id,transfer->payload_size);
if(transfer->metadata.remote_node_id == canard.node_id)
{
slavePackDef *p = (slavePackDef *)transfer->payload;
recCmd = p->packCmd;
}
}
データtransfer->payload
は に保存されます。実行後にメモリを解放します: canard.memory_free(&canard, transfer.payload);
。
5. ニュースを購読する
canard プロトコルには 3 種類のメッセージがあります。つまり、 です。CanardTransferKindMessage``CanardTransferKindResponse
違いCanardTransferKindRequest
は次のとおりです。
CanardTransferKindMessage
: パブリッシャからすべてのサブスクライバへのブロードキャスト。
CanardTransferKindResponse
: サーバーからクライアントへのピアツーピア。
CanardTransferKindRequest
: クライアントからサーバーへのピアツーピア。
一般に、スレーブはサーバーであり、マスターはクライアントです。
void slave_control_init()
{
(void) canardRxSubscribe(&canard, // Subscribe to an arbitrary service response.
CanardTransferKindResponse, // Specify that we want service responses, not requests.
SLAVE_RESPONSE_PORT_ID, // The Service-ID whose responses we will receive.
1536, // The extent (see above).
CANARD_DEFAULT_TRANSFER_ID_TIMEOUT_USEC,
&responseSubscription);
}
上記はCanardTransferKindResponse
メッセージの種類、Service-ID をサブスクライブしましたSLAVE_RESPONSE_PORT_ID
。
#define SLAVE_RESPONSE_PORT_ID 123
関数のプロトタイプは次のとおりです。
int8_t canardRxSubscribe(CanardInstance* const ins,
const CanardTransferKind transfer_kind,
const CanardPortID port_id,
const size_t extent,
const CanardMicrosecond transfer_id_timeout_usec,
CanardRxSubscription* const out_subscription);
パラメータ分析:
ins
: canard のインスタンスは、上記で初期化された変数です。
transfer_kind
: メッセージ タイプ。上記の 3 つ。
port_id
: メッセージ ID。
extent
: 送信ペイロード メモリ バッファのサイズを定義します。
transfer_id_timeout_usec
:デフォルトのトランスポート ID タイムアウト値の定義。
out_subscription
: リクエストに基づいて新しいサブスクリプションが作成された場合、戻り値は 1 です。
関数の呼び出し時にそのようなサブスクリプションが存在する場合、戻り値は 0 です。この場合、既存のサブスクリプションを終了し、代わりに新しいサブスクリプションを作成します。保留中の転送は失われる可能性があります。
入力引数のいずれかが無効な場合、戻り値は負の Invalid ArgumentError になります。
3. アプリケーション層のデータ送信
static void send_data()
{
static uint8_t messageTransferId = 0;
const CanardTransferMetadata transferMetadata = {
.priority = CanardPriorityNominal,
.transfer_kind = CanardTransferKindResponse,
.port_id = SLAVE_RESPONSE_PORT_ID,
.remote_node_id = id,
.transfer_id = messageTransferId,
};
uint8_t sendBuf[100];
for(uint8_t i=0;i<sizeof(sendBuf);i++)
{
sendBuf[i] = i;
}
++messageTransferId; transmission on this subject.
int32_t result = canardTxPush(&txQueue,
&canard,
0,
&transferMetadata,
sizeof(sendBuf),
sendBuf);
if (result < 0)
{
LOG_W("slave cmd send failed!");
}
}
注意しなければならないことは次のとおりです。
const CanardTransferMetadata transferMetadata = {
.priority = CanardPriorityNominal,
.transfer_kind = CanardTransferKindResponse,
.port_id = SLAVE_RESPONSE_PORT_ID, // This is the subject-ID.
.remote_node_id = id, // Messages cannot be unicast, so use UNSET.
.transfer_id = messageTransferId,
};
transfer_kind
正常に受信するには、上でサブスクライブしたメッセージ タイプと同じである必要があります。
messageTransferId
:送信するたびに1ずつインクリメントする必要があります。
port_id
: サブスクリプション メッセージの port_id と同じである必要もあります。