CANopen开源库canfestival的移植

本文记录将CANopen开源库CANfestival移植到GD32F470单片机的过程。CANopen协议理解请参考博客:CANopen协议的理解-CSDN博客

CANfestival开源库下载链接

CSDN链接: https://download.csdn.net/download/heqiunong/89774627

官网链接:https://hg.beremiz.org/canfestival/file/de1fc3261f21

objdictedit字典工具下载链接

https://download.csdn.net/download/heqiunong/89774674

https://download.csdn.net/download/supcool/12492303

视频参考链接

1 canfestival移植_哔哩哔哩_bilibili

移植正文

上述各种链接都是些准备工作,主要有两个东西需要下载,一个是CANfestival库,一个是objdictedit对象字典工具。

1、CANfestival库文件预处理

下载好了CANfestval库后,里面有些文件是多余的,需要删除

1.1 src文件夹下的文件删除,src重命名为source

左边红色框住的部分删除掉

1.2 include文件夹下的文件删除,AVR文件夹重命为gd32

 

1.3 gd32文件夹(原AVR文件夹)下的文件删除

2、将CANfestival库放入工程文件当中

先把预处理的CANfestivel库拷贝到keil的工程文件夹当中,具体拷到哪里由自己定。

2.1 将所有源文件添加进入项目

 

2.2 添加include路径

3、处理编译的报错

3.1 注释config.h文件中的部分内容

3.2 缺函数的问题

完成3.1后,编译工程会缺函数

 

3.3 处理前两个缺函数的报错

start_and_seek_node 和 start_node这两个函数,是有的,只是因为inline关键字没有被识别,去掉inline就可以了。

全局搜索一下,找到位置。(这两个函数定义是在dcf.c文件中)

 

 删除这两个inline,现在就只缺下面3个函数了。

3.4 添加canSend函数以及CAN接收的处理

在添加canSend函数之前,我们需要对GD32F470的CAN进行外设层的配置,这部分配置完最好拿个USB-to-CAN的工具验证一下配置成功没有。 这部分工作可GD32F470提供的参考例程,本文的重点不在这里。

假设我们已经把CAN的外设部分都配置好了,下面来添加canSend函数。

#include "canfestival.h"// This function is called by CANopen library
uint8_t canSend(CAN_PORT notused, Message *message)
{uint8_t transmit_mailbox = 0;uint32_t timeout = 0xFFFF;transmitMessage.tx_dlen = message->len;memcpy(transmitMessage.tx_data, message->data, message->len);transmitMessage.tx_ff = CAN_FF_STANDARD;transmitMessage.tx_sfid = message->cob_id;// Check here if an accident occurstransmitMessage.tx_ft = (message->rtr == 0) ? CAN_FT_DATA : CAN_FT_REMOTE;// Transmit messagetransmit_mailbox = can_message_transmit(CAN0, &transmitMessage);// Waiting for transmit completedtimeout = 0xFFFF;while((CAN_TRANSMIT_OK != can_transmit_states(CAN0, transmit_mailbox)) && (0 != timeout)){timeout--;}return (timeout!=0) ? 0:1;
}

Message这个结构体是canfestival的库里面定义的,所以这里需要包含canfestival.h。我们需要做的是理解Message结构体里面的内容,然后把信息通过gd32的CAN对应的外设函数把Message发送出去。

★那么同样的道理,当CAN在接收外部发来的信息的时候,我们也要把接收到的信息,按照Message的格式,存到Message里面去。gd32的CAN接收是用中断来做的,下面给出代码参考一下。

