自建极简Ethercat主站-底层驱动编写

news2024/12/28 19:18:25

1、简介

MECM(Mini Ethercat Master),名字随便起的。已经学习了一段时间的Ethercat总线了,目前的想法就是自己简单实现一个Ethercat主站,没有太多的冗余功能,暂时不考虑太多的容错机制,仅实现目前用到的FOE、COE、过程数据通信这三个功能,仅用于学习和加深理解。主站的硬件是GD32F450Z_EVAL开发板,板载的以太网芯片是DP83848VV。

2、底层编写

从根本来讲,Ethercat主站就是用来读写从站的。因此驱动的最核心功能有三个:网卡初始化、写入据到从站、读取从站数据。还有一个非必要功能,就是系统时钟,用来超时计数,也算是个简单的容错。当然,如果你不做超时也可以,选择发送和接收时一直死等也可以。

1、网卡初始化

关于GD32的以太网理解和初始化流程,请看文章Ethercat学习-GD32以太网学习 下面是部分代码,代码的初始化流程也是按照文章中的流程来的。

/* 初始化代码片段 */
int enet_system_init(void)
{
    ErrStatus reval_state = ERROR;
    /* 初始化以太网模块时钟 */
    enet_clock_init();
    /* 初始化以太网引脚 RMII接口*/
    enet_gpio_init();
    /* 初始化以太网MAC和DMA */
    reval_state = enet_mac_dma_config();
    return reval_state;
}
/* MAC 和 DMA 的初始化 */
static int enet_mac_dma_config(void)
{
    int i;
    ErrStatus reval_state = ERROR;
    /* 复位,并等待完成 */
    enet_deinit();
    reval_state = enet_software_reset();
    if(ERROR == reval_state) {
        return ERROR;
    }
    /* 以太网初始化 配置网卡自协商模式、使能接收端校验和检测功能、接收所有的广播帧*/
    reval_state = enet_init(ENET_AUTO_NEGOTIATION, ENET_AUTOCHECKSUM_DROP_FAILFRAMES, ENET_BROADCAST_FRAMES_PASS);
    if(ERROR == reval_state) {
        return ERROR;
    }
    /* 设置MAC地址 */
    enet_mac_address_set(ENET_MAC_ADDRESS0, mac);
    /* 初始化收发的描述符,常规描述符,链结构*/
    enet_descriptors_chain_init(ENET_DMA_TX);
    enet_descriptors_chain_init(ENET_DMA_RX); 
    /* 设置接收描述符 RDES1的31位为0,接收完成后会立马产生中断*/
    for(i=0; i<ENET_RXBUF_NUM; i++){ 
        enet_rx_desc_immediate_receive_complete_interrupt(&rxdesc_tab[i]);
    }
    /* 使能硬件IP包头和数据域的校验和计算和插入 */
    for(i=0; i < ENET_TXBUF_NUM; i++){
        enet_transmit_checksum_config(&txdesc_tab[i], ENET_CHECKSUM_TCPUDPICMP_FULL);
    }
//    /* 正常中断汇总使能 接收中断属于正常中断 */
//    enet_interrupt_enable(ENET_DMA_INT_NIE);
//    /* 使能接收中断 */
//    enet_interrupt_enable(ENET_DMA_INT_RIE);
    /* 使能以太网 */
    enet_enable();  
    return reval_state;
}

2、数据读写

数据的读写很简单,读数据采用的时轮询的方式,不是中断的方式。

/**
 * @******************************************************************************: 
 * @func: [enet_buf_send]
 * @description: 以太网数据发送
 * @note: 
 * @author: 
 * @param [uint8_t] *buf  待发送数据的地址
 * @param [uint32_t] length 待发送数据的长度
 * @return [*]
 * @==============================================================================: 
 */
int enet_buf_send(uint8_t *buf,uint32_t length)
{
    ErrStatus reval_state = ERROR; 
    reval_state = enet_frame_transmit(buf,length);
    return  reval_state;
}
/**
 * @******************************************************************************: 
 * @func: [enet_buf_send]
 * @description: 以太网数据接收
 * @note: 
 * @author: 
 * @param [uint8_t] *buf 接收数据的地址
 * @param [uint32_t] length 接收数据缓存的最大长度
 * @return [*]
 * @==============================================================================: 
 */
int enet_buf_recv(uint8_t *buf,uint32_t length)
{
    ErrStatus reval_state;
    if(enet_rxframe_size_get()){
        reval_state = enet_frame_receive(buf,length);
    }else{
        reval_state = 0;
    }    
    return reval_state;
}

3、系统时钟

