Attiny med USI som SPI-slav [löst]

PIC, AVR, Arduino, Raspberry Pi, Basic Stamp, PLC mm.
Användarvisningsbild
gunne
Inlägg: 2088
Blev medlem: 17 juni 2004, 15:00:31
Ort: sthlm
Kontakt:

Attiny med USI som SPI-slav [löst]

Inlägg av gunne »

Nu när jag fått igång min Attiny861 försöker jag få den att prata med en Atmega128. Atmegan pratar redan SPI med ett SD-kort och en digital pot så på den fungerar SPI som det ska. Där använder jag SPI-funktioner från AVRlib.

Tinyn har ju USI och jag har ställt in den att agera SPI enligt koden nedan. Det verkar funka men resultatet är lite stokastiskt. Det smyger sig in en massa skräptecken mellan de tecken jag skickar. I nuläget skickar jag först 0xCC från Megan tills den får 0xAA tillbaka, sen skickar Megan tio 0xFF och skriver ut de tio tecken den får tillbaka.

Jag vill i slutändan kunna föra över valfri struct byte för byte mellan processorerna.

Atmega@4MHz, Attiny@8MHz. De sitter ihopkopplade via en flatkabel på 10cm. SPI-frekvensen är 4MHz/4/64/128, samma resultat med alla. En (dålig) bild på SS- och SCK-linorna finns längst ner.

Resultatet visar jag i ett terminalfönster via UART. Det kan se ut så här:

Kod: Markera allt

command 0xCC, 0, 00AA
Data return: 0, 204
Data return: 1, 255
Data return: 2, 0
Data return: 3, 10
Data return: 4, 255
Data return: 5, 255
Data return: 6, 255
Data return: 7, 255
Data return: 8, 255
Data return: 9, 255
Testar SPI: 255
Knapp 1
command 0xCC, 0, 00AA
Data return: 0, 204
Data return: 1, 0
Data return: 2, 255
Data return: 3, 255
Data return: 4, 10
Data return: 5, 255
Data return: 6, 255
Data return: 7, 20
Data return: 8, 255
Data return: 9, 255
Testar SPI: 255
Första tecknet kommer ju som det ska. Men sen blir det skumt. Det har som synes smugit sig in en massa 255 mellan tecknen jag vill skicka. Det är samma 0xFF som jag skickar från Megan så om jag t.ex. skickar 0xAA istället så får jag en massa 170 tillbaka. Edit: Första "Data return" blir alltid 204, dvs 0xCC som jag skickar från Megan för att initiera överföringen.

På nåt sätt hänger inte Tinyn med i svängarna verkar det som men det är ju skumt. Atmegan ska ju skriva ut saker via UART mellan varje byte så det blir ju en liten paus där och sen är ju Tinyn snabbare än Megan.

Det kanske är enkelt men jag lyckas inte komma på det. Ska man använda en buffert på något sätt? Har jag gjort nån enkel blunder i koden?



###### Nedan är koden ######
Här är koden på Atmegan. Den skickar först 0xCC tills den får 0xAA till svar. Sen skickar den 0xFF 10 gånger för att ta emot tio byte

Kod: Markera allt

u08 batteryGetStatus(BatteryStatusType* battery_status) {
	u08 i;
	u08 data;	// Byt mot battery_status och fixa pekare0

	// Set SS low
	cbi(BATTMON_PORT, BATTMON_SS);

	asm("nop");		//Wait for slave to initiate
	asm("nop");

	// Send command byte, command to recieve data, wait for 0xAA = ready to send 
	for (i=0;i<10;i++) {
		data = spiTransferByte(0xCC);
		rprintf("command 0xCC, %d, %x\r\n", i, data);
		if (data==0xAA) break;
	}

	// If answer is AA = "ready to send"
	if (data==0xAA) {
		for (i=0;i<10;i++) {
			data = spiTransferByte(0xFF);
			rprintf("Data, %d, %x\r\n", i, data);						
		}
		sbi(BATTMON_PORT, BATTMON_SS);		// Set SS high
		return data;
	}	
	else {
		sbi(BATTMON_PORT, BATTMON_SS); 		// Set SS high
		return 0;
	}
}
satan
Här ställs ISP-läget in på attinyn och så ser ni funktionen som skickar/tar emot data.

Kod: Markera allt

//! Initiate SPI slave mode
void spiInit(void) {
	// setup SPI I/O pins
	cbi(SPI_DDR, SPI_SCK);   	// set USCK as input
	sbi(SPI_PORT, SPI_SCK);  	// set SCK internal pullup
	cbi(SPI_DDR, SPI_MOSI);   	// set DI as input
	sbi(SPI_PORT, SPI_MOSI);   	// set DI internal pullup
	sbi(SPI_DDR, SPI_MISO);   	// set DO as output
	cbi(SPI_DDR, SPI_SS);   	// SS is input for slave operation
	sbi(SPI_PORT, SPI_SS);		// set SS internal pullup
	// Set three-wire mode and external clock in USI Control Register
	USICR = (1<<USIOIE)|(1<<USIWM0)|(1<<USICS1);
	USISR |= (1<<USIOIF); //Clear counter overflow flag
}

