gxlujd 发表于 2006-10-30 22:13:28

TWI主从机实验,卡死在主机的while ((TWCR & _BV(TWINT)) == 0);语句

近日做TWI主从机实验,主机和从机都是M16,2.2K外部上拉。主机软件使用芯艺例程里的;从机仅打开TWI接口,使能TWI中断,置位TWCR寄存器的TWEN、TWEA、TWINT、TWIE位,并设置了从机地址为0000001,允许广播识别。



出现的问题是,主机发送STARTC成功(返回0x08),接着发送SLA+W的时候,没有返回正常的0x18,而是卡死在发送函数里的while ((TWCR & _BV(TWINT)) == 0);语句,说明没有收到从机ACK,主机TWINT没有被置位,同时总线SCL为低电平,SDA没有被拉低,从机的TWI中断也没有发生,使用了老农的怪招:每次发送前先TWCR &= ~(1 << TWEN);也不见效,实在百思不得其解,请大家帮帮忙。



按理说AVR的TWI接口都是硬件产生时序,并不需要程序干预,所以从机收到了SLA+W应该会自动回一个ACK给主机的,可是从机死活就是不肯拉低SDA./emotion/em040.gif

Tomcat 发表于 2006-10-30 23:10:17

这种情况一般是低级错误,比如我前两天就遇到这样类似的问题,查了好久结果是忘记把从机的中断打开,加条sei()就好了。



你再仔细检查一下。

gxlujd 发表于 2006-10-30 23:26:14

已经sei()了。

Tomcat 发表于 2006-10-30 23:36:08

// 测试I2C总线

// 使用ATMega16L单片机

// 创建于2005/5/19

// 最后一次修改 2005/7/21



#include <avr/io.h>

#include <util/twi.h>

#include <avr/wdt.h>

#include <avr/sleep.h>

#include <avr/pgmspace.h>

#include <avr/interrupt.h>

#include "Controller.h"



#define CPU_CLOCK        16000000

#define SCL_CLK                100000



// UART value defInition

#define        UART_TX_BUFFER_SIZE        32        /* 1,2,4,8,16,32,64,128        or 256 bytes */

#define        UART_TX_BUFFER_MASK        (UART_TX_BUFFER_SIZE - 1)

#define UART_RX_BUFFER_SIZE        32

#define UART_RX_BUFFER_MASK        (UART_RX_BUFFER_SIZE - 1)



#if (UART_TX_BUFFER_SIZE & UART_TX_BUFFER_MASK)

#error RX buffer size is not a power of        2

#endif



// UART Data

volatile unsigned char UART_TxBuf;

volatile unsigned char UART_RxBuf;

volatile unsigned char UART_TxHead;

volatile unsigned char UART_TxTail;

volatile unsigned char UART_RxTail;

volatile unsigned char UART_RxHead;



// TWI value definition

#define        TWI_TX_BUFFER_SIZE        32        /* 1,2,4,8,16,32,64,128        or 256 bytes */

#define        TWI_TX_BUFFER_MASK        (TWI_TX_BUFFER_SIZE - 1)

#define TWI_RX_BUFFER_SIZE        32

#define TWI_RX_BUFFER_MASK        (TWI_RX_BUFFER_SIZE - 1)



#if (TWI_TX_BUFFER_SIZE & TWI_TX_BUFFER_MASK)

#error RX buffer size is not a power of        2

#endif



// TWI Data

volatile unsigned char TWI_TxBuf;

volatile unsigned char TWI_RxBuf;

volatile unsigned char TWI_TxHead;

volatile unsigned char TWI_TxTail;

volatile unsigned char TWI_RxTail;

volatile unsigned char TWI_RxHead;



//

#define        TWI_START        0

#define        TWI_SLA_W        1

#define        TWI_SLA_R        2

#define        TWI_DATA_W        3

#define        TWI_DATA_R        4

#define        TWI_REP_ST        5

#define TWI_STOP        6

#define TWI_RECEIVE        7



//

#define        UART_RECEIVED        0

#define TWI_RECEIVED        1

#define        TWI_DATA_SENT        2

#define        TWI_DATA_RECV        3



//

volatile unsigned char gcSignal,gcTWI_State,gcTWSR,gcTWDR;



