SPI never signals SPIF after writing to SPDR

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

I'm trying to send SPI commands to a PGA2311 audio chip with an atmega168, but writing to the SPDR never yields a 1 in SPIF in SPSR (status register).

The atmega168 is the controller in a four channel (two front, two rear) audio amplifier.

The pins I use are:

    * PB0: CS pin of the front channels PGA2311
    * PB1: CS pin of the rear channels PGA2311
    * PB3 (MOSI): goes to SDI on the PGA2311
    * PB5 (SCK): goes to SCK on the PGA2311

Because SPI has no fault checks, I've tested with and without the PGA2311 connected, it doesn't matter. I've also tried disconnecting the ISP (in system programmer), which also connects to the SPI bus.

This is my relevant code:

// Obtain data direction register of given port
#define DDR(x) (*(&x - 1))

// Some configs
#define SPI_CS_PORT (PORTB)
#define FRONT_CS_PIN (0)
#define REAR_CS_PIN (1)

// Define shorthands for accessing the PGA2311's
#define front_cs_low() (SPI_CS_PORT &= ~_BV(FRONT_CS_PIN))
#define front_cs_high() (SPI_CS_PORT |= _BV(FRONT_CS_PIN))
#define rear_cs_low() (SPI_CS_PORT &= ~_BV(REAR_CS_PIN))
#define rear_cs_high() (SPI_CS_PORT |= _BV(REAR_CS_PIN))

void init_spi()
{
  // Set CS pin of front stereo stage to output (PB0)
  DDR(SPI_CS_PORT) |= _BV(FRONT_CS_PIN);

  // Set CS pin of rear stereo stage to output (PB1)
  DDR(SPI_CS_PORT) |= _BV(REAR_CS_PIN);

  // Set MOSI and SCK as output
  DDR(SPI_CS_PORT) |= (_BV(DDB3) | _BV(DDB5));

  // Set SS (slave select pin) to output. Pin is not used for anything and this
  // ensures proper config in SPI master mode. 
  DDR(SPI_CS_PORT) |= _BV(DDB2);

  // Enable SPI
  SPCR |= _BV(SPE);

  // Set SPI Master
  SPCR |= _BV(MSTR);

  // Set SPI speed to clock/128
  SPCR |= _BV(SPR1);
  SPCR |= _BV(SPR0);
}

void send_volume_to_chip(int front, int rear)
{
  front_cs_low();

  SPDR = 200;
  int count = 0;
  while (!(SPSR & _BV(SPIF)))
  {
    count++;
    lcd_clear();    
    lcd_formatted_string("Can't show string here, because the forum borks out when I use percent signs.", SPSR, count);
    _delay_us(1000000);
  }

  SPDR = 200;
  while (!(SPSR & _BV(SPIF)));

  front_cs_high();
}

So what could be causing it to hang when writing to SPDR?

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

Quote:

So what could be causing it to hang when writing to SPDR?


It looks OK. Is your DDR() macro proven?

Also, is the write to SPDR good enough to clear a previous SPIF? (I think you are right; the write to SPDR constitutes an "access"...)

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

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

I do not like using |= when initialising. Especially when it is one of the cranky special function registers.

I ran your program in Simulator2 for a mega168.
It sets all the DDRB bits properly. (especially the SS pin)
But when you use |= for the MSTR bit, it does not seem to 'stick'. If you do all the bits together with =, everything works fine.

This may be Simulaor2 being very clever. i.e. it is accurately mimicking a feature of the mega168.
Because it reads the SPCR, and tries to write 0x50. The Simulator just reads 0x40 back.

Edit.
Whoopee! I just tried it with a real mega168 and a real JTAGICEmkII. Guess what? The real SPI canot |= the MSTR bit either!

Edit again.
It works fine if you set the bits in the right order. i.e. do the SPE bit last.

David.

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

Hey, it works! I do this now:

// Enable SPI, set Master and set clock to clock/128
SPCR = (1<<SPE) | (1<<MSTR) | (1<<SPR1) | (1<<SPR0);

I would have never realized that, but you're right that using |= requires a read first, and on such registers, that may not work well.

Clever simulator BTW, that is know the chip does that.

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

Also the SS pin of AVR must be configured as output. If it is input and it happens to be ever low, the AVR SPI hardware will drop out of master mode into slave mode, and it will not resume until you put it back into master mode. So basically you cannot use AVR SS pin as input when you want to be the SPI master.

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

Quote:

I do not like using |= when initialising.

I don't either, unless it is really an update. The chain of |= assuming a "clean" starting register is at the minimum wasteful of flash and cycles.

Quote:

But when you use |= for the MSTR bit, it does not seem to 'stick'.

But that is weird. A simulator situation? What if I really >>want<< to change from slave to master?!?

Quote:

Also the SS pin of AVR must be configured as output.

I did check that in the original post before responding.

======================
I know y'all have done all the legwork, but it makes no sense to me. If I >>did<< have a hermaphrodite app that switched from slave to master I'd be pulling out my remaining hairs about now. A re-read of various '88-family datasheets (non-P, P, PA) shows no errata about SPI and no notes that I can find in the SPI chapter that would indicate this behaviour. In fact,

Quote:
• Bit 4 – MSTR: Master/Slave Select
This bit selects Master SPI mode when written to one, and Slave SPI mode when written logic zero. ... If SS is configured as an input and is driven low while MSTR is set, MSTR will be cleared, and SPIF in SPSR will become set. The user will then have to set MSTR to re-enable SPI Master mode.
which almost screams to do an |= ...

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

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

theusch wrote:
But that is weird.

Facts:
First bit set in SPCR is SPE.
After that SPI is enabled and in slave mode.
In slave mode SS is an input regardless of the DDR setting.
Pullup is not enabled.

Possibility:
The floating SS pin is low, therefore the SPI module is active.

Suspicion (haven't found a confirmation in the data sheet):
SPCR cannot be written while the SPI module is active.

Stefan Ernst

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

Quote:

Facts:
First bit set in SPCR is SPE.
After that SPI is enabled and in slave mode.

I won't disagree.

Quote:

In slave mode SS is an input regardless of the DDR setting.

I won't disagree. Now, the port diagram/override info isn't clear to me on whether the bit in DDR is actually cleared or not. I'd say no, but the evidence here says yes.

Quote:

Possibility:
The floating SS pin is low, therefore the SPI module is active.

Probably is low on the OP's AVR, since it was just made a low output.

Quote:

Suspicion (haven't found a confirmation in the data sheet):
SPCR cannot be written while the SPI module is active.

The datasheet sure hints that you just have to tickle MSTR, but apparently not. At least now I see a chain of events that would indeed follow the symptoms. I cannot remember this particular discussion before on 'Freaks; my guess is that few if any apps have a reason to switch slave<=>master.

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

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

theusch wrote:
I cannot remember this particular discussion before on 'Freaks; my guess is that few if any apps have a reason to switch slave<=>master.
And even less (if any) apps have a reason to do such switch during an ongoing transmission.

Stefan Ernst