¿Cómo puedo configurar una interfaz SPI entre un LPC2132 ARM y un Cyclone FPGA?

Estoy tratando de hacer que un chip ARM LPC2132 y un FPGA Altera Cyclone se comuniquen usando el protocolo SPI. Específicamente, tengo la placa Saxo-L de KNJN, que tiene las señales precableadas entre las dos placas, pero no puedo hacer que los dos chips se comuniquen.

La documentación proporciona ejemplos de código, pero no tengo suerte con estos. ¿Alguien puede compartir cómo hacer que estos dos chips hablen?

Aquí está la fuente que estoy usando:

Código BRAZO:

// 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);
}

Montaje BRAZO:

/*
 * 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 ***/   

Código Verilog:

// 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
Puaj. KNJN. Ese lugar perdió mi respeto después de que se negaron a darme el esquema de sus placas de desarrollo, para que pudiera modificar la que compré. Es una placa de desarrollo, necesitas el esquema.
Sí, no estoy realmente tan emocionado con el tablero en general...
@FakeName, ¿cómo no das un esquema para una placa de desarrollo? ¿No es ese el punto? ¿Diseño de referencia?
@Kortuk - ¿Ves por qué estaba irritado por eso? Lo que sucede es que el tipo que ejecuta KNJN ideó una forma inteligente de programar un FPGA (normalmente toma JTAG) usando solo las líneas TX y RX de un puerto serie. Aparentemente lo considera un secreto comercial o algo así. Todavía tengo los correos electrónicos donde pedí el esquema, y ​​él me dijo que no, le respondí que lo revertiría desmontando una de las placas, y él siguió diciendo que no lo liberaría. Terminé sin molestarme (más fácil de hacer el mío, con un JTAG normal), pero esa es la actitud que toma KNJN para apoyar sus productos...
Básicamente, no proporciona un esquema completo para ninguno de sus tableros (¡incluso los que son solo ADC!), Y casi NO proporciona documentación aparte del pinout para la mayoría de ellos. Además, la mayor parte del código de demostración está disponible solo una vez que compra los productos.
@FakeName, a veces me sorprende cómo la gente se mantiene en el negocio.
Exactamente. Horrible, no compren un KNJN. Terminé cambiando de placa.
Todavía estoy tentado a aplicar ingeniería inversa a la interfaz de programación por despecho, pero soy perezoso y es mucho trabajo.
@samoz, si cambió de producto, ¿eso hace que esta pregunta sea un poco muda? Puede actualizarlo con el motivo por el que eligió pasar a un nuevo producto y aceptarlo para que otros puedan aprender de su dolor. También podemos simplemente cerrar la pregunta y eliminarla, ya que no hay una forma real de resolverla. Preferiría lo primero para que otros puedan aprender de tu lucha.

Respuestas (2)

Oh, la alegría de la depuración. Necesita dividir este problema en partes... primero asegúrese de que el maestro SPI (ARM) esté enviando el CS, CLK y MOSI correctos. Luego verifique que el Esclavo (FPGA) recibió el mensaje del maestro y respondió.

Recomendaría usar un reloj SPI más lento para descartar problemas de integridad de la señal entre sus conexiones. Da un paso a la vez y lo conseguirás. ¡Buena suerte!

Hay 4 formas de configurar el borde de reloj activo y el estado inactivo de la línea de reloj SPI. Tienen que coincidir en ambos extremos. (Por lo general, el cuarto que prueba funciona, independientemente del orden en que los pruebe :)

  • Cuelgue un visor fuera de la línea del reloj y verifique que esté limpio (especialmente sin escalones ni zumbidos alrededor del punto medio de la pendiente)
  • Cuelgue un analizador lógico de las señales y verifique que estén transmitiendo en el borde que espera y que estén inactivos en el estado que espera.