Ein Blick in die Leitung
Sie sind überall. Im Kaffeeautomat. Im Kühlschrank. Am Schlüsselbund. Sie haben viele Größen und Formen. Sie steuern und regeln (fast) alles.
Die Illuminaten? Nein! Mikrocontroller
Als Informatiker pflegt man deren Existenz meist weg zu abstrahieren und konzentriert sich dagegen auf Algorithmen, Frameworks, Softwarearchitektur - und ist entsprechend blind für "low-level" Industriespionage / Reengineering / Hackerangriffe.
Grund genug, einen kurzen Blick nach "ganz unten" zu werfen: Auf die Kommunikation zwischen Systemkomponenten auf Platinenebene.
Die Platine
Als Beispiel haben wir bewusst kein geschlossenes Entwicklungssystem a’la Arduino gewählt. Stattdessen betrachten wir ein auf einem µC aufbauendes Produkt - einen Einplatinen Spektrumanalysator, den man sehr günstig (z.B. bei der netten Amazone) beziehen kann.
Im Folgenden wird diese Variante verwendet:
Die für den Artikel relevanten Komponenten / Schnittstellen der Platine sind:
-
SMA Buchse für die HF Ausgabe (Anschluss Oszilloskop/Spektrumanalysator)
Der Anwendungsfall
Auf dem HF Ausgang soll dauerhaft ein 42MHz Signal anliegen.
Verfahren
-
Der µC kommuniziert via seriellem SPI Protokoll über drei Leitungen mit dem HF Oszillator
-
DATA: Seriell/unidirektional zu übertragende Daten (32bit Worte) in das Schieberegister des Oszillators
-
CLK: Synchronisierungssignal
-
LE: Übernahmesignal
-
Bei "Low"-Pegel: Datenübertragung
-
Bei "High"-Pegel: Übernahme des Wortes aus dem Schieberegister
-
-
Fragestellungen
-
Die Kommunikation soll mit einem Oszilloskop visualisiert und analysiert werden
-
Welche Datenwörter werden übertragen?
-
Kann man Metainformationen aus der Kommunikation ableiten?
-
-
Das Ausgabesignal soll mit einem Oszilloskop und mit einem Spektrumanalysator visualisiert werden
-
Liegt ein HF-Signal an?
-
Hat es die gewünschte Frequenz von 42MHz?
-
Handelt es sich um ein "sauberes" Sinussignal?
-
Aufbau des Mikrocontrollers
Der kleine µC Chip mag unscheinbar aussehen - dessen Aufbau ist allerdings durchaus bemerkenswert. Er besteht aus einer Vielzahl von Hardware-Einzelkomponenten, die via internem Bus miteinander verbunden sind. Das zugehörige Blockdiagramm erspare ich Ihnen an dieser Stelle - bei Interesse lohnt ein Blick in das Datenblatt.
Wichtig für das Verständnis:
-
Eines der Komponenten ist ein ARM CPU Kern
-
Weitere Komponenten dienen u.a. der Kommunikation (SPI, i2c, UART, CAN, USB)
-
Die Komponenten müssen programmatisch via Firmware konfiguiert werden - von "sich aus" macht ein µC nichts
-
Die Pin-Belegung ist dabei Teil dieser Konfiguration - ein Pin kann also mit unterschiedlichen Komponenten verbunden werden
-
Auch die Taktfrequenz ist Teil der Konfiguration (!) (Der µC wird extern mit einem 8MHz Quarz getaktet)
Konfiguration des Mikrocontrollers
Leider hat jeder µC-Hersteller seinen eigenen Satz an - teilweise etwas angestaubten - Software-Werkzeugen. Für 32bit µCs von STMicroelectronics kann für die Konfiguration / Codegenerierung STM32CubeMX verwendet werden.
Mit diesem Werkzeug wird unser µC wie folgt konfiguriert:
Also:
-
SPI LE / CLK / LE liegen an Pins 25 / 26 / 28
-
LEDs D1 und D2 liegen an Pins 19 / 32
-
Der 8 Mhz Quarz liegt an Pins 5 und 6 - via internem PLL wird daraus ein 72MHz Systemtakt erzeugt
-
Die SWD (Debug Interface) Pins liegen an Pins 34 und 37
Alle Pins sind dabei natürlich durch das Platinendesign vorgegeben.
Den gesamten Beispielcode finden Sie auf GitHub.
Die STM32CubeMX-Datei ist dort ebenfalls verfügbar: sa_35m_4400m_act01.ioc
Code Generierung
Mit dem Button "Generate Code" in STM32CubeMX kann ein komplettes, passend konfiguriertes Quellcode Projekt für das Build-Werkzeug CMake generiert werden.
Für die weitere Programmierung kann also eine passende IDE frei gewählt werden (z.B. CLion von JetBrains).
Da das Kompilationsziel eine ARM CPU ist, muss zusätzlich eine entsprechende Crosscompiler Toolchain installiert werden.
Für Debian-Derivate (nativ oder als WSL Distribution unter Windows):
sudo apt install gcc-arm-none-eabi
SPI Programmierung — Bit Banging
Das Codegerüst ist generiert. Das CMake Projekt ist in der IDE geöffnet. Jetzt geht es an die Umsetzung des Anwendungsfalls (42MHz HF Signal Ausgabe).
Als Hochsprachen-Programmierer würde man dazu in Kategorien eines Methoden-Aufrufs denken, etwa so:
def activateSignal(self, channel: RfChannel, freq: int, power: float):
# Fancy Code
So einfach ist das natürlich nicht - Firmware-Programmierung ist nichts für schwache Gemüter.
Vielmehr muss via SPI Software-Interface eine Folge von 32bit Wörtern in die sechs internen Register des HF Oszillators übertragen werden, und zwar:
uint32_t words[] = {
0x00580005,
0x00EC8124,
0x000004B3,
0x00004E42,
0x0800E1A9,
0x0035B2C8
};
Wenn Sie den Aufbau der Wörter validieren möchten: Ein kurzer Blick auf das Datenblatt bringt (fast) sofortige Klarheit. Falls nicht, dann kann Wissen zum Thema Oszillator/PLL z.B. durch dieses gelungene Video aufgefrischt werden.
Den SPI Transfer überlässt man einer SPI Hardware-Komponente im µC. So jedenfalls die etwas voreilige Erwartung. Dem ist aber nicht so.
Vielmehr sind die Pins für LE / DATA / CLK als "General Purpose" konfiguriert.
Dies hat einen guten Grund: Der verwendete µC hat zwar eine SPI Komponente. Diese kann aber nicht an die notwendigen Pins gelegt werden. Die Platine ist offenbar für eine andere µC Version entwickelt worden…
Kein SPI? Kein Problem! Wir setzen SPI in Software um. Das Verfahren dazu nennt man Bit Banging. Das ist zwar ungefähr um den Faktor 100 langsamer als Hardware SPI. Aber für die 6 Wörter sind lediglich 6 * 4 * 8 Bit seriell zu übertragen. Das ist unproblematisch.
Der dazu notwendige Code ist trivial:
void spi_transfer(uint32_t word) {
uint8_t i;
for (i = 0; i < 32; i++) {
HAL_GPIO_WritePin(
SPI_GPIOX,
SPI_DATA_PIN,
(word & 0x80000000) != 0 ? GPIO_PIN_SET : GPIO_PIN_RESET);
word <<= 1;
HAL_GPIO_WritePin(SPI_GPIOX, SPI_CLK_PIN, GPIO_PIN_SET);
delay_ticks(5, &htim1);
HAL_GPIO_WritePin(SPI_GPIOX, SPI_CLK_PIN, GPIO_PIN_RESET);
delay_ticks(2, &htim1);
}
delay_ticks(72, &htim1); // 1us
}
Also:
-
Das zu übertragende Wort wird mit dem höchsten Bit vorab (MSB) sukzessive auf den DATA-Pin gelegt und mit einem Clock-Signal (CLK-Pin) aktiviert
-
Der lauschende Oszillator-Baustein empfängt diese und legt die Bits sukzessive in seinem Schieberegister ab
-
Ein abschliessendes "High" auf dem LE-Pin lässt den Oszillator das Wort in eines seiner sechs Arbeitsregister schreiben (Register Adresse ist Teil des Wortes)
-
Zusätzlich werden für das "richtige" Timing noch einige Delays via Hardware-Timer eingebaut
Damit ist das Bit-Bang SPI implementiert!
Jetzt muss nur noch die Gesamt-Übertragung programmiert werden:
void sa_35m_4400m_Init(void) {
HAL_TIM_Base_Start(&htim1);
enable_line();
int len = sizeof(words) / sizeof(words[0]);
for (int i = 0; i < len; i++) {
spi_transfer(words[i]);
latch_data();
enable_line();
}
}
Programmierung des µC
Mit der IDE kann nun die Firmware Cross-kompiliert und gelinkt werden. Ergebnis ist ein Binärfile mit nativem ARM Maschinencode:
$ file sa_35m_4400m_act01.elf
sa_35m_4400m_act01.elf: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), statically linked, with debug_info, not stripped
Die Firmware muss nun auf den µC geschrieben werden.
Auch dazu hat jeder µC Hersteller grundsätzlich seine eigenen Verfahren und Werkzeuge.
Die einfachste und günstigste Möglichkeit dazu besteht in der Verwendung eines st-link USB Adapters (z.B. bei der Amazon verfügbar).
Als Software für den Adapter stehen u.a. die stlink-tools (hier: Debian/Ubuntu) zur Verfügung.
$ sudo apt install stlink-tools
Verbindet man den Adapter mit der vierpoligen SWD Schnittstelle auf der Platine kann mit dem µC kommuniziert werden.
Check:
$ st-info --probe
Found 1 stlink programmers
version: V2J45S7
serial: AABBCCDDEE
flash: 32768 (pagesize: 1024)
sram: 10240
chipid: 0x123
dev-type: STM32F1xx_LD
Grundsätzlich lässt sich mit diesen stlink-tools die Firmware flashen…
…es geht aber einfacher mit dem "Meta"-Tool openocd. Über dieses Tool kann man nicht nur flashen, sondern seinen Code auch bequem in der IDE debuggen.
$ sudo apt install openocd
Im Beispielcode ist eine passende Konfigurationsdatei openocd.cfg beigefügt.
$ /usr/bin/openocd -s /usr/share/openocd/scripts -f ../openocd.cfg -c "tcl_port disabled" -c "gdb_port disabled" -c "tcl_port disabled" -c "program \"$PWD/cmake-build-debug/sa_35m_4400m_act01.elf\"" -c reset -c shutdown
Open On-Chip Debugger 0.12.0
...
** Programming Started **
Info : device id = 0x10004711
Info : flash size = 32 KiB
Warn : Adding extra erase range, 0x08001b00 .. 0x08001bff
** Programming Finished **
Tipp: CLion verfügt sowohl für openocd als auch für STM32CubeMX passende Integrationen.
Analyse und Auswertung
Ein mit den Leitungen DATA und CLK verbundenes Oszilloskop liefert folgende Ausgabe (Auszug)…
…für die Leitungen LE (Line Enable/Latch) sowie CLK:
Auswertung
-
Die 32bit Wörter lassen sich via Oszilloskop oder via Hacking-Werkzeug dekodieren und z.B. via LoRaWAN ausschleusen
-
Es ist ersichtlich, dass via Bit Banging übertragen wird (Clock-Signale High/Low sind unheitlich lang, geringe Übertragungsfrequenz von 250kHz)
-
Die Wörter wurden vorab generiert oder sind statisch vorgegeben (keine langen Zwischenräume zwischen den Latches).
Darstellung der HF-Signale
Das Signal (Oszilloskop):
Das Signal hat also in der Tat eine Frequenz von 42MHz.
Schaut man sich dies via SDR/Spektrumanalysator an, erkennt man Unsauberkeiten:
Der Grund dafür: Der Oszillator macht für hohe Frequenzen (bis 4,4GHz) sehr saubere Sinus-Signale. Bei Herunterteilen dieser Signale degenerieren diese aber immer mehr zu Rechteck-Signalen.
Speist man dieses Signal in einen separaten 35m-4400m Spektrumanalysator sieht man mit passender Software diese Ausgabe:
Die sichtbare Einkerbung bei 42MHz ist natürlich artifiziell. Unser kleiner 35€ Spektrumanalysator hat nämlich nur eine sehr schmale Auflösungs-Bandweite (fixen Bandpassfilter). Dafür kostet es aber auch keine Monatsmiete!