STM32F103_I2C硬件模拟

本文详细介绍了STM32F103如何进行I2C硬件模拟,从I2C协议的基础知识到STM32的I2C通讯过程,再到硬件设计和编程要点,包括数据传输模式、信号有效性、地址及数据方向等,并提供了代码详解,指导读者实现STM32与24C02 EEPROM的I2C通信。
摘要由CSDN通过智能技术生成

I2C简介

I2C 通讯协议(Inter-Integrated Circuit)是由Phiilps公司开发的,由于它引脚少,硬件实现简单,可扩展性强,不需要USART、CAN等通讯协议的外部收发设备,现在被广泛地使用在系统内多个集成电路(IC)间的通讯。

I2C物理层的特点

在这里插入图片描述

1.它是一个支持多设备的总线。“总线”指多个设备共用的信号线。在一个I2C通讯总线中,可连接多个I2C通讯设备,支持多个通讯主机及多个通讯从机。
2.一个I2C总线只使用两条总线线路,一条双向串行数据线(SDA) , 一条串行时钟线 (SCL)。数据线即用来表示数据,时钟线用于数 据收发同步。
3.每个连接到总线的设备都有一个独立的地址,主机可以利用这个 地址进行不同设备之间的访问。
4.总线通过上拉电阻接到电源。当I2C设备空闲时,会输出高阻态,而当 所有设备都空闲都输出高阻态时,由上拉电阻把总线拉成高电平

5.多个主机同时使用总线时,为了防止数据冲突,会利用仲裁方式决定由 哪个设备占用总线。 具有三种传输模式:标准模式传输速率为100kbit/s ,快速模式为 400kbit/s ,高速模式下可达 3.4Mbit/s,但目前大多I2C设备尚不支持高 速模式。

I2C的协议层

I2C的协议定义了通讯的起始和停止信号、数据有效性、响应、
仲裁、时钟同步和地址广播等环节。
I2C基本读写过程
主机写数据到从机:
在这里插入图片描述
在这里插入图片描述

I2C基本读写过程
在这里插入图片描述
在这里插入图片描述
通讯复合格式:

在这里插入图片描述
在这里插入图片描述

通讯的起始和停止信号

在这里插入图片描述

• 当 SCL 线是高电平时 SDA 线从高电平向低电平切换,这个情况 表示通讯的起始。
• 当 SCL 是高电平时 SDA
线由低电平向高电平切换,表示通讯的 停止。 • 起始和停止信号一般由主机产生。

数据有效性

I2C使用SDA信号线来传输数据,使用SCL信号线进行数据同步。
SDA数据线在SCL的每个时钟周期传输一位数据。
在这里插入图片描述

• SCL为高电平的时候SDA表示的数据有效,即此时的SDA为高电平时 表示数据“1”,为低电平时表示数据“0”。 •
当SCL为低电平时,SDA的数据无效,一般在这个时候SDA进行电平 切换,为下一次表示数据做好准备。

地址及数据方向

• I2C总线上的每个设备都有自己的独立地址,主机发起通讯时,通过 SDA信号线发送设备地址(SLAVE_ADDRESS)来查找从机。设备地址可以是7位10位。 •
紧跟设备地址的一个数据位R/W用来表示数据传输方向,数据方向位为 “1”时表示主机由从机读数据,该位为“0”时表示主机向从机写数据。

在这里插入图片描述

响应

I2C的数据和地址传输都带响应。响应包括“应答(ACK)”和“非应答(NACK)”两种信号。在这里插入图片描述
传输时主机产生时钟,在第9个时钟时,数据发送端会释放SDA的控制权,由数据接收端控制SDA,若SDA为高电平,表示非应答信号(NACK),低电平表示应答信号(ACK)。

STM32的I2C特性及架构
软件模拟协议:使用CPU直接控制通讯引脚的电平,产生出符合通讯协议标准的逻辑。
硬件实现协议:由STM32的I2C片上外设专门负责实现I2C通讯协议,只要配置好该外设,它就会自动根据协议要求产生通讯信号,收发数据并缓存起来,CPU只要检测该外设的状态和访问数据寄存器,就能完成数据收发。这种由硬件外设处理I2C协议的方式减轻了CPU的工作,且使软件设计更加简单。
在这里插入图片描述