void Init_UART(int BAUD)

{

        // 设置UART

        UBRRH = (char)(BAUD >> 8) & 0x0F;

        UBRRL = (char)BAUD;        // 设波特率为BAUD

        UCSRB = (1<<RXCIE) | (1<<RXEN) | (1<<TXEN);        // 开RX完成中断,开RX允许,开TX允许

        UART_TxHead = 0; UART_TxTail = 0; UART_RxTail = 0; UART_RxHead = 0;

}



void TransmitByte(unsigned char data)

{

        /* calculate buffer index */

        UART_TxHead = (UART_TxHead + 1) & UART_TX_BUFFER_MASK;

        /* wait        for free space in buffer */

        while(UART_TxHead == UART_TxTail);

        UART_TxBuf = data;        /* store data in buffer        */

        sbi(UCSRB,UDRIE);        // enable UDR interrupt

}



void print(unsigned char * string)

{

        while(*string != 0)

        {

                TransmitByte(*string);

                if(*string == '
') TransmitByte(0x0D);

                string++;

        }

}



void char_to_ascii(unsigned char data, unsigned char * ascii)

{

        unsigned char i,j;

        i = data & 0x0F;

        j = data>>4;

        if(i < 10) i += '0';

        else i += 'A'-10;

        if(j < 10) j += '0';

        else j += 'A'-10;

        *ascii = j;ascii++;

        *ascii = i;

}



void TWI_Send_Byte(unsigned char data)

{

        gcTWI_State = TWI_START;

        /* calculate buffer index */

        TWI_TxHead = (TWI_TxHead + 1) & TWI_TX_BUFFER_MASK;

        /* wait        for free space in buffer */

        while(TWI_TxHead == TWI_TxTail);

        TWI_TxBuf = data;        /* store data in buffer        */

        TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN) | (1<<TWIE);

}



void TWI_Insert_Byte(unsigned char data)

{

        /* calculate buffer index */

        TWI_TxHead = (TWI_TxHead + 1) & TWI_TX_BUFFER_MASK;

        /* wait        for free space in buffer */

        while(TWI_TxHead == TWI_TxTail);

        TWI_TxBuf = data;        /* store data in buffer        */

}



void TWI_Send(void)

{

        gcTWI_State = TWI_START;

        TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN) | (1<<TWIE);

}

       

void TWI_Receive_Byte(void)

{

        gcTWI_State = TWI_RECEIVE;

        TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN) | (1<<TWIE);

}



void Delay(unsigned long cout)

{

        for(unsigned long i = cout; i >0; ) i--;

}



int main(void)

