hallo,

In het volgende hoofdstuk van de ATmega ben ik nu eens aan het kijken naar de seriele communicatie.
om precies te zijn de TWI interface. die leek me wel handig vanwege de compatibiliteit met het I2C protocol dat ik nog wel eens tegenkom, maar vooral vanwege het makkelijke adresseren van een slave.

De tekst in het datablad maakt al een hele hoop duidelijk (ziet er eigenlijk best makkelijk uit). voor de master transmitter staat ook zo'n beetje de complete code erin.

op een breadboardje heb ik 2 ATmega's zitten met 1 voedingslijn gebruikt voor de voeding en de ander voor de twee communicatielijnen.
even ter illustratie:
http://i17.photobucket.com/albums/b94/timberleek/breadboard.png

links de master en rechts de slave met aan die laatste 8 leds op poort b. er zitten wel weerstanden op, maar die heb ik even weg gelaten in de tekening.

in het midden 2 pullups voor de lijnen.

de bedoeling is voor nu dat de master een int optelt tot 255 en dan weer bij 0 begint. de waarde wordt telkens verstuurd naar de slave die hem uitvoert naar de 8 leds. even wat simpels om het te testen. nou het werkt niet...

vanaf het inschakelen branden de eerste 2 leds (pb0 en 1) en de rest is uit.

inmiddels weet ik dat het zit in het verzenden van het slave adres met write bit (als hij geen ack krijgt gaat hij naar ERROR(2) en zet hij de led op PB1 aan), maar ik heb niet de kennis om te zien waar het ligt.

dit is de code van de master:
[code=C]

#include <avr/io.h>
#include <util/twi.h>
#include <util/delay.h>

void verzenden(int slaveadres, int databyte);
void ERROR(int fase);

int main(void){
DDRB = 0xff;
DDRC = 0x00;
PORTC = 0xff;

int slaveadres = 1;
int databyte = 0;

TWBR = 2;
TWSR = (1<<TWPS1); // prescaler op 16 en baudrate divider op 2 geeft 100kHz klok

while(1){

verzenden(slaveadres, databyte);
databyte++;
_delay_ms(2000);
if(databyte >= 256){
databyte = 0;
}
}
}

void verzenden(int slaveadres, int databyte){
slaveadres = (slaveadres*2)+1; // adres komt op bit 7 tot 1 en r/w bit op bit 0

TWCR = (1<<TWINT)|(1<<TWSTA)|(1<<TWEN); //interrupt clearen, startconditie verzenden en het hele dingen enablen

while(!(TWCR & (1<<TWINT))); //wacht tot je een interrupt krijgt

if((TWSR & 0xF8) != TW_START){ //als de interrupt 'reden' in het statusregister niet overeen komt met de start=verzonden code --> ga naar error 1
ERROR(1);
}

TWDR = slaveadres; //het adres in het dataregister
TWCR = (1<<TWINT)|(1<<TWEN); //interrupt clearen en het hele ding weer in gang zetten (adres + R/W sturen)

while(!(TWCR & (1<<TWINT))); //wachten op interrupt

if ((TWSR & 0xF8) != TW_MT_SLA_ACK){ //als het adres niet goed is ontvangen --> error 2 (hier zit ik nu ook)
ERROR(2);
}

TWDR = databyte; //data in dataregister

TWCR = (1<<TWINT)|(1<TWEN); //interrupt clearen, en de laatste slag in gang zetten.

while (!(TWCR & (1<<TWINT))); // wacht op interrupt

if ((TWSR & 0xF8) != TW_MT_DATA_ACK){ // kijken of data goed verzonden is, anders error 3
ERROR(3);
}

TWCR = (1<<TWINT)|(1<<TWEN)|(1<<TWSTO); interrupt clearen, enable, en stop conditie verzenden
}

void ERROR(int fase){
PORTB = fase;
}

[/code]

en die van de slave:

[code=C]
#include <avr/io.h>
#include <util/twi.h>
#include <util/delay.h>

