stm32或gd32移植libcanard实现UAVCAN协议

一、源码下载

1、git下载

点击我下载

2、csdn下载

自己上传的点击下载

二、源码移植

我自己是使用rt-thread操作系统移植的。但是不局限与操作系统,裸机也可以。

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、初始化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_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、实现can硬件接收处理函数

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

实现方法是,在can的中断中接收到数据放入slave_rec_msgq队列,

	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协议中消息有三种类型,分别是: CanardTransferKindMessage``CanardTransferKindResponseCanardTransferKindRequest
区别在于:
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:消息类型,上面说的三个。
port_id:消息id。
extent:定义了传输有效载荷存储器缓冲器的大小。
transfer_id_timeout_usec:默认传输ID超时值定义。
out_subscription:如果已根据请求创建新订阅,则返回值为1。
如果在调用函数时存在此类订阅,则返回值为0。在这种情况下,终止现有订阅,然后在其位置创建新订阅。挂起的传输可能会丢失。
如果任何输入参数无效,则返回值为否定的无效参数错误。

三、应用层数据发送

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一样。

四、移植成功源码下载

点击我下载

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/3738.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

js 左右滑动切换图片

一、效果图 二、代码 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewport" content"widthdev…

vscode设置可以搜索包含node_modules中的文件

步骤3中删除掉node_modules&#xff0c;再搜索的时候&#xff0c;node_modules的匹配到代码也会展示出来了。 如果不想要被搜索文件包含node_modules,再添加上就可以。

MySQL 连接的使用

MySQL 连接的使用 在前几章节中&#xff0c;我们已经学会了如何在一张表中读取数据&#xff0c;这是相对简单的&#xff0c;但是在真正的应用中经常需要从多个数据表中读取数据。 ​ 本章节我们将向大家介绍如何使用 MySQL 的 JOIN 在两个或多个表中查询数据。 你可以在 SEL…

webstorm配置vue开发环境

&#x1f333;&#x1f333;&#x1f333;前言&#xff1a;本文章针对于如何用IDE和webstorm运行一个别人的vue项目进行步骤记录。 &#x1f4d9;参考&#xff1a;(10条消息) idea配置vue开发环境_idea配置vue运行环境_drinkworld的博客-CSDN博客https://blog.csdn.net/drinkwo…

容器生态系统概念讲解

容器生态系统 这张图显示了 Docker、Kubernetes、CRI、OCI、containerd 和 runc 在这个生态系统中是如何结合的。 其工作流程简单来说是这样的&#xff1a; Docker&#xff0c;Kubernetes 等工具来运行一个容器时会调用容器运行时&#xff08;CRI&#xff09;比如 containerd…

探索Java集合框架的奥秘

文章目录 1. 引言2. Java 集合框架概览2.1 Collection接口2.1.1 List接口(1) ArrayList&#xff08;数组&#xff09;(2) Vector&#xff08;数组实现、线程同步&#xff09;(3) LinkedList&#xff08;链表&#xff09; 2.1.2 Set 接口(1) HashSet&#xff08;Hash 表&#xf…

选择高考志愿:聚焦计算机科学与技术,规避土木工程

选择高考志愿&#xff1a;聚焦计算机科学与技术&#xff0c;规避土木工程 高考季已至&#xff0c;各地高考成绩陆续公布&#xff0c;许多毕业生和家长开始面临疑惑&#xff1a;如何填报志愿、选专业还是选学校、什么专业好就业&#xff1f;张雪峰曾提到&#xff1a;“普通家庭…

nvm 和 nrm安装使用

前端工具推荐&#xff1a;nvm&#xff08;Node 版本管理工具&#xff09; 和 nrm&#xff08;管理npm源&#xff09;&#xff1a; 一、nvm 1.1 nvm 是什么 1.2 安装 nvm 1.3 使用 nvm 二、nrm 2.1 nrm 是什么 2.2 安装 nrm 2.3 使用 nrm 一、nvm 如果直接将 node 安装到…

常见面试题之线程中并发锁(一)

1. 讲一下synchronized关键字的底层原理&#xff1f; 1.1. 基本使用 如下抢票的代码&#xff0c;如果不加锁&#xff0c;就会出现超卖或者一张票卖给多个人 Synchronized【对象锁】采用互斥的方式让同一时刻至多只有一个线程能持有【对象锁】&#xff0c;其它线程再想获取这…

Midjourney教程古风人像类

古风图像的特点&#xff1a; 人物发型多为飘逸的长发&#xff0c;或是精致的盘发&#xff1b; 人物服装多为飘逸的长袍、长裙&#xff1b; 整体画风以水墨、水彩、工笔为c主&#xff0c;线条写意&#xff0c;色彩清新淡雅&#xff1b; 背景中多用花鸟、亭台楼阁、桃林等构建氛…

INDEMIND双目视觉惯性模组实时生成点云并保存点云图

双目惯性相机最开始是从VINS中了解到的&#xff0c;2018年VINS中推荐过Loitor视觉惯性相机&#xff0c;但是后来看到GitHub Issue中有人反映Loitor丢帧、无技术支持等问题&#xff0c;加之购入渠道非官方故未入手Loitor&#xff0c;浏览知乎时关注到Indemind的该款产品&#xf…

FreeRTOS—任务基础知识

文章目录 一、FreeRTOS任务特性二、FreeRTOS任务状态三、FreeRTOS任务优先级四、FreeRTOS任务实现五、任务控制块六、任务堆栈 一、FreeRTOS任务特性 简单没有使用限制&#xff08;任务数量没有显示&#xff0c;一个优先级下可以有多个任务&#xff09;支持抢占&#xff08;高…