{

        // 设波特率为9600

        Init_UART(103);

        // 设置看门狗

        wdt_enable(WDTO_60MS);

        // 允许睡眠模式

        set_sleep_mode(0);

        // TCCR0分频64

        TCCR0 = 0x03;

        // 开定时器0中断

        //sbi(TIMSK,TOIE0);

        // 设置TWI

        TWBR = (CPU_CLOCK/SCL_CLK-16)/2;

        PORTC |= 0x03;

        // 允许全局中断

        sei();

        print("Start
");

        gcSignal = 0;

        TWI_TxHead = 0;TWI_RxHead = 0;TWI_TxTail = 0;TWI_RxTail = 0;

        unsigned char ascii = {0,0,0};

       

        while(1)

        {

                wdt_reset();

                if(bit_is_set(gcSignal, UART_RECEIVED))

                {

                        cbi(gcSignal, UART_RECEIVED);

                        TransmitByte(UART_RxBuf);print("
");        //回显

                        TWI_Send_Byte(UART_RxBuf);

                        unsigned char temp = UART_RxBuf;

                        UART_RxHead = (UART_RxHead + 1) & UART_RX_BUFFER_MASK;

                        if(temp == 0x0A) TWI_Send();

                        else TWI_Insert_Byte(temp);

                }

                if(bit_is_set(gcSignal, TWI_RECEIVED))

                {

                        cbi(gcSignal, TWI_RECEIVED);

                        print("TWSR = ");

                        char_to_ascii(gcTWSR, ascii);

                        print(ascii);print("
");

                }

                if(bit_is_set(gcSignal, TWI_DATA_SENT))

                {

                        cbi(gcSignal, TWI_DATA_SENT);

                        print("TWI transmit OK
");

                        TWI_Receive_Byte();

                }

                if(bit_is_set(gcSignal, TWI_DATA_RECV))

                {

                        cbi(gcSignal, TWI_DATA_RECV);

                        print("Echo char is ");

                        TransmitByte(TWI_RxBuf);print("
");        //回显

                        TWI_RxHead = (TWI_RxHead + 1) & TWI_RX_BUFFER_MASK;

                }

        }

}





SIGNAL(SIG_UART_DATA)

{

        /* check if all data is transmitted */

        if(UART_TxHead != UART_TxTail )

        {

                /* calculate buffer index */

                UART_TxTail = (UART_TxTail + 1) & UART_TX_BUFFER_MASK;

                UDR = UART_TxBuf; /* start transmition        */

        }

        else cbi(UCSRB,UDRIE);        // disable UDR interrupt

}



SIGNAL(SIG_UART_RECV)

{

        unsigned char cReceivedByte;

        cReceivedByte = UDR;

        UART_RxBuf = cReceivedByte;

        UART_RxTail = (UART_RxTail + 1) & UART_RX_BUFFER_MASK;

        sbi(gcSignal, UART_RECEIVED);

}



SIGNAL(SIG_2WIRE_SERIAL)

{

        gcTWSR = TWSR;gcTWDR = TWDR;

        sbi(gcSignal, TWI_RECEIVED);

        switch(gcTWSR)

        {

        case TW_START:                        //0x08

        case TW_REP_START:                //0x10

                if(gcTWI_State == TWI_START)

                {

                        gcTWI_State = TWI_SLA_W;

                        TWDR = 0xA0 | TW_WRITE;

                        TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWIE);

                }

                if(gcTWI_State == TWI_RECEIVE)

                {

                        gcTWI_State = TWI_SLA_R;

                        TWDR = 0xA0 | TW_READ;

                        TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWIE);

                }

                break;

        case TW_MT_SLA_ACK:                //0x18

        case TW_MT_SLA_NACK:        //0x20

                if(gcTWI_State == TWI_SLA_W)

                {

                        gcTWI_State = TWI_DATA_W;

                        TWI_TxTail = (TWI_TxTail + 1) & TWI_TX_BUFFER_MASK;

                        TWDR = TWI_TxBuf;

                        TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWIE);

                }

                break;

        case TW_MT_DATA_ACK:        //0x28

        case TW_MT_DATA_NACK:        //0x30

                if(gcTWI_State == TWI_DATA_W)

                {

                        if(TWI_TxTail != TWI_TxHead)

                        {

                                TWI_TxTail = (TWI_TxTail + 1) & TWI_TX_BUFFER_MASK;

                                TWDR = TWI_TxBuf;

                                TWCR = 1<<TWINT | 1<<TWEN | 1<<TWIE;

                        }

                        else

                        {

                                gcTWI_State = TWI_STOP;

                                TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWIE) | (1<<TWSTO);

                                sbi(gcSignal, TWI_DATA_SENT);

                        }

                }

                break;

        case TW_MR_SLA_ACK:                //0x40

        case TW_MR_SLA_NACK:        //0x48

                if(gcTWI_State == TWI_SLA_R)

                {

                        gcTWI_State = TWI_DATA_R;

                        TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWIE);

                }

                break;

        case TW_MR_DATA_ACK:        //0x50

        case TW_MR_DATA_NACK:        //0x58

                if(gcTWI_State == TWI_DATA_R)

                {

                        gcTWI_State = TWI_STOP;

                        TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWIE) | (1<<TWSTO);

                        TWI_RxBuf = gcTWDR;

                        TWI_RxTail = (TWI_RxTail + 1) & TWI_RX_BUFFER_MASK;

                        sbi(gcSignal, TWI_DATA_RECV);

                }

                break;

        case TW_NO_INFO:                //0xF8

                break;

        default:

                TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWIE) | (1<<TWSTO);

        }

}

Tomcat 发表于 2006-10-30 23:36:49

上个主机的参考一下,希望能帮到你。前天才用过,中断方式
-----此内容被Tomcat于2006-10-30,23:37:27编辑过

