IIC Master 设计实现

news2024/9/20 22:56:40

写个IIC的主机来玩一玩。

  1. 仅100M时钟输入
  2. SCL波形工整,任意两个上升沿之间均为整数倍周期,占空比50%
  3. 发送数据时SDA严格对其到SCL低电平正中间
  4. 尽可能少的状态机
  5. 不浪费资源
  6. 数据逻辑和时序逻辑分离

接口设计中,我的思路是将数据与时序分离开,将和输入输出时序相关的逻辑放在最后面的模块中。使用stream接口实现数据交互。
找一个EEPROM来当从机:
仿真文件:
MicroChip 24XX04

先仿真一个IIC主机:

interface iic_master_sim (
    inout   sda,
    inout   scl
);

    task init;
    task start;
    task re_start;
    task stop;
    task write;
    task read;

endinterface

通过delay实现完美的主机,仿真:

在这里插入图片描述
观察波形,分割出有规律的部分:
在这里插入图片描述
在这一部分,可以看出1和0明显不一样。
在这里插入图片描述
显然可以得到:

DATA1:SDA 1111
       SCL 0110
DATA0: SDA 0000
       SCL 0110

那么,Start,ReStart,Stop:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

Start  : SDA 1100
         SCL 1110
Restart: SDA 1100
         SCL 0110
Stop   : SDA 0011
         SCL 0111

为了方便描述,将这些称呼为iic符号。每个IIC符号均为2*4bit

那么这里就是串化了,接下来借助GTX的思路来设计。

先用逻辑实现一个oserdes,ODDR模式。数据内部存储1小拍。
发送一些东西:
在这里插入图片描述
ACK在发送侧看来就是DATA1。由于整个系统都是100M来驱动的,所以这里的phy_sync其实是逻辑,并不是真正的时钟。
用phy_sync[1]产生ready,往前送,整一组数据:
在这里插入图片描述
效果不错,波形很完美。由于SCL的生成依赖于phy_sync,所以SCL一定是频率稳定的。

一个字节+ACK一共9个IIC符号,而其他控制位均为1个IIC符号。

需要一个编码模块,将符号与用户数据分离开:
iic_master_encode
再将字节的串化也做进去。

"Start"   encode => 11001110
"ReStart" encode => 11000110
"Stop"    encode => 00110111
"0xA0"    encode => "DATA1", "DATA0", "DATA1", "DATA0", "DATA0", "DATA0", "DATA0", "DATA0", "DATA1"
                 => 11110110 00000110 11110110 00000110 00000110 00000110 00000110 00000110 11110110

数据就用Stream接口传入,8bit数据放不下,用user来标识是数据还是控制符。

user
000 未定义
001 写数据,不带ACK,SDA让从机去拉SDA
010 读数据,数据自动改为0xFF,带ACK,连续读取
011 读数据,数据自动改为0xFF,不带ACK,结束连续读取
100 Start
101 ReStart
110 Stop
111 未定义

用ready信号做反压,控制,反压一个IIC符号的时间,数据,反压9个IIC符号时间:

写

读

这样就实现发送部分。接下来就是接收部分。

接收部分,iserdes->decode->data。

数据采样时钟比发送时钟晚了半周期:

在这里插入图片描述
这样就能完美的采样到数据。

decode无法分辨是发送的数据还是接收的数据,所以一律按读数据来算:

user
010 读数据,带ACK
011 读数据,不带ACK
100 Start
101 ReStart
110 Stop

仿真:
在这里插入图片描述

下面需要一个模块来产生phy_sync信号。这个也是非常简单,用计数器就可以生成了。

case (sync)
    {2'b00, 2'b00} : sync <= {2'b11, 2'b00};
    {2'b11, 2'b00} : sync <= {2'b11, 2'b11};
    {2'b11, 2'b11} : sync <= {2'b10, 2'b11};
    {2'b10, 2'b11} : sync <= {2'b10, 2'b10};
    {2'b10, 2'b10} : sync <= {2'b01, 2'b10};
    {2'b01, 2'b10} : sync <= {2'b01, 2'b01};
    {2'b01, 2'b01} : sync <= {2'b00, 2'b01};
    {2'b00, 2'b01} : sync <= {2'b00, 2'b00};
endcase
assign {tx_sync, rx_sync} = sync;

软件配置时,需要100K,那么就直接在寄存器中写入12500/100=(125)即可。

整个IIC最核心的部分已经完成了,没有状态机。


考虑软件层如何操作代码。
操作语句可以一次性都执行完,IIC的波形就是连续的。
也可以一条确认生效后再执行下一条,IIC的波形就是不连续的。
在这里插入图片描述

