SPI mit sam4s16c einrichten

Ich habe als Programmierer für Software angefangen und habe einen professionellen Hintergrund mit großen Softwareprojekten in C++ und QT. Jetzt habe ich die Firma gewechselt und muss auch etwas Low-Level-Hardwarecode schreiben, und ich habe einige Startprobleme, indem ich mit Tönen von Datenblättern herumgespielt habe. Ich bin neu in der Hardwareprogrammierung im Allgemeinen und weiß nicht viel über Tools oder wie ich mir selbst helfen kann.

Mein Plan ist es, die Kommunikation mit einem Sam4s16c (auf einem SAM4SXplained-Board ) mit einem L6470- Motor über SPI zu starten. Mein Problem ist, dass das SPI-Setup mir Kopfschmerzen bereitet und nicht funktioniert. Beim Messen von CS/SS (Pin31A) zeigt ein Oszilloskop nur eine hohe Spannung (~3,2 V), aber keinen gültigen Takt (ich sollte hier ein "Auf und Ab" sehen, oder nicht?).

Ich bin mir nicht sicher, was ein guter Wert für den Baudratenteiler ist (und was er im Allgemeinen bedeutet). Nach viel Lesen habe ich gehört, dass ich die Pins richtig "konfigurieren" muss, ich habe dies mit diesen Zeilen versucht

gpio_configure_pin(SPI_NPCS1_PA31_GPIO, SPI_NPCS1_PA31_FLAGS);
gpio_set_pin_high(SPI_NPCS1_PA31_GPIO);

Mein Code bisher.

#include "asf.h"
#include "stdio_serial.h"
#include "conf_board.h"
#include "conf_clock.h"
#include "conf_spi_example.h"

#ifdef __cplusplus
extern "C" {
#endif

/* Chip select. */
#define SPI_CHIP_SEL 0
#define SPI_CHIP_PCS spi_get_pcs(SPI_CHIP_SEL)

/* Clock polarity. */
#define SPI_CLK_POLARITY 1

/* Clock phase. */
#define SPI_CLK_PHASE 1

/* Delay before SPCK. */
//#define SPI_DLYBS 0x40
#define SPI_DLYBS 0xFF

/* Delay between consecutive transfers. */
#define SPI_DLYBCT 0x10

/* SPI clock setting (Hz). */
static uint32_t gs_ul_spi_clock = 1000000;


volatile uint32_t g_ul_ms_ticks = 0;
void SysTick_Handler(void)
{
    g_ul_ms_ticks++;
}
static void mdelay(uint32_t ul_dly_ticks)
{
    uint32_t ul_cur_ticks;

    ul_cur_ticks = g_ul_ms_ticks;
    while ((g_ul_ms_ticks - ul_cur_ticks) < ul_dly_ticks);
}


/**
 * \brief Initialize SPI as master.
 */
static void spi_master_initialize(void)
{
    //Assign I/O lines to peripheral
    #define SPI_MISO_IOPIN  IOPORT_CREATE_PIN(PIOA, PIO_PA12_IDX)
    #define SPI_MOSI_IOPIN  IOPORT_CREATE_PIN(PIOA, PIO_PA13_IDX)
    #define SPI_SPCK_IOPIN  IOPORT_CREATE_PIN(PIOA, PIO_PA14_IDX)
    #define SPI_NPCS1_IOPIN  IOPORT_CREATE_PIN(PIOA, PIO_PA31_IDX)

    ioport_set_pin_mode(SPI_MISO_IOPIN, PIO_PERIPH_A);
    ioport_disable_pin(SPI_MISO_IOPIN);
    ioport_set_pin_mode(SPI_MOSI_IOPIN, PIO_PERIPH_A);
    ioport_disable_pin(SPI_MOSI_IOPIN);
    ioport_set_pin_mode(SPI_SPCK_IOPIN, PIO_PERIPH_A);
    ioport_disable_pin(SPI_SPCK_IOPIN);
    ioport_set_pin_mode(SPI_NPCS1_IOPIN, PIO_PERIPH_A);
    ioport_disable_pin(SPI_NPCS1_IOPIN);

    /* Configure an SPI peripheral. */
    spi_enable_clock(SPI_MASTER_BASE);
    spi_disable(SPI_MASTER_BASE);
    spi_reset(SPI_MASTER_BASE);
    spi_set_lastxfer(SPI_MASTER_BASE);
    spi_set_master_mode(SPI_MASTER_BASE);
    spi_disable_mode_fault_detect(SPI_MASTER_BASE);
    spi_set_peripheral_chip_select_value(SPI_MASTER_BASE, spi_get_pcs(SPI_CHIP_PCS));
    spi_set_fixed_peripheral_select(SPI_MASTER_BASE);
    spi_set_delay_between_chip_select(SPI_MASTER_BASE, 0);

    // Set the Chip Select register
    spi_set_transfer_delay(SPI_MASTER_BASE, SPI_CHIP_SEL, SPI_DLYBS, SPI_DLYBCT);

    int16_t baudrate = spi_calc_baudrate_div(96000, sysclk_get_cpu_hz());
    spi_set_baudrate_div(SPI_MASTER_BASE, SPI_CHIP_SEL, 8); //sysclk_get_cpu_hz() / gs_ul_spi_clock);
    spi_set_bits_per_transfer(SPI_MASTER_BASE, SPI_CHIP_SEL, SPI_CSR_BITS_8_BIT);
    //spi_configure_cs_behavior(SPI_MASTER_BASE, SPI_CHIP_SEL, SPI_CS_RISE_NO_TX);
    spi_set_clock_polarity(SPI_MASTER_BASE, SPI_CHIP_SEL, SPI_CLK_POLARITY);
    spi_set_clock_phase(SPI_MASTER_BASE, SPI_CHIP_SEL, SPI_CLK_PHASE);

    spi_enable(SPI_MASTER_BASE);

    mdelay(100);
}

static void spi_master_write(uint16_t value)
{
    spi_write(SPI_MASTER_BASE, value, SPI_CHIP_SEL, 0);
    while ((spi_read_status(SPI) & SPI_SR_RDRF) == 0);/* Wait transfer done. */
}

static spi_status_t spi_master_read(uint16_t* data)
{
    uint8_t uc_pcs;

    spi_status_t returnValue = spi_read(SPI_MASTER_BASE, data, &uc_pcs);
    return returnValue;
}

static void ledHello(void)
{
    for (int i=0; i < 6; ++i)
    {
        LED_Toggle(LED0_GPIO);
        LED_Toggle(LED1_GPIO);
        mdelay(150);
    }
    mdelay(150);

    for (int i=0; i < 5; ++i)
    {
        LED_Toggle(LED0_GPIO);
        mdelay(150);
    }

    for (int i=0; i < 5; ++i)
    {
        LED_Toggle(LED1_GPIO);
        mdelay(150);
    }
    // Both LEDs stay permanent on
}

int main(void)
{
    board_init();
    sysclk_init();

    NVIC_DisableIRQ(SPI_IRQn);
    NVIC_ClearPendingIRQ(SPI_IRQn);
    NVIC_SetPriority(SPI_IRQn, 0);
    NVIC_EnableIRQ(SPI_IRQn);

    // [main_step_systick_init]
    if (SysTick_Config(sysclk_get_cpu_hz() / 1000)) {
        // Systick configuration error
        while (1);
    }

    ledHello(); // Code here toggles leds on board

    /* Configure SPI as master, set up SPI clock. */
    spi_master_initialize();

    // activate the SPI of the micro controller of the L6470 via SPI
    spi_master_write(0x18); // SetParam(Config)
    spi_master_write(0x16);
    spi_master_write(0x10);

    mdelay(100);
    while (1) {
        spi_master_write(0x38); // GetParam(Config)
        static uint16_t data;
        spi_master_read(data);
        spi_master_read(data);
    }
}

#ifdef __cplusplus
}
#endif

Im Moment versuche ich, die Spi-Uhr / Ausgabe richtig einzustellen. Habe ich etwas verpasst?

Nachdem @Tom L. mir bei der Pin-Konfiguration geholfen hat, funktioniert meine Uhr, hat aber einige "Schluckauf" (was ist das Fachwort dafür?). Es zeigt 8 Pulse, dann eine Pause für ca. 6 Pulse, dann wieder beginnend mit 8. sehr regelmäßig. Irgendeine Idee dazu?

Antworten (1)

Ihnen fehlt das GPIO-Setup. Sie dürfen die GPIO-Funktion NICHT zuweisen, aber Sie müssen dem Pin mitteilen, welche alternative Funktion er tatsächlich hat. Das wird etwas in der Art sein:

#define SPI_SO_PIN                      PIN_PC28B_SPI_MISO  
#define SPI_SO_MUX                      MUX_PC28B_SPI_MISO  
#define SPI_SI_PIN                      PIN_PC29B_SPI_MOSI  
#define SPI_SI_MUX                      MUX_PC29B_SPI_MOSI  
#define SPI_SCK_PIN                     PIN_PC30B_SPI_SCK   
#define SPI_SCK_MUX                     MUX_PC30B_SPI_SCK   
#define SPI_CS0_PIN                     PIN_PC31B_SPI_NPCS0 
#define SPI_CS0_MUX                     MUX_PC31B_SPI_NPCS0 

#define ioport_set_pin_peripheral_mode(pin, mode) \
do {\
    ioport_set_pin_mode(pin, mode);\
    ioport_disable_pin(pin);\
} while (0)

ioport_set_pin_peripheral_mode(SPI_SCK_PIN, SPI_SCK_MUX);
ioport_set_pin_peripheral_mode(SPI_SO_PIN, SPI_SO_MUX);
ioport_set_pin_peripheral_mode(SPI_SI_PIN, SPI_SI_MUX);
ioport_set_pin_peripheral_mode(SPI_CS0_PIN, SPI_CS0_MUX);

Atmel hat ein ziemlich gutes Beispiel-Repository. Starten Sie Atmel Studio, wählen Sie ein neues Beispielprojekt aus, wählen Sie Ihren Controller oder Ihr Board aus und laden Sie etwas, das sich auf die Komponente bezieht, die Sie implementieren (in Ihrem Fall SPI). Folgen Sie dann "main", bis Sie zu dem Punkt kommen, der Sie interessiert. Von da an ist es normalerweise einfach.

Wenn sich ein Pin aus irgendeinem Grund nicht bewegt, ist die beste Vermutung, dass Sie Ihr GPIO (oder eine alternative Ping-Konfiguration) falsch eingerichtet haben.

Es scheint, dass sich der SAM4S ein wenig vom SAM4L unterscheidet. Wenn Sie sich das mitgelieferte SPI-Beispiel aus dem ASF ansehen, sehen Sie die Init-Sequenz (Funktion board_init() in SPI_EXAMPLE1\src\ASF\sam\boards\sam4s_ek\init.c) wie folgt:

/* Configure an SPI peripheral. */
spi_enable_clock(SPI_MASTER_BASE);
spi_disable(SPI_MASTER_BASE);
spi_reset(SPI_MASTER_BASE);
spi_set_lastxfer(SPI_MASTER_BASE);
spi_set_master_mode(SPI_MASTER_BASE);
spi_disable_mode_fault_detect(SPI_MASTER_BASE);
spi_set_peripheral_chip_select_value(SPI_MASTER_BASE, SPI_CHIP_PCS);
spi_set_clock_polarity(SPI_MASTER_BASE, SPI_CHIP_SEL, SPI_CLK_POLARITY);
spi_set_clock_phase(SPI_MASTER_BASE, SPI_CHIP_SEL, SPI_CLK_PHASE);
spi_set_bits_per_transfer(SPI_MASTER_BASE, SPI_CHIP_SEL, SPI_CSR_BITS_8_BIT);
spi_set_baudrate_div(SPI_MASTER_BASE, SPI_CHIP_SEL, (sysclk_get_peripheral_hz()/ gs_ul_spi_clock));
spi_set_transfer_delay(SPI_MASTER_BASE, SPI_CHIP_SEL, SPI_DLYBS, SPI_DLYBCT);
spi_enable(SPI_MASTER_BASE);

