1
\$\begingroup\$

I'm trying to get SPI communications working on a ATmega1284P. This is the code I'm using:

// clocked by external 12.000 MHz crystal, lfuse=0xce
#define F_CPU 12000000

#include <avr/io.h>

#define SPI_SCK PB7
#define SPI_MISO PB6
#define SPI_MOSI PB5
#define SPI_SS PB4
#define SPI_PORT PORTB
#define SPI_DDR DDRB
#define SPI_PIN PINB
//#include "spi.h"
//#include "serial.h"

void initUart()
{
#define BAUD 38400
#include <util/setbaud.h>
  UBRR0H = UBRRH_VALUE;
  UBRR0L = UBRRL_VALUE;
#if USE_2X
  UCSR0A |= _BV(U2X0);
#else
  UCSR0A &= ~_BV(U2X0);
#endif
  UCSR0B |= (_BV(RXEN0) | _BV(TXEN0));
  UCSR0C |= (_BV(UCSZ01) | _BV(UCSZ00));
}

void cOut(unsigned char c)
{
  while (!(UCSR0A & _BV(UDRE0)))
    ;
  UDR0 = c;
}

inline void spi_init()
{
  // set MISO as input
  SPI_DDR &= ~(_BV(SPI_MISO));
  // set MOSI and SCK as output
  SPI_DDR |= (_BV(SPI_SCK) | _BV(SPI_MOSI));
  // enable SPI, master mode, and SPI prescaler
  SPCR = (_BV(SPE) | _BV(MSTR) | _BV(SPR1) | _BV(SPR0));
//  SPCR = (_BV(SPE) | _BV(MSTR));
}

unsigned char spi_transact(unsigned char c)
{
  cOut('t');
  // put byte to transmit into buffer
  SPDR = c;
  cOut('+');
  // wait until the SPI timer has overflowed
  while (!(SPSR & _BV(SPIF)))
    ;
  cOut('T');
  // return the byte transferred in via SPI
  return SPDR;
}

int main()
{
  // disable JTAG for full access to port C
//  register unsigned char regTemp = MCUCR | _BV(JTD);
//  MCUCR = regTemp;
//  MCUCR = regTemp;

  initUart();
  SPI_DDR |= SPI_SS;
  spi_init();

//  sOut_P(CRLF);
  cOut('\r');
  cOut('\n');

//  SPI_PORT |= SPI_SS;
  cOut('1');
  spi_transact(0xa0);
  cOut('2');
  spi_transact(0);
  cOut('3');
  spi_transact(0);
  cOut('4');
  spi_transact(0x12);
  cOut('5');
  spi_transact(3);
  cOut('6');
  spi_transact(1);
  cOut('7');
  spi_transact(1);
  cOut('8');
  spi_transact(0x13);
  cOut('9');
//  SPI_PORT &= ~SPI_SS;
//  sOut_P(CRLF);
  cOut('\r');
  cOut('\n');
  cOut('!');

//  sOut("Clock reset\r\n");
}

I currently have nothing actually connected to the SPI pins yet, as I'm trying to see if I can get it working before I connect the SPI slave to it.

My terminal outputs "1t+T2t+" from the UART, showing a stall on waiting for SPIF to be set, but if I touch the end of a lead connected to SCK it does eventually get past that and completes the SPI session. Why would loading SCK externally make it work, when the clock is supposed to be generated by the master internally?

\$\endgroup\$
5
  • \$\begingroup\$ I suggest you don't put an include in the middle of the code. Typically these are placed at the very beginning of the code with the rest of the includes. The same with defines. \$\endgroup\$ Oct 21, 2013 at 20:54
  • \$\begingroup\$ @GustavoLitovsky: <util/setbaud.h> was explicitly designed to work when included in the middle of code, even multiple times. \$\endgroup\$ Oct 21, 2013 at 20:59
  • \$\begingroup\$ I realize it is designed to work because of inclusion guards, but it is not standard practice in C coding and could result in issues in the future when including other headers. Just a warning, it is your code after all. \$\endgroup\$ Oct 21, 2013 at 21:10
  • \$\begingroup\$ @GustavoLitovsky: He's not relying on inclusion guards. He's using it as an abbreviated way to include a chunk of functional code. Still not standard practice; at the very least, I'd recommend renaming the file to have a .c extension (which can also be included, but will give a better indication as to the purpose of the include). (Other extensions could be considered as well: .inc, .macro, .donotcompile...) \$\endgroup\$ Oct 21, 2013 at 21:18
  • \$\begingroup\$ @ScottWinder - Now I understand. \$\endgroup\$ Oct 21, 2013 at 21:26

1 Answer 1

4
\$\begingroup\$

I haven't verified this since I have no hardware at hand but i think I see the problem.

You are not initializing the direction of the SS pin properly.

SPI_DDR |= SPI_SS;

sets the wrong pin (PB2). It should be

SPI_DDR |= _BV(SPI_SS);

with _BV() actually setting the fourth bit (SPI_SS = PB4 = 4).

As to the strange behavior that may result from not setting SS as an output.

If SS is configured as an input, it must be held high to ensure Master SPI operation. If the SS pin is driven low by peripheral circuitry when the SPI is configured as a Master with the SS pin defined as an input, the SPI system interprets this as another master selecting the SPI as a slave and starting to send data to it.

This would cause your system to become a SPI slave, thus requiring an external clock to send further data and trigger the next SPIF. When touching the SCK, you may induce ripple that acts as a clock.

\$\endgroup\$
1
  • \$\begingroup\$ Yes, of course that was it. facepalm \$\endgroup\$ Oct 21, 2013 at 20:26

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.