使用 GD32F470ZGT6,手写 I2C 的实现

我的代码:https://gitee.com/a1422749310/gd32_-official_-code
I2C 具体代码位置:https://gitee.com/a1422749310/gd32_-official_-code/blob/master/Hardware/i2c/i2c.c
黑马 - I2C原理
官方 - IIC 协议介绍

个人学习过程中的理解,有错误,欢迎指出

移植

【I2C 具体代码位置】中,要更改代码的地方

#define SCL(bit) gpio_bit_write(GPIOB, GPIO_PIN_6, bit == 1 ? SET : RESET);
#define SDA(bit) gpio_bit_write(GPIOB, GPIO_PIN_7, bit == 1 ? SET : RESET);
#define DELAY()  delay_1us(5);

#define SDA_IN() gpio_mode_set(GPIOB, GPIO_MODE_INPUT, GPIO_PUPD_NONE, GPIO_PIN_7);
#define SDA_OUT() gpio_mode_set(GPIOB, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN_7);
#define SDA_STATE() gpio_input_bit_get(GPIOB, GPIO_PIN_7)

以及 
void I2C_Init()

其他地方,都是和平台无关的

写数据

SCL、SDA 数据的四种形式

在这里插入图片描述

在这里插入图片描述

SCL 和 SDA 的高低电平的持续时间是多少?

回答这个问题,需要涉及:I2C总线的时钟频率
I2C总线的时钟频率,通常在100kHz到400kHz之间,其中

  • 100kHz是标准模式(Standard Mode)
    • 1秒钟,就是 1 000 000us,100 000 个bit,相除,可得:10us 1bit
    • 👆 10us,才得到一个 bit,那么,睡眠的时候,就是 5us(一半,用于开始、停止的变化)
  • 400kHz是快速模式(Fast Mode)
    • 1秒钟,就是 1 000 000us,400 000 个bit,相除,可得:2.5us 1bit
      此外,I2C总线还支持更高速度
  • 高速模式(High Speed Mode):1MHz
  • 超高速模式(Ultra-Fast Mode):5MHz

因此,SCL 和 SDA 的高低电平的持续时间是多少?要看具体使用的 I2C 的外设的 datasheet
比如,如果某个 datasheet 上面写了 100kHz,那么,就是 10us 1bit,就 #define DELAY() delay_1us(5);

开始、结束、发送、等待响应总结

  • 开始(Start):正弦函数
  • 结束(Stop):左边有 1 竖(都是低电平)的【凹函数】
  • 发送(Send):SDA准备 🏃‍,持续3个delay;SCL 从暴富到破产(或者理解为跳水,但是,跳水,SDA 用了。 所以,可以这么理解:SCL,老板,指挥官,与财富相关,SDA,员工,行动派,与行为相关)
  • 等待响应(Wait ACK):SCL、SDA 亲亲😘;SCL 从暴富到破产,SDA 控制权转移
    👆 很奇怪,之前用【亲亲】,感觉不好意思🥵,于是改成【靠近】,后来发现,还是亲亲好,上头😓
    👇 可以看到,只有【开始】(绿点)和【结束】(橙点),有比较长的 SCL(上面的信号是 SCL)(也印证了SCL 连续高两次)
    在这里插入图片描述

懒得复制图片了,直接看这个链接吧 黑马 - IIC

1、开始 Start:【正弦函数】

[图片]

  • 1:SCL 之前,SDA,一定是高的,不然没法下落
  • 2:SCL,暴富,站起来了(起作用了)
  • 3:将 SDA 拉低
  • 4:SCL,破产,没落了,代表之后不起作用了(虎落平阳被犬欺)

买的淘宝链接(👈 LIXIN 给的链接,买贵了。。。)19.5 USB逻辑分析仪
黑马 - 逻辑分析仪
下载:逻辑分析仪Logic-2.4.9-windows-x64.exe

[图片]
[图片]

I2C 要进行配置 👇
[图片]
[图片]
[图片]

最终 👇,注意采样率,不要太低,我之前设置为 100kS/s,采样的波形,是错误的(当时没理解概念,将 100kS/s 和之前的 100kHz是标准模式 那个联系在一起了🤡。。。)
[图片]

2、结束 Stop:左边有 1 竖的【凹函数】

[图片]
[图片]
👆,这里,开始的 4 和结束的 1,共了

  • 1:SCL、SDA 同时变低,方便 SCL 站起来

  • 2:SCL,暴富,站起来了(起作用了)

  • 3:SDA,上岸(游泳🏊‍结束)
    static void stop() {
    SCL(0);
    SDA(0);
    DELAY(); // <— 视频中没有加 delay

    SCL(1);
    DELAY();

    SDA(1);
    DELAY();
    }

2.1、结束代码,有问题

👆 视频中代码有问题,没有加 delay
[图片]
[图片]
测试代码如下 👇,对应波形 👆
可以看到,stop 中,SCL(0)、SDA(0) 后,立马 SCL(1),在波形上,显示的是,是很窄的一个波形(👆 右图中,绿色,1、1 的框框)
另外,注意上面图片中,中间的区域,我将其设置为 11、00、11,目的是为了对比(之后 2.2 中,有关于对这个波形的讨论)
static void stop()
{
SDA_OUT(); // SDA 重新获得控制权,SDA 变成输出

SCL(0);
SDA(0);
// DELAY(); 未加 delay

SCL(1);
DELAY();

SDA(1);
DELAY();

}

void test_wave()
{
start();

// 中间
SCL(1);    SDA(1);    DELAY();

SCL(0);    SDA(0);    DELAY();

SCL(1);    SDA(1);    DELAY();

stop();

}
加上 delay 之后的波形 👇
[图片]
[图片]
所以我认为,虽然效果是一样,但是,还是加下好些

2.2、结束代码,有问题例子中,中间波形分析

// 中间
SCL(1); SDA(1); DELAY();

SCL(0); SDA(0); DELAY();

SCL(1); SDA(1); DELAY();
[图片]
[图片]
👆,并不是我之前认为的,SCL(1) 、SDA(1),波形就同时变高,而是有一个偏移(SDA,向右偏一点)
并且,SCL(1)、SDA(1),宽度也是不一样的

那么,如果我先设置 SDA 呢?
// 中间
SDA(1); SCL(1); DELAY();

SDA(0); SCL(0); DELAY();

SDA(1); SCL(1); DELAY();
[图片]

[图片]
👆 右图,因为 SDA 提前设置,所以,在 SCL 之前,变低(图中显示,红色在黄色,内部)

但是,之后,第 3 个 SDA(1); SCL(1);,预想中,应该是 SDA 先拉高,SCL 在拉高(可实际上,是 SCL 先拉高)
一种解释,是程序编译的时候,SDA(1); SCL(1); 交换了位置???那为什么 SDA(0); SCL(0); 没有交换?
所以,应该不是这个原因
另一种解释:SDA(1),SDA 拉高的时候,相比于 SCL(1) ,比较耗时,我认为这个是比较合理一点的解释

问下 ChatGPT
你的分析非常到位!让我们一起深入探讨你提出的两个可能的解释:

  1. 程序编译时的代码顺序
    这种解释是比较不太可能的原因。在编译阶段,SDA(1) 和 SCL(1) 的顺序应该是严格按照程序中写的顺序执行的,编译器并不会自动交换这些信号的顺序,除非有特别的宏或硬件抽象层对这些函数做了特殊的处理。所以,SDA(1) 和 SCL(1) 顺序交换的问题,可以排除。
  2. SDA 拉高的耗时问题
    这个解释是比较合理的。
  • SDA 拉高时的时间问题:由于 I2C 总线的物理特性,SDA 线上的信号变化相对较慢,尤其是在高电平拉升时。如果你直接将 SDA 设置为高电平,可能需要一点时间来使 SDA 线达到稳定的高电平状态。而 SCL 线在此时是由外部的时钟拉升的,这意味着如果没有延时,SCL 会很快拉高,但 SDA 可能还没有达到稳定的高电平,导致了信号的不稳定或者错误。
  • SCL 的变化时间较短:SCL 线的状态变化通常由外部时钟控制,所以其变化速度通常比较快。而 SDA 由于是由主设备控制,并且存在内部拉升电阻或者其他电路因素,所以 SDA 的拉高通常会稍微慢一些,尤其是在低电平拉升到高电平时,可能会有延迟。
    为什么 SDA(0); SCL(0); 没有问题?
    当你将 SDA 和 SCL 都拉低时,情况就简单了。因为 I2C 的标准定义在 SDA 和 SCL 都为低电平时,它们处于稳定状态,几乎没有信号的延迟或时序问题。所以 SDA(0); SCL(0); 不会引起问题。
    总结:
    更可能的原因是 SDA 拉高时的时间延迟。 由于 SDA 拉高的过程相较于 SCL 更加耗时,可能会导致在 SDA(1) 和 SCL(1) 同时设置时,SCL 先拉高了,而 SDA 还没有完全稳定。
    3、发送 Send:SDA准备,持续3个delay;SCL 从暴富到破产
    [图片]
    [图片]
  • 1:SDA 准备了
  • 2:SCL,暴富,站起来了(起作用了)
  • 3:SCL,破产,没落了,代表之后不起作用了(虎落平阳被犬欺)
    👆,可以看到,起作用的传输数据,持续了 3 的 delay(准备前、SCL 变高时、SCL 变低时)

4、等待响应 Wait ACK:SCL、SDA 亲亲😘;SCL 从暴富到破产,SDA 控制权转移
[图片]
[图片]

  • 1:SCL、SDA 亲亲😘:SDA 变高,期望跳水;;;SCL 变低,破产,期望暴富,起作用,站起来
  • 2:SCL 高电平,站起来了(起作用了),此时,SDA,已经让出控制权了,然后,观察 SDA
    • SDA:低电平,代表应答
    • SDA:高电平,代表不应答
    • SDA,代表了,主动权
  • 3:SCL,没落了,代表之后不起作用了(虎落平阳被犬欺)

模拟下成功,然后看看波形
模拟的时候,不要开 SDA_IN,会影响波形
static void wait_ack()
{
SDA(1);
SCL(0);
DELAY();

#if MOCK_WAIT_ACK_SUCCESS == 0
// SDA 交出控制权,SDA 变为输入
SDA_IN();
#endif

SCL(1);

#if MOCK_WAIT_ACK_SUCCESS
// 模拟从机,将 SDA 置 1,代表成功响应主机
SDA(0);
#endif
DELAY();

if (RESET == SDA_STATE() || 1 == MOCK_WAIT_ACK_SUCCESS)
{
   // 成功
   SCL(0);
   SDA_OUT();  // SDA 重新获得控制权,SDA 变成输出
}
else
{
   stop();
}

}
最终波形如下 👇
[图片]

[图片]


read
rece 和 send 通讯信号都是一样的,但是区别:SDA 的信号来源,来自:从设备
[图片]

发送响应:SCL、SDA 开始:ACK 不亲亲🫸,NACK 亲亲😘;SCL 从暴富到破产;最后都亲亲😘
[图片]
[图片]

最后,I2C_Read(0x01, 0x02, &data, 1);(后面 len 改为 2,可以分析 ACK) 输出波形如下
[图片]

[图片]
[图片]
[图片]
可以看到,接收,只有 1、2 —— 设置,是在 SCL(0) 的时候,设置的

模拟代码如下 👇,1011 0010 = 0xB2,和波形一致
#define MOCK_READ_DATA 1

static uint8_t recv()
{
#if MOCK_READ_DATA == 0
// SDA 交给从设备,主设备,应该是输入状态
SDA_IN();
#endif

#if MOCK_READ_DATA
// 1011 0010 = 0xB2
uint8_t mock_bit_array[8] = {1, 0, 1, 1, 0, 0, 1, 0};
#endif

uint8_t data = 0;
for (uint8_t i = 0; i < 8; i++)
{
   // 给机会,给从设备,准备数据
   SCL(0);

#if MOCK_READ_DATA
SDA(mock_bit_array[i]); // <— 设置,是在 SCL(0) 的时候,设置的
#endif
DELAY();

   // 开始设置数据有效性
   SCL(1);

#if MOCK_READ_DATA
data |= mock_bit_array[i] << (7 - i);
#else
data |= SDA_STATE() << (7 - i);
#endif

   DELAY();

   // SCL(0); 因为下一个循环,还是低电平,这里可以省略
}
// 最后一次,是高电平,不会循环回来,所以,要加上低电平,代表 SCL 没落了
SCL(0);

return data;

}

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

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