void CAN0_RX0_IRQHandler(void)
{// For CANopen communication   Message Rx_Message;can_message_receive(CAN0, CAN_FIFO0, &receiveMessage);Rx_Message.cob_id = receiveMessage.rx_sfid;Rx_Message.rtr = (receiveMessage.rx_ft == CAN_FT_DATA) ? 0:1;  // be carefulRx_Message.len =receiveMessage.rx_dlen;memcpy(Rx_Message.data, receiveMessage.rx_data, receiveMessage.rx_dlen);// TODO we need objdictedit// canDispatch(&GD32Master_Data,&Rx_Message);}

 

注意函数最后一行的// canDispatch(&GD32Master_Data,&Rx_Message);是需要添加完对象字典后,需要解开注释的,

可以从上述函数分析,CAN接收中断函数把接收到的信息,存到了一个Message类型的结构体变量里面,最后调用canDispatch(&GD32Master_Data,&Rx_Message)函数,把接收到的信息和对象字典两个变量传进canfestival库进行处理。

那么搞完3.4这一步,就只剩下两个错误了。

 

3.5 添加getElapsedTime和setTimer函数

canfestival库的运行是需要一个定时器的,这个定时器需要由单片机给它提供,因此我们需要配置一个gd32的定时器给canfestival库。关于GD32的定时器配置内容,不是本文的重点,这里直接给出代码供参考。

static void CanOpenTimerConfig(void)
{timer_parameter_struct initPara;rcu_periph_clock_enable(RCU_TIMER2);    // Timer 0 1 3 4 7 has been used for other purposes // TIMER2_CLK = 240MHztimer_deinit(TIMER2);// CANopen requires a 10us timer, which is 100kHzinitPara.prescaler  = 240 - 1;               // 240MHz -> 100kHzinitPara.period     = TIMEVAL_MAX - 1;         initPara.alignedmode       = TIMER_COUNTER_EDGE;initPara.counterdirection  = TIMER_COUNTER_UP;initPara.clockdivision     = TIMER_CKDIV_DIV1;  initPara.repetitioncounter = 0;timer_init(TIMER2, &initPara);timer_auto_reload_shadow_enable(TIMER2);        // Auto-reload preload enabletimer_flag_clear(TIMER2, TIMER_FLAG_UP);timer_interrupt_enable(TIMER2, TIMER_INT_UP);   // Enable count up interruptnvic_irq_enable(TIMER2_IRQn, 1U, 1U);           // Enable and set timer interrupt prioritytimer_enable(TIMER2);
}// This function is called by CANopen library
void setTimer(TIMEVAL value)  
{  TIMER_CAR(TIMER2) =  value;  
}// This function is called by CANopen library
TIMEVAL getElapsedTime(void)  
{  return TIMER_CNT(TIMER2);  
}// TIMER2 is assigned as a CANopen timer 
void TIMER2_IRQHandler(void)  
{  if(SET == timer_interrupt_flag_get(TIMER2,TIMER_INT_UP)){  TimeDispatch();  timer_interrupt_flag_clear(TIMER2,TIMER_INT_UP);  }       
}

我们把定时器的计数频率配置成了1MHz。计数周期这里配置成TIMEVAL_MAX - 1;

注意:canfestival库里面默认的频率是125kHz,所以canfestival库里面timerscfg.h文件几个定义需要改改

 

注意:除了报错所要求我们添加的getElapsedTime和setTimer函数以外, 定时器的计数溢出中断里面,也调用了一个canfestival库里的函数TimeDispatch(); 而且canfestival库规定,计数中断周期是2ms。所以我们在配置定时器的时候才使用TIMEVAL_MAX-1来配置的。 如何理解#define TIMEVAL_MAX 2000的意思?定时器是1MHz,2000即表示2000*(1/1MHz)= 2000us = 2ms,这个细节需要去理解和注意的。

同理,比如我是100kHz(10us计数一次)定时器呢?那TIMEVAL_MAX这里就是 200了,200*(1/100kHz)= 2000us = 2ms。 一个计数值 = 10us, 下面这两个define应该这么改。

最需要注意的就是3.4和3.5,这里很容易出问题。  

4、添加对象字典

如果前面的操作都没有问题,那么这时候编译工程是不会报错的啦,但是这时候移植是没有完成的。 我们还需要添加单片机主节点的CANopen对象字典。 这时候就要用到前面我们提到的objdictedit字典啦。Objdictedit这个工具可以保存你的配置到一个xxxxx.od文件中,在开发的过程中,你可以每次只修改一部分。然后保存到.od文件中。下一次再改呢,又把这个.od文件再打开。 我们keil工程里面,需要的不是.od文件,而是利用.od文件生成的.c和.h文件。所以我们每次修改完.od文件保存之后,同时,我们还要利用objdictedit来生成一次.c和.h文件,把更新后的.c和.h文件替换keil工程里面原来的对象字典.c和.h文件。

下面我们以配置一个1ms的同步报文为例,来举例说明objdictedit的使用过程。

 

 

 把objdictedit生成的Master.c Master.h添加进入项目中。

 将Master.c代码最底部的对象字典变量名拷贝一下

 

下面把代码贴出了,方便copy 

// user_can.c...
#include "canfestival.h"extern CO_Data Master_Data;...
...
...void CAN0_RX0_IRQHandler(void)
{// For CANopen communication   Message Rx_Message;can_message_receive(CAN0, CAN_FIFO0, &receiveMessage);Rx_Message.cob_id = receiveMessage.rx_sfid;Rx_Message.rtr = (receiveMessage.rx_ft == CAN_FT_DATA) ? 0:1;  // be carefulRx_Message.len =receiveMessage.rx_dlen;memcpy(Rx_Message.data, receiveMessage.rx_data, receiveMessage.rx_dlen);// ★canDispatch(&Master_Data,&Rx_Message);}...

另外,在main主函数这边也需要做一个CANopen的基本的初始化操作。

// main.c...
#include "Master.h"
...void main()
{
...setNodeId(&Master_Data,0x00);setState(&Master_Data,Initialisation);setState(&Master_Data,Pre_operational);setState(&Master_Data,Operational);...
}

5、实验现象

温故而知新,写这篇文章的时候对3.4,3.5这部分内容理解又加深了。 CANfestival的移植相比于CANopen协议的理解还是要简单一些,后续应该会根据实际的项目,更新一些除了SYNC操作以外的其他操作,欢迎关注/阅订。

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

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

相关文章

智能BI项目第五期

本期主要内容 系统问题分析异步化业务流程分析线程池讲解(入门 原理 实战)系统异步化改造开发 1.系统问题分析 当系统面临大量用户请求时,我们后端的 AI 处理能力有限,例如服务器的内存、CPU、网络带宽等资源有限&#xff0c…

基于微信小程序的游泳馆管理系统--论文源码调试讲解

2 关键技术介绍 2.1 SSM框架 开发信息管理系统的主流框架是SSM(Spring Spring MVC MyBatis),SSM框架web层使用Spring MVC框架,使传输前后端数据变得简单;对于业务层使用Spring作为轻量级控制反转和面向切面的容器框…

redis分布式锁(看门枸机制)

分布式锁确保在同一时间只有一个节点能获得对共享资源的独占访问权限&#xff0c;从而解决并发访问问题。 Redisson锁(简称看门狗) 它可以实现锁的延长&#xff0c;确保某个线程执行完才能让其他线程进行抢锁操作 引入看门狗机制后 如何使用&#xff1f; 1、引入依赖包 <…

Java数据结构专栏介绍

专栏导读 在软件工程的世界里&#xff0c;数据结构是构建高效、可靠程序的基石。"Java数据结构"专栏致力于为Java开发者提供一个全面、深入的学习平台&#xff0c;帮助他们掌握各种数据结构的原理、实现及其在Java中的应用。通过这个专栏&#xff0c;读者将能够提升…

【第34章】Spring Cloud之SkyWalking分布式日志

文章目录 前言一、准备1. 引入依赖 二、日志配置1. 打印追踪ID2. gRPC 导出 三、完整日志配置四、日志展示1. 前端2. 后端 总结 前言 前面已经完成了请求的链路追踪&#xff0c;这里我们通过SkyWalking来处理分布式日志&#xff1b; 场景描述&#xff1a;我们有三个服务消费者…

Hive企业级调优[3]—— Explain 查看执行计划

Explain 查看执行计划 Explain 执行计划概述 EXPLAIN 命令呈现的执行计划由一系列 Stage 组成。这些 Stage 之间存在依赖关系&#xff0c;每一个 Stage 可能对应一个 MapReduce Job 或者一个文件系统的操作等。如果某 Stage 对应了一个 MapReduce Job&#xff0c;则该 Job 在 …

OpenHarmony(鸿蒙南向开发)——小型系统内核(LiteOS-A)【内核通信机制】下

往期知识点记录&#xff1a; 鸿蒙&#xff08;HarmonyOS&#xff09;应用层开发&#xff08;北向&#xff09;知识点汇总 鸿蒙&#xff08;OpenHarmony&#xff09;南向开发保姆级知识点汇总~ 子系统开发内核 轻量系统内核&#xff08;LiteOS-M&#xff09; 轻量系统内核&#…

微信支付开发-后台统计工厂实现

一、数据库设计图 二、后端统计工厂逻辑 1、统计父抽象类 a、StatisticsHandle.php 2、统计工厂通道类 a、StatisticsFactory.php 3、查询实现类 a、答题统计(Answer.php) 三、后端统计工厂代码实现 1、统计父抽象类(StatisticsHandle.php) <?php /*** 统计父抽象类* Use…

VirtualBox 7.1.0 发布下载 - 开源跨平台虚拟化软件

VirtualBox 7.1.0 (macOS, Linux, Windows) - 开源跨平台虚拟化软件 Oracle VM VirtualBox 7 请访问原文链接&#xff1a;https://sysin.org/blog/virtualbox-7/&#xff0c;查看最新版。原创作品&#xff0c;转载请保留出处。 作者主页&#xff1a;sysin.org 2024 年 9 月 …

Redis面试真题总结(三)

文章收录在网站&#xff1a;http://hardyfish.top/ 文章收录在网站&#xff1a;http://hardyfish.top/ 文章收录在网站&#xff1a;http://hardyfish.top/ 文章收录在网站&#xff1a;http://hardyfish.top/ 什么是缓存雪崩&#xff1f;该如何解决&#xff1f; 缓存雪崩是指…

算法课习题汇总(2)

整数划分问题 将正整数n表示成一系列正整数之和&#xff0c;nn1n2…nk(n1>n2>…>nk,k>1)。正整数n的这种表示称为正整数n的划分。 思路&#xff1a; n表示待划分数&#xff0c;m表示最大减数。 #include<iostream> using namespace std;int q(int n, int…

面试题给图例举测试用例或测试点

目录 从功能测试的角度考虑&#xff1a; 从性能角度考虑&#xff1a; 从兼容性的角度考虑&#xff1a; 从自动化角度考虑&#xff1a; 从安全性角度考虑&#xff1a; 用户体验的角度测试&#xff1a; 面试通常会有技术和人事两种&#xff0c;侧重点不一样。 今天聊一下测…

Qt日志输出及QsLog日志库

目录 Qt日志输出及QsLog日志库日志输出格式化日志普通格式化条件格式化环境变量设置格式化日志输出位置日志输出对象信息禁用输出 QsLog日志库使用方法1. 将QsLog目录添加到项目中2. 配置CMakeLists.txt文件3. 配置.pro文件4. 日志记录器的配置5. 运行程序6. 启用行号和文件名C…

有奖直播 | onsemi IPM 助力汽车电气革命及电子化时代冷热管理

在全球汽车行业向电气化和智能化转型的浪潮中&#xff0c;功率管理技术的创新和应用成为了关键驱动力。作为全球领先的半导体解决方案供应商&#xff0c;onsemi&#xff08;安森美&#xff09;致力于通过其先进的智能功率模块&#xff08;IPM&#xff09;技术&#xff0c;推动汽…

[Linux#55][网络协议] 序列化与反序列化 | TcpCalculate为例

目录 1. 理解协议 1.1 结构化数据的传输 序列化与反序列化 代码感知&#xff1a; Request 类 1. 构造函数 2. 序列化函数&#xff1a;Serialize() 3. 反序列化函数&#xff1a;DeSerialize() 补充 4. 成员变量 Response 类 1. 构造函数 2. 序列化函数&#xff1a;…

JavaWeb - 5 - 前端工程化

一.前后端分离开发 前后端混合开发 缺点&#xff1a;沟通成本高&#xff0c;分工不明确&#xff0c;不便管理&#xff0c;不便维护拓展 前后端分离开发 当前最为主流的开发模式&#xff1a;前后端分离 前后端分离开发中很重要的是API接口文档&#xff08;如&#xff1a;YApi&…

胤娲科技:谷歌DeepMind祭出蛋白质设计新AI——癌症治疗迎来曙光

在科技的浩瀚星空中&#xff0c;DeepMind的“阿尔法”家族总是能带来令人瞩目的璀璨光芒。这一次&#xff0c;它们再次以惊人的姿态&#xff0c; 将AI的触角深入到了生命的微观世界——蛋白质设计领域&#xff0c;为我们描绘了一幅未来医疗的宏伟蓝图。 想象一下&#xff0c;一…

Scrapy爬虫实战——某瓣250

# 按照我个人的习惯&#xff0c;在一些需要较多的包作为基础支撑的项目里&#xff0c;习惯使用虚拟环境&#xff0c;因为这样能极大程度的减少出现依赖冲突的问题。依赖冲突就比如A、B、C三个库&#xff0c;A和B同时依赖于C&#xff0c;但是A需要的C库版本大于N&#xff0c;而B…

VUE3配置路由(超级详细)

第一步创建vue3的项目

多版本node管理工具nvm

什么是nvm&#xff1f; 在项目开发过程中&#xff0c;使用到vue框架技术&#xff0c;需要安装node下载项目依赖&#xff0c;但经常会遇到node版本不匹配而导致无法正常下载&#xff0c;重新安装node却又很麻烦。为解决以上问题&#xff0c;nvm&#xff1a;一款node的版本管理工…