系统时钟的思路就是用滴答定时器,每1us记一次数,这个数就时我们的系统时钟,这个数我们只用来做超时检测。

/**
 * @******************************************************************************: 
 * @func: [osal_current_time]
 * @description: 返回系统时钟计数,单位us
 * @note: 
 * @author: 
 * @return [*]
 * @==============================================================================: 
 */
uint64_t osal_current_time_us(void)
{
  return timercount;
}

3、数据打包

1、Ethercat报文简介

基本接口已经写好,接下来就是按照Ethercat的数据包对数据进行打包,然后将发送出去。

在这里插入图片描述

如图所示,一个标准的ethercat包括了6部分:

名称说明长度
Destination目的MAC地址,这个可以暂时忽略,在简单的连接中,这个是真的无关紧要6byte
Source本机MAC地址,这个可以暂时忽略,在简单的连接中,这个是也无关紧要6byte
EthercatType0x88A42byte
EthercatHeader有11bit的length、1bit的预留、4bit的type组成2byte
Datagrams数据包46~1500byte
FCS帧校验序列,这个不需要我们去填写,自动生成4byte

这里我们关注两个部分EthercatHeader和Datagrams,其他的要么是固定的,要么是自动生成的,只有这两个需要我们字节计算。另外可以看到图中所示,ethercat报文最大长度写的是1522,这是因为它算上了VLAN的4byte,我们用的Ethercat总长度是1518。ethercat报文的最小长度为64,而Datagrams的最小长度就是64-6-6-2-4 = 46。如果我们的Datagrams小于44个长度,MAC子系统会自动将剩余的补齐。如下图所示,图中的pad bytes就是系统补齐的。
在这里插入图片描述

EthercatHeader中有一个length,这个长度就是Datagrams的长度,但是它不包含补齐的长度,也不包含FCS的长度,例如我们自己的数据是10个字节,然后系统又给我们填充了34个自己凑够了64字节,那么这个length的值依然是10。EthercatHeader中的type只能为0x01

2、Datagrams报文简介

Datagrams可以只包含一个Datagram,也可以是好多个Datagram拼接起来的。如下图所示:

在这里插入图片描述

在一包Datagram中又分为了三部分,DatagramHeader、data、WKC。DatagramHeader固定长度为10,说明如下:

名称说明长度
CmdEthercat命令,用来表明该数据包的作用:LWR、BRD、FPRW、APRW等1byte
idx索引,我们字节生成的,不会被从机修改,可以判断数据是否重复或者丢失1byte
Address地址。除了逻辑地址是站四个字节外,其他地址都是一个从机地址(2byte)+内存地址(2byte)4byte
Len数据包中的数据长度,也就是data的长度11bit
R预留,01bit
C帧循环,由从机的处理单元自动置位,用来识别该帧是否已经循环了,例如从机下次又收到了相同的帧,但是帧循环这里已经置1了,说明该帧已经处理过一次了,由于某种原因在重复循环。1bit
M该数据包是否是最后一包,1表示后续还有数据包1bit
IRQ所有从站ethercat事件请求寄存器的OR,主机发送时写0就好了。值是在从站读取出来的。2byte

3、数据组包

int mecm_BRD_test()
{
    int i = 0;
    mecm_fram_t fram;
    mecm_datagram_header_t dg_header;
    /* 填充Destination、Source、EthercatType */
    for(i = 0;i<MAXBUF;i++)
    {
        memcpy( fram.en_header.dst,dstMAC,6);
        memcpy( fram.en_header.src,srcMAC,6);
        fram.en_header.etype = 0x88A4;
    }
    /* 填充Ethercat header */
    fram.ec_header.clehgth = 13;
    fram.ec_header.reserve = 0;
    fram.ec_header.ctype   = 0x01;
    /* 填充数据包的datagram header */
    dg_header.cmd     = CMD_BRD;
    dg_header.idx     = 1;
    dg_header.addr.np.adp = 0x0001;
    dg_header.addr.np.ado = 0x0508;
    /* 数据长度为 1 */
    dg_header.dlength = 1;  
    dg_header.irq     = 0x0000; 
    memcpy((uint8_t *)fram.datagram,(uint8_t *)&dg_header,10);
    /* 填充数据包 data */
    fram.datagram[10] = 0x01;
    /* 填充WCK 发送时置 0*/
    uint16_t wck = 0;
    memcpy((uint8_t *)&fram.datagram[11],(uint8_t *)&wck,2);
    mecm_data_send((uint8_t *)&fram,29);
}

上电测试,开发板于电脑网口连接,用wireshark抓包,结果如下:

在这里插入图片描述