也把底层做到最简单:

iic_start();
iic_write(0b10100000);
iic_write(0x00);
iic_write(0x11);
iic_write(0x22);
iic_write(0x33);
iic_write(0x44);
iic_write(0x55);
iic_stop();

iic_start();
iic_write(0b10100000);
iic_write(0x01);
iic_restart();
iic_write(0b10100001);
rd_data[0] = iic_read(CONTINUE); // 0x22
rd_data[1] = iic_read(CONTINUE); // 0x33
rd_data[2] = iic_read(CONTINUE); // 0x44
rd_data[3] = iic_read(STOP);     // 0x55
iic_stop();

每条指令均会产生一个tx_stream数据,并接收到一个rx_stream数据。

addr   
0x00  w  0x01 总线发送Start
      r  0x00 总线未检测到Start
      r  0x01 总线已完成Start发送,读取清零
0x01  w  0x01 总线发送Restart
      r  0x00 总线未检测到Restart
      r  0x01 总线已完成Restart发送,读取清零
0x02  w  0x01 总线发送Stop
      r  0x00 总线未检测到Stop
      r  0x01 总线已完成Stop发送,读取清零
0x03  w  0x00 此时通过0x04发送数据的ACK被主机拉低
      w  0x01 此时通过0x04发送数据的ACK被主机释放
      r  0x00 此时通过0x06读出的数据ACK为低电平
      r  0x01 此时通过0x06读出的数据ACK为高电平
0x04  w  0x55 发送数据0x55
0x05  w  0x01 清空发送FIFO
      r  0x02 发送FIFO中有2个数据
0x06  r  0x55 读取数据为0x55
0x07  w  0x01 清空读取FIFO
      r  0x02 读取FIFO中有2个数据

写操作流程:

0x00 w 0x01       发送Start
0x03 w 0x01       接下来发送的数据释放ACK
0x04 w 0b10100000 发送器件地址
0x04 w 0x00       寄存器地址
0x04 w 0x11       写数据
0x04 w 0x22       写数据
0x04 w 0x33       写数据
0x04 w 0x44       写数据
0x04 w 0x55       写数据
0x02 w 0x01       发送Stop

0x02 r 0x00       还未检测到Stop
0x02 r 0x00       还未检测到Stop
0x02 r 0x00       还未检测到Stop
0x02 r 0x00       还未检测到Stop
0x02 r 0x01       检测到Stop

读操作流程:

0x00 w 0x01       发送Start
0x03 w 0x01       接下来发送的数据释放ACK
0x04 w 0b10100000 发送器件地址,写
0x04 w 0x01       寄存器地址
0x01 w 0x01       发送ReStart
0x04 w 0b10100001 发送器件地址,读
0x03 w 0x00       接下来发送的数据拉低ACK
0x04 w 0xFF       写数据
0x04 w 0xFF       写数据
0x04 w 0xFF       写数据
0x03 w 0x01       接下来发送的数据释放ACK
0x04 w 0xFF       写数据
0x02 w 0x01       发送Stop

0x02 r 0x00       还未检测到Stop
0x02 r 0x00       还未检测到Stop
0x02 r 0x00       还未检测到Stop
0x02 r 0x00       还未检测到Stop
0x02 r 0x01       检测到Stop

0x06 r 0x22       读取到数据0xA0
0x06 r 0x22       读取到数据0x01
0x06 r 0x22       读取到数据0xA1
0x06 r 0x22       读取到数据0x22
0x06 r 0x33       读取到数据0x33
0x06 r 0x44       读取到数据0x44
0x06 r 0x55       读取到数据0x55

看样子需要增加一些机制,能把接收到的数据做区分?

整理一下:

    ┌────────┐    ┌─────────┐    ┌────────┐
───►│ encode ├───►│ oserdes ├───►│        │
    └────────┘    └─────────┘    │ open   │
                                 │ drain  │◄───►
    ┌────────┐    ┌─────────┐    │ buffer │
◄───┤ decode │◄───┤ iserdes │◄───┤        │
    └────────┘    └─────────┘    └────────┘

最简单的方发,在oserdes上发送sda的辅助信号,经过iserdes让decode解码出是读还是写。

    ┌────────┐    ┌─────────┐     ┌────────┐
───►│ encode ├───►│ iserdes ├──┬─►│        │
    └────────┘    └─────────┘  │  │ open   │
                               ▼  │ drain  │◄───►
    ┌────────┐    ┌─────────┐  │  │ buffer │
