ATMega16 - SPI - ATMega16 => Not responding

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

Subject : ATMega16 - SPI - ATMega16 => Not responding

Hardware : Two different ATMega16 kits, both working
for programming and running LED output, switch inputs,
uart, etc.

Software : WinAVR-GCC 4.1.2 20070525
(using past few months working).

Develpment : Programmers Notepad 2
Programming : (1) Dedicated ISP (2) LancOS PonyProg v2.07c
(for the two different kits) Both robust and working.

Familiar with LEDs, LCD, UART, Timer, 4x4-Switches etc.

Now learning about SPI. Background search ATMEL especially
ATMEL ATMega16 2466L-AVR-06/05 and 2466N-AVR-10/06, AVR151
Setup and Use of SPI Rev 2585B-AVR-09/05, AVRFreaks Design
Notes #035, Other AVR SPI Application notes and various
forum notes of AVRFreaks, ScienceProg etc. Nowhere a full
simple working of SPI between two uC was found. From the
datasheets and their code snippets following two codes
(1) Master (2) Slave have been written. On loading
respective codes to the two different kits, it seems there
is no communication between the two controllers (ATMega16).
It was expected that LEDs on respective PORTD should indicate
the initial value being sent viz 'A'.

For diagnosing, set of 8-LEDs at respective PORTD of Master
and Slave ATMega16 have been connected with suitable delays
and programmed. Respective MOSI-MOSI, MISO-MISO, SCK-SCK
have been verified to be connected. The Master's /SS is
connected to Vcc, the Slave's /SS is connected to ground.
The fuse-bits on both ATMegas' have been checked for
correctness (other codes working okay).

After every initial startup of Slave, there is always some
different LEDs displayed; also for Master there is never
any display on LEDs even at startup. In this 'on' state
if the MOSI-MISO-SCK cable between Master and Slave is
removed and Master is reset then too the LEDs on Master
does not glow. I.E. Master is hung. However if after
removing the MOSI-MISO-SCK cable, the ATMegas are then
powered on, the Master's LEDs go through the sequence
as programmed. Even after the Master is reset the
initial startup of LEDs takes place every time. But
Slave after each initial LED sequence shows different
data. The Slave LEDs do not change when Master is
reset. Presently there is no reset switch on the
Slave kit hence could not try. I do not have an
oscilloscope, but my digital multimeter does indicate
some high frequency on MOSI, MISO and SCK lines of
Master.

Conclusion
(1) if MOSI-MISO-SCK cable is pre-connected, the Master
latches up at start.
(2) Even if no transmission takes place the Master program
goes through to the end (it should have stopped at transmit
as there is error). [ when SPI is disconected ].
(3) When Master transmits, the data in Slave should have
come to Master and seen on its LEDs, but never seen.

The Master and Slave SPI codes are included herein for
reference. PORTD to LEDs with delays is being used for
debugging the program.

Please may I humbly seek your able guidance to indicate
where I am making a mistake.

Many Thanks in advance.
India_AVR

MASTER :

/*     V11a_SPI_MST\main.c    */

/*  ATMega16 SPI - 2446N-AVR-10/06
    missing codes from elsewhere threads
    and deduced from datasheets
	
	F_OSC = 1000000
	
	Master		  Slave
	    MOSI+---+MOSI---------> PB5
       	    MISO+---+MISO---------> PB6
	    SCK	+---+SCK----------> PB7
	Vcc-/SS	+   +/SS-GND------> PB4
			
	(PD)8LED[   ]8LED(PD)
	
	18-Jan-2008
*/

#include 
#include 

#define DD_MOSI DDB5
#define DD_SCK  DDB7
#define DD_MISO  PINB6
#define DDR_SPI PORTB

typedef unsigned long u32;

// #define DD_LED PORTD	// signal out
void PortInit(void);
void Delay(u32 count);

void SPI_MasterInit(void);
void SPI_MasterTransmit(char cData);

void PortInit(void)
{
   /*
   DDRB  = 0XB0;	// SCK=1, MISO=0
			// MOSI=1, /SS=1
			// rest=0
   This defined in SPI_MasterInit
   */
   DDRD  = 0xFF;	// Init PD o/p
   PORTD = 0xFF;
   _delay_ms(10000);
   _delay_ms(10000);
   _delay_ms(10000);
   _delay_ms(10000);
   _delay_ms(10000);
   _delay_ms(10000);
   //PORTD = ~(0x55);
   // PORTD = ~(0x41); // 'A'
   PORTD = ~(0xff);
   _delay_ms(10000);
   _delay_ms(10000);
   _delay_ms(10000);
   _delay_ms(10000);
   _delay_ms(10000);
   _delay_ms(10000);
}


// A delay loop
void Delay(u32 count)
{
    while(count--);
}


void SPI_MasterInit(void)
{
   /* Set MOSI and SCK output all others input 
      This agrees to AVR151 Table 1 also */
   DDR_SPI = (1<<DD_MOSI) | (1<<DD_SCK); // ie DDRB=0xA0
   
   /* Enable SPI, Master, set clock rate fck/16 */
   SPCR = (1<<SPE) | (1<<MSTR) | (1<<SPR0); // ie SPCR=0x51
   
   /* Chip select not applicable = PB0...PB4 ?
   PORTB &= ~(1<<4); // hence PB4 masked off ?
   PB0...PB4 can be used for other device RST, RDY
   select, START, etc o/p or i/p as reqd define pins 1st
   */
   
   /* write SPI2X = 0, read-only => SPIF, WCOL 
   SPSR = 0x00; // may define else default = 0x00
   [ SPCR(SPR1=0,SPR0=1) + SPSR(SPI2X=1) ] => mode x
   somewhere MST & SLV mode should be = ?  
   */
}


void SPI_MasterTransmit(char cData)
{
   /* Start transmission */
   //SPDR = cData;
   PORTD = cData;   // 'see' cData on LEDs
   SPDR = cData;
   
   /* Wait for transmission complete */
   while ( ! (SPSR & (1<<SPIF)))
   ;
}


int main(void)
{
   PortInit();
   SPI_MasterInit();
   SPI_MasterTransmit(~('A'));
   PORTD = SPDR;
   _delay_ms(10000);
   _delay_ms(10000);
   _delay_ms(10000);
   _delay_ms(10000);
   _delay_ms(10000);
   _delay_ms(10000);
   PORTD = 0x55;
   _delay_ms(10000);
   _delay_ms(10000);
   _delay_ms(10000);
   _delay_ms(10000);
   _delay_ms(10000);
   _delay_ms(10000);
   /* Above line to check should not reach
   if SPI_MasterTransmit does not work in
   completing the buffer sent and received
   back !
   */
   while(1)
   {
   }
}   

SLAVE:

/*     V11a_SPI_SLV\main.c    */

/*  ATMega16 SPI - 2446N-AVR-10/06
    missing codes from elsewhere threads
    and deduced from datasheets
	
	F_OSC = 1000000
	
	Master		  Slave
	   MOSI+---+MOSI---------> PB5
	   MISO+---+MISO---------> PB6
	   SCK +---+SCK----------> PB7
	Vcc-/SS+   +/SS-GND------> PB4
			
       (PD)8LED[   ]8LED(PD)
			
	18-Jan-2008
*/

#include 
#include 

#define DD_MISO PB6	// signal in => OUT  VSM
#define DDR_SPI DDRB	// signal in to set bits
#define DD_MOSI PINB5	// signal in - VSM
#define DD_SCK  PINB7	// signal IN - VSM


typedef unsigned long u32;

// #define DD_LED PORTD	// signal out
void PortInit(void);
void Delay(u32 count);

void SPI_SlaveInit(void);
char SPI_SlaveReceive(void);


void PortInit(void)
{
   /*
   DDRB  = 0X40;	// SCK=0, MISO=1
			// MOSI=0, /SS=0
			// rest=0
	Defined in SPI_Slave_Init				
   */

   DDRD  = 0xFF;	// Init PD o/p
   //PORTD = 0xFF;
   // PORTD = 0x55;   // alt LEDs glow
   PORTD = 0x41;
   //Delay(6000);
   _delay_ms(10000);
   _delay_ms(10000);
   _delay_ms(10000);
   _delay_ms(10000);
   _delay_ms(10000);
   _delay_ms(10000);
   // PORTD = ~(0x55);
   PORTD = ~(0x41);  // 'A'
   _delay_ms(10000);
   _delay_ms(10000);
   _delay_ms(10000);
   _delay_ms(10000);
   _delay_ms(10000);
   _delay_ms(10000);
}


// A delay loop
void Delay(u32 count)
{
    while(count--);
}


