STM32-IIC通信解析

一、IIC通信
I2C,两线式串行总线,由数据线SDA和时钟SCL构成的穿行总线,可发送和接受数据。在CPU与被控IC之间、IC与IC之间进行双向传送,高速IIC总线一般可达400kbps以上。半双工通信方式
二、IIC协议

1. 空闲状态:I2C总线,总线的SDA和SCL同时处于高电平时,规定为总线的空闲状态。
2. 起始信号和停止信号:

  • 起始信号:当SCL为高期间,SDA由高到低的跳变;启动信号是一种电平跳变时序信号,而不是一个电平信号。

  • 停止信号:当SCL为高期间,SDA由低到高的跳变;停止信号也是一种电平跳变时序信号,而不是一个电平信号。
    在这里插入图片描述

  • 代码解析

void IIC_Start(void)
{
	
	SDA_H;						
	SCL_H;						
	DelayUs(iic_info.speed);	//延时,速度控制
	
	SDA_L;						//当SCL线为高时,SDA线一个下降沿代表开始信号
	DelayUs(iic_info.speed);	
	SCL_L;						//钳住SCL线,以便发送数据	

}
void IIC_Stop(void)
{

	SDA_L;						
	SCL_L;						
	DelayUs(iic_info.speed);	
	
	SCL_H;						
	SDA_H;						//拉高SDA线,当SCL为高时,SDA线一个上升沿代表停止
	DelayUs(iic_info.speed);

}

3. 应答信号ACK

  • 发送器每发送一个字节(8位),就在时钟脉冲9期间释放数据线,由接收器反馈一个应答信号。应答信号为低电平时,规定为有效应答位(ACK简称应答为)表示接收器成功接收该字节;应答信号为高电平,规定为费应答位(NACK),一般表示接收器接收该字节没有成功。 ***对于反馈有效应答位ACK的要求是,接收器在第9个时钟脉冲之前的低电平期间将SDA线拉低,并且确保在该时钟的高电平期间为稳定的低电平。***如果接收器是主控器,则在它收到最后一个字节后,发送一个NACK信号,以通知被控发送器结束数据发送,并释放SDA线,以便主控接收器发送一个停止信号P
    在这里插入图片描述
  • 代码解析
void IIC_Ack(void)
{
	
	SCL_L;						
	SDA_L;						
	DelayUs(iic_info.speed);
	SCL_H;						
	DelayUs(iic_info.speed);
	SCL_L;						
	
}
void IIC_NAck(void)
{
	
	SCL_L;						
	SDA_H;						
	DelayUs(iic_info.speed);
	SCL_H;						
	DelayUs(iic_info.speed);
	SCL_L;						
	
}

4.数据有效性

  • I2C总线进行数据传送时,时钟信号为高电平期间,数据线上的数据必须保持稳定,只有在时钟线上的信号为低电平期间,数据上的高电平或低电平状态才允许变化。
  • 即数据在SCL的上升沿到来之前就需要准备好。并在下降沿来到之前必须稳定
    在这里插入图片描述

5.数据的传送

  • 在I2C总线上传送的每一位数据都有一个时钟脉冲相对应(或同步控制),即在SCL串行时钟的配合下,在SDA上逐位地串行传送每一位数据。数据位的传输是边沿触发。具体的数据传输方式可以看https://www.cnblogs.com/aaronLinux/p/6218660.html
  • 代码解析结合上图
void IIC_SendByte(unsigned char byte)
{

	unsigned char count = 0;
	
    SCL_L;							//拉低时钟开始数据传输
	
    for(; count < 8; count++)		//循环8次,每次发送一个bit
    {
		if(byte & 0x80)				//发送最高位
			SDA_H;
		else
			SDA_L;
		
		byte <<= 1;					//byte左移一位
		
		DelayUs(iic_info.speed);
		SCL_H;
		DelayUs(iic_info.speed);
		SCL_L;
    }

}

三、代码
1.i2c.c

#include "stm32f10x.h"

#include "i2c.h"
#include "delay.h"
#include "usart.h"