//! Transfer one byte through SPI
u08 spiTransferByte(u08 data) {
	USIDR = data;					// move data to transfer register
	while(!(USISR&(1<<USIOIF)) && SS);	// wait for transfer to complete, if SS goes high abort
	return USIDR;					// return the received data
}

ISR(USI_OVF_vect) {
	USISR |= (1<<USIOIF);	// Clear overflow flag
}
Här är mailoopen på attinyn. Den skickar först 0xAA för att visa att allt funkar och sen skickar den 0,10,20...90.

Kod: Markera allt

int main(void) {
	u08 i;
	u08 spi_get;

	sei();
	spiInit();	// Init SPI
	
	sbi(DDRB, PB5);		// PP5 as output for blinking LED
	sbi(PORTB, PB5);	// Turn off LED

	while(1)
	{
		// If SS pin is low do SPI-transfer
		if(SS) {
			cbi(PORTB, PB5);	//Light LED
			//spi_get = spiTransferByte(0x00);	// Send dummy byte to initiate transfer
			spi_get = spiTransferByte(0xAA);	// Send 0xAA = ready to send								
			for (i=0;i<10;i++) {
				spi_get = spiTransferByte(i*10);	// Send echo
			}
			sbi(PORTB, PB5);	//LED off
		}
	}
	return 0;
}
SS och SCK. 10 mikrosekunder/div är det:
Bild
Senast redigerad av gunne 5 februari 2009, 16:36:45, redigerad totalt 2 gånger.
Användarvisningsbild
exile
EF Sponsor
Inlägg: 496
Blev medlem: 21 oktober 2005, 23:32:07

Re: Attiny med USI som SPI-slav, får inte till det

Inlägg av exile »

Varför nolla du flagan för USIOIF i ett interrupt?

Det kan ställa till med en hel del trassel då nollningen kan ske på ett olyckligt ställe.

exempel

Kod: Markera allt

//! Transfer one byte through SPI
uint8_t spiTransferByte(uint8_t data) {
  76:	8f b9       	out	0x0f, r24	; 15
   USIDR = data;               // move data to transfer register
   while(!(USISR&(1<<USIOIF)) && SS);   // wait for transfer to complete, if SS goes high abort
  78:	76 99       	sbic	0x0e, 6	; 14
  7a:	02 c0       	rjmp	.+4      	; 0x80 <spiTransferByte+0xa>
  7c:	b4 99       	sbic	0x16, 4	; 22
  7e:	fc cf       	rjmp	.-8      	; 0x78 <spiTransferByte+0x2>
   return USIDR;               // return the received data
  80:	8f b1       	in	r24, 0x0f	; 15
}
  82:	99 27       	eor	r25, r25
  84:	08 95       	ret
Om vi antar att USIOIF blir 1 vid rad 7c så kommer flaggan att vara nollad (via interruptet) när den kommer till 78 och gör testen så kommer den att noll fortfarande....

lägg till USISR |= (1<<USIOIF) efter du gjort testet.

Kod: Markera allt

u08 spiTransferByte(u08 data) {
   USIDR = data;               // move data to transfer register
   while(!(USISR&(1<<USIOIF)) && SS);   // wait for transfer to complete, if SS goes high abort
   USISR |= (1<<USIOIF); //Clear counter overflow flag
   return USIDR;               // return the received data
}
Hoppas att det var till någe hjälp ^^
Användarvisningsbild
gunne
Inlägg: 2088
Blev medlem: 17 juni 2004, 15:00:31
Ort: sthlm
Kontakt:

Re: Attiny med USI som SPI-slav, får inte till det

Inlägg av gunne »

Nope, tyvärr inte. Eller, jo kanske.

När jag började skriva inlägget så hade jag gjort EXAKT som du skriver och då funkade ingenting. Sen när jag testade runt lite för att skriva i inlägget vad jag hade gjort så kom jag på att jag skulle testa ett interrupt för att se om det funkade, och då blev det bättre! Av nån anledning så sattes aldrig flaggan USIOIF ö.h.t. när jag inte hade interruptet.

Du kan ju ha rätt och då måste jag istället luska reda på varför det inte funkade utan interruptet. Eller skriva en smartare kod som utnyttjar interruptet istället.

Jag kanske kan ha kvar interruptet men ha det är tomt? Ska testa det imorgon.
Användarvisningsbild
exile
EF Sponsor
Inlägg: 496
Blev medlem: 21 oktober 2005, 23:32:07

Re: Attiny med USI som SPI-slav, får inte till det

Inlägg av exile »

när jag kolla på assembler exemplet ser jag att det nollställer 4 bits räknaren innan de tar emot "data"

Kod: Markera allt

SlaveSPITransfer:
out USIDR,r16
ldi r16,(1<<USIOIF)
out USISR,r16
SlaveSPITransfer_loop:
sbis USISR,USIOIF
rjmp SlaveSPITransfer_loop
in r16,USIDR
ret
vilket borde bli något så här

Kod: Markera allt

uint8_t spiTransferByte(uint8_t data) {
   USIDR = data;
   USISR = (1<<USIOIF);
   while(!(USISR&(1<<USIOIF)));
   return USIDR;
}
om det har någon betydelse... men någe mer kan jag tyvärr inte komma på :/

Ett tips angående typer
så skulle jag rekommendera att man använder stdint.h så slipper man definiera egna "typer" (exempel u08)
jag tycker det är smidigt men smaken är som baken delad ^^
Användarvisningsbild
gunne
Inlägg: 2088
Blev medlem: 17 juni 2004, 15:00:31
Ort: sthlm
Kontakt:

Re: Attiny med USI som SPI-slav, får inte till det

Inlägg av gunne »

Jo. Anledningen till att jag använder de typerna är att jag använder AVRlib för att sköta bland annat grafisk LCD, GPS-modul, SD-kort etc och där används såna datatyper. Så jag tyckte att det var lika bra att fortsätta på samma spår så det blir konsekvent i koden. Annars är jag benägen att hålla med dej om att det är smartare att hålla sig till standard...
Användarvisningsbild
gunne
Inlägg: 2088
Blev medlem: 17 juni 2004, 15:00:31
Ort: sthlm
Kontakt:

Re: Attiny med USI som SPI-slav, får inte till det

Inlägg av gunne »

Nu ska vi se...

Precis som exile sa så är problemet att interruptet nollställer USIOIF så att while-satsen i spiTransferByte inte avbryts. Nu har jag skrivit om koden som följer och då fungerar det.

Kod: Markera allt

u08 spiTransferByte(u08 data) {
	USIDR = data;						// move data to transfer register
	while(!(USISR&(1<<USIOIF)) && SS);	// wait for transfer to complete, if SS goes high abort
	USISR |= (1<<USIOIF);				// Clear overflow flag
	return USIDR;						// return the received data
}
ISR(USI_OVF_vect) {}
Det märkliga är att jag MÅSTE ha kvar den tomma interruptrutinen för att USIOIF över huvud taget skall flaggas. Är det alltid så? Att det måste finnas en interruptrutin för att flaggan i registret skall sättas?

Nu ska jag få till så att jag kan föra över en struct byte för byte och måste ge mig in i pekarnas mörka värld. Det var ett par år sen sist så det kanske dyker upp lite hinder... :wacko:
Användarvisningsbild
gunne
Inlägg: 2088
Blev medlem: 17 juni 2004, 15:00:31
Ort: sthlm
Kontakt:

Re: Attiny med USI som SPI-slav, får inte till det

Inlägg av gunne »

Jag vill skicka en struct som ser ut så här byte för byte.

Kod: Markera allt

typedef struct battery_status_struct
	u08 SOC;			// State of charge 0-100%
	u08 voltage;		// Battery voltage
	u08 current;		// Current
	u08 temperature;	// Temperature
BatteryStatusType;
Sen definerar jag en pekare av den typen:

Kod: Markera allt

BatteryStatusType *battery_status;
battery_status->SOC = 50;
battery_status->voltage = 65;
battery_status->current = 20;
battery_status->temperature = 22;
Jag har försökt med det mesta i form av * och & när jag ska skicka den. Jag vill ju skicka _värdet_ som finns i en viss adressposition i structen och typecasta den till en u08.

Jag har skrivit nåt sånt här och testat lite olika varianter av * & ;)

Kod: Markera allt

for (i=0;i<sizeof(battery_status);i++)
	spi_get = spiTransferByte((u08)(*(&battery_status + i)));
Jag får: ../Battmon.c:97: warning: cast from pointer to integer of different size

Sen stämmer inte de bytes som skickas heller...

I min värld borde det vara så här:
(&battery_status + i) borde ju ge _adressen_ till byten jag vill åt. *(&battery_status + i) borde då ge _värdet_ lagrat på den adressen.

Sen om jag skickar sizeof(battery_status) så får jag "2". Den borde ju vara 4 bytes stor tycker jag? Eller är det så att allt adresserad med 16 bitar och att den då tar upp två st "word"?

