PicoPi & OnlyWrite SPI blockiert

OP #8063929
Lesenswert?

Ich habe an meinem Pico den DA-MCP4922 an SPI angeschlossen.
Der DA hat nur einen Write Kanal. Der SPI_MOSO Pin ist unbeschaltet, aber als SPI-Pin initalisiert.
Wenn ich nun dahin Daten sende, bleibt in der API-Funktion in Zeile 12 das Programm hängen. Da wird ja gefragt ob Readable.

1
int __not_in_flash_func(spi_write16_blocking)(spi_inst_t *spi, const uint16_t *src, size_t len) {
2
    invalid_params_if(HARDWARE_SPI, 0 > (int)len);
3
    // Deliberately overflow FIFO, then clean up afterward, to minimise amount
4
    // of APB polling required per halfword
5
    for (size_t i = 0; i < len; ++i) {
6
        while (!spi_is_writable(spi))
7
            tight_loop_contents();
8
        spi_get_hw(spi)->dr = (uint32_t)src[i];
9
    }
10
    while (spi_is_readable(spi))
11
        (void)spi_get_hw(spi)->dr;
12
    while (spi_get_hw(spi)->sr & SPI_SSPSR_BSY_BITS)
13
        tight_loop_contents();
14
    while (spi_is_readable(spi))
15
        (void)spi_get_hw(spi)->dr;
16
    // Don't leave overrun flag set
17
    spi_get_hw(spi)->icr = SPI_SSPICR_RORIC_BITS;
18
    return (int)len;
19
}

Meine DA-SPI-Ic ist aber nicht readable. Wie geht sowas trotzdem?

(Firma: Gast) #8063932
Lesenswert?

Rudi schrieb:

Meine DA-SPI-Ic ist aber nicht readable.

Uninteressant!

Rudi schrieb:

SPI_MOSO

Du meinst MISO! Irgendwo muss MISO als Eingang am RP definiert werden. Dann kann man ihn mit einem Pullup oder Pulldown versehen um eindeutige Pegel zu erhalten. Die µC SPI Einheit merkt nicht, dass da kein Baustein angeschlossen ist.

: Bearbeitet durch User
#8063965
Lesenswert?

Bei SPI gibt es kein "OnlyWrite". SPI tauscht auf unterstem Level immer gleichzeitig Bytes zwischen Master und Slave in beide Richtungen aus, auf MOSI (= Master out, Slave In) vom Master zum Slave, auf MISO (= Master In, Slave Out) vom Slave zum Master. Der Master ist derjenige, der den Clock für die Taktung der Übertragung erzeugt.

Ob deine Software dann eines der übertragenen Bytes ignoriert, bleibt ihr überlassen.

: Bearbeitet durch User
Moderator (Firma: Titel) Persönliche Seite #8063970
Lesenswert?

Rudi schrieb:

SPI_MOSO Pin

Master Out Slave Out, also eine Buskollision.

Wie geht sowas trotzdem?

Sieh dir an, was die von dir verwendete spi_is_readable(spi) macht. Und dann wirf sie raus oder schreib sie um.

Mein Tipp:

  1. sieh dir an, was SPI ist und wie er funktioniert (das sind nur gekoppelte Schieberegister)
  2. sieh dir an, welche Struktur der SPI deines uC hat und verstehe, wie du sie ansteuern musst, dass das Schieberegister lostaktet
  3. schreib diese Software selber, lass das nicht die KI machen
(Firma: 1984now) #8063979
Lesenswert?

Lothar M. schrieb:

Sieh dir an, was die von dir verwendete spi_is_readable(spi) macht. Und dann wirf sie raus oder schreib sie um.

Nein, auf keinen Fall. Die gesamte Routine spi_write16_blocking() stammt direkt aus dem SDK und sie funktioniert so, wie sie ist. Benutze ich an etlichen Stellen in etlichen Programmen.

Sie ist allerdings etwas schräg konstruiert. Um schneller schreiben zu können, wird während des Schreibvorgangs erst mal alles ignoriert, was mit der Empfangsseite der SPI-Hardware zu tun hat.

Dadurch "hängen" dann natürlich ungelesene Daten im RX-FIFO, u.U. ist auch der RX-FIFO übergelaufen. Alles ab Zeile 10 in dieser Routine dient einzig dazu, nach dem Schreiben auch die RX-Seite der Hardware "aufzuräumen" und wieder in einen sauberen und leeren Zustand zu versetzen. Diese SPI-Schnittstelle ist eben doch MEHR als einfach nur ein Schieberegister!

Das Problem des TO liegt wahrscheinlich an einer inkorrekten Initialisierung der SPI-Hardware. Die hat er aber leider nicht gezeigt.

OP #8063982
Lesenswert?

Rainer W. schrieb:

Bei SPI gibt es kein "OnlyWrite".

Und wenn der Slave MCP4922 keinen Sendepin hat?

Lothar M. schrieb:

Und dann wirf sie raus oder schreib sie um.

Das ist eine API-Funktion! Die kann ich nicht.

Ob S. schrieb:

Die hat er aber leider nicht gezeigt.

1
MCP4922::MCP4922 (){
2

3
    if ( !spi_is_writable (SPI_PORT)) {
4
        // SPI initialisation. This example will use SPI at 1Mhz.
5
        spi_init(SPI_PORT, 1000*1000);
6
        gpio_set_function(PIN_MISO, GPIO_FUNC_SPI);
7
        gpio_set_function(PIN_CSi,  GPIO_FUNC_SIO);
8
        gpio_set_function(PIN_SCK,  GPIO_FUNC_SPI);
9
        gpio_set_function(PIN_MOSI, GPIO_FUNC_SPI);
10

11
        gpio_set_dir(PIN_CSi, GPIO_OUT);
12
        gpio_put(PIN_CSi, 1);
13
    }
14
    Vout( 0.0 );
15
    Aout( 0.0 );
16
}
17
//----------------------------------------------------------------------------------------------------------------------------
18
void MCP4922::Vout( float v ){
19
    float           v3 = v / 3.0 / 0.0007324;
20
    uint16_t     vi = v3; 
21
    vi >= 4096 ? 4095 : vi;
22
    vi |= 0xb000;                   // B Kanal select + Activmode + Gain1x
23

24
    asm volatile("nop \n nop \n nop");
25
    gpio_put(PIN_CSi, 0);  // Active low
26
    asm volatile("nop \n nop \n nop");
27

28
    spi_write16_blocking(SPI_PORT, &vi, 1);
29

30
    asm volatile("nop \n nop \n nop");
31
    gpio_put(PIN_CSi, 1);
32
    asm volatile("nop \n nop \n nop");
33
}
#8063993
Lesenswert?

Mach mal das ganze init Zeug aus deinem Konstruktor in eine eigene init() Methode und rufe diese zu einem definierten Zeitpunkt in setup/main auf.

Die Aufrufe momentan im Konstruktor passieren sehr früh. Vielleicht zu früh. Außerdem kannst du mit einer init() auch gezielter steuern wann der Zugriff auf die Register genau passiert. Momentan ist das abhängig vom Zeitpunkt der Erzeugung der Objekte.

Nicht dass da nochmal später in main was überschrieben wird oder die Clocks noch garnicht initialisiert waren.

: Bearbeitet durch User
#8064000
Lesenswert?

Rudi schrieb:

Und wenn der Slave MCP4922 keinen Sendepin hat?

Das interessiert nicht.

Bevor du sendest, hast du im SSPSR nur TNF: 1 | TFE: 1.

Wenn du dann sendest, jedoch keine Daten abholst, bzw. noch nicht einmal einen GPIO MOSI zugewiesen hast, dann kommt im SSPSR erst RNE: 1 und nach kurzer Zeit RFF: 1.

Aber, und das ist wichtig, das sind lediglich /flags/. Es wird nichts blockiert.

PS. Sollte diese Zeile:

1
vi >= 4096 ? 4095 : vi;

nicht

1
vi = vi >= 4096 ? 4095 : vi;

lauten?

: Bearbeitet durch User
#8064005
Lesenswert?

Ob S. schrieb:

Dadurch "hängen" dann natürlich ungelesene Daten im RX-FIFO, u.U. ist auch der RX-FIFO übergelaufen. Alles ab Zeile 10 in dieser Routine dient einzig dazu, nach dem Schreiben auch die RX-Seite der Hardware "aufzuräumen" und wieder in einen sauberen und leeren Zustand zu versetzen.

Beim OP können da ja gar keine Daten in den FIFO reinlaufen, weil sein Slave keine liefert. Also den MISO-Pin fest auf Low legen. Wenn das nicht genügt, die API-Doku studieren (was genau macht spi_is_readable). Es kann ja nicht sein, dass man nur Slaves anschließen kann, die bidirektional arbeiten.

Nebenbei: "Readable" heißt "lesbar". Das ist zumindest zweideutig. Gemeint ist "Es befinden sich derzeit Daten im FIFO, die man auslesen kann (Rx data available)". Gemeint ist nicht das Gegenteil von "readonly", also der grundsätzlichen Möglichkeit, irgendetwas etwas zu lesen. Steht ja in der Doku: https://www.raspberrypi.com/documentation/pico-sdk/hardware.html#group_hardware_spi_1ga20dcaa3037e92bbcbab50b8635f102ff

OP #8064043
Lesenswert?

Soweit Danke.
Jetzt hänge ich am nächsten Problem.

1
spi_set_format ( SPI_PORT, 16, 0, 0, SPI_MSB_FIRST);

Weil ich ja 16Bits senden will soll ich diese Zeile einfügen. Der Compiler mekert aber die beiden Nullen an. Ohne dieser Zeile messe ich 8 Clockimpulse. Seltsammer Weise sind diese nur dreieckig. Bei Data kommt nur Dauerhigh. CS ist ok.

#8064051
Lesenswert?

Rudi schrieb:

Weil ich ja 16Bits senden will soll ich diese Zeile einfügen.

spi_set_format ( SPI_PORT, 16, 0, 0, SPI_MSB_FIRST);

Der Compiler mekert aber die beiden Nullen an.

Es hilft, wenn du sagst, wie die Fehlermeldung lautet. :-( Ich rate mal: "Falscher Typ". Dort werden ja auch keine Integer ("0") erwartet, sondern zwei Namen von enum-Konstanten. Siehe Funktionsdefinition. So, wie SPI_MSB_FIRST ja auch eine enum-Konstante ist. Die Namen der der Konstanten findest du unter dem Link, den ich schon genannt habe.

Nimm also SPI_CPHA_0 und SPI_CPOL_0. Dann ist das Programm auch besser lesbar.

: Bearbeitet durch User
(Firma: 1984now) #8064088
Lesenswert?

Rolf schrieb:

Das sind dann Zufallswerte, keine Daten.

Was ist der Unterschied zwischen Werten und Daten?

Bei einer Datenübertragung werden Daten vom sie sendenden Partner generiert.

Ja. Nur ist in dieser konkreten Anwendung eben eine Datenübertragung in dieser Richtung nicht gewünscht, ja nicht einmal möglich, weil eben der Partner NICHT in der Lage ist, irgendwas sinnvolles zu generieren, schon weil ihm ein passender Pin dafür fehlt.

Aber gelesen wird unweigerlich trotzdem irgendetwas. Und genau das ist hier der springende Punkt. Und eben deswegen existieren die Zeilen ab Nr. 10 im Code der SDK-Routine.

Das kann doch nicht so schwer zu begreifen sein?!

(Firma: 1984now) #8064109
Lesenswert?

Wastl schrieb:

.... denn MISO ist ein Daten-Eingangspin der dem SPI-Input- Register einfach nur Daten liefert die am Pin anliegen. Und das können genau zwei Werte sein: 1 oder 0. MISO beeinflusst nicht den Ablauf der SPI-Statemachine.

Genauso ist das. Der Slave hat keinerlei Einfluss auf die Statemachine des Masters. Der Hardware und dem "Treiber" ist vollkommen Wurscht, was genau da gelesen wird. Es wird aber immer irgendwas gelesen. Das muss der "Treiber" berücksichtigen und tut das auch.

OP #8064145
Lesenswert?

Hallo, ein großteil der Disskussion geht von missverstandenen Umständen aus. Ich hab in Beitrag Beitrag "PicoPi & OnlyWrite SPI blockiert" gefragt warum die API-Funtion hängen bleibt. Im Beitrag Beitrag "Re: PicoPi & OnlyWrite SPI blockiert" sieht man meinen Fehler: Die SPI-Init sollte da nur ausgeführt werden , falls die SPI noch nicht da ist. Das aber geht nicht so! Ohne die IF-READABLE Abfrage ist der Fehler weg.

OP #8064251
Lesenswert?

Rudi schrieb:

1
 if ( !spi_is_writable (SPI_PORT)) {
2
         // SPI initialisation. This example will use SPI at 1Mhz.
3
         spi_init(SPI_PORT, 1000*1000);
4
         gpio_set_function(PIN_MISO, GPIO_FUNC_SPI);

Es tut mir leid. Ich habe schon wieder einen Flüchtigkeitsfehler gemacht.

Rudi schrieb:

Ohne die IF-READABLE Abfrage

Ich habe nicht die API- spi_is_readable(spi) Fkt entfernt sondern die IF Abfrage in der INIT-Sequenz. Denn so wurde diese Init nicht ausgeführt, was zur zur Blockade führte.
Mein Logigfehler war: wenn der SPI-Port schon vorher, möglicher Weise, Initalisiert war, dann kann man gleich senden. Dass diese Abfrage auch ohne spi_init True meldet konnte ich nicht vermuten.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren