打印
[AVR单片机]

TWI问题请教,开发10年也未碰到的问题。

[复制链接]
6022|22
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
老狼迈克|  楼主 | 2008-10-21 16:46 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
我利用AVR的TWI接口模仿IIC总线的EEPROM发现很奇怪的现象,我无法理解,也解决不了。


有一个动作就是MASTER读我128个连续字节的内容,当然我是SLAVE。我写入TWDR任何数据,然后发出去的最高位都为0,其余不变。即TWDR写0xff,MASTER收到的只是0x7f;同理写0x80,收到的就是0x00.

另外,读128个字节的过程中,如果我第一个字节写入0xFF,则后面的10多个字节最高位就可以正确的收到,但再朝后,无论写入TWDR什么,最高位就又变成0了。

此外,如果我在收到上个字节,启动下个字节发送之间中插入一定的延时,则数据都可以正常接收,但会丢失后面一般的数据,即只收到128个字节中的64个左右。

我百思不得其解,只好怀疑芯片损坏,请大家指教。



相关帖子

沙发
老狼迈克|  楼主 | 2008-10-21 16:55 | 只看该作者

程序

//ICC-AVR application builder : 2008-10-7 10:35:42
// Target : M8
// Crystal: 8.0000Mhz

#include <iom8v.h>
#include <macros.h>
#define NONE 0
#define RD 1
#define WR 2
#define CT 10
#define true 1
#define false 0

unsigned char DATADR,WRINDEX,status,TWIS,OKF;
unsigned char Count,tempdata,i,TEXT,m,n;

unsigned char EDID[256] = {

0x00,0xff,0xff,0xff,0xff,0xff,0xff,0x00,  0x32,0x6e,0x03,0x08,0x16,0x79,0x02,0x00,
0x2b,0x2b,0x01,0x03,0x81,0x1e,0x17,0xaa,  0xea,0xc1,0xe5,0xa3,0x57,0x4e,0x9c,0x23,

0x1d,0x50,0x54,0xbf,0xee,0x00,0x01,0x01,  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
0x01,0x01,0x01,0x01,0x01,0x01,0x64,0x19,  0x00,0x40,0x41,0x00,0x26,0x30,0x18,0x88,

0x36,0x00,0x33,0xe6,0x10,0x00,0x00,0x18,  0x00,0x00,0x00,0xff,0x00,0x20,0x42,0x5a,
0x20,0x20,0x31,0x36,0x32,0x30,0x37,0x30,  0x0a,0x20,0x00,0x00,0x00,0xfc,0x00,0x4c,

0x45,0x44,0x20,0x31,0x30,0x32,0x34,0x58,  0x37,0x36,0x38,0x0a,0x00,0x00,0x00,0xfd,
0x00,0x38,0x56,0x1e,0x45,0x0a,0x00,0x0a,  0x20,0x20,0x20,0x20,0x20,0x20,0x00,0xda,

0x02,0x03,0x19,0x01,0x4f,0x85,0x14,0x22,  0x04,0x13,0x11,0x12,0x02,0x03,0x06,0x07,
0x00,0x00,0x00,0x00,0x65,0x03,0x0c,0x00,  0x10,0x01,0x1d,0x00,0x72,0x51,0xd0,0x1e,

0x20,0x6e,0x28,0x55,0x00,0xc4,0x8e,0x21,  0x00,0x00,0x1e,0xd6,0x09,0x80,0xa0,0x20,
0xe0,0x2d,0x10,0x08,0x60,0x22,0x00,0x12,  0x8e,0x21,0x08,0x08,0x18,0x8c,0x0a,0xa0,

0x14,0x51,0xf0,0x16,0x00,0x26,0x7c,0x43,  0x00,0x13,0x8e,0x21,0x00,0x00,0x98,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,

0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1a
};

void delay()
{
 for(m=0;m<80;m++)//111
 {
       for(n=0;n<4;n++)
    {
     n++;
    }
 }
}

void port_init(void)
{
 
 PORTB = 0xFF;
 DDRB  = 0x00;
 PORTC = 0x7F; //m103 output only
 DDRC  = 0x02;//0x02
 PORTD = 0xFF;
 DDRD  = 0x00;
}

//Watchdog initialisation
// prescale: 16K cycles
void watchdog_init(void)
{
 WDR(); //this prevents a timout on enabling
 WDTCR = 0x08; //WATCHDOG ENABLED - dont forget to issue WDRs
}

//TIMER0 initialisation - prescale:1024
// WGM: Normal
// desired value: 5mSec
// actual value:  4.992mSec (0.2%)
void timer0_init(void)
{
 TCCR0 = 0x00; //stop
 TCNT0 = 0xD9; //set count
 TCCR0 = 0x05; //start timer
}

#pragma interrupt_handler timer0_ovf_isr:10
void timer0_ovf_isr(void)
{
 TCNT0 = 0xD9; //reload counter value
}

void light(void)

   PORTC &= 0xfd;//1111 1101
}



//TWI initialisation
void twi_init(void)
{
 TWCR= 0X00; //disable twi
 TWBR= 0x00; //set bit rate 
 TWAR= 0xA0; //set slave address
 TWCR= 0xC5; //enable twi 0x45
 TWSR &= 0xfe;
}

#pragma interrupt_handler twi_isr:18
void twi_isr(void)
{
 //twi event
 TWIS = TWSR & 0xf8;//读TWI状态字
 
 switch(TWIS)
 {
  case 0x60:;
  case 0x68://被控接收,收到本机SLA+W,ACK已发出
                 status = WR;
            WRINDEX = 0;
            TWCR = 0x45; 
                 break;
  case 0x80:;//被控接收,已收到本机SLA+W,data已收到,ACK已发出
                 if(WRINDEX == 0)
            {  //接收到第一写入数据为地址
               DATADR = TWDR;
               WRINDEX = 1;
            }
            else
            {
               EDID[DATADR] = TWDR;
               DATADR++;
            }
            TWCR = 0x45;
            break;
  case 0xA0:;//被控接收,STOP或repeat start已收到
                 EDID[DATADR] = TWDR;
                 WRINDEX = 0;
            status = NONE;
            TWCR = 0x45;
            //off();
            break;
  case 0xA8:;//被控发送,收到本机SLA+R,ACK已发出
                 status = RD;
            TWDR = 0x0;//EDID[DATADR];
            //TWDR = DATADR;
            DATADR++;
                 TWCR = 0x45;//下一步发送最后一个,接收ACK信号
            break;
  case 0xB8:;//被控发送,DATA发出,收到ACK信号

            TWDR = EDID[DATADR];        
            //delay();
            TWCR = 0xc5; 
            DATADR++;
                 break;
  case 0xC0:;//被控发送,data已发,收到NACK信号
                 //发送数据完成
            TWCR = 0x45;
            status = NONE;
                 break;
            
  case 0xC8:;//被控发送,最后一个data已发出(TWEA=0)收到ACK信号
                 //发送数据完成
            TWCR = 0x45;
                 break;            
  case    0:;//被控发送,data已发,收到NACK信号
                 //TWSTO 置1 从错误状态恢复
            TWAR= 0xA0; //set slave address
            TWCR |= 0x10;//0001 0000
            status = NONE;
                 break;
       
 }
 
 TWCR |= 0x80;//clear TWIINT  flag
}

//call this routine to initialise all peripherals
void init_devices(void)
{
 //stop errant interrupts until set up
 CLI(); //disable all interrupts
 port_init();
 
 
 watchdog_init();
 timer0_init();
 twi_init();

 MCUCR = 0x00;
 GICR  = 0x00;
 TIMSK = 0x01; //timer interrupt sources
 SEI(); //re-enable interrupts
 //all peripherals are now initialised

}

void main()
{

 init_devices();
  PORTC &= 0xfd;
 DATADR = 1;
 while(1)
 {
  WDR();
 }
}

使用特权

评论回复
板凳
dudongdao| | 2008-10-21 20:30 | 只看该作者

芯片坏了就换啊~ 然后试试

1.换芯片
2.用外部晶振。
3.IIC速度多少,最好低点。
4.你读的程序是模拟IIC吗?把读的程序速度降低,尤其是判断对方回应信号的时候,加长时间,或者直接等待IIC应答信号。

使用特权

评论回复
地板
dudongdao| | 2008-10-21 20:43 | 只看该作者

等待应答

等待应答的意思是你主程序要是模拟的话,你读对方应答信号前先判断一下SCL信号是否为高电平,也许AVR正在处理接收到的数据,这时候会拉住SCL,你发出读应答的信号后,等待SCL变高~ 然后再读。 

使用特权

评论回复
5
老狼迈克|  楼主 | 2008-10-22 10:06 | 只看该作者

谢谢

谢谢dudongdao,您提得建议我尝试过

1.换芯片,问题依旧
2.用外部晶振,问题依旧
3.IIC速度为20K,这个速度不快,我觉得不大可能是这个造成的。
4。我用mega8模拟被主机读取数据的IIC接口EEPROM,实际上被主机读的。主机程序绝对没问题,因为主机是标准显卡,工作正常。
  我的程序并不是模拟IIC接口的,因为TWI接口可以当作IIC接口来用,不需要模拟这个总线。

另外,您所说的:等待应答的意思是你主程序要是模拟的话,你读对方应答信号前先判断一下SCL信号是否为高电平,也许AVR正在处理接收到的数据,这时候会拉住SCL,你发出读应答的信号后,等待SCL变高~ 然后再读。
-----------------------------------------------------------------
由于我的程序不是模拟的,所以判断SCL信号之类的动作都是TWI接口自动完成的,所以您说的问题也不存在。

使用特权

评论回复
6
dudongdao| | 2008-10-22 10:46 | 只看该作者

显卡上也可能是模拟的

显卡上也有可能是模拟的IIC,不一定是标准的。

/*此外,如果我在收到上个字节,启动下个字节发送之间中插入一定的延时,则数据都可以正常接收,但会丢失后面一般的数据,即只收到128个字节中的64个左右。
*/
U8 IICRecAck(void)
{
    unsigned char bCy;
    IIC_SDA_OUT();
    IIC_SDA_HIGH();
    ShortDly(1);
    IIC_SCL_HIGH();//准备读响应信号
    ShortDly(4);//延时后读,这里按标准应该是等待SCL变高之后读。因为可能总线被对方拉住,因为对方忙,如果是24C就没问题,速度是固定的,而且快。M8的话接收到数据可能要处理,处理的时候可能拉住总线了。
    IIC_SDA_IN();
    if(IIC_SDA)
    {
        bCy=1;
    }
    else
    {
        bCy=0;
    }           
    IIC_SCL_LOW();
    return(bCy);
}

最后绝招,拿示波器看波形,自己数数,到底数送出去没有。

使用特权

评论回复
7
老狼迈克|  楼主 | 2008-10-22 11:51 | 只看该作者

dudongdao 真是明白人

我就是拿示波器看的,搞开发时间长了,什么问题都不相信,原始信号最可信。
原始信号就是最高位为0。

我发现只要上一个字节最高位为0, 这个字节以后所有的数据最高都为0,如果上个字节的最高为1,则这个字节的最高位就可以为1。

比如第一个被读取字节如果是0X80的话,则第二个字节就可以输出为0XFF,0X80 之类的,然后一直到某个字节最高位为0,则以后的字节所有最高位输出都为0。



个人感觉应该是SDA没来得及释放,被强行拉低了,但我所做的基本上都作了,不明白为什么会被拉低。。。因为我是中断处理的,而进入中断处理的前提就是已经正确收到显卡发出的ACK信号,否则TWI的状态玛不会正确的,可能显卡发出ACK后过了很久不释放SDA。。。。

使用特权

评论回复
8
老狼迈克|  楼主 | 2008-10-22 11:57 | 只看该作者

补上:您说的很有道理,我再仔细看看。。。

否则没有办法解释了

使用特权

评论回复
9
农民讲习所| | 2008-10-22 12:00 | 只看该作者

在I2C连线中串个小电阻,看电阻电压,看是那边拉低

使用特权

评论回复
10
老狼迈克|  楼主 | 2008-10-22 12:07 | 只看该作者

农民讲习所真是聪明

农民讲习所真是聪明,这个方法我怎么就没想到了。。。满脑袋的都是寄存器设置是否正确,脑子浆住了。。。谢谢

使用特权

评论回复
11
dudongdao| | 2008-10-22 12:35 | 只看该作者

看来是硬件问题了

农民高招~   还有一点~
用外部上拉了吗,用了就把内部的去掉。拉一次就够。

使用特权

评论回复
12
老狼迈克|  楼主 | 2008-10-22 17:12 | 只看该作者

问题依然解决不了

MEGA8用作被动发送模式下,即模仿被主机读取数据的IIC接口的24LC22A(EEPROM)。

首先排除SCL被拉低的可能性,因为无论数据正确与否,示波器测量的每个字节数据对应的SCL都是9个,所以不存在哪个被拉低导致出错的可能性,也就是说SCL工作时正常的。

SDA串了个电阻,发现是MEGA8在发送最高位的时候自己将SDA拉低,无论最高位是否为0,我想不明白。


另外:dudongdao所提到的:


延时后读,这里按标准应该是等待SCL变高之后读。因为可能总线被对方拉住,因为对方忙,如果是24C就没问题,速度是固定的,而且快。M8的话接收到数据可能要处理,处理的时候可能拉住总线了。
-------------------------------------------------
我认为不存在这个问题,因为您说的这个指的是能否正确接收ACK信号,而TWI接口这个动作是自动执行的,且进入中断程序后,对应的状态码就表示已经正确的收到ACK信号,

所以不是ACK接收问题,而是主机送出ACK响应后,SDA应该被释放,但实际情况是,主机对SD释放后,从机却将其继续拉低。我无法解释这个现象。

使用特权

评论回复
13
农民讲习所| | 2008-10-22 17:59 | 只看该作者

为什么是TWCR = 0xc5不是0X45?

case 0xB8:;//被控发送,DATA发出,收到ACK信号

            TWDR = EDID[DATADR];        
            //delay();
            TWCR = 0xc5; 

使用特权

评论回复
14
老狼迈克|  楼主 | 2008-10-23 09:07 | 只看该作者

答13楼

农民讲习所 发表于 2008-10-22 17:59 AVR 单片机 ←返回版面    

13楼: 为什么是TWCR = 0xc5不是0X45? 

case 0xB8:;//被控发送,DATA发出,收到ACK信号

            TWDR = EDID[DATADR];        
            //delay();
            TWCR = 0xc5;
------------------------------------------
开始用45是考虑程序的可观性,这样无论状态码是什么,TWCR中的TWINT位可以在中断程序的最后统一被清零,启动TWI发送。

后来用0xc5是发现问题后,把启动发送提前几步,这样在发送完上一个数据收到主机的响应可以更迅速的发出下一个数据。

但根据现实结果来看,没有任何区别。  
 

使用特权

评论回复
15
农民讲习所| | 2008-10-23 09:44 | 只看该作者

俺看了MEGA8的TWI资料

缺乏连续读、写部分。是否不支持连续读写数据?

使用特权

评论回复
16
hotpower| | 2008-10-23 09:54 | 只看该作者
17
老狼迈克|  楼主 | 2008-10-23 10:47 | 只看该作者

答15楼

俺看了MEGA8的TWI资料 

缺乏连续读、写部分。是否不支持连续读写数据? 
 ---------------------------------------------


根据TWI接口的状态码可以推断 是支持的,因为状态码有分为是否发送最后一个字节的状态码。

可能是我时钟速度不够快,我刚出中断释放掉SCL,马上SCL就被拉高,变成了第一个SCL时钟周期的高电平部分。正常情况下,因为SCL继续保持低电平一段时间,让数据更新在低电平时间完成的。现在由于数据更新必须在SCL低电平时更新,导致第一个被送出去的位(即最高位)无法有效更新,就直接等同于上一个字节响应后SDA的状态了。

使用特权

评论回复
18
农民讲习所| | 2008-10-23 11:07 | 只看该作者

有可能

你可以对程序专门做个优化:case判断,把0xb8放到最前面。

使用特权

评论回复
19
dudongdao| | 2008-10-23 11:31 | 只看该作者

是程序问题吗,还是硬件,先确定吧

没有好的板子吗,你做十年了,板子一大堆了吧,找个以前用过可以的,试试~ 看到底程序还是硬件,还有自己写一个读的程序,别用显卡,控制一下速度,看行不行。

使用特权

评论回复
20
农民讲习所| | 2008-10-23 12:01 | 只看该作者

可能:

ACK到开始下个发送,TWI中断是最短的时间,处理速度可能更不上。可以先将MEGA8提高到16M测试或将MASTER速度降下来测试,确认这点。

解决建议:
用汇编写中断处理。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

12

主题

102

帖子

0

粉丝