1.通讯引脚
STM32芯片有多个I2C外设,它们的I2C通讯信号引出到不同的GPIO引脚上,使用时必须配置到这些指定的引脚.
2.时钟控制逻辑

SCL线的时钟信号,由I2C接口根据时钟控制寄存器(CCR)控制, 控制的参数主要为时钟频率。 •
可选择I2C通讯的“标准/快速”模式,这两个模式分别I2C对应 100/400Kbit/s的通讯速率。 •
在快速模式下可选择SCL时钟的占空比,可选Tlow/Thigh=2或 Tlow/Thigh=16/9模式。 •
CCR寄存器中12位的配置因子CCR,它与I2C外设的输入时钟源共同 作用,产生SCL时钟。STM32的I2C外设输入时钟源为PCLK1。

3.数据控制逻辑

I2C的SDA信号主要连接到数据移位寄存器上,数据移位寄存器的数据来源及目标是数据寄存器(DR)、地址寄存器(OAR)、PEC寄存器以及SDA数据线。
• 当向外发送数据的时候,数据移位寄存器以“数据寄存器”为数据源,把数据一位一位地通过SDA信号线发送出去; •
当从外部接收数据的时候,数据移位寄存器把SDA信号线采样到的数据一位一位地存储到“数据寄存器”中。

STM32的I2C通讯过程

使用I2C外设通讯时,在通讯的不同阶段它会对“状态寄存器(SR1及SR2)”的不同数据位写入参数,通过读取这些寄存器标志来了解通讯状态。
1.主发送器
在发送了地址和清除了ADDR位后, 主设备通过内部移位寄存器将字节从DR寄存器发送到SDA线上。
主设备等待,直到TxE被清除,(见图245的EV8)。
当收到应答脉冲时:
● TxE位被硬件置位,如果设置了INEVFEN和ITBUFEN位,则产生一个中断。
如果TxE被置位并且在上一次数据发送结束之前没有写新的数据字节到DR寄存器,则BTF被硬件置位,在清除BTF之前I2C接口将保持SCL为低电平;读出I2C_SR1之后再写入I2C_DR寄存器
将清除BTF位。关闭通信在DR寄存器中写入最后一个字节后,通过设置STOP位产生一个停止条件(见图245的EV8_2),然后I2C接口将自动回到从模式(M/S位清除)。
在这里插入图片描述

2.主接收器
在发送地址和清除ADDR之后,I2C接口进入主接收器模式。在此模式下,I2C接口从SDA线接收数据字节,并通过内部移位寄存器送至DR寄存器。在每个字节后,I2C接口依次执行以下操作:
● 如果ACK位被置位,发出一个应答脉冲。
● 硬件设置RxNE=1,如果设置了INEVFEN和ITBUFEN位,则会产生一个中断(见图246的EV7)。
如果RxNE位被置位,并且在接收新数据结束前,DR寄存器中的数据没有被读走,硬件将设置BTF=1,在清除BTF之前I2C接口将保持SCL为低电平;读出I2C_SR1之后再读出I2C_DR寄存器将清除BTF位。
关闭通信主设备在从从设备接收到最后一个字节后发送一个NACK。接收到NACK后,从设备释放对SCL和SDA线的控制;主设备就可以发送一个停止/重起始条件。
● 为了在收到最后一个字节后产生一个NACK脉冲,在读倒数第二个数据字节之后(在倒数第二个RxNE事件之后)必须清除ACK位。
● 为了产生一个停止/重起始条件,软件必须在读倒数第二个数据字节之后(在倒数第二个RxNE事件之后)设置STOP/START位。
● 只接收一个字节时,刚好在EV6之后(EV6_1时,清除ADDR之后)要关闭应答和停止条件的产生位。
在产生了停止条件后,I2C接口自动回到从模式(M/SL位被清除)
在这里插入图片描述

3.从发送器

在接收到地址和清除ADDR位后,从发送器将字节从DR寄存器经由内部移位寄存器发送到SDA线上。
从设备保持SCL为低电平,直到ADDR位被清除并且待发送数据已写入DR寄存器。(见下图中的EV1和EV3)。
当收到应答脉冲时:
● TxE位被硬件置位,如果设置了ITEVFEN和ITBUFEN位,则产生一个中断。
如果TxE位被置位,但在下一个数据发送结束之前没有新数据写入到I2C_DR寄存器,则BTF位被置位,在清除BTF之前I2C接口将保持SCL为低电平;读出I2C_SR1之后再写入I2C_DR寄存器
将清除BTF位。
在这里插入图片描述
4.从接收器
在接收到地址并清除ADDR后,从接收器将通过内部移位寄存器从SDA线接收到的字节存进DR
寄存器。I2C接口在接收到每个字节后都执行下列操作:
● 如果设置了ACK位,则产生一个应答脉冲
● 硬件设置RxNE=1。如果设置了ITEVFEN和ITBUFEN位,则产生一个中断。
如果RxNE被置位,并且在接收新的数据结束之前DR寄存器未被读出,BTF位被置位,在清除BTF之前I2C接口将保持SCL为低电平;读出I2C_SR1之后再写入I2C_DR寄存器将清除BTF位。
在这里插入图片描述
关闭从通信
在传输完最后一个数据字节后,主设备产生一个停止条件, I2
C接口检测到这一条件时:
● 设置STOPF=1,如果设置了ITEVFEN位,则产生一个中断。

硬件设计


24C02 的 SCL 和 SDA 分别连在 STM32F1 的 PB6 和 PB7 上的
在这里插入图片描述
24C02 (EEPROM)芯片
在这里插入图片描述
本实验板中的 EEPROM 芯片 (型号:AT24C02) 的 SCL 及 SDA 引脚连接到了 STM32 对应的 I2C引脚中,结合上拉电阻,构成了 I2C 通讯总线,它们通过 I2C 总线交互。EEPROM 芯片的设备地址一共有 7 位,其中高 4 位固定为:1010 b,低 3 位则由 A0/A1/A2 信号线的电平决定,见图EEPROM 设备地址 ,图中的 R/W 是读写方向位,与地址无关。
在这里插入图片描述

按照我们此处的连接,A0/A1/A2 均为 0,所以 EEPROM 的 7 位设备地址是:101 0000b,即 0x50。
由于 I2C 通讯时常常是地址跟读写方向连在一起构成一个 8 位数,且当 R/W 位为 0 时,表示写方向,所以加上 7 位地址,其值为“0xA0”,常称该值为 I2C 设备的“写地址”;当 R/W 位为 1时,表示读方向,加上 7 位地址,其值为“0xA0”,常称该值为“读地址”。
EEPROM 芯片中还有一个 WP 引脚,具有写保护功能,当该引脚电平为高时,禁止写入数据,当引脚为低电平时,可写入数据,我们直接接地,不使用写保护功能。

编程要点

(1) 配置通讯使用的目标引脚为开漏模式;
(2) 使能 I2C 外设的时钟;
(3) 配置 I2C 外设的模式、地址、速率等参数并使能 I2C 外设;
(4) 编写基本 I2C 按字节收发的函数;
(5) 编写读写 EEPROM 存储内容的函数;
(6) 编写测试程序,对读写数据进行校验。

代码详解

串口初始化

//部分代码使用宏定义,具体请看.h文件
void GPIOx_Config(void)
{
   
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIO_X,ENABLE);   //开时钟
	GPIO_InitTypeDef  GPIO_InitStuct;
	GPIO_InitStuct.GPIO_Mode = GPIO_Mode_X;                 //开漏输出
	GPIO_InitStuct.GPIO_Pin = GPIO_Pin_SCL|GPIO_Pin_SDA;
	GPIO_InitStuct.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIO_X,&GPIO_InitStuct);
} 

I2C初始化

