STM32学习——IIC

把参考链接里的我觉着对我来说有用的搬运过来了,方便以后自己查阅。(有点乱,第一次写)
参考链接1: IIC原理超详细讲解—值得一看
参考链接2: STM32之IIC详细解析

IIC 简介

  两线式串行总线,用于连接微控制器及其外围设备。多用于主控制器和从器件间的主从通信,在小数据量场合使用,传输距离短,任意时刻只能有一个主机等特性。
  在 CPU 与被控 IC 之间、IC 与 IC 之间进行双向传送,高速 IIC 总线一般可达 400kbps 以上。
物理拓扑图
  SCL和SDA都需要接上拉电阻 (大小由速度和容性负载决定一般在3.3K-10K之间) 保证数据的稳定性,减少干扰。
  IIC是半双工,而不是全双工 ,同一时间只可以单向通信

IIC有哪些线?

  只有两个总线: 双向的串行数据线SDA;串行时钟线SCL

SDA(Serial data)是数据线,D代表Data也就是数据,Send Data 也就是用来传输数据的
SCL(Serial clock line)是时钟线,C代表Clock 也就是时钟 也就是控制数据发送的时序的
IIC的线
  用到IIC的设备都将两条线连在上面,每个设备都有一个唯一的地址,来区分不同设备。

IIC 使用介绍

IIC数据传输过程存在的信号

  传送数据过程中共有三种类型信号, 它们分别是:开始信号结束信号应答信号

  ——开始信号:SCL 为高电平时,SDA 由高电平向低电平跳变下降沿),开始传送数据。
  ——结束信号:SCL 为高电平时,SDA 由低电平向高电平跳变上升沿),结束传送数据。
  ——应答信号:接收数据的 IC 在接收到 8bit 数据后,向发送数据的 IC 发出特定的低电平脉冲,表示已收到数据。CPU 向受控单元发出一个信号后,等待受控单元发出一个应答信号,CPU 接收到应答信号后,根据实际情况作出是否继续传递信号的判断。若未收到应答信号,由判断为受控单元出现故障。
IIC总线协议
  因为有上拉电阻的存在,所以在空闲时刻保证了SDA和SCL保持高电平状态

IIC数据传输过程信号产生范例

  ——IIC初始化——:SCL、SDA均处于高电平状态。

void IIC_init()       //IIC初始化
{
       SCL=1; //首先把时钟线拉高
       delay_us(4);//延时函数
       SDA=1; //在SCL为高的情况下把SDA拉高
       delay_us(4); //延时函数
}

  ——开始信号——:SCL保持高电平,SDA由高电平变为低电平后,延时(>4.7us),SCL变为低电平。
开始信号

//产生IIC开始信号
//1.先拉高SDA,再拉高SCL,空闲状态
//2.拉低SDA
void IIC_Start()         //启动信号
{
       SDA=1; //确保SDA线为高电平
       delay_us(5);
       SCL=1;  //确保SCL高电平
       delay_us(5);
       SDA=0; //在SCL为高时拉低SDA线,即为起始信号
       delay_us(5);
       SCL=0;   //钳住I2C总线,准备发送或接收数据 
}

  ——结束信号——:停止信号:SCL保持高电平。SDA由低电平变为高电平。
开始信号

//产生IIC结束信号
//1.先拉低SDA,再拉低SCL
//2.拉高SCL
//3.拉高SDA
//4.停止接收数据
void IIC_Stop(void)
{
	IIC_SCL=0;
	IIC_SDA=0;    //STOP:当SCL高时,数据由低变高
 	delay_us(4);
	IIC_SCL=1; 
	IIC_SDA=1;    //发送I2C总线结束信号
	delay_us(4);							   	
}

  ——应答信号:主机SCL拉高,读取从机SDA的电平,为低电平表示产生应答。

  应答信号为低电平时,规定为有效应答位(ACK,简称应答位),表示接收器已经成功地接收了该字节;(ACK:在大于一个时钟脉冲的时间保持SDA为低电平)
  应答信号为高电平时,规定为非应答位(NACK),一般表示接收器接收该字节没有成功。(NACK:在大于一个时钟脉冲的时间保持SDA为高电平)

