stm32或gd32移植libcanard实现UAVCAN协议

这篇具有很好参考价值的文章主要介绍了stm32或gd32移植libcanard实现UAVCAN协议。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、源码下载

1、git下载

点击我下载文章来源地址https://www.toymoban.com/news/detail-698812.html

2、csdn下载

自己上传的点击下载

二、源码移植

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

1、首先将源码加入到工程

uavcan协议,stm32,嵌入式硬件,单片机

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

四、移植成功源码下载

点击我下载

到了这里,关于stm32或gd32移植libcanard实现UAVCAN协议的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • GD32F470 移植STM32F429工程 Keil调试笔记

    keil版本:5.25 安装 GigaDevice.GD32F4xx_DFP.3.0.4.pack Keil.STM32F4xx_DFP.2.15.0.pack 1、原项目为STM32F429 工程,切换到GD32F470 只需在 Options for Target\\\"“对话框的Device菜单中选中“GD32F470II”,重新编译即可,一般不会有编译错误。 2、将项目工程在切换回STM32F429,在 Options for Target”\\\"对话框的D

    2024年02月09日
    浏览(47)
  • GD32F450+LAN8720A,#FreeRTOS_Plus_TCP 网络协议栈移植教程

    (此博文在2023年10月写完后,在2023年11月有更新,更新内容包括博文内容与代码文件,代码效果不变,但更新后的逻辑更合理) FreeRTOS-Plus-TCP 是一种适用于 FreeRTOS 的可扩展的开源和线程安全 TCP/IP 堆栈。 FreeRTOS-Plus-TCP 提供了一个熟悉的 基于标准 Berkeley 套接字的接口, 简单易用

    2024年01月16日
    浏览(49)
  • POWERLINK协议源码(最新)在stm32单片机上的移植指南

    最近着了powerlink的道,连续几晚十二点前没睡过觉。不得不说兴趣这东西劲太大了,让人睡不着。喜欢上研究POWERLINK,最新版的源码结构挺清晰的,移植并测试了嵌入式linux作为从站和电脑主站之间的通信,挺有趣的。接下来想尝试下在单片机上的移植,降低POWERLINK协议的使

    2024年02月06日
    浏览(37)
  • 嵌入式 STM32 通讯协议--MODBUS

    目录 一、自定义通信协议 1、协议介绍 2、网络协议 3、自定义的通信协议  二、MODBUS通信协议 1、概述 2、MODBUS帧结构  协议描述 3、MODBUS数据模型   4、MODBUS事务处理的定义 5、MODBUS功能码  6、功能码定义   7、MODBUS数据链路层 8、MODBUS地址规则  9、MODBUS帧描述 10、MODBUS两种

    2024年02月11日
    浏览(62)
  • POWERLINK协议在stm32单片机+w5500移植成功经验分享

    连续折腾了多个晚上,又趁周末又花了一天时间,终于把powerlink协议移植成功到单片机上啦。本想放弃,但想了下不管我能不能用上,结个尾吧,分享给有需要的人。放弃并不难,但坚持一定很酷。为了移植测试这个协议花了不少血本。stm32开发板就买了两套,其中第一套板

    2024年02月09日
    浏览(57)
  • 基于STM32F407-LAN9252的EtherCAT从站协议移植过程

    EtherCAT移植过程 前言:刚拿到一个EtherCAT的开发需求,本来想安心当个CV战士的,结果在网上找了一圈,只有https://www.hexcode.cn/article/5e3ee9a835616641b2daef97 这篇写的相对详细,看来偷不了懒了,只有自己重新整理开发了。 1 、需求 基于STM32F407芯片、LAN9253芯片(自带PHY芯片),验

    2024年02月09日
    浏览(34)
  • 基于gd32f103移植freemodbus master 主栈

    1.移植freemodbus master需要先移植RT-Thread操作系统 GD32F103C8T6移植 RTT Nano 教程-CSDN博客 2.移植freemodbus master协议栈 在移植了RTT以后,我们需要移植就只有串口相关的函数 移植freemodbus master协议栈具体步骤 下载移植freemodbus master协议栈 源码 添加协议栈文件 向mdk添加头文件路径 修改

    2024年01月18日
    浏览(46)
  • 单片机(STM32,GD32,NXP等)中BootLoader的严谨实现详解

    Bootloader( 引导加载程序 )的主要任务是引导加载并运行应用程序,我们的软件升级逻辑也一般在BootLoader中实现。本文将详细介绍BootLoader在单片机中的实现,包括 STM32、GD32、NXP Kinetis 等等的所有单片机,因为无论是什么样的芯片,它实现的逻辑都是一样的。 注意,本篇文章主

    2024年02月02日
    浏览(56)
  • 《嵌入式 – GD32开发实战指南》第12章 ADC

    开发环境: MDK:Keil 5.30 开发板:GD32F207I-EVAL MCU:GD32F207IK GD32F2系列有 3 个逐次逼近型的ADC,精度为 12 位,有18个多路复用通道,可以转换来自16个外部通道和2个内部通道的模拟信号。其中ADC0 和 ADC1都有 16 个外部通道, ADC2 根据 CPU 引脚的不同通道数也不同,一般都有8 个外部

    2023年04月08日
    浏览(61)
  • RT-Thread 1. GD32移植RT-Thread Nano

    1. RT-Thread Nano 下载 RT-Thread Nano 是一个极简版的硬实时内核,它是由 C 语言开发,采用面向对象的编程思维,具有良好的代码风格,是一款可裁剪的、抢占式实时多任务的 RTOS。其内存资源占用极小,功能包括任务处理、软件定时器、信号量、邮箱和实时调度等相对完整的实

    2024年02月05日
    浏览(44)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包