gpio_configure_pin(SPI_MISO_GPIO, SPI_MISO_FLAGS);
gpio_configure_pin(SPI_MOSI_GPIO, SPI_MOSI_FLAGS);
gpio_configure_pin(SPI_SPCK_GPIO, SPI_SPCK_FLAGS);
gpio_configure_pin(SPI_NPCS0_GPIO, SPI_NPCS0_FLAGS);

Um tatsächlich mit einem Gerät zu sprechen, müssen Sie dieses Gerät auswählen (dies tut spi_set_peripheral_chip_select_value, es enthält auch Logik, wenn Sie einen DEMUX an Bord haben, weshalb es das Makro spi_get_pcs verwendet). Da SPI_CHIP_SEL 0 ist, sprechen Sie mit jedem Gerät, das mit CS0 verbunden ist. Wenn Sie mit einem Gerät auf einer anderen Chipauswahlleitung sprechen möchten, müssen Sie diese Werte und Ihr GPIO-Setup entsprechend ändern.

Ihre erste Aufgabe besteht darin, die Chip-Select-Leitung niedrig zu halten. Wenn dies nicht getan wird, erhalten Sie niemals Daten vom Slave, da er denkt, dass Sie nicht mit ihm, sondern mit einem anderen Gerät auf demselben SPI-Bus sprechen.

Danke, das gibt mir einen großen Durchbruch. Jetzt kann ich meine Uhr mit dem Oszilloskop sehen. Auch das Schreiben von Daten sieht jetzt ziemlich gut aus. Nur das Lesen von Daten erhält jedes Mal 0x00.
Jetzt versuche ich, den Loopback-Modus zu verwenden, spi_enable_loopback(SPI_MASTER_BASE)um meine Daten aufzuspüren, aber ich habe immer noch 0x00 erhalten. sieht aus wie einige Konfiguration ist auch falsch
Haben Sie überprüft, ob Ihre MISO/SO-Zeile tatsächlich Daten enthält? Geht Ihre CS-Leitung richtig niedrig? Zeigen Sie uns den Code, den Sie bisher haben - wahrscheinlich haben Sie vergessen, CS zu bestätigen. Haben Sie überprüft, ob Sie die richtige Pin-Konfiguration haben?
Ich habe meinen Code mit GPIO-Setup, Slave-Initialisierung und Lesen der Konfiguration vom Slave in While-Schleife aktualisiert. Ich konnte Ihr GPIO-Setup nicht testen, mir fehlt der PIN_PC31B_SPI_NPCS0PIN_PC31B_SPI_NPCS0 defines (like in my code, they named slightly different, but i can't find out an equivalent variable for the MUX_`). Und ja, meine CS-Linie ändert sich nicht. Die MISO/SO-Linie sieht gut aus.
´#define SPI_CHIP_SEL 0 #define SPI_CHIP_PCS spi_get_pcs(SPI_CHIP_SEL)` was hat es mit diesen Definitionen auf sich? Atmel Studio sagt mir, dass SPI_CHIP_PCS = 14. Ist dieser "Wert" der Pin meines CS? Ich meine meinen CS-Pin PA31.
Nein, du machst es falsch :-). Gehen Sie zum Datenblatt Ihres Controllers, sehen Sie sich die GPIO-Tabelle am Anfang an (eine große, 3 oder 4 Seiten lange Tabelle). Es listet die Pin-Identität wie PA31 auf und daneben listet es seine Funktionen auf (um Seite 52 - PIO Controller A Multiplexing).
Ich werde meinem Hauptpost etwas hinzufügen
Auf dem [sam4s xplained Board habe ich den Erweiterungsheader 1[( atmel.com/webdoc/sam4s16xplained/… ) verwendet, der mit cs1 alias PA31 verbunden ist (ich brauche ewig, um das herauszufinden). Das Ändern #define SPI_CHIP_SEL 0in #define SPI_CHIP_SEL 1Code behebt dies. so viel kleine schritte zu gehen :) danke für deine tipps und hilfe. Ich markiere deine Antwort als richtig
Freut mich zu hören, dass es funktioniert. Ja, es dauert eine Weile, bis man herausgefunden hat, wie man Dinge richtig macht, aber sobald man das herausgefunden hat, funktionieren alle Peripheriegeräte auf ähnliche Weise.