Eine Apparatur zum Erlernen des richtigen Hammerschlags bei Operationen an (künstlichen) Hüftgelenken. Eingebettet in einer AR-Anwendung, an deren Entwicklung ich nicht beteiligt war.
Doch dazu kam ich nur scheibchenweise, um schließlich doch alles machen zu müssen. Abgesehen von der Mechanik.
Irgendwas aus Dänemark mit Absolutwertgeber, was mir hier vorgesetzt wird.
Das Bedienteil will irgendetwas standardisiertes sein, tatsächlich entpuppt sich die Parametrierungsschnittstelle als closed-source sowie (absichtlich?) falsch dokumentiert: Die im Datenblatt angegebene Baudrate stimmt nicht.
Nummer | Name | Richtung | Funktion |
---|---|---|---|
1 | +5V | Bedienteil | Spannung 5 V |
2 | 0V | keine | Bezugspotenzial 0 V |
3 | Steuerteil | Interrupt (Unterbrechungsanforderung) negiert | |
4 | Rx | Bedienteil | Empfangsdaten TTL |
5 | Tx | Steuerteil | Sendedaten TTL |
6 | - | - | nicht belegt (von wegen!) |
Ziel ist es, das Bedienteil durch eine PC-Software zu ersetzen, um die Parametrierung durch Software erledigen zu können. Im Blick ist dabei LabVIEW.
Dazu muss das Protokoll erschnüffelt werden. In diesem Fall steht sigrok/PulseView sowie ein preisgünstiges CY7C68013A-Entwicklungsboard aus China zur Verfügung.
Gleich zu Anfang stellt sich heraus:
Der „burst1“ erschließt sich durch scharfes Hingucken. Dabei ist [0A] die String-Länge. Das [0B] vermutlich die Paket-Länge; hier nur 1 Sub-Item. Die anderen Konstanten werden wohl nicht entzifferbar (= bedeutungslos) bleiben. Unklar ist noch, warum der burst1 mit einem unbeantworteten [01] endet. Könnte sein dass das bereits zum burst2 gehört.
Der „burst2“ ist ebenfalls entziffert. Darin ist vermerkt, wieviele Parameter und Werte es gibt, und welche 8 Bit und welche 16 Bit breit sind. Daraus ergibt sich die Einschränkung, dass 16-Bit-Parameter stets hinter allen 8-Bit-Parametern angeordnet sein müssen; das gleiche für die Werte. Und dass es maximal 79 (Parameter + Werte) geben kann, da Parameter 1 auf „Adresse“ 21 und Folgebytes von Mehrbyte-Werten auf „Adresse“ 100 (jeweils dezimal) abgefragt werden.
Da auf dem Zielrechner das LabVIEW streikt, wurde kurzerhand das Programm in C++ in Visual Studio 6 erstellt. Alle Werte werden korrekt angezeigt. Die Zeilentitel können vom Anwender geändert werden und werden persistent je nach angeschlossener Steuerung (Name als Referenz) in der Registrierung gespeichert. Die Parameter können vom Anwender zwar editiert werden, werden jedoch noch nicht zur Steuerung zurückgeschrieben. Ein Tastaturinterface zum Umbenennen der Zeile bzw. zur Wertänderung ist (seitens Windows) nicht vorhanden, wird wohl noch mit F2 bzw. F3 realisiert (Explorer-kompatibel).
Das gesamte Teilprojekt, den Linak-Servo zu „reverse-engineeren“ erwies sich nachfolgend als hinfällig, da die entscheidende Steuerungsgröße Kraft nicht verfügbar ist und die Alternative Weg nur per Analogwert vorgebbar ist.
Nun kommt die Katze aus dem Sack: Der Linak-Servo dient dazu, eine Fahrradbremse anzuziehen. Da sich alles bloß nicht die Stellkraft und die Position des Servos digital einstellen lässt, müssen mechanisch federnde Elemente dazwischen (Hebel, Bowdenzug) die Proportionalität von Weg und Kraft herstellen und ein Analogwert legt die Position fest. Der FT232R-Adapter wird wieder frei und durch die Firmware des Arduino(?) ersetzt. Zum Einsatz kam schließlich ein Board mit STM32F103. Und eine Firmware mit WebUSB für die Funktionsdarstellung im Browser.
Hinfällig:Da dies nicht ohne Bauchschmerzen mit einem Arduino Uno geht, tendiere ich umgehend zu meinem Liebling Arduino Leonardo mit nativem USB-Interface. Das Computerprogramm soll in LabVIEW laufen und mit dem Versuchsaufbau über USB (genau 1 Kabel) kommunizieren.
Zusätzlich soll noch eine mit einem Hammer einwirkende Kraft gemessen werden. Im Vorversuch wurde eine Abtastrate von 20 kSa/s als geradeso ausreichend festgelegt, um den Kraftstoß zu erfassen. Eine geeignete Kraftmessdose mit Dehnmessstreifen wurde konstruiert, aufgebaut und eingebaut. Zur Erfassung des Kraftstoßes fallen die üblichen Wägezellenverstärker mit Sigma-Delta-Umsetzer aus; diese sind viel zu langsam.
Und ein Verschiebeweg wird mit einem digitalen Messschieber aufgenommen. Der misst nur langsam; die Position vor und nach dem Hammerschlag genügt. Die üblichen Messchieber haben 1/100 mm = 10 µm Auflösung. Siehe auch caliper2pc.
Schließlich soll die Festhaltekraft (Bremskraft) außer durch das Computerprogramm auch analog an einem Potenziometer eingestellt werden können.
Die Linak-Steuerung benötigt zudem ein Netzteil mit 24 V und 2,2 A. Dass diese ihrerseits 5 V liefern kann wird hier nur genutzt, um der Steuerung einen Analogwert vorzugeben.
Der allfällige Vorverstärker für die Kraftmessdose mit OPA2376 verstärkt das Brückenausgangssignal um den Faktor 300, allein mit R4 einstellbar. R2 dient zum Brückenabgleich. Durch die Gegenkopplung des OPV arbeitet die Brücke im Kurzschlussbetrieb, und es wird effektiv der Diagonalstrom in eine Spannung gewandelt.
Hinfällig:Die Variante mit dem teureren und größeren Arduino Micro verwendet den differenziellen A/D-Wandler mit umschaltbaren Verstärkungen (×1, ×10, ×40 und ×200), um verschieden starke Schläge angemessen erfassen zu können. Leider bleiben dann nur 8 Bit Auflösung laut Datenblatt übrig, und man muss die Schlagstärke a priori kennen.Die Variante mit dem chinesischen Pro Micro verwendet einen Instrumentationsverstärker und muss asymmetrisch messen, da bei diesem weder ADC0 noch ADC1 (noch AREF) herausgeführt ist. Dafür gibt es 10 Bit Auflösung.
ATmegas sind für die Abtastung des Kraftstoßes etwas überfordert. Externe A/D-Wandler erscheinen Overkill. Daher tendiere ich nun eher zur Verwendung eines STM32F103C8T6 auf einem Bluepill-Board. (Liegt schon zu Hause herum.) Den fälligen USB-Seriell-Konverter, den man leider zum Betanken dieses Boards benötigt, gewinne ich vom FT232R-Adapter für den Linak-Servo.
Der 12-bit-A/D-Wandler schafft 1 MSa/s, und es sind zwei davon drin. Leider nicht differenziell, daher Single-Ended-Konvertierung mit OPA2376 + INA155 (liegen herum, waren wohl kostenlose TI-Muster gewesen). Das USB-Interface wird wieder als WebUSB implementiert. Um Betriebsspannungskollisionen auszuschließen liefert der Linak-Servo die 5 V für den Verstärker vom PWM-Ausgang: Die Linak-Steuerung bekommt einen Analogwert nur dann, wenn diese auch 5 V liefert, also eingeschaltet ist. So kommt die gesamte Schaltung ohne wacklige 5 V vom USB aus, sondern vom 3,3-V-Festspannungsregler auf dem BlackPill-Board.
Die verwendeten Analogschaltkreise:
Hoppla, ich hatte wohl die schwarze und nicht die blaue Pille genommen! Da unterscheidet sich die Pinbelegung ganz erheblich! Die Pin-1-Festlegung der Stiftleisten erscheint willkürlich, ich habe es hier wie bei DIL-Schaltkreisen umlaufend nummeriert.
Zur besseren Übersicht über herausgeführte Pins und deren Sonderfunktionen diese sortierbare Tabelle — Angaben ohne Gewähr:
LQFP48 | Sonderfunktion | Portpin | 5V? | UART | I²C/SPI | PWM | ADC | BluePill-40 | Label | BlackPill-34 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
1 | UBAT1 | VB | -
| 2 | Tamper-RTC | PC13 | n | 2, Blaue LED nach UDD | C13 | 4
| 3 | Osc32 In | PC14 | n | 3, Quarz 32 KHz | C14 | Quarz 32 KHz
| 4 | Osc32 Out | PC15 | n | 4, Quarz 32 KHz | C15 | Quarz 32 KHz
| 5 | Osc In | PD0 | n | Quarz 8 MHz
| 6 | Osc Out | PD1 | n | Quarz 8 MHz
| 7 | n | 17 | R | 5
| 8 | GNDA | 19, 20, 39 | G | 1
| 9 | UDDA | 18, 40 | 3V3 | 33
| 10 | WakeUp | PA0 | n | Cts2 | T2C1ETR | 0 | 5 | A0 | 6
| 11 | PA1 | n | Rts2 | T2C2 | 1 | 6 | A1 | 7
| 12 | PA2 | n | Tx2 | T2C3 | 2 | 7 | A2 | 8
| 13 | PA3 | n | Rx2 | T2C4 | 3 | 8 | A3 | 9
| 14 | PA4 | n | Ck2 | 4 | 9 | A4 | 10
| 15 | PA5 | n | SCK1 | 5 | 10 | A5 | 11
| 16 | PA6 | n | MISO1 | T3C1/ | 6 | 11 | A6 | 12
| 17 | PA7 | n | MOSI1 | T3C2/ | 7 | 12 | A7 | 13
| 18 | PB0 | n | T3C3/ | 8 | 13 | B0 | 14
| 19 | PB1 | n | T3C4/ | 9 | 14 | B1 | 15
| 20 | Boot1 | PB2 | n |
| 21 | PB10 | j | Tx3 | SCL2 | T2C3 | 15 | B10 | 16
| 22 | PB11 | j | Rx3 | SDA2 | T2C4 | 16 | B11 | 17
| 23 | GND | 19, 20, 39 | G | 1
| 24 | UDD | 18, 40 | 3V3 | 33
| 25 | PB12 | j | Ck3 | 21 | B12 | 18, LED nach UDD
| 26 | PB13 | j | Cts3 | SCK2 | 22 | B13 | 19
| 27 | PB14 | j | Rts3 | MISO2 | 23 | B14 | 20
| 28 | PB15 | j | MOSI2 | 24 | B15 | 21
| 29 | MCO | PA8 | j | Ck1 | T1C1 | 25 | A8 | 22
| 30 | PA9 | j | Tx1 | T1C2 | 26 | A9 | 23
| 31 | PA10 | j | Rx1 | T1C3 | 27 | A10 | 24
| 32 | USBD- | PA11 | j | Cts1 | T1C4 | 28, USB | A11 | 25, USB
| 33 | USBD+ | PA12 | j | Rts1 | T1ETR | 29, USB | A12 | 26, USB
| 34 | TMS | PA13 | j | X4-2 | DIO | X4-3
| 35 | GND | 19, 20, 39 | G | 1
| 36 | UDD | 18, 40 | 3V3 | 33
| 37 | TCK | PA14 | j | X4-3 | SCK | X4-2
| 38 | TDI | PA15 | j | T2C1ETR | 30 | A15 | 27
| 39 | TDO | PB3 | j | SCK1 | T2C2 | 31 | B3 | 28
| 40 | PB4 | j | MISO1 | T3C1 | 32 | B4 | 29
| 41 | PB5 | n | SMBAI1/MOSI1 | T3C2 | 33 | B5 | 30
| 42 | PB6 | j | Tx1 | SCL1 | T4C1 | 34 | B6 | 31
| 43 | PB7 | j | Rx1 | SDA1 | T4C2 | 35 | B7 | 32
| 44 | Boot0 | n |
| 45 | CANRX | PB8 | j | SCL1 | T4C3 | 36 | B8 | 2
| 46 | CANTX | PB9 | j | SDA1 | T4C4 | 37 | B9 | 3
| 47 | GND | 19, 20, 39 | G | 1
| 48 | UDD | 18, 40 | 3V3 | 33
| 5P | 38, USB | 5V | 34,USB
| |
Hinfällig:Um sich den Analogvergleicher zu sparen (leider ist keiner drin) erfolgt die Pegelkonvertierung vom Messschieber mittels A/D-Wandler. Der ist mit seinen 1 MSa/s eh' schnell genug.
Der STM32F103 kommt leider ohne USB-Urlader ab Werk. Und leider ist das Angebot von Urladern aus der Arduino-Szene ungenügend. Der beste zu findende ist ein HID-Urlader von serasidis.gr (aus Griechenland) mit 2 KiByte Platzbedarf, leider per Jumper BOOT1 zu starten: Zu umständlich. Eine Neukompilierung ohne Interrupts und ohne angezogene Handbremse (hier: USB-FIFO-Größe von 8 auf 64 Byte geändert) ist nun so gestaltet, dass man mit der Reset-Taste zum Urlader kommt; alle anderen Reset-Ursachen (bspw. Strom weg) führen zum Anwendungsprogramm, für das noch 62 KiByte Flash-Platz übrig bleibt. (Auf 1 KiByte ist der Urlader wohl nur mit ARM-Assembler hinzubekommen, wenn überhaupt. Wenn man bedenkt, dass der „Original“-Arduino-Urlader schlappe 20 KiByte wegschnappt, ist das schon ganz gut.) Zudem waren die Programme für seriellen Zugriff (stm32flash.exe) und HID-Zugriff (hid-flash.exe) viel zu groß; wurden mit MSVC6 auf 1/4 der ursprünglichen Größe eingedampft, ohne den Quelltext auszulichten. Bei „hid-flash“ ging mir der Zwang zum COM-Port (!) auf den Keks, dieses ist jetzt allenfalls optional, da eine Steuerung von BOOT1 nun nicht mehr erforderlich ist.
Hier ist alles dokumentiert und herunterladbar.
Auf einem für mich neuen Mikrocontroller etwas zu implementieren, was auch noch an die Grenzen des Machbaren geht, ist ein Weg mit vielen 🪨 Stolpersteinen.
Das WebUSB-Interface war, ausgehend vom Urlader, recht schnurstracks hinzubekommen. Man muss allerdings aufpassen, dass der Controller nicht in Interruptroutinen erstickt! Im Gegensatz zum AVR, wo bei 100 % Interruptlast das Hauptprogramm mit wenigstens 1 Instruktion pro Interrupt vorankommt (also im 🐌 Schneckentempo), geht beim STM32F103 „dank“ Interruptverkettung (tail chaining) gar nichts mehr! 🛑 Man sagt, die Hauptschleife verhungert. Und so suchte ich den Fehler prompt an der falschen Stelle.
Bei der Entscheidung, den Analogvergleicher LM393 wegzulassen und stattdessen den A/D-Wandler zu benutzen hatte ich mich wohl 🐎 vergaloppiert! Denn das Taktsignal des Messschiebers kommt mit immerhin 80 kHz, man muss es mit 200 kSa/s abtasten. Das passt gerade so ins Taktregime, wenn man die 4 A/D-Eingänge zyklisch abtastet, mit minimaler Abtastzeit: Der A/D-Wandler, der mit 12 MHz getaktet wird, benötigt 14 Takte pro Kanal, das ergibt eine Kanalabtastrate von 12 MHz/14/4 = 214 kSa/s. Aber eine Flankendetektierung pro Abtastwert geht nicht, der Controller stirbt an Interrupt-Überlast. Vorbereitung: Das Taktregime gewissenhaft kontrollieren, dass der Controller, der A/D-Wandler und die übrige Peripherie tatsächlich mit dem maximal möglichen Durchsatz läuft. Die Lösung: Die Wandlerdaten werden en bloc von bspw. 16 Quadrupeln eingesammelt und in einer Schleife deserialisiert. Das senkt die Interruptfrequenz und damit die Last. Das verlustfreie Abholen der Daten erfolgt per DMA und Interrupts bei halbem und vollem Puffer; die jeweils inaktive Pufferhälfte wird verarbeitet. Der Messschieber verwendet ein 2 × 24-Bit-Binärprotokoll, von dem nur der 2. Block ausgewertet wird. Ein Skalierungsfaktor wird dann im WebUSB per JavaScript angerechnet. Die Ausgabe erfolgt auf 1/100 mm.
Schließlich wurde doch noch ein LM393 spendiert, in invertierender Beschaltung, und das Ganze an SPI1 zu betreiben. Der Pferdefuß steckt im Detail: Wie empfängt man mit dem Mikrocontroller 49 Bit? Die einzige Möglichkeit, die 49. Taktflanke zu verschlucken (= Rücksetzen des internen Bitzählers) besteht mit der Steuerung von Slave-Select. Aus- und Einschalten des SPI-Moduls tut's nicht!!
Im Prinzip ist eine Art Einstrahl-Oszilloskop gefragt, bei dem 100 kSa/s genügen. Durch den Messschieber kommen ohnehin bereits reichlich 200 kSa/s an. Die Triggerbedingung in Software zu realisieren führte erneut zum Verhungern der Hauptschleife, auch wenn man größere Blöcke verarbeitet. Daher wurde kurzerhand das Regime so geändert, dass der DMA-Puffer riesig ist und die Triggerbedingung per ADC-Watchdog 🐕 läuft. Da dazu zeitraubende Fallunterscheidungen nötig sind, wurde das Problem dadurch gelöst, die Interruptvektortabelle in den RAM zu verlegen und den Vektor bedarfsgerecht umzustellen. Das funktioniert nun am Fallbeispiel einer steigenden Flanke so:
Späterhin fiel die Entscheidung, zusätzlich oder an Stelle des Messschiebers (mit 10 µm Auflösung) einen Absolut-Wegsensor einzubauen. Am einfachsten ein Potenziometer. Denn ein inkrementeller Wegsensor, hier der Messschieber kann beim schnellen Versetzen (per Hammerschlag) springen oder (wahrscheinlicher) Striche verschlucken. In Verbindung mit dem internen 12-Bit-A/D-Wandler wird eine Auflösung von 60 mm / 4096 = 20 µm erreicht, mit Rauschanteil verlässlich 0,1 mm, das reicht.
Damit kann man nun durch Erweiterung der Oszi-Funktion auf „Zweistrahlbetrieb“ Kraftstoß und Verschiebung gleichzeitig messen und verfolgen. Die dazu notwendige Halbierung der Einzelabtastrate auf ca. 400 kSa/s ist vertretbar. Gefordert sind gar nur 200 kSa/s, sodass sogar Drei- und Vierkanalbetrieb offen steht.
Zunächst wurde die Schaltung so aufgebaut, dass der eingebaute D/A-Wandler genutzt und das Ausgangssignal auf 5 V verstärkt wird. Aber Pustekuchen! Das STM32F103-Datenblatt ist dazu gemacht, zigtausendfache Umweltsünden zu begehen!! Man muss es nämlich ausdrucken und alle nichtzutreffenden Passagen durchstreichen! Das sind sehr, sehr viele. Dann bleibt gar nicht mehr so viel übrig (und wichtige Seiten über den Interruptcontroller fehlen noch), und das D/A-Wandlerkapitel entfällt ganz! Ist also gar keiner drin. Also das übliche: Mit PWM. (Ein echter D/A-Wandler stand schwachsinnigerweise im Pflichtenheft.) Als AVR-Verwöhnter hat man es mit STM32F103 echt schwer. Ein zutreffend gefiltertes Datenblatt ist man auch von 8-Bit-PICs sowie EZUSB gewöhnt. Hingegen kenne ich keinen 16- oder 32-Bit-Controller mit zutreffendem Datenblatt, vom C167 vielleicht abgesehen.
Das Brems-Potenziometer braucht nur gelegentlich abgefragt zu werden.
Da das eine lückenlose Erfassung des Kraftsensors behindert,
wurde es trotzdem in das 200-kSa/s-Regime hineingepresst.
Mit dem zweiten A/D-Wandler sollte es möglich sein,
das Potenziometer und auch die Chiptemperatur nach dem
Telegramm vom Messschieber abzufragen; die Pause ist ziemlich lang.
Dazu muss der Kraftsensor vom ADC2 angefragt werden,
der dann durchlaufen muss.
Das Datenblatt schweigt sich IMHO aus, was passiert,
wenn ADC1 lange (17,1 µs vorgeschrieben für Temperatursensor) abtastet
und ADC2 währrenddessen laufend neue A/D-Werte produziert.
Und wie sich das Ganze resynchronisiert.
Günstig erscheint es für diesen Ansatz, die 3 langsamen Werte (Potenziometer,
Temperatur, Referenzspannung) per „eingespritzten“ (injected) Wandlungsprozess
in die laufende Erfassung der Takt- und Datenleitung vom Messschieber einzuschieben.
Die Verwendung von ADC2 für die Oszilloskopfunktion des Kraftsensors
ermöglicht es, den Analog-Watchdog des ADC1 für die Erkennung von
Taktimpulsen zu verwenden.
Damit kann man die Interruptlast für den Messschieber
dramatisch senken.
Der durchgestrichene Text ist hinfällig, weil der Messschieber nicht mehr per A/D-Wandler sondern per Pegelwandler und SPI abgefragt wird. Dadurch kann sich ADC1 um die Oszilloskopfunktion mit Trigger kümmern, und ADC2 kümmert sich um alles andere. Temperaturmessung fällt dadurch flach, weil diese nur an ADC1 möglich ist und der Hardware-Trigger (notwendig für das Oszi) ebenfalls an ADC1 hängt.
Der wichtigste Teil der Servosteuerung ist der D/A-Wandler.
Alle anderen Parmeter des Servos werden über die serielle Schnittstelle
des STM32F103 vermittelt. Das erspart einen gesonderten USB-RS232-Umsetzer,
hält einen USB-Port frei und die Apparatur kompakt.
TODO: Noch zu implementieren
Das letztendliche Problem ist eine verbleibende Diskrepanz zur Praxis, weil während des Einschlagimpulses der Widerstand nicht zunimmt wie beim kegelförmigen Formstück (echtes Implantat) sondern nach Überwindung der Haftreibung in etwa konstant bleibt. Ein dem echten Einschlag entsprechenden Widerstand könnte man durch kegelförmige Gleitflächen erreichen (dann fixer Zusammenhang zwischen Weg und Kraftzunahme in Verbindung mit den Federkonstanten des Zustellsystems) oder durch eine schnellwirkende Wegmessung und Bremskraftveränderung, die dann in Mikrosekunden (!) reagieren müsste. Was an Railguns erinnert: Schneller als Körperschall zu sein. Angemerkt , recht spät für so ein Projekt!
Das Ganze ist eine „Brettschaltung“, die schließlich auf einen stabilen Tisch montiert wird. In der Mitte befindet sich der Linearmotor, der über ein raumgreifendes Hebelgestänge und einen Bowdenzug eine Fahrradbremse betätigt, die den (zunehmenden) Widerstand beim Einschlagen simuliert. Im linken Teil befinden sich Netzteil, Linak-Steuerung sowie die via USB angeschlossene Gesamtsteuerung mit STM32F103-Mikrocontroller und analoger Außenbeschaltung. Auf der rechten Seite befindet sich der Hüftknochen-Simulator, in den ein stangenförmiges Gebilde mit Schlagkopf eingeführt wird, welches dem medizinischen Gerät einigermaßen nachgebildet sein soll. Es besteht nicht aus teurem Medizin-Edelmetall.
Firmware für STM32F103 — siehe Makefile zum Bau und Flashen
WebUSB-Äpp — Zugriff auf das Gerät über das WebUSB-Interface
WebHID-Äpp — Zugriff auf das Gerät über das HID-Interface
DLL 32+64 Bit mit Test-🦎Echse und ⛲Quelle
Kraftvorgabe in kN | Gemittelter Digitalwert | Steilheit in 1/kN |
---|---|---|
0 | 720 | - |
10 | 1480 | 76 |
40 | 3060 | 58,5
|
Damit passt die Dimensionierung der Verstärkerschaltung zum Problem. Der Jumper JP1 ist nicht gesteckt. Die Linearität lässt zu wünschen übrig. (Die Steilheit sollte gleich sein.) Ob die Firmware oder die PC-Software die Linearisierung vornehmen soll ist noch unklar.
float
statt int16_t
umzustellen.
Wie sich schließlich herausstellte, ist das Quatsch
und verdammt viel unnütze Arbeit.
Weiterhin wurde das Handpotenziometer von ADC9 auf ADC4 umgeroutet.
Sowie der ADC1-Scanbetrieb auf 2 Kanäle erweitert.
Letztlich scheint es so zu sein, dass die DMA-ISR mit einer Aufruffrequenz
von 6 kHz keine zwei Statistikeinträge (je 2 Vergleiche und 1 Addition)
aktualisieren kann, dann blinkt die blaue LED nicht mehr,
und USB enumeriert nicht.
Bei 72 MHz CPU-Taktfrequenz.
Ohne zwei Statistiken kann das Wegpotenziometer nicht ausgewertet werden!
Ein zunächst vermuteter Überlauf von DMA-Puffern
scheint doch nicht in Betracht zu kommen.
Daher ein nahezu komplettes Rollback.
Geblieben ist:min max sum
und einem globalen n
besser (und ausreichend) min max avg
(Wurde implementiert aber durch Rollback vernichtet)
repI.kraftnull
)
uint16_t
schien nichts gebracht zu haben,
obwohl damit direkt die ISR-Auffrequenz sinken sollte.
Ursprünglich war sogar die Triggerung in Software implementiert,
ohne die umständliche Wurstelei mit dem Hardware-Trigger
und den umschaltenden ISRs.
Sogar mit parallel synchron laufenden ADCs, wobei ADC1 den Messschieber,
das Handpoti und die Temperatur abgefragt hat
und ADC2 (nur) den Kraftsensor.
Aber das klappte damals ebenfalls überhaupt nicht,
gleiches Problem: Zu wenig Rechenzeit.
Alles extrem fraglich.
usb::send()
liefert false
.
Erforderliches Verhalten für Folge-Transfers (> 63 Byte) aus mehreren Paketen.
usb::send()
liefert true
:
Bulk-Paket wurde von (nicht laufender) WebUSB-Äpp nicht abgeholt,
der frisch gestarteten WebUSB-Äpp keine alten Daten präsentieren.
RepI
an, wobei bei WebUSB alles ganz sauber funktioniert.
Während letzteres Problem sich schließlich von selbst löste, war das mit dem Oszillogramm wie verhext:
Ursache war ein großzügig angesetztes bInterval im Endpoint-Deskriptor:
bInterval ist auch für das Inter-Paket-Intervall (eines längeren Transfers) zuständig!!
Dadurch brach usb::send()
mit dem 2-ms-Timeout ab, und usbhid.sys
ignoriert schweigsam dieses Problem.
Ein aggresives bInterval = 1 erschlägt das Problem, aber die Moral von der Geschicht':
Über den HID-Interrupt-Endpoint übertrage man gefälligst keine Oszilloskop-Tracedaten!
Wenn schon HID dann sollte das via getFeature() erfolgen.
Irgendwie wird der Kraftsensor überhaupt nicht mehr ausgereizt, die maximal erreichbaren Kraftwerte
liegen bei 4 kN (400 kp).
Also nur bei 10 % vom Endwert.
Dabei ist der gesamte Kraftstoß etwa 1 ms breit, während sich die Potimechanik rund um das Wegmesssystem
eher 4 ms Zeit lässt, bei straffer Bremse.
Ob das jetzt die gebremste Masse oder aber die Seilkonstruktion um die Potiachse ist
lässt sich nicht ohne weiteres feststellen.
Mit einer High-Speed-Kamera vielleicht.
Der ermittelte Kraftstoß liegt so bei Werten um 20 (nach der Division mit dem 1-ms-Integrationsintervall).
Und die Maximalkraft bei reichlich 100.
Wohlgemerkt, bei einem 12-Bit-A/D-Wandler.
Alles in allem eine schlechte Dimensionierung.
Aber für's Prinzip reicht es jetzt:
Es kommen für unterschiedlich starke Schläge unterschiedlich starke Impulse, Kraft-Maxima und Verschiebewege.
Die AC-Triggerung ist hinreichend empfindlich.
Das Ergebnis bei poll()
kommt sehr schnell, innerhalb von 30 ms.
Das ist IMHO der größte Vorteil der Umgestaltung.
Weiterhin stimmt der Weg-Wert immer (weil Absolutwertgeber), der Messschieber springt gelegentlich.
Werte vom Messschieber werden von der DLL nicht mehr geliefert.
Die DLL braucht das Oszillogramm nicht (wie bisher) auszuwerten, das macht jetzt die Firmware.
Die Firmware ist nunmehr als ein bis-zu-4-Kanal-Oszilloskop ausgelegt.
Die Triggerquellenumschaltung funktioniert jedoch nicht so recht.
Die Voreinstellung in der Firmware ist 2 Kanäle (ADC8+ADC9), 4 ms Samplezeit, Triggerhysterese +50,
Triggerquelle ADC8, AC-Kopplung, kurzer Prätrigger.
Die Voreinstellung wird nach Reset wirksam, daher hilft beim irrtümlichen Verstellen das Ab- und Anstecken.
Allerdings kann man nicht ohne Neuübersetzung andere Voreinstellungen
(bei geänderten Aufbauparametern, bspw. mehr Rauschen, was mehr Hysterese erfordert) festlegen.
Mit den 4 ms ist der RAM des STM32F103 nahezu erschöpft, das sind knapp 2 K Samples pro Kanal.
Für mehr Zeit müsste man die Summenabtastrate heruntersetzen.
Strukturell steht eine DMA-Pufferumschaltung bei Triggerereignis an.
Das erspart die murksig erscheinenden memcpy()
-Aufrufe.
Für die nächste Firmware-Überarbeitung …