Tomcat 发表于 2006-10-30 23:40:40

从机的,用ATMega8,中断方式



// Motor controller for robot

// MCU type: ATMega8

// Created in 2005/5/13

// Last modify in 2005/6/13



#include <avr/io.h>

#include <avr/twi.h>

#include <avr/wdt.h>

#include <avr/sleep.h>

#include <avr/signal.h>

#include <avr/pgmspace.h>

#include <avr/interrupt.h>

#include "Controller.h"



#define CPU_CLOCK        8000000

#define SCL_CLK                100000



// Debug

//#define DEBUG



// TWI definition

#define SLA_ADDR        1



// Mode definition

#define        PWM                        0

#define        ICP                        1

#define CLKSTOP                0

#define CLKIO                1

#define CLK8                2

#define        CLK64                3

#define        CLK256                4

#define        CLK1024                5

#define        CLKT1DOWN        6

#define CLKT1UP                7

#define EDGEUP                0

#define EDGEDOWN        1



// Signal definition

#define        TWI_DATA_RECEIVED        0

#define        TWI_SIGNAL                        1



volatile unsigned char gcSignal,gcTWI_State,gcTWI_buf_ptr;

volatile unsigned char gcTWI_Buf;



void Init_TC1(unsigned char mode)

{

        unsigned char temp;

        temp = TIMSK;

        temp &= 0xC3;

        TIMSK = temp;

        switch(mode)

        {

                case(PWM):

                TCCR1A = 0xA1;                // Fast PWM mode 8 bit

                TCCR1B = 0x08;

                sbi(DDRB,1);        // Se PB1 to output

                break;

               

                case(ICP):

                // Clear TIMSK about T1

                sbi(TIMSK,TICIE1);        // Open T1 Input capture interrupt

                TCCR1A = 0;

                TCCR1B = 0;

                sbi(TCCR1B,ICES1);        // Set ICP edge downs

                break;

        }

}



void Start_TC1(unsigned char div)

{

        char temp;

        temp = TCCR1B;

        temp &= 0xF8;



        switch(div)

        {

                case(CLKSTOP):

                break;

                case(CLKIO):

                temp |= 0x01;

                break;

                case(CLK8):

                temp |= 0x02;

                break;

                case(CLK64):

                temp |= 0x03;

                break;

                case(CLK256):

                temp |= 0x04;

                break;

                case(CLK1024):

                temp |= 0x05;

                break;

                case(CLKT1DOWN):

                temp |= 0x06;

                break;

                case(CLKT1UP):

                temp |= 0x07;

                break;

        }

        TCCR1B = temp;

}



void Init_TC2()

{

        TCCR2 = 0x60;        // PWM phase correct mode, clear OC2 on compare match when upcounting

        sbi(DDRB,3);        // Set PB3 to output

}



void Start_TC2()

{

        TCCR2 |= 0x01;        // PWM frequency 15625 Hz

}



void Stop_TC2()

{

        TCCR2 &= 0xF8;        // Clear prescaling bits

        cbi(PORTB,3);

}



void Init_TWI()

{

        TWAR = SLA_ADDR<<1 | (1<<TWGCE);        // Slave address 1

        TWCR = (1<<TWEA) | (1<<TWEN) | (1<<TWIE);

        TWBR = (CPU_CLOCK/SCL_CLK-16)/2;

        PORTC |= 0x30;        // Enable pull up resistor

}



int main(void)

{

        // init TC2

        Init_TC2();

        // enable watch dog

        wdt_enable(WDTO_60MS);

        // enable sleep

        set_sleep_mode(0);

        // TCCR0 prescaling 64

        TCCR0 = 0x03;

        // Enable interrupt of timer 0

        //sbi(TIMSK, TOIE0);

        // Initial TWI

        Init_TWI();



        gcSignal = 0;

        gcTWI_buf_ptr = 0;

        // Enable all interrupt

        sei();



        while(1)

        {

                wdt_reset();

                if(bit_is_set(gcSignal, TWI_SIGNAL))

                {

                        cbi(gcSignal, TWI_SIGNAL);

                }

                if(bit_is_set(gcSignal, TWI_DATA_RECEIVED))

                {

                        cbi(gcSignal, TWI_DATA_RECEIVED);

                        OCR2 = gcTWI_Buf;

                }

        }

}