int main(void){
DDRB = 0xff;
DDRC = 0x00;
PORTC = 0xff;

int data = 0;

TWAR = 2; //het adres van de slave (adres is 1, maar bit0 hoort niet bij het adres
TWCR = (1<<TWEA)|(1<<TWEN); // enable de twi

while(1){

if(TWCR & (1<< TWINT)){ //als je een interrupt krijgt
if(TWSR & 0x60){ // en die komt neer op een ontvangen adres
TWCR |= (1<<TWINT); // clear de interrupt voor de rest van de boodschap
}

if(TWSR & 0x80){ //als je data hebt ontvangen
data = TWDR; // zet die in data
TWCR |= (1<<TWINT); // clear de interrupt
}
}

PORTB = data; //uitvoer van data (die altijd op data = 3 blijft)

}
}
[/code]

waar ga ik de mist in?

hopelijk kunnen jullie me daar mee helpen

Zonder de code te bekijken, is de voeding en signaal lijnen wel verbonden?

Ssommige breadboards hebben namelijk een onderbreking in het midden

ja die zijn bij dit breadboard al verbonden.

immers, anders konden de leds van de slave nooit branden (voeding zit aangesloten aan de linkerkant)

mhh moet je die interrupt bits setten om te clearen?

TWCR |= (1<<TWINT);

naar

TWCR &= ~(1<<TWINT);

dat begreep ik uit de datasheet.

daar staat:
"writing a one to TWINT clears the flag. The twi will not start any operation as long as the TWINT bit in TWCR is set. Immediately after the application has cleared TWINT, the TWI will initiate transmission of ..."

de master code (van het verzenden) is bijna een kopie van de stappen uit de datasheet (blz 227)

writing a one to TWINT clears the flag.

wees er maar van overtuigd dat dat NIET juist is !

dan ga ik even een zooitje veranderen en het nogmaals proberen

dan kom ik uit op deze code:



#include <avr/io.h>
#include <util/twi.h>
#include <util/delay.h>

void verzenden(int slaveadres, int databyte);
void ERROR(int fase);




int main(void){
DDRB = 0xff;
DDRC = 0x00;
PORTC = 0xff;


int slaveadres = 1;
int databyte = 0;

TWBR = 2;
TWSR = (1<<TWPS1);

	_delay_ms(1000);

	while(1){
	
		verzenden(slaveadres, databyte);
		databyte++;
		_delay_ms(2000);
		if(databyte >= 256){
			databyte = 0;
		}
	}
}

void verzenden(int slaveadres, int databyte){
	slaveadres = (slaveadres*2)+1;


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

	while(!(TWCR & (1<<TWINT)));

	if((TWSR & 0xF8) != TW_START){
		ERROR(1);
	}

	TWDR = slaveadres;
	TWCR = (0<<TWINT)|(1<<TWEN);



	while(!(TWCR & (1<<TWINT)));
	
	if ((TWSR & 0xF8) != TW_MT_SLA_ACK){
		ERROR(2);
	}

	TWDR = databyte;

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


	while (!(TWCR & (1<<TWINT)));
	
	if ((TWSR & 0xF8) != TW_MT_DATA_ACK){
		ERROR(3);
	}

	TWCR = (0<<TWINT)|(1<<TWEN)|(1<<TWSTO);
}


void ERROR(int fase){
	PORTB = fase;
	main();
}


en



#include <avr/io.h>
#include <util/twi.h>
#include <util/delay.h>



int main(void){
DDRB = 0xff;
DDRC = 0x00;
PORTC = 0xff;

int data = 0;

TWAR = 2;
TWCR = (1<<TWEA)|(1<<TWEN);

while(1){

if(TWCR & (1<< TWINT)){
	if(TWSR & 0x60){
		TWCR = (1<<TWEA)|(1<<TWEN)|(0<<TWINT);
	}

	if(TWSR & 0x80){
		data = TWDR;
		TWCR = (1<<TWEA)|(1<<TWEN)|(0<<TWINT);
	}
}

PORTB = data;

}
}

maar geen succes.
de leds bij de slave branden in elk geval niet meer
de error 2 led van de master blijft ook nu branden

[Bericht gewijzigd door timberleek op (93%)]

