SPIDR unwriteable

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

SPIDR unwriteable

3,367 Views
kevinrock
Contributor I
I have somehow managed to make a register unwriteable.

The following code snippet initializes the SPI portion of an NE64 FreeScale chip. I am grabbing UDP packets in another section of the code and then sending them via SPI to a digital compass.

The data is coming through fine. However when I try writing it to SPIDR it simply will not change the register. I've tried hardwiring a number to SPIDR with no results. The information gets to the B register but STAB 0xDD does not write the B information to SPIDR located at 0xDD.

Can anyone give me a hint as to where I am going astray?
Kevin.

PS. I replaced the microcontroller with an exact duplicate with the same results.
KJR




void SPI_init(void)
{ // 1.25 MHz
// SPIBR: ??=0,SPPR2=1,SPPR1=1,SPPR0=0,??=0,SPR2=1,SPR1=1,SPR0=0
SPIBR = 0x66; // Set the baud rate register
// SPICR2: ??=0,??=0,??=0,MODFEN=1,BIDIROE=0,??=0,SPISWAI=0,SPC0=0
SPICR2 = 0x10; // Set control register 2
// SPICR1: SPIE=1,SPE=1,SPTIE=0,MSTR=1,CPOL=0,CPHA=1,SSOE=1,LSBFE=1
SPICR1 = 0xD7; // Set control register 1
}

interrupt void SPI(void)
{
int i=0;
int j=0;

// Send Command String to Compass
SPIDR = (UINT8) in_buf[i++]; // Send 0xA4
while ((SPISR & 0x20) == 0) ; // Wait for transmit buffer empty
SPIDR = (UINT8) in_buf[i++]; // Send command byte
while ((SPISR & 0x20) == 0) ; // Wait for transmit buffer empty
SPIDR = (UINT8) in_buf[i++]; // Send 0xA0
while ((SPISR & 0x20) == 0) ; // Wait for transmit buffer empty

// Receive Data String from Compass
out_buf[j++] = (UINT8) SPIDR; // Receive 0xA4
//while ((SPISR & 0x80) == 0) ; // Wait for receiver full
out_buf[j++] = (UINT8) SPIDR; // Receive 0x02
//while ((SPISR & 0x80) == 0) ; // Wait for receiver full
out_buf[j++] = (UINT8) SPIDR; // Receive first data byte
//while ((SPISR & 0x80) == 0) ; // Wait for receiver full
out_buf[j++] = (UINT8) SPIDR; // Receive second data byte
//while ((SPISR & 0x80) == 0) ; // Wait for receiver full
out_buf[j++] = (UINT8) SPIDR; // Receive 0xA0

// loop until SPIDR == 0xA0
// since this char ends all data and error strings

// byte out_buf[]; output to UDP packet
// byte in_buf[]; input from UDP packet
}
Labels (1)
0 Kudos
11 Replies

862 Views
bigmac
Specialist III
Hello kevinrock,
 
Is data actually being sent to the compass via MOSI line?  When you say "the register does not change", are you trying to read the register?  If so, that is the expected result.  When reading SPIDR you are actually reading the last received byte value, not the byte value just sent.
 
Regards,
Mac
 
0 Kudos

862 Views
kevinrock
Contributor I
Yes, it is being sent over MOSI. I am tracking the reads and writes with the debugger and see no change in 0xDD which is the address of SPIDR. Nothing changes in that memory location even though STAB 0xDD has the correct data in register B. If I was receiving data I would expect some change in 0xDD. Zilch, nada, nothing, bupkis. As I mentioned: I tried hardwiring a write. SPIDR = 0xFF. There was absolutely no change when I ran this line of code through the debugger. Nothing I do changes 0xDD. Any idea how I can prevent a R/W register from being written?
Kevin.
0 Kudos

862 Views
rocco
Senior Contributor II
Hi, Kevin:

Any idea how I can prevent a R/W register from being written?
Keep in mind that SPIDR is not a read/write register. It a one write-only register (the transmitter) sharing the address with one read-only register (the receiver). In effect, you are not seeing the data that the SPI should be receiving, or you are but it never changes.

