In diesem Artikel werde ich Ihnen sagen, wie:- Erstellen Sie ein Projekt in STM32CubeMX und richten Sie Timer für die Erfassung externer Signale ein.
- Dekodieren Sie ein PPM-Signal von einer Flugzeugmodellkonsole.
- Erstellen Sie ein Human Interface Device auf STM32 und schreiben Sie Ihren HID Report Descriptor.
- Fliegen Sie in einem Simulator auf einem Renn-Quadrocopter. :) :)
Vorwort
In letzter Zeit werden FPV-Rennen auf Quadrocoptern der 250. Klasse (FPV - First Person View) immer beliebter. Die Zahl 250 bedeutet den Abstand zwischen den Achsen der Motoren diagonal, typisch für kleine wendige Hubschrauber. Solche Geräte basieren auf langlebigen Carbonrahmen, die Stürzen und Kollisionen standhalten. Propeller mit einem Durchmesser von 5 bis 6 Zoll mit einer großen Stufe (Neigungswinkel der Blätter) werden für einen dynamischsten Flug auf leistungsstarke Motoren gesetzt. Das Bild vom analogen Steuerkurs-Camcorder wird mit einer Frequenz von 5,8 GHz an den Monitor oder die Videobrille des Piloten übertragen. Da die digitale Übertragung über WLAN eine lange Verzögerung (200-300 ms) verursacht, wird das Video immer auf einem analogen Kanal übertragen. Um spektakuläre Clips aufzunehmen, sind Action-Kameras an Bord (GoPro, Mobius, SJcam, Xiaomi Yi usw.).Hier sind einige spannende Videos über FPV-Copter:
Bevor ich meinen Mini-Quadrocopter baute, wollte ich in einem Simulator fliegen und sehen, ob ich an FPV-Rennen interessiert wäre. Für das Training ist der FPV FreeRider- Simulator gut geeignet . Es ist kostengünstig, hat eine kostenlose Demoversion und ahmt nach Ansicht erfahrener Piloten die wahre Flugmechanik sehr genau nach.Sie können das Flugzeug im Simulator über die Tastatur oder den Joystick steuern. Die Tastatur ist für die Steuerung schlecht geeignet, da die Tasten nur einen diskreten Wert übertragen können (die Taste wird gedrückt / nicht gedrückt) und es unmöglich ist, gleichmäßig variierende Zwischenwerte zu übertragen. Joysticks von Spielekonsolen mit analogen Sticks sind viel besser, aber sie haben einen sehr kleinen Stick, mit dem Sie das Gerät nicht genau genug steuern können. Eine ideale Option für einen Simulator ist eine Flugzeugmodellkonsole, die über einen speziellen Adapter mit einem Computer verbunden ist, dank dessen das Betriebssystem sie als Joystick betrachtet.Ich hatte bereits einen Quadrocopter, der für gemächliche Flüge und Fotografie zusammengestellt war, aber zu groß und schwer für Rennen. Dementsprechend gab es eine Fernbedienung - Turnigy 9X (in der ersten Abbildung). Auf der Rückseite befindet sich ein Anschluss zum Anschließen eines Adapters, an den ein PPM-Signal ausgegeben wird. Dieses Signal ist ein kurzer Impuls mit Intervallen von 1 bis 2 Millisekunden, deren Dauer der Position der Bedienelemente entspricht (mehr dazu im Abschnitt zur Decodierung).Ich muss sagen, dass die Adapter zum Anschließen der Fernbedienung von PPM an USB schon lange freigegeben sind und mit Macht und Kraft verkauft werden. Ein ähnlicher Adapter im Formfaktor des Flash-Laufwerks kann in China für 5 USD oder in russischen Geschäften etwas teurer gekauft werden. Es gibt auch Open-Source- Adapterprojekte auf AVR-Controllern.Aber am späten Abend, als alle Moskauer Flugzeugmodellläden bereits geschlossen waren, kam mir sofort der akute Wunsch zu fliegen. Ich wollte morgens nicht warten, es gab keine Zeit, die Platine mit ATmega zu vergiften und zu löten, und so entschied ich mich, einen PPM-USB-Adapter auf der STM32F3Discovery-Platine herzustellen, die lange Zeit im Leerlauf war und gerade zur Hand war.Was ist notwendig
Um einen Adapter herzustellen, benötigen Sie:Discovery-Debug-Boards sind ziemlich teuer. Der beschriebene F3 kostet ungefähr 20 US-Dollar und seine Funktionen sind für ein so einfaches Projekt überflüssig. Ich habe es benutzt, weil dies zum Zeitpunkt des Schreibens das einzige Board mit Hardware-USB war, das ich zu Hause gefunden habe. Für diejenigen, die es noch nicht gekauft haben, kann ich Ihnen raten, auf Miniaturkarten mit dem STM32F103C8T6- Controller von AliExpress für 3 US-Dollar und dem ST-Link-Programmierer von dort zu achten . Der Prozess unterscheidet sich nicht von dem im Artikel beschriebenen. Wenn zu Beginn kein anderer Regler ausgewählt werden muss, weisen Sie auf das Vorhandensein eines Quarzresonators hin und verwenden Sie eine etwas andere Pinbelegung.Erstellen eines Projekts in STM32CubeMX
STM32Cube ist ein Paket, das von STMicroelectronics entwickelt wurde, um STM32-Geräteentwicklern das Leben zu erleichtern. Es besteht aus dem CubeMX-Grafikdienstprogramm, HAL-Treibern und Middleware-Komponenten.CubeMX ist ein Tool zum Erstellen von Projekten und Initialisieren von Peripheriegeräten. Wählen Sie einfach die Steuerung aus, aktivieren Sie die Kontrollkästchen für die erforderlichen Module, wählen Sie die erforderlichen Modi im Menü aus und geben Sie die gewünschten Werte in mehrere Felder ein. CubeMX generiert das Projekt und verbindet die erforderlichen Bibliotheken damit. Der Entwickler des Geräts schreibt nur die Logik der Anwendung.HAL-Fahrer(Hardware Abstraction Layer) sind eine API für die Arbeit mit Modulen und Peripheriegeräten des Mikrocontrollers. Mit HAL können Sie die oberste Ebene der vom Entwickler erstellten Anwendung von der Arbeit mit Registern trennen und den Programmcode zwischen den STM32-Controller-Familien so portabel wie möglich gestalten.Zu den Middlewares oder Zwischenkomponenten gehören das FreeRTOS-Betriebssystem, eine Bibliothek zum Arbeiten mit dem Dateisystem, USB-Bibliotheken, TCP / IP usw.Es scheint, dass es jetzt möglich ist, "mit der Maus zu programmieren", anstatt manuell Bits in Register zu schreiben. Einfachheit und Bequemlichkeit heben jedoch nicht die Tatsache auf, dass Sie die Dokumentation studieren müssen, insbesondere in Fällen, in denen Sie die maximale Geschwindigkeit, den minimalen Stromverbrauch oder die Verwendung von Peripheriegeräten in nicht standardmäßigen Modi reduzieren müssen. STM32Cube deckt noch nicht 100% aller Funktionen des Mikrocontrollers ab, nähert sich jedoch diesem. STMicroelectronics aktualisiert Cube von Zeit zu Zeit, erweitert Funktionen und behebt Fehler. Wenn Sie Cube bereits installiert haben, überprüfen Sie daher, ob es sich um die neueste Version handelt.Grundeinstellungen
Die Arbeit mit dem Projekt beginnt mit der Wahl des Controllers. Starten Sie STM32CubeMX und klicken Sie auf Neues Projekt . Auf der Registerkarte MCU- Auswahl können Sie den gewünschten Controller aus den Filtern auswählen. Da wir ein fertiges Debug-Board haben, finden wir auf der Registerkarte Board Selector STM32F3Discovery . Nach Auswahl einer Karte wird ein Bild des Controllers mit hervorgehobenen und signierten Pins angezeigt.Im oberen Teil des Fensters befinden sich vier große Registerkarten:Pinbelegung - zum Konfigurieren der Pin-Funktionen und zum Voreinstellen von Modulen. Wir sind gerade dabei.Uhrenkonfiguration - Uhreinstellung, PLL, Teiler.Konfiguration - Detailliertere Konfiguration von Peripheriegeräten und Middleware.Stromverbrauchsrechner - Berechnung des vom Mikrocontroller verbrauchten Stroms.
Im linken Menü auf der Registerkarte Pinbelegung können Sie die gewünschten Peripheriegeräte verwenden und auf der Steuerschaltung eine Funktion für einen der Ausgänge des Mikrocontrollers auswählen. Einige Elemente auf der linken Seite sind Warnsymbole. Dies bedeutet, dass die Module (in diesem Fall ADC, DAC, OPAMP2, RTC) jetzt unvollständig verwendet werden können, da einige ihrer Ausgänge bereits von anderen Funktionen belegt sind.Konfigurierte Pins werden in der Steuerschaltung grün hervorgehoben. Da wir uns nicht für einen nackten Controller ohne Umreifung entschieden haben, sondern für eine vorgefertigte F3-Discovery-Debugging-Karte, sind einige der Ausgänge bereits konfiguriert, z. B. ist eine blaue Taste an PA0 und LEDs an PE8 ... 15 angeschlossen. Die Pins, an die einige externe Geräte in Discovery angeschlossen sind, werden orange hervorgehoben, aber die Peripheriemodule für sie wurden noch nicht konfiguriert. Wie Sie sehen können, sind dies Pins für USB, Quarzresonatoren, SPI und I2C für Gyroskop und Kompass, DP und DM für USB. Graue Schlussfolgerungen werden derzeit nicht verwendet, keine davon können wir für unsere Zwecke anwenden.Eingangsauswahl
Wir werden die Dauer der Impulse erfassen, daher muss der Eingang mit einem der Kanäle eines beliebigen Timers verbunden sein. Außerdem beträgt der Signalpegel bei Turnigy 9X nicht 3,3 V wie die Versorgungsspannung des STM32, sondern 5 V. Wir sind zu faul, um den Spannungsteiler zu löten, daher müssen Sie einen Eingang wählen, der 5 V standhält (diese Eingänge werden als 5 V-tolerant bezeichnet). Geeignete Pins finden Sie im Datenblatt zu STM32F303VCT6 im Abschnitt Pinbelegung und Pinbeschreibung . Es gibt viele Timer im STM32F3, die über fast alle Pins verteilt sind. Eine bequeme Option ist PC6. Es kann 5 Volt standhalten und befindet sich in der unteren linken Ecke der Platine neben GND. Weisen Sie diesem Pin den 1. Kanal des 3. Timers TIM3_CH1 zu.Uhreinstellung
Damit der USB funktioniert, muss der Mikrocontroller mit einer sehr stabilen Frequenz getaktet werden, weshalb fast alle USB-Geräte über Quarzresonatoren verfügen. Die Frequenzstabilität des eingebauten RC-Generators reicht für USB nicht aus. Aber auf dem STM32F3 Discovery Board waren Entwickler aus irgendeinem Grund gierig und haben keinen Quarz eingesetzt. Wenn Sie jedoch die Schaltung sorgfältig untersuchen , können Sie feststellen, dass das MCO- Signal an den Eingang PF0-OSC_IN angeschlossen ist , an den der Quarz angeschlossen werden soll . Es kommt vom ST-Link-Programmierer auf derselben Platine, auf der sich Quarz befindet. Das Benutzerhandbuch für F3 Discovery (UM1570) im Abschnitt OSC Clock besagt, dass 8 MHz an diese Leitung gesendet werden.
Somit wird der Mikrocontroller von einer externen Quelle getaktet. Dieser Modus wird als Bypass bezeichnet. Wählen Sie im Peripherie-Einstellungsmenü im Abschnitt RCC zum Takten der Hochgeschwindigkeitsuhr die Option BYPASS-Taktquelle aus .
Bevor wir zu einer detaillierteren Uhreinstellung übergehen, stellen wir im Peripheriemenü fest, dass der Mikrocontroller als USB-Gerät fungiert.
Jetzt können Sie zur nächsten großen Registerkarte wechseln - Uhrenkonfiguration . Hier sehen wir ein riesiges Diagramm, das zeigt, welche Taktsignale im Mikrocontroller vorhanden sind, woher sie kommen, wie sie sich verzweigen, multiplizieren und teilen. In gelb habe ich die Parameter hervorgehoben, die beachtet werden sollten.
Überprüfen Sie die EingangsfrequenzDie Eingangsfrequenz beträgt 8 MHz.Wir stellen den PLL Source Mux- Schalter auf HSE (High Speed External), um von einer externen Quelle anstatt von einer internen Quelle zu takten.PLL - Phase Lock Loop oder PLL - Phase Locked Loop dient dazu, die externe Frequenz mehrmals zu multiplizieren. Stellen Sie den PLLMul- Multiplikator auf 9. Dann erreichen wir die maximal mögliche Frequenz für den STM32F303 - 72 MHz.Der System Clock Mux muss sich in der PLLCLK- Position befinden, damit die Taktfrequenz mit der PLL multipliziert wird.Für das USB-Modul werden 48 MHz benötigt. Stellen Sie daher einen 1,5-Teiler vor USB.Achten Sie auf die FrequenzDer APB1-Timer taktet auf der linken Seite der Schaltung. Es geht an die Timer und ist für uns in Zukunft nützlich.Wenn eine Frequenz falsch konfiguriert ist, den maximal möglichen Wert überschreitet oder sich die Schalter in einer ungültigen Position befinden, hebt CubeMX diese Stelle rot hervor.Timer-Einstellung
Um die Pulsdauer zu messen, starten wir den TIM3-Timer im Input Capture-Modus. Im Referenzhandbuch finden Sie im Abschnitt Allzweck-Timer (TIM2 / TIM3 / TIM4) ein Diagramm, das die Funktionsweise von Timern veranschaulicht. Mit Farben habe ich die im Input Capture-Modus verwendeten Signale und Register hervorgehoben.
Das grün hervorgehobene Taktsignal tritt kontinuierlich in das CNT-Zählerregister ein und erhöht seinen Wert bei jedem Taktzyklus um 1. Im Prescaler PSC- Teiler kann die Taktfrequenz für eine langsamere Zählung abnehmen.Ein externes Signal wird in TIMx_CH1 eingegeben . KantendetektorEs erkennt Flanken des Eingangssignals - Übergänge von 0 nach 1 oder von 1 nach 0. Bei der Registrierung einer Front werden zwei gelb hervorgehobene Befehle ausgegeben:- Ein Befehl zum Schreiben des Werts des CNT- Zählers in das Register Register Capture / Compare 1 (CCR1) und Aufruf des CC1I- Interrupts .- Ein Befehl für den Slave-Modus-Controller , mit dem der CNT- Wert auf 0 zurückgesetzt wird und der Countdown erneut startet.Hier ist eine Illustration des Prozesses in einer Zeitleiste:
Wenn ein Interrupt auftritt, führen wir Aktionen mit dem erfassten Wert aus. Wenn die Eingangsimpulse zu oft auftreten und die im Interrupt-Handler auftretenden Aktionen zu lang sind, kann der Wert von CCR1 überschrieben werden, bevor der vorherige gelesen wird. In diesem Fall müssen Sie das Overcapture-Flag aktivieren oder DMA (Direct Memory Access) anwenden, wenn die Daten von CCR1 das vorbereitete Array automatisch in den Speicher füllen. In unserem Fall hat der kürzeste Impuls eine Dauer von 1 Millisekunde, und der Interrupt-Handler ist einfach und kurz. Machen Sie sich also keine Sorgen über das Überschreiben. Kehren Sie zurRegisterkarte Pinbelegung zurück und stellen Sie den TIM3- Timer im Menü Peripheriegeräte ein .
Slave-Modus: Reset-Modus- bedeutet, dass in einem bestimmten Fall der Timer auf 0 zurückgesetzt wird.Triggerquelle: TI1FP1 - Das Ereignis, das zum Zurücksetzen und Starten des Timers verwendet wird, ist die vom TI1-Eingang erfasste Signalflanke.ClockSource: Interne Uhr - Der Timer wird vom internen Generator des Mikrocontrollers getaktet.Kanal 1: Input Capture Direct-Modus - Erfassungsintervalle vom 1. Kanal im CCR1-Register.Auf der nächsten großen Registerkarte " Konfiguration" nehmen wir zusätzliche Timer-Einstellungen vor.Prescaler ist ein Timer-Teiler. Wenn es 0 ist, wird die Frequenz direkt vom Taktbus APB Takt - 72 MHz genommen. Wenn der Vorteiler 1 ist, wird die Frequenz durch 2 geteilt und wird zu 36 MHz. Stellen Sie den Divisor auf 71 ein, so dass die Frequenz durch 72 geteilt wird. Dann beträgt die Timerfrequenz 1 MHz und die Intervalle werden mit einer Auflösung von 1 Mikrosekunde gemessen.Zähler Perioden - stellen Sie den maximal möglichen 16-Bit - Wert 0xFFFF. Die Periode ist wichtig für die Erzeugung von Zeitschlitzen, beispielsweise für PWM. Die Periode ist jedoch für die Erfassung von Signalen nicht wichtig, wir werden bekannt geben, dass sie für alle Eingangsimpulse groß ist.Polaritätsauswahl: Fallende Flanke - Timer-Werte werden an der fallenden Flanke des Eingangssignals erfasst. Setzen Sie auf derRegisterkarte NVIC-Einstellungen eine MorgendämmerungGlobaler TIM3-Interrupt , sodass Ereignisse, die sich auf den 3. Timer beziehen, einen Interrupt erzeugen.USB-Geräte-Setup
Wir haben bereits festgestellt, dass der Controller ein USB-Gerät sein wird. Da der Joystick zur Klasse der HID-Geräte gehört , wählen Sie im Menü Middlewares -> USB_DEVICE die Option Class For FS IP: Human Interface Device Class (HID) . Anschließend verbindet CubeMX Bibliotheken für das HID-Gerät mit dem Projekt.
Gehen wir zu den USB_DEVICE- Einstellungen auf der Registerkarte Konfiguration im Abschnitt Middlewares :Hersteller-ID und Produkt-ID sind zwei 16-Bit-Kennungen, die für jedes Modell eines USB-Geräts eindeutig sind. VID entspricht dem Gerätehersteller, und jeder Hersteller weist PID zu, basierend auf seinen eigenen Überlegungen. Ich konnte die offizielle Liste von VID und PID nicht finden, sondern nur die von Enthusiasten unterstützte Identifikatorbasis . Um Ihre eigene Vendor ID zu erhalten, müssen Sie das USB Implementers Forum auf usb.org besuchen und ein paar tausend Dollar bezahlen. Kleine Unternehmen oder Open-Source-Entwickler, die sich ihre VID nicht leisten können, können einen USB-Chip-Hersteller anfordern und offiziell ein VID / PID-Paar für ihr Projekt erhalten. Ein solcher Service wird beispielsweise von FTDI oder Silicon Laboratories angeboten.Wenn Sie zwei Geräte mit derselben VID / PID verbinden, jedoch von einem anderen Typ (z. B. eines ist ein HID-Gerät und das andere ist Massenspeicher), versucht das Betriebssystem, denselben Treiber für sie und mindestens eines davon zu installieren wird nicht funktionieren. Aus diesem Grund müssen VID / PID-Paare für verschiedene Gerätemodelle eindeutig sein.Da wir kein Gerät für uns selbst herstellen, werden wir es nicht verkaufen und vertreiben. Wir werden die VID 0x0483, die STMicroelectronics entspricht, verlassen und unsere eigene PID entwickeln. Standardmäßig bietet CubeMX die PID 0x5710 für das HID-Gerät an. Ersetzen Sie es beispielsweise durch 0x57FF.Ersetzen Sie die Produktzeichenfolge durch den STM32 PPM-USB-Adapter. Dieser Name wird in der Liste der Geräte in der Windows-Systemsteuerung angezeigt. Wir werden die Seriennummer (S \ N) noch nicht ändern.Wenn Windows erkennt, dass es ein Gerät mit einer Kombination aus VID, PID und S \ N erkennt, die es zuvor noch nicht gesehen hat, installiert das System den entsprechenden Treiber dafür. Wenn die Kombination aus VID, PID und S \ N bereits verwendet wurde, ersetzt Windows automatisch den zuvor verwendeten Treiber. Sie können dies beispielsweise sehen, wenn Sie das Flash-Laufwerk an USB anschließen. Das erste Anschließen und Installieren dauert einige Zeit. Bei nachfolgenden Verbindungen beginnt das Laufwerk fast sofort zu arbeiten. Wenn Sie jedoch eine andere Instanz des Flash-Laufwerks desselben Modells mit einer anderen Seriennummer anschließen, installiert das System einen neuen Treiber dafür, obwohl es dieselbe VID und PID hat.Ich werde erklären, warum dies wichtig ist. Wenn Sie auf Ihrem STM32 eine USB-Maus mit Ihrer VID, PID und S \ N erstellt, diese an den Computer angeschlossen und dann einen USB-Joystick erstellt haben, ohne die VID, PID und S \ N zu ändern, erkennt Windows das neue Gerät als eine Maus, die bereits vorhanden ist wird im System verwendet und installiert den Joystick-Treiber nicht. Dementsprechend funktioniert der Joystick nicht. Wenn Sie den Typ Ihres Geräts ändern möchten und die VID / PID unverändert lassen möchten, müssen Sie daher die Seriennummer ändern.Projektgenerierung für IDE
Die letzten Einstellungen, die Sie vornehmen müssen, sind die Einstellungen für die Projektgenerierung. Dies geschieht über Projekt -> Einstellungen ... Dort legen wir den Namen, den Zielordner und die gewünschte IDE fest, unter der CubeMX das Projekt erstellt. Ich wählte MDK-ARM V5 , weil ich Keil uVision verwende 5. Auf dem Code Generator Registerkarte können Sie das überprüfen Kopieren Sie nur die erforderlichen Bibliotheksdateien Checkbox , um das Projekt nicht mit unnötigen Dateien zu vollstopfen.Klicken Sie auf die Schaltfläche Projekt -> Code generieren . CubeMX erstellt ein Projekt mit Code, der in Keil uVision geöffnet und ohne zusätzliche Einstellungen kompiliert und geflasht werden kann. In der Datei main.c in der Funktion main (void)Es wurden bereits Funktionen zum Initialisieren von Uhr, Anschlüssen, Timer und USB eingefügt. In ihnen werden die Mikrocontrollermodule gemäß den in CubeMX festgelegten Modi konfiguriert.
Im Code finden sich häufig Konstruktionen dieser Art:
(...)
Es wird davon ausgegangen, dass der Benutzer seinen Code in diese Abschnitte einbettet. Wenn die Option Benutzercode beim erneuten Generieren beibehalten in den CubeMX-Projekteinstellungen aktiviert ist , wird der zwischen diesen Zeilen eingeschlossene Code während der sekundären Generierung eines vorhandenen Projekts nicht überschrieben. Leider werden nur von CubeMX erstellte Abschnitte gespeichert. Vom Benutzer erstellte Abschnitte / * USER CODE * / gehen verloren. Wenn Sie nach dem Schreiben des Codes in die IDE zu CubeMX zurückkehren und das Projekt mit den neuen Einstellungen erneut generieren möchten, empfehle ich daher, eine Sicherungskopie des Projekts zu erstellen.In den Firmware-Einstellungen in uVision ( Flash -> Flash-Tools konfigurieren ) empfehle ich Ihnen, die Option Nach Flash zurücksetzen zu aktivierenso dass der Mikrocontroller sofort nach dem Blinken startet. Standardmäßig ist es deaktiviert und nach jedem Blinken müssen Sie die Reset-Taste auf der Karte drücken.
PPM-Decodierung
PPM - Pulse Position Modulation - eine in der Flugzeugmodellelektronik weit verbreitete Methode zur Codierung übertragener Signale. Es ist eine Folge von Impulsen, deren Zeitintervalle den übertragenen Zahlenwerten entsprechen.Gemäß diesem Protokoll sendet die Konsole Informationen an das sendende Funkmodul, das auf der Rückseite in die Konsole eingesetzt wird. Viele Empfänger, die an Bord des Copters platziert sind, können Steuersignale für den Flugregler über PPM übertragen. Darüber hinaus verfügt fast jede Konsole über Anschlüsse zum Anschließen einer zweiten Konsole im Trainer-Student-Modus und zum Anschließen der Konsole an einen Simulator, der normalerweise auch PPM verwendet.Wir schreiben ein Signal vom Simulatorausgang von Turnigy 9X mit einem Logikanalysator:Jede Sequenz codiert den aktuellen Status der Steuerelemente auf der Fernbedienung. Normalerweise entsprechen die ersten vier Werte (auch Kanäle genannt) der Position der Analogsticks und die nachfolgenden der Position der Kippschalter oder Potentiometer.Die minimale Position der Steuerung entspricht dem Intervall 1000 μs, das Maximum - 2000 μs, die durchschnittliche Position - 1500 μs. Impulsstöße oder Rahmen werden durch wesentlich längere Intervalle getrennt und folgen mit einer Periode von 20–25 ms.Schauen wir uns das Signal genauer an:Wie Sie sehen können, befinden sich drei Stöcke in der neutralen Position (1, 3, 4) und einer in der extremen Position (2). Drei Kippschalter sind ausgeschaltet (5, 6, 7) und der letzte ist eingeschaltet (8). Der als Adapter fungierende Mikrocontroller muss eine solche Sequenz erfassen, die Werte einem Array hinzufügen und als Befehl vom Joystick über USB senden. Schreiben wir einen Pulssequenzdecoder.Unterbrechung erfassen
Starten Sie
nach der Initialisierung in main.c vor der main while-Schleife den TIM3-Timer im Input Capture-Erfassungsmodus von Kanal 1 aus, und generieren Sie Erfassungsunterbrechungen. Verwenden Sie dazu die entsprechende Funktion von HAL:HAL_TIM_IC_Start_IT(&htim3, TIM_CHANNEL_1);
Die htim3 Struktur in deklarierten main.c ist die TIM3 timer - Handler, der alle Variablen enthält Strukturen und mit dem Zeitgeber verbunden ist : Parameter für die Initialisierung, Zeiger auf alle Zeitgeberregister (Zählerwert, Teiler, alle Einstellungen, Interrupt - Flags), Zeiger auf einem Handler-DMA, der mit diesem Timer usw. funktioniert Der Entwickler muss nicht suchen, welche Bits in welchem Register für was verantwortlich sind, und diese manuell setzen und zurücksetzen. Es reicht aus, den Handler an die HAL-Funktion zu übergeben. HAL-Bibliotheken erledigen den Rest selbst.Die Prinzipien der HAL-Struktur werden ausführlicher im Dokument Beschreibung der STM32F3xx-HAL-Treiber beschrieben .(UM1786). Es ist zu beachten, dass die HAL-Bibliotheken selbst gut dokumentiert sind. Um zu verstehen, wie die HAL für den Timer funktioniert und wie sie verwendet wird, können Sie die Kommentare in den Dateien stm32f3xx_hal_tim.h und stm32f3xx_hal_tim.c lesen .Für jeden Interrupt, den der TIM3-Timer generiert, wird der TIM3_IRQHandler- Handler aufgerufen . Es befindet sich in der Datei stm32f3xx_it.c. Daraufhin wird der für alle Timer standardmäßige HAL_TIM_IRQHandler- Handler aufgerufen und ein Zeiger auf die htim3-Struktur an ihn übergeben.void TIM3_IRQHandler(void)
{
HAL_TIM_IRQHandler(&htim3);
}
Wenn wir in die HAL_TIM_IRQHandler- Datei stm32f3xx_hal_tim.c schauen , sehen wir einen riesigen Handler, der die Interrupt-Flags auf Timer überprüft, eine Rückruffunktion verursacht und das Flag nach der Ausführung löscht. Wenn ein Erfassungsereignis auftritt , ruft es die Funktion HAL_TIM_IC_CaptureCallback auf . Es sieht aus wie das:__weak void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
}
Dies bedeutet, dass wir diese Funktion in main.c überschreiben können . Fügen Sie daher diesen Rückruf vor der Funktion int main (void) ein :void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
};
Ich würde gerne sehen, wie der Interrupt ausgeführt wird. Fügen Sie dazu ein kurzes Ein- und Ausschalten einer der Schlussfolgerungen hinzu:void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
HAL_GPIO_WritePin(GPIOE, GPIO_PIN_8, GPIO_PIN_SET);
__nop();__nop();__nop();__nop();__nop();__nop();
HAL_GPIO_WritePin(GPIOE, GPIO_PIN_8, GPIO_PIN_RESET);
};
Der PE8-Pin wurde bereits als Ausgang initialisiert. Zwischen dem Ein- und Ausschalten werden __nop () - Anweisungen eingefügt , die eine Verzögerung von 1 Taktzyklus bilden. Dies geschieht, damit mein chinesischer 8-Dollar-Logikanalysator, der mit 24 MHz arbeitet, keinen zu kurzen Impuls von einem 72-MHz-Mikrocontroller verpasst. Kompilieren Sie nun das Projekt Projekt -> Ziel erstellen und fragen Sie den Flash- Controller -> Download . Wir werden das PPM von der Fernbedienung an PC6 anschließen und sehen, was mit dem Analysegerät auf PC6 und PE8 passiert.Rückruf wird wirklich im richtigen Moment aufgerufen - direkt nachdem das Eingangssignal von 1 auf 0 übergegangen ist. Also wurde alles richtig gemacht.Erfassen und verarbeiten Sie erfasste Daten
Wir werden den Rückruf so bearbeiten, dass jeder erfasste Wert unverändert zum erfassten_Wert- Puffer hinzugefügt wird . Wenn der Timer einen sehr großen Wert erfasst (mehr als 5000 μs), bedeutet dies, dass eine Pause aufgezeichnet wurde, das Paket vollständig empfangen wurde und verarbeitet werden kann. Die verarbeiteten Werte werden dem Array rc_data mit 5 Elementen hinzugefügt . In den ersten vier werden die Steuerknüppelpositionen auf den Bereich [0; 1000], im fünften werden einzelne Bits gemäß den Kippschaltern gesetzt, die als Drücken von Tasten auf dem Gamepad interpretiert werden.uint16_t captured_value[8] = {0};
uint16_t rc_data[5] = {0};
uint8_t pointer = 0;
uint8_t data_ready = 0;
...
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
uint8_t i;
uint16_t temp;
temp = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1);
if ((temp > 5000) && (!data_ready))
{
pointer = 0;
for (i = 0; i < 4; i++)
{
if (captured_value[i] < 1000)
captured_value[i] = 1000;
else if (captured_value[i] > 2000)
captured_value[i] = 2000;
rc_data[i] = captured_value[i]-1000;
};
rc_data[4] = 0;
if (captured_value[4] > 1500)
rc_data[4] |= (1<<4);
if (captured_value[5] > 1500)
rc_data[4] |= (1<<5);
if (captured_value[6] > 1500)
rc_data[4] |= (1<<6);
if (captured_value[7] > 1500)
rc_data[4] |= (1<<7);
data_ready = 1;
}
else
{
captured_value[pointer] = temp;
pointer++;
};
if (pointer == 8)
pointer = 0;
}
Lassen Sie mich erklären, warum ich die den Schaltflächen entsprechenden Bits nicht in den unteren 4 Bits, sondern in den fünften bis achten Bits platziert habe. Im Simulator soll ein Gamepad von der Xbox angeschlossen werden, auf dem die Tasten LB, RB, Start und Back verwendet werden. Die Nummern 5 bis 8.In der Hauptschleife wird das Flag data_ready kontinuierlich gedreht, wodurch Daten an den Computer gesendet werden.while (1)
{
if (data_ready)
{
data_ready = 0;
}
}
Um zu überprüfen, wie dies funktioniert, schließen Sie die Fernbedienung an, kompilieren und flashen Sie sie erneut und starten Sie dann das Debuggen von Debug -> Debug-Sitzung starten / stoppen .Öffnen Sie das Fenster zum Verfolgen von Variablen Ansicht -> Windows überwachen -> 1 überwachen und fügen Sie dort den erfassten Wert und die rc_data hinzu .Wir starten das Debuggen mit dem Befehl Debug -> Ausführen und in Echtzeit, ohne auch nur Haltepunkte hinzuzufügen, werden wir sehen, wie sich die Zahlen nach den Bewegungen der Sticks ändern.Als Nächstes müssen Sie Daten in Form eines Joystick-Befehls an den Computer senden.Konfigurieren Sie das HID-Gerät und erstellen Sie den HID-Berichtsdeskriptor
USB HID (Human Interface Device) ist eine Klasse von Geräten für die Mensch-Computer-Interaktion. Dazu gehören Tastaturen, Mäuse, Joysticks, Gamepads und Touchpanels. Der Hauptvorteil von HID-Geräten besteht darin, dass sie in keinem Betriebssystem spezielle Treiber benötigen: Windows, OS X, Android und sogar iOS (über den USB-Lightning-Adapter). Eine ausführliche Beschreibung finden Sie im Dokument Geräteklassendefinition für HID . Das Wichtigste, was wir wissen müssen, um einen PPM-USB-Adapter zu erstellen, ist, was HID Report und HID Report Descriptor sind .Das HID-Gerät sendet Bytepakete in einem vordefinierten Format an den Computer. Jedes dieser Pakete ist ein HID-Bericht. Das Gerät informiert den Computer über das Datenformat, wenn es eine Verbindung herstellt, und sendet einen HID-Berichtsdeskriptor, eine Paketbeschreibung, die angibt, wie viele Bytes das Paket enthält und welchen Zweck jedes Byte und Bit im Paket hat. Beispielsweise besteht der HID-Bericht einer einfachen Maus aus vier Bytes: Das erste Byte enthält Informationen zu den gedrückten Tasten, das zweite und dritte Byte enthalten die relative Bewegung des Cursors entlang X und Y und das vierte Byte enthält die Drehung des Scrollrads. Der Berichtsdeskriptor wird im Gerätesteuerungsspeicher als Array von Bytes gespeichert.Bevor ich einen Deskriptor erstelle, möchte ich mich separat mit der Terminologie befassen. In der englischsprachigen Umgebung sind zwei Begriffe üblich - Joystick und Gamepad . Das Wort Joystick wird normalerweise als Manipulator bezeichnet, der mit einer Hand gehalten und in verschiedene Richtungen geneigt wird. Das Gamepad ist ein Gerät mit Tasten und Stöcken, das mit zwei Händen gehalten wird. Russisch sprechende Benutzer nennen den Joystick normalerweise sowohl diesen als auch einen anderen. In der Beschreibung des HID-Geräts gibt es einen Unterschied zwischen dem Joystick und dem Gamepad. Die Flugzeugmodellkonsole ähnelt in ihrem funktionalen Zweck eher einem Gamepad, daher werde ich in Zukunft manchmal den Begriff „Gamepad“ verwenden.Wir haben ein Projekt erstellt, das angibt, dass das Gerät als Human Interface Device fungiert. Dies bedeutet, dass eine USB-HID-Bibliothek mit dem Projekt verbunden ist und bereits ein Geräte-Deskriptor generiert wurde. Es befindet sich in der Datei usbd_hid.c , beschreibt den Mausbericht und sieht folgendermaßen aus:HID_Mouse_Report_Descriptor__ALIGN_BEGIN static uint8_t HID_MOUSE_ReportDesc[HID_MOUSE_REPORT_DESC_SIZE] __ALIGN_END =
{
0x05, 0x01,
0x09, 0x02,
0xA1, 0x01,
0x09, 0x01,
0xA1, 0x00,
0x05, 0x09,
0x19, 0x01,
0x29, 0x03,
0x15, 0x00,
0x25, 0x01,
0x95, 0x03,
0x75, 0x01,
0x81, 0x02,
0x95, 0x01,
0x75, 0x05,
0x81, 0x01,
0x05, 0x01,
0x09, 0x30,
0x09, 0x31,
0x09, 0x38,
0x15, 0x81,
0x25, 0x7F,
0x75, 0x08,
0x95, 0x03,
0x81, 0x06,
0xC0, 0x09,
0x3c, 0x05,
0xff, 0x09,
0x01, 0x15,
0x00, 0x25,
0x01, 0x75,
0x01, 0x95,
0x02, 0xb1,
0x22, 0x75,
0x06, 0x95,
0x01, 0xb1,
0x01, 0xc0
};
Das manuelle Erstellen des HID-Berichtsdeskriptors ist äußerst zeitaufwändig. Um die Aufgabe zu erleichtern, gibt es ein Tool namens HID Descriptor Tool (DT). Dieses Programm kann einen Deskriptor für Ihr Gerät erstellen. Im Archiv finden Sie einige Beispiele für Deskriptoren für verschiedene Geräte.Hier ist ein sehr guter Artikel über das Erstellen eines eigenen HID-Deskriptors für Maus und Tastatur (auf Englisch). Ich werde Ihnen auf Russisch sagen, wie man einen Griff für ein Gamepad macht.Der von der Konsole gesendete HID-Bericht muss vier 16-Bit-Werte für zwei Achsen von Analogsticks und 16 Ein-Bit-Werte für Tasten enthalten. Insgesamt 10 Bytes. Das in DT erstellte Handle sieht folgendermaßen aus: 0x05, 0x01,
0x09, 0x05,
0xa1, 0x01,
0x09, 0x01,
0xa1, 0x00,
0x09, 0x30,
0x09, 0x31,
0x15, 0x00,
0x26, 0xe8, 0x03,
0x75, 0x10,
0x95, 0x02,
0x81, 0x02,
0xc0,
0xa1, 0x00,
0x09, 0x33,
0x09, 0x34,
0x15, 0x00,
0x26, 0xe8, 0x03,
0x75, 0x10,
0x95, 0x02,
0x81, 0x02,
0xc0,
0x05, 0x09,
0x19, 0x01,
0x29, 0x10,
0x15, 0x00,
0x25, 0x01,
0x75, 0x01,
0x95, 0x10,
0x81, 0x02,
0xc0
Es sieht nicht weniger einschüchternd aus als ein Mausdeskriptor. Wenn Sie jedoch verstehen, was jede Zeile bedeutet, ist alles verständlich und logisch.USAGE zeigt, wie das System Daten interpretieren soll, die weiter gehen.Es gibt viele Verwendungsarten, sie sind in Gruppen sortiert - Verwendungsseiten. Um eine bestimmte Verwendung auszuwählen, müssen Sie daher zunächst auf die entsprechende USAGE_PAGE verweisen. Informationen zur Verwendung finden Sie im Dokument Versteckte Verwendungstabellen . Ganz am Anfang des Deskriptors geben wir an, dass der Joystick beschrieben wird:USAGE_PAGE (generischer Desktop)
USAGE (Game Pad)
COLLECTION kombiniert mehrere verwandte Datensätze.Die physische Sammlung wird für Daten verwendet, die sich auf einen bestimmten geometrischen Punkt beziehen, z. B. einen analogen Stick. Die Anwendungssammlung wird verwendet, um verschiedene Funktionen in einem Gerät zu kombinieren. Beispielsweise kann eine Tastatur mit einem integrierten Trackpad zwei Anwendungssammlungen haben. Wir beschreiben nur den Joystick, was bedeutet, dass die Sammlung eine sein wird:COLLECTION (Anwendung)
...
END_COLLECTION
Danach müssen Sie angeben, dass die Elemente, die die Koordinaten übertragen, beschrieben werden. Der Verwendungszeiger beschreibt Mäuse, Joysticks, Gamepads und Digitalisierer:VERWENDUNG (Zeiger)
Im Folgenden finden Sie Beschreibungen von analogen Sticks, die in einer Sammlung zusammengefasst sind:SAMMLUNG (physisch)
USAGE (X)
USAGE (Y)
LOGICAL_MINIMUM (0)
LOGICAL_MAXIMUM (1000)
REPORT_SIZE (16)
REPORT_COUNT (2)
INPUT (Data,Var,Abs)
END_COLLECTION
USAGE gibt hier an, dass die Abweichungswerte entlang der beiden Achsen X und Yverwendet werden . LOGICAL_MINIMUM und LOGICAL_MAXIMUM geben an, inwieweit der übertragene Wert variieren kann.REPORT_COUNT und REPORT_SIZE legen jeweils fest, wie viele Zahlen und welche Größe übertragen werden sollen, nämlich zwei 16-Bit-Zahlen.INPUT (Data, Var, Abs) bedeutet, dass die Daten vom Gerät zum Computer gelangen und sich ändern können. Die Werte in unserem Fall sind absolut. Beispielsweise kommen relative Werte von der Maus, um den Cursor zu bewegen. Manchmal werden Daten als Const beschrieben, nicht als Var. Dies ist notwendig, um nicht signifikante Bits zu übertragen. Beispielsweise werden in einem Mausbericht mit drei Schaltflächen 3 Bit Var für Schaltflächen und 5 Bit Const übertragen, um die Übertragungsgröße auf ein Byte zu ergänzen.Wie Sie sehen können, sind die Beschreibungen der X- und Y-Achse zusammengefasst. Sie haben die gleiche Größe, die gleichen Grenzen. Der gleiche analoge Stick könnte wie folgt beschrieben werden, wobei jede Achse einzeln beschrieben wird. Ein solcher Deskriptor funktioniert ähnlich wie der vorherige:SAMMLUNG (physisch)
VERWENDUNG (X)
LOGICAL_MINIMUM (0)
LOGICAL_MAXIMUM (1000)
REPORT_SIZE (16)
REPORT_COUNT (1)
EINGABE (Daten, Var, Abs)
VERWENDUNG (Y)
LOGICAL_MINIMUM (0)
LOGICAL_MAXIMUM (1000)
REPORT_SIZE (16)
REPORT_COUNT (1)
EINGABE (Daten, Var, Abs)
END_COLLECTION
Nach dem ersten Stick wird der zweite analoge Stick beschrieben. Seine Achsen haben eine andere Verwendung, so dass Sie sie vom ersten Stick unterscheiden können - Rx und Ry:SAMMLUNG (physisch)
VERWENDUNG (Rx)
VERWENDUNG (Ry)
LOGICAL_MINIMUM (0)
LOGICAL_MAXIMUM (1000)
REPORT_SIZE (16)
REPORT_COUNT (2)
EINGABE (Daten, Var, Abs)
END_COLLECTION
Nun müssen Sie einige Schaltflächen des Gamepads beschreiben. Dies kann wie folgt erfolgen:USAGE_PAGE (Schaltfläche) USAGE (Schaltfläche
1) USAGE (Schaltfläche
2) USAGE (Schaltfläche
3)
...
USAGE (Schaltfläche 16)
Die umständliche Aufzeichnung von Schaltflächen desselben Typs kann mithilfe des Verwendungsbereichs reduziert werden:USAGE_PAGE (Schaltfläche)
USAGE_MINIMUM (Schaltfläche 1)
USAGE_MAXIMUM (Schaltfläche 16)
Die von den Tasten übertragenen Daten sind 16 Einzelbitwerte, die von 0 bis 1 variieren:LOGICAL_MINIMUM (0)
LOGICAL_MAXIMUM (1)
REPORT_SIZE (1)
REPORT_COUNT (16)
INPUT (Data,Var,Abs)
Die Reihenfolge der Zeilen im Deskriptor ist nicht streng. Beispielsweise können Logical_Minimum und Logical_Maximum vor der Verwendung (Schaltfläche) geschrieben oder die Zeilen Report_Size und Report_Count ausgetauscht werden.Es ist wichtig, dass der Eingabebefehl alle erforderlichen Parameter für die Datenübertragung enthält (Verwendung, Minimum, Maximum, Größe, Anzahl).Wenn der Deskriptor gebildet wird, kann er mit dem Befehl Deskriptor analysieren auf Fehler überprüft werden.Wenn alles in Ordnung ist, exportieren Sie es mit der Erweiterung h. Ersetzen Sie in der Datei usbd_hid.c den Deskriptor durch einen neuen und passen Sie in usbd_hid.h die Größe des Deskriptors HID_MOUSE_REPORT_DESC_SIZE von 74 auf 61 an.Berichte werden mit dem Flag data_ready gesendet . Dafür zumain.c wird die Header-Datei usbd_hid.h einschließen und in der Hauptschleife die Funktion zum Senden von Berichten aufrufen. Das Array rc_data ist vom Typ uint16, daher muss der Zeiger darauf in einen 8-Bit-Typ umgewandelt werden und die Größe 10 anstelle von 5 übergeben.#include "usbd_hid.h"
...
while (1)
{
if (data_ready)
{
USBD_HID_SendReport(&hUsbDeviceFS, (uint8_t*)rc_data, 10);
data_ready = 0;
};
};
Wir kompilieren das Projekt und flashen es erneut.Anschluss und Verwendung
Schließen Sie das USB-Kabel vom ST-LINK-USB-Anschluss wieder an den USER-USB-Anschluss an. Windows erkennt das neue Gerät und installiert den Treiber automatisch. Gehen wir zur Systemsteuerung -> Geräte und Drucker und sehen unser STM32 USB-PPM-Adaptergerät mit einem Gamepad-Symbol.In den Geräteparametern können Sie sehen, wie sich das Kreuz um das Feld bewegt und wie sich die Spalten bewegen, nachdem die Sticks bewegt wurden, und die Schaltflächensymbole leuchten am Kippschalter auf. Eine Kalibrierung ist nicht erforderlich, da die Minimal- und Maximalwerte bereits im Deskriptor festgelegt wurden.Beim Starten von FPV FreeRider sehen wir, wie sich die Sticks auf dem Hauptbildschirm des gezeichneten virtuellen Gamepads gemäß unserer Fernbedienung bewegen. Wenn die Achsen aus irgendeinem Grund nicht richtig zugewiesen sind, können Sie sie im Abschnitt Controller kalibrieren neu konfigurieren .Mit den Kippschaltern, die den Tasten auf der Fernbedienung entsprechen, können Sie den Flugmodus (akrobatisch / stabilisiert) wechseln, die Kameraansicht (vom Brett / vom Boden aus) wechseln, einen Flug von Anfang an starten oder das Rennen für eine Weile einschalten.Geflogen!
Auf dem Video - das Ergebnis mehrerer Tage meines Trainings. Während ich mit Auto-Leveling fliege und nicht im akrobatischen Modus, wie es alle Meister des FPV-Rennsports tun. Wenn Sie im Akro-Modus die Sticks loslassen, kehrt der Copter nicht automatisch in die horizontale Position zurück, sondern fliegt im gleichen Winkel weiter, in dem er geflogen ist. Das Verwalten im Akro-Modus ist viel schwieriger, aber Sie können eine höhere Geschwindigkeit und Manövrierfähigkeit erreichen, Umwälzungen in der Luft vornehmen und sogar verkehrt herum fliegen.Zu den Charpu-MeisternIch bin noch sehr weit weg, aber ich trainiere weiter und ich kann mit Sicherheit sagen, dass mich die Idee eines Renn-Mini-Copter noch mehr interessiert hat. Und bald werde ich definitiv mit dem Bau und den Flügen nicht mehr im Simulator beschäftigt sein, sondern in der harten Realität, mit echten Abstürzen, kaputten Propellern, kaputten Motoren und ausgebrannten Batterien. Aber das ist ein Thema für andere Artikel :)
Das Projekt für Keil uVision 5 und STM32CubeMX ist auf GitHub .