Hi, ich möchte ein Bit in einem Byte ansprechen. Nun liest man immer wieder das: uint8_t bit=3; uint8_t byte=1; byte<<=bit; in eine Schleife umgesetzt wird. Gibt es eine bessere Möglichkeit? Ich finde eine Tabelle eigentlich übertrieben - ist es wirklich die einzige Möglichkeit? Hannes
Hannes schrieb: > ich möchte ein Bit in einem Byte ansprechen. Nun liest man immer wieder > das: > uint8_t bit=3; > uint8_t byte=1; > > byte<<=bit; Wo liest man denn so etwas in diesem Zusammenhang? Mit dieser Operation wird kein Bit angesprochen, sondern der Inhalt der Variable Byte um 3 Positionen nach links geschoben. Numerisch entspricht das einer Multiplikation von Byte mit 8. Zum Thema Bitmanipulation: http://www.mikrocontroller.net/articles/Bitmanipulation
1 | // Bit setzen:
|
2 | byte |= (1 << bit); |
3 | // Bit löschen:
|
4 | byte &= ~(1 << bit); |
Und das packt man dann am besten in ein Makro BIT_SET() oder so ähnlich.
Max G. schrieb: > Und das packt man dann am besten in ein Makro BIT_SET() oder so ähnlich. Warum nur? BIT_SET wäre für mich eine geeignete /Abfrage/: if (BIT_SET(x,y)) {} Wenn schon, dann sollte das Makro SET_BIT() heißen... Und weil das Makro auch eine ganze Zeile Code braucht und auch nicht schneller ist, kommt es bei mir nicht in ein Makro. Dann sieht jeder, auch ohne die Makrodefinition zu suchen, sofort was da gemacht wird.
Hi, danke für die Rückmeldung. Ich habe einen Grafikspeicher: uint8_t RAM[8]; nun möchte ich ein Bit darin setzen. void grafik_setze_pixel (uint8_t x, uint8_t y) { RAM[y&0x7]|=0x01<<(x&0x07); } Nun ist das ein variabler shift. Er wird doch jedes Mal in eine Schleife umgesetzt....oder? Hannes
Du hast ein Arry aus 8 Bytes und jedes Byte hat 8 Bit. Was soll die Funktion genau tun, was ist die GENAUE! Bedeutung von x und y in deiner Funktion?
Hannes schrieb: > ich möchte ein Bit in einem Byte ansprechen. Nun liest man immer wieder > das: > uint8_t bit=3; > uint8_t byte=1; > > byte<<=bit; > > in eine Schleife umgesetzt wird. Das gilt normalerweise nur für Prozessoren ohne Barrel-Shifter, und auch dort nur, wenn das Ergebnis nicht bereits zur Compilezeit berechnet werden kann. Lothar Miller schrieb: > Max G. schrieb: >> Und das packt man dann am besten in ein Makro BIT_SET() oder so ähnlich. > Warum nur? > BIT_SET wäre für mich eine geeignete /Abfrage/: > if (BIT_SET(x,y)) {} > Wenn schon, dann sollte das Makro SET_BIT() heißen... Ansichtssache. Ich arbeite gern mit einheitlichen Präfixen, daher bevorzuge ich es, wenn am Anfang des Namens steht, auf was sich die Funktion/das Marko bezieht (in dem Fall also ein Bit) und danach erst die Aktion kommt, die damit durchgeführt wird. Deshalb ist mir ein Name wie SET_BIT() eher ein Dorn im Auge. Die Abfrage sollte meines Erachtens eher BIT_IS_SET() heißt.
...x und y sind die Koordinaten des Pixels. Jedes Bit steht für einen Pixel auf einem 8x8 Pixel großen Display. Hannes
>>Das gilt normalerweise nur für Prozessoren ohne Barrel-Shifter, und auch >>dort nur, wenn das Ergebnis nicht bereits zur Compilezeit berechnet >>werden kann. Ich nutze einen Atmega168. Das Ergebnis steht nicht fest, ansonsten wäre es eine gute Lösung. Die Pixel werden aber erst zur Laufzeit berechnet. So wie ich das sehe, geht es nur mit einer Tabelle... In meinem Original sind es zudem uint16_t Variablen... Hannes
Tabelle im Progmem: uint8_t Tabelle[8]={0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};
Hannes schrieb: >>>Das gilt normalerweise nur für Prozessoren ohne Barrel-Shifter, und auch >>>dort nur, wenn das Ergebnis nicht bereits zur Compilezeit berechnet >>>werden kann. > > Ich nutze einen Atmega168. Also keinen Barrelshifter. > So wie ich das sehe, geht es nur mit einer Tabelle... Wäre vernünftig.
Hannes schrieb: > In meinem Original sind es zudem uint16_t Variablen... Wie groß ist denn Dein Display?
Hannes schrieb: > ...x und y sind die Koordinaten des Pixels. Jedes Bit steht für einen > Pixel auf einem 8x8 Pixel großen Display. Mußt du die Pixel denn beliebig kreuz und quer setzen können? Wenn du da z.B. eh immer in einer Schleife durchgehst, kann man sich ja Zwischenergebnisse des Shifts merken. Wenn das nicht geht, wirst du wohl die Tabelle nutzen müssen. > In meinem Original sind es zudem uint16_t Variablen... Hast du dann nur vier davon, oder wie sind die angeordnet, wenn es nur 8x8 Pixel mit je 1 Bit sind?
...au man. der 168 ist doch ein 8bitter, oder? Momentan sieht mein Ram etwa so aus: uint16_t RAM[64]; Ich habe also eine Pixelfläche von 16x64 Pixeln und möchte dort mit zwei Funktionen möglichst effektiv, aber dennoch in c Pixel setzen und Rücksetzen... Ich denke darüber nach, dies auf uint8_t RAM[128] oder RAM[2][64] umzustellen und mit einer 8-wertigen Tabelle wie oben gezeigt zu arbeiten... Die 16 Bit Werte werden doch auch wieder zusammengebastelt, oder? Hannes
Hannes schrieb: > Tabelle im Progmem: > > uint8_t Tabelle[8]={0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80}; Schreib dir mal die Binäre Form der Zahlen in deinem Array auf. Dann wirst du sehen das in jeden Wert der Tabelle nur 1 Bit gesetzt ist und dieses wandern noch von rechts nach links (wenn das LSB ganz rechts steht). Also kannst du die Tabelle durch eine Variable ersetzen die am Start deiner Schleife den Wert 1 hat und im jeden Durchlauf um 1 nach links geschoben wird.
>>Wie groß ist denn Dein Display? 16x64 Pixel, hätte ich gleich sagen sollen... >>Mußt du die Pixel denn beliebig kreuz und quer setzen können? frei wählbar, es wird alles mögliche dargestellt. >>Hast du dann nur vier davon mein Original ist größer, wollte es vereinfachen, um es besser verständlich zu machen, war wohl ein Fehler :-) Hannes
Hannes schrieb: > ...x und y sind die Koordinaten des Pixels. Jedes Bit steht für einen > Pixel auf einem 8x8 Pixel großen Display. Die Frage ist, ob Du wirklich die Pixel immer völlig durcheinander zugreifen mußt, oder das ein Teil einer Schleife über alle Pixel ist. Im letzteren Fall kann man einfach auch eine Maske in jedem Durchlauf um 1 weiter schieben. Oder umgekehrt, man schiebt das Pixelbyte jeweils einmal, bis man alle 8 Pixel zusammen hat. Quasi wie bei einem Software-SPI. Peter
>>Also kannst du die Tabelle durch eine Variable ersetzen
das macht der Compiler doch schon für mich, das möchte ich ja vermeiden.
Wenn ich 64 Pixel in einer Linie setze, dann ist er nur noch am dhiften,
für jeden Pixel aufs neue...
Hannes
M. K. schrieb: > Also kannst du die Tabelle durch eine Variable ersetzen die am Start > deiner Schleife den Wert 1 hat und im jeden Durchlauf um 1 nach links > geschoben wird. Genau darum gehts ja: die Schleife durch eine Tabelle mit Indizierung zu ersetzen.
>>einfach auch eine Maske
Ich schreibe konverteierte Serifen-Schriftarten aufs Panel, zeichne
Kreise und Linien mit dem Bresenham...
Hannes
> Ich finde eine Tabelle eigentlich übertrieben
Wart ab, bis du drauf kommst, dass du dir massiv Geschwindigkeit holen
kannst, wenn du ausnutzt, dass in vielen Operationen nebeneinander
liegende Pixel angesprochen werden und dieser Fall gar nicht so selten
ist. Bei jeder waagrechten Linie bzw. bei jedem Rechteck malen kommt
sowas vor. Und diese Fälle sind gar nicht so selten.
Der Gedanke, müsse nur eine möglichst schnelle SetPixel haben ist zwar
naheliegend, aber Speed kriegt man mit anderen Mitteln.
Hannes schrieb: > Ich schreibe konverteierte Serifen-Schriftarten aufs Panel Und wie liegen die vor? Als Bitmap? Dann hast Du doch quasi schon ein Abbild des "Bildschirmspeichers" und es wäre unnötig jeden Pixel dieser Bitmap per Shift in Deinen Ausgabespeicher zu übertragen.
Hannes schrieb: > ...au man. > > der 168 ist doch ein 8bitter, oder? Ja. Deshalb ja auch die Nachfrage. Eine 16Bit-Variable wird auf dem eher ineffizenter sein als zweimal 8 Bit, schon alleine, weil dann bei jedem Aufruf deiner Pixel-Funktion zwei Bytes aus dem RAM gelesen und wieder geschrieben werden müssen statt nur eins. > Ich denke darüber nach, dies auf > > uint8_t RAM[128] oder RAM[2][64] umzustellen und mit einer 8-wertigen > Tabelle wie oben gezeigt zu arbeiten... Gute Idee. > Die 16 Bit Werte werden doch auch wieder zusammengebastelt, oder? Ja. Der Prozessor hat ja nur 8 Bit große Register. Hannes schrieb: >>>Also kannst du die Tabelle durch eine Variable ersetzen > > das macht der Compiler doch schon für mich, das möchte ich ja vermeiden. > > Wenn ich 64 Pixel in einer Linie setze, dann ist er nur noch am dhiften, > für jeden Pixel aufs neue... Nunja, genau da wäre der Punkt, wo es sich lohnen kann, sich Zwischenwerte zu merken. Du malst die Pixel der Linie ja nicht in zufälliger Reihenfolge, sondern einen nach dem anderen. Da mußt du ja immer nur dann, wenn du einen Pixel weitergehst, um ein Bit shiften. Hannes schrieb: >>>einfach auch eine Maske > > Ich schreibe konverteierte Serifen-Schriftarten aufs Panel, zeichne > Kreise und Linien mit dem Bresenham... Für Kreise gilt eigentlich das gleich wie für die Linien. Und bei den Schriftarten kann man sich noch bessere Optimierungen ausdenken (ganze Bytes auf einmal schreiben), wenn man die Schriftgröße und Position passend wählt.
Ich weiss, im Titel steht 'möglichst in C' aber das schließt Inline Assembler ja nicht aus;-) Gab's das nicht mal diese wunderschönen SBR/CBR Mnemonics? 3 Zeilen Inline Assembler könnten vielleicht die Lösung sein. Datenbyte holen, Bit reinschiessen und wieder weg damit...
Die Schriften werden ja als Bitmap vorliegen. Und da wäre es Unsinn, die Bitmap in Pixel zu zerlegen und fürs GLCD dann wieder zusammen zu setzen. Man kopiert besser die Bitmap byteweise. Ein Problem ist nur, wenn das Zeichen nicht Byte-aligned anfängt. Da schreibt man sich am besten 8 optimierte Routinen, für Anfang an Pixel 0 .. 7. Peter
Norbert schrieb: > Gab's das nicht mal diese wunderschönen SBR/CBR Mnemonics? Die gibt's, aber die bekommen die Bitnummer als Immediate. Eine zur Laufzeit ermittelte Bitnummer kann man denen nicht übergeben.
Bei einem 16*64 Pixel Display, das nun selbst unter besten vorstellbaren Bedingungen nur 1024 Pixel darstellen kann, muß man meiner Meinung nach nicht mit Gewalt optimieren. So eine Sequenz zum Setzen eines Bits im SRAM dauert: LDS 2cycl SBR 1cycl STS 2cycl Wenn man vielleicht die 128 Bytes des Displays als Kopie im SRAM hält und das Display zum Schluss in einem Rutsch beschreibt, so reden wir von 1024 * (5 + was sonst noch so im Loop gebraucht wird) cycles.
Rolf Magnus schrieb: > Die gibt's, aber die bekommen die Bitnummer als Immediate. Eine zur > Laufzeit ermittelte Bitnummer kann man denen nicht übergeben. Ahh, OK, also noch eine kleine Sprungtabelle mit IJMP Braucht insgesamt dann ein paar cycles mehr. Man muß wirklich schauen/untersuchen wer besser optimiert, Mensch oder Maschine.
Hi, wenn ich zu den Schriften komme, muss ich noch mal sehen wie ich das mache. Zur Zeit male ich Linien und Kreise, und setze/rücksetze ein paar Pixel. >>Gute Idee. ist es egal ob RAM[128] oder RAM[2][64]. Ich weiß nicht wie es umgesetzt wird, aber ich schätze das 2 Indizies einfach miteinander multipliziert werden o.ä... dann kann ich auch gleich ersteres nehmen... >>im Titel steht 'möglichst in C' Naja, es steht eigentlich nicht da, aber .-) Ich würde gerne c beibehalten. Eigentlich ist es schön blöde sich Gedanken um die internen Abläufe zu machen, genau das sollte c ja eigentlich abnehmen... naja, wenn man es mit einem anderen Gedankenansatz jedoch viel besser angehen kann, reicht mir das schon völlig... >>Thema: Organisation: Meine Routine sieht zur Zeit so: uint16_t mtx_data[64]; dsp_set_pixel( x, y) { mtx_data[((y^0x0f)&0x0f)|(x&0x30)]|=0x01<<(x&0x0f); } aus. in 5 min mal eben schnell dahingepfuscht, da möchte ich dran arbeiten.... Leider ist es hadrwaremäßig so angeordnet: Register 1 Bit7 Zeile15--LED(0,15)---LED(1,15)-...-LED(63,15)- ... ... ... ... Register 1 Bit0 Zeile8 --LED(0,8)----LED(1,8)--...-LED(63,8)-- Register 2 Bit7 Zeile7 --LED(0,7)----LED(1,7)--...-LED(63,7)-- ... ... ... ... Register 2 Bit0 Zeile0 --LED(0,0)----LED(1,0)--...-LED(63,0)-- R3,0 - Sp.0 R3,1 - Sp.1 R10,7 - Sp63 Es sind 10 hintereinandergeschaltete 74HC595. Die ersten beiden machen das Zeilensignal, schalten also die jeweilige Zeile ein, die hinteren 8 enthalten dann die 64 darzustellenden bits.... In meinem Beispiel liegen die 16Bit werte nun immer in blöcken waagerecht hintereinander: word15 word31 word47 word63 ... ... ... ... word0 word16 word32 word48 deswegen rutscht ein Teil der x Koordinate mit in den Index des Speichers: V [((y^0x0f)&0x0f)|(x&0x30)] um das richtige Word zu treffen... >>Thema Merken: Wenn ich mir das nun merke, dann ist es doch schon wichtig sich zu merken welche Koordinate gerade angespochen wurde, ob sich die nächste noch im Word oder Byte befindet etc... dann ist es auch wichtig ob ich Linien von links nach rechts zeichne, oder umgekehrt...Wieviel sollte ich mir denn merken..? Hannes
Hannes schrieb: >>>Thema Merken: > > Wenn ich mir das nun merke, dann ist es doch schon wichtig sich zu > merken welche Koordinate gerade angespochen wurde, ob sich die nächste > noch im Word oder Byte befindet etc... dann ist es auch wichtig ob ich > Linien von links nach rechts zeichne, oder umgekehrt...Wieviel sollte > ich mir denn merken..? Du versteifst dich hier zu sehr auf die setpixel-Funktion. Wenn du eine Linie zeichnest, rufst du die nicht auf, sondern setzt die Pixel direkt aus der Linienzeichen-Funktion. Die weiß ja, wo sie gerade steht und in welche Richtung sie läuft. Statt die Koordinate zu inkrementieren, schiebst du einfach die Maske um eins nach links, und wenn die dann 0 ist (die 1 ist rausgefallen), setzt du sie wieder auf 1 und gehst um ein Byte weiter.
Rolf Magnus schrieb: > Hannes schrieb: >>>>Thema Merken: >> >> Wenn ich mir das nun merke, dann ist es doch schon wichtig sich zu >> merken welche Koordinate gerade angespochen wurde, ob sich die nächste >> noch im Word oder Byte befindet etc... dann ist es auch wichtig ob ich >> Linien von links nach rechts zeichne, oder umgekehrt...Wieviel sollte >> ich mir denn merken..? > > Du versteifst dich hier zu sehr auf die setpixel-Funktion. Wenn du eine > Linie zeichnest, Es lohnt auch, für die häufigen Sonderfälle "waagrecht"/"senkrecht" eigene Linienfunktionen zu machen. Gerade wenn man eine GUI baut, kommen die oft vor. Graphik ist nur in der Theorie 'ganz einfach'.
...meine Linienfunktion zeichnet auch schräge Linien...dann müsste ich dafür ja tiefer in diese Funktion eingreifen... Ich organisiere gerade meinen Speicher um: Ein Speicher mit 2 Indizies führt zu folgender Funktion: uint8_t mtx_data[8][16]; dsp_set_pixel( x, y) { mtx_data[(x>>3)&0x07][y&0x0f]|=0x01<<(x&0x07); } ich muss einen Teil aus x holen und dem Index zuführen, der wird sicher im Hintergrund eh wíeder ausmultiplisziert mit dem y Anteil [zweiter index]. Die Frage wäre ob: uint8_t mtx_data[128]; dsp_set_pixel( x, y) { mtx_data[((x<<1)&0x70)|(y&0x0f)]|=0x01<<(x&0x07); } besser ist.... Hannes
>>Graphik ist nur in der Theorie 'ganz einfach'.
da ist was dran - danke für die Tips und Eure Zeit bis hierher :-)
Hannes
Hannes schrieb: > ...meine Linienfunktion zeichnet auch schräge Linien...dann müsste ich > dafür ja tiefer in diese Funktion eingreifen... Ja. Es kommt dann eben drauf an, wie stark deine Grafikroutinen optimert sein sollen und wieviel Aufwand du reinstecken willst. Du kannst natürlich mit einer einzigen Linienzeichenroutine alles abdecken. Dann sind deine horizontalen und vertikalen Linien halt nicht schneller als diagonale. Du kannst mit generischen Funktionen alles machen, aber wenn du das Maximum an Geschwindigkeit rausholen willst, wird es sich immer lohnen, für häufig vorkommende Spezialfälle noch mal separate genau dafür optimierte Routinen zu bauen.
ok, das habe ich verstanden. Ich möchte im ersten Schritt erstmal das offensichtliche verbessern. Die Punkte wären: A)Speicherorganisation ideal wählen B)Bit im Byte setzen/rücksetzen effektiver machen. Nächste Schritte wären für mich C)Grafikroutinen erweitern mit "häufigen" fällen D)Textroutinen erweitern Ich habe verstanden das man an den Stellen C und D viel drehen kann udn dort eine Menge rausholen kann, ich denke aber auch in A und B kann ich DInge ideal wählen, um es erstmal grundsätzlich richtig zu machen und darum geht es mir momentan primär. Hannes
Wie schon mehrfach gesagt wurde versteif dich nicht auf das setzen einzelner pixel. Bearbeite einfach immer ganze bytes. D.h. byte lesen, betroffene pixel ändern, byte zurück scheiben. Das ist unabhängig davon was bzw in welche Richtung gezeichnet wird.
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.