FM-Kreisskala mit Display          

von Günther Zöppel                       
 
                      Elektronik-Labor  Bastelecke  Projekte  Mikrocontoller                         



Beim Beschäftigen mit FM-Empfangs-Chips, welche mittels Poti in der Frequenz abgestimmt werden können, brauchte ich eine Übersichtsanzeige, in welchem Frequenzbereich man sich etwa aufhält. Bei bisherigen Projekten hatte ich ein LCD Display 20x4 und ein Mini-OLED 0.96“ (siehe Osterwettbewerb 2025) dazu verwendet. Es kommt gegenwärtig noch eine Alternative hinzu, ein preiswert erhältliches 1.28“ Farbdisplay mit der Bezeichnung GC9A01A, welches eine kreisförmige Darstellfläche bietet und damit prädestiniert ist für die Darstellung einer Kreisskala. Es wird über SPI-Interface an den Mikrocontroller angeschlossen. Dafür habe ich einen Sketch erstellt, der auf einem ESP8266 ausgeführt wird und die sich von 0 – 3,3V ändernde Steuerspannung (für die Frequenzeinstellung  des jeweiligen Empfangschips) in eine Zeigerbewegung umsetzt, die auf die entsprechend markierbare Frequenz zeigt. Alle Parameter, die für die Darstellung relevant sind, können im Sketch angepasst werden, indem man sie in einem Editor ändert und dann neu kompiliert und auf den Controller hochlädt. Der Sketch ist mit vielen Kommentaren versehen, um Nachnutzern vielseitige Anpassmöglichkeiten zu geben.

Hier der Sketch :

//Kreisskala FM
#include <Adafruit_GFX.h>
#include <Adafruit_GC9A01A.h>

// Pinbelegung für ESP8266 D1 mini
#define TFT_CS    D2
#define TFT_RST   D3
#define TFT_DC    D4
#define TFT_MOSI  D7
#define TFT_SCLK  D5

// Display-Objekt initialisieren
Adafruit_GC9A01A tft = Adafruit_GC9A01A(TFT_CS, TFT_DC, TFT_MOSI, TFT_SCLK, TFT_RST);

// Zeigerposition
const int centerX = 120;
const int centerY = 120;
const int pointerLength = 80;  // Länge des Zeigers
const int pointerWidth = 5;    // Breite des Zeigers

// Dreieckspitze-Eigenschaften
const int triangleWidth = 4;   // Breite der Dreiecksspitze (kleiner gemacht)
const int triangleOffset = 5;  // Abstand der Dreiecksspitze vom Zeigerende

// Breite der gelben Kreislinie
const int lineWidth = 2;

float lastVoltage = -1.0; // Letzter Spannungsspeicher

void setup() {
  tft.begin();
  tft.setRotation(1); // Display-Ausrichtung
  tft.fillScreen(GC9A01A_BLACK); // Bildschirm löschen

  drawScale();
  drawFMLabel(); // Separates Zeichnen der Beschriftung
}

void loop() {
  // Spannung am Pin A0 einlesen
  float voltage = analogRead(A0) * 3.3 / 1023.0;

  // Zeichne den Zeiger nur neu, wenn sich die Spannung geändert hat
  if (abs(voltage - lastVoltage) > 0.01) {
    float angle = mapVoltageToAngle(voltage);
    drawPointer(angle);
    lastVoltage = voltage;
  }

  delay(50); // Kurze Pause
}

