自己一直在使用的模拟I2C/IIC底层函数

作者:Joey 分类: 嵌入式 发布于:2016-4-23 18:44 ė4176次浏览 60条评论

自己在51和STM32上面都验证过,需要外部接上拉电阻,STM32中需要设置为开漏输出.

/* Private typedef -----------------------------------------------------------*/
typedef enum {FALSE = 0, TRUE = !FALSE} bool; 


/* Private define ------------------------------------------------------------*/
#define I2C_SLAVE_ADDRESS7		0x3F	//注意此vi2c底层的从器件地址是7bit,底层函数会左移加上最后1bit是Read or Write

#define I2C_SCL_H()     I2C_SCL = 1
#define I2C_SCL_L()     I2C_SCL = 0

#define I2C_SDA_H()     I2C_SDA = 1
#define I2C_SDA_L()     I2C_SDA = 0

#define I2C_SCL_read    I2C_SCL
#define I2C_SDA_read    I2C_SDA

/* Private macro -------------------------------------------------------------*/




/* Includes ------------------------------------------------------------------*/
#include "vi2c.h"
//#include <stdio.h>
/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
/* Private function prototypes -----------------------------------------------*/
/* Private functions ---------------------------------------------------------*/



//I2C延时,调节I2C时序和速率
void I2C_delay(void)
{
	u8 i=5;
	while(i--);
}

//启动I2C总线
bool I2C_Start(void)
{
	I2C_SDA_H();
	I2C_SCL_H();
	I2C_delay();
	if(!I2C_SDA_read) return FALSE;		//SDA线为低电平则总线忙,退出
	I2C_SDA_L();
	I2C_delay();
	if(I2C_SDA_read)  return FALSE;		//SDA线为高电平则总线出错,退出
	I2C_SDA_L();
	I2C_delay();
	return TRUE;
}

//停止I2C总线
void I2C_Stop(void)
{
	I2C_SCL_L();
	I2C_delay();
	I2C_SDA_L();
	I2C_delay();
	I2C_SCL_H();
	I2C_delay();
	I2C_SDA_H();
	I2C_delay();
}

//主机给从机发送应答信号
void I2C_SendAck( void)
{
	I2C_SCL_L();
	I2C_delay();
	I2C_SDA_L();
	I2C_delay();
	I2C_SCL_H();
	I2C_delay();
	I2C_SCL_L();
	I2C_delay();
}

//主机给从机发送非应答信号,读取的最后一个字节不能发送应答信号,应该发送NoACK非应答
//I2C read最后一个字节后(从机不知何为最后字节),若主机回应ACK,从机会自动将下一字节
//准备好,并将其高位输出到SDA上。若高位为0(SDA=0),主机发不出STOP信号(SDA不能由低
//变高),总线进入死锁态。
void I2C_SendNoAck( void)
{
	I2C_SCL_L();
	I2C_delay();
	I2C_SDA_H();
	I2C_delay();
	I2C_SCL_H();
	I2C_delay();
	I2C_SCL_L();
	I2C_delay();
}

//主机接受从机的应答信号
bool I2C_WaitAck(void)	//返回为:=1有ACK,=0无ACK
{
	I2C_SCL_L();
	I2C_delay();
	I2C_SDA_H();                        
	I2C_delay();
	I2C_SCL_H();
	I2C_delay();
	if(I2C_SDA_read)
	{
		I2C_SCL_L();
		//printf("\r\nI2C_WaitAck Fail!\r\n");
		return FALSE;
	}
	I2C_SCL_L();
	return TRUE;
}

//主机发送一个字节
void I2C_SendByte(u8 SendByte)
{
	u8 i=8;
	while(i--)
	{
		I2C_SCL_L();
        I2C_delay();
      	if(SendByte&0x80)
			I2C_SDA_H();  
		else
			I2C_SDA_L();   
        SendByte<<=1;
        I2C_delay();
		I2C_SCL_H();
		I2C_delay();
	}
	I2C_SCL_L();
}

//主机读取一个字节
u8 I2C_ReceiveByte(void)  //数据从高位到低位//
{ 
	u8 i=8;
	u8 ReceiveByte=0;
	
    I2C_SDA_H();
    while(i--)
    {
		ReceiveByte<<=1;      
		I2C_SCL_L();
		I2C_delay();
		I2C_SCL_H();
		I2C_delay();
		if(I2C_SDA_read)
		{
			ReceiveByte|=0x01;
		}
	}
	I2C_SCL_L();
    return ReceiveByte;
}

/*******************************************************************************
* 名    称: I2C_BufferWrite(u8* pBuffer, u8 WriteAddr, u16 NumByteToWrite)
* 功    能: 将缓冲区中的数据写到I2C从机中
* 入口参数: - pBuffer         缓冲区指针
*           - WriteAddr       寄存器地址
*           - NumByteToWrite  要写的字节数
* 出口参数: - TURE or FALSE   成功or失败
* 说    明:
* 调用方法: I2C_BufferWrite(buf,0x00,8)
*******************************************************************************/
bool I2C_BufferWrite(u8* pBuffer, u8 WriteAddr, u16 NumByteToWrite)
{
	if (!I2C_Start()) 
	{
		//printf("\r\nI2C_Start Fail!\r\n");
		return FALSE;
	}
	I2C_SendByte((I2C_SLAVE_ADDRESS7<<1) | 0);	//I2C从地址+写标志
	if (!I2C_WaitAck())
	{
		I2C_Stop();
		return FALSE;
	}
	I2C_SendByte(WriteAddr);					//要写入的寄存器地址
	I2C_WaitAck();
	while(NumByteToWrite--)
	{
		I2C_SendByte(* pBuffer);
		I2C_WaitAck();
		pBuffer++;
	}
	I2C_Stop();
	return TRUE;
}

/*******************************************************************************
* 名    称: I2C_BufferRead(u8* pBuffer, u8 ReadAddr, u16 NumByteToRead)
* 功    能: 读取I2C从机数据数据到将缓冲区
* 入口参数: - pBuffer         缓冲区指针
*           - ReadAddr        读地址
*           - NumByteToRead   要读的字节数
* 出口参数: - TURE or FALSE   成功or失败
* 说    明:
* 调用方法: I2C_BufferRead(buf,0x00,16)
*******************************************************************************/
bool I2C_BufferRead(u8* pBuffer, u8 ReadAddr, u16 NumByteToRead)
{
	if (!I2C_Start()) return FALSE;
	I2C_SendByte((I2C_SLAVE_ADDRESS7<<1) | 0);	//I2C从地址+写标志
	if (!I2C_WaitAck()) 
	{
		I2C_Stop(); 
		return FALSE;
	}
    I2C_SendByte(ReadAddr);						//要读出的寄存器地址
	I2C_WaitAck();
	I2C_Stop(); 
	I2C_Start();
	I2C_SendByte((I2C_SLAVE_ADDRESS7<<1) | 0x01);	//I2C从地址+读标志
	I2C_WaitAck();
	while(NumByteToRead)
	{
		*pBuffer = I2C_ReceiveByte();
		if(NumByteToRead == 1)
			I2C_SendNoAck();
		else
			I2C_SendAck();
		pBuffer++;
		NumByteToRead--;
	}
	I2C_Stop();
	return TRUE;
}



/* -------------------------------- END --------------------------------------*/

本文出自 LcdBBS,转载时请注明出处及相应链接。

0

Ɣ回顶部