Forum: Mikrocontroller und Digitale Elektronik STM32Fxxxx: Kurze Frage zu GPIO Bitbanging


von Christian J. (Gast)


Lesenswert?

Hallo,

ich möchte auf einem F103 Blue Pill Board einen HCT595 mit Bits 
befüllen. Mangels freier SPI schiebe ich die Bits einzeln heraus.

Frage: Muss man bei eingeschalteter GCC Optimierung -Os beim Toggeln 
eines Portpins einen Delay einlegen? Ich kenne die interne Schaltung des 
Cortex nicht, weiss nicht wie Piplines oder Caching da reinfummeln 
können, denn das physikalische Setzen eines Pins mit sofortigem 
Rücksetzen könnte ja auch verschluckt werden.

Könnte mir da jemand eine sichere Lösung nennen, die aber optimale 
Geschwindigkeit bietet? (72 Mhz)

Grüße,
Christian

von (prx) A. K. (prx)


Lesenswert?

Überrennen wirst du nichts, keine Sorge. Erwarte aber keine 
herausragende Toogle-Frequenz vom F103, denn bei dem hängen die GPIOs an 
einem der APB Peripheriebusse. Bei späteren STM32 hängen sie am 
flotteren AHB.

von Jim M. (turboj)


Lesenswert?

Christian J. schrieb:
> Frage: Muss man bei eingeschalteter GCC Optimierung -Os beim Toggeln
> eines Portpins einen Delay einlegen? [...] 72 MHz


Ja, aber nur weil der Pin sonst nicht hinterher kommt.
Ich würde einfach ein oder zwei __NOP(); einfügen - und mit dem Oszi das 
Signal kontrollieren, falls was nicht tut.

Verschluckt wird nix, aber der Pin hat ja nur eine begrenzte Stromstärke 
und muss die Eingagskapazität des/der 595 umladen.

Übrigens kommt es auch darauf an wie genau Du die Ports setzt - falls da 
ein Funktionsaufruf statt findet, brauchst Du keine Nops mehr einfügen. 
Direktes Schreiben der Pinsetz/Pinlösch- Register geht am schnellsten.

von (prx) A. K. (prx)


Lesenswert?

Jim M. schrieb:
> Verschluckt wird nix, aber der Pin hat ja nur eine begrenzte Stromstärke
> und muss die Eingagskapazität des/der 595 umladen.

So krass ist die Eingangskapazität eines nah angebundenen 595 nicht, 
dass man sich eigens deshalb Sorgen machen müsste. Die GPIO Pins sind 
zudem auf die vorgesehene Frequenz konfigurierbar.

: Bearbeitet durch User
von grundschüler (Gast)


Lesenswert?

code von elm chan für AVR, geht auch beim ARM
1
static
2
void xmit_mmc (
3
  const BYTE* buff,  /* Data to be sent */
4
  UINT bc        /* Number of bytes to send */
5
)
6
{
7
  BYTE d;
8
9
10
  do {
11
    d = *buff++;  /* Get a byte to be sent */
12
    if (d & 0x80) DI_H(); else DI_L();  /* bit7 */
13
    CK_H(); CK_L();
14
    if (d & 0x40) DI_H(); else DI_L();  /* bit6 */
15
    CK_H(); CK_L();
16
    if (d & 0x20) DI_H(); else DI_L();  /* bit5 */
17
    CK_H(); CK_L();
18
    if (d & 0x10) DI_H(); else DI_L();  /* bit4 */
19
    CK_H(); CK_L();
20
    if (d & 0x08) DI_H(); else DI_L();  /* bit3 */
21
    CK_H(); CK_L();
22
    if (d & 0x04) DI_H(); else DI_L();  /* bit2 */
23
    CK_H(); CK_L();
24
    if (d & 0x02) DI_H(); else DI_L();  /* bit1 */
25
    CK_H(); CK_L();
26
    if (d & 0x01) DI_H(); else DI_L();  /* bit0 */
27
    CK_H(); CK_L();
28
  } while (--bc);
29
}

von Christian J. (Gast)


Lesenswert?

Hallo,

ok. Liege ich hiermit jetzt total falsch? Ein AVR hat mit einem ARM 
allerdings gemeinsam und solche Sachen wie oben sind mir zu aufgeblasen. 
Man kann auch Schleifen entrollen lassen wenn man will. Und so 10 Mhz 
reichen auch, das menschliche Auge kommt da eh nicht mehr mit bei den 
LEDs, die am 595 dran sind.
1
/* Den 74HCT595 konfigurieren */
2
  GPIO_StructInit(&GPIO_InitStructure);                  // Struct Init
3
  GPIO_InitStructure.GPIO_Pin   = HC595_DS | HC595_STCP | HC595_OE | HC595_SHCP;
4
  GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_Out_PP;
5
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
6
  GPIO_Init(HC595_PORT, &GPIO_InitStructure);            // Ausführen
7
8
  GPIO_SetBits(HC595_PORT,HC595_OE);                     // Outputs = Z-State
9
  GPIO_ResetBits(HC595_PORT,HC595_STCP);                 // Storage Register = LOW
10
  GPIO_ResetBits(HC595_PORT,HC595_SHCP);                 // Shift Register = LOW
11
12
/* Schreibt ein 8 Bit Wort in den 74HCT595  */
13
void Set74HCT595(uint8_t data)
14
{
15
    for (uint8_t i = 0; i < 8; i++) {
16
        // Datenbit setzen
17
        GPIO_WriteBit(HC595_PORT,HC595_DS,(data & 0x80)); __DMB();    // Bit anlegen
18
        GPIO_SetBits(HC595_PORT,HC595_SHCP);  __DMB();                // SHCP -> High
19
        GPIO_ResetBits(HC595_PORT,HC595_SHCP); __DMB();               // SHCP -> LOW
20
        data = data << 1;
21
    }
22
    // 16 Bit an die Ausgaenge clocken
23
    GPIO_SetBits(HC595_PORT,HC595_STCP); __DMB();      // STCP -> HIGH
24
    GPIO_ResetBits(HC595_PORT,HC595_STCP); __DMB();    // STCP -> LOW
25
    GPIO_ResetBits(HC595_PORT,HC595_OE); __DMB();      // OE -> LOW (aktiv)
26
}

von STMler (Gast)


Lesenswert?

Christian J. schrieb:
> Hallo,
>
> ich möchte auf einem F103 Blue Pill Board einen HCT595 mit Bits
> befüllen. Mangels freier SPI schiebe ich die Bits einzeln heraus.


Du kannst auch mehrere ICs an den SPI hängen.

von W.S. (Gast)


Lesenswert?

Christian J. schrieb:
> /* Schreibt ein 8 Bit Wort in den 74HCT595  */
> void Set74HCT595(uint8_t data)
> {
>     for (uint8_t i = 0; i < 8; i++) {
>         // Datenbit setzen
>         GPIO_WriteBit(HC595_PORT,HC595_DS,(data & 0x80)); __DMB();    //
> Bit anlegen
>         GPIO_SetBits(HC595_PORT,HC595_SHCP);  __DMB();                //
> SHCP -> High
>         GPIO_ResetBits(HC595_PORT,HC595_SHCP); __DMB();               //
> SHCP -> LOW
>         data = data << 1;

Mir kommen da Bedenken bzgl. Beleidigung der Ostfriesen, wenn ich bei 
obigem an die Frage denke, wie viele Ostfriesen man zum Reinschrauben 
einer Glühbirne braucht.

Geht's vielleicht noch etwas komplizierter?

W.S.

von Nop (Gast)


Lesenswert?

Christian J. schrieb:

> for (uint8_t i = 0; i < 8; i++)

Genereller Tip für AVR-Umsteiger: nimm auf ARM Cortex-M keine 
8bit-Variablen, wenn dies nicht unumgänglich ist (Strings, große 
Structs, Grafiken oder so). Es ist deutlich langsamer, als wenn man mit 
32bit arbeitet. Insbesondere Schleifenvariablen sollten immer mit 32bit 
realisiert werden.

Entweder, Du nimmst gleich uint32_t, oder wenn Du es richtig sauber 
machen willst, dann lautet der korrekte Datentyp "uint_fast8_t".

von W.S. (Gast)


Lesenswert?

Nop schrieb:
> Entweder, Du nimmst gleich uint32_t, oder wenn Du es richtig sauber
> machen willst, dann lautet der korrekte Datentyp "uint_fast8_t".

Richtig sauber? Quatsch mit Soße sowas.
Für normale lokale Variablen einfach int oder long und gut isses. Die 
angebrannte Diskussion über uintXYZ_t hatten wir schon - und sie war 
komplett albern.

Nein, das Codebeispiel von Christian sieht einfach grauselig aus. Das 
ist der Punkt.
1
long m;
2
3
 m = 0x80;
4
 while (m)
5
 { clockbit_low();
6
   databit(data & m);
7
   clockbit_high();
8
   m = m>>1;
9
 }

und die Funktionen
void clockbit_low(void)
und
void clockbit_high(void)
sowie
void databit(bool b)
kann sich der TO selber schreiben.

Das läuft im Prinzip auf ein __inline void ....
mit GPIOx_BSRR = 1<<bitnummer oder 1<<(bitnummer+16) heraus.

W.S.

von Marcus H. (Firma: www.harerod.de) (lungfish) Benutzerseite


Lesenswert?

Christian J. schrieb:
> Könnte mir da jemand eine sichere Lösung nennen, die aber optimale
> Geschwindigkeit bietet? (72 Mhz)

Beim STM kannst Du per DMA Daten zwischen SRAM und Peripherie 
verschieben.
Du kannst also mit ein paar GPIO's und DMA eine flotte SPI emulieren. 
Für die DMA brauchst Du noch einen Trigger, aber irgendein Timer wird 
sich noch finden, oder?

Das ist typisch für einen ARM + IP-Cores ->
Hohe mittlere Rechenleistung in der CPU, aber richtig schnell wird das 
Embedded Dingens erst mit DMA.

Und wenn der -o3 GCC zu langsam ist, bleibt immer noch die Möglichkeit 
Assembler zu schreiben. ;)

Grad nochmal geschaut - einem NXP HCT595 @ 3V3 bei Zimmertemperatur 
würde ich nicht mehr als 10..15MHz zumuten. ;)

von Nop (Gast)


Lesenswert?

W.S. schrieb:

> Richtig sauber? Quatsch mit Soße sowas.

Es stand im zitierten Text deutlich "uint8_t"; anders als Dir sind dem 
Mitposter also die Errungenschaften von C99 vertraut. Nur falsch benutzt 
für ARM.

> Für normale lokale Variablen einfach int oder long und gut isses.

Und dann rumheulen, daß die Datentypen überall ne andere Breite haben. 
Nimm einfach mal zur Kenntnis, daß der Rest der C-Welt die portablen 
Datentypen seit 18 Jahren verstanden hat UND in der Lage ist, damit den 
eigenen Code leserlicher zu gestalten.

von W.S. (Gast)


Lesenswert?

Nop schrieb:
> Nimm einfach mal zur Kenntnis, daß

Nimm du einfach mal zur Kenntnis, daß wir da ganz offensichtlich völlig 
konträre Positionen vertreten UND daß die Welt weitaus größer ist als 
nur die C99-Welt UND daß ich keine Lust mehr habe, mit all den uint123_t 
Leuten darüber zu diskutieren. Sowas wie "uint_fast8_t" ist die Krätze 
und auf keinem ARM vonnöten.

Also laß es, wir sollten uns besser um ganz andere Probleme kümmern.

W.S.

von Christian J. (Gast)


Lesenswert?

Nop schrieb:
> Es ist deutlich langsamer, als wenn man mit
> 32bit arbeitet. Insbesondere Schleifenvariablen sollten immer mit 32bit
> realisiert werden.

Stimmt das wirklich? Denn eine 8 Bit Variable braucht auch nur 8 Bit im 
Speicher und habe mal gelernt, dass man immer den kleinsten Datentyp 
verwenden soll der geht. Und bei den stdtype.h Typen weiss ich nunmal 
wie lang die sind, bei einem int nicht. Ok, der hat hier 32 Bit aber 
woanders eben nicht.

Wie managed der Arm denn Variablen, die kleiner als die Busbreite sind?

PS: Danke für den TIP; dass eine SPI auch mehrere Geräte ueber den CS 
steuern kann. Völlig übersehen :-)

@W.S.: Ich magh Deine schnoddrige Art nicht aber das lass ich mal aussen 
vor. Ich benutze soweit es geht die mitgelieferten Funktionen, wobei es 
völlig unerheblich ist wieviel Quelltext die produzieren. Kompiliert und 
Optimiert ist die Schleife winzig klein, da der Compiler die Subroutinen 
alle wegoptimiert und nur noch registerzugriffe einfügt. Totoptimiert 
lässt man sie von 8 runter auf 0 laufen und spart noch ein Compare ein, 
hat nur noch einen Zero Flag Test.

DMA ist mir zwar gut vertraut aber hier eher nicht angebracht. Es werden 
5 kaskadierte HC595, die viele LEDs dran haben gefüttert und das nur 
recht selten. Ist eine Bargraph Anzeige für Spannungen. Und um die  5x8 
Bit raus zu trommeln habe ich die Zeit. DMA ist auch recht viel 
nachdenken, mache ich gerne bei AD Wandlern, die mir fortlaufend Arrays 
vollschreiben, die ich dann gleich auslesen kann.

Mehr als 8 Mhz habe ich auf Lochrasterplatine noch nicht gescheit zum 
Laufen gekriegt. Ab 12 Mhz verhaspeln sich die Bits, zb bei einem 
Display.

von (prx) A. K. (prx)


Lesenswert?

Christian J. schrieb:
> Wie managed der Arm denn Variablen, die kleiner als die Busbreite sind?

Nicht nur bei ARM, auch bei x86 sind Typen kleiner als "int" im 
Nachteil.
1
extern int g1(unsigned char);
2
extern int g2(unsigned);
3
4
void f1(unsigned char a, unsigned char b, unsigned char c)
5
{
6
        if (a+b > c)
7
                g1(a+b);
8
}
9
10
void f2(unsigned a, unsigned b, unsigned c)
11
{
12
        if (a+b > c)
13
                g2(a+b);
14
}
ARM, gcc 4.9.2, -O2:
1
f1:     add     r0, r0, r1
2
        cmp     r0, r2
3
        bgt     .L4
4
        bx      lr
5
.L4:    uxtb    r0, r0    ;<--- truncation nötig
6
        b       g1
7
8
f2:     add     r0, r0, r1
9
        cmp     r0, r2
10
        bhi     .L7
11
        bx      lr
12
.L7:    b       g2
x86-64, gcc 4.7.2, -O2, besonders krass, da der Compiler zwar einen 
korrekt auf 32-Bits erweiterten Wert an g1() übergibt, aber bei den 
eigenen Parametern von 8-Bit Werten ausgeht und folglich erst einmal 
erweitert:
1
f1:     movzbl  %dil, %ecx
2
        movzbl  %sil, %eax
3
        movzbl  %dl, %edx
4
        addl    %ecx, %eax
5
        cmpl    %edx, %eax
6
        jg      .L4
7
        rep
8
        ret
9
.L4:    addl    %esi, %edi
10
        movzbl  %dil, %edi
11
        jmp     g1
12
13
f2:     addl    %esi, %edi
14
        cmpl    %edx, %edi
15
        ja      .L7
16
        rep
17
        ret
18
.L7:    jmp     g2

: Bearbeitet durch User
von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

Um Bitbanging direkt zu machen, bieten sich die BRR und BSRR Register 
an:
1
// from VL Discovery
2
#define BLUE_LED_PIN  GPIO_Pin_8
3
#define GREEN_LED_PIN  GPIO_Pin_9
4
#define LED_GPIO_PORT GPIOC
5
6
// light the blue LED
7
LED_GPIO_PORT->BSRR = BLUE_LED_PIN;
8
// clear the blue LED
9
LED_GPIO_PORT->BRR = BLUE_LED_PIN;
10
// light the blue and green LED
11
LED_GPIO_PORT->BSRR = BLUE_LED_PIN | GREEN_LED_PIN;
12
// and switch them off
13
LED_GPIO_PORT->BRR = BLUE_LED_PIN | GREEN_LED_PIN;
Pins sollten bereits als Ausgang definiert sein.
Siehe RM0008, Seite 168 und 169.

: Bearbeitet durch User
von Peter D. (peda)


Lesenswert?

Nop schrieb:
> Es ist deutlich langsamer, als wenn man mit
> 32bit arbeitet. Insbesondere Schleifenvariablen sollten immer mit 32bit
> realisiert werden.