//部分代码使用宏定义,具体请看.h文件
void Hardware_Config(void)
{
   
	GPIOx_Config();
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1,ENABLE);  //开IIC的时钟
	I2C_DeInit(EEPROM_I2C_X);                             //将外设 I2Cx 寄存器重设为缺省值   即初始化
  I2C_InitTypeDef I2C_InitStuct;
	I2C_InitStuct.I2C_Mode = I2C_Mode_I2C; //设置 I2C 为 I2C 模式
	I2C_InitStuct.I2C_DutyCycle = I2C_DutyCycle_2;   //I2C 快速模式 Tlow / Thigh = 2 
	I2C_InitStuct.I2C_OwnAddress1  = I2Cx_OWN_ADDRESS7;     //该参数用来设置第一个设备自身地址,它可以是一个 7 位地址或者一个 10 位地址。
	I2C_InitStuct.I2C_Ack =I2C_Ack_Enable;  //使能应答(ACK)
	I2C_InitStuct.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit; //7位应答地址
	I2C_InitStuct.I2C_ClockSpeed = I2C_Speed;  //该参数用来设置时钟频率,这个值不能高于 400KHz。
	I2C_Init(EEPROM_I2C_X,
  • 7
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 9
    评论
以下是 STM32F103 上使用 GPIO 模拟 I2C 从机的代码示例,供您参考: ```c #include "stm32f10x.h" // I2C GPIO 端口定义 #define I2C_SCL_PIN GPIO_Pin_6 #define I2C_SDA_PIN GPIO_Pin_7 #define I2C_GPIO_PORT GPIOB // I2C 状态定义 #define I2C_STA_IDLE 0 #define I2C_STA_START 1 #define I2C_STA_ADDR_R 2 #define I2C_STA_ADDR_W 3 #define I2C_STA_DATA_R 4 #define I2C_STA_DATA_W 5 #define I2C_STA_STOP 6 // I2C 从机状态定义 typedef struct { uint8_t State; // 当前状态 uint8_t Addr; // 从机地址 uint8_t Reg; // 寄存器地址 uint8_t Data; // 数据 } I2C_SwSlaveTypeDef; I2C_SwSlaveTypeDef SwSlaveI2C; // 设置 SCL 和 SDA 方向 #define SET_SCL_DIR(DIR) GPIO_WriteBit(I2C_GPIO_PORT, I2C_SCL_PIN, (DIR) ? Bit_SET : Bit_RESET) #define SET_SDA_DIR(DIR) GPIO_WriteBit(I2C_GPIO_PORT, I2C_SDA_PIN, (DIR) ? Bit_SET : Bit_RESET) // 读取 SDA 状态 #define READ_SDA_STATE() GPIO_ReadInputDataBit(I2C_GPIO_PORT, I2C_SDA_PIN) // 延时一段时间(单位:微秒) void DelayUs(uint32_t us) { uint32_t i; for (i = 0; i < us * 8; i++) {} // 根据 CPU 主频调整 } // 初始化 I2C 从机 void I2C_SwSlaveInit(void) { GPIO_InitTypeDef GPIO_InitStruct; // 使能 I2C GPIO 端口时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); // 配置 I2C GPIO 端口为推挽输出 GPIO_InitStruct.GPIO_Pin = I2C_SCL_PIN | I2C_SDA_PIN; GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(I2C_GPIO_PORT, &GPIO_InitStruct); // 初始化 I2C 从机状态 SwSlaveI2C.State = I2C_STA_IDLE; SwSlaveI2C.Addr = 0; SwSlaveI2C.Reg = 0; SwSlaveI2C.Data = 0; } // 处理 I2C 从机中断 void I2C_SwSlaveHandler(void) { uint8_t i; // 处理 I2C 从机状态 switch (SwSlaveI2C.State) { // 空闲状态 case I2C_STA_IDLE: // 等待 I2C 主机发起起始信号 if (READ_SDA_STATE() == RESET) { DelayUs(5); if (READ_SDA_STATE() == RESET) { SET_SCL_DIR(1); SwSlaveI2C.State = I2C_STA_START; } } break; // 起始状态 case I2C_STA_START: // SCL 保持高电平,SDA 下降沿表示起始信号 SET_SDA_DIR(0); DelayUs(5); SET_SCL_DIR(0); SwSlaveI2C.State = I2C_STA_ADDR_R; break; // 读取从机地址状态 case I2C_STA_ADDR_R: // 读取从机地址,并根据 R/W 位设置状态 for (i = 0; i < 8; i++) { SET_SCL_DIR(1); DelayUs(5); SwSlaveI2C.Addr <<= 1; SwSlaveI2C.Addr |= READ_SDA_STATE(); SET_SCL_DIR(0); DelayUs(5); } if (SwSlaveI2C.Addr & 0x01) { SwSlaveI2C.State = I2C_STA_DATA_R; } else { SwSlaveI2C.State = I2C_STA_DATA_W; } break; // 读取数据状态 case I2C_STA_DATA_R: // 读取数据,并
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

梨花落-

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值