模拟IIC——关于模拟IIC的IO口的配置选取推挽输出还是开漏输出,以及是否需要更改IO口输入输出模式和是否需要对IO配置上拉

news/2024/11/22 17:15:05/

问题如下

当时我以为引脚配错了,原理图明明是B引脚,为何程序是C呢
在这里插入图片描述
在这里插入图片描述

查了一下资料,顿悟了

https://blog.csdn.net/m0_62243928/article/details/125779308

在使用模拟IIC的时候,观看别人的程序的时候发现了程序之间的一些不一样的地方

——————————————————————————————————代码1————————————————————————————————————//IO方向设置
#define SDA_IN()  {GPIOB->MODER&=~(3<<(9*2));GPIOB->MODER|=0<<9*2;}	//PB9输入模式
#define SDA_OUT() {GPIOB->MODER&=~(3<<(9*2));GPIOB->MODER|=1<<9*2;} //PB9输出模式
//IO操作函数	 
#define IIC_SCL    PBout(8) //SCL
#define IIC_SDA    PBout(9) //SDA	 
#define READ_SDA   PBin(9)  //输入SDA 
———————————————————————————————————————————————————————————————————————————
——————————————————————————————————代码2————————————————————————————————————#define BH1750_I2C_SCL_1()  GPIO_SetBits(GPIOB, GPIO_Pin_6)		/* SCL = 1 */
#define BH1750_I2C_SCL_0()  GPIO_ResetBits(GPIOB, GPIO_Pin_6)	/* SCL = 0 */#define BH1750_I2C_SDA_1()  GPIO_SetBits(GPIOB, GPIO_Pin_7)		/* SDA = 1 */
#define BH1750_I2C_SDA_0()  GPIO_ResetBits(GPIOB, GPIO_Pin_7)	/* SDA = 0 */#define BH1750_I2C_SDA_READ()  GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_7)	
/* 读SDA口线状态 */
———————————————————————————————————————————————————————————————————————————

代码1中间有一个对SDA数据线的模式的配置, 也就是输入输出的设置;

在向从设备写数据的时候会先调用SDA_OUT(),将引脚配置为输出模式;

在向从设备写数据的时候会先调用SDA_IN(),将引脚配置为输入模式;

但是问题来了,代码2中间并没有对引脚的输入输出模式进行改变,代码2也能和从机进行正常的通信,这是为什么呢?模拟IIC通信时对引脚的输入输出模式的配置是否有必要呢?

我仔细对比了两份代码,发现两份代码在引脚的初始化部分不一样。

——————————————————————————————————代码1————————————————————————————————————
void SHT3x_Init(void)
{    RCC->APB2ENR|=1<<6;              //使能PORTE时钟					 GPIOE->CRL&=0XFF00FFFF;          //PE4,PE5GPIOE->CRL|=0X00330000;	         //推挽输出 GPIOE->ODR|=3<<4;                //将PE4、PE5设为1
}
——————————————————————————————————代码2————————————————————————————————————static void I2C_BH1750_GPIOConfig(void)
{GPIO_InitTypeDef GPIO_InitStructure;RCC_APB2PeriphClockCmd(BH1750_RCC_I2C_PORT, ENABLE);	/* 打开GPIO时钟 */GPIO_InitStructure.GPIO_Pin = BH1750_I2C_SCL_PIN | BH1750_I2C_SDA_PIN;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;  	    /* 开漏输出 */GPIO_Init(BH1750_GPIO_PORT_I2C, &GPIO_InitStructure);/* 给一个停止信号, 复位I2C总线上的所有设备到待机模式 */i2c_Stop();
}
RCC_APB2PeriphClockCmd(BH1750_RCC_I2C_PORT, ENABLE);	/* 打开GPIO时钟 */GPIO_InitStructure.GPIO_Pin = BH1750_I2C_SCL_PIN | BH1750_I2C_SDA_PIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;  	    /* 开漏输出 */
GPIO_Init(BH1750_GPIO_PORT_I2C, &GPIO_InitStructure);/* 给一个停止信号, 复位I2C总线上的所有设备到待机模式 */
i2c_Stop();

}

可以发现:推挽输出对应->需要切换输入输出模式;开漏输出对应->不需要切换输入输出模式;

我学习IIC的时候明明记得IIC需要使用开漏输出和接上拉电阻的;如果使用推挽输出,当多个设备

连接到一个总线上面时,如果一个设备输出低电平一个设备输出高电平就会出现短路的情况。而且

不能推挽输出实现线与。

为什么这里可以使用推挽呢?我猜测这是模拟IIC和硬件IIC的不同了。

我使用模拟IIC一般来说不会出现需要多个设备连接到一个总线上面情况,也就不会出现上面的短

路的可能以及线与的需求了。所以模拟IIC这里是可以使用推挽输出的。

那为什么推挽输出需要切换输入输出模式,开漏输出不需要切换输入输出模式呢?

我们首先需要了解到GPIO口的输入和输出模式有什么不同以及推挽输出和开漏输出的不同:

下面对于GPIO口的内容引用

GPIO口的输入,输出模式及其说明
https://blog.csdn.net/qq_42384937/article/details/82428812
在输入模式下只有红色圈出的部分处于工作状态,也就是说下半部分的输出电路,实际上是与端口处于隔离状态,不能工作,这个时候我们不能去读取端口的电平。
在这里插入图片描述
在输出模式下,图的上半部,施密特触发器处于开启状态,这意味着CPU可以在“输入数据寄存器”的另一端,随时监控I/O端口的状态;
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
我们可以发现:

在输出模式下,施密特触发器处于开启状态,这意味着CPU可以在“输入数据寄存器”的另一端,随时监控I/O端口的状态,也就是可以读取IO口的值;