Op 17 augustus 2011 21:26:32 schreef free_electron:
[...]
wees er maar van overtuigd dat dat NIET juist is !

Toch wel, interruptflags in een AVR clear je door er een 1 naar toe te schrijven.

dan kan ik de interrupt clears dus weer terugzetten.

op zich vind ik het raar klinken, maar er zal wel een architectuur voordeel in zitten oid. ze hebben het er wel volmondig over in de datasheet.

die AVR311 lijkt grotendeels op het datablad zelf. alleen hebben ze hier een speciaal header en source file bij gemaakt.
of zie ik dat verkeerd?

is i2c eigenlijk volledig compatible met de twi module? of is het een gelijksoortige communicatie die ook wel gemaakt kan worden met de twi module, maar toch een paar flinke verschillen heeft?

ik zie trouwens dat de master transmitter de klok en datalijn laag blijft houden.

edit:
ik heb al punt gevonden in de code.

de eerste 2 leds van de slave bleven maar branden. toen bedacht ik me dat het adres van de slave 1 is, maar omdat het bij bit 2 van de x begint en daarvoor een R/W bit zit die ook 1 is kom je uit op de eerst 2 bits van de serie.

om het idee te controleren heb ik het adres veranderd in 85 (1010101).
en inderdaad.
hij leest zo te zien de adres input plus rw bit als data.

.

bij de transmitter stopt hij eerst 'databyte' in het dataregister voordat hij weer aan gaat.

en bij de receiver doet hij volgens mij eerst het adres ontvangen
dan alles opnieuw activeren
dan wacht hij tot hij data krijgt
en dan leest hij dat weer uit.

maar ik denk dat het bij de receiver fout gaat
ik vermoed eigenlijk dat de if loops niet goed zijn en dat hij ze nu gewoon beide doorloopt.

dus eerst reset hij alles zodat de master de data gaat voorbereiden en zenden. maar dan meteen leest hij ook de datastroom uit (waar het adres nog in staat) en voert die uit als data naar de leds.

en dan reset hij nogmaals de registers, maar ver voor de master klaar is met zenden. dus die zend vrolijk verder, maar krijgt nooit zijn ack terug (de slave is immers heel ergens anders mee bezig).

vandaar gaat de master daarna ook in error, hij krijgt zijn ack nooit meer te zien en blijft dus hangen.
vandaar blijven de lijnen ook laag

[Bericht gewijzigd door timberleek op (53%)]

Op 17 augustus 2011 21:26:32 schreef free_electron:
[...]
wees er maar van overtuigd dat dat NIET juist is !

Ga er maar vanuit dat dat wel juist is. Wat er feitelijk staat is dat het hele TWI gebeuren on hold gaat zolang er een interrupt pending is. Als je software fatsoenlijk in elkaar zit, heb je daar geen last van (je bent snel genoeg), als het niet fatsoenlijk in elkaar zit, blijft het toch werken. Je ACKed die interrupt door een 1 naar de 'interrupt pending' flag te schrijven.

Ik ken de TWI van Atmel niet goed genoeg om zo uit de losse pols te zeggen waarom dat zo in elkaar zit. Ken wel voldoende stukjes Atmel om er eerst maar eens vanuit te gaan dat er over nagedacht is om het zo in elkaar te zetten en dat dat in het grotere plaatje handig is.

@TS: je bent nu gelijktijdig met een master en een slave aan het pielen. Tenzij je kunt zien wat er op de 2 lijntjes gebeurt, is het moeilijk om zo vast te stellen waar je misère zit. Heb je een memory scoop? LA? Desnoods een gewone scoop en dan repeterend hetzelfde doen op je master? (alhoewel dat wel erg primitief is). Als je er echt niet verder mee komt: clock erg laag zetten en een paar LEDs met een driver torretje op de lijn zodat je kunt zien wat er gebeurt. (da's nog wat primitiever :) ). Eerst eens kijken of je master kant doet wat jij denkt.
Evt eerst eens tegen een I2C chippie aan kletsen. Je kunt vrij veilig aannemen dat die slave wel werkt (anders verkoopt die fabrikant er echt niet veel van).

Het verschil tussen I2C en TWI zit 'm met name in merken gedoe. I2C is van Philips. En dat mag je vast wel gebruiken. Als je ervoor betaalt... TWI is een iets generiekere interface, die 'toevallig' ook heel mooi aansluit bij I2C.

ik heb jammer genoeg (nog) geen fancy LA of digitale scoop.
en de school is op moment dicht, dus daar kan ik ook niet even heen (over een paar werken pas).

Maar ik ben er uit zo te zien.

ik ben even aan het 'debuggen' gegaan door gewoon naar de leds uit te laten voeren wat er steeds in het status register stond. en daar kwam de hele tijd 0xA0 in te staan waarna alles spaak liep.

en ik wou hebben 0x60 (SLA+R/w) of 0x80 (data ontvangen).

na wat zoeken in het datasheet zag ik dat de A0 code toebehoort aan de slave transmitter functie.
toen ging er een lichtje branden. en inderdaad, ik heb een hele stomme fout gemaakt bij de transmitter. De R/W bit had ik hoog gemaakt, maar voor schrijven is hij laag.

de transmitter stond dus vrolijk te luisteren terwijl de slave nooit ging zenden.

toen dat was opgelost en nog een paar kleine puntjes werkte hij ineens.

ik heb nu een transmitter die opteld. de waarde verstuurt naar de slave welke het uitvoert naar een stel leds.

het is niet veel, maar een goed begin.

Daarmee kan ik voorlopig weer even verder, bedankt weer voor alle tips

Snap je probleem. Het op die manier debuggen van communicatie... Die tijd heb ik wel een beetje gehad... Beetje tool had het je bij je eerste test verteld.

Afijn, succes met de rest van je project :)

tja ik heb jammer genoeg niks om echt naar de lijnen te kijken (afgezien van wat leds en een analoge scoop).

en dat hele jtag systeem moet ik nog een keertje leren.

al met al was het wel weer een gevalletje 'beter lezen!'

al met al was het wel weer een gevalletje 'beter lezen!'

As usual. Been there, done it. Als je dit vaker gaat doen en je budget laat het toe: koop iets om te bekijken wat er gebeurt. Dat scheelt je zo vreselijk veel tijd (en ergenis!).

ja ik twijfel ook al een tijdje om een rigol scoopje te kopen.
mag dan misschien niet de beste kwaliteit zijn (vergeleken de teks etc op school), maar voor hobby gebruik prima. en het werkt super handig.

ook in dit soort gevallen. het is dan geen echte LA, maar je kan wel gewoon een single run laten doen op het moment dat je wat gaat zenden. dan kun je mooi zien wat er nou is.

ik heb een tijdje geleden communicatie tussen 2 atmega's gemaakt.

software voor atmega32 was compatible met atmega164, dus ook vast wel voor jouw 324. Misschien kun je daaruit wijzer worden? Laat maar weten of je daar wat aan hebt...

ik heb een 328.

die is geloof ik niet compatible, maar misschien wel gelijkwaardig.

als je de code zou willen mailen graag, je kan er altijd wat van leren

timberleek waarom gebruik je de software van atmel avr311 niet. Kan je vrij downloaden:
http://www.atmel.com/dyn/resources/prod_documents/AVR311.zip

Deze werkt nagenoeg feilloos!

oh pardon, te snel gelezen..

ik post het wel hier online dan kunnen er nog meer mensen van genieten.
Het stukje code dat ik hier post is om een led en LED aan en uit te zetten. en daarnaast relais te bedienen.
Ik zie dat ik de i2c master van peter fleury gebruik...

om iets te verzenden doe ik het volgende:

			if(!(i2c_start(0x50+I2C_WRITE)))
			{
				i2c_write(0x01);
				i2c_stop();
			}

en om iets te ontvangen heb ik een ISR:

#define VALID 1
#define INVALID 0

char buffer_access=INVALID;

void init_twi_slave(uint8_t adr)
{
	TWAR= adr;
	TWCR &= ~(1<<TWSTA)|(1<<TWSTO);
	TWCR|= (1<<TWEA) | (1<<TWEN)|(1<<TWIE); 	 
	sei();
}

