ATmega328P SPI Is Stuck Waitng for the SPI Transmit Flag to Be Set - Page 1
Forum section: electronics / general

author: Kalcifer
author_rank: Regular Contributor
timestamp: May 10, 2021, 12:56:56 am
content: 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:void spi_init(void) // SPI initialization function.{DDRB&=~(1 << DDB4);// Ensure that the MISO pin is configured as in inputDDRB|=(1 << DDB3);// Configure the MOSI pin as an outputDDRB|=(1 << DDB5);// Configure the SCK pin as an outputDDRD|=(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 lowDDRB|=(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 interruptSPCR|=(1 << SPE);// Enable the SPISPCR&=~(1 << DORD);// Transmit the data MSB firstSPCR|=(1 << MSTR);// Configure the SPI as MasterSPCR&=~(1 << CPOL);// Idle the clock line (SCK) at logic lowSPCR&=~(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 trasferwhile(!(SPSR & (1 << SPIF)));// Wait until the transfer has complete <-- where the problem seems to bereturn 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).

author: Kalcifer
author_rank: Regular Contributor
timestamp: May 10, 2021, 01:22:10 am
content: 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 thathttps://imgur.com/a/QnuvkhT

author: WattsThat
author_rank: Frequent Contributor
timestamp: May 10, 2021, 03:14:11 am
content: while(!(SPSR & (1 << SPIF)));Is not the same as:while (!(SPSR & _BV(SPIF))) ;PS: don't shift left by x80

author: oPossum
author_rank: Super Contributor
timestamp: May 10, 2021, 04:16:55 am
content: Quote from: WattsThat on May 10, 2021, 03:14:11 amHow is it different?sfr_defs.hCode:#define_BV(bit) (1 << (bit))iom328p.hCode:#define SPIF 7

author: pqass
author_rank: Super Contributor
timestamp: May 10, 2021, 04:37:53 am
content: 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:#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 11void 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) != 0) { // if we're before the left-most (Q0 of CD4017) display digit (Q1 is left-most).currDigit = 0;spi_transfer(displayBuffer); // resend sync'd to first digit}spi_load();...

author: cv007
author_rank: Frequent Contributor
timestamp: May 10, 2021, 04:50:01 am
content: 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)..

author: Kalcifer
author_rank: Regular Contributor
timestamp: May 10, 2021, 06:37:09 am
content: Quote from: cv007 on May 10, 2021, 04:50:01 amThank 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