Author Topic: ATmega328P SPI Is Stuck Waitng for the SPI Transmit Flag to Be Set  (Read 1517 times)

0 Members and 1 Guest are viewing this topic.

Offline KalciferTopic starter

  • Regular Contributor
  • *
  • Posts: 82
  • Country: ca
I'm having some issues trying to get SPI working with an ATmega328p. From what I understand from the datasheet, the SPIF flag is set when an SPI transfer is complete, and then is cleared when the SPI status register (SPSR) is first read, and then the SPI data register (SPDR) is read; however what seems to be happening is that I transfer the data, but the transmit flag is never being set. I have a while loop that waits for the flag to be set, but the microcontroller never leaves the loop for some reason.


Code: [Select]
void spi_init(void) // SPI initialization function.
{
  DDRB &=~ (1 << DDB4); // Ensure that the MISO pin is configured as in input
  DDRB |= (1 << DDB3); // Configure the MOSI pin as an output
DDRB |= (1 << DDB5); // Configure the SCK pin as an output

DDRD |= (1 << DDD6); // TX Slave Select (CSN) // I have two modules that I am communicating with over SPI. the first I have named TX and the second RX. Unrelated to the problem, but just for references sake.
PORTD |= (1 << PORTD6); // Default the TX slave select pin to be idle high as it is active low

DDRB |= (1 << DDB0); // RX Slave Select (CSN)
PORTB |= (1 << PORTB0); // Default the RX slave select pin to be idle high as it is active low

//SPCR |= (1 << SPIE); // Enable the interrupt
SPCR |= (1 << SPE); // Enable the SPI
SPCR &=~ (1 << DORD); // Transmit the data MSB first
SPCR |= (1 << MSTR); // Configure the SPI as Master
SPCR &=~ (1 << CPOL); // Idle the clock line (SCK) at logic low
SPCR &=~ (1 << CPHA); // Set the clock phase so that data is sampled on the leading edge, and setup on the trailing edge
}

uint8_t spi_transfer(uint8_t transfer_data) // SPI transfer function
{
SPDR = transfer_data; // Write the data to be transfered into the SPI data register which will instantiate the trasfer
  while(!(SPSR & (1 << SPIF))); // Wait until the transfer has complete <-- where the problem seems to be

return SPDR; // Return the data returned from the slave
}

When I call the transfer function, I first pull the specific slave select pin low, and then call the transfer function. After the transfer has complete, I pull the slave select pin back high. But what I am seeing is that the slave select pin is stuck low. It never gets pulled high. I have narrowed it down to the loop which waits for the interrupt flag to be set, as if I remove the loop, the transfer goes through (unsuccessfully I might add, but that's a separate issue I haven't pinpointed yet).
« Last Edit: May 10, 2021, 01:40:21 am by Kalcifer »
 

Offline KalciferTopic starter

  • Regular Contributor
  • *
  • Posts: 82
  • Country: ca
Re: ATmega328P SPI Is Stuck Waitng for the SPI Transmit Flag to Be Set
« Reply #1 on: May 10, 2021, 01:22:10 am »
What's also strange, is that when I probe the clock, and MOSI lines, there is no output - no clock, nor data. The slave select (CSN) line goes low, but then nothing else happens. The slave select line just stays low. In addition to that, right at the transition point for the slave select line, there's a little "blip" in the clock and MOSI lines, but that could just be current draw, I'm not entirely sure. Here's a picture of that https://imgur.com/a/QnuvkhT1217974-0" alt="" class="bbc_img" />
 

Offline WattsThat

  • Frequent Contributor
  • **
  • Posts: 769
  • Country: us
Re: ATmega328P SPI Is Stuck Waitng for the SPI Transmit Flag to Be Set
« Reply #2 on: May 10, 2021, 03:14:11 am »
while(!(SPSR & (1 << SPIF)));

Is not the same as:

while (!(SPSR & _BV(SPIF))) ;

PS: don't shift left by x80
« Last Edit: May 10, 2021, 03:16:45 am by WattsThat »
 

Online oPossum

  • Super Contributor
  • ***
  • Posts: 1421
  • Country: us
  • Very dangerous - may attack at any time
Re: ATmega328P SPI Is Stuck Waitng for the SPI Transmit Flag to Be Set
« Reply #3 on: May 10, 2021, 04:16:55 am »
while(!(SPSR & (1 << SPIF)));

Is not the same as:

while (!(SPSR & _BV(SPIF))) ;

PS: don't shift left by x80

How is it different?

sfr_defs.h
Code: [Select]
#define _BV(bit)   (1 << (bit))

iom328p.h
Code: [Select]
#define SPIF 7
 

Online pqass

  • Frequent Contributor
  • **
  • Posts: 731
  • Country: ca
Re: ATmega328P SPI Is Stuck Waitng for the SPI Transmit Flag to Be Set
« Reply #4 on: May 10, 2021, 04:37:53 am »
I've used this code in several places with great success.
It's part of a 7seg multiplex display where a '595 is a segment driver and CD4017+ULN2803 (SPISS connected to both CP0 and ST_CP) form the digit driver.
Substitute the Arduino APIs with your own equivalent.
I don't recall off-hand why, but I think it's important to read SPSR and SPDR, then delay 10ms as part of final spi setup.

Code: [Select]
#define SPISS    10      // 74HC595 ST_CP pin 12
#define SPIMOSI  11      // 74HC595 DS    pin 14
#define SPIMISO  12      // CD4017  Q0    pin  3
#define SPICLK   13      // 74HC595 SH_CP pin 11

void spi_setup() {
  byte clr;

  pinMode(SPISS,   OUTPUT);  digitalWrite(SPISS, LOW);  // Set device to normal position; latch occurs on rising edge.
  pinMode(SPIMOSI, OUTPUT);
  pinMode(SPIMISO, INPUT);
  pinMode(SPICLK,  OUTPUT);

  SPCR = (1<<SPE)|(1<<MSTR)|(1<<SPR1);  // SPI is enabled, as master, 250Kbps clock.
  clr=SPSR;
  clr=SPDR;
  delay(10);
}

byte spi_transfer(volatile byte data) {
  SPDR = data;                    // Start the transmission.
  while (!(SPSR & (1<<SPIF)));    // Spinwait until the end of the transmission.
  return SPDR;                    // Return the received byte.
}

void spi_load() {
  digitalWrite(SPISS, HIGH);      // Latch on rising edge.
  digitalWrite(SPISS,  LOW);      // Return device to normal position.
}

void setup() {
  spi_setup();
  // other stuff here
}

// in a timer interrupt:
...
  if (spi_transfer(displayBuffer[currDigit]) != 0) {  // if we're before the left-most (Q0 of CD4017) display digit (Q1 is left-most).
    currDigit = 0;
    spi_transfer(displayBuffer[currDigit]);   // resend sync'd to first digit
  }
  spi_load();
...

« Last Edit: May 10, 2021, 04:51:17 am by pqass »
 

Offline cv007

  • Frequent Contributor
  • **
  • Posts: 828
Re: ATmega328P SPI Is Stuck Waitng for the SPI Transmit Flag to Be Set
« Reply #5 on: May 10, 2021, 04:50:01 am »
Check your /SS pin- I believe if set to input (is input by default), then you have a floating /ss pin which can take you out of master mode if seen as low. You could verify if this is happening by checking if the MSTR bit is set before transmitting.

The SPIF flag is supposed to be set when this happens, but not sure if putting data in SPDR clears it or maybe you are getting through the function one time (but nothing is sent since pins were switched to input) then the next time through you really get stuck because you are in slave mode (your previous time through cleared SPIF by reading SPDR)..
 
The following users thanked this post: Kalcifer

Offline KalciferTopic starter

  • Regular Contributor
  • *
  • Posts: 82
  • Country: ca
Re: ATmega328P SPI Is Stuck Waitng for the SPI Transmit Flag to Be Set
« Reply #6 on: May 10, 2021, 06:37:09 am »
Check your /SS pin- I believe if set to input (is input by default), then you have a floating /ss pin which can take you out of master mode if seen as low. You could verify if this is happening by checking if the MSTR bit is set before transmitting.

The SPIF flag is supposed to be set when this happens, but not sure if putting data in SPDR clears it or maybe you are getting through the function one time (but nothing is sent since pins were switched to input) then the next time through you really get stuck because you are in slave mode (your previous time through cleared SPIF by reading SPDR)..

Thank you so much! This solved it. Reference in the datasheet is Section 19.3.2 of the ATmega48A/PA/88A/PA/168A/PA/328/P Datasheet Revision B - 2020-09
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf