软件模拟IIC通信(STM32)

        在学习STM32的时候,难免会使用IIC通信,在STM32中有硬件IIC和软件模拟IIC,ST公司为了防止专利,就直自己设计一套硬件IIC,但是个人感觉不太好用哎,还是喜欢软件模拟IIC,有两个原因:

①:硬件IIC通信接口只有两个:I2C1和I2C2。接口太少了。

②:得是固定的那几个接口,不太灵活。

下面我给大家完整介绍一下IIC通信过程:

        IIC软件模拟通信一共需要两根线,SDA和SCL,SDA是数据线,专门传输数据;SCL是时钟线,专门是控制作用。

        IIC通信是半双工通信(就一根数据线),通信过程是双向的,MCU既可以发送给外设控制信号,外设也可以发送数据给MCU。所以就得控制SDA是输出模式还是输入模式。

IIC通信的大致流程:

1、发送开始信号;

2、发送控制指令;(控制指令可以是多个)

3、等待应答;(MCU向外设发送控制指令,发送完成后,外设给一个应答信号)

4、停止信号;

下面是控制信号时序:

(1) 起始信号和停止信号:

        开始信号: SCL 为高电平时, SDA 由高电平向低电平跳变,开始传送数据。

        结束信号: SCL 为高电平时, SDA 由低电平向高电平跳变,结束传送数据。
(2) 数据的有效性:
        IIC 总线进行数据传送时,SDA 数据线上的数据必须在时钟线 SCL 的高电平期间保持稳定,数据线的电平状态只有在 SCL 线的时钟信号为低电平时才能改变。
(3) 数据传输
        发送到 SDA 线上的每个字节必须为 8 位,每次传输可以发送的字节数量可以不受限
制,但每个字节传输结束后必须跟一个应答信号,首先传送的是数据的最高位。
(4) 应答信号:
        发送器每发送一个字节,就在时钟脉冲 9 期间释放数据线,由接收器反馈一个应答信号。 应答信号为低电平时,规定为有效应答位(ACK 简称应答位),表示接收器已经成功地接收了该字节;应答信号为高电平时,规定为非应答位(NACK),一般表示接收器接收该字节没有成功。 对于反馈有效应答位 ACK 的要求是,接收器在第 9 个时钟脉冲之前的低电平期间将 SDA 线拉低,并且确保在该时钟的高电平期间为稳定的低电平。 如果接收器是主控器,则在它收到最后一个字节后,发送一个 NACK 信号,以通知被控发送器结束数据发送,并释放 SDA 线,以便主控接收器发送一个停止信号。

 IIC.H文件:

//先把IIC.H文件粘出来,多理解一下这个宏定义

#ifndef __OLED_H
#define __OLED_H 

#include "stm32f10x.h"

#define IIC_GPIO GPIOB
#define IIC_SCL  GPIO_Pin_6
#define IIC_SDA  GPIO_Pin_7
#define IIC_GPIO_RCC RCC_APB2Periph_GPIOB

/*设置输入输出状态*/   /* 8:表示输入;3:表示输出;28:每位GPIO由CRL4位数据控制 */
#define IIC_SDA_IN()	{IIC_GPIO->CRL&=0x0FFFFFFF;IIC_GPIO->CRL|=8<<28;}//CRL是GPIO低八位控制寄存器,每4位控制因为GPIO模式以及速度
#define IIC_SDA_OUT()	{IIC_GPIO->CRL&=0x0FFFFFFF;IIC_GPIO->CRL|=3<<28;}//CRL是GPIO低八位控制寄存器,每4位控制因为GPIO模式以及速度

//控制SDA,SCL的输出电平
#define IIC_SCL_State(n)	GPIO_WriteBit(IIC_GPIO,IIC_SCL,(BitAction)n)
#define IIC_SDA_State(n)	GPIO_WriteBit(IIC_GPIO,IIC_SDA,(BitAction)n)

#define IIC_SDA_Read	GPIO_ReadInputDataBit(IIC_GPIO,IIC_SDA)//读取SDA的值

void IIC_Start(void);
void IIC_Stop(void);
void IIC_Send_Byte(u8 dat);
void IIC_NAck(void);//不产生ACK应答
void IIC_Ack(void);//产生ACK应答
u8 IIC_WaitAck(void);
u8 CT_IIC_Read_Byte(unsigned char ack);
   


#endif

IIC.C文件

#include "iic.h"  
#include "delay.h"                  // Device header

void IIC_Init()
{
	GPIO_InitTypeDef  GPIO_InitStructure;
	RCC_APB2PeriphClockCmd(IIC_GPIO_RCC,ENABLE);
	
  GPIO_InitStructure.GPIO_Pin = IIC_SCL|IIC_SDA;	 // 端口配置
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 		 //推挽输出
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;		 //IO口速度为50MHz
  GPIO_Init(IIC_GPIO, &GPIO_InitStructure);					 //根据设定参数初始化GPIO 
	
  GPIO_SetBits(IIC_GPIO,GPIO_Pin_7|GPIO_Pin_6);					//拉高IIC数据线和时钟线	

}

//起始信号
void IIC_Start(void)
{
	IIC_SDA_OUT();
	IIC_SDA_State(1);
	IIC_SCL_State(1);
	IIC_SDA_State(0);
	IIC_SCL_State(0);
}

//结束信号
void IIC_Stop(void)
{
	IIC_SDA_OUT();
	IIC_SCL_State(1);
	IIC_SDA_State(0);
	IIC_SDA_State(1);
}


//等待应答信号到来
//返回值:1,接收应答失败
//        0,接收应答成功
u8 IIC_WaitAck(void)
{
	u8 ucErrTime=0;
	IIC_SDA_IN();      //SDA设置为输入  
	IIC_SDA_State(1); 
	IIC_SCL_State(1);
	while(IIC_SDA_Read) //等待SDA=0,产生应答信号
	{
		ucErrTime++;
		if(ucErrTime>250)
		{
			IIC_Stop();
			return 1;
		} 
	}
	IIC_SCL_State(0);
	return 0;  
} 


//IIC写入一个字节
void IIC_Send_Byte(u8 dat)
{
	u8 i;
	IIC_SDA_OUT();
	for(i=0;i<8;i++)
	{
		IIC_SCL_State(0);//将时钟信号设置为低电平
		if(dat&0x80)//将dat的8位从最高位依次写入
		{
			IIC_SDA_State(1);
    }
		else
		{
			IIC_SDA_State(0);
    }
		IIC_SCL_State(1);//将时钟信号设置为高电平
		IIC_SCL_State(0);//将时钟信号设置为低电平
		dat<<=1;
  }
}


//产生ACK应答
void IIC_Ack(void)
{
	IIC_SCL_State(0);//SCL低
	IIC_SDA_OUT();
	IIC_SDA_State(0);//SDA低
	IIC_SCL_State(1);//SCL高
	IIC_SCL_State(0);//SCL低
}


//不产生ACK应答		    
void IIC_NAck(void)
{
	IIC_SCL_State(0);//SCL低
	IIC_SDA_OUT();
	IIC_SDA_State(1);//SDA高
	IIC_SCL_State(1);//SCL高
	IIC_SCL_State(0);//SCL低
}

//读1个字节,ack=1时,发送ACK,ack=0,发送nACK 
//MCU读取数据完成后,可以发送应答,可以不发送应答  
u8 CT_IIC_Read_Byte(unsigned char ack)
{
	u8 i,receive=0;
 	IIC_SDA_IN();//SDA设置为输入
	delay_us(30);
	for(i=0;i<8;i++ )
	{ 
		IIC_SCL_State(0) ; 	    	   
		IIC_SCL_State(1);//SCL高
		receive<<=1;
		if( IIC_SDA_Read )receive++;   
	}	  				 
	if (!ack)IIC_NAck();//发送nACK
	else IIC_Ack(); //发送ACK   
 	return receive;
}

你可能感兴趣的:(博客,大数据)