◄───┤ decode │◄───┤ oserdes │◄─┴──┤        │
    └────────┘    └─────────┘     └────────┘

解码器的user这次多了状态:

3'b000   WriteData & ack
3'b001   WriteData & Nack
3'b010   ReadData & Ack
3'b011   ReadData & Nack
3'b100   Start
3'b101   Re-Start
3'b110   Stop

把rx的数据做一下解码:

  52665 ns, rx: Start
 142665 ns, rx: WriteData a0, with Ack 
 232665 ns, rx: WriteData 00, with Ack 
 322665 ns, rx: WriteData 11, with Ack 
 412665 ns, rx: WriteData 22, with Ack 
 502665 ns, rx: WriteData 33, with Ack 
 592665 ns, rx: WriteData 44, with Ack 
 682665 ns, rx: WriteData 55, with Ack 
 692665 ns, rx: Stop

5752665 ns, rx: Start
5842665 ns, rx: WriteData a0, with Ack 
5932665 ns, rx: WriteData 01, with Ack 
5942665 ns, rx: Re-Start
6032665 ns, rx: WriteData a1, with Ack 
6122665 ns, rx: ReadData  22, with Ack 
6212665 ns, rx: ReadData  33, with Ack 
6302665 ns, rx: ReadData  44, with Ack 
6392665 ns, rx: ReadData  55, Without Ack 
6402665 ns, rx: Stop

这样发出的数据和读回的数据就可以区别了。

寄存器说明

addr   
0x00  w  0x01 总线发送Start
      r  0x00 总线未检测到Start
      r  0x01 总线已完成Start发送,读取清零
0x01  w  0x01 总线发送Restart
      r  0x00 总线未检测到Restart
      r  0x01 总线已完成Restart发送,读取清零
0x02  w  0x01 总线发送Stop
      r  0x00 总线未检测到Stop
      r  0x01 总线已完成Stop发送,读取清零
0x03  w  0x00 此时通过0x04,0x06发送数据的ACK被主机拉低
      w  0x01 此时通过0x04,0x06发送数据的ACK被主机释放
      r  0x00 此时通过0x06读出的数据ACK为低电平
      r  0x01 此时通过0x06读出的数据ACK为高电平
0x04  w  0x55 发送数据0x55
0x05  w  0x01 清空发送FIFO
      r  0x02 发送FIFO中有2个数据
0x06  w  0x00 回读一个数据
      r  0xAA 读取数据为0xAA
0x07  w  0x01 清空读取FIFO
      r  0x02 读取FIFO中有2个数据

回读流程

0x00 w 0x01       发送Start
0x03 w 0x01       接下来发送的数据释放ACK
0x04 w 0b10100000 发送器件地址,写
0x04 w 0x01       寄存器地址
0x01 w 0x01       发送ReStart
0x04 w 0b10100001 发送器件地址,读
0x03 w 0x00       接下来发送或读取的数据拉低ACK
0x06 w 0x00       读数据
0x06 w 0x00       读数据
0x06 w 0x00       读数据
0x03 w 0x01       接下来发送或读取的数据释放ACK
0x06 w 0x00       读数据
0x02 w 0x01       发送Stop

0x02 r 0x00       还未检测到Stop
0x02 r 0x00       还未检测到Stop
0x02 r 0x00       还未检测到Stop
0x02 r 0x00       还未检测到Stop
0x02 r 0x01       检测到Stop

0x07 r 0x04       读取FIFO中由4个数据
0x06 r 0x22       读取到数据0x22
0x06 r 0x33       读取到数据0x33
0x06 r 0x44       读取到数据0x44
0x06 r 0x55       读取到数据0x55
0x03 r 0x01       0x55数据没有ACK

读取数据的过程中,读取0x03不是必须的。
保险起见,可以在回读前清空回读FIFO。
如果有超长的IIC操作,为了防止FIFO溢出,需要读取0x05,0x07寄存器已确认操作空间。
当然这个大小也取决于FIFO的大小。一般情况下操作数量不会超过256。
扫描主机的操作:

0x00 w 0x01       发送Start
0x03 w 0x01       接下来发送的数据释放ACK
0x04 w 0b00000000 发送器件地址,写
0x02 w 0x01       发送Stop
0x02 r 0x00       还未检测到Stop
0x02 r 0x01       检测到Stop
0x03 r 0x01       没有ACK

0x00 w 0x01       发送Start
0x03 w 0x01       接下来发送的数据释放ACK
0x04 w 0b00000010 发送器件地址,写
0x02 w 0x01       发送Stop
0x02 r 0x00       还未检测到Stop
0x02 r 0x01       检测到Stop
0x03 r 0x01       没有ACK