Was ist für Dich deutlich, 1%?
Ich würde die Compilerbauer nicht für Idioten halten, die werden als 
Loopzähler natürlich ein ganzes Register nehmen, d.h. keinerlei 
merkbaren Geschwindigkeitseinbuße.
Und selbst wenn in einer kleinen Loop von 20 Befehlen noch ein 
überzähliges AND 0x000000FF drin sein sollte, merkt das niemand.

Sowas zählt für mich unter Mikrooptimierung, da ist mir die Portabilität 
deutlich mehr wert.
Ich bin jetzt auch dabei, in printf/scanf die portablen Formatbezeichner 
zu verwenden.

von Carl D. (jcw2)


Lesenswert?

Peter D. schrieb:
> Nop schrieb:
>> Es ist deutlich langsamer, als wenn man mit
>> 32bit arbeitet. Insbesondere Schleifenvariablen sollten immer mit 32bit
>> realisiert werden.
.
> Was ist für Dich deutlich, 1%?
> Ich würde die Compilerbauer nicht für Idioten halten, die werden als
> Loopzähler natürlich ein ganzes Register nehmen, d.h. keinerlei
> merkbaren Geschwindigkeitseinbuße.
> Und selbst wenn in einer kleinen Loop von 20 Befehlen noch ein
> überzähliges AND 0x000000FF drin sein sollte, merkt das niemand.
.
> Sowas zählt für mich unter Mikrooptimierung, da ist mir die Portabilität
> deutlich mehr wert.
> Ich bin jetzt auch dabei, in printf/scanf die portablen Formatbezeichner
> zu verwenden.

Genau, weil sie eh "ein ganzes Register nehmen", macht es keinen Sinn da 
ein paar Bits sparen zu wollen.

Aber ich werde mal den GCC fragen, wie er das sieht.

: Bearbeitet durch User
von W.S. (Gast)


Lesenswert?

Christian J. schrieb:
> @W.S.: Ich magh Deine schnoddrige Art nicht aber das lass ich mal aussen
> vor. Ich benutze soweit es geht die mitgelieferten Funktionen, wobei es
> völlig unerheblich ist wieviel Quelltext die produzieren.

Nun, da mußt du halt mit zurechtkommen, schließlich muß ich ja auch mit 
mir selbst zurechtkommen - ABER: Wieso Nop auf die Idee gekommen ist, 
ausgerechnet so ein Ungetüm wie "uint_fast8_t" vorzuschlagen, ist mir 
ein Rätsel. Er hat doch ansonsten recht vernünftige Ansichten.

Guck dir mal meinen aus dem Handgelenk geschlenkerten Vorschlag an, da 
wirst du sehen, daß es auch ganz ohne Schleifenzähler geht und damit die 
ganze Diskussion um "uint_fast8_t" und Konsorten per se völlig 
überflüssig ist. Sieht denn sowas Simples kein anderer unter den 
Disputanten hier?

Ich sag's mal so: Ein guter Programmierer zeichnet sich nicht dadurch 
aus, daß er sämtliche "uint_fast8_t" und Konsorten auswendig kennt, 
sondern daß er gute Herangehensweisen und gute Algorithmen draufhat.

Es ist wichtig, den Kern einer Sache schnell und gründlich zu 
durchblicken. Das ist nicht nur Erfahrungssache, sondern braucht auch 
Kenntnisse von Dingen, die scheinbar außerhalb des Gesichtskreises 
liegen. Olle Napoleon soll wohl mal Bewerber auf einen Offiziers-Posten 
gefragt haben "Hat er Fortune?" - nee, nicht ob er bislang Schwein 
gehabt hat.., eher ob er Durchblick und ein Händchen für's 
Richtig-Machen und damit für's Siegen hat. Er brauchte eher nen 
Strategen und keinen Beckmesser. Das ist der Punkt.

Angehenden C-Programmierern würde ich deswegen anraten, parallel dazu 
ein Mathe-Studium zu belegen, Elektrotechnikern und Informatikern ein 
paralleles Physikstudium und Chemikern ein Stück Maschinenbau. Kurzum, 
einen größeren eigenen Horizont zu erwerben.

W.S.

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.