2
\$\begingroup\$

I am trying to make an ARM LPC2132 chip and a Altera Cyclone FPGA communicate using the SPI protocol. Specifically, I have the Saxo-L board from KNJN, which has the signals pre-wired between the two boards, but I cannot get the two chips talking.

The documentation supplies code examples, but I am having no luck with these. Can someone share how to get these two chips talking?

Here is the source I am using:

ARM Code:

// SPI master using SPI1/SSP
// (c) KNJN LLC 2007, 2008

// Tested with LPC ARM on Saxo-L (CPOL=0, CPHA=1, MSB first, SSEL controlled through GPIO)

#include "lpc23xx.h"

void SSP_init()
{
  SSP0CPSR = 0x02;  // SSP max speed
  SSP0CR0 = 0x87;  // SSP max speed, 8 bits, CPHA=1
  SSP0CR1 = 0x02;  // SSP master mode

  PINSEL1 = 0x0A8;  // SPI mode for pins P0.17 to P0.19, while P0.20 (SSEL) stays as GPIO
  IOSET0 = 0x00100000;  // SSEL inactive (up)
}

void SSP_send_recv(char* ob, int len_ob, char* ib, int len_ib)
{
  // before doing anything, let's make sure the SSP receive FIFO is empty (by reading data out of it if necessary)
  while(SSP0SR & 0x04) SSP0DR;

  IOCLR0 = 0x00100000;  // SSEL active (down)
  while(len_ob || len_ib)
  {
    if(len_ob && (SSP0SR & 0x02))
    {
        SSP0DR=*ob++;
        len_ob--;
    }
    if(len_ib && (SSP0SR & 0x04))
    {
        *ib++=SSP0DR;
        len_ib--;
    }
  }
  IOSET0 = 0x00100000;  // SSEL inactive (up)
}

void SSP_send_only(char* ob, int len)
{
  IOCLR0 = 0x00100000;  // SSEL active (down)
  while(len) if(SSP0SR & 0x02) { SSP0DR=*ob++; len--; }  // transmit
  while(!(SSP0SR & 0x11));  // wait until transmission is completed
  while(SSP0SR & 0x04) SSP0DR;  // empty the receive FIFO
  IOSET0 = 0x00100000;  // SSEL inactive (up)
}

int main(void)
{
  char bufo[2] = {0x55, 0x54};
  char bufi[2];

  SSP_init();
  IODIR0 = 0x80100000;  // turn on LED driver (P0.31) and P0.20 (SSEL)

  while(1)
  {
    SSP_send_recv(bufo, sizeof(bufo), bufi, sizeof(bufo));

    if(((bufi[0]+bufi[1])
                    &0xFF)==254)
      IOSET0 = 0x80000000;
    else
      IOCLR0 = 0x80000000;
  }

  return(0);
}

ARM assembly:

/*
 * Some defines for the program status registers
 */
   ARM_MODE_USER  = 0x10      /* Normal User Mode                             */ 
   ARM_MODE_FIQ   = 0x11      /* FIQ Fast Interrupts Mode                     */
   ARM_MODE_IRQ   = 0x12      /* IRQ Standard Interrupts Mode                 */
   ARM_MODE_SVC   = 0x13      /* Supervisor Interrupts Mode                   */
   ARM_MODE_ABORT = 0x17      /* Abort Processing memory Faults Mode          */
   ARM_MODE_UNDEF = 0x1B      /* Undefined Instructions Mode                  */
   ARM_MODE_SYS   = 0x1F      /* System Running in Priviledged Operating Mode */
   ARM_MODE_MASK  = 0x1F

   I_BIT          = 0x80      /* disable IRQ when I bit is set */
   F_BIT          = 0x40      /* disable IRQ when I bit is set */

/*
 * Register Base Address
 */

   .section .vectors,"ax"
   .code 32

/****************************************************************************/
/*               Vector table and reset entry                               */
/****************************************************************************/
_vectors:
   ldr pc, ResetAddr    /* Reset                 */
   ldr pc, UndefAddr    /* Undefined instruction */
   ldr pc, SWIAddr      /* Software interrupt    */
   ldr pc, PAbortAddr   /* Prefetch abort        */
   ldr pc, DAbortAddr   /* Data abort            */
   ldr pc, ReservedAddr /* Reserved              */
   ldr pc, IRQAddr      /* IRQ interrupt         */
   ldr pc, FIQAddr      /* FIQ interrupt         */


ResetAddr:     .word ResetHandler
UndefAddr:     .word UndefHandler
SWIAddr:       .word SWIHandler
PAbortAddr:    .word PAbortHandler
DAbortAddr:    .word DAbortHandler
ReservedAddr:  .word 0
IRQAddr:       .word IRQHandler
FIQAddr:       .word FIQHandler

   .ltorg


   .section .init, "ax"
   .code 32

   .global ResetHandler
   .global ExitFunction
   .extern main
/****************************************************************************/
/*                           Reset handler                                  */
/****************************************************************************/
ResetHandler:
/*
 * Wait for a stable oscillator
 */   
   nop
   nop
   nop
   nop
   nop
   nop
   nop
   nop

   /*
    * Setup a stack for each mode
    */    
   msr   CPSR_c, #ARM_MODE_UNDEF | I_BIT | F_BIT   /* Undefined Instruction Mode */     
   ldr   sp, =__stack_und_end

   msr   CPSR_c, #ARM_MODE_ABORT | I_BIT | F_BIT   /* Abort Mode */
   ldr   sp, =__stack_abt_end

   msr   CPSR_c, #ARM_MODE_FIQ | I_BIT | F_BIT     /* FIQ Mode */   
   ldr   sp, =__stack_fiq_end

   msr   CPSR_c, #ARM_MODE_IRQ | I_BIT | F_BIT     /* IRQ Mode */   
   ldr   sp, =__stack_irq_end

   msr   CPSR_c, #ARM_MODE_SVC | I_BIT | F_BIT     /* Supervisor Mode */
   ldr   sp, =__stack_svc_end


    /* 
     * Copy initialized variables .data section (copy from flash to RAM)
     */
   ldr   r1, =etext
   ldr   r2, =__data_start
   ldr   r3, =__data_end
data_copy_loop:
   cmp   r2, r3
   ldrlo r0, [r1], #4
   strlo r0, [r2], #4
   blo   data_copy_loop

   /*
    * Clear .bss section
    */
   ldr   r1, =__bss_start
   ldr   r2, =__bss_end
   ldr   r3, =0
bss_clear_loop:
   cmp   r1, r2
   strlo r3, [r1], #+4
   blo   bss_clear_loop


   /*
    * Jump to main
    */
   mrs   r0, cpsr
   bic   r0, r0, #I_BIT | F_BIT     /* Enable FIQ and IRQ interrupt */
   msr   cpsr, r0

   mov   r0, #0 /* No arguments */
   mov   r1, #0 /* No arguments */
   ldr   r2, =main
   mov   lr, pc
   bx    r2     /* And jump... */

ExitFunction:
   nop
   nop
   nop
   b ExitFunction   


/****************************************************************************/
/*                         Default interrupt handler                        */
/****************************************************************************/

UndefHandler:
   b UndefHandler

SWIHandler:
   b SWIHandler

PAbortHandler:
   b PAbortHandler

DAbortHandler:
   b DAbortHandler

IRQHandler:
   b IRQHandler

FIQHandler:
   b FIQHandler

   .weak ExitFunction
   .weak UndefHandler, PAbortHandler, DAbortHandler
   .weak IRQHandler, FIQHandler

   .ltorg
/*** EOF ***/   

Verilog code:

// SPI slave
// (c) KNJN LLC 2007, 2008

// Configures the LPC ARM with CPOL=0, CPHA=0/1, MSB first

/////////////////////////////////
module SPI_slave(clk, SCK, MOSI, MISO, SSEL, LED);
input clk;

input SCK, MOSI, SSEL;
output MISO;

output LED;