0x00 w 0x01       发送Start
0x03 w 0x01       接下来发送的数据释放ACK
0x04 w 0b00000100 发送器件地址,写
0x02 w 0x01       发送Stop
0x02 r 0x00       还未检测到Stop
0x02 r 0x01       检测到Stop
0x03 r 0x01       没有ACK

0x00 w 0x01       发送Start
0x03 w 0x01       接下来发送的数据释放ACK
0x04 w 0b00000110 发送器件地址,写
0x02 w 0x01       发送Stop
0x02 r 0x00       还未检测到Stop
0x02 r 0x01       检测到Stop
0x03 r 0x00       有ACK

具体总线与FIFO部分之后再说。

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

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

相关文章

数据结构-测试5

一、判断题 1.二叉树只能用二叉链表表示&#xff08;F&#xff09; 二叉树的存储结构有两种&#xff0c;顺序存储结构和链式存储结构 2. 装填因子是散列表的一个重要参数&#xff0c;它反映散列表的装满程度。(T) 装填因子越小&#xff0c;发生冲突的可能性越小 3. 在任何情况…

损失函数 - Focal Loss

b站账号 : Enzo_Mi 知识星球 : Enzo AI学习小组 | 小白分会 欢迎加入我的知识星球,一起来学习吧 ~ Focal Loss 1、Focal Loss 提出的背景2、正负样本数量不均衡问题 的解决 : baseline3、难分类样本/易分类样本 数量不均衡问题 的解决 : Focal Loss3、类别加权 Focal L…

1.7数算PPT选择汇总,PTA选择汇总,计算后缀表达式,中缀转后缀、前缀、快速排序

PTA选择汇总 在第一个位置后插入&#xff0c;注意是在后面插入&#xff0c;而不是前面&#xff1b;要移动49&#xff0c;为50-I&#xff0c;第25个的话&#xff0c;移25个 如果是插在前面&#xff0c;就移动50&#xff0c;N-I1&#xff0c;注意是插在前面还是后面 删第一个&a…

今日实践 — 附加数据库/重定向失败如何解决?

WMS数据库与重定向 前言正文如何建立数据库连接&#xff1f;第一步&#xff1a;打开SSMS&#xff0c;右击数据库&#xff0c;点击附加第二步&#xff1a;点击添加第三步&#xff1a;找到自己的数据库文件&#xff0c;点击确定按钮第四步&#xff1a;若有多个数据库&#xff0c;…

Hyperledger Fabric 管理链码 peer lifecycle chaincode 指令使用

链上代码&#xff08;Chaincode&#xff09;简称链码&#xff0c;包括系统链码和用户链码。系统链码&#xff08;System Chaincode&#xff09;指的是 Fabric Peer 中负责系统配置、查询、背书、验证等平台功能的代码逻辑&#xff0c;运行在 Peer 进程内&#xff0c;将在第 14 …

如何查找native服务的接口实现

以Netd为例&#xff1a; 首先adb看一下服务的接口&#xff1a; 接口文件是INetd&#xff0c;去源码找一下INetd.aidl 已经确定了接口API&#xff0c;对于native服务端的实现&#xff0c;一般的继承顺序为&#xff1a; 根据继承关系&#xff0c;对于BnXxx/XxxService 对象&…

IO类day02

JAVA IO java io可以让我们用标准的读写操作来完成对不同设备的读写数据工作. java将IO按照方向划分为输入与输出,参照点是我们写的程序. 输入:用来读取数据的,是从外界到程序的方向,用于获取数据. 输出:用来写出数据的,是从程序到外界的方向,用于发送数据. java将IO比喻为…

15个等轴视图设计的电动车汽车无人机等PR剪辑素材视频制作元素

包含15个等轴视图、等距视角电动车、汽车、无人机、沙漏、飞机等PR剪辑素材视频制作元素mogrt动画模板。 特征&#xff1a; 等距设计&#xff1b; 可以更改颜色&#xff1b; 分辨率&#xff1a;全高清&#xff08;19201080&#xff09;&#xff1b; 持续时间&#xff1a;15秒&a…

第四站:C/C++基础-指针

目录 为什么使用指针 函数的值传递&#xff0c;无法通过调用函数&#xff0c;来修改函数的实参 被调用函数需要提供更多的“返回值”给调用函数 减少值传递时带来的额外开销&#xff0c;提高代码执行效率 使用指针前: 使用指针后: 指针的定义: 指针的含义(进阶): 空指针…

【C++进阶04】STL中map、set、multimap、multiset的介绍及使用

一、关联式容器 vector/list/deque… 这些容器统称为序列式容器 因为其底层为线性序列的数据结构 里面存储的是元素本身 map/set… 这些容器统称为关联式容器 关联式容器也是用来存储数据的 与序列式容器不同的是 其里面存储的是<key, value>结构的键值对 在数据检索时…

ARCGIS PRO SDK 设置UI控件状态:启用/禁用

举例&#xff1a; 第一步&#xff1a;添加两个 Button 分别命名为Connect、Disconnect 第二步&#xff1a;nfig.daml添加状态和条件&#xff1a;在 DAML 中定义条件。请记住&#xff0c;条件存在于模块标记<modules>之外&#xff0c;下代码定义&#xff1a;Disconnected_…

【K8S 云原生】Kurbernets集群的调度策略

目录 一、Kubernetes的list-watch机制 1、List-watch 2、创建pod的过程&#xff1a; 二、scheduler调度的过程和策略&#xff1a; 1、简介 2、预算策略&#xff1a;predicate 3、优先策略&#xff1a; 3.1、leastrequestedpriority&#xff1a; 3.2、balanceresourceal…

计操进程同步(信号量pv灵魂三问法狂练版)

文章目录 解题秘诀-灵魂三问法一 同步问题1.1 围棋问题1.2 数据采集问题1.3 三进程文件打印问题1.4 司机售票员问题 二 同步互斥问题2.1 果盘问题 三 同步资源管控问题3.1 兔子问题3.2 数据写入和读取问题3.3 图书馆问题3.4 超市问题3.4.1 解法一3.4.2 解法二 解题秘诀-灵魂三问…

基于ODBC的数据库应用(MFC)

文章目录 1.预备知识1.数据库概述1.数据库和DBMS2.结构化查询语言SQL(Structured Query Language)3.数据库方式种类1.ODBC(Open DataBase Connectivity)开放数据库连接2.DAO(Data Access Objects)数据访问对象3.OLE DB(OLE数据库) 2.MFC ODBC1.CRecordset类构造记录集属性记录集…

c++学习:容器stack栈+queue+map(简易输入法)+deque

目录 stack 模板原型 头文件 模板的成员类型和成员对象和成员函数 栈类模板的容器对象 实例 queue 模板原型 头文件 模板的成员类型和成员对象和成员函数 队列类模板的容器对象 实例 map 模板原型 头文件 模板的成员类型和成员对象和成员函数 关联类模板的容器…

天锐绿盾|绿盾加密软件|电脑文件防泄密|文件加密|图纸加密软件|源代码加密|源代码防泄密系统|公司办公终端核心文件数据\资料防止外泄管理软件系统!

天锐绿盾是一款专业的数据加密和管理软件&#xff0c;旨在保护企业的重要数据不被泄露或损坏。该软件采用了先进的加密技术&#xff0c;确保数据在存储、传输和使用过程中的安全性。同时&#xff0c;天锐绿盾还提供了完善的管理功能&#xff0c;方便企业对加密数据进行统一管理…

分布式系统架构设计之分布式消息队列架构解析

分布式消息队列架构是构建在分布式系统之上的消息队列架构&#xff0c;旨在提高高性能、高可用性和可伸缩性。它包括以下架构相关部分&#xff1a; 1、架构优势 分布式消息队列架构的优势主要体现在以下几个方面&#xff1a; 01 高可用性 在分布式消息队列架构中&#xff0…

Unity组件开发--相机跟随角色和旋转

1.相机跟随组件&#xff0c;节点&#xff1a; 2.相机跟随组件脚本&#xff1a; using System; using System.Collections; using System.Collections.Generic; using Unity.Burst.Intrinsics; using UnityEngine; using UnityEngine.UI;public class CameraFollow : Singleton&…

【经验分享】如何看论文的分区、SCI检索号、EI检索号等信息

0 前言 一般而言&#xff0c;被SCI检索的论文&#xff0c;都会同时被EI检索。我们以论文《Learning Disentangled Representation for Multimodal Cross-Domain Sentiment Analysis》为例&#xff0c;讲解一下如何查询论文的各项信息。 我们首先百度这个论文 可以看到它是发表…

python 基础语法 异常 模块 包

异常捕获 try:f open("./abc.txt","r",encoding"UTF-8") except:print("")f open("./abc.txt","w",encoding"UTF-8")#指定异常 try:print(xxx_test) except NameError as e:print(f"error{e}&q…