相关文章

WPF Prism ViewInjection

ViewInjection介绍 ViewInjection是Prism框架提供的一种机制&#xff0c;用于将视图动态地注入到指定的容器&#xff08;Region&#xff09;中。这种注入方式允许你在运行时动态地添加、移除或替换视图&#xff0c;从而实现更灵活的用户界面设计。 ViewInjection示例 GitHub…

软考高级架构 - 11.1- 信息物理系统CPS

信息物理系统CPS 信息物理系统(CPS)是控制系统、嵌入式系统的扩展与延伸。通过集成先进的感知、计算、通信、控制等信息技术和自动控制技&#xff0c;构建了物理空间与信息空间中人、机、物、环境、信息等要素相互映射、适时交互、高效协同的夏杂系统。 CPS的本质是基于…

后端开发工程师需要掌握哪些设计模式?

大家好&#xff0c;我是袁庭新。 作为后端开发者&#xff0c;学习和掌握设计模式是非常有必要的。不仅可以帮助后端开发者更好地设计和实现软件架构&#xff0c;还可以提高代码的质量和可维护性。此外&#xff0c;设计模式也是后端开发面试中常见的考点之一&#xff0c;掌握它…

【Android Studio】学习——数据存储管理

AndroidStudio实验——数据存储管理 文章目录 AndroidStudio实验——数据存储管理[toc]一&#xff1a;实验目标和实验内容&#xff1a;二&#xff1a;数据库的CRUD操作【一】创建&#xff08;Create&#xff09;【2】读取&#xff08;Read&#xff09;【3】更新&#xff08;Upd…

科研绘图系列:R语言绘制热图和散点图以及箱线图(pheatmap, scatterplot boxplot)

