Sida 1 av 2

AVR + I2C + 3.3V *LÖST*

Postat: 25 oktober 2006, 13:03:37
av speakman
Har ett himla bekymmer med en ATmega88 som är slave på en I2C-buss.
Master är en ETRAX 100LX MCM från Axis och leverar 3.3V på bussen.
Hastigheten är låg (kring 1KHz).
Problemet är att AVR:en helt enkelt inte reagerar (ger interrupt) ö.h.t. när datat kommer.
Har kopplat in en 2-kanals scopemeter och bussen ser bra ut.
Har det kopplat via Axis 82+ Developer Board och in på ett STK500 där AVR:en sitter. Har även en AVR Dragon genom vilken jag debuggar koden.

Ett prov som jag gjorde genom att göra en oändlig loop i main (programmerar i C) som läser av SCL eller SDA (båda funkar) och ger en blinkning på PORTB (kopplad till lysdioderna på STK500) då respektive pinne rycks ner, och det funkar kanon.
Alltså, AVR:en tar 3,3V som hög om man läser av pinnarna manuellt.
Kan det vara så att den inte är tillräckligt hög för att ge interrupt?

Här är koden jag använder för TWI/I2C:

Kod: Markera allt

#include <avr/io.h>
#include <inttypes.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include "serial.h"

/* TWI interrupt routine */
ISR(TWI_vect)
{
	for(;;) {
		PORTB = 0x00;
		_delay_ms(100);
		PORTB = 0xFF;
		_delay_ms(100);
	}

	/* Clear interrupt flag */
	TWCR &= ~_BV(TWINT);
}

void twi_init(uint8_t addr)
{
	/* Set device address */
	TWAR = addr << 1 | 0x01;

	/* Clear content */
	TWDR = 0x00;

	/* Enable TWI interface with Acknowledge and interrupt */
	TWCR = _BV(TWEN) | _BV(TWEA) | _BV(TWIE) | _BV(TWINT);
}
Koden i ISR:en visar bara när den hoppat in där. Kan även lägga en breakpoint där, ingendera reagerar.

Mycket tacksam för tips och idéer!

Mvh
speakman

Postat: 25 oktober 2006, 13:34:20
av björn
Jag kan inte se något fel i koden (förutom att det ser illa ut med en evighetsloop i interrupten... :) ), har du pullup motstånd på SCL och SDA?

Postat: 25 oktober 2006, 13:56:19
av cykze
Kan du inte lägga in den kompletta koden som krävs för att visa felet? Det kan ju vara något så enkelt som att du har glömt sei() eller liknande. Ett fel som säkert någon skulle upptäcka, men som kan vara svårt att se själv om man har suttit med koden för länge.

Dessutom kanske någon till och med orkar testa koden IRL.


Förresten ska det väl vara:

/* Clear interrupt flag */
TWCR |= _BV(TWINT);

Postat: 25 oktober 2006, 14:26:59
av Isoz
Dum fråga kanske men vad har du för matningsspänning till AVR 'en ?

/Isoz

Postat: 25 oktober 2006, 14:59:10
av speakman
Main ser ut som följer:

Kod: Markera allt

ISR(__vector_default) {
	/* Non handled interrupt accoured! */
	PORTB |= _BV(PB0);
}

int main(void)
{
	uint8_t n,t;

	DDRB = 0xFF;
	PORTB = 0xAA;

	DDRC = 0x00; /* input */
	PORTC = 0x00; /* no pullup */

	ser_init();
	twi_init(10);

	sei();

	twi_init(10);

	for(;;) {
		if(TWCR & _BV(TWINT))
			PORTB = 0xFF;
		else
			PORTB = 0x00;
	}
}
ser_init() aktiverar serieporten, och där funkar även interruptet.

AVR:en matas med +5V från STK500.

Och TWCR |= _BV(TWINT); sätter väl ändå flaggan?
Den kommer hur-som-helst aldrig dit, så det är inte felet. :)

Pullup kommer från 82+ Dev Board (och är därför 3.3V). Men det tolkas som sagt som "hög" om man läser porten manuellt.
Men är den för låg för att aktivera interruptet?
AVR:en går ju att köra på 3.3V, funkar det isåfall bara då?

Förstår inte riktigt hur något så här enkelt och elementärt kan ofunka... :)

Tack alla som engagerar sig!

Mvh
speakman

Postat: 25 oktober 2006, 15:45:33
av Icecap
På min hemsida, under "Freebies", finns en simpel och välfungerande nivåomvandlare.

Postat: 25 oktober 2006, 16:54:51
av cykze
Från databladet:
The TWINT Flag must be cleared by software by writing a logic one to it. Note that this flag is not automatically cleared by hardware when executing the interrupt routine.

Ska du verkligen ha med TWINT här?
TWCR = _BV(TWEN) | _BV(TWEA) | _BV(TWIE) | _BV(TWINT);

3.3V är ju okej för en hög signal. Kolla databladet.

Tyvärr har jag inte använt hårdvaru-I2C så jag har inga direkta råd att ge. Men jag kanske kan prova lite senare idag.

Postat: 25 oktober 2006, 17:51:09
av björn
jag har iallafall med TWINT och mitt funkade när jag höll på med det.
Bifogar min kod här så kan du kolla:


Kod: Markera allt