void drawScale() {
  const int radiusOuter = 100; // Radius für äußere Markierungen
  const int radiusInner = 90;  // Radius für innere Markierungen
  const int radiusText = 110;  // Radius für die Beschriftung
  const float startAngle = -270; // Startwinkel in Grad (unten)
  const float angleStep = 330.0 / 21; // Schrittweite in Grad

  // Gelbe Kreislinie zeichnen
  for (int i = 0; i <= 330; i++) {
    float rad = radians(startAngle + i);
    int x1 = centerX + (radiusOuter - lineWidth) * cos(rad);
    int y1 = centerY + (radiusOuter - lineWidth) * sin(rad);
    int x2 = centerX + radiusOuter * cos(rad);
    int y2 = centerY + radiusOuter * sin(rad);
    tft.drawLine(x1, y1, x2, y2, GC9A01A_YELLOW);
  }

  for (int i = 0; i <= 21; i++) {
    float angle = startAngle + i * angleStep;
    float rad = radians(angle);

    // Koordinaten für die äußere Markierung
    int xOuter = centerX + radiusOuter * cos(rad);
    int yOuter = centerY + radiusOuter * sin(rad);

    // Koordinaten für die innere Markierung
    int xInner = centerX + radiusInner * cos(rad);
    int yInner = centerY + radiusInner * sin(rad);

    // Linie für die Markierung zeichnen
    tft.drawLine(xInner, yInner, xOuter, yOuter, GC9A01A_WHITE);

    // Beschriftung
    int xText = centerX + radiusText * cos(rad);
    int yText = centerY + radiusText * sin(rad);
    if (i == 1) { // Anfangsmarkierung 88 MHz
      tft.setTextSize(1);
      tft.setTextColor(GC9A01A_WHITE, GC9A01A_BLACK);
      tft.setCursor(xText - 6, yText - 3);
      tft.print("88");
    } else if (i == 7) { // Markierung 94 MHz
      tft.setTextSize(1);
      tft.setTextColor(GC9A01A_WHITE, GC9A01A_BLACK);
      tft.setCursor(xText - 6, yText - 3);
      tft.print("94");
    } else if (i == 13) { // Markierung 100 MHz
      tft.setTextSize(1);
      tft.setTextColor(GC9A01A_WHITE, GC9A01A_BLACK);
      tft.setCursor(xText - 6, yText - 3);
      tft.print("100");
    } else if (i == 18) { // Markierung 105 MHz
      tft.setTextSize(1);
      tft.setTextColor(GC9A01A_WHITE, GC9A01A_BLACK);
      tft.setCursor(xText - 6, yText - 3);
      tft.print("105");
    } else if (i == 21) { // Endmarkierung 108 MHz
      tft.setTextSize(1);
      tft.setTextColor(GC9A01A_WHITE, GC9A01A_BLACK);
      tft.setCursor(xText - 6, yText - 3);
      tft.print("108");
    }
  }

  // Roten Mittelpunkt zeichnen
  tft.fillCircle(centerX, centerY, 5, GC9A01A_RED);
}

void drawFMLabel() {
  // "FM MHz" mittig oben zeichnen
  tft.setTextSize(2);
  tft.setTextColor(GC9A01A_WHITE, GC9A01A_BLACK);
  tft.setCursor(centerX - 36, centerY - 50); // Position angepasst
  tft.print("FM MHz");
}

float mapVoltageToAngle(float voltage) {
  // Mappe Spannung (0-3,3 V) auf Winkelbereich (88 bis 108 MHz auf Skala)
  return -270 + (voltage / 3.3) * 330;
}

void drawPointer(float angle) {
  static float prevAngle = -270; // Letzte Position des Zeigers
  float radPrev = radians(prevAngle);

  // Vorherigen Zeiger und Pfeilspitze löschen
  int xEndPrev = centerX + pointerLength * cos(radPrev);
  int yEndPrev = centerY + pointerLength * sin(radPrev);
  tft.drawLine(centerX, centerY, xEndPrev, yEndPrev, GC9A01A_BLACK);
  drawTriangle(radPrev, GC9A01A_BLACK);

  // Aktuellen Zeiger zeichnen
  float rad = radians(angle);
  int xEnd = centerX + pointerLength * cos(rad);
  int yEnd = centerY + pointerLength * sin(rad);
  tft.drawLine(centerX, centerY, xEnd, yEnd, GC9A01A_YELLOW);
  drawTriangle(rad, GC9A01A_YELLOW);

  // Roter Mittelpunkt neu zeichnen
  tft.fillCircle(centerX, centerY, 5, GC9A01A_RED);

  // "FM MHz" erneut zeichnen, um Überschreiben zu vermeiden
  drawFMLabel();

  // Update vorherigen Winkel
  prevAngle = angle;
}

void drawTriangle(float rad, uint16_t color) {
  int xTip = centerX + (pointerLength + triangleOffset) * cos(rad);
  int yTip = centerY + (pointerLength + triangleOffset) * sin(rad);
  int xLeft = centerX + (pointerLength - triangleWidth) * cos(rad - radians(10));
  int yLeft = centerY + (pointerLength - triangleWidth) * sin(rad - radians(10));
  int xRight = centerX + (pointerLength - triangleWidth) * cos(rad + radians(10));
  int yRight = centerY + (pointerLength - triangleWidth) * sin(rad + radians(10));

  tft.fillTriangle(xTip, yTip, xLeft, yLeft, xRight, yRight, color);
}
//Ende Sketch

Infrage kommende FM-Empfangs-Chips sind z.B.  RDA5807,  RDA7088 oder CL6017S, um nur einige zu nennen. Die Steuerspannung greift man massebezogen dann einfach am Schleifer des Frequenzeinstellungspotis ab und führt sie dem Analogeingang des Mikrocontrollers (hier A0 des ESP8266 D1 mini) zu.  Das Hochladen des kompilierten Sketches kann z. B. über USB mit der Arduino IDE 1.8.19 erfolgen, nachdem man die im Sketch unter #include genannten erforderlichen Bibliotheken installiert hat. Auf dem beigefügten Video erkennt man den sich bewegenden Zeiger beim Abstimmen eines Potis, welches einfach zwischen Masse und dem 3,3 V Ausgang des ESP8266  geschaltet ist.




Elektronik-Labor  Bastelecke  Projekte  Mikrocontoller