So the question becomes: Is the SPI actually transmitting and receiving? Is it programmed as the master? Do you see any SCLKS?

(Sorry, that was three questions.)
0 Kudos

862 Views
kevinrock
Contributor I
Three questions; two answers, one waffle :smileyhappy:   Yes, the SPI is configured as Master.  Yes, I am receiving clocks. 
 
I do believe the SPI port is transmitting.  I do not have a digital scope but rather a Tek 2335.  It is sufficient to find clocks and logic levels but not store any imagery. 
 
As I single step through my TX/RX routine the status register changes as it should.  However, the data register does not ever show a change.  The data never once changes color to signify the register data has been written. 
 
I just switched my oscilloscope probe to MOSI.  It fires each time assignment to SPIDR occurs. 
 
MISO fires (goes low) after the entire command string is sent to the compass as per the documentation included with it. 
 
The only thing I am not getting is SPIDR to be written in either direction.
 
What else should I check?
    Kevin.
0 Kudos

862 Views
bigmac
Specialist III
Hello Kevin,
 
In order to receive each data byte from the compass on the MISO line, you need the master to send a (dummy) byte.  This is so the clock pulses will be generated by the master.  Your code does not appear to do this.
 
It is also not clear how you are controlling the SS (slave select) line to the slave device.  If this is not correct, you would not get any data returned.
 
Finally, I have a problem with multiple SPI transactions occurring sequentially within the ISR - with this arrangement there is no point in using interrupts since you would not exit the ISR until all the byte transactions were complete - and this may disrupt other more critical interrupts.  ISRs should always be as short as possible.  For an SPI master, there is usually no advantage to use interrupts, unless the SPI clock is very slow.
 
Regards,
Mac
 
0 Kudos

862 Views
kevinrock
Contributor I
   Here is how my code has evolved:
 
void SPI_init(void)
   {                                    // 1.25 MHz   / 896
   // SPIBR: ??=0,SPPR2=1,SPPR1=1,SPPR0=0,??=0,SPR2=1,SPR1=1,SPR0=0 
   SPIBR = 0x66;                        // Set the baud rate register
    
   // SPICR2: ??=0,??=0,??=0,MODFEN=1,BIDIROE=0,??=0,SPISWAI=0,SPC0=0 
   SPICR2 = 0x10;                       // Set control register 2
    
   // SPICR1: SPIE=1,SPE=1,SPTIE=0,MSTR=1,CPOL=0,CPHA=1,SSOE=1,LSBFE=1 
   SPICR1 = 0xD7;                       // Set control register 1         
   }
void SPI_func(void)
   {
   int i;
  
   PTG_PTG2 = 0;     // Drive SS* low
    
   //EnterCritical();                     // Save the PS register
   for (i=0; i<10; i++)
      {
      SPIDR = in_buf[i];           // Send 0xA4
      while ((SPISR & 0x80) == 0) ;
      out_buf[i] = SPIDR;          // Receive 0xA4
      }
   //ExitCritical();                      // Restore the PS register
   }
 
 
As you can see I am no longer using an interrupt driven SPI system.  I do get a little data from the compass but only one byte.  That one byte fills the return buffer over and over again.  If I reset the NE64 the number which fills the return buffer changes.  But, I still only get one byte. 
 
 
What should be appearing in the return buffer is:
0xA4
0x02
two bytes of data
0xA0
 
What is sent to the compass as a command string is:
0xA4
0x02
0xA0
 
Errors are written as:
0xAE
8 bit error code (0xFF, 0xFE, or 0xFD)
0xA0
 
I am using an external pin PinG2 to drive the SS*.  This is what allowed me to gain even one byte back from the compass. 
 
My debugger shows the input buffer contains the correct string.  My oscilloscope only shows a flicker and then SS* goes back to where it was, and the SCK only shows one pulse and then it returns to baseline.  I am beginning to think the SPI port of the compass is dead.
 
Any more thoughts?
   Kevin.
0 Kudos

862 Views
bigmac
Specialist III
Hello Kevin,
 
I notice that you set SS low, but do not raise the line at any point.  What are the requirements of the compass in this respect?
 
You define only three bytes in the command sent to the compass, but expect a response of five bytes.  Therefore the your send buffer must contain at least three dummy bytes, appropriately positioned.  This is not clear from your code.  Note that the first byte returned as the first command byte is sent will be garbage.  Any response to the first command byte will occur as the second command byte is sent.
 
The master generates the SCK pulses - I presume that the compass definitely operates as a SPI slave device.  Try disconnecting the compass and see if you can observe any SCK pulses.
 
Regards,
Mac
 
0 Kudos

862 Views
kevinrock
Contributor I
The init and call functions now read:
 
 
void SPI_init(void)
  {                                    // 1.25 MHz   
  // SPIBR: ??=0,SPPR2=1,SPPR1=1,SPPR0=0,??=0,SPR2=1,SPR1=1,SPR0=0 
  SPIBR = 0x66;                        // Set the baud rate register
   // SPICR2: ??=0,??=0,??=0,MODFEN=0,BIDIROE=0,??=0,SPISWAI=0,SPC0=0 
  SPICR2 = 0x00;                       // Set control register 2
   
  // SPICR1: SPIE=1,SPE=1,SPTIE=0,MSTR=1,CPOL=0,CPHA=1,SSOE=0,LSBFE=1 
  SPICR1 = 0xD5;                       // Set control register 1      
  }
 
void SPI_func(void)
  {
  int i;
  
  for (i=0; i<3; i++)                  // send command string
     {
     PTG_PTG2 = 0;                     // Drive SS* low
     SPIDR = in_buf[i];                // Send 0xA4
     while ((SPISR & 0x20) == 0) ;     // Transmit buffer empty
     PTG_PTG2 = 1;     // Drive SS* high
     }
  for (i=0; i<6; i++)                  // receive response
     {
     PTG_PTG2 = 0;                     // Drive SS* low
     SPIDR = 0;                        // start transfer
     while ((SPISR & 0x20) == 0) ;     // Transmit buffer empty   
     out_buf[i] = SPIDR;               // Receive
     PTG_PTG2 = 1;                     // Drive SS* high
     }
  }
 
Yes, the compass can act as a SPI slave device.  It is the missing clock pulses which are the mystery.
 
The input buffer has the command string as before.  I have tested this new routine both attached and unattached to the compass.  In neither case am I seeing a clock pulse string. 
 
I am about to give up on SPI and try another interface.  This simply does not work with the FreeScale processor.  It seems to be broken on all three of the NE64 demo boards I have tried.
 
Kevin.
0 Kudos

862 Views
bigmac
Specialist III
Hello Kevin,
 
What you now seem to be attempting to do is -
1.  Send three command  bytes to the compass, and ignore the return data, and then
2. Send six dummy bytes to the compass, and expect to receive six data bytes from the compass.
3. The SS signal frames each individual byte sent to the compass.
 
I this the way the compass is supposed to operate?
 
A problem - you are raising SS prematurely, before each byte is sent (and exacerbated by the double buffering within the SPI send buffer).  Additionally you are now attempting to read the return data prior to its arrival in the receive buffer.  These problems can be avoided by waiting until the receive buffer full flag is set, before reading the current byte, or sending the next byte, and ultimately raising SS.
 
The following code demonstrates this.  I have created a new general function SPI_trans() to handle a single SPI byte transaction.  Also, I presume that in_buf[] and out_buf[] global arrays are declared as byte or unsigned char.
 
byte SPI_trans (byte sendchr)
{
   while ((SPISR & 0x20) == 0);     // Wait for transmit buffer empty
   SPIDR = sendchr;                 // Send byte value
   while ((SPISR & 0x80) == 0);     // Wait for receive buffer full
   return (SPIDR);                  // Received byte value
}
 
