SPI in der Programmiersprache Icon

  • Hallo Andreas,


    ich fasse mal zusammen.


    Deine Lösung beinhaltet zwei grundlegende Ideen.
    1. Per "nop"-Zählung (ich behalte mal den Terminus Deiner Zählschleife so bei) stellst Du Deine Zeitrechnung auf dem CPU-Takt ab - also unabhängig von irgendwechen anderen Systemtimern, welche ggf. schon "nur" mit 1MHz (->1µs) laufen. D.h. Deine Auflösung wird bestimmt vom CPU-Takt und der Anzahl der Takte, die zur Ausführung der Schleife benötigt werden. Damit bist Du auch unabhängig vom Laufzeitverhalten eines clock_gettime oder anderer Kernelobjekte. Die Messung selbst hat den Vorteil, vollständig im Userspace laufen zu können.
    2. Du kalibrierst Dein Zeitmodell über das "Zeitempfinden" Deiner Peripherie. D.h. laufen die Uhren in Raspi und Peripherie unterschiedlich schnell, dann paßt sich Dein Raspi-Zeitmodell (mittels Messung zweier bekannter Pulsweiten der Peripherie) an das Deiner zu steuernden HW an. Das hat Vorteile (Kompensation konstanter Gangabweichungen), kann aber auch Nachteile (bei veränderlichen Gangabweichungen) mit sich bringen.


    Folgende Problemstellungen sehe ich jedoch unbehandelt.
    1. Das Problem langer Einzelimpulse
    2. Das Problem einer kontinuierlichen Übertragung von Impulsen


    Beides geht zurück auf das Kernproblem, daß Deine Schleife...


    Code
    1. procedure wait_falling(pin, korr, timeout)
    2.    loop := 0
    3.    start := &time
    4.    while GPIO(pin) = 1 do
    5.    {    loop +:= 1
    6.         if \timeout then if &time – start > timeout then break
    7.    }
    8.    if /korr then return loop else return loop * korr
    9. end


    letztendlich nicht ununterbrechbar ist, da sie vom Scheduler preempted werden kann. Da kannst Du im Prinzip auch nichts dagegen tun, außer auf Applikationsebene das Problem zu erkennen und zu behandeln. Für den Fall 1.) kann man allerdings etwas Zusatzcode in die Schleife einbauen, der durchaus hilfreich sein kann. Willst Du sehr lange Zeiten (also Zeiten größer den Latenzzeiten des Kernels) überbrücken, so könntest Du dich für den Restbetrag (jitternd) schlafen legen (z.B. nanosleep) und die Restzeit per nop-Schleife "verheizen". Das Problem ist hierbei lediglich, daß Du in jedem Fall die Zeit Deines Schlafes messen müßstest und damit aus Deinem (genaueren) Zeitmodell herausfallen würdest. Im Falle des Raspi würde sich dazu ggf. der mit 1MHz laufende System Takt anbieten, womit man zumindest im µs Bereich operiert. Nachdem Du dadurch allerdings Zeitauflösung verlierst, kann man in diesem Fall auch gleich von Anfang an auf dem System Timer aufbauen.


    Vermutlich würde eine generische Methode der Zeitmessung auf mehreren Konzepten abstellen, je nachdem, wie lang die zu messenden Zeiten ausfallen und wie genau man sie benötigt.


    So, und nachdem Du meine graue "CPU" wieder mal gequält hast ;) (und das am Wochenende!!!), geb' ich Dir jetzt auch mal was zum tüfteln, so Du magst. :baeh2:


    Gesucht ist ein Algorithmus, der in O(1) einen Wert, welcher die Registergröße der CPU überschreitet, um n Stellen shiftet.
    Beispiel: Ein 32 Bitwert z.B. dx:ax (bleiben wir mal bei Intel) soll um n Bits nach links geschoben werden. O(1) - also ohne Schleife mit Carrybit! Und komm' mir jetzt nicht mit eax ;)


    Hinweis: Du mußt in Assembler denken. Hochsprachen fehlen die hierzu notwendigen Operatoren.


    Schöne Grüße


    schnasseldag

  • Hallo Schnasseldag,



    So, und nachdem Du meine graue "CPU" wieder mal gequält hast ;) (und das am Wochenende!!!), geb' ich Dir jetzt auch mal was zum tüfteln, so Du magst. :baeh2:


    gerne ... ;)



    Gesucht ist ein Algorithmus, der in O(1) einen Wert, welcher die Registergröße der CPU überschreitet, um n Stellen shiftet.
    Beispiel: Ein 32 Bitwert z.B. dx:ax (bleiben wir mal bei Intel) soll um n Bits nach links geschoben werden. O(1) - also ohne Schleife mit Carrybit! Und komm' mir jetzt nicht mit eax ;)


    Hinweis: Du mußt in Assembler denken. Hochsprachen fehlen die hierzu notwendigen Operatoren.



    ich bin mir nicht ganz sicher, ob ich Dich da richtig verstanden habe.


    Du willst eine negative Zahl haben, die zu ihrer binären Darstellung mehr Bits benötigt, als der Prozessor in einem Register darstellen kann. Und diese Zahl soll durch einen Algorithmus verschoben (also mit einer Zweierpotenz multipliziert) werden, ohne dass das Vorzeichenbit hopps geht?


    Hmmm ... :s ... :huh: ... :cool:


    Und Dein einziger Hinweis besteht darin, dass ich dabei in Assembler denken soll ... So so ... Lass mich mal an Assembler denken... :s mein letztes Assemblerprogramm habe ich 1987 oder davor geschrieben. Es war ein Programm zur Berechnung von Orbitalenergien. Matrixmultiplikation und Matrixinversion in Assembler. Tolle Sache damals.


    Seit 2003 habe ich (zu)viel in Icon programmiert. So denke ich erstmal in Icon (weiter).


    In Icon gibt es ein Feature, das sich Large Integer Arithmetic - oder kurz LIA - nennt. Da sollte es ersteinmal kein Problem sein, große positive oder negative Zahlen zu erzeugen. Ich habe mal ein Programm geschrieben, dass Wurzeln auf mehrere Tausend Stellen genau berechnen kann. Mit LIA alles kein Problem.


    Code
    1. bit256 := 0
    2. every i := 0 to 255 do bit256 -:= ishift(?[1,0], i) # Konstruktion einer 256-Bit-Zahl
    3. write(bit256, " => ", dec2bin(bit256)) # und deren Ausgabe in dec und bin


    Klatsch, eine 256-Bitzahl, z.B. die hier

    Code
    1. -14647772500698755210929181230453831102933287463430047855367659781671697911159


    oder in ihrer Binärdarstellung:

    Code
    1. 1111110011101101001111001000101110111001000110011011010001000011101010100011001100110101101100000101001000110110110001000100111000101101101111101101101001000100001111100100010011000011011000101101001110110111001111000101100110101011001011111001010001001


    Diese Zahl verschiebe ich jetzt nicht mit einem Algorithmus (habe keinen Sinn darin gesehen, einen Algorithmus auszudenken, wenn es dafür einen festeingebauten Befehl gibt) um 512 Stellen. Ich unterstelle jetzt einmal, dass praktisch kein handelsüblicher Prozessor eine 256Bit-Zahl im Register aufnehmen kann. In Icon sieht das dann so (einfach) aus:

    Code
    1. bit256 := ishift(bit256, 512)


    Dabei wird das, was Du als Carry-Bit (Vorzeichen-Bit) bezeichnest, ohne weitere Behandlung (also kompletter Ignorierung) beibehalten.

    Wir kommen dann auf eine Zahl mit 768 Binärstellen. In dem Beispiel:

    Code
    1. -196394520290863876031905153277323077456630476333766471083615393243430081509976326786169307647605856026092659090770921051997961928805580844801722042592476389997321491942488536347560536774443216944254956802756194961919839765490827264


    was dem Binärcode

    Code
    1. 111111001110110100111100100010111011100100011001101101000100001110101010001100110011010110110000010100100011011011000100010011100010110110111110110110100100010000111110010001001100001101100010110100111011011100111100010110011010101100101111100101000100100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000


    entspricht.


    Hier der Gesamtcode:



    Vielleicht noch ein paar Worte zu den hier benutzten Funktionen:
    [font="Courier New"]every[/font] ... entspricht dem [font="Courier New"]for[/font] anderer Programmiersprachen.


    [font="Courier New"]ishift(a,b)[/font] verschiebt das Bitmuster um [font="Courier New"]b[/font] Stellen nach links, wenn [font="Courier New"]b > 0[/font] ist, sonst um [font="Courier New"]b[/font] Stellen nach rechts.


    [font="Courier New"]iand(a,b)[/font] entspricht der bitweisen UND-Verknüpfung von [font="Courier New"]a[/font] und [font="Courier New"]b[/font].


    Und das am Samstag... ;)


    Schönes Wochenende!


    Andreas

    Ich bin wirklich nicht darauf aus, Microsoft zu zerstören. Das wird nur ein völlig unbeabsichtigter Nebeneffekt sein.
    Linus Torvalds - "Vater" von Linux

    • Icon-Tutorials (IDE: Geany) - GPIO-Library - µController-Programmierung in Icon! - ser. Devices - kein Support per PM / Konversation

    Linux is like a wigwam, no windows, no gates, but with an apache inside dancing samba, very hungry eating a yacc, a gnu and a bison.

    Edited once, last by Andreas ().

  • Hallo Andreas,


    sooo groß sollten die Zahlen gar nicht sein. Zunächst noch mal kurz die Aufgabe. ein 32 Bit Wert (unsigned long) soll um n (sagen wir mal o.B.d.A. 5) Stellen nach links geschoben werden. Die CPU ist aber nur 16 bittig. D.h. der unsigned long steckt in 2 Wortregistern a 16 Bit.
    dx := HiWord
    ax := LoWord


    gesucht ist: dx:ax << 5


    Gemeinhin würde man eine for-Schleife bauen und 5 mal um ein Bit nach links schieben. Das MSB von ax wird nach jedem Durchlauf in's Carrybit des Statusregisters geschoben und dann beim Linksshift des dx-Registers in dessen LSB übertragen. Dazu müßte man aber eben eine Schleife bauen.


    Es geht aber auch so:


    Vorbedingung
    dx := HiWord
    ax := LoWord


    Code
    1. mov bx, ax ; Kopie in bx anfertigen
    2. shl ax, 5 ; hier fallen die oberen 5 Bit raus - die hätten wir aber gern, da sie "unten" in das dx Register reingeschoben werden müssen
    3. rol bx, 5 ; hier werden die 5 oberen Bits wieder unten hineinrotiert
    4. xor bx, ax ; die oberen 11 Bits von bx und ax sind identisch -> d.h. werden (durch xor) in bx gelöscht; die unteren 5 Bits von bx enthalten genau den Übertrag!
    5. shl dx, 5 ; HiWort schieben
    6. or dx, bx ; 5 Bit Übertrag von bx in dx reinbeamen


    Nebenbemerkung: Die Intel-Prozessoren < 80186 konnten nur um cl shiften. Ich hab hier nur die 5 aus Anschaulichkeitsgründen gewählt. Auch würde ein Shift >=15 Stellen noch ein wenig Code bedingen. Aber das spielt hier mal keine Rolle.


    Du wirst Dich vielleicht fragen, wozu man das benötigt?! In Zeiten, in denen die CPU's noch keine FPU's hatten (und auch heute gibt's noch jede Menge davon - siehe manche ARM Cores), mußte der Float halt irgendwie emuliert werden. Ich hatte vor 20 Jahren einen NEC V25 unter den Fingern, auf dem ein Echtzeitbetriebssystem lief. Zwar wurde der Code in C geschrieben, jedoch konnte ich die MS-floating lib nicht einsetzen, da sie nicht reentrant war. Das ganze System war integerskaliert. Blöd nur, daß die zu programmierenden Antriebsregler manchmal in Geschwindigkeiten von 1Digit/1Takt liefen. Da ist der Fehler enorm groß, wenn Du mit Ganzzahlen rechnest. Also mußte ein Float her. Wenn Du nun den Integer in einen Float wandelst oder umgedreht, so wirst Du feststellen, daß Du den Integer auf die Mantissenstelle des Float schiebst und dann den Exponenten reinoderst (und umgekehrt). Insofern schrieb ich mir also entsprechende Cast-Operatoren integer->float, float->integer und einige arithmetische Operatoren (+,-,*,/) und Hilfsoperatoren zur Normierung des FLoat. Das ganze lief mit verkürzter Mantisse, da ein V25 ungefähr die Rechenleistung eines 80186 hatte.
    Nunja, bei dieser Minilib hatte ich o.g. Technik bis zum Erbrechen eingesetzt, um das O(n) Kalkül bei der Shifterei zu umgehen.


    Den Trick hatte ich irgendwo mal aufgeschnappt. Von mir stammt er nicht. Die Assembler-Freaks haben sicher noch mehr von diesen Dingen auf Lager. Für mich ist er allerdings bis heute als eines der brilliantesten Stücke Code im Gedächtnis haften geblieben. Glasklar, einfach, kurz und dadurch extrem performant!


    Schönen Sonntag noch...


    schnasseldag

  • Hallo Dreamshader,


    lass Dich in unserem Bund der Dritte sein... :bravo2:


    Beste Grüße


    Andreas

    Ich bin wirklich nicht darauf aus, Microsoft zu zerstören. Das wird nur ein völlig unbeabsichtigter Nebeneffekt sein.
    Linus Torvalds - "Vater" von Linux

    • Icon-Tutorials (IDE: Geany) - GPIO-Library - µController-Programmierung in Icon! - ser. Devices - kein Support per PM / Konversation

    Linux is like a wigwam, no windows, no gates, but with an apache inside dancing samba, very hungry eating a yacc, a gnu and a bison.

    Edited once, last by Andreas ().


  • ( das waren noch Zeiten: 8 bit CPU und sagenhafte 4,77 MHz Taktfrequenz :lol: )


    Diese Zeit hatte auch ihr Gutes. Man hat sich viel mehr Gedanken darüber gemacht, wie man programmiert. Heute haut man doch schnell mal mit ein paar GHz auf das Problem und schreibt Loggingwerte in eine Datenbank... =(
    Kein Wunder, daß die echten Interrupts ausgestorben sind und kaum einer weis, was Arm und Thumb sind... :no_sad: