Forum: Mikrocontroller und Digitale Elektronik 40 Bit Schieberegister verwalten


von Christian J. (Gast)



Lesenswert?

Hallo,

allein werkelt man ja manchmal auch mit einem Brett vor dem Kopf und 
erzeugt verschwurbelten Code.

Hardware:
- Arm Cortex M3 BLue Pill Board mit einem HC595 Schiebregister 5-fach in 
Serie
- An jedem HC595 PIN klebt eine LED, insgesamt 38 Stueck.
- Jede LED gehört zu einer anderen Anzeige auf einer Platine, zb  3 
Bargraphen a je 10 LEDs
- Aber auch einzelne LEDs die etwas anzeigen.
- Alle LEDs werden über PWM abgedunkelt indem der OE Pin an einen Timer 
Compare gekoppelt wird.

Um die LEDs handlich zu verwalten wollte ich zuerst ein uint64_t 
erzeugen, also ein unsigned long long und den bitweise in die Shift 
Register eintrommeln. Sämtliche High Level Routinen müssen ja 
zwangsweise auf die eine Low Level Routine zugreifen, die die LEDs 
aktualisiert. Manche blinken, andere nicht usw. Wird später alles in 
einer ISR erledigt.
Das Füttern der Anzeigen geschieht dann über Schattenregister aus demn 
Hauptprogramm, die von der ISR ausgelesen werden und auf Veränderung zum 
alten Wert geprüft werden.

Auf die LL Routine drauf gesetzt werden dann einzelne Routinen wie

Set_Bar_1(data, mode, ...)
Set_Bar_2(data, mode,...)
Set_Bar_3(....)
Show_Time(.....)

Ist so eine 64 Bit Variable gut zu handeln? Nein!

Zur Verfügung habe ich noch das Bitbanding, nur fehlt mir derzeit die 
Idee was das nützen könnte. 2 x 32bit Variablen würden da ausreichen, um 
alle Bits zu erreichen.

Hat da jemand ne Idee, wie man so 38 LEDS, die in Gruppen organisiert 
sind und von denen jede ein einziges Bit in einer langen Kette ist gut 
handhabt?

Gruss,
Christian

: Verschoben durch User
von Markus M. (adrock)


Lesenswert?

Naja, da die Schieberegister 8 Bit sind, würde ich vom Gefühl her die 
Bits als ein Array aus Bytes (uint8_t) verwalten.

uint8_t LedData[5];

Dann würde ich mir Makros definieren, um einzelne Bits zu 
setzen/löschen:

#define SET_BIT(d, x) d[(x)/8] |= 1<<((x)&0x07)
#define CLR_BIT(d, x) d[(x)/8] &= ~(1<<((x)&0x07))

SET_BIT(LedData, 12);
CLR_BIT(LedData, 21);

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


Lesenswert?

Markus M. schrieb:
> vom Gefühl her die Bits als ein Array aus Bytes (uint8_t) verwalten.

Machen wir genauso. Und wenn der uC mit 16 oder 32 bit besser klar 
kommt, .... na dann ändere die Makros entsprechend. Die Aufrufe können 
trotzdem so bleiben.

von PittyJ (Gast)


Lesenswert?

>> Ist so eine 64 Bit Variable gut zu handeln? Nein!

Das verstehe ich nicht?
Ob du mit
#define SET_BIT(d, x) d[(x)/8] |= 1<<((x)&0x07)

Oder die 64 Bit Variable nimmst, die der Compiler selber auseinander 
pflückt, das bleibt doch letzendlich gleich.


Warum ist die 64bit Variable so schwer?

von Markus M. (adrock)


Lesenswert?

Ich sähe den Vorteil darin, das man ohne große Änderungen im Code 
beliebige Anazhlen von LEDs haben kann, ohne sich darum zu kümmern, ob 
jetzt die Bits alle in ein Wort (16/32/64 Bit) passen.

von S. K. (hauspapa)


Lesenswert?

Da Du 5 Schieberegister hast bist Du auf 8 Bit festgelegt. Da braucht 
man garnicht nachsehen ob die SPI auch 16 bit Modes kennen würde.

Mit Makros hab ichs eher nicht so. Ich löse sowas in gutem Anfänger Stil 
mit einzelnen Variablen für jeden Leuchtmelder.

An einer Stelle schwinge dann die grosse if/else Keule um daraus eine 
Variable zu machen die ich in die SPI Register schreiben kann. Anzeigen 
schreibe ich höchstens 10x pro Sekunde, da kommt es mir auf ein paar 
Rechenzyklen und ein paar Bytes für Variablen nicht an.
Nicht super effizient aber alles was mit Anzeige zu tun hat am selben 
Ort versammelt.

viel Erfolg
hauspapa

von Christian J. (Gast)


Lesenswert?

Hallo,

danke für den Tip. Mit Makros habe ich es nicht ganz so, muss mir die 
erstmal verinnerlichen. Wird kein SPI, sondern Bit Getrommel. Das spielt 
mit fast 15 Mhz am Logic Tester, viel zu schnell für Lochraster mit 
Draht. Dieser F103 ist megaschnell, cooles Teilchen.

Bringt eigentlich ein packed array bei einer 32 Bit Maschine etwas, wenn 
man nur uint8_t hat?

Mit dem Bitbanding kann man natürlich auch einzelne Bits als LED 
definieren, dafür habe ich sogar Routinen abgeschrieben, die das in 
einem Rutsch auslesen.
1
/* Bitbanding: Setzt ein Bit einer Variablen */
2
void set_bit(uint32_t* addr, const int bit, _Bool val)
3
{
4
     *((volatile uint32_t*)(32 * (((uint32_t)addr) - 0x20000000) + 0x22000000 + 4 * bit)) = val;
5
}
6
7
/* Bitbanding Teste ein Bit. Rückgabe: Bit */
8
uint8_t test_bit(uint32_t* addr, const int bit)
9
{
10
     return *((volatile uint32_t*)(32 * (((uint32_t)addr) - 0x20000000) + 0x22000000 + 4 * bit));
11
}

von Christian J. (Gast)


Lesenswert?

Wäre das für die 32 Bit CPU folgerichtig so?

#define SET_BIT(d, x) d[(x)/32] |= 1<<((x)&0x1F)
#define CLR_BIT(d, x) d[(x)/32] &= ~(1<<((x)&0x1F))

mit uint32_t LED[2]

?

von Markus M. (adrock)


Lesenswert?

Ja, wenn Du uint32_t verwenden willst ist das so korrekt.

von THOR (Gast)


Lesenswert?

Das heisst übrigens Bit-Banging.

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

THOR schrieb:
> Das heisst übrigens Bit-Banging.

 Nein.

von STMApprentice (Gast)


Lesenswert?

Christian J. schrieb:
> - An jedem HC595 PIN klebt eine LED, insgesamt 38 Stueck.

Christian J. schrieb:
> Wäre das für die 32 Bit CPU folgerichtig so?

Mit 5 Ports a 8 Bit wird das schwierig in einer 32 Bit Variablen....

von icke (Gast)


Lesenswert?

Marc V. schrieb:
> THOR schrieb:
>> Das heisst übrigens Bit-Banging.
>
>  Nein.

Doch.
https://de.wikipedia.org/wiki/Bit-Banging

von STMApprentice (Gast)


Lesenswert?

Bei mir sieht das so aus

uint8_t  bit00 = 0;  /* jeweils 0 oder 1  */
uint8_t  bit01 = 0;
uint8_t  bit02 = 0;

bis

uint8_t  bit38 = 0;
uint8_t  bit39 = 0;

Diese kommen durch Schiebeoperationen in ein Array von Bytes[0...4]
welches ich dann mit einer einfachen Write_SPI(&Bytes, 5) an die
595 schicken kann.

Vorteil: ich sehe den Zustand jedes Bits und ich kann ihn ohne
Verklausulierung und Makroisierung verändern.

von Peter D. (peda)


Lesenswert?