void SPI_SlaveInit(void)
{
   /* Set MISO as output all others input 
      This agrees to AVR151 Table 1 also */
   DDR_SPI = (1<<DD_MISO); // DDRB = 0x40
   
   /* Enable SPI */
   SPCR = ((1<<SPE)|(1<<SPR0));  // SPCR = 0x80
   // above SPRO set as MST & SLV modes to be same
   
   /* being only 1 slave seperate select
      not done ? Forced by /SS => GND
      PORTB PB4 connect to MST one of 
      selection pins ? See comments in MST
	  
   SPSR = 0x00; by default ie SPI2X=0
   */
}

char SPI_SlaveReceive(void)
{
   SPDR = 0x33; 
   /* to see if this gets to Transmitter 
      in exchange when it sends data
   */
   
   /* Wait for reception complete */
   while ( ! (SPSR & (1<<SPIF)))
   {
   PORTD = SPDR;   // 'see' cData on LEDs
   
   /* Return data register */
   return SPDR;
   }
}   // compiler warning void => return ?


int main(void)
{
   PortInit();
   SPI_SlaveInit();
   SPI_SlaveReceive();
   while(1)
   {
   }
}   

-----------------------------------------
Wonderful world of "0"s & "1"s
-----------------------------------------

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

Remember that SS on the master must either be configured as output all the time or tied high if likely to be an input otherwise a transition to low while it's input will switch from master to slave operation. This is the SPI issue that catches most people - but you ought to be driving an SS out of the master and into the slave to keep the shift registers in sync anyway and as long as you drive it as an output on the master you should be OK

BTW always use / and never \ in include paths and large values of parm to _delay_ms() only work if you are using the December 2007 version of WinAVR

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

Clawson:

Corrected \ to /, thanks. As per ATMEL doc AVR151, the /SS of Master was connected to Vcc and /SS of Slave was connected to Ground. But for synching, as correctly indicated by you, I tried /SS connected to /SS respectively. But in both cases the problem remains, viz, if the MOSI-MISO-SCK cable set are connected the Master "hangs". With MOSI-MISO-SCK cable set disconnected the Master "runs". (Slave always independantly
runs, except ofcourse no data transfer). Incidently regarding the _delay_ms(), now I did notice actual delay is not 10000ms, I was just using it for visible delay. My initial problem remains.
Requesting for guidance. Many thanks in advance.
India_AVR

-----------------------------------------
Wonderful world of "0"s & "1"s
-----------------------------------------

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

Update :
(1) The problem of LEDs always "off" on Master was identified to the Slave COM(Program)/UART "D9" connector was always connected to PC and to the TX0(PD0)/RX0(PD1) of ATMega16. When this D9 was removed and program was run the LEDs on Master are functioning correct. [This problem did not appear when the Slave kit was being tested alone without SPI cabling].
(2) The Master /SS connected to Slave/SS and initialisation of DDR_SPI setting of DD_SCK as output in Master. Still running it at fck/16. F_OSC=1000000. The Slave gets initialised okay. But the Master does not transfer data to Slave.

(a) Should I make SPI-clock slower (minimum = fck/128) ?
(b) Should I add delay in Master so that data sent is after the Slave has had time to stabilise ?
(c) Should I send data twice (read this somewhere, but applicable only to read from Slave) ?

I have reproduced "Leaner" base minimum code for easy viewing
Master :

/*     V11a_SPI_MST\main_.c    */

/*  F_OSC = 1000000
	1210 : 18-Jan-2008 start
	1515 : 20-Jan-2008 minimum comments
*/

#include 
#include 

#define DD_SS   DDB4	// o/p
#define DD_MOSI DDB5	// o/p
#define DD_MISO PINB6	// i/p
#define DD_SCK  DDB7	// o/p
#define DDR_SPI PORTB	// o/p

typedef unsigned long u32;

void PortInit(void);

void SPI_MasterInit(void);
void SPI_MasterTransmit(char cData);


void PortInit(void)
{
   // DDRB defined in SPI_MasterInit
   
   DDRD  = 0xFF;   // Init PD o/p
   PORTD = 0x55;   // alt LEDs glow
   
}

void SPI_MasterInit(void)
{
   // Set MOSI, SCK, /SS output all others input 
   DDR_SPI = ((1<<DD_MOSI)|(1<<DD_SCK)|(1<<DD_SS)) ; 
   
   // Enable SPI, Master, set clock rate fck/16
   SPCR = (1<<SPE) | (1<<MSTR) | (1<<SPR0);
   
   // SPSR = 0x00; // may define else default = 0x00
   
}


void SPI_MasterTransmit(char cData)
{
   /* Start transmission */
   PORTD = cData;   // display Master data
   SPDR  = cData;
   
   /* Wait for transmission complete */
   while ( ! (SPSR & (1<<SPIF)))
   ;
}


int main(void)
{
   PortInit();
   SPI_MasterInit();
   SPI_MasterTransmit(~('A'));
   // PORTD = SPDR;  // display Slave data
   while(1)
   {
   }
}   

Slave :

/*     V11a_SPI_SLV\main_.c    */

/*  F_OSC = 1000000
	
	1235 : 18-Jan-2008 start
	1530 : 20-Jan-2008 minimum comments
*/

#include 
#include 

#define DD_MISO PB6	// o/p
#define DDR_SPI DDRB	// i/p
#define DD_MOSI PINB5	// i/p
#define DD_SCK  PINB7	// i/p

void PortInit(void);

void SPI_SlaveInit(void);
char SPI_SlaveReceive(void);


void PortInit(void)
{
   // DDRB defined in SPI_Slave_Init
   
   DDRD  = 0xFF;	// Init PD o/p
   PORTD = 0x55;	// alt LEDs glow
}


void SPI_SlaveInit(void)
{
   /* Set MISO as output all others input */
   DDR_SPI = (1<<DD_MISO); // DDRB = 0x40
   
   /* Enable SPI */
   SPCR = ((1<<SPE)|(1<<SPR0));
   // above SPRO set as MST & SLV modes to be same
   
   // SPSR = 0x00; // also by default 
   
}


char SPI_SlaveReceive(void)
{
   // SPDR = 0x33; 
   /* to see if this gets to Master
   in exchange when it sends data */
   
   // Wait for reception complete 
   while ( ! (SPSR & (1<<SPIF)))
   {
   PORTD = SPDR;   // 'see' cData on LEDs
   
   /* Return data register */
   return SPDR;
   }
}

int main(void)
{
   PortInit();
   SPI_SlaveInit();
   SPI_SlaveReceive();
    while(1)
   {
   }
}   

Humbly awaiting kind advice to proceed in right direction. Many thanks in advance.

India_AVR

-----------------------------------------
Wonderful world of "0"s & "1"s
-----------------------------------------

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

The two things I'd do to resolve this are:

(a) use a scope on the MISO/MOSI/SCK/SS lines to see that they are being driven as expected, and

(b) use a JTAG to one, other or both mega16 to see that the code is operating as expected - debug trace statements to a UART output would be a more inferior alternative if a JTAG interface were not available.

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

In the master you have

#define DDR_SPI PORTB   // o/p 
#define DDR_SPI DDRB

would be better :D
/Lars

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

Clawson:
Thanks for your kind guidance. I am learning at home and have no access to a scope or JTAG. I was using LEDs as debug, the program would cross over the "SPI_MasterTransmit"; was expecting to halt there, if there was a transmission error, by "Wait for transmission complete". I was avoiding using LCD initially to keep code simple. (I will require 2 LCDs - Master & Slave).
Lars:
Thanks for the correction. Also after reading a "Sparkfun" code but slave being an accelerometer, it seems in Master I need to "/SS high" "before" MOSI-SCK are set. I will revert.
Best Wishes,
India_AVR

-----------------------------------------
Wonderful world of "0"s & "1"s
-----------------------------------------

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

Clawson & Lars :
Very sorry this thread got suspended as :-
(1) The Master and Slave code snippets picked up from ATMEL datasheets I realised may not work together as is.
(2) The "defines" seemed to be incorrect.
(3) I received some direct help from Mr Dean Camera which I started off afresh.
In view of above, I propose to start a "fresh" thread as the new code after tweaking a bit works, but requires hardware "resets" every time. With further additional polling code in Master and Slave the same gets stuck. I seek the forum's permission to start a fresh post.
India_AVR

-----------------------------------------
Wonderful world of "0"s & "1"s
-----------------------------------------

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

Hi all,

I'm reviving this long dead thread to post a solution I developed by reading on a few forums.

The code is located at:
http://code.google.com/p/awesome-o/source/browse/#svn/trunk/atmega16-spi-atmega16

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

Master:



#include 
#include 

/* ATMEGA16 SPI configuration */
#define SPI_SS   PB4
#define SPI_MOSI PB5
#define SPI_MISO PB6
#define SPI_SCK  PB7
#define SPI_DDR  DDRB
#define SPI_PORT PORTB

/* 
 * proper communication is checked by synchronuous 
 * flashing leds on the master and slave 
 */
#define DEBUG_DDR  DDRD
#define DEBUG_PORT PORTD
#define DEBUG_LED  PB6

void debug_led_init(void)
{
  /* set the pin connected to the LED as output */
  DEBUG_DDR = _BV(DEBUG_LED);
}

void debug_led_on(void)
{
  DEBUG_PORT |= _BV(DEBUG_LED);
}

void debug_led_off(void)
{
  DEBUG_PORT &= ~(_BV(DEBUG_LED));
}

void spi_master_init(void)
{
  /* Set MOSI, SCK  and SS output, all others input.
   * IMPORTANT: SS must be set as output on MASTER to
   * prevent the master from switching to slave state.
   */
  SPI_DDR = _BV(SPI_MOSI)|_BV(SPI_SCK)|_BV(SPI_SS);
  /* Enable SPI, Master, set clock rate fck/16 */
  SPCR = _BV(SPE)|_BV(MSTR)|_BV(SPR0);

  /* set SS to HIGH => the slave is not 
     selected for data transfers */
  SPI_PORT |= _BV(SPI_SS);
}

/*
 * Before transmitting any bytes the master chip 
 * must select the destination slave.
 * Slave selection is done by setting the slave's
 * SS pin to LOW.
 */
void spi_master_begin_transmission()
{
  SPI_PORT &= ~_BV(SPI_SS);
}

void spi_master_transmit(char data)
{
  /* start transmission */
  SPDR = data;
  /* wait for transmission complete */
  while(! (SPSR & (1<<SPIF)))
    ;
}

/*
 * After transmitting data, the master chip will tell the 
 * destination slave it finished.
 * The slave is "unselecting" by putting it's SS pin to HIGH.
 */
void spi_master_end_transmission()
{
  SPI_PORT |= _BV(SPI_SS);
}


int main(void)
{
  /* boolean flag: is the debug led on? */
  char led_on = 0;
  
  debug_led_init();
  spi_master_init();

  for(;;)
  {
    led_on = !led_on;
    if (led_on)
      debug_led_on();
    else
      debug_led_off();


    spi_master_begin_transmission();
    spi_master_transmit(led_on);
    /* here we can call spi_master_transmit()
       multiple times to transmit more than one byte. */
    spi_master_end_transmission();
  
    /* the delay is not needed by SPI but by us
       to see the leds flashing */
    _delay_ms(5000);
  }
}

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

Slave


#include 
#include 

/* ATMEGA16 SPI configuration */
#define SPI_MISO PB6
#define SPI_DDR  DDRB
#define SPI_PORT PORTB

/* proper communication is checked by synchronuous
   flashing leds on the master and slave */
#define DEBUG_DDR  DDRD
#define DEBUG_PORT PORTD
#define DEBUG_LED  PB6

void debug_led_init(void)
{
  /* set the pin connected to the LED as output */
  DEBUG_DDR = _BV(DEBUG_LED);
}

void debug_led_on(void)
{
  DEBUG_PORT |= _BV(DEBUG_LED);
}

void debug_led_off(void)
{
  DEBUG_PORT &= ~(_BV(DEBUG_LED));
}


void spi_slave_init(void)
{
  /* Set MISO output, all others input */
  /* IMPORTANT: SS is INPUT!
     => when the master sets the line to LOW
        we'll start receiving data */
  SPI_DDR = _BV(SPI_MISO);

  /* Enable SPI */
  SPCR = _BV(SPE);
}

char spi_slave_receive(void)
{
  /* Wait for reception complete */
  while(!(SPSR & _BV(SPIF)))
    ;
  /* Return data register */
  return SPDR;
}


int main(void)
{
  /* boolean flag: is the debug led on? */
  char led_on = 0;
  
  debug_led_init();
  spi_slave_init();

  for(;;)
  {
    led_on = spi_slave_receive();
    if (led_on)
      debug_led_on();
    else
      debug_led_off();
  }
}