I2C模拟通信 从器件TCN75A无任何响应

qq_24751415 2015-07-27 01:45:55
单片机用的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();
}
...全文
253 9 打赏 收藏 转发到动态 举报
写回复
用AI写文章
9 条回复
切换为时间正序
请发表友善的回复…
发表回复
qq_24751415 2015-08-11
  • 打赏
  • 举报
回复
再补充一句,后来又试了下,其实原来的方法也是可行的。但是我读取的是什么啊,是什么啊,是LAT啊,自然是什么也读不到了……
qq_24751415 2015-07-28
  • 打赏
  • 举报
回复
使用51的试了一下,畅通无阻,说明串口协议和时序并无问题,一定是端口的问题。现在明白了。 虽然使用的SSP模块的引脚,而且设置了开漏,但貌似那个开漏也只是在SSP模块可使用时开生效。 可以使用普通I/O口,但思路并不是输出时调为输出,然后更改PORT或LAT,需要接受时再改为输入。 而是,应该将LAT设为0,加上拉,这样的效果,就是设为输入,则输出1(因为上拉,其实输出1的意思,在I2C中,就是不起作用,因为任一方拉低则拉低),设为输出,则输出0。就是输出的数值实际从未变过。在设为输入时,通过读取PORT的状态,则可以知道总线上是0或者1,因为任一方拉低则低。 #define SDA_OUT TRISC0 #define SCK_OUT TRISC1 #define Read_SDA PORTC0 TRISC0=0; TRISC1=0;
mangoalx 2015-07-27
  • 打赏
  • 举报
回复
面包板分布电容很大,所以降低I2C的时钟频率试试。高速信号恐怕不行。单片机时钟也可能过高,跟踪程序可以运行吗
worldy 2015-07-27
  • 打赏
  • 举报
回复
引用 4 楼 qq_24751415 的回复:
[quote=引用 2 楼 worldy 的回复:] 做了上拉和电容去耦?去什么耦?去耦了,信息还传的出去?
我没说清楚,是GND和VDD之间的,可以么?[/quote] VDD到GND一般都要加 使用双宗示波器,测SDA和SCL的波形,和通常的IIC波形对比一下,是否能一致,特别关注上线沿的关系是否正确
胜负多少 2015-07-27
  • 打赏
  • 举报
回复
引用 3 楼 qq_24751415 的回复:
[quote=引用 1 楼 pppppp11 的回复:] 用双综示波器看看SDA和SCL的波形,IIC最开始是很不好调的。。。
然而并没有,要哭[/quote] 连示波器都没还怎么玩。。。
qq_24751415 2015-07-27
  • 打赏
  • 举报
回复
引用 2 楼 worldy 的回复:
做了上拉和电容去耦?去什么耦?去耦了,信息还传的出去?
我没说清楚,是GND和VDD之间的,可以么?
qq_24751415 2015-07-27
  • 打赏
  • 举报
回复
引用 1 楼 pppppp11 的回复:
用双综示波器看看SDA和SCL的波形,IIC最开始是很不好调的。。。
然而并没有,要哭
worldy 2015-07-27
  • 打赏
  • 举报
回复
做了上拉和电容去耦?去什么耦?去耦了,信息还传的出去?
胜负多少 2015-07-27
  • 打赏
  • 举报
回复
用双综示波器看看SDA和SCL的波形,IIC最开始是很不好调的。。。

27,375

社区成员

发帖
与我相关
我的任务
社区描述
硬件/嵌入开发 单片机/工控
社区管理员
  • 单片机/工控社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