STM32L475裸机例程学习 模拟IIC传输读取温度湿度数据

IIC

此文章仅限于记录个人知识盲区,没有指导作用,如果有任何疑问,欢迎在评论区提问讨论~

定义

​ I2C总线是一种串行数据总线,只有两根信号线,一根是双向的数据线SDA,另一根是时钟线SCL,两根线可以挂多个设备。IIC总线上可以挂很多设备:多个主设备,多个从设备(外围 设备)。
在这里插入图片描述
​ 上图中主设备是两个单片机,剩下的都是从设备。多主机会产生总线裁决问题。当多个主机同时想占用总线时,企图启动总线传输数据,就叫做总线竞争。I2C通过总线仲裁,以决定哪台主机控制总线。

​ 每个接到I2C总线上的器件都有唯一的地址。主机与其它器件间的数据传输可以是由主机发送数据到其它器件,这时主机即为发送器,总线上收数据的器件则为接收器。

TIPS:

I2C总线通过上拉电阻接正电源。当总线空闲的时候,两根线均为高电平。连到总线上的任一器件输出的低电平,都将使总线的信号变低,即各器件的SDA及SCL都是“与”关系。

在这里插入图片描述

IIC通信时序详解
  • 初始化、起始和停止

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-idZtxarJ-1592964147332)(C:\Users\EDZ\AppData\Roaming\Typora\typora-user-images\image-20200623180742580.png)]

初始化:IIC的初始化为SDA和SCL均为高。

开始信号:处理器让SCL时钟保持高电平,然后让SDA数据信号*由高变低就表示一个开始信号。同时IIC总线上的设备检测到这个开始信号它就知道处理器要发送数据了。

停止信号:处理器让SCL时钟保持高电平,然后让SDA数据信号由低变高就表示一个停止信号。同时IIC总线上的设备检测到这个停止信号它就知道处理器已经结束了数据传输,我们就可以各忙各个的了,如休眠等。

  • 数据传输和响应信号

    总线寻址:主机向从机发送8位数据,这8位数据是在起始信号之后发送的第一个字节,后面的字节都是数据,不再是寻址,除非又重新来一个起始信号。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oDvBe5C3-1592964147336)(C:\Users\EDZ\AppData\Roaming\Typora\typora-user-images\image-20200623184335947.png)]

    ​ 主机发送地址时,总线上的每个从机都将这7位地址码与自己的地址进行比较,若相同,则认为自己正在被主机寻址,根据R/T位将自己确定为发送器和接收器

    数据传输SDA上的数据只能在SCL为低电平期间翻转变化,在SCL为高电平期间必须保持稳定,IIC设备只在SCL为高电平期间采集SDA数据。

    响应信号(ACK):单片机发完8bit数据后就不再驱动总线了(SDA引脚变输入),而SDA和SDL硬件设计时都有上拉电阻,所以这时候SDA变成高电平。每当发送器传输完一个字节的数据之后,发送端会等待一定的时间,等接收方的应答信号。接收端通过拉低SDA数据线,给发送端发送一个应答信号,以提醒发送端我这边已经接受完成,数据可以继续传输,接下来,发送端就可以继续发送数据了。

    在总线的一次数据传输中,可以有一下几种组合方式:

    (1)、主机向从机发送数据,数据传送方向在整个传递过程中不变:

img

(2)、主机在第一个字节后,立即从从机读数据(传输方向不变):

img

(3)、在传送过程中,当需要改变传递方向时,起始信号和从机地址都被重复一次产生一次,但两次读/写方向位正好相反

img

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个基于STM32的IO模拟IIC的完整例程: 首先,需要在STM32的CubeMX中配置GPIO口,使其能够模拟IIC的时钟和数据线。具体方法如下: 1. 打开CubeMX软件,选择相应的芯片型号。 2. 在Pinout选项卡中,选择需要使用的GPIO引脚,右键单击进入Pinout Configuration。 3. 在Pinout Configuration中,将引脚的Mode设置为GPIO_Output,并将Type设置为Open-Drain。 4. 在Configuration选项卡中,选择I2C模块,将I2C的Mode设置为I2C GPIO模拟,并将SCL和SDA引脚分别设置为上一步中配置的GPIO引脚。 接下来,可以使用以下代码实现IO模拟IIC的读写操作: ``` #include "iic.h" #define IIC_SCL_PIN GPIO_PIN_8 #define IIC_SDA_PIN GPIO_PIN_9 #define IIC_SCL_LOW() HAL_GPIO_WritePin(GPIOB, IIC_SCL_PIN, GPIO_PIN_RESET) #define IIC_SCL_HIGH() HAL_GPIO_WritePin(GPIOB, IIC_SCL_PIN, GPIO_PIN_SET) #define IIC_SDA_LOW() HAL_GPIO_WritePin(GPIOB, IIC_SDA_PIN, GPIO_PIN_RESET) #define IIC_SDA_HIGH() HAL_GPIO_WritePin(GPIOB, IIC_SDA_PIN, GPIO_PIN_SET) #define IIC_SDA_READ() HAL_GPIO_ReadPin(GPIOB, IIC_SDA_PIN) void delay_us(uint32_t nus) { uint32_t ticks = nus * (SystemCoreClock / 1000000) / 5; while (ticks--) { __NOP(); } } void IIC_Init(void) { GPIO_InitTypeDef GPIO_InitStruct = {0}; __HAL_RCC_GPIOB_CLK_ENABLE(); GPIO_InitStruct.Pin = IIC_SCL_PIN | IIC_SDA_PIN; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD; GPIO_InitStruct.Pull = GPIO_PULLUP; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); IIC_SCL_HIGH(); IIC_SDA_HIGH(); } void IIC_Start(void) { IIC_SDA_HIGH(); IIC_SCL_HIGH(); delay_us(5); IIC_SDA_LOW(); delay_us(5); IIC_SCL_LOW(); } void IIC_Stop(void) { IIC_SDA_LOW(); IIC_SCL_HIGH(); delay_us(5); IIC_SDA_HIGH(); delay_us(5); } uint8_t IIC_WriteByte(uint8_t data) { uint8_t i, res; for (i = 0; i < 8; i++) { if (data & 0x80) { IIC_SDA_HIGH(); } else { IIC_SDA_LOW(); } delay_us(5); IIC_SCL_HIGH(); delay_us(5); IIC_SCL_LOW(); data <<= 1; } IIC_SDA_HIGH(); delay_us(5); IIC_SCL_HIGH(); delay_us(5); res = IIC_SDA_READ(); IIC_SCL_LOW(); return res; } uint8_t IIC_ReadByte(uint8_t ack) { uint8_t i, res = 0; IIC_SDA_HIGH(); for (i = 0; i < 8; i++) { delay_us(5); IIC_SCL_HIGH(); res <<= 1; if (IIC_SDA_READ()) { res |= 0x01; } IIC_SCL_LOW(); } if (ack) { IIC_SDA_LOW(); } else { IIC_SDA_HIGH(); } delay_us(5); IIC_SCL_HIGH(); delay_us(5); IIC_SCL_LOW(); IIC_SDA_HIGH(); return res; } ``` 在以上代码中: - `IIC_Init()`函数用于初始化GPIO引脚。 - `IIC_Start()`函数用于发送起始信号。 - `IIC_Stop()`函数用于发送停止信号。 - `IIC_WriteByte()`函数用于向IIC总线写入一个字节的数据。 - `IIC_ReadByte()`函数用于从IIC总线读取一个字节的数据。 使用以上代码时,只需要在需要读写数据的地方调用相应的函数即可。例如: ``` IIC_Start(); // 发送起始信号 IIC_WriteByte(0x50); // 向IIC总线写入设备地址 IIC_WriteByte(0x00); // 向IIC总线写入寄存器地址 IIC_Start(); // 发送重复起始信号 IIC_WriteByte(0x51); // 向IIC总线写入设备地址(读模式) data = IIC_ReadByte(1); // 从IIC总线读取数据,并发送ACK IIC_Stop(); // 发送停止信号 ``` 以上代码实现了IO模拟IIC的读写操作。在实际使用时,还需要根据实际情况进行修改和优化。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值