STM32之IIC总结

IIC串行通信协议,是一种同步、半双工的通信方式。用于连接微控制器和外部设备,如传感器、存储器、显示器等,是一种简单灵活的通信协议。

IIC的基本原理

1、双线制:串行时钟线(SCL)和串行数据线(SDA)。SDA上的数据在SCL的时钟信号下进行同步传输。

2、主从结构:IIC通信中,设备分为主设备(通常是微控制器)和从设备(如传感器、显示器等),主设备发送通信和控制通信的时序,从设备响应主设备的指令。

IIC通信基本过程

1、开始信号:SDA 线从高电平转为低电平,而SCL线保持高电平。

2、发送地址和读写位:主设备发送目标设备的地址,并指定是读取数据还是写入数据。

3、接收应答:从设备在接收到正确的地址后,会在下一个时钟脉冲期间将SDA拉低,发送一个应答(ACK)信号。

4、数据传输:主设备和从设备根据读写位的设置进行数据传输。在写操作中,主设备发送数据;在读操作中,从设备发送数据。

5、发送应答:在每次字节传输后,接收方(无论是主设备还是从设备)需要发送一个应答信号。如果接收方准备好接收下一个字节,它会发送ACK;如果传输应该停止,它会发送NACK

6、重复开始条件:如果主设备想要在不释放SDA和SCL线的情况下改变传输方向(从写变读或从读变写),它会发送一个重复开始条件。和开始信号类似。如可以在写入操作后立即发送重复开始条件,随后发送从设备的地址和读操作的位。

7、停止信号:主设备通过在SCL为高电平时将SDA从低电平拉高到高电平,结束当前的I2C通信。这表示通信的结束。

 IIC时序

开始和停止信号

1、当SCL线是高电平时,SDA线从高电平向低电平切换,表示通讯的开始。

2、当SCL线是高电平时,SDA线从低电平向高电平切换,表示通讯的停止。

开始和停止信号一般由主机产生。

应答信号

IIC的数据和地址传输都带响应,响应包扣“应答(ACK)”和“非应答(NACK)"两种信号。

作为数据接收端时,当设备(无论住从机)接收到IIC传输的一个字节数据或地址后,如果希望对方继续发送数据,则需要对方发送一个应答“ACK”信号,发送方就会继续发送下一个数据。

若接收方希望结束数据传输,则需要对方发送非应答信号“NACK”,发送方接收到该信号后就会产生一个停止信号,结束信号传输。

 传输时主机产生时钟,数据发送端会释放SDA的控制权,由数据接收端控制SDA,若SDA为高电平,表示非应答信号(NACK),低电平表示应答信号(ACK)。

 数据有效性

IIC使用SDA信号来传输数据,使用SCL信号线进行数据同步,SDA数据线在SCL的每个时钟周期传输一位数据。

传输时,

当SCL为高电平时,SDA表示的数据有效,即在此时的SDA为高电平时表示数据1,低电平时表示数据0。

当SCL为低电平时,SDA表示的数据无效,一般在这个时候进行SDA的电平切换,为下一次的表示数据做好准备。

每次数据传输都以字节为单位,每次传输的字节数不受限制。

 软件模拟IIC协议

软件IIC优点:1、灵活性 2、适用性 3、移植性

 iic.h

#ifndef __OLED_IIC_H
#define __OLED_IIC_H


#define OLED_RCC_IIC_SCL   	RCC_AHB1Periph_GPIOE //端口时钟
#define OLED_IIC_SCL_PORT  	GPIOE				   //端口号
#define OLED_IIC_SCL 	  	GPIO_Pin_3		   //引脚

#define OLED_RCC_IIC_SDA   	RCC_AHB1Periph_GPIOE
#define	OLED_IIC_SDA_PORT  	GPIOE
#define	OLED_IIC_SDA 	  	GPIO_Pin_4

//io操作

