stm32或gd32移植libcanard实现UAVCAN协议

news2024/11/20 2:19:16

一、源码下载

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.coloradmin.cn/o/691465.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Keras-深度学习-神经网络-电影评论情感分析模型

目录 模型搭建 模型训练 模型搭建 使用到的数据集为IMDB电影评论情感分类数据集&#xff0c;该数据集包含 50,000 条电影评论&#xff0c;其中 25,000 条用于训练&#xff0c;25,000 条用于测试。每条评论被标记为正面或负面情感&#xff0c;因此该数据集是一个二分类问题。…

AD利用嘉立创的封装

1.首先&#xff0c;打开元件库&#xff0c;搜索元器件 2.点开它的封装&#xff08;符号&#xff09; 3.文件-->导出-->Altium Designer 4.然后在AD上面打开这个文件 5.将其复制&#xff0c;粘贴放到PCB库中 6.然后在原理图中的封装管理器中&#xff0c;添加封装&#xf…

ODrive引脚排列

对引脚配置的更改仅在odrv0.save_configuration()和odrv0.reboot()之后生效 如果 GPIO 设置为不支持的模式,它将保持未初始化状态。 当将 GPIO 设置为特殊用途模式(例如GpioMode.UART_A)时,您还必须启用相应的功能(例如<odrv>.config.enable_uart_a)。 数字模式是一…

如何创新玩转元服务开发-趋势、分类与我们实践的方向!

一、软件发展分类与元服务&#xff08;一&#xff09;软件分类发展简要分析 软件总体分为系统软件和应用软件两大类。用户、设备、操作系统系统软件、流量入口、应用形态应用软件关系及发展见下表—— 从表中分析得知&#xff0c;从互联网时期到移动互联网主导的发展&#xff…

前端开发两年半,我裸辞了

☀️ 前言 一晃两年半过去了&#xff0c;我离开了我的第一份前端开发工作&#xff0c;当你看到这篇文章&#xff0c;我已经离职两个月了&#xff0c;目前仍在艰难求职中&#xff0c;想记录分享一下我的经历&#xff0c;感兴趣的可以继续往下看&#xff0c;希望能给大家一些启示…

学Python能做哪些副业?我一般不告诉别人!建议存好

前两天一个朋友找到我吐槽&#xff0c;说工资一发交完房租水电&#xff0c;啥也不剩&#xff0c;搞不懂朋友圈里那些天天吃喝玩乐的同龄人钱都是哪来的&#xff1f;确实如此&#xff0c;刚毕业的大学生工资起薪都很低&#xff0c;在高消费、高租金的城市&#xff0c;别说存钱&a…

C++继承机制下析构和构造函数的执行分析

析构函数在下边3种情况时被调用&#xff1a; 对象生命周期结束&#xff0c;被销毁时&#xff1b;delete指向对象的指针时&#xff0c;或delete指向对象的基类类型指针&#xff0c;而其基类虚构函数是虚函数时&#xff1b;对象i是对象o的成员&#xff0c;o的析构函数被调用时&a…

SRM 供应商管理系统都有哪些模块?

3k字干货&#xff01; SRM必备6大模块&#xff1a;供应商管理、采购需求管理、采购寻源、采购履约、交付结算。下面针对环节中的核心场景进行讲解。 1、供应商全生命周期管理 过去&#xff0c;企业业务简单&#xff0c;对接供应商数量少&#xff0c;需求供给匹配、价格合适就…

如何使用KoodousFinder搜索和分析Android应用程序中的安全威胁

关于KoodousFinder KoodousFinder是一款功能强大的Android应用程序安全工具,在该工具的帮助下,广大研究人员可以轻松对目标Android应用程序执行安全研究和分析任务,并寻找出目标应用程序中潜在的安全威胁和安全漏洞。 账号和API密钥 在使用该工具之前,我们首选需要访问该…

适合初创企业租赁的办公模式-共享办公室

随着共享经济的兴起&#xff0c;共享办公室已经成为越来越多人的选择。共享办公室提供了一个灵活、高效、舒适的工作环境&#xff0c;能够帮助个人和团队提高工作效率和创造力。下面我将从三个角度来介绍共享办公室。 共享办公室的优势 首先&#xff0c;共享办公室具有成本效益…

