ATTINY85: respond as a slave with the USI on SPI mode

Go To Last Post
10 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hello,

First of all, sorry for my poor english, I'm french :).
So this is my problem:

I'm currently working on a system where an ATmega328p (master) communicate whith an ATtiny85 (slave) through SPI.
I managed to send bytes with the ATmega328p but the USI give me some issues: I can read a byte sent by the master but I can't send any response.

I'm trying since yesterday but i don't have any result...

 

This is my code for the Attiny85(SLAVE):

/*
* UC2.c
*
* Created: 20/03/2020 15:53:57
* Author : Onion
*/

//horloge de 1MHz

#include <avr/io.h>
#include <avr/interrupt.h>

void SPI_USI_init(){
	//activation des ports
	//MOSI:	PB0 input
	//MISO:	PB1 output
	//SCK:	PB2 input
	/*DDRB|=0x02; //0000 0010
	DDRB&=~0x05;//0000 0101
	USICR=0x52;//0 1(COV interrupt) 01(3 wires) 100(master clock ?) 0   */

	USICR = ((1<<USIWM0)|(1<<USICS1));
	USICR |= (1<<USIOIE);
}

ISR (USI_OVF_vect){

	uint8_t reception=USIDR;

	if(reception == 0x01){
		PORTB^=0x10; //PB3 test
		PORTB^=0x10; //PB3 test
	}

	USISR = 1<<USIOIF;

	USIDR = 0x05;//ACK ping

	while ( (USISR & (1<<USIOIF)) == 0 ) {
		USISR|=(1<<USITC);
	}

}

int main(void)
{

	DDRB|=0x10; //PB4 test 0001 0000
	PORTB&=~0x10;

	SPI_USI_init();

	sei();

	for(;;){
	}
}

So as you can see, my ATmega328p is sending 0x01 and wait for 0x05 as a response.

The ATtiny blink the led on PB4 so it "hears" the 0x01

I have an old scope which show that there is only 8 clock cycles (instead of 16) but the trigger don't really work so it can be a fake info.

 

This is the function I use on the ATmega328p(MASTER):

uint8_t SPI_MasterEnvoieReception(unsigned char data){
	SPDR = data;
	while(!(SPSR & (1 << SPIF)));

	return SPDR;
}

used on a timer event:

//Heartbeat du salve SPI
if(SPI_MasterEnvoieReception(0x01)!=0x00){ //envoie de 0x01 (0x02 ACK)
	USART0_sendByte('^'); //envoie de '^' pour heartbeat de salve SPI
	if(SPI_MasterEnvoieReception(0x01)==0x05){
            USART0_sendByte('!');
        }
}else{
	USART0_sendByte('%');
}

My UART term only send me: "*%" instead of "*!" for a good reception

 

 

ALSO:

I didn't change any Fuse (I have several bad experiences with that :D)

This is NOT a hardware issue (I tried with multiple ATtiny85 and the SPI is correctly wired)

 

So I really need help, I'm desperate... It looks so simple but it don't work, I tried all the combination.
I also searched on internet and I didn't find anything, the datasheet don't explain a lot about sending as a slave... My only hope is to find answer here.

Thanks you very much for your help for this post and, for all the time I was on this website finding solution to my ATproblems. You are amazing ! :)

 

PS: I know that i can do it with Bit Banging but I really want to do it with the hardware

 

This topic has a solution.
Last Edited: Thu. Mar 26, 2020 - 07:00 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Welcome to AVRFreaks!

 

SPI is a full duplex protocol, so both sides send at the same time, i.e. while the master is clocking out 8 bits, so does the slave!

One way to get around this is for the master to send data to the slave, the slave loads its response in the data register and signals the master data it is ready by wiggling a pin,

then the master sends a dummy byte(usually 0x00 or 0xFF, but can be any value) to the slave to clock out its reply.

Another way is for the master to just wait some time, (so the slave can load its reply) then sends a dummy byte to retrieve the data from the slave....

 

Jim

BTY: your English is fine, much better then my French! 

 

 

 

 

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

One more little quirk, the SS pin of the Mega needs to be an output, even if it not used!

 

 

 

 

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Oh my god, I was so focused on the USI that I didn't think about it, i took the SPI like an I2C laugh
So, your solution is to write this:

 

uint8_t SPI_MasterEnvoieReception(unsigned char data){
	SPDR = data;
	while(!(SPSR & (1 << SPIF)));
        
        //wait a bit
        SPDR = 0;
        while(!(SPSR & (1 << SPIF)));


	return SPDR;
}

 

Is this correct ?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

ki0bk wrote:

One more little quirk, the SS pin of the Mega needs to be an output, even if it not used!

 

 

 

I'm using it on another part of the code to control a PWM, so it's an output (sorry for forgeting to mention it, my code is huge and seems irrelevant to my problem)

Last Edited: Wed. Mar 25, 2020 - 05:42 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Onion2222 wrote:
Is this correct ?

Yes

 

 

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The reception don't work now :(
I managed to get my scope working so i clearly see the 16 clock cycles with the 0x01 I'm sending but now the ATttiny85 don't see the byte.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

EDIT: I forgot something on the code, the ATtiny is receiving 1 on 3 bytes, but don't send anything

This is the SPI_init on the ATmega328P(MASTER):

void SPI_init(){
	// MOSI et SCK en output le reste en input
	//MOSI:	PB3
	//SCK:	PB5
	//MISO:	PB4
	DDRB |= (1<<3)|(1<<5); //SCK MOSI output
	DDRB &= ~(1<<4); //MISO input

	//activation du SPI en master avec prescaler de /128
	SPCR = (1<<SPE)|(1<<MSTR)|(1<<SPR1)|(1<<SPR0);
}

 

Last Edited: Wed. Mar 25, 2020 - 06:37 PM
This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Oh, nice... I managed to get my code working !

 

This is my code:

ATtiny85(SLAVE/USI):

The ATtiny has to respond 0x05 to a 0x01 sent by the master

/*
* UC2.c
*
* Created: 20/03/2020 15:53:57
* Author : Onion
*/

//horloge de 1MHz

#include <avr/io.h>
#include <avr/interrupt.h>



void SPI_USI_init(){
	//activation des ports
	//MOSI:	PB0 input
	//MISO:	PB1 output
	//SCK:	PB2 input? output selon datasheet -> marche avec: INPUT
	DDRB|=0x02; //0000 0010
	DDRB&=~0x05;//0000 0101
	/*USICR=0x52;//0 1(COV interrupt) 01(3 wires) 100(master clock ?) 0   */

	USICR = ((1<<USIWM0)|(1<<USICS1));
	USICR |= (1<<USIOIE);
}

ISR (USI_OVF_vect){
	PORTB^=0x10; //PB3 test
	uint8_t reception=USIDR;

	if(reception == 0x01){
     USIDR = 0x05;//ACK ping

	}

	USISR = 1<<USIOIF;

	PORTB^=0x10; //PB3 test

}

int main(void)
{
	//PORTB=0xFF;//0000 1000
	DDRB=0x10;
	DDRB|=0x10; //PB4 test 0001 0000
	PORTB&=~0x10;

	SPI_USI_init();

	sei();

	for(;;){
	}
}

 

Thanks for you help.
A long sleeping night succeed to bring me new ideas... 15hours of coding a day during 3 days is not good for my mental health/coding skill :D

 

Last Edited: Thu. Mar 26, 2020 - 10:09 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Onion2222 wrote:
I managed to get my code working !

Excellent!

 

Now please see Tip #5 in my signature

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...