SPIF Flag Strange Behavior

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

Hi all -


It appears that the SPIF flag is set on the first SPI transmit and then fails to set on the second.


To my understanding (based on the datasheet), this code should transmit 10 bytes over SPI. However, it appears as if it transmits 1 byte and starts but never finishes transmitting a second byte (based on the serial debug output).



#include <avr/io.h>
#include <avr/pgmspace.h>
#include "usart.h"

// SPI pins and such
#define CS_LOW()	PORTB &= ~_BV(PORTB7)	/* Set CS low */
#define	CS_HIGH()	PORTB |=  _BV(PORTB7)	/* Set CS high */
#define	IS_CS_LOW	!(PINB & _BV(PINB7))	/* Test if CS is low */

#define FCLK_SLOW() SPCR |= _BV(SPR1) | _BV(SPR0)   /* Set slow clock (F_CPU / 64) */
#define FCLK_FAST() SPCR &= ~_BV(SPR1) & ~_BV(SPR0)   /* Set fast clock (F_CPU / 2) */

// USART debug messages
#define LEN_STARTUP 13
const char PROGMEM startup[] = "\r\n\r\nstartup\r\n";

#define LEN_INIT_S 12
const char PROGMEM init_s[] = "init start\r\n";
#define LEN_INIT 7
const char PROGMEM init[] = "init\r\n";
#define LEN_INIT_E 10
const char PROGMEM init_e[] = "init end\r\n";

#define LEN_XMIT_S 12
const char PROGMEM xmit_s[] = "xmit start\r\n";
#define LEN_XMIT 6
const char PROGMEM xmit[] = "xmit\r\n";
#define LEN_XMIT_E 10
const char PROGMEM xmit_e[] = "xmit end\r\n";

#define LEN_RCV_S 11
const char PROGMEM rcv_s[] = "rcv start\r\n";
#define LEN_RCV 5
const char PROGMEM rcv[] = "rcv\r\n";
#define LEN_RCV_E 9
const char PROGMEM rcv_e[] = "rcv end\r\n";

#define LEN_LOOP 6
const char PROGMEM loop[] = "loop\r\n";

void init_spi(void) {
  transmit_string_flash(init_s, LEN_INIT_S);

  // outputs
  DDRB |= _BV(DDB7) | _BV(DDB3) | _BV(DDB5);

  // turn on and configure SPI peripheral
  PRR &= ~_BV(PRSPI);
  SPCR = _BV(SPE) | _BV(MSTR);
  SPSR = _BV(SPI2X);

  // gotta go fAST

  transmit_string_flash(init_e, LEN_INIT_E);

void xmit_spi(uint8_t d) {
  transmit_string_flash(xmit_s, LEN_XMIT_S);
  SPDR = d;
  //loop_until_bit_is_set(SPSR, SPIF);
  while(!(SPSR & (1 << SPIF))) {}
  transmit_string_flash(xmit_e, LEN_XMIT_E);

uint8_t rcv_spi(void) {
  transmit_string_flash(rcv_s, LEN_RCV_S);
  transmit_string_flash(rcv_e, LEN_RCV_E);
  return SPDR;

int main(void) {
  transmit_string_flash(startup, LEN_STARTUP);

  // similar construct as in the petitFATFS code
  for(uint8_t n= 10; n; n--) {
    transmit_string_flash(loop, LEN_LOOP);


Serial Debug Output:

init start
init end
rcv start
xmit start
xmit end
rcv end
rcv start
xmit start


Platform Info: ATMEGA328, internal clock, fuses E:FF, H:D9, L:E2

Compiler Info: avr-gcc 9.2.0 built from scratch (including avr-libc) on Gentoo with crossdev-20191222


The circuit is identical to the schematic in the git repo below; the SPI lines are connected to the ISP and a microSD card reader breakout board with no card inserted.


This is part of a larger SD card based music player - full repository is at https://github.com/npiscitello/g.... The posted code is in the spi_debug folder. The funny thing is, this used to work - I had it playing music off the SD card about a month ago. This project uses petitFATFS so I've tried to mimic the SPI portion of the code in this minimal example.


I've tried multiple chips, countless code tweaks, and even re-generating the cross toolchain from scratch - I'm completely baffled. As far as I can tell, this complies with everything in the datasheet. I did try adding a dummy SPDR read after the wait loop in xmit_spi but it did not change anything. I have not tried simulating this code and I do not have a debugger.


Any help is greatly appreciated - this is one of the weirdest errors I've ever seen! It might be that I just need some fresh eyes on the code cool



This topic has a solution.

"Try assembler", they said!

"It'll be fun!", they said...

Last Edited: Tue. Jan 7, 2020 - 09:30 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

SPI is full duplex, ie it sends and receives at the same time, so you only need a single spi_xfer() function, rather than a send and a receive functions!

Perhaps that is the issue.




(Possum Lodge oath) Quando omni flunkus, moritati.

"I thought growing old would take longer"


This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I ran your code on the simulator. The MSTR flag is getting cleared because you don't handle the SS pin. configuring PB2 as an output appears to fix it in the simulator.

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

Like I said, fresh eyes...


I changed the SS pin (from B2) for board layout reasons and totally didn't even think about the floating input knocking the chip out of Master mode. Set B2 to output and it works perfectly.


Thanks so much balisong42!




For anyone else looking at this who likes to see the code: I changed this line (in the init_spi function):

DDRB |= _BV(DDB7) | _BV(DDB3) | _BV(DDB5);

to this:

DDRB |= _BV(DDB7) | _BV(DDB3) | _BV(DDB5) | _BV(DDB2);

and it fixed the problem.

"Try assembler", they said!

"It'll be fun!", they said...