Edit: definerar pekare. Edit 2: Menade battery_status som Sodjan påpekar nedan.
Senast redigerad av gunne 4 februari 2009, 16:41:13, redigerad totalt 2 gånger.
sodjan
EF Sponsor
Inlägg: 43251
Blev medlem: 10 maj 2005, 16:29:20
Ort: Söderköping

Re: Attiny med USI som SPI-slav, [ny fråga]

Inlägg av sodjan »

Hur är battery_struct definierat ?
(Eller menar du battery_status ??)
Användarvisningsbild
gunne
Inlägg: 2088
Blev medlem: 17 juni 2004, 15:00:31
Ort: sthlm
Kontakt:

Re: Attiny med USI som SPI-slav, [ny fråga]

Inlägg av gunne »

Okej! Nu har jag fått till det så att valfri struct kan överföras från slaven till mästaren. För att nån annan ska kunna dra nytta av den här tråden i framtiden så lägger jag in den fungerande koden. :tumupp: 8)

Så här definieras structen i mitt fall:

Kod: Markera allt

typedef struct battery_status_struct {
	u08	SOC;			// State of charge 0-100%
	float voltage;		// Battery voltage;
	float current;		// Current
	s08 temperature;	// Temperature;
} BatteryStatusType;
Här är funktionen som för över structen från slaven till mästaren byte för byte

Kod: Markera allt

u08 transferBatteryStatus(BatteryStatusType * battery_status) {
	u08 spi_get,size, i;
	u16 *ptr;

	spi_get = spiTransferByte(0xAA);	// Send 0xAA = ready to send								

	size = (u08)(((u16*)(battery_status + 1)) - ((u16*)(battery_status))); // Memory size of struct
	ptr = (u16 *)battery_status - 1;	 // Get u16-pointer to battery_status

	for (i=0;i<size;i++) {
		ptr ++;

		spi_get = spiTransferByte((u08)(*ptr&0x00FF));	// Send low byte
		spi_get = spiTransferByte((u08)(*ptr>>8));		// Send high byte
	}
	if (spi_get) return 1;	// If send succesful
	else return 0;			// Fail
}
Så här initieras structen på slaven:

Kod: Markera allt

BatteryStatusType battery_status;

battery_status.SOC = 83;
battery_status.voltage = 50.342;
battery_status.current = 0.745;
battery_status.temperature = -4;
Denna kodsnutt visar hur en överföring triggas i mainloopen genom SS-pinnen.

Kod: Markera allt

// If SS pin is low do SPI-transfer
if(SS) {
	check = transferBatteryStatus(&battery_status);
	if (check) 	cbi(PORTB, PB5);	// Light LED
	else sbi(PORTB, PB5);	// Turn off LED
}
Så här ser koden ut som sparar ner de överförda bytsen i structen på mästaren:

Kod: Markera allt

u08 batteryGetStatus(BatteryStatusType* battery_status) {
	u08 i, hi, lo, size, data;
	u16 *ptr;

	size = (u08)(((u16*)(battery_status + 1)) - ((u16*)(battery_status))); // Memory size of struct

	cbi(BATTMON_PORT, BATTMON_SS);	// Set SS low

	// Send command byte, command to recieve data, wait for 0xAA = ready to send 
	for (i=0;i<40;i++) {
		data = spiTransferByte(0xCC);
		rprintf("command 0xCC, %d, %x\r\n", i, data);
		if (data==0xAA) break;
	}

	// If answer is AA = "ready to send"
	if (data==0xAA) {

		ptr = (u16 *)battery_status -1; // Get u16 pointer to battery_status
		for (i=0;i<size;i++) {
			ptr++;						// Increase pointer

			lo = spiTransferByte(0xFF);
			pauseMs(1);		//Wait for slave to initiate			
			hi = spiTransferByte(0xFF);	
	
			rprintf("Data %d high: %d, %x \r\n", i, lo, lo);
			rprintf("Data %d low:  %d, %x \r\n", i, hi, hi);

			*ptr = (u16)(hi<<8)|(lo&0x00FF);			
		}
		sbi(BATTMON_PORT, BATTMON_SS);	// Set SS high
		return data;
	}	
	else {
		sbi(BATTMON_PORT, BATTMON_SS);	// Set SS high
		return 0;
	}
}
Och så här ser datan ut som printas genom UART från mästaren:

Kod: Markera allt

command 0xCC, 0, 00AA
Data 0 high: 83, 0053
Data 0 low:  53, 0035
Data 1 high: 94, 005E
Data 1 low:  73, 0049
Data 2 high: 66, 0042
Data 2 low:  82, 0052
Data 3 high: 184, 00B8
Data 3 low:  62, 003E
Data 4 high: 63, 003F
Data 4 low:  252, 00FC

battery_status.SOC = 83
battery_status.voltage = +50.341
battery_status.current = +0.7450
battery_status.temperature = -4
Voila! :waving:
Skriv svar