可以看到,虽然数据发出来了,但是wireshark识别的并不时Ethercat包,0x88A4 变成了0xA488。这里要把上面的0x88A4改为0xA488,主要是因为本地是小端存储,而网络字节序中在前的被认为是高字节,改完之后可以看到,ethercat包被正确识别出来了,结果如下:

在这里插入图片描述

4、其他

关于网络字节序的理解。我们用的单片机时小端存储,低字节的放在低地址处,数据在传输的时候是由低到高。因此我们的0x88A4,首先传输的时0xA4。但是到了对方网卡那里,它是按照网络字节序来识别的,而网络字节序统一是高字节在前,因此网卡在识别协议的时候先到达的0xA4被认为是高字节,所以就被识别为了0xA488。因此我们在传输前可以将网络协议部分的报文转换为网络字节序,例如MAC地址,以太网类型等报文,这样网卡在识别网络协议的时候可以识别出来。至于数据部分,我们可以不需要管,因为主机和从机都是小端存储,不会出错。当然,如果接收方是大端的话,数据部分需要转换为大端再传输,或者让接收方自己收到数据后自己转换。

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

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

相关文章

chatgpt赋能python:Python奇偶求和:简单实用的算法

Python奇偶求和&#xff1a;简单实用的算法 Python作为一门高级编程语言&#xff0c;不仅适用于数据分析及科学计算领域&#xff0c;也可用于日常生活中的实用问题。例如&#xff0c;人们常常需要对一个整数序列中的奇数和偶数进行求和&#xff0c;以便了解各自的总数或者对它…

【软件测试】测试经验:IT 软件测试技术系统化学习方法

目录 一、IT 软件测试技术的介绍 &#xff08;1&#xff09;相关职称证书 ① 「中级」软件评测师 ②「高级」项目管理师 &#xff08;2&#xff09;背景 &#xff08;3&#xff09;作用 &#xff08;4&#xff09;行业应用 &#xff08;5&#xff09;技术概况 二、…

ES数据库介绍

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 目录 前言 一、ES(ElasticSearch)是什么&#xff1f; 二、ES的使用场景 三、ES的特点 四、ES和传统数据库对比 总结 前言 今天项目通过python用到了ES数据库…

ubuntu22.04安装docker

1、卸载旧版本&#xff08;如果有的话&#xff09; sudo apt-get remove docker docker-engine docker.io containerd runc 2、 添加 Docker 的官方 GPG 密钥 curl -fsSL https://mirrors.ustc.edu.cn/docker-ce/linux/ubuntu/gpg | sudo apt-key add - 3、设置稳定版仓库 …

python+vue健身房会员管理系统97s0t

健身管理系统根据需求分析&#xff0c;分为多个角色模块&#xff0c;分别为普通管理员和超级管理员模块&#xff0c;其中健身俱乐部普通管理员和健身俱乐部超级管理员都可以使用&#xff0c;不同角色登录显示不同的权限功能。本健身房管理系统管理员&#xff0c;会员&#xff0…

二叉树part7 | ● 530.二叉搜索树的最小绝对差 ● 501.二叉搜索树中的众数 ● 236. 二叉树的最近公共祖先

文章目录 530.二叉搜索树的最小绝对差思路代码困难 501.二叉搜索树中的众数思路官方题解代码困难 236. 二叉树的最近公共祖先思路代码困难 今日收获 530.二叉搜索树的最小绝对差 530.二叉搜索树的最小绝对差 思路 题目中要求在二叉搜索树上任意两节点的差的绝对值的最小值。…

51小车测速及OLED显示速度

1.小车测速 用途&#xff1a;广泛用于电机转速检测&#xff0c;脉冲计数,位置限位等。有遮挡&#xff0c;输出高电平&#xff1b;无遮挡&#xff0c;输出低电平接线 VCC 接电源正极3.3-5V GND 接电源负极 DO TTL开关信号输出 AO 此模块不起作用 测试原理和单位换算 轮子走一…

Springboot常见注解总结

给实体类使用该注解&#xff0c;结合后续的EXCEL工具类进行使用&#xff0c;能加快开发过程中关于文件导入的需求 文章目录 目录 前言 1. Excel注解 2. Annotation注解 3. Retention注解 4. Target注解 4. Document注解 5.Inherited注解 5.RestController注解 6.swagger注解(AP…

《10.21作业修正》

【一】 cookie概念解析&#xff1a;cookie是一种保存在客户端的小型文本文件&#xff0c;用于保存服务器通过set-cookie字段返回的数据&#xff0c;在下次请求服务器道德时候通过cookie字段将内容返回发送给服务器&#xff0c;是http进行客户端维护的一中方式&#xff0c;并且c…