应答信号电平变化
  在一个字节(8bit)传输的第九个时钟期间内,接收数据方必须回一个ACK应答信号给发送方。(应答出现在每一次主机完成8个数据位传输后紧跟着的时钟周期,低电平0表示应答,1表示非应答

//****************主机产生应答信号ACK****************//
//1.先拉低SCL,再拉低SDA
//2.拉高SCL
//3.拉低SCL## 标题
//SCL	___——_
//SDA	——____
void I2C_Ack(void)
{
   IIC_SCL=0;   //先拉低SCL,使得SDA数据可以发生改变(因为只有SCL低电平,数据才能变)
   IIC_SDA=0;   
   delay_us(2);
   IIC_SCL=1;
   delay_us(5);
   IIC_SCL=0;
}

//****************主机不产生应答信号NACK****************//
//1.先拉低SCL,再拉高SDA
//2.拉高SCL
//3.拉低SCL
void I2C_NAck(void)
{
   IIC_SCL=0;   //先拉低SCL,使得SDA数据可以发生改变
   IIC_SDA=1;   //拉高SDA,不产生应答信号
   delay_us(2);
   IIC_SCL=1;
   delay_us(5);
   IIC_SCL=0;
}
//****************等待应答信号到来****************//
//返回值:1,接收应答失败
//        0,接收应答成功
char IIC_Wait_Ack(void)
{
	u8 ucErrTime=0;
	 
	IIC_SDA=1;delay_us(1);	   
	IIC_SCL=1;delay_us(1);	 
	while(IIC_SDA)
	{
		ucErrTime++;
		if(ucErrTime>250)
		{
			IIC_Stop();
			return 1;
		}
	}
	IIC_SCL=0;//时钟输出0 	   
	return 0;  
} 

  SDA线上的数据在SCL时钟“高”期间必须是稳定的,只有当SCL线上的时钟信号为低时,数据线上的“高”或“低”状态才可以改变。输出到SDA线上的每个字节必须是8位,数据传送时,先传送最高位(MSB),每一个被传送的字节后面都必须跟随一位应答位(即一帧共有9位)。
  **当一个字节按数据位从高位到低位的顺序传输完后,紧接着从设备将拉低SDA线,回传给主设备一个应答位ACK, 此时才认为一个字节真正的被传输完成 **

在这里插入图片描述

向从机发送数据总体的一个过程:
  1-主机发送start信号;
  2-主机发送从机地址,高7bit是地址,bit0是读写控制位,0表示写,1表示读;
  3-从机返回ACK响应信号;
  4-主机发送要给从机写入数据的地址;(有的设备不用)
  5-从机返回ACK响应信号;
  6-主机发送数据;
  7-从机返回ACK响应信号;
  8-重复第6和7步,直到从机返回一个NACK非响应信号;
  9-主机发送停止信号,结束数据传输。

读取从机发来数据的一个过程:
  先向从机写入从机地址,因为IIC总线上可能不止一台从机设备,只有地址相等的从机才会返回应答,才可以进行之后的步骤;之后发送要读取数据的地址;再重启IIC总线,发送从机地址并改为读数据模式;接下来就可以读取数据直到主机发送非响应信号为止。
  1-主机发送start信号;
  2-主机发送从机地址,高7bit是地址,bit0是读写控制位,0表示写,1表示读;
  3-从机返回ACK响应信号;
  4-主机发送要给从机写入数据的地址;(有的设备不用)
  5-从机返回ACK响应信号;
  6-重新启动IIC总线,发送start信号;(前面步骤的目的向从机传送地址,下面开始读取数据)
  7-主机发送从机地址,高7bit是地址,bit0是读写控制位,0表示写,1表示读;
  8-从机返回ACK响应信号;
  9-主机接收数据;
  10-从机返回ACK响应信号;
  11重复第9和01步,直到从机返回一个NACK非响应信号;
  12主机发送停止信号,结束数据传输。

  • 2
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,关于STM32模拟IIC,下面是一份示例代码,你可以参考一下。 首先,需要在STM32的CubeMX中配置IIC引脚,选择PB6和PB7作为SCL和SDA引脚。 接下来,打开CubeMX的“Pinout”选项卡,在左侧的“Categories”中选择“I2C1”,将PB6和PB7分别设置为SCL和SDA引脚。然后点击“Generated Code”生成代码。 在生成的代码中,你需要找到以下部分: ``` /* I2C1 GPIO Configuration PB6 ------> I2C1_SCL PB7 ------> I2C1_SDA */ HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); ``` 这是IIC引脚的配置代码,确保你的引脚与这里的配置相同。 接下来,是IIC初始化和读写数据的代码: ``` I2C_HandleTypeDef hi2c1; void MX_I2C1_Init(void) { hi2c1.Instance = I2C1; hi2c1.Init.ClockSpeed = 100000; hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2; hi2c1.Init.OwnAddress1 = 0; hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT; hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE; hi2c1.Init.OwnAddress2 = 0; hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE; hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE; if (HAL_I2C_Init(&hi2c1) != HAL_OK) { Error_Handler(); } } void I2C_WriteData(uint8_t addr, uint8_t reg, uint8_t data) { HAL_I2C_Mem_Write(&hi2c1, addr << 1, reg, I2C_MEMADD_SIZE_8BIT, &data, 1, 1000); } void I2C_ReadData(uint8_t addr, uint8_t reg, uint8_t* data, uint8_t len) { HAL_I2C_Mem_Read(&hi2c1, addr << 1, reg, I2C_MEMADD_SIZE_8BIT, data, len, 1000); } ``` 其中,MX_I2C1_Init()函数用于初始化IIC,I2C_WriteData()函数用于向设备写入数据,I2C_ReadData()函数用于从设备读取数据。 使用示例: ``` uint8_t data = 0x12; I2C_WriteData(0x50, 0x20, data); //向地址为0x50的设备的0x20寄存器写入0x12 uint8_t readData[2] = {0}; I2C_ReadData(0x50, 0x20, readData, 2); //从地址为0x50的设备的0x20寄存器读取2个字节的数据 ``` 你可以根据你的具体需求修改代码中的地址、寄存器和数据等参数。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值