void SPI_func(void)
{
   int i;
  
   for (i=0; i<3; i++)              // send command string
   {
      PTG_PTG2 = 0;                 // Drive SS* low
      SPI_trans (in_buf[i]);        // Ignore return byte
      PTG_PTG2 = 1;                 // Drive SS* high
   }
   for (i=0; i<6; i++)              // receive response
   {
      PTG_PTG2 = 0;                 // Drive SS* low
      out_buf[i] = SPI_trans(0)     // Send dummy byte
      PTG_PTG2 = 1;                 // Drive SS* high
   }
}
 
Another thing you might need to check - after raising SS, how long does the compass require this to remain high before being again lowered?  If the requirement is more than a few bus cycles, you may need to add short delays to the above code.
 
Regards,
Mac
 

Message Edited by bigmac on 2006-09-08 03:37 PM

0 Kudos

862 Views
kevinrock
Contributor I
I finally got my compass and my NE64 talking with one another!!!!
 
This code works:
 
void SPI_init(void)
   {                                    // 1.25 MHz   / 896
   // SPIBR: ??=0,SPPR2=1,SPPR1=1,SPPR0=0,??=0,SPR2=1,SPR1=1,SPR0=0 
   SPIBR = 0x66;                        // Set the baud rate register
   
   // SPICR2: ??=0,??=0,??=0,MODFEN=1,BIDIROE=0,??=0,SPISWAI=0,SPC0=0 
   SPICR2 = 0x10;                       // Set control register 2
  
   // SPICR1: SPIE=0,SPE=1,SPTIE=0,MSTR=1,CPOL=1,CPHA=0,SSOE=0,LSBFE=0 
   SPICR1 = 0x58;                       // Set control register 1      
   }
void SPI_func(void)
   {
   int i;
  
   PTG_PTG2 = 0;                     // Drive SS* low
   for (i=0; i<3; i++)                  // send command string
      {
      while ((SPISR & 0x20) == 0) ;     // Transmit buffer empty
      SPIDR = in_buf[i];                // Send 0xA4
      }   
   for (i=0; i<7; i++)                  // receive response
      {
      while ((SPISR & 0x20) == 0) ;     // Transmit buffer empty
      SPIDR = 0;                        // start transfer      
      while ((SPISR & 0x80) == 0);     // Wait for receive buffer full  
      out_buf[i] = SPIDR;               // Receive
      }
   PTG_PTG2 = 1;                     // Drive SS* high
   }
 
I may want to clean a few things up now that I have the SCK working.  Part of what led me to success was changing the polarity of the clock.  That may or may not have been the problem but help from you all was what really got me over the hump.
 
A hearty thank you is well deserved!
    Kevin.
0 Kudos

862 Views
rocco
Senior Contributor II
Hi, Kevin:

I have not looked closely at your init code yet (as that would require that I pull the manual :smileywink: ), but I see a potential problem with your subroutine.

When you load the data into the transmitter's data register, you wait for the register to go empty, and then remove Slave-Select. But if the transmitter is operating double-buffered (which I need to pull the manual to verify), then that register will go empty at the START of the transmission, not when the transmission is complete, truncating the operation.

Maybe you need something like this?
void SPI_func(void)
{
   int i;

   PTG_PTG2 = 0;                     // Drive SS* low

   for (i=0; i3; i++)                   // send command string
   {
      SPIDR = in_buf[i];                // Send 0xA4
      while ((SPISR & 0x20) == 0) ;     // Transmit buffer empty
   }
   for (i=0; i6; i++)                   // receive response
   {
      SPIDR = 0;                        // start transfer
      while ((SPISR & 0x20) == 0) ;     // Transmit buffer empty  
      out_buf[i] = SPIDR;               // Receive
   }

   SPIDR = 0;                        // one last transfer to wait for the buffering
   while ((SPISR & 0x20) == 0) ;     // Transmit buffer empty for the last time
   PTG_PTG2 = 1;                     // Drive SS* high
}
0 Kudos