SIGNAL(SIG_2WIRE_SERIAL)

{

        unsigned char cTWSR = TWSR;

        switch(cTWSR)

        {

        case TW_SR_SLA_ACK:        // 0x60

                TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWEA) | (1<<TWIE);

                break;

        case TW_SR_DATA_ACK:        // 0x80

                gcTWI_Buf = TWDR;

                gcTWI_State = TW_SR_DATA_ACK;

                TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWEA) | (1<<TWIE);

                break;

        case TW_SR_DATA_NACK:

                gcTWI_Buf = TWDR;

                gcTWI_State = TW_SR_DATA_NACK;

                TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWEA | (1<<TWIE));

                break;

        case TW_SR_STOP:        // 0xA0

                if(gcTWI_State == TW_SR_DATA_ACK || gcTWI_State == TW_SR_DATA_NACK)

                {

                        sbi(gcSignal, TWI_DATA_RECEIVED);

                }

                TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWEA) | (1<<TWIE);

                gcTWI_State = TW_SR_STOP;

                break;

        case TW_SR_GCALL_DATA_ACK:

                break;

        case TW_ST_SLA_ACK:        // 0xA8

                TWDR = gcTWI_Buf;

                TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWEA | (1<<TWIE));

                break;

        case TW_ST_DATA_ACK:        // 0xB8

                TWDR = gcTWI_Buf;

                TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWEA | (1<<TWIE));

                break;

        case TW_ST_DATA_NACK:

                gcTWI_buf_ptr = 0;

                TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWEA | (1<<TWIE));

                break;

        case TW_ST_LAST_DATA:

                TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWEA) | (1<<TWIE);

                break;

        default:

                TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWIE) | (1<<TWSTO);

        }

}

gxlujd 发表于 2006-10-30 23:50:33

非常感谢Tomcat 菜猫!我试试看。

gxlujd 发表于 2006-10-31 01:10:32

终于搞定了,原因如下:



1、从机收到主机寻址的SLA+R/W以及数据都要通过从机程序来发送ACK/NACK,TWI硬件并不能自动产生。



从机发送ACK:TWCR= _BV(TWEN)|_BV(TWINT);

从机发送NACK:TWCR=_BV(TWEA) | _BV(TWEN)|_BV(TWINT);



2、由于主从系统存在2个以上单片机,所以就有上电先后顺序的问题,如果你的程序初始化系统后就直接开始TWI通讯,务必保证从机比主机要就绪在先,否则的话第一次通讯从机就不能ACK主机了,主机必须restart多次直到从机就绪才能正确通讯。



解决的办法很简单:

从机的熔丝start-up time设为:16K CK+4ms

主机的熔丝start-up time设为:258K CK+64ms

testcode 发表于 2006-10-31 01:25:42

谢谢经验分享!

曾经遇到类似的问题,加入一个时间溢出,如果超过一定时间没有反应,再重新发送......

aini 发表于 2006-11-8 10:25:45

真不知道拿那么多的分干什么吃的

如此幼稚的问题还有人说好:明明从机在地址符合时候是自动发送ACK的。还有

“从机发送ACK:TWCR= _BV(TWEN)|_BV(TWINT);

从机发送NACK:TWCR=_BV(TWEA) | _BV(TWEN)|_BV(TWINT); ”

对吗?

该是从机发送NACK:TWCR= _BV(TWEN)|_BV(TWINT);

从机发送ACK:TWCR=_BV(TWEA) | _BV(TWEN)|_BV(TWINT);

turejameng 发表于 2014-7-28 20:41:58

我也遇到类似问题,,不过是。。。用m16通过i2c找at24c256。
郁闷的是,我用中断和马潮老师的程序都调试不出来。。
用中断是发送完16位内部地址之后,数据发送开始返回错误。。
用马潮老师书上的程序,在studio中看到。发完器件地址之后,和你这个情况一样twsr没有正常返回0x18。。也不知道为什么,都快愁死了。。
卡了快一周了。两个程序都调试不出来。。。。
页: [1]
查看完整版本: TWI主从机实验,卡死在主机的while ((TWCR & _BV(TWINT)) == 0);语句