#define TWCR_ACK TWCR = (1<<TWEN)|(1<<TWIE)|(1<<TWINT)|(1<<TWEA)|(0<<TWSTA)|(0<<TWSTO)|(0<<TWWC);  
#define TWCR_NACK TWCR = (1<<TWEN)|(1<<TWIE)|(1<<TWINT)|(0<<TWEA)|(0<<TWSTA)|(0<<TWSTO)|(0<<TWWC);
#define TWCR_RESET TWCR = (1<<TWEN)|(1<<TWIE)|(1<<TWINT)|(1<<TWEA)|(0<<TWSTA)|(1<<TWSTO)|(0<<TWWC);  

ISR (TWI_vect)  
{
	char received_data;
	
	switch (TW_STATUS)
	{
		case TW_SR_SLA_ACK:		/*if there is a valid address received, make data buffer accesable*/
			TWCR_ACK;
			if((TWDR&0xFE) == SLAVE_ADRESS)
				buffer_access=VALID;
			break;
	
		case TW_SR_DATA_ACK:
			if (buffer_access == VALID){
				received_data = TWDR;
				set_data(received_data);
			}
			
			buffer_access = INVALID;	/* no set actions before adressbyte is valid. */
			
			TWCR_ACK;
			break;

		default: 	
			TWCR_RESET;
			break;
	}
}

void set_data(char data)
{
	/*MSB represents the job. If it's 0 you want to set or clear the commled.
	 * if it's 1 you want to set the output relais.
	 * the MBS's represents the data for this action */
	
	if((data & 0x80) == 0x00)
	{
		if ((data & 0x01) == 0x01)
			PORTA&=~(1<<COMM_LED);
		else
			PORTA|=(1<<COMM_LED);
	}
	else
	{
		if((data&0x01)==0x01)
			PORTA|=(1<<RELAIS1);
		else
			PORTA&=~(1<<RELAIS1);
		
		if((data&0x02)==0x02)
			PORTA|=(1<<RELAIS2);
		else
			PORTA&=~(1<<RELAIS2);
		
		if((data&0x04)==0x04)
			PORTA|=(1<<RELAIS3);
		else
			PORTA&=~(1<<RELAIS3);
	}
}

je doet natuurlijk eerst initialiseren, en vervolgens doet je ISR de rest vanzelf. Nu ik zo kijk zit de code wat gammel in elkaar maar ik denk dat je er wel uit komt zo...

geen idee

op het eerste gezicht door de enorme hoeveelheid code ;-)

maar dat zal gewoon even doorworstelen zijn

die code van VWG3JB zal ik ook eens doornemen.

het zelf uitzoeken adhv de datasheet zoals ik nu heb geprobeerd heeft ook wel zijn voordelen imo. punt 1 leer je er wat van (hoe gaat het in zijn werk) en je leert een datasheet uitpluizen (ook handig).
en nu heb ik het gevoel alsof ik zelf iets voor elkaar gekregen heb ;-)

[Bericht gewijzigd door timberleek op (59%)]

heeft iemand enig idee of wat voor capaciteit een telefoonkabel oid heeft?

ik had het idee om een 4 aderige kabel rond mijn kamer te leggen met de uC's daaraan.
in het datablad hebben ze het over 400pF max, maar is dat veel?

aan hoeveel meter kabel moet ik dan denken (bij benadering).
of kan ik de bus beter op een andere manier neerleggen (level converters oid)?

nattevinger: ongeveer 1pf/cm.

dat zou dus uitkomen op 4 meter max.

is daar nog wat aan te doen?
iets van een buffertrap of level converter om die afstand langer te kunnen maken?

Inderdaad max 4m. i2c of twi of hoe je het ook wilt noemen kan verlengd worden met bepaalde bus drivers. Maar ik heb me daar nooit mee bezig gehouden.
(de huisgenoot van een vriend van me had z'n kamer geautomatiseerd (in 1987!) met I2C. Dat werkte dus niet goed en toen heb ik dus die 400pf -> 4 meter uitgerekend voor hem... :-) )