IIC_INFO iic_info;
/*
************************************************************
*	函数名: IIC_SpeedCT1
*
*	函数功能:	软件IIC速度控制
*
*	入口参数:	speed:延时参数
*
*	返回参数:	无
*
*	说明:		单位:微妙
************************************************************
*/
void IIC_SpeedCtl(unsigned short speed)
{
	iic_info.speed = speed;
}
/*
************************************************************
*	函数名:	IIC_Init
*
*	函数功能:	软件IIC总线IO初始化
*
*	入口参数:	无
*
*	返回参数:	无
*
*	说明:使用开漏方式,不用切换IO口输入输出方向
************************************************************
*/
void IIC_Init(void)
{

	GPIO_InitTypeDef gpio_initstruct;
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	
	gpio_initstruct.GPIO_Mode = GPIO_Mode_Out_OD;	//开漏,不用切换出入输出方向		
	gpio_initstruct.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11;
	gpio_initstruct.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &gpio_initstruct);
	
	IIC_SpeedCtl(5);
	
	SDA_H;			//拉高SDA线,处于空闲状态									
	SCL_H;			//拉高SCL,处于空闲状态										

}

/*
************************************************************
*	函数名: IIC_Start
*
*	函数功能:	软件IIC开始信号
*
*	入口参数:	无
*
*	返回参数:	无
*
*	说明:		
************************************************************
*/
void IIC_Start(void)
{
	
	SDA_H;						
	SCL_H;						
	DelayUs(iic_info.speed);	//延时,速度控制
	
	SDA_L;						//当SCL线为高时,SDA线一个下降沿代表开始信号
	DelayUs(iic_info.speed);	
	SCL_L;						//钳住SCL线,以便发送数据	

}

/*
************************************************************
*	函数名: IIC_Stop
*
*	函数功能:	软件IIC停止信号
*
*	入口参数:	无
*
*	返回参数:	无
*
*	说明:		
************************************************************
*/
void IIC_Stop(void)
{

	SDA_L;						
	SCL_L;						
	DelayUs(iic_info.speed);	
	
	SCL_H;						
	SDA_H;						//拉高SDA线,当SCL为高时,SDA线一个上升沿代表停止
	DelayUs(iic_info.speed);

}

/*
************************************************************
*	函数名: IIC_SpeedCT1
*
*	函数功能:	软件IIC等待应答
*
*	入口参数:	timeOut:超时时间
*
*	返回参数:	无
*
*	说明:		单位:微妙
************************************************************
*/
_Bool IIC_WaitAck(unsigned int timeOut)
{
	
	
	SDA_H;DelayUs(iic_info.speed);			
	SCL_H;DelayUs(iic_info.speed);			
	
	while(SDA_R)							//如果读到SDA线为1,等待,应答信号应是0
	{
		if(--timeOut == 0)
		{
			UsartPrintf(USART1, "WaitAck TimeOut\r\n");

			IIC_Stop();						//超时未收到应答,则停止总线
			
			return IIC_Err;					//返回失败
		}
		
		DelayUs(iic_info.speed);
	}
	
	SCL_L;									
	
	return IIC_OK;							//返回成功
	
}

/*
************************************************************
*	函数名: IIC_ACK
*
*	函数功能:	软件IIC产生一个应答
*
*	入口参数:	无
*
*	返回参数:	无
*
*	说明:		当SDA线为低时,SCL线一个正脉冲代表发送一个应答信号
************************************************************
*/
void IIC_Ack(void)
{
	
	SCL_L;						
	SDA_L;						
	DelayUs(iic_info.speed);
	SCL_H;						
	DelayUs(iic_info.speed);
	SCL_L;						
	
}

/*
************************************************************
*	函数名: IIC_ACK
*
*	函数功能:	软件IIC产生一个非应答
*
*	入口参数:	无
*
*	返回参数:	无
*
*	说明:		当SDA线为高时,SCL线一个正脉冲代表发送一个非应答信号
************************************************************
*/
void IIC_NAck(void)
{
	
	SCL_L;						
	SDA_H;						
	DelayUs(iic_info.speed);
	SCL_H;						
	DelayUs(iic_info.speed);
	SCL_L;						
	
}

/*
************************************************************
*	函数名: IIC_SendByte
*
*	函数功能:	软件IIC发送一个字节
*
*	入口参数:	byte:需要发送的字节
*
*	返回参数:	无
*
*	说明:		
************************************************************
*/
void IIC_SendByte(unsigned char byte)
{

	unsigned char count = 0;
	
    SCL_L;							//拉低时钟开始数据传输
	
    for(; count < 8; count++)		//循环8次,每次发送一个bit
    {
		if(byte & 0x80)				//发送最高位
			SDA_H;
		else
			SDA_L;
		
		byte <<= 1;					//byte左移一位
		
		DelayUs(iic_info.speed);
		SCL_H;
		DelayUs(iic_info.speed);
		SCL_L;
    }

}

/*
************************************************************
*	函数名: IIC_RecvByte
*
*	函数功能:	软件IIC接受一个字节
*
*	入口参数:	无
*
*	返回参数:	无
*
*	说明:		
************************************************************
*/
unsigned char IIC_RecvByte(void)
{
	
	unsigned char count = 0, receive = 0;
	
	SDA_H;							//拉高SDA线,开漏状态下,需线拉高以便读取数据
	
    for(; count < 8; count++ )		//循环8次,每次发送一个bit
	{
		SCL_L;
		DelayUs(iic_info.speed);
		SCL_H;
		
        receive <<= 1;				
		
        if(SDA_R)					//如果SDA线为1,则receive变量自增,每次自增都是对bit0的+1,然后下一次循环会先左移一次
			receive++;
		
		DelayUs(iic_info.speed);
    }
	
    return receive;
	
}

/*
************************************************************
*	函数名: IIC_WriteByte
*
*	函数功能:	软件IIC写一个数据
*
*	入口参数:	slaveAddr:从机地址
*				regAddr:寄存器地址
*				byte:需要写入的数据
*
*	返回参数:	0-写入成功 1-写入失败
*
*	说明:		*byte是缓存写入数据的变量的地址,因为有些寄存器只需要控制下寄存器,并不需要写入值
************************************************************
*/
_Bool I2C_WriteByte(unsigned char slaveAddr, unsigned char regAddr, unsigned char *byte)
{

	unsigned char addr = 0;

	addr = slaveAddr << 1;		//IIC地址是7bit,这里需要左移1位,bit0:1-读 0-写
	
	IIC_Start();				//起始信号
	
	IIC_SendByte(addr);			//发送设备地址(写)
	if(IIC_WaitAck(5000))		//等待应答
		return IIC_Err;
	
	IIC_SendByte(regAddr);		//发送寄存器地址
	if(IIC_WaitAck(5000))		//等待应答
		return IIC_Err;
	
	if(byte)
	{
		IIC_SendByte(*byte);	//发送数据
		if(IIC_WaitAck(5000))	//等待应答
			return IIC_Err;
	}
	
	IIC_Stop();					//停止信号
	
	return IIC_OK;

}

/*
************************************************************
*	函数名: IIC_ReadByte
*
*	函数功能:	软件IIC写一个数据
*
*	入口参数:	slaveAddr:从机地址
*				regAddr:寄存器地址
*				byte:需要写入的数据
*
*	返回参数:	0-写入成功 1-写入失败
*
*	说明:		*byte是缓存写入数据的变量的地址,因为有些寄存器只需要控制下寄存器,并不需要写入值
************************************************************
*/
_Bool I2C_ReadByte(unsigned char slaveAddr, unsigned char regAddr, unsigned char *val)
{

	unsigned char addr = 0;

    addr = slaveAddr << 1;		//IIC地址是7bit,这里需要左移1位,bit0:1-读 0-写
	
	IIC_Start();				//起始信号
	
	IIC_SendByte(addr);			//发送设备地址(写)
	if(IIC_WaitAck(5000))		//等待应答
		return IIC_Err;
	
	IIC_SendByte(regAddr);		//发送寄存器地址
	if(IIC_WaitAck(5000))		//等待应答
		return IIC_Err;
	
	IIC_Start();				//重启信号
	
	IIC_SendByte(addr + 1);		//发送设备地址(读)
	if(IIC_WaitAck(5000))		//等待应答
		return IIC_Err;
	
	*val = IIC_RecvByte();		//接收
	IIC_NAck();					//产生一个非应答信号,代表读取接收
	
	IIC_Stop();					//停止信号
	
	return IIC_OK;

}

