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
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);
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.
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....
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.
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
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.
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
080014BCpush{r4,r5,r6,r7,lr}
3
080014BEsubsp,#20
4
080014C0addr7,sp,#0
5
080014C2strdr0,r1,[r7]
6
(110)for(uint8_ti=0;i<NSHIFT;i++){
7
080014C6movsr3,#0
8
080014C8strbr3,[r7,#15]
9
080014CAb.n0x8001510<Set74HCT595+84>
10
0800150Aldrbr3,[r7,#15]
11
0800150Caddsr3,#1
12
0800150Estrbr3,[r7,#15]
13
08001510ldrbr3,[r7,#15]
14
08001512cmpr3,#39;0x27
15
08001514bls.n0x80014cc<Set74HCT595+16>
16
(111)// Datenbit setzen
17
(112)GPIO_WriteBit(HC595_CTRL,HC595_DS,(data>>63));__DMB();// Bit anlegen