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
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
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.
>> 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?
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.
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
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 | }
|
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] ?
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....
Marc V. schrieb: > THOR schrieb: >> Das heisst übrigens Bit-Banging. > > Nein. Doch. https://de.wikipedia.org/wiki/Bit-Banging
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.
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.
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
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.
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
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.
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.
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.
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] |
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.