/////////////////////////////////
reg [2:0] SCKr;  always @(posedge clk) SCKr <= {SCKr[1:0], SCK};
wire SCK_risingedge = (SCKr[2:1]==2'b01);
wire SCK_fallingedge = (SCKr[2:1]==2'b10);

reg [2:0] SSELr;  always @(posedge clk) SSELr <= {SSELr[1:0], SSEL};
wire SSEL_active = ~SSELr[1];  // SSEL is active low
wire SSEL_startmessage = (SSELr[2:1]==2'b10);  // message starts at falling edge
wire SSEL_endmessage = (SSELr[2:1]==2'b01);  // message stops at rising edge

reg [1:0] MOSIr;  always @(posedge clk) MOSIr <= {MOSIr[0], MOSI};
wire MOSI_data = MOSIr[1];

/////////////////////////////////
reg [2:0] bitcnt;
reg byte_received;
reg [7:0] byte_data_received;

always @(posedge clk)
begin
    if(~SSEL_active)
        bitcnt <= 3'b000;
    else
    if(SCK_risingedge)
    begin
        bitcnt <= bitcnt + 3'b001;
        byte_data_received <= {byte_data_received[6:0], MOSI_data};
    end
end

always @(posedge clk) byte_received <= SSEL_active && SCK_risingedge && (bitcnt==3'b111);

/////////////////////////////////
//assign MISO = 1'b0;
reg [7:0] byte_data_sent;

reg [7:0] cnt;
always @(posedge clk) if(SSEL_startmessage) cnt<=cnt+8'h1;  // count the messages

always @(posedge clk)
if(SSEL_active)
begin
    if(SSEL_startmessage)
        byte_data_sent <= cnt;
    else
    if(SCK_fallingedge)
    begin
        if(bitcnt==3'b000)
            byte_data_sent <= ~cnt;
        else
            byte_data_sent <= {byte_data_sent[6:0], 1'b0};
    end
end

assign MISO = byte_data_sent[7];  // we assume that there is only one slave on the SPI bus, so we don't bother with a tri-state buffer there
// otherwise we would need to tri-state MISO when SSEL is inactive

/////////////////////////////////
reg LED;
always @(posedge clk) if(byte_received) LED <= byte_data_received[0];

endmodule
\$\endgroup\$
  • \$\begingroup\$ Ugh. KNJN. That place lost my respect after they refused to give out the schematic for their dev boards, so I could modify the one I bought. It's a dev board, you kind of need the schematic. \$\endgroup\$ – Connor Wolf Jul 30 '10 at 0:34
  • \$\begingroup\$ Yea, I'm not really that thrilled with the board overall... \$\endgroup\$ – samoz Jul 30 '10 at 12:37
  • 1
    \$\begingroup\$ @Kortuk - See why I was irritated about it? What's going on is the guy running KNJN came up with some clever way to program a FPGA (normally takes JTAG) using only the TX and RX lines of a serial port. Apparently he considers it a trade secret or something. I still have the e-mails where I asked for the schematic, and he told me no, I replied saying I would reverse it by taking one of the boards apart, and he went on about not releasing it. I wound up not bothering (easier to roll my own, with a normal JTAG), but that's the attitude KNJN takes for supporting their products... \$\endgroup\$ – Connor Wolf May 12 '11 at 9:39
  • 1
    \$\begingroup\$ Basically, he doesn't provide a complete schematic for any of his boards (even the ones that are just ADCs!), and provides almost NO documentation aside from pinout for most of them. Furthermore, most of the demo code is available only once you buy the products. \$\endgroup\$ – Connor Wolf May 12 '11 at 9:49
  • 1
    \$\begingroup\$ @samoz, if you changed products does that make this question a bit mute? You can update it with why you chose to move to a new product and accept that so others might learn from your pain. We can also just close the question and remove it as there is no real way to resolve it. I would prefer the former so that others might learn from your strife. \$\endgroup\$ – Kortuk May 13 '11 at 5:48
2
\$\begingroup\$

Oh, the joy of debugging. You need to break this problem down into pieces... first make sure that the SPI master (ARM) is sending the right CS, CLK, & MOSI. Then check that the Slave(FPGA) got the message from master and replying.

I would recommend using a slower SPI clock to rule out signal integrity issues between your connections. Take it one step at a time, and you'll get it. Good luck!

\$\endgroup\$
0
\$\begingroup\$

There are 4 ways to set up the active clock edge and idle state of the SPI clock line. They have to match at both ends. (Usually the 4th one you try works - whichever order you try them in :)

  • Hang a scope off the clock line and check it is clean (esp. no steps or ringing around the mid-point of the slope)
  • Hang a logic analyser off the signals and check that they are transmitting on the edge you expect and idling to the state you expect.
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.