IIC Master 设计实现

写个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_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部分之后再说。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

群晖安装MariaDB

群晖安装MariaDB 在套件中心安装MariaDB给root开启远程访问权限使用工具连接数据库 在套件中心安装MariaDB 给root开启远程访问权限 # ssh 登陆群晖后执行下面操作 $ mysql -uroot -p[数据库密码] $ use mysql; $ select User,authentication_string,Host from user; # 查看账…

服务端性能测试——性能测试体系

目录&#xff1a; 1.性能测试介绍 性能测试介绍性能体系&#xff1a;性能测试与分析优化&#xff1a;行业流行性能压测工具介绍行业流行性能监控工具介绍行业流行性能剖析工具介绍性能测试流程与方法性能测试计划 计划&#xff1a;DAU&#xff0c;PV(perday)&#xff0c;订单量…

大模型LLM训练的数据集

引言 2021年以来&#xff0c;大预言模型的开发和生产使用呈现出爆炸式增长。除了李开复、王慧文、王小川等“退休”再创业的互联网老兵&#xff0c;在阿里巴巴、腾讯、快手等互联网大厂的中高层也大胆辞职&#xff0c;加入这波创业浪潮。 通用大模型初创企业MiniMax完成了新一…

使用 matlab 求解最小二乘问题

有约束线性最小二乘 其标准形式为&#xff1a; min ⁡ x 1 2 ∥ C x − d ∥ 2 2 \mathop {\min }\limits_x \quad \frac{1}{2}\left\| Cx-d \right\|_2^2 xmin​21​∥Cx−d∥22​ 约束条件为&#xff1a; A ⋅ x ≤ b A e q ⋅ x b e q l b ≤ x ≤ u b \begin{aligned} …

黑马苍穹外卖学习Day3

目录 公共字段自动填充问题分析实现思路代码实现 新增菜品需求分析和设计接口设计代码开发开发文件上传接口功能开发 菜品分页查询需求分析和设计代码开发 菜品删除功能需求分析与设计代码实现代码优化 修改菜品需求分析和设计代码实现 公共字段自动填充 问题分析 员工表和分…

洗地机哪种牌子好?智能洗地机排行

选择一款性能稳定、使用方便的洗地机&#xff0c;对于家庭清洁至关重要。近年来&#xff0c;随着懒人经济的兴起&#xff0c;智能家电不断涌现。特别是在家居清洁领域&#xff0c;人们追求更加轻松便捷的清洁体验。洗地机行业近年来迎来了快速增长&#xff0c;各大厂商竞相推出…

Java学习笔记(六)——基本数据类型及其对应的包装类

文章目录 包装类基本数据类型及其对应的包装类获取Integer对象的方式(了解)获取Integer对象两种方式的区别(掌握) 包装类的计算&#xff1a;自动装箱和自动拆箱Integer成员方法综合练习练习1练习2练习3练习4练习5 包装类 包装类&#xff1a;基本数据类型对应的引用数据类型。 …

基于ssm的常见小儿疾病中医护理系统的设计+jsp论文

摘 要 现代经济快节奏发展以及不断完善升级的信息化技术&#xff0c;让传统数据信息的管理升级为软件存储&#xff0c;归纳&#xff0c;集中处理数据信息的管理方式。本小儿疾病中医护理系统就是在这样的大环境下诞生&#xff0c;其可以帮助管理者在短时间内处理完毕庞大的数据…

一款完整的单节锂离子电池采用恒定电流/恒定电压线性充电器

一、基本概述 TX5806是一款完整的单节锂离子电池采用恒定电流/恒定电压线性充电器。芯片外部元件少&#xff0c;使芯片成为便携式应用的理想选择。芯片可以适合 USB 电源和适配器电源工作。由于采用了内部P-MOS架构&#xff0c;加上防倒充电路&#xff0c;所以不需要外部隔离二…

大创项目推荐 深度学习大数据物流平台 python

文章目录 0 前言1 课题背景2 物流大数据平台的架构与设计3 智能车货匹配推荐算法的实现**1\. 问题陈述****2\. 算法模型**3\. 模型构建总览 **4 司机标签体系的搭建及算法****1\. 冷启动**2\. LSTM多标签模型算法 5 货运价格预测6 总结7 部分核心代码8 最后 0 前言 &#x1f5…

基于SSM图书管理系统【源码】【最详细运行文档】

SSM图书管理系统【源码】【最详细运行文档】 系统简介系统涉及系统运行系统演示源码获取 系统简介 以往的图书馆管理事务处理主要使用的是传统的人工管理方式&#xff0c;这种管理方式存在着管理效率低、操作流程繁琐、保密性差等缺点&#xff0c;长期的人工管理模式会产生大量…

k8s的集群调度

k8s的集群调度: scheduler: 负责调度资源&#xff0c;把pod调度到node节点。 预算策略 优先策略 List-watch k8s集群当中,通过list-watch的机制进行每个组件的协作&#xff0c;保持数据同步,每个组件之间的解耦。 kubectl配置文件&#xff0c;向APIserver发送命令---apiserve…

解压方法之一 tar

文章目录 解压方法之一 tar语法压缩文件查看压缩文件的内容解压文件更多信息 解压方法之一 tar … note:: 十年磨一剑&#xff0c;霜刃未曾试。 贾岛《剑客 / 述剑》 Linux的tar命令可以用来压缩或者解压缩文件。 官方定义为&#xff1a; tar - an archiving utility 语法 …

7.27 SpringBoot项目实战 之 整合Swagger

文章目录 前言一、Maven依赖二、编写Swagger配置类三、编写接口配置3.1 控制器Controller 配置描述3.2 接口API 配置描述3.3 参数配置描述3.4 忽略API四、全局参数配置五、启用增强功能六、调试前言 在我们实现了那么多API以后,进入前后端联调阶段,需要给前端同学提供接口文…

花七天时间整理了3.5W字的全栈自动化测试面试题(答案+学习路线)!(适合各级软件测试人员)

在面试战场上&#xff0c;我们需要像忍者一样灵活&#xff0c;像侦探一样聪明&#xff0c;还要像无敌铁金刚一样坚定。只有掌握了这些技巧&#xff0c;我们才能在面试的舞台上闪耀光芒&#xff0c;成为那个令HR们心动的测试人 前言&#xff1a; 我相信大多测试开发的或多或少经…

微服务概述之单体架构

微服务概述 互联网始于 1969年美国的阿帕网&#xff08;ARPA&#xff09;&#xff0c;最开始的阿帕网只在美国军方使用。随着时间的推移&#xff0c;一些大学也开始加入建设&#xff0c;慢慢演化成了现在的因特网 &#xff08;Internet&#xff09;。随着计算机网络的普及&…

猫头虎分享已解决Bug || Error: ImagePullBackOff (K8s)

博主猫头虎的技术世界 &#x1f31f; 欢迎来到猫头虎的博客 — 探索技术的无限可能&#xff01; 专栏链接&#xff1a; &#x1f517; 精选专栏&#xff1a; 《面试题大全》 — 面试准备的宝典&#xff01;《IDEA开发秘籍》 — 提升你的IDEA技能&#xff01;《100天精通Golang》…

FPGA——时序分析与约束

FPGA时序分析与约束 FPGA结构基础数据传输模型Quartus II 时序报告Quartus II 中TimeQuest的操作实操 时序分析&#xff1a;通过分析FPGA内部各个存储器之间的数据和时钟传输路径&#xff0c;来分析数据延迟和时钟延迟的关系&#xff0c;保证所有寄存器都可以正确寄存数据。 数…

云卷云舒:【实战篇】云主机/虚拟机迁移

1. 简介 用户原有业务通过不同版本型号、不同操作系统的主机承载&#xff0c;形式上包括物理服务器、虚拟机、公有云主机等。随着业务不断扩张&#xff0c;需要将其业务云化转型&#xff0c;必须保证上云过程数据完整&#xff0c;业务平滑过度。 如果将所有业务系统都重新部署…

JS加密/解密之常见的JS代码加密

JS混淆加密是一种将JS代码转换为更难理解和阅读的格式的技术&#xff0c;目的是为了保护JS代码的版权和安全&#xff0c;防止被恶意修改或盗用。JS混淆加密通常包括以下几种方法&#xff1a; 变量重命名&#xff1a;将变量名替换为随机的字母或符号&#xff0c;使得代码的逻辑…