stm32 or gd32 transplant libcanard to realize UAVCAN protocol

1. Source code download

1. git download

click me to download

2. csdn download

Uploaded by yourself click to download

2. Source code porting

I myself ported it using the rt-thread operating system. But it is not limited to the operating system, bare metal is also available.

1. First add the source code to the project

insert image description here

2. Implement a memory allocation and release function respectively. It is a pointer function and its prototype istypedef 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. Initialize canard

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_idSet the local machine id
canardTxInit(1536, CANARD_MTU_CAN_CLASSIC);to initialize the sending queue, and the size is 1536. CANARD_MTU_CAN_CLASSICIt means that ordinary can is used, and the maximum data size is 8 bytes, CANARD_MTU_CAN_FDwhich means that can fd is used.

3. Realize the sending function

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));
	}		
}

The canard protocol will write the sent packet into the queue after processing, canardTxPeek(&txQueue))take out the data from the queue, slave_send_ext(ti->frame.extended_can_id,(void *)ti->frame.payload,ti->frame.payload_size))send the function for the hardware, and call can to send. 注意:UAVCAN使用的是扩展帧id.
The hardware send function is:

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_EXTIDIndicates the use of extended frames

4. Implement can hardware receiving and processing functions

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.
		}
	}	
}

The implementation method is to receive data in the interrupt of can and put it into slave_rec_msgqthe queue.

	struct rt_can_msg rxMsg = {
    
    0};
	rt_device_read(slaveDev,0,&rxMsg,sizeof(rxMsg));
	rt_mq_send(&slave_rec_msgq, &rxMsg, sizeof(rxMsg));	

The protocol then reads data from the queue for processing.
Convert can data to data types supported by canard.

		receivedFrame.extended_can_id = canRxMsg.id;
		receivedFrame.payload_size = canRxMsg.len;
		receivedFrame.payload = canRxMsg.data;

When the protocol receives a complete frame of data, it returns resultequal to 1 and processes the received data by itself.

process_received_transfer(0, &transfer);  // A transfer has been received, process it.

Implemented as:

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;
		
	}
		
}

The data is saved transfer->payloadin . Free memory after execution: canard.memory_free(&canard, transfer.payload);.

5. Subscribe to news

There are three types of messages in the canard protocol, namely: CanardTransferKindMessage``CanardTransferKindResponse, CanardTransferKindRequest
the difference is:
CanardTransferKindMessage: broadcast, from the publisher to all subscribers.
CanardTransferKindResponse: Peer-to-peer, from server to client.
CanardTransferKindRequest: Peer-to-peer, from client to server.

Generally speaking, the slave is the server and the master is the client.

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);
	
}

The above subscribed to a CanardTransferKindResponsetype of message, Service-ID is SLAVE_RESPONSE_PORT_ID,

#define SLAVE_RESPONSE_PORT_ID 123

The function prototype is:

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);

Parameter analysis:
ins: An instance of canard is the variable initialized above.
transfer_kind: Message type, the three mentioned above.
port_id: message id.
extent: Defines the size of the transmit payload memory buffer.
transfer_id_timeout_usec:Default transport id timeout value definition.
out_subscription: The return value is 1 if a new subscription has been created based on the request.
If such a subscription exists when the function is called, the return value is 0. In this case, terminate the existing subscription and create a new subscription in its place. Pending transfers may be lost.
If any of the input arguments is invalid, the return value is a negative Invalid ArgumentError.

3. Application layer data sending

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!");
	}	
}

have to be aware of is:

	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_kindIt needs to be the same as the message type subscribed above to receive successfully.
messageTransferId: It needs to be incremented by 1 each time it is sent.
port_id: It also needs to be the same as the subscription message port_id.

4. Successful porting Source code download

click me to download

Guess you like

Origin blog.csdn.net/qq_15181569/article/details/131415387