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 这种情况一般是低级错误,比如我前两天就遇到这样类似的问题,查了好久结果是忘记把从机的中断打开,加条sei()就好了。
你再仔细检查一下。 已经sei()了。 // 测试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:37:27编辑过 从机的,用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);
}
} 非常感谢Tomcat 菜猫!我试试看。 终于搞定了,原因如下:
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 谢谢经验分享!
曾经遇到类似的问题,加入一个时间溢出,如果超过一定时间没有反应,再重新发送...... 真不知道拿那么多的分干什么吃的
如此幼稚的问题还有人说好:明明从机在地址符合时候是自动发送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); 我也遇到类似问题,,不过是。。。用m16通过i2c找at24c256。
郁闷的是,我用中断和马潮老师的程序都调试不出来。。
用中断是发送完16位内部地址之后,数据发送开始返回错误。。
用马潮老师书上的程序,在studio中看到。发完器件地址之后,和你这个情况一样twsr没有正常返回0x18。。也不知道为什么,都快愁死了。。
卡了快一周了。两个程序都调试不出来。。。。
页:
[1]