Marc V. schrieb:
> THOR schrieb:
>> Das heisst übrigens Bit-Banging.
>
>  Nein.

Richtig.
Das eine ist eine Adressierungsart, um atomar auf einzelne Bits eines 
Ports zugreifen zu können.
Das andere ist die Nachbildung eines seriellen Interfaces in Software.

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

icke schrieb:
> Doch.
> https://de.wikipedia.org/wiki/Bit-Banging

 Nein.
https://developer.mbed.org/cookbook/bit-banding

Christian J. schrieb:
> Mit dem Bitbanding kann man natürlich auch einzelne Bits als LED
> definieren, dafür habe ich sogar Routinen abgeschrieben, die das in
> einem Rutsch auslesen.

 Und hier etwas ( auch abgeschrieben ) für die Adresse:
1
volatile uint32_t* getBitBandAddress( volatile void* address, int bit )
2
{
3
    uint32_t addr = (uint32_t)address ;
4
    uint32_t word_band_base = addr & 0xf0000000 ;
5
    uint32_t bit_band_base = word_band_base | 0x02000000 ;
6
    uint32_t offset = addr - word_band_base ;
7
8
    return  (volatile uint32_t*)(bit_band_base + (offset * 32) + (bit * 4)) ;
9
}

 P.S.
1
// Example: STM32 USART1 Tx Int Enable/Disable
2
uint32_t* tx_intr_enable_bit_addr = getBitBandAddress( (&(USART1->CR1)), 7 ) ;
3
4
*tx_intr_enable_bit_addr = 0 ; // Disable Tx
5
*tx_intr_enable_bit_addr = 1 ; // Enable Tx

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


Lesenswert?

Christian J. schrieb:
> Das Füttern der Anzeigen geschieht dann über Schattenregister aus demn
> Hauptprogramm, die von der ISR ausgelesen werden und auf Veränderung zum
> alten Wert geprüft werden.

Also ganz verstehe ich deine Probleme nicht. Deine TTL-Schieberegister 
sind offenbar hintereinander angeordnet, so daß du alle Daten bloß 
seriell hineintakten mußt. Ob du da nun for i:= 10 to 40 do... oder 5x 
for i:= 1 to 8 do... benutzt, ist eigentlich egal.

Allerdings sind Datenbreiten beim Cortex vorzugsweise 32 bittig. Aber in 
einer ISR würde ich sowas nicht veranstalten. Setze lieber bei jedem 
Ändern deiner "Schattenregister" eine boolean Variable, die du in der 
Grundschleife in main auswertest und dann rücksetzt.

W.S.

von Christian J. (Gast)


Lesenswert?

THOR schrieb:
> Das heisst übrigens Bit-Banging.

Das heisst "Bit-Banding", eine Besonderheit des Cortex auf jedes Bit im 
Adressraum des RAM atomar als Bit zugreifen zu können.

Und ja, meine Art die Nummern zu schieben heisst Bit-Banging, also 
ähnlich wie Gang Banging, nur halt mit Bits >:-O

von Jim M. (turboj)


Lesenswert?

Man könnte auch mit Bitfeldern arbeiten:
1
struct {
2
  uint8_t bar1:5;
3
  uint8_t bar2:5;
4
  uint8_t led1:1;
5
  uint8_t led2:1;
6
} bitField;

Allerdings sind die nicht portabel, und der Compiler erzeugt 
read-modify-write Code beim Zugriff - was beim Schreiben im Interrupt 
Betrieb stören kann.

von Christian J. (Gast)


Lesenswert?

Jim M. schrieb:
> Man könnte auch mit Bitfeldern arbeiten:
> struct {
>   uint8_t bar1:5;
>   uint8_t bar2:5;
>   uint8_t led1:1;
>   uint8_t led2:1;
> } bitField;
>
> Allerdings sind die nicht portabel, und der Compiler erzeugt
> read-modify-write Code beim Zugriff - was beim Schreiben im Interrupt
> Betrieb stören kann.

Die Idee ist auch gut! Gar nicht mehr dran gedacht.

Danke erstmal!

Ich setze das morgen mal alles um, falls kein Flugwetter ist. Werde 
wahrscheinlich mit dem Array arbeiten und diesen dann per Macro 
ansprechen.
Ob eine RMW Operation stattfindet oder nicht ist egal. Grundsätzlich 
trenne ich Routinen in einer ISR immer vollständig so ab, dass ich mit 
Schattenregistern und f_modify Flags arbeite. Zugriffe auf die gleiche 
Variable gibt es gar nicht. Die ISR schaut nach ob es Differenzen gibt 
und nur wenn es eine gibt wird auch die Hardware bedient. Die Störungen 
von einer Lochraster im Radio sind nicht ohne, eine 10 Mhz SPI mit 5cm 
langen Drähten ist deutlichst zu hören.

von Markus M. (adrock)


Lesenswert?

Es wäre mal interessant wieviel Code erzeugt wird für die Variante mit 
dem Makro und dem Bitbanding, also ob es wirlich optimaler ist.

von Christian J. (Gast)


Lesenswert?

Markus M. schrieb:
> Es wäre mal interessant wieviel Code erzeugt wird für die Variante
> mit
> dem Makro und dem Bitbanding, also ob es wirlich optimaler ist.

Bin fertig, habe einfach mit der uint64_t weiter gemacht und darauf ganz 
normal die C Operanden angewendet. Ob das "optimaler" ist weiss ich 
nicht, es geht jedenfalls rasend schnell in wenigen Mikrosekunden laut 
Debug Timer die ganze Kette voll zu schreiben und auf die paar Clocks 
kommt es mir nicht an, der Code ist jedenfalls winzig.
1
109)  {
2
080014BC  push  {r4, r5, r6, r7, lr}
3
080014BE  sub  sp, #20
4
080014C0  add  r7, sp, #0
5
080014C2  strd  r0, r1, [r7]
6
(110)      for (uint8_t i = 0; i < NSHIFT; i++) {
7
080014C6  movs  r3, #0
8
080014C8  strb  r3, [r7, #15]
9
080014CA  b.n  0x8001510 <Set74HCT595+84>
10
0800150A  ldrb  r3, [r7, #15]
11
0800150C  adds  r3, #1
12
0800150E  strb  r3, [r7, #15]
13
08001510  ldrb  r3, [r7, #15]
14
08001512  cmp  r3, #39  ; 0x27
15
08001514  bls.n  0x80014cc <Set74HCT595+16>
16
(111)          // Datenbit setzen
17
(112)          GPIO_WriteBit(HC595_CTRL,HC595_DS, (data >> 63)); __DMB();    // Bit anlegen
18
080014CC  ldrd  r3, r4, [r7]
19
080014D0  lsrs  r5, r4, #31
20
080014D2  movs  r6, #0
21
080014D4  uxtb  r3, r5
22
080014D6  mov  r2, r3
23
080014D8  movs  r1, #1
24
080014DA  ldr  r0, [pc, #104]  ; (0x8001544 <Set74HCT595+136>)
25
080014DC  bl  0x8000aec <GPIO_WriteBit>
26
(113)          GPIO_SetBits(HC595_CTRL,HC595_SHCP);  __DMB();                // SHCP -> High
27
080014E4  movs  r1, #8
28
080014E6  ldr  r0, [pc, #92]  ; (0x8001544 <Set74HCT595+136>)
29
080014E8  bl  0x8000ab4 <GPIO_SetBits>
30
(114)          GPIO_ResetBits(HC595_CTRL,HC595_SHCP); __DMB();               // SHCP -> LOW
31
080014F0  movs  r1, #8
32
080014F2  ldr  r0, [pc, #80]  ; (0x8001544 <Set74HCT595+136>)
33
080014F4  bl  0x8000ad0 <GPIO_ResetBits>
34
(115)          data = data << 1;
35
080014FC  ldrd  r3, r4, [r7]
36
08001500  adds  r3, r3, r3
37
08001502  adc.w  r4, r4, r4
38
08001506  strd  r3, r4, [r7]

von Christian J. (Gast)


Lesenswert?


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.