但是对于推挽输出而言:推挽输出是强输出电流模式,在此模式下的输出通道上的推挽结构MOS管,属于强上拉和强下拉的,这会影响读取IDR时的值,强上拉意味着会将来自外部的低电平输入强制置高,强下拉意味着会将来自外部的高电平输入强制置低

在开漏模式下,实现了虚拟的I/O端口双向通信:只要CPU输出逻辑“1”,由于N-MOS管处于关闭状态,I/O端口的电平将完全由外部电路决定,因此,CPU可以在“输入数据寄存器”读到外部电路的信号,而不是它自己输出的逻辑“1”。

了解stm32的双向io口

请问STM32F4的GPIO管脚可以同时配置成输入、输出模式吗
详细一点的内容可以看这两个链接

现在我们可以分析两个代码的差异:推挽输出对应->需要切换输入输出模式;开漏输出对应->不需要切换输入输出模式;

因为设置为推挽输出时 输出通道上的推挽结构MOS管,属于强上拉和强下拉的,这会影响读取IDR时的值,强上拉意味着会将来自外部的低电平输入强制置高,强下拉意味着会将来自外部的高电平输入强制置低。所以我们去读取IO口的值会是输出的值,并不能得到外部电路的值。所以在IIC通信时需要读取外部数据的时候需要将IO的模式配置成输入模式;

但是对于开漏输出来说,只要CPU输出逻辑“1”,由于N-MOS管处于关闭状态,I/O端口的电平将完全由外部电路决定,因此,CPU可以在“输入数据寄存器”读到外部电路的信号,而不是它自己输出的逻辑“1”。所以不需要去将IO口的模式配置成输入模式

这也就造成了上面两个代码的差异。

上拉电阻:查看F4的中文参考手册可以知道
在这里插入图片描述
IO口配置成上拉的时候可以解决推挽输出输出高电平时高阻态没办法拉高电平的情况。

注意:
使用开漏输时,如果需要读取电平值,最好输出高电平,由于N-MOS管处于关闭状态,I/O端口的电平将完全由外部电路决定,这时读到的才是外部电路的电平值。

每个线上加了4.7K的上拉电阻【上拉电阻具体数值需要计算和测试,基本选择10K往下】

对于MCU和EE单对单的:1.SCL并不是加了上拉电阻就得用推挽输出;2.SCL可以用推挽也可以用开漏,SDA必须用开漏输出;3.对于主从电平不一致的情况,SCL需要做上拉用开漏,将电平拉到从电平。

不会存在所谓的线与,IO口短路。SDA采用推挽,有可能会出现高电平被下拉,大概率是驱动负载过大的原因。


http://www.ppmy.cn/news/1027066.html

相关文章

redis学习笔记(三)

文章目录 key操作&#xff08;1&#xff09;查找键&#xff08;2&#xff09;判断键是否存在&#xff08;3&#xff09;查看键的的值的数据类型&#xff08;4&#xff09;删除键以及键对应的值&#xff08;5&#xff09;查看键的有效期&#xff08;6&#xff09;设置key的有效期…

SAP BAPI 创建/修改MD61/MD62计划独立需求预测

MD61 创建&#xff1a; BAPI: BAPI_REQUIREMENTS_CREATE CLEAR: lv_error,ls_requirements_item,lt_requirements_schedule_in,ls_requirements_schedule_in,lt_return_n,ls_return_n,lv_reqmtsplannumber."工厂ls_requirements_item-plant lv_werks."MRP AR…

Xray配置文件详解

Xray配置文件详解 1.并发配置2.HTTP配置3.插件配置4.被动代理配置5.反连平台配置1.并发配置 在配置文件中可以用下面的配置改变漏洞探测的 worker 数量: parallel: 30 # 漏洞探测的 worker 数量,可以简单理解为同时有 30 个 POC 在运行这个值并非越大越好,因为高并发的情况…

监控Kubernetes Node组件的关键指标

所有的 Kubernetes 组件&#xff0c;都提供了 /metrics 接口用来暴露监控数据&#xff0c;Kube-Proxy 也不例外。通过 ss 或者 netstat 命令可以看到 Kube-Proxy 监听的端口&#xff0c;一个是 10249&#xff0c;用来暴露监控指标&#xff0c;一个是 10256 &#xff0c;作为…

WeiSpeechSynthesizer.java

目录 1 WeiSpeechSynthesizer.java 1.1 onSpeakProgress 1.2 onSpeakPaused 1.3 onCompleted WeiSpeechSynthesizer.javainitListener private InitListener initListener=new InitListener() {@Override

项目管理师基础之项目管理计划和项目文件

项目管理过程中&#xff0c;会使用并产生两大类文件&#xff1a;项目管理计划和项目文件。内容一般如下&#xff1a; 整个项目生命周期需要收集、分析和转化大量的数据。从各个过程收集项目数据&#xff0c;并在项目团队内共享。在各个过程中所收集的数据经过结合相关背景的分…

【Linux进行时】进程概念

进程的概念 什么是进程呢&#xff1f; ❓首先我们需要认识一下什么叫进程呢&#xff1f; 课本概念&#xff1a;程序的一个执行实例&#xff0c;正在执行的程序等 &#x1f525;内核观点&#xff1a;担当分配系统资源&#xff08;CPU时间&#xff0c;内存&#xff09;的实体。…

Redis - 数据类型映射底层结构

简介 从数据类型上体现就是&#xff0c;同一个数据类型&#xff0c;在不同的情况下会使用不同的编码类型&#xff0c;底层所使用的的数据结构也不相同。 字符串对象 字符串对象的编码可以是 int、raw 和 embstr 三者之一。 embstr 编码是专门用于保存简短字符串的一种优化编…