单片机用的PIC18F46k80,硬件上乖乖滴做了上拉和电容去耦,不过是面包板,不知有无影响。SDA和SCK是可以置1和置0的,但是从器件连ACK响应都不答应我一下啊,换了一个还是不行……我感觉我一定忽略了什么至关重要对于内行来说理所当然对于外行来说难以注意的地方,
程序如下:
#define I2C_SDA_IO TRISCbits.TRISC5
#define I2C_SCL_IO TRISCbits.TRISC3
#define MY_I2C_SDA LATCbits.LATC5
#define MY_I2C_SCL LATCbits.LATC3
void I2CDelayForSCLKH()
{
__delay_us(4);
}
void I2CDelayForDataChange()
{
__delay_us(2);
}
void I2CDelayAfterDataChange()
{
__delay_us(2);
}
void I2CStart(void)
{
I2C_SDA_IO=0; //设置SDA端口为输出端口
I2C_SCL_IO=0; //设置SCL端口为输出端口
MY_I2C_SCL=0;
MY_I2C_SDA=1;
I2CDelayForDataChange();
MY_I2C_SCL=1;
I2CDelayForDataChange();
MY_I2C_SDA=0;
I2CDelayAfterDataChange();
MY_I2C_SCL=0;
I2CDelayAfterDataChange();
}
void I2CStop(void)
{
// I2C_SDA_IO=0; //设置SDA端口为输出端口
// I2C_SCL_IO=0; //设置SCL端口为输出端口
// MY_I2C_SCL = 0;
// I2CDelayAfterDataChange();//ACK末尾做了AfterDataCHange
MY_I2C_SDA = 0;
I2CDelayForDataChange();
MY_I2C_SCL = 1;
I2CDelayForDataChange();
MY_I2C_SDA = 1;
I2CDelayAfterDataChange();
}
/*
* FunctionName: I2CFree
* Purpose: 模拟I2C空闲状态信号
* Parameters: 无
*/
void I2CFree(void)
{
// I2C_SDA_IO=0; //设置SDA端口为输出端口
// I2C_SCL_IO=0; //设置SCL端口为输出端口
MY_I2C_SDA = 1;
I2CDelayForSCLKH();
MY_I2C_SCL = 1;
I2CDelayForSCLKH();
}
/*
* FunctionName: I2CSendACK
* Purpose: 模拟I2C发送ACK响应
* Parameters: 无
*/
void I2CSendACK(void)
{
// I2C_SDA_IO=0; //设置SDA端口为输出端口
// I2C_SCL_IO=0; //设置SCL端口为输出端口
// MY_I2C_SCL=0;
// I2CDelayForDataChange();//在receive部分已置0,并等待
MY_I2C_SDA=0;
I2CDelayForDataChange();
MY_I2C_SCL=1;
I2CDelayForSCLKH();
MY_I2C_SCL=0;
I2CDelayAfterDataChange();
}
/*
* FunctionName: I2CSendNoACK
* Purpose: 模拟I2C无ACK响应
* Parameters: 无
*/
void I2CSendNoACK(void)
{
// I2C_SDA_IO=0; //设置SDA端口为输出端口
// I2C_SCL_IO=0; //设置SCL端口为输出端口
// MY_I2C_SCL=0;
// I2CDelayForDataChange();//之前接收数据时,已经是SCL置0,并等待了
MY_I2C_SDA=1;
I2CDelayForDataChange();
MY_I2C_SCL=1;
I2CDelayForSCLKH();
MY_I2C_SCL=0;
I2CDelayAfterDataChange();
}
/*
* FunctionName: I2CCheckACK
* Purpose: 检查I2C是否有ACK响应
* Parameters: 无
*/
UCHAR I2CCheckACK(void)//返回1,则说明接收到响应
{
UCHAR tempACK;
// I2C_SDA_IO=0; //设置SDA端口为输出端口
// I2C_SCL_IO=0; //设置SCL端口为输出端口
MY_I2C_SDA=1;
I2CDelayForDataChange();
MY_I2C_SCL = 1;
I2C_SDA_IO=1; //设置SDA端口为输入端口,检查Slave是否有响应
I2CDelayForSCLKH();
if(MY_I2C_SDA)
tempACK=FALSE;
else
tempACK=TRUE;
MY_I2C_SCL=0;
I2CDelayAfterDataChange();
I2C_SDA_IO=0; //设置SDA端口为输出端口
// MY_I2C_SDA=1;//没有意义
return tempACK ;
}
/*
* FunctionName: I2CSendByte
* Purpose: 模拟I2C发送一个字节数据
* Parameters: sendData-发送的一个字节数据
*/
void I2CSendByte(unsigned char sendData)
{
unsigned char serialNum = 0;
// I2C_SDA_IO=0; //设置SDA端口为输出端口
// I2C_SCL_IO=0; //设置SCL端口为输出端口
for(serialNum=8;serialNum>=1;serialNum--) //以MSB方式按位发送一个字节数据
{
MY_I2C_SDA = (sendData>>(serialNum-1))&0x01;//与start衔接无问题
I2CDelayForDataChange();
MY_I2C_SCL = 1;
I2CDelayForSCLKH();
MY_I2C_SCL = 0;
I2CDelayAfterDataChange();
}
}
/*
* FunctionName: I2CReceiveByte
* Purpose: 模拟I2C接收一个字节数据
* Parameters: 无
*/
unsigned char I2CReceiveByte(void)
{
unsigned char serialNum = 0;
unsigned char dataValue=0;
I2C_SDA_IO=1; //设置SDA端口为输入端口
I2C_SCL_IO=0; //设置SCL端口为输出端口
for(serialNum=0;serialNum<=7;serialNum++)//以MSB方式按位接收一个字节数据
{
I2CDelayForDataChange();//与start,receive,SendACK衔接无问题
MY_I2C_SCL=1;
I2CDelayForSCLKH();
if(MY_I2C_SDA)
dataValue|=(0b10000000>>serialNum);
MY_I2C_SCL=0;
I2CDelayAfterDataChange();
}
I2C_SDA_IO=0; //设置SCL端口为输出端口
return dataValue;
}
void I2CWriteToSlave(unsigned char SlaveAddress,unsigned char Reg_Num,unsigned char wrNumber,unsigned char* wrPointer)
{
UCHAR rxdACK;
I2CStart();
I2CSendByte(SlaveAddress); //发送器件地址,地址LSB最后一位为0代表写入,1代表读取
rxdACK=I2CCheckACK();
I2CSendByte(Reg_Num); //发送指针
rxdACK=I2CCheckACK();
for(;wrNumber!=0;wrNumber--,wrPointer++)
{
I2CSendByte(*wrPointer); //按字节写入数据
rxdACK=I2CCheckACK();
}
I2CStop();
}
void I2CReadFromSlave(unsigned char SlaveAddress,unsigned char Reg_Num,unsigned char rdNumber,unsigned char* rdPointer)
{
UCHAR rxdACK;
I2CStart();
I2CSendByte(SlaveAddress); //发送器件地址
rxdACK=I2CCheckACK();
I2CSendByte(Reg_Num); //发送寄存器指针
rxdACK=I2CCheckACK();
// I2CStop();
I2CStart();
I2CSendByte(SlaveAddress); //发送器件地址
rxdACK=I2CCheckACK();
for(;rdNumber!=0;rdNumber--,rdPointer++)
{
*rdPointer=I2CReceiveByte(); //按字节读取数据
if(rdNumber!=1)
I2CSendACK();
else
I2CSendNoACK();
}
I2CStop();
}