SOEM源码解析——eeprom_read(读EEPROM)

news2025/1/12 8:52:19

0 工具准备

1.SOEM-master-1.4.0源码

1 eeprom_read函数总览

eeprom_read函数在SOEM源码的路径如下:test->linux->eepromtool->eepromtool.c,功能是读取指定字地址和长度的EEPROM数据:

/**
 * @brief 读取EEPROM数据
 * 
 * @param slave 从站序号
 * @param start 起始字地址
 * @param length 长度(以字为单位)
 * @return int 
 */
int eeprom_read(int slave, int start, int length)
{
    int j = 0;
    int i, ainc = 2;
    uint16 estat, aiadr;
    uint32 b4;
    uint64 b8;
    uint8 eepctl;

    if ((ec_slavecount >= slave) && (slave > 0) && ((start + length) <= MAXBUF))
    {
        aiadr = 1 - slave;
        eepctl = 2;
        /* force Eeprom from PDI */
        /* 强制PDI操作释放,复位0x0501.0为0,将EEPROM访问控制权分配给主站 */
        ec_APWR(aiadr, ECT_REG_EEPCFG, sizeof(eepctl), &eepctl, EC_TIMEOUTRET); 
        eepctl = 0;
        /* set Eeprom to master */
        /* 设置EEPRO访问权限分配给主站 */
        ec_APWR(aiadr, ECT_REG_EEPCFG, sizeof(eepctl), &eepctl, EC_TIMEOUTRET);

        estat = 0x0000;
        aiadr = 1 - slave;
        /* read eeprom status */
        /* 读取EEPROM状态 */
        ec_APRD(aiadr, ECT_REG_EEPSTAT, sizeof(estat), &estat, EC_TIMEOUTRET); 
        estat = etohs(estat);
        if (estat & EC_ESTAT_R64)
        {
            ainc = 4;
            for (i = start; i < (start + length); i += ainc)
            {
                b8 = ec_readeepromAP(aiadr, i, EC_TIMEOUTEEP);
                ebuf[j] = b8 & 0xFF;
                ebuf[j + 1] = (b8 >> 8) & 0xFF;
                ebuf[j + 2] = (b8 >> 16) & 0xFF;
                ebuf[j + 3] = (b8 >> 24) & 0xFF;
                ebuf[j + 4] = (b8 >> 32) & 0xFF;
                ebuf[j + 5] = (b8 >> 40) & 0xFF;
                ebuf[j + 6] = (b8 >> 48) & 0xFF;
                ebuf[j + 7] = (b8 >> 56) & 0xFF;
                j += 2 * ainc;
            }
        }
        else
        {
            for (i = start; i < (start + length); i += ainc)
            {
                b4 = ec_readeepromAP(aiadr, i, EC_TIMEOUTEEP) & 0xFFFFFFFF;
                ebuf[j] = b4 & 0xFF;
                ebuf[j + 1] = (b4 >> 8) & 0xFF;
                ebuf[j + 2] = (b4 >> 16) & 0xFF;
                ebuf[j + 3] = (b4 >> 24) & 0xFF;
                j += 2 * ainc;
            }
        }

        return 1;
    }

    return 0;
}

从以上代码可以看到,SOEM主站读从站EEPROM的操作可以分为3块:
(1)发送APWR报文夺取EEPROM访问控制权
(2)发送APRD报文获取EEPROM支持读字节数
(3)通过ec_readeepromAP函数读取从站EEPROM数据

1.1 夺取EEPROM访问控制权

夺取EEPROM访问控制权涉及的寄存器如下:
在这里插入图片描述
主站通过APWR报文设置0x0501.0=0,强制PDI操作释放,随后通过APWR报文设置0x0501=0x0将EEPROM访问控制权分配给主站。相关语句如下:

aiadr = 1 - slave;
        eepctl = 2;
        /* force Eeprom from PDI */
        /* 强制PDI操作释放,复位0x0501.0为0,将EEPROM访问控制权分配给主站 */
        ec_APWR(aiadr, ECT_REG_EEPCFG, sizeof(eepctl), &eepctl, EC_TIMEOUTRET); 
        eepctl = 0;
        /* set Eeprom to master */
        /* 设置EEPRO访问权限分配给主站 */
        ec_APWR(aiadr, ECT_REG_EEPCFG, sizeof(eepctl), &eepctl, EC_TIMEOUTRET);

1.2 获取EEPROM支持读字节数

获取EEPROM支持读字节数涉及的寄存器如下:
在这里插入图片描述
主站发送APRD报文去读取0x0502寄存器bit6,确定主站读取EEPROM一次返回的数据长度。相关语句如下:

/* read eeprom status */
        /* 读取EEPROM状态 */
        ec_APRD(aiadr, ECT_REG_EEPSTAT, sizeof(estat), &estat, EC_TIMEOUTRET); 
        estat = etohs(estat);

1.3 通过ec_readeepromAP函数读取从站EEPROM数据

ec_readeepromAP函数实际上调用的是ecx_readeepromAP函数:

/** Read EEPROM from slave bypassing cache. APRD method.:绕过本地EEPROM缓存,用APRD(主站使用顺序寻址从从站读取一定长度数据)方法
 * @param[in] context     = context struct 句柄
 * @param[in] aiadr       = auto increment address of slave 自动增量地址
 * @param[in] eeproma     = (WORD) Address in the EEPROM EEPROM地址(以字为单位)
 * @param[in] timeout     = Timeout in us.
 * @return EEPROM data 64bit or 32bit EEPROM数据 32bit或64bit
 */
uint64 ecx_readeepromAP(ecx_contextt *context, uint16 aiadr, uint16 eeproma, int timeout)
{
   uint16 estat;
   uint32 edat32;
   uint64 edat64;
   ec_eepromt ed;
   int wkc, cnt, nackcnt = 0;

   edat64 = 0;
   edat32 = 0;
   if (ecx_eeprom_waitnotbusyAP(context, aiadr, &estat, timeout))
   {
      /* error bits are set */
      /* 如果EEPROM有错误标志 */
      if (estat & EC_ESTAT_EMASK) 
      {
         /* clear error bits */
         /* 清空错误位 */
         estat = htoes(EC_ECMD_NOP); 
         wkc = ecx_APWR(context->port, aiadr, ECT_REG_EEPCTL, sizeof(estat), &estat, EC_TIMEOUTRET3);
      }

      do
      {
         /* 设置命令为读取命令、设置读取地址 */
         ed.comm = htoes(EC_ECMD_READ);
         ed.addr = htoes(eeproma);
         ed.d2   = 0x0000;
         cnt = 0;
         do
         {
            /* 发送EEPROM读命令 */
            wkc = ecx_APWR(context->port, aiadr, ECT_REG_EEPCTL, sizeof(ed), &ed, EC_TIMEOUTRET);
         }
         while ((wkc <= 0) && (cnt++ < EC_DEFAULTRETRIES));
         if (wkc)
         {
            osal_usleep(EC_LOCALDELAY);
            estat = 0x0000;
            if (ecx_eeprom_waitnotbusyAP(context, aiadr, &estat, timeout))
            {
               if (estat & EC_ESTAT_NACK)
               {
                  nackcnt++;
                  osal_usleep(EC_LOCALDELAY * 5);
               }
               else
               {
                  nackcnt = 0;
                  /* 根据支持的读字节数不同读取不同大小数据 8byte 或 4byte*/
                  if (estat & EC_ESTAT_R64)
                  {
                     cnt = 0;
                     do
                     {
                        wkc = ecx_APRD(context->port, aiadr, ECT_REG_EEPDAT, sizeof(edat64), &edat64, EC_TIMEOUTRET);
                     }
                     while ((wkc <= 0) && (cnt++ < EC_DEFAULTRETRIES));
                  }
                  else
                  {
                     cnt = 0;
                     do
                     {
                        wkc = ecx_APRD(context->port, aiadr, ECT_REG_EEPDAT, sizeof(edat32), &edat32, EC_TIMEOUTRET);
                     }
                     while ((wkc <= 0) && (cnt++ < EC_DEFAULTRETRIES));
                     edat64=(uint64)edat32;
                  }
               }
            }
         }
      }
      while ((nackcnt > 0) && (nackcnt < 3));
   }

   return edat64;
}