Mycat中间件综合部署高可用-读写分离-分库分表(1.6)

Mycat中间件综合部署&#xff08;1.6&#xff09; 实施拓扑 一&#xff0c;环境搭建 1.服务分配 主机服务192.168.2.1MySQL-cluster-1192.168.2.2MySQL-cluster-2192.168.2.3Mycat 2.MySQL-cluster1分配 server-id端口角色13306master123307slave133308master243309slave2…

c语言中字符串比较的库函数是什么

说起比较运算&#xff0c;肯定第一时间想到了C语言中关于比较的相关运算符 “>、<、&#xff01;、>、<、”&#xff0c;那么要比较两个字符串是否相等是不是直接用“”比较就行了。下面就来看看这种方法行不行&#xff1f; 先看一个例子 void main( void ) {cha…

chatgpt赋能python:Python中如何使用局部变量

Python中如何使用局部变量 Python是一种高级编程语言&#xff0c;它是一种解释型语言&#xff0c;因此它的速度可能不如C 或Java等编译型语言快&#xff0c;但是Python的语法简洁&#xff0c;易于阅读和编写&#xff0c;并且具有强大的功能。 在Python中&#xff0c;变量是一…

SAP VK11税码的理解

背景:销售订单税率获取逻辑 1.工厂&#xff0c;工厂定义维护了国家代码信息 (SO–工厂–国家代码) 2.客户主数据通用数据维护了国家代码 (SO-客户–国家代码) 3.客户主数据销售数据维护了国家代码对应的税分类&#xff08;此国家代码由销售组织对应国家代码维护的国家代码带出来…

DAY08_JavaScript

目录 1 JavaScript简介2 JavaScript引入方式2.1 内联脚本2.2 内部脚本2.3 外部脚本 3 JavaScript基础语法3.1 书写语法3.2 输出语句3.3 变量3.4 数据类型3.5 运算符3.5.1 \和区别3.5.2 类型转换 3.6 流程控制语句3.6.1 if 语句3.6.2 switch 语句3.6.3 for 循环语句3.6.4 while …

c++ ——day1 homework

1.整理思维导图 2.有以下定义&#xff0c;哪些变量可以改变 const和*的位置&#xff0c;const在前&#xff0c;里面的值不能动*p&#xff0c;地址可以动p const在后&#xff0c;地址不能动&#xff0c;值可以动 const char *p&#xff1a;内容不可变&#xff0c;地址可变 co…

PCL点云处理之添加高斯噪点的两种方法(详细注释版)(一百八十一)

PCL点云处理之添加高斯噪点的两种方法(详细注释版)(一百八十一) 一、实验效果二、算法简介三、具体流程四、PCL自带函数实现1.代码2.结果五、Boost函数实现1.代码2.结果总结一、实验效果 通过实验测试,效果如上所示,算法可以正常运行 二、算法简介 高斯噪声是指它的概率…

实习内容总结

目录 项目BMS后台管理系统FantasyPayment通用支付框架 技术文档 实习时间&#xff1a;2022.12 &#xff5e; 2023.06 公司是初创公司&#xff0c;主要专精于AIGC方向&#xff0c;之前主要是To C的业务&#xff0c;做三消类游戏。今年刚刚转向To B业务&#xff0c;我去的时候Go…

Java性能权威指南-总结6

Java性能权威指南-总结6 垃圾收集入门垃圾收集概述GC算法选择GC算法 垃圾收集入门 垃圾收集概述 GC算法 JVM提供了以下四种不同的垃圾收集算法: Serial垃圾收集器 Serial垃圾收集器是四种垃圾收集器中最简单的一种。如果应用运行在Client型虚拟机(Windows平台上的32位JVM或…

【TaskMatrix.AI - Visual ChatGPT】连接超大模型和超多API来完成任务

Github项目地址&#xff1a;https://github.com/microsoft/TaskMatrix 目前大规模预训练模型&#xff08;比如ChatGPT&#xff09;已经能够完成多个任务&#xff0c;例如 提供强大的对话功能&#xff0c;in-context learning能力和代码生成能力生成高层次的解决问题框架 然而…

Vue.js 比较重要知识点总结四

概述 ref toRef toRefs进阶&#xff0c;深入理解vue3 setupVue3为何比Vue2快&#xff1f;怎样理解 Vue 的单向数据流&#xff1f;Vue 中事件绑定原理vue3 mitt 使用 ref toRef toRefs进阶&#xff0c;深入理解 为何需要ref&#xff1f; 返回值类型&#xff0c;会丢失响应式se…