禁止商业或二改转载,仅供自学使用,侵权必究,如需截取部分内容请后台联系作者! 文章目录 介绍加载R包数据下载图1图2图3系统信息参考介绍 R语言绘制热图和散点图以及箱线图(pheatmap, scatterplot & boxplot) 加载R包 library(magrittr) library(dplyr) library(ve…

【Qt】信号、槽

目录 一、信号和槽的基本概念 二、connect函数&#xff1a;关联信号和槽 三、自定义信号和槽 1.自定义槽函数 2.自定义信号函数 例子&#xff1a; 四、带参的信号和槽 例子&#xff1a; 五、Q_OBJECT宏 六、断开信号和槽的连接 例子&#xff1a; 一、信号和槽的基本…

一种构建网络安全知识图谱的实用方法

文章主要工作 论述了构建网络安全知识库的三个步骤&#xff0c;并提出了一个构建网络安全知识库的框架;讨论网络安全知识的推演 1.框架设计 总体知识图谱框架如图1所示&#xff0c;其包括数据源&#xff08;结构化数据和非结构化数据&#xff09;、信息抽取及本体构建、网络…

JAVA后端实现全国区县下拉选择--树形结构

设计图如图&#xff1a; 直接上代码 数据库中的格式&#xff1a; JAVA实体类&#xff1a; Data public class SysAreaZoningDO {private Long districtId;private Long parentId;private String districtName;private List<SysAreaZoningDO> children; } MapperSQL语句…

青少年夏令营管理系统的设计与开发(社团管理)(springboot+vue)+文档

&#x1f497;博主介绍&#x1f497;&#xff1a;✌在职Java研发工程师、专注于程序设计、源码分享、技术交流、专注于Java技术领域和毕业设计✌ 温馨提示&#xff1a;文末有 CSDN 平台官方提供的老师 Wechat / QQ 名片 :) Java精品实战案例《700套》 2025最新毕业设计选题推荐…

安卓低功耗蓝牙BLE官方开发例程(JAVA)翻译注释版

官方原文链接 https://developer.android.com/develop/connectivity/bluetooth/ble/ble-overview?hlzh-cn 目录 低功耗蓝牙 基础知识 关键术语和概念 角色和职责 查找 BLE 设备 连接到 GATT 服务器 设置绑定服务 设置 BluetoothAdapter 连接到设备 声明 GATT 回…

Windows 系统中的组策略编辑器如何打开?

组策略是 Windows 操作系统中用于设置计算机和用户配置的重要工具。它允许管理员控制各种系统功能&#xff0c;从桌面背景到安全设置等。对于 Windows 专业版、企业版和教育版用户来说&#xff0c;可以通过组策略编辑器&#xff08;Group Policy Editor&#xff09;来管理这些设…

MySQL删除外键报错check that column/key exists

在我们删除外键的时候&#xff0c;报了check that column/key exists这个错误&#xff0c;这是因为你的外键名字没写对&#xff0c;我们以为我们写的字段名就是我们的外键其实并不是&#xff0c;我们可以通过show create table[ ]来查看外键的名字 所以删除外键的时候应该这样…

python学opencv|读取图像(十)用numpy创建彩色图像

【1】引言 前序文章中&#xff0c;我们已经学会了用numpy规划数据控制像素大小&#xff0c;然后用像素规划矩阵&#xff0c;对矩阵赋值后输出灰度图&#xff0c;相关链接为&#xff1a; python学opencv|读取图像&#xff08;八&#xff09;用numpy创建纯黑灰度图-CSDN博客 p…

线程池(ThreadPoolExecutor)

目录 一、线程池 标准提供的线程池 ThreadPoolExecutor 自定义线程池 一、线程池 为什么要引入线程池? 这个原因我们需要追溯到线程&#xff0c;我们线程存在的意义在于&#xff0c;使用进程进行并发编程太重了&#xff0c;所以引入了线程&#xff0c;因为线程又称为 “轻…

hbase读写操作后hdfs内存占用太大的问题

hbase读写操作后hdfs内存占用太大的问题 查看内存信息hbase读写操作 查看内存信息 查看本地磁盘的内存信息 df -h查看hdfs上根目录下各个文件的内存大小 hdfs dfs -du -h /查看hdfs上/hbase目录下各个文件的内存大小 hdfs dfs -du -h /hbase查看hdfs上/hbase/oldWALs目录下…

【IntelliJ IDEA 集成工具】TalkX - AI编程助手

前言 在数字化时代&#xff0c;技术的迅猛发展给软件开发者带来了更多的挑战和机遇。为了提高技术开发群体在繁多项目中的编码效率和质量&#xff0c;他们需要一个强大而专业的工具来辅助开发过程&#xff0c;而正是为了满足这一需求&#xff0c;TalkX 应运而生。 一、概述 1…

vue2+element-ui实现多行行内表格编辑

效果图展示 当在表格中点击编辑按钮时:点击的行变成文本框且数据回显可以点击确定按钮修改数据或者取消修改回退数据: 具体实现步骤 1. 行数据定义编辑标记 行数据定义编辑标记 当在组件中获取到用于表格展示数据的方法中,针对每一行数据添加一个编辑标记 this.list.f…

React 第十六节 useCallback 使用详解注意事项

useCallback 概述 1、useCallback 是在React 中多次渲染缓存函数的 Hook&#xff0c;返回一个函数的 memoized的值&#xff1b; 2、如果多次传入的依赖项不变&#xff0c;那么多次定义的时候&#xff0c;返回的值是相同的,防止频繁触发更新&#xff1b; 3、多应用在 父组件为函…

【智体OS】官方上新发布智体机器人:使用rtrobot智体应用远程控制平衡车机器人

【智体OS】官方上新发布智体机器人&#xff1a;使用rtrobot智体应用远程控制平衡车机器人 dtns.network是一款主要由JavaScript编写的智体世界引擎&#xff08;内嵌了three.js编辑器的定制版-支持以第一视角浏览3D场馆&#xff09;&#xff0c;可以在浏览器和node.js、deno、e…

Windows系统VSCode 搭建ESP-IDF环境

VS Code&#xff0c;安装ESP-IDF插件 快捷键CTRLSHIFTP&#xff0c;弹出显示所有命令的窗口&#xff0c;选择ESP-IDF的欢迎 使用第一个选项&#xff0c;要选择一个ESP-IDF版本&#xff0c;选最新的就行 点击Install,等待下载 提示安装成功&#xff0c;如果过程中出现python已存…