该函数涉及的寄存器如下:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述在这里插入图片描述
在这里插入图片描述

(1)等待EEPROM接口空闲
主站发送APRD报文检查0x0502寄存器的bit15,等待EEPROM接口空闲。相关语句如下:

if (ecx_eeprom_waitnotbusyAP(context, aiadr, &estat, timeout))
   {
      /* error bits are set */
      /* 如果EEPROM有错误标志 */
      if (estat & EC_ESTAT_EMASK) 
      {
         /* clear error bits */
         /* 清空错误位 */
         estat = htoes(EC_ECMD_NOP); 
         wkc = ecx_APWR(context->port, aiadr, ECT_REG_EEPCTL, sizeof(estat), &estat, EC_TIMEOUTRET3);
      }

当EERPOM接口空闲时会检查错误标志位,如果错误标志置位则设置0x0502寄存器为0,清空错误标志位。
(2)设置读命令及读取地址
主站通过APWR报文设置0x0502寄存器的bit8位为1表示读命令,设置0x0504-0x0507EEPROM地址寄存器为需要读取的EEPROM字地址。相关语句如下:

/* 设置命令为读取命令、设置读取地址 */
         ed.comm = htoes(EC_ECMD_READ);
         ed.addr = htoes(eeproma);
         ed.d2   = 0x0000;
         cnt = 0;
         do
         {
            /* 发送EEPROM读命令 */
            wkc = ecx_APWR(context->port, aiadr, ECT_REG_EEPCTL, sizeof(ed), &ed, EC_TIMEOUTRET);
         }

(3)等待EEPROM接口空闲
主站发送APRD报文检查0x0502寄存器的bit15,等待EEPROM接口空闲。相关语句如下:

if (ecx_eeprom_waitnotbusyAP(context, aiadr, &estat, timeout))

(4)读取EEPROM数据
主站发送APRD报文,读取0x0508寄存器开头,长度为EEPROM支持读取字节数的寄存器数据。支持读取字节数有32bit和64bit这2种。相关语句如下:

/* 根据支持的读字节数不同读取不同大小数据 8byte 或 4byte*/
                  if (estat & EC_ESTAT_R64)
                  {
                     cnt = 0;
                     do
                     {
                        wkc = ecx_APRD(context->port, aiadr, ECT_REG_EEPDAT, sizeof(edat64), &edat64, EC_TIMEOUTRET);
                     }
                     while ((wkc <= 0) && (cnt++ < EC_DEFAULTRETRIES));
                  }
                  else
                  {
                     cnt = 0;
                     do
                     {
                        wkc = ecx_APRD(context->port, aiadr, ECT_REG_EEPDAT, sizeof(edat32), &edat32, EC_TIMEOUTRET);
                     }
                     while ((wkc <= 0) && (cnt++ < EC_DEFAULTRETRIES));
                     edat64=(uint64)edat32;
                  }

至此,主站读取从站EEPROM的工作结束。

2 总结

eeprom_read函数读取从站EEPROM数据可以分为以下3个步骤:
(1)夺取EEPROM访问控制权
(2)获取EEPROM支持读字节数
(3)通过ec_readeepromAP函数读取从站EEPROM数据

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

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

相关文章

Vue3:一页多题答案校正及radio和checkbox混合使用

一页多题&#xff0c;类型包括单选&#xff0c;判断多选&#xff0c;涉及radio和checkbox同时使用&#xff0c;答案校正数据匹配&#xff0c;正确答案格式化&#xff0c;答案提交数据格式化&#xff0c;数据提交。 效果&#xff1a; 数据获取&#xff1a; 数据提交&#xff1a…

k8s中实现mysql主备

文章目录 一、k8s中实现mysql主备1.1 环境信息1.2 部署nfs-provisioner1.2.1 安装nfs1.2.2 部署nfs-provisioner 1.3 安装mysql1.4 备库上查看是否同步 一、k8s中实现mysql主备 1.1 环境信息 机器操作系统ipmysql版本k8s版本storageClassmaster1CentOS7.8192.168.0.20mysql5.…

Xbox漫游指南

以Xbox series s为例 开机启动 用手柄连接&#xff0c;注意两颗电池要方向相反插入&#xff0c;虽然里面2个插槽长一样&#xff1b; Xbox APP极其难用&#xff0c;放弃&#xff0c;直接用手柄连接 转区 只需要一个空U盘&#xff0c;大小不限制&#xff0c;格式化为NTPS格式…

【51单片机】DS1302时钟(学习笔记)

一、DS1302时钟 1、DS1302介绍 DS1302是由美国DALLAS公司推出的具有涓细电流充电能力的低功耗实时时钟芯片。它可以对年、月、日、周、时、分、秒进行计时&#xff0c;且具有闰年补偿等多种功能 RTC(Real Time Clock)&#xff1a;实时时钟&#xff0c;是一种集成电路&#xf…

基础课22——云服务(SaaS、Pass、laas、AIaas)

1.云服务概念和类型 云服务是一种基于互联网的计算模式&#xff0c;通过云计算技术将计算、存储、网络等资源以服务的形式提供给用户&#xff0c;用户可以通过网络按需使用这些资源&#xff0c;无需购买、安装和维护硬件设备。云服务具有灵活扩展、按需使用、随时随地访问等优…

Spring-IOC容器深度剖析详解

&#x1f388;个人公众号:&#x1f388; :✨✨✨ 可为编程✨ &#x1f35f;&#x1f35f; &#x1f511;个人信条:&#x1f511; 知足知不足 有为有不为 为与不为皆为可为&#x1f335; &#x1f349;本篇简介:&#x1f349; 本篇由表及里分析Spring-IOC容器始末&#xff0c;如…

LeetCode算法心得——找到冠军(反向推理)

大家好&#xff0c;我是晴天学长&#xff0c;今天的周赛第二题&#xff0c;需要的小伙伴可以关注支持一下哦&#xff01;后续会继续更新的。 1) .找到冠军 一场比赛中共有 n 支队伍&#xff0c;按从 0 到 n - 1 编号。每支队伍也是 有向无环图&#xff08;DAG&#xff09; 上的…

Technology Strategy Pattern 学习笔记2-Creating the Strategy-World Context

Creating the Strategy-World Context 1 PESTEL 1.1 从6个方案看外部 PoliticalEconomicSocialTechnologicalEnvironmentalLegal 1.2 参考URL https://zhuanlan.zhihu.com/p/192522082https://www.docin.com/p-449396129.htmlhttps://blog.csdn.net/xiaoyw71/article/deta…

发现一款PDF转换成翻页电子书的网站

​随着科技的发展&#xff0c;电子书越来越受到人们的喜爱。而PDF格式的文件也越来越多地被人们使用。那么&#xff0c;如何将PDF文件转换成翻页电子书呢&#xff1f;今天就为大家推荐一款好用的PDF转翻页电子书网站。 一、网站介绍 这款网站是一款非常实用的在线转换工具&…

7.spark sql编程

概述 spark 版本为 3.2.4&#xff0c;注意 RDD 转 DataFrame 的代码出现的问题及解决方案 本文目标如下&#xff1a; RDD ,Datasets,DataFrames 之间的区别入门 SparkSession创建 DataFramesDataFrame 操作编程方式运行 sql 查询创建 DatasetsDataFrames 与 RDDs 互相转换 使用…

Navicat连接mysql 8.0.35 2059错误解决办法

这2天在家重装电脑&#xff0c;顺便把mysql升级8.0&#xff0c;安装完成后&#xff0c;用Navicat连接&#xff0c;报错2059&#xff0c;如下 网上查了一下&#xff0c; 【报错原因】mysql8.0 之前的版本中加密规则是 mysql_native_password&#xff0c;而 mysql8.0 之后的版本…

超越 GLIP! | RegionSpot: 识别一切区域,多模态融合的开放世界物体识别新方法

本文的主题是多模态融合和图文理解&#xff0c;文中提出了一种名为RegionSpot的新颖区域识别架构&#xff0c;旨在解决计算机视觉中的一个关键问题&#xff1a;理解无约束图像中的各个区域或patch的语义。这在开放世界目标检测等领域是一个具有挑战性的任务。 关于这一块&…

Vim快速插入常用代码模板

1 修改home目录下.vimrc 家目录中ls -a找到隐藏文件.vimrc 2 编辑.vimrc 输入i编辑&#xff0c;在尾巴插入代码&#xff0c;按:wq保存并退出。 noremap io i#include <stdio.h><Esc>o<Esc> noremap im iint main(int argc, char *argv[])<Esc> map …

使用自定义函数拟合辨识HPPC工况下的电池数据(适用于一阶RC、二阶RC等电池模型)

该程序可以离线辨识HPPC工况下的电池数据&#xff0c;只需要批量导入不同SOC所对应的脉冲电流电压数据&#xff0c;就可以瞬间获得SOC为[100% 90% 80% 70% 60% 50% 40% 30% 20% 10% 0%]的所有电池参数,迅速得到参数辨识的结果并具有更高的精度&#xff0c;可以很大程度上降低参…

第7章-使用统计方法进行变量有效性测试-7.1-假设检验

目录 女士品茶 假设检验 样本与总体 原假设与备择假设 检验法、拒绝域与检验统计量 显著性水平 决策方法——临界值法和p值&#xff08;p-value&#xff09;法 假设检验步骤 参考文献 假设检验&#xff0c;我们从女士品茶这个故事开始说起。希望这篇文章能给您带来极大…

三、操作系统

&#xff08;一&#xff09;概述 操作系统是管理整个系统的软、硬件资源的系统&#xff0c;既是人和硬件之间的一种接口&#xff0c;也是应用软件与硬件之间的接口。 &#xff08;二&#xff09;进程管理 1.进程的状态 进程的状态是操作系统对进程进行管理的时候设置的几种状…

CLion2022安装

1. CLion下载 地址&#xff1a;https://www.jetbrains.com.cn/clion/download/other.html 下载你需要的版本&#xff0c;这里以2022.2.4为例 之后获取到对应的安装包 2. 安装 1、双击运行安装包&#xff0c;next 2、选择安装路径&#xff0c;建议非系统盘&#xff0c;nex…

Jetpack:029-Jetpack中的网格布局

文章目录 1. 概念介绍2. 使用方法3. 代码与效果3.1 示例代码3.2 运行效果 4. 内容总结 我们在上一章回中介绍了Jetpack中Card相关的内容&#xff0c;本章回中主要介 网格布局。闲话休提&#xff0c;让我们一起Talk Android Jetpack吧&#xff01; 1. 概念介绍 我们在本章回中…

第九章《搞懂算法:决策树是怎么回事》笔记

决策树算法是机器学习中很经典的一个算法&#xff0c;它既可以作为分类算法&#xff0c;也可以作为回归算法。 9.1 典型的决策树是什么样的 决策树算法是依据“分而治之”的思想&#xff0c;每次根据某属性的值对样本进行分类&#xff0c;然后传递给下个属性继续进行分类判断…

【CMU15445】Fall 2019, Project 3: Query Execution 实验记录

目录 实验准备实验测试Task 1: CREATING A CATALOG TABLE SQL 执行是由数据库解析器转化为一个由多个 executor 组成的 Query Plan 来完成的&#xff0c;本实验选择了火山模型来完成 query execution&#xff0c;这一次的 project 就是实现各种 exeutor&#xff0c;从而可以通过…