/*
************************************************************
*	函数名: IIC_WriteBytes
*
*	函数功能:	软件IIC写多个数据
*
*	入口参数:	slaveAddr:从机地址
*				regAddr:寄存器地址
*				byte:需要写入的数据
*
*	返回参数:	0-写入成功 1-写入失败
*
*	说明:		*buf是一个数组或指向一个缓存区的指针
************************************************************
*/
_Bool I2C_WriteBytes(unsigned char slaveAddr, unsigned char regAddr, unsigned char *buf, unsigned char num)
{

	unsigned char addr = 0;

	addr = slaveAddr << 1;		//IIC地址是7bit,这里需要左移1位,bit0:1-读 0-写
	
	IIC_Start();				//起始信号
	
	IIC_SendByte(addr);			//发送设备地址(写)
	if(IIC_WaitAck(5000))		//等待应答
		return IIC_Err;
	
	IIC_SendByte(regAddr);		//发送寄存器地址
	if(IIC_WaitAck(5000))		//等待应答
		return IIC_Err;
	
	while(num--)				//循环写入数据
	{
		IIC_SendByte(*buf);		//发送数据
		if(IIC_WaitAck(5000))	//等待应答
			return IIC_Err;
		
		buf++;					//数据指针偏移到下一个
		
		DelayUs(10);
	}
	
	IIC_Stop();					//停止信号
	
	return IIC_OK;

}

/*
************************************************************
*	函数名: IIC_ReadBytes
*
*	函数功能:	软件IIC读多个数据
*
*	入口参数:	slaveAddr:从机地址
*				regAddr:寄存器地址
*				byte:需要写入的数据
*
*	返回参数:	0-写入成功 1-写入失败
*
*	说明:		*buf是一个数组或指向一个缓存区的指针
************************************************************
*/
_Bool I2C_ReadBytes(unsigned char slaveAddr, unsigned char regAddr, unsigned char *buf, unsigned char num)
{

	unsigned short addr = 0;

    addr = slaveAddr << 1;		//IIC地址是7bit,这里需要左移1位,bit0:1-读 0-写
	
	IIC_Start();				//起始信号
	
	IIC_SendByte(addr);			//发送设备地址(写)
	if(IIC_WaitAck(5000))		//等待应答
		return IIC_Err;
	
	IIC_SendByte(regAddr);		//发送寄存器地址
	if(IIC_WaitAck(5000))		//等待应答
		return IIC_Err;
	
	IIC_Start();				//循环写入数据
	
	IIC_SendByte(addr + 1);		//发送设备地址(读)
	if(IIC_WaitAck(5000))		//等待应答
		return IIC_Err;
	
	while(num--)
	{
		*buf = IIC_RecvByte();
		buf++;					//偏移下一个数据存储地址
		
		if(num == 0)
        {
           IIC_NAck();			//最后一个数据需要回NOACK
        }
        else
        {
          IIC_Ack();			//回应ACK
		}
	}
	
	IIC_Stop();
	
	return IIC_OK;

}

2.i2c.h

#ifndef _I2C_H_
#define _I2C_H_





#define IIC_OK		0

#define IIC_Err		1


//SDA		PB11
//SCL		PB10
#define SDA_H	GPIO_SetBits(GPIOB, GPIO_Pin_11)
#define SDA_L	GPIO_ResetBits(GPIOB, GPIO_Pin_11)
#define SDA_R	GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11)

#define SCL_H	GPIO_SetBits(GPIOB, GPIO_Pin_10)
#define SCL_L	GPIO_ResetBits(GPIOB, GPIO_Pin_10)


typedef struct
{

	unsigned short speed;

} IIC_INFO;

extern IIC_INFO iic_info;


void IIC_Init(void);

void IIC_SpeedCtl(unsigned short speed);

_Bool I2C_WriteByte(unsigned char slaveAddr, unsigned char regAddr, unsigned char *byte);

_Bool I2C_ReadByte(unsigned char slaveAddr, unsigned char regAddr, unsigned char *val);

_Bool I2C_WriteBytes(unsigned char slaveAddr, unsigned char regAddr, unsigned char *buf, unsigned char num);

_Bool I2C_ReadBytes(unsigned char slaveAddr, unsigned char regAddr, unsigned char *buf, unsigned char num);

void IIC_Start(void);

void IIC_Stop(void);

_Bool IIC_WaitAck(unsigned int timeOut);

void IIC_Ack(void);

void IIC_NAck(void);

void IIC_SendByte(unsigned char byte);

unsigned char IIC_RecvByte(void);


#endif
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值