Forum: Mikrocontroller und Digitale Elektronik PicoPi & OnlyWrite SPI blockiert


von Rudi (rudils)


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?
von Arduino F. (Firma: Gast) (arduinof)


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
von Rainer W. (rawi)


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
von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


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
von Rahul D. (rahul)


Lesenswert?

Lothar M. schrieb:
> Master Out Slave Out, also eine Buskollision.

oder dicke Finger, wenn man bedenkt, wo sich "I" und "O" auf der 
Tastatur befinden ;)
von Ob S. (Firma: 1984now) (observer)


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.
von Rudi (rudils)


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
}
von N. M. (mani)


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
von Norbert (der_norbert)


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
von Rolf (rolf22)


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
von Rudi (rudils)


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.
von Rainer W. (rawi)


Lesenswert?

Rudi schrieb:
> Rainer W. schrieb:
>> Bei SPI gibt es kein "OnlyWrite".
> Und wenn der Slave MCP4922 keinen Sendepin hat?

Dann liest der Master trotzdem ein, was bei ihm an MISO anliegt.
von Rolf (rolf22)


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
von Norbert (der_norbert)


Lesenswert?

Rudi schrieb:
> Weil ich ja 16Bits senden will soll ich diese Zeile einfügen.

Frechheit, dass ShitGPT das nicht besser erklärt.
von Ob S. (Firma: 1984now) (observer)


Lesenswert?

Rolf schrieb:

> Beim OP können da ja gar keine Daten in den FIFO reinlaufen, weil sein
> Slave keine liefert.

Unsinn. Da wird immer irgendetwas gelesen. Auch dann, wenn es überhaupt 
keine echte Datenquelle gibt. Selbst wenn der RX-Pin einfach nur in der 
Luft hängt. Dann werden halt Zufallsdaten gelesen.
von Ob S. (Firma: 1984now) (observer)


Lesenswert?

Norbert schrieb:
> Rudi schrieb:
>> Weil ich ja 16Bits senden will soll ich diese Zeile einfügen.
>
> Frechheit, dass ShitGPT das nicht besser erklärt.

LOL

Naja, mit Nachhaken kann man vermutlich auch eine bessere Erklärung 
bekommen. Aber ob die irgendwas mit der objektiven Realität zu tun hat 
oder reine KI-Halluzination ist, wer weiß das schon...
von Rolf (rolf22)


Lesenswert?

Ob S. schrieb:

> Selbst wenn der RX-Pin einfach nur in der
> Luft hängt. Dann werden halt Zufallsdaten gelesen.

Das sind dann Zufallswerte, keine Daten. Bei einer Datenübertragung 
werden Daten vom sie sendenden Partner generiert.
von Ob S. (Firma: 1984now) (observer)


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?!
von Rudi (rudils)


Lesenswert?

Rolf schrieb:
> Nimm also SPI_CPHA_0  und  SPI_CPOL_0. Dann ist das Programm auch besser
> lesbar.

Das hatte ich schon probiert, aber leider in falscher Reihenfolge. 
Sorry, manchmal bin ich doch zu oberflächlich.

An Alle:
Seit 12:00 Uhr blockiert der MISO nicht mehr.
von Wastl (hartundweichware)


Lesenswert?

Rudi schrieb:
> An Alle:
> Seit 12:00 Uhr blockiert der MISO nicht mehr.

Ich behaupte mal dass MISO nie blockiert und auch bei dir
nie blockiert hat.
von Wastl (hartundweichware)


Lesenswert?

.... 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.
von Ob S. (Firma: 1984now) (observer)


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.
von Rudi (rudils)


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.
von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Rudi schrieb:
> Ohne die IF-READABLE Abfrage ist der Fehler weg.
Also in etwa das, was

ich schrieb:
>>> Sieh dir an, was die von dir verwendete spi_is_readable(spi) macht. Und
>>> dann wirf sie raus
: Bearbeitet durch Moderator
von Rudi (rudils)


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.
von Norbert (der_norbert)


Lesenswert?

Wie viel Wind muss man eigentlich machen, wenn es insgesamt um die 
Konfiguration zweier GPIO Pins und um genau drei (DREI) SPI-Register 
Zugriffe zur Initialisierung geht?

Denn damit hat man ein lauffähiges SPI eingerichtet.
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.