自己一直在使用的模拟I2C/IIC底层函数
自己在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,转载时请注明出处及相应链接。