合肥先进光源束测步进电机及驱动器的选择

大规模电机控制的方案选择-电机和驱动器篇 在上面文档的系统里选择的是免电池带绝对值编码器的步进伺服电机方案&#xff0c;现在有些场合只是普通的步进电机就好了&#xff0c;同样从电机控制的龙头企业鸣志的产品中选择&#xff0c;依然选择现成熟的ethercat总线技术的驱动器…

深度学习模型在图像识别中的应用:CIFAR-10数据集实践与准确率分析

文章目录 前言导入所需的库忽略证书验证下载并加载 CIFAR-10 数据集数据预处理构建深度学习模型编译模型模型训练模型评估进行图片识别测试图片运行效果完整代码完结 前言 深度学习模型在图像识别领域的应用越来越广泛。通过对图像数据进行学习和训练&#xff0c;这些模型可以自…

ChatGPT独家汇总:发现最优秀的人工智能对话体验

欢迎来到我们的 ChatGPT 镜像网站汇总博客&#xff01;在这个令人激动的人工智能时代&#xff0c;ChatGPT 作为一款顶尖的语言模型&#xff0c;已经引起了全球范围内的热议。但是&#xff0c;您是否曾经为了找到最佳的 ChatGPT 使用体验而苦苦搜寻&#xff1f;别担心&#xff0…

(15)第一人称视角视频

文章目录 前言 15.1 推荐的零件 15.2 连接图示 15.3 通过任务计划器最小化OSD设置 15.4 集成式OSD 15.5 用户视频/博客 15.6 与FPV飞行特别相关的安全警告 15.7 政府/地方法规 前言 第一人称视角在飞行时为你提供了真正的飞行员视角&#xff0c;它将视频摄像机和发射器…

梯度下降求函数极小值

梯度下降求函数极小值 Himmelblau 函数表达式 ∫ ( x , y ) ( x 2 y − 11 ) 2 ( x y 2 − 7 ) 2 \int(x,y)(x^{2}y-11)^{2} (xy^{2}-7)^{2} ∫(x,y)(x2y−11)2(xy2−7)2 Himmelblau.py 绘制 import numpy as np import matplotlib.pyplot as plt# from mpl_toolkits.mp…

Redis中的缓存雪崩、击穿、穿透的原因以及解决办法

缓存雪崩、击穿、穿透一旦发生&#xff0c;会导致大量的请求积压到数据库层。如果请求的并发量很大&#xff0c;就会导致数据库宕机或是故障&#xff0c;这就是很严重的生产事故了。 俗话说&#xff0c;知己知彼&#xff0c;百战不殆。了解了问题的成因&#xff0c;我们就能够…

PentestGPT:一款由ChatGPT驱动的强大渗透测试工具

关于PentestGPT PentestGPT是一款由ChatGPT驱动的强大渗透测试工具,该工具旨在实现渗透测试任务执行过程的自动化。该工具基于ChatGPT实现其功能,允许广大研究人员以交互式的方式使用,并指导渗透测试人员进行渗透测试任务的总体进度调控并执行指定操作。 除此之外,Pentes…

[学习笔记]python的web开发全家桶2(ing)

初识网站 默认编写的静态的效果动态&#xff1a;需要用到Web框架的功能。 对于目前的我们来看&#xff0c;什么可以做数据存储&#xff1a;txt文件excel文件专业的软件&#xff1a;数据库管理系统 MySQL/Oracle/SQLServer/DB2/Access... 7 MysQL 7.1 安装MySQL MySQL&…

#10042. 「一本通 2.1 练习 8」收集雪花

题目描述 不同的雪花往往有不同的形状。在北方的同学想将雪花收集起来&#xff0c;作为礼物送给在南方的同学们。一共有 n 个时刻&#xff0c;给出每个时刻下落雪花的形状&#xff0c;用不同的整数表示不同的形状。在收集的过程中&#xff0c;同学们不希望有重复的雪花。你可以…

Tomcat【redis-session共享】

目录 1、创建虚拟主机&#xff08;01-02&#xff09;相同【192.168.200.121、192.168.200.122】 2、LB配置文件【192.168.200.120】 3、tomcat节点配置TomcatClusterRedisSessionManager方式实现redis共享&#xff08;01-02&#xff09;相同 4、安装redis、配置【192.168.20…