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

四、移植成功源码下载

点击我下载

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于预测帧的视频异常检测经典论文

16年上海科技的论文&#xff0c;上海科技做这个方向的系大佬多多的。 摘要 受基于稀疏编码的异常检测能力的激励&#xff0c;我们提出了一种时间相干稀疏编码(TSC)&#xff0c;其中我们强制用相似的重构系数对相似的相邻帧进行编码。然后&#xff0c;我们用一种特殊类型的层叠…

【开源工具】使用Whisper提取视频、语音的字幕

这里写目录标题 一、语音转字幕操作步骤1、下载安装包Assets\WhisperDesktop.zip[^2]2、加载模型2.1 下载模型2.1.1 进入Hugging Face[^3]的仓库2.1.2 选择需要下载的模型2.1.3 配置模型路径 3、语音转字幕4、实时语言转录功能 二、相关简介[^1]特点开发人员指南构建说明其他注…

英国 Tortoise Media发布2023年全球AI指数排名;美团宣布完成收购光年之外

&#x1f989; AI新闻 &#x1f680; 美团宣布完成收购光年之外&#xff0c;加强人工智能竞争力 摘要&#xff1a;美团在公告中宣布于2023年6月29日盘后收购光年之外的全部权益&#xff0c;以加强其在快速增长的人工智能行业中的竞争力。光年之外是中国领先的通用人工智能创新…

SpringBoot整合RabbitMQ实现消息延迟队列(含源码)

环境依赖 SpringBoot 3.1.0 JDK 17 前期准备 安装MQ: liunxdockerrabbitmq安装延迟队列插件 实例 实现延迟队列的一种方式是在 RabbitMQ 中使用消息延迟插件&#xff0c;这个插件可以让你在消息发送时设置一个延迟时间&#xff0c;超过这个时间后消息才会被消费者接收到…

【JVM内存模型】—— 每天一点小知识

&#x1f4a7; J V M 内存模型 \color{#FF1493}{JVM内存模型} JVM内存模型&#x1f4a7; &#x1f337; 仰望天空&#xff0c;妳我亦是行人.✨ &#x1f984; 个人主页——微风撞见云的博客&#x1f390; &#x1f433; 《数据结构与算法》专栏的文章图文并茂&#x…

智谱AI-算法实习生(知识图谱方向)实习面试记录

岗位描述 没错和我的经历可以说是match得不能再match了&#xff0c;但是还是挂了hh。 面试内容 给我面试的是唐杰老师的博士生&#xff0c;方向是社交网络数据挖掘&#xff0c;知识图谱。不cue名了&#xff0c;态度很友好的 &#xff0c;很赞。 date&#xff1a;6.28 Q1 自…

【Spark】介绍,部署与快速入门

文章目录 介绍核心模块Spark CoreSpark SQLSpark StreamingSpark MLlibSpark GraphX 部署命令行Web UI提交应用Local 模式Standalone配置文件添加 JAVA_HOME 环境变量和集群对应的 master 节点启动集群配置历史服务添加日志存储路径添加日志配置webui 配置高可用 Yarn模式配置文…

使用npm install -g @vue/cli 命令安装最新的脚手架与Vue版本不匹配的问题

使用npm install -g vue/cli 命令安装最新的脚手架 创建项目时不要选择Vue版本&#xff0c;让它默认选择&#xff08;默认选择 Vue2&#xff09;否则会出现 vue版本和脚手架版本vue-cli 不兼容的问题&#xff08;怪哉&#xff09; 脚手架兼容vue2 不兼容vue3 &#xff1f; 不理…

2023 年 10 大前端发展趋势

新技术的出现和老技术的淘汰让前端开发者们需要不断地学习和更新知识。特别是在经济不好的情况下&#xff0c;是否掌握新的技术很大程度决定着你是否被淘汰。 虽然应用程序试图将网站替代&#xff0c;但前端 Web 开发业务仍在快速变化和增长&#xff0c;前端开发人员的功能并没…

配置Jenkins slave agent(通过jnlp)方式连接

上一章&#xff0c;使用ssh的方式添加了两个agent&#xff0c;并都成功完成了构建任务&#xff0c;这一章使用jnlp的方式配置agent&#xff0c;jnlp方式配置agent有个好处&#xff0c;就是agent是主动去找到Master请求连接的&#xff0c;master->agent的通道可以配置一个age…

Leetcode-每日一题【234.回文链表】

题目 给你一个单链表的头节点 head &#xff0c;请你判断该链表是否为回文链表。如果是&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。 示例 1&#xff1a; 输入&#xff1a;head [1,2,2,1]输出&#xff1a;true 示例 2&#xff1a; 输入&#xff1a;head…

要从HTML中提取img标签的src属性(图片链接),可以使用正则表达式方式。

1. 定义提取src属性的正则表达式: const srcRegex /<img\s(?:[^>]*?\s)?src\s*\s*(["])((?:[^\1"]|\\\1|.)*?)\1/g 这个正则会匹配类似<img src"http://example.com/1.jpg">中的src属性和括号中的连接。2. 调用字符串的matchAll()方法…

【数据仓库】Apache Doris介绍

Apache Doris介绍 Apache Doris应用场景 Apache Doris核心特性 Apache Doris架构 Doris数据模型三种 Aggregate模型介绍 Uniq模型介绍 在某些多维分析场景下,用户更关注的是如何保证Key的唯一性Key 唯一性约束。因此&#xff0c;我们引入了 Unig 的数据模型。该模型本质上是聚…

redis高可用(二)

redis高可用&#xff08;二&#xff09; 一、主从复制 1.概念 主从复制&#xff0c;是指将一台Redis服务器的数据&#xff0c;复制到其他的Redis服务器。前者称为主节点(Master)&#xff0c;后者称为从节点(Slave)&#xff1b;数据的复制是单向的&#xff0c;只能由主节点到…

爬虫入门指南(5): 分布式爬虫与并发控制 【提高爬取效率与请求合理性控制的实现方法】

文章目录 前言多线程与多进程多线程多进程多线程和多进程的选择 使用Scrapy框架实现分布式爬虫1. 创建Scrapy项目2. 配置Scrapy-Redis3. 创建爬虫4. 启动爬虫节点5. 添加任务到队列 并发控制与限制请求频率并发控制限制请求频率 未完待续... 前言 在进行爬虫任务时&#xff0c;…

开源 sysgrok — 用于分析、理解和优化系统的人工智能助手

作者&#xff1a;Sean Heelan 在这篇文章中&#xff0c;我将介绍 sysgrok&#xff0c;这是一个研究原型&#xff0c;我们正在研究大型语言模型 (LLM)&#xff08;例如 OpenAI 的 GPT 模型&#xff09;如何应用于性能优化、根本原因分析和系统工程领域的问题。 你可以在 GitHub …

Scrapy框架--settings配置 (详解)

目录 settings配置 官网-参考配置 配置文档 Scrapy默认BASE设置 settings配置 Scrapy框架中的配置文件&#xff08;settings.py&#xff09;是用来管理爬虫行为和功能的关键部分。它是一个Python模块&#xff0c;提供了各种配置选项&#xff0c;可以自定义和控制爬虫的行为。…

Excel实用技巧 如何将EXCEL中在同个单元格中的汉字和数字分开

右边字符串&#xff0c;左边数字 RIGHT(A1,LENB(A1)-LEN(A1)) LEFT(A1,2*LEN(A1)-LENB(A1)) 左边字符串&#xff0c;右边数字 LEFT(A1,LENB(A1)-LEN(A1)) RIGHT(A1,2*LEN(A1)-LENB(A1))

关注个人信息安全

近日&#xff0c;某高校毕业生在校期间窃取学校内网数据&#xff0c;收集全校学生个人隐私信息的新闻引发了人们对互联网生活中个人信息安全问题的再度关注。在大数据时代&#xff0c;算法分发带来了隐私侵犯&#xff0c;在享受消费生活等便捷权利的同时&#xff0c;似乎又有不…

jupyter-notebook使用指南

jupyter-notebook使用指南 jupyter-notebook安装[python版][anaconda版] jupyter-notebook如何导出PDF&#xff1f;【没解决&#xff0c;直接看最后&#xff0c;不要跟着操作&#xff01;】正常导出步骤安装Pandoc安装Xelatex问题没解决&#xff0c;懒得安装了&#xff0c;放弃…