Wie kann ich eine SPI-Schnittstelle zwischen einem LPC2132 ARM und einem Cyclone FPGA einrichten?

Ich versuche, einen ARM LPC2132-Chip und ein Altera Cyclone FPGA über das SPI-Protokoll kommunizieren zu lassen. Insbesondere habe ich das Saxo-L- Board von KNJN, bei dem die Signale zwischen den beiden Boards vorverdrahtet sind, aber ich kann die beiden Chips nicht zum Sprechen bringen.

Die Dokumentation liefert Codebeispiele, aber ich habe kein Glück mit diesen. Kann jemand sagen, wie man diese beiden Chips zum Sprechen bringt?

Hier ist die Quelle, die ich verwende:

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-Montage:

/*
 * 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
Pfui. KNJN. Dieser Ort verlor meinen Respekt, nachdem sie sich geweigert hatten, den Schaltplan für ihre Entwicklungsplatinen herauszugeben, damit ich den, den ich gekauft hatte, modifizieren konnte. Es ist ein Entwicklungsboard, Sie brauchen irgendwie den Schaltplan.
Ja, ich bin nicht wirklich begeistert von dem Board insgesamt ...
@FakeName, wie gibt man keinen Schaltplan für ein Entwicklungsboard? Ist das nicht der Punkt? Referenzdesign?
@Kortuk - Sehen Sie, warum ich darüber irritiert war? Was los ist, ist, dass der Typ, der KNJN betreibt, eine clevere Möglichkeit gefunden hat, ein FPGA (normalerweise JTAG) zu programmieren, indem nur die TX- und RX-Leitungen einer seriellen Schnittstelle verwendet werden. Anscheinend hält er es für ein Geschäftsgeheimnis oder so etwas. Ich habe immer noch die E-Mails, in denen ich nach dem Schaltplan gefragt habe, und er sagte nein, ich antwortete, ich würde es rückgängig machen, indem ich eine der Platinen auseinander nehmen würde, und er fuhr fort, es nicht freizugeben. Am Ende habe ich mich nicht darum gekümmert (einfacher, mein eigenes zu rollen, mit einem normalen JTAG), aber das ist die Haltung, die KNJN einnimmt, um ihre Produkte zu unterstützen ...
Grundsätzlich liefert er für keines seiner Boards (selbst für die, die nur ADCs sind!) Einen vollständigen Schaltplan und für die meisten von ihnen abgesehen von der Pinbelegung fast KEINE Dokumentation. Darüber hinaus ist der größte Teil des Democodes erst verfügbar , wenn Sie die Produkte kaufen.
@FakeName, wie Leute im Geschäft bleiben, erstaunt mich manchmal.
Exakt. Schrecklich, kaufen Sie kein KNJN. Am Ende habe ich die Boards gewechselt.
Ich bin immer noch versucht, die Programmierschnittstelle aus Trotz zurückzuentwickeln, aber ich bin faul und es ist eine Menge Arbeit.
@samoz, wenn du das Produkt gewechselt hast, ist diese Frage dann etwas stumm? Sie können es mit den Gründen aktualisieren, warum Sie sich für ein neues Produkt entschieden haben, und dies akzeptieren, damit andere von Ihren Schmerzen lernen können. Wir können die Frage auch einfach schließen und entfernen, da es keine wirkliche Möglichkeit gibt, sie zu lösen. Ich würde Ersteres bevorzugen, damit andere aus Ihrem Streit lernen könnten.

Antworten (2)

Oh, die Freude am Debuggen. Sie müssen dieses Problem in Stücke zerlegen ... stellen Sie zuerst sicher, dass der SPI-Master (ARM) das richtige CS, CLK und MOSI sendet. Überprüfen Sie dann, ob der Slave (FPGA) die Nachricht vom Master erhalten hat und antwortet.

Ich würde empfehlen, einen langsameren SPI-Takt zu verwenden, um Probleme mit der Signalintegrität zwischen Ihren Verbindungen auszuschließen. Machen Sie einen Schritt nach dem anderen, und Sie werden es bekommen. Viel Glück!

Es gibt 4 Möglichkeiten, die aktive Taktflanke und den Ruhezustand der SPI-Taktleitung einzurichten. Sie müssen an beiden Enden übereinstimmen. (Normalerweise funktioniert die 4., die du versuchst - egal in welcher Reihenfolge du sie versuchst :)

  • Hängen Sie ein Zielfernrohr an die Uhrenlinie und überprüfen Sie, ob es sauber ist (insbesondere keine Stufen oder Klingeln um den Mittelpunkt der Steigung).
  • Hängen Sie einen Logikanalysator an die Signale und überprüfen Sie, ob sie an der erwarteten Flanke übertragen werden und im Leerlauf den erwarteten Zustand erreichen.