//TWI initialize
void twi_init(void)
{
  	 
	 TWAR = 0x10;
	 //TWSR = TWI_TWPS;                                  // Not used. Driver presumes prescaler to be 00.
  	 TWDR = 0xFF;                                      // Default content = SDA released.
  	 TWCR = (1<<TWEN)|                                 // Enable TWI-interface and release TWI pins.
         (1<<TWIE)|(1<<TWINT)|                      // Enable Interupt.
         (1<<TWEA)|(0<<TWSTA)|(0<<TWSTO)|           // Signal requests.
         (0<<TWWC);

}

Kod: Markera allt

#pragma interrupt_handler twi_isr:18
void twi_isr(void)
{
    unsigned char data;
	 
    data = TWDR;
 	
//Do what you want with the data

    TWCR = (1<<TWEN)|                                 // TWI Interface enabled
                (1<<TWIE)|(1<<TWINT)|                      // Enable TWI Interupt and clear the flag to send byte
                (1<<TWEA)|(0<<TWSTA)|(0<<TWSTO)|   // Send ACK after next reception
                (0<<TWWC);     
}
EDIT: Min kod är skriven för ICCAVR och därför är det inte samma deklarering riktigt av interrupt pragma.

Postat: 25 oktober 2006, 18:18:10
av oJsan
Nivåproblemet borde ju vara väldigt enkelt att lösa genom att skippa pullup på Etrax:en och istället koppla lämpliga pullup (~10k) till atmegans 5V-matning. Kan fungera med atmegans interna pullup också...
Hela I2C bygger ju på open-drain och Etrax:en är 5V-tolerant har jag för mig (men kolla upp det för säkerhets skull!).

Postat: 25 oktober 2006, 18:49:36
av speakman
På 82+ Developer Board sitter även en RTC som går på samma I2C. Frågan är hur den beter sig, annars har tanken slagit mig om det också.
Pullupen är fast på 82+-kortet, därför blir det lite svårt att ta bort den.
Avlödning av 0603 är väl inget man gör för ros skull... :)
Dessutom skulle ju, som sagt, RTC:n sluta funka om inte STK500 är inkopplad.

Mvh
speakman

Postat: 26 oktober 2006, 10:42:12
av speakman
Har m.h.a. STK500 sänkt spänningsmatningen till 3.3V även på AVR:en (hade missat den funktionen, vi kommer att köra 3.3V på den rätta applikationen sedan).
Så nu borde det hela fungera som tänkt rent spänningsmässigt.

Precis kört igång AVR311: Using the TWI module as I2C slave, men funkar ej den heller.
Hänger sig på rad 75:

Kod: Markera allt

73: for (;;) {
74: 		// Check if the TWI Transceiver has completed an operation.
75: 		if (!TWI_Transceiver_Busy()) {
76: 			// Check if the last operation was successful
77: 			if (TWI_statusReg.lastTransOK) {
78: 				// Check if the last operation was a reception
79: 				if (TWI_statusReg.RxDataInBuf) {
där TWI_Transceiver_Busy() ser ut som följer:

Kod: Markera allt

unsigned char TWI_Transceiver_Busy(void)
{
	return (TWCR & (1 << TWIE));	// IF TWI interrupt is enabled then the Transceiver is busy
}
Eftersom TWIE manuellt är satt, så returneras alltid "true", varav den aldrig går vidare i if-satsen.

Här kan man ju tänka sig att Atmels egna exempel borde fungera...
Någon som provat den tidigare med bättre resultat?

Mvh
speakman

Postat: 26 oktober 2006, 13:49:01
av björn
Jag har inte använt den rakt av men jag hade iallafall inga problem att använda mig av appnotens upplägg.
Hur ser koden i main() ut innan forloopen? Skicka gärna allt så att man kan försöka se var det går fel.

Postat: 26 oktober 2006, 21:23:22
av speakman
Det börjar rör på sig! Efter att ha sänkt spänningen på STK500 till 3.3V reagerade äntligen AVR:en. Och p.g.a. att I2C-rutinerna på ETRAXen är så odokumenterade, uppstod ett fel vid adresseringen.
Vi tar det lite lungt, så återkommer jag med resultaten!
Tack så länge! :)

Mvh
speakman

Postat: 26 oktober 2006, 21:42:59
av oJsan
"ETRAX" och "odukumenterade" är två ord jag lätt kopplar samman... Jag har hållit på med precis samma sak som dig, men den gången var det ett FOX-board kopplat till en STK500 och atmega8. Efter många timmar med oscilloskop fick jag igång I2C... Hade dock inget problem med nivåerna på det sätt du beskriver. När jag väl satt ditt pullup så var det elektriskt sett OK.

Postat: 27 oktober 2006, 10:18:59
av speakman
Detta är allt som finns som "dokumentation":

Kod: Markera allt

/*
EXAMPLE usage:

     i2c_arg = I2C_WRITEARG(STA013_WRITE_ADDR, reg, val);
     ioctl(fd, _IO(ETRAXI2C_IOCTYPE, I2C_WRITEREG), i2c_arg);

     i2c_arg = I2C_READARG(STA013_READ_ADDR, reg);
     val = ioctl(fd, _IO(ETRAXI2C_IOCTYPE, I2C_READREG), i2c_arg);

*/
Inte helt självklar kod heller för den delen, men inte framgår det att adressen ska vänsterskiftas ett steg för att bli korrekt mot I2C-enheterna!

Mvh
speakman