#define OLED_IIC_SCL_H     GPIO_SetBits(OLED_IIC_SCL_PORT,OLED_IIC_SCL);  //SCL置1
#define	OLED_IIC_SCL_L     GPIO_ResetBits(OLED_IIC_SCL_PORT,OLED_IIC_SCL);//SCL置0

#define OLED_IIC_SDA_H     GPIO_SetBits(OLED_IIC_SDA_PORT,OLED_IIC_SDA);  //SDA置1
#define	OLED_IIC_SDA_L     GPIO_ResetBits(OLED_IIC_SDA_PORT,OLED_IIC_SDA);//SDA置0

#define OLED_READ_SDA 	  GPIO_ReadInputDataBit(OLED_IIC_SDA_PORT,OLED_IIC_SDA)//读取SDA输入引脚电平

void OLED_IIC_GPIO_Init(void);
void OLED_IIC_SDA_OUT(void);
void OLED_IIC_SDA_IN(void);
void OLED_IIC_Start(void);
void OLED_IIC_Stop(void);
void OLED_IIC_ACK(void);
void OLED_IIC_NACK(void);
void OLED_IIC_SendByte(uint8_t data);
uint8_t OLED_IIC_ReadByte(uint8_t ack);
uint8_t OLED_IIC_WaitACK(void);

 iic.c

#include "iic.h"

void OLED_IIC_GPIO_Init(void)
{
	GPIO_InitTypeDef  GPIO_InitStructure;
	
	//GPIO时钟
	RCC_AHB1PeriphClockCmd(OLED_RCC_IIC_SCL, ENABLE);
	RCC_AHB1PeriphClockCmd(OLED_RCC_IIC_SDA, ENABLE);
	
	//SCL GPIO初始化
	GPIO_InitStructure.GPIO_Pin=OLED_IIC_SCL; 
	GPIO_InitStructure.GPIO_Mode= GPIO_Mode_OUT; 
	GPIO_InitStructure.GPIO_OType=GPIO_OType_OD; //开漏输出
	GPIO_InitStructure.GPIO_Speed=GPIO_High_Speed;
	
	GPIO_Init(OLED_IIC_SCL_PORT, &GPIO_InitStructure);
	
	//SDA GPIO初始化
	GPIO_InitStructure.GPIO_Pin=OLED_IIC_SDA; 
	GPIO_InitStructure.GPIO_Mode= GPIO_Mode_OUT; 
	GPIO_InitStructure.GPIO_OType=GPIO_OType_OD; //开漏输出
	GPIO_InitStructure.GPIO_Speed=GPIO_High_Speed;
	
	GPIO_Init(OLED_IIC_SDA_PORT, &GPIO_InitStructure);
	
	OLED_IIC_SCL_H;
	OLED_IIC_SDA_H;
}

//配置SDA数据线为输出
void OLED_IIC_SDA_OUT(void)
{
	GPIO_InitTypeDef  GPIO_InitStructure;
	
	//SDA GPIO初始化
	GPIO_InitStructure.GPIO_Pin=OLED_IIC_SDA; 
	GPIO_InitStructure.GPIO_Mode= GPIO_Mode_OUT; 
	GPIO_InitStructure.GPIO_OType=GPIO_OType_OD; //开漏输出
	GPIO_InitStructure.GPIO_Speed=GPIO_High_Speed;
	
	GPIO_Init(OLED_IIC_SDA_PORT, &GPIO_InitStructure);
}


//配置SDA数据线为输入
void OLED_IIC_SDA_IN(void)
{
	GPIO_InitTypeDef  GPIO_InitStructure;
	
	//SDA GPIO初始化
	GPIO_InitStructure.GPIO_Pin=OLED_IIC_SDA; 
	GPIO_InitStructure.GPIO_Mode= GPIO_Mode_IN; 
	GPIO_InitStructure.GPIO_Speed=GPIO_High_Speed;
	GPIO_InitStructure.GPIO_PuPd=GPIO_PuPd_UP; //上拉,仅对输入有效
	
	GPIO_Init(OLED_IIC_SDA_PORT, &GPIO_InitStructure);
}

//IIC时序信号

//IIC开始信号
void OLED_IIC_Start(void)
{
	OLED_IIC_SDA_OUT();
	
	OLED_IIC_SCL_H;
	OLED_IIC_SDA_H;
	Delay_us(4);//让高电平保持稳定
	
	OLED_IIC_SDA_L;  // 拉低SDA
	Delay_us(4);
	OLED_IIC_SCL_L;	//钳住IIC总线,准备发送或者接收数据	
}

//IIC停止信号
void OLED_IIC_Stop(void)
{
	OLED_IIC_SDA_OUT();
	OLED_IIC_SCL_H;
	OLED_IIC_SDA_L;  //SAD低
	Delay_us(4);//等待时序稳定
	OLED_IIC_SDA_H;  //SDA高	
}

//IIC应答
void OLED_IIC_ACK(void)
{
	OLED_IIC_SDA_OUT();
	OLED_IIC_SCL_L;  //在SCL低电平的时候,SDA可以进行数据切换(1和0切换)
	OLED_IIC_SDA_L;  //SDA低电平表示应答型号
	Delay_us(1);//让电平稳定
	
	OLED_IIC_SCL_H;  //拉高SCL,表示此时SDA的数据有效
	Delay_us(1);
	OLED_IIC_SCL_L;  //拉低SCL,表示SCL一个周期结束
}

//IIC非应答
void OLED_IIC_NACK(void)
{
	OLED_IIC_SDA_OUT();
	OLED_IIC_SCL_L;  //在SCL低电平的时候,SDA可以进行数据切换(1和0切换)
	OLED_IIC_SDA_H;  //SDA高电平表示非应答型号
	Delay_us(1);//让电平稳定
	
	OLED_IIC_SCL_H;  //拉高SCL,表示此时SDA的数据有效
	Delay_us(1);
	OLED_IIC_SCL_L;  //拉低SCL,表示SCL一个周期结束
}

//IIC等待应答 ,返回0表示应答,返回1表示非应答
uint8_t OLED_IIC_WaitACK(void)
{
	OLED_IIC_SDA_IN();
	
	uint8_t temp=0;
	OLED_IIC_SDA_H;
	Delay_us(1);
	OLED_IIC_SCL_H;
	Delay_us(1);
	while(OLED_READ_SDA)
	{
		temp++;
		if(temp>250)
		{
			OLED_IIC_Stop();
			return 1;
		}
	}
	OLED_IIC_SCL_L;
	return 0;
	
}

//IIC发送一个字节
void OLED_IIC_SendByte(uint8_t data)
{
	OLED_IIC_SDA_OUT();
	OLED_IIC_SCL_L; //在SCL低电平的时候,SDA可以进行数据切换(1和0切换)
	
	uint8_t i=0;
	for(i=0;i<8;i++)
	{
		if((data&0x80)>0)   //0x80  1000 0000  
		{
			OLED_IIC_SDA_H;
		}
		else
		{
			OLED_IIC_SDA_L;
		}
		OLED_IIC_SCL_H;  //拉高SCL ,数据有效
		Delay_us(1); //延时,将数据发送出去
		
		OLED_IIC_SCL_L;
		Delay_us(1);
		data<<=1;
	}
}


//IIC读取一个字节
uint8_t OLED_IIC_ReadByte(uint8_t ack) //ack 1 应答 0 非应答
{
	OLED_IIC_SDA_IN();
	
	uint8_t i=0;
	uint8_t receive=0;
	
	for(i=0;i<8;i++)
	{
		OLED_IIC_SCL_L;
		Delay_us(1);
		OLED_IIC_SCL_H;
		
		receive<<=1; 
		if(OLED_READ_SDA)
		{
			receive++; 
		}
		Delay_us(1);
		
	}
	if(ack)
		OLED_IIC_ACK();
	else
		OLED_IIC_NACK();
	
	return receive;
	
}

  • 12
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值