README.md hinzugefügt
This commit is contained in:
187
README.md
Normal file
187
README.md
Normal file
@@ -0,0 +1,187 @@
|
|||||||
|
# Hirnfrei Floppinator
|
||||||
|
|
||||||
|
USB-Floppy-Emulator auf Basis eines Raspberry Pi Pico. Zeigt `.img`- und
|
||||||
|
`.adf`-Dateien von einer SD-Karte als USB-Massenspeicher an, Auswahl über
|
||||||
|
Display + 3 Taster, Oberfläche mit LVGL.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- Erscheint am Host als normales USB-Massenspeichergerät (kein Treiber nötig)
|
||||||
|
- Unterstützt `.img` (PC/generisch) und `.adf` (Amiga) — Kapazität wird
|
||||||
|
automatisch aus der Dateigröße ermittelt, kein festes Format nötig
|
||||||
|
- 160×128-TFT-Display mit LVGL-Oberfläche (Liste, Statuszeile, Aktivitätsanzeige)
|
||||||
|
- Alphabetisch sortierte Image-Liste
|
||||||
|
- Manuelles Neuladen der Liste per Tastenkombination (z.B. nach SD-Kartenwechsel)
|
||||||
|
- Aktivitäts-LED (Onboard-LED) leuchtet bei Lese-/Schreibzugriffen
|
||||||
|
- Debug-Ausgaben über USB-Seriell, zentral abschaltbar
|
||||||
|
|
||||||
|
## Hardware
|
||||||
|
|
||||||
|
- Raspberry Pi Pico (RP2040)
|
||||||
|
([Berrybase Pico Light Starter Kit](https://www.berrybase.de/raspberry-pi-pico-light-starter-kit-bestehend-aus-pico-offiziellem-kabel-und-stiftleisten))
|
||||||
|
- 1,8" SPI-TFT-Display, 160×128, ST7735S-Controller
|
||||||
|
([Berrybase 1,8" LCD-Anzeigemodul](https://www.berrybase.de/1-8-zoll-lcd-anzeigemodul-fuer-raspberry-pi-pico-65k-farben-160x128-spi))
|
||||||
|
- Micro-SD-Kartenmodul, SPI, 3,3V (6-Pin: 3V3/CS/MOSI/CLK/MISO/GND)
|
||||||
|
([Berrybase Micro-SD-Card-Reader-Modul, kompakt](https://www.berrybase.de/micro-sd-card-reader-modul-mit-spi-schnittstelle-kompakt))
|
||||||
|
- 3× Kurzhubtaster, 6×6mm, vertikale Printmontage, H=5,0mm
|
||||||
|
([Berrybase Kurzhubtaster](https://www.berrybase.de/kurzhubtaster-vertikale-printmontage-6x6mm-h-5-0mm))
|
||||||
|
- microSD-Karte (FAT16/FAT32 formatiert)
|
||||||
|
|
||||||
|
### Schaltplan / Verkabelung
|
||||||
|
|
||||||
|
```
|
||||||
|
Raspberry Pi Pico
|
||||||
|
┌──────────────────┐
|
||||||
|
TFT-Display │ │
|
||||||
|
────────── │ │
|
||||||
|
CS ───────────────┤ GP9 │
|
||||||
|
DC ───────────────┤ GP8 │
|
||||||
|
RST ───────────────┤ GP12 │
|
||||||
|
BL ───────────────┤ GP13 │
|
||||||
|
SCK ───────────────┤ GP10 (SPI1 SCK) │
|
||||||
|
MOSI ───────────────┤ GP11 (SPI1 TX) │
|
||||||
|
MISO ───────────────┤ GP28 (SPI1 RX) │
|
||||||
|
VCC ───────────────┤ 3V3(OUT) │
|
||||||
|
GND ───────────────┤ GND │
|
||||||
|
│ │
|
||||||
|
SD-Kartenmodul │ │
|
||||||
|
────────────── │ │
|
||||||
|
CS ───────────────┤ GP5 │
|
||||||
|
MOSI ───────────────┤ GP7 (SPI0 TX) │
|
||||||
|
CLK ───────────────┤ GP6 (SPI0 SCK) │
|
||||||
|
MISO ───────────────┤ GP4 (SPI0 RX) │
|
||||||
|
3V3 ───────────────┤ 3V3(OUT) │
|
||||||
|
GND ───────────────┤ GND │
|
||||||
|
│ │
|
||||||
|
Taster │ │
|
||||||
|
────── │ │
|
||||||
|
Zurück ───────────────┤ GP14 (INPUT_PULLUP, gegen GND) │
|
||||||
|
Weiter ───────────────┤ GP15 (INPUT_PULLUP, gegen GND) │
|
||||||
|
Auswahl ──────────────┤ GP20 (INPUT_PULLUP, gegen GND) │
|
||||||
|
│ │
|
||||||
|
Aktivitäts-LED │ Onboard-LED │
|
||||||
|
(keine Verkabelung │ (LED_BUILTIN) │
|
||||||
|
nötig) │ │
|
||||||
|
└──────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
Alle Taster schalten gegen GND (interner Pull-Up wird im Code aktiviert,
|
||||||
|
kein externer Widerstand nötig).
|
||||||
|
|
||||||
|
**Wichtig:** SD-Modul und Display hängen an unterschiedlichen SPI-Bussen
|
||||||
|
(SD an SPI0, Display an SPI1) und benutzen absichtlich *nicht* die
|
||||||
|
RP2040-Standard-SPI0-Pins (GP16-19), da diese im ursprünglichen
|
||||||
|
Prototyp-Aufbau elektrisch beansprucht waren. Bei einem Neuaufbau von
|
||||||
|
Grund auf können auch die Standard-Pins verwendet werden — einfach die
|
||||||
|
`#define SD_*_PIN`-Zeilen im Sketch anpassen.
|
||||||
|
|
||||||
|
### Pin-Übersicht
|
||||||
|
|
||||||
|
| Funktion | Pico-Pin | Bus/Modus |
|
||||||
|
|-------------------|----------|-------------------|
|
||||||
|
| TFT CS | GP9 | SPI1 |
|
||||||
|
| TFT DC | GP8 | SPI1 |
|
||||||
|
| TFT RST | GP12 | SPI1 |
|
||||||
|
| TFT Backlight | GP13 | digital OUT |
|
||||||
|
| TFT SCK | GP10 | SPI1 |
|
||||||
|
| TFT MOSI | GP11 | SPI1 |
|
||||||
|
| TFT MISO | GP28 | SPI1 |
|
||||||
|
| SD CS | GP5 | SPI0 |
|
||||||
|
| SD MOSI | GP7 | SPI0 |
|
||||||
|
| SD CLK | GP6 | SPI0 |
|
||||||
|
| SD MISO | GP4 | SPI0 |
|
||||||
|
| Taster Zurück | GP14 | INPUT_PULLUP |
|
||||||
|
| Taster Weiter | GP15 | INPUT_PULLUP |
|
||||||
|
| Taster Auswahl | GP20 | INPUT_PULLUP |
|
||||||
|
| Aktivitäts-LED | LED_BUILTIN (GP25) | digital OUT |
|
||||||
|
|
||||||
|
## Abhängigkeiten (Arduino Libraries)
|
||||||
|
|
||||||
|
Über den Arduino Library Manager installieren:
|
||||||
|
|
||||||
|
| Library | Autor | Version | Zweck |
|
||||||
|
|---------------------------|---------------|---------|----------------------------------|
|
||||||
|
| Adafruit GFX Library | Adafruit | aktuell | Basis-Grafikbibliothek |
|
||||||
|
| Adafruit ST7735 and ST7789 Library | Adafruit | aktuell | Display-Treiber |
|
||||||
|
| SdFat | Bill Greiman | 2.x | SD-Karten-/FAT-Dateisystemzugriff |
|
||||||
|
| Adafruit TinyUSB Library | Adafruit | aktuell | USB-Massenspeicher-Gerät (MSC) |
|
||||||
|
| lvgl | kisvegabor | 9.x | Grafische Oberfläche |
|
||||||
|
|
||||||
|
## Arduino IDE Einstellungen
|
||||||
|
|
||||||
|
Board-Paket: **Raspberry Pi Pico/RP2040** (von Earle Philhower,
|
||||||
|
`arduino-pico`) über den Boardverwalter installieren.
|
||||||
|
|
||||||
|
Unter `Werkzeuge`:
|
||||||
|
|
||||||
|
| Einstellung | Wert |
|
||||||
|
|---------------------|-------------------------------|
|
||||||
|
| Board | Raspberry Pi Pico |
|
||||||
|
| Flash Size | 2MB (no FS) |
|
||||||
|
| CPU Speed | 200 MHz (oder Standard 133 MHz) |
|
||||||
|
| Optimize | Small (-Os) (Standard) |
|
||||||
|
| **USB Stack** | **Adafruit TinyUSB** ⚠️ zwingend erforderlich |
|
||||||
|
| Upload Method | Default (UF2) |
|
||||||
|
|
||||||
|
**Wichtig:** Ohne `USB Stack: Adafruit TinyUSB` funktioniert weder die
|
||||||
|
USB-Massenspeicher-Funktion noch `Serial` (da beides über TinyUSB läuft).
|
||||||
|
Reine Test-/Diagnose-Sketches ohne TinyUSB (z.B. isolierte SPI-Tests)
|
||||||
|
brauchen stattdessen `USB Stack: Pico SDK`, damit `Serial` ohne
|
||||||
|
`Adafruit_TinyUSB.h`-Include funktioniert.
|
||||||
|
|
||||||
|
## LVGL-Konfiguration (`lv_conf.h`)
|
||||||
|
|
||||||
|
LVGL benötigt eine aktive `lv_conf.h`. Vorgehen:
|
||||||
|
|
||||||
|
1. Die Vorlage `lv_conf_template.h` aus dem `lvgl`-Bibliotheksordner
|
||||||
|
eine Ebene höher kopieren, z.B. nach:
|
||||||
|
```
|
||||||
|
Arduino/libraries/lv_conf.h (Ebene über .../libraries/lvgl/)
|
||||||
|
```
|
||||||
|
2. In der kopierten Datei ganz oben `#if 0` zu `#if 1` ändern, damit sie
|
||||||
|
aktiv ist.
|
||||||
|
3. Ansonsten reicht die Standard-Vorlage unverändert — Farbformat
|
||||||
|
(RGB565) und Schriftgröße (Montserrat 14, in der Vorlage standardmäßig
|
||||||
|
aktiv) werden bereits im Sketch selbst passend gesetzt/verwendet.
|
||||||
|
|
||||||
|
## Display-Kalibrierung
|
||||||
|
|
||||||
|
Das verwendete ST7735S-Panel benötigt einen manuellen Adressierungs-Offset
|
||||||
|
(`tft.setColRowStart(2, 1)`), der empirisch für das konkret verbaute
|
||||||
|
Panel-Exemplar ermittelt wurde. Bei einem anderen Panel/Batch kann dieser
|
||||||
|
Wert leicht abweichen. Symptom bei falschem Offset: ein bunter,
|
||||||
|
"verrauschter" Rand am Bildschirmrand (Zeilen-/Spaltenversatz zwischen
|
||||||
|
GRAM und sichtbarem Glas). Falls das auftritt: Werte wie `(0, 0)`,
|
||||||
|
`(1, 2)` oder `(0, 32)` ausprobieren.
|
||||||
|
|
||||||
|
## Bedienung
|
||||||
|
|
||||||
|
| Aktion | Tasten |
|
||||||
|
|-----------------------------------|------------------------------|
|
||||||
|
| In der Liste navigieren | Zurück / Weiter |
|
||||||
|
| Fokussiertes Image mounten | Auswahl |
|
||||||
|
| Image-Liste neu laden (z.B. nach SD-Kartenwechsel) | Zurück + Weiter gleichzeitig |
|
||||||
|
|
||||||
|
Das SD-Modul hat keinen Card-Detect-Pin — ein Kartenwechsel im laufenden
|
||||||
|
Betrieb wird daher **nicht automatisch** erkannt (frühere Versuche mit
|
||||||
|
automatischer Erkennung per periodischem Polling haben sich als
|
||||||
|
unzuverlässig erwiesen). Nach einem Kartenwechsel einfach kurz
|
||||||
|
Zurück+Weiter drücken.
|
||||||
|
|
||||||
|
## Bekannte Einschränkungen
|
||||||
|
|
||||||
|
- Kein automatisches Remounten des zuletzt gewählten Images nach einem
|
||||||
|
Kartenwechsel (bewusst, um kein Image von einer inzwischen anderen
|
||||||
|
Karte "weiterlaufen" zu lassen)
|
||||||
|
- Debounce-basierte Tastenerkennung, kein Auto-Repeat bei gehaltener Taste
|
||||||
|
- `MAX_IMAGES` ist aktuell auf 16 begrenzt (Konstante im Sketch anpassbar)
|
||||||
|
|
||||||
|
## Debug-Ausgaben
|
||||||
|
|
||||||
|
Über `Serial` (115200 Baud), zentral steuerbar über die Konstante
|
||||||
|
`DEBUG_ENABLED` im Sketch (`true`/`false`). Bei `false` entfällt auch der
|
||||||
|
Start-Delay auf den seriellen Monitor.
|
||||||
|
|
||||||
|
## Kontakt
|
||||||
|
|
||||||
|
Fragen, Anregungen oder Bugs? Discord: https://discord.projekt-hirnfrei.de
|
||||||
@@ -1,25 +1,33 @@
|
|||||||
/*
|
/*
|
||||||
Hirnfrei Floppinator - Standalone-Version (Pico only)
|
Hirnfrei Floppinator - LVGL-Version
|
||||||
|
|
||||||
Der Arduino Mega samt UART-Backend ist raus. Die SD-Karte haengt jetzt
|
Backend (SD-Karte, USB-MSC, Rescan, ADF/IMG-Support, Aktivitaets-LED)
|
||||||
direkt per SPI0 am Pico (das neue Modul mit 3V3/CS/MOSI/CLK/MISO/GND).
|
ist identisch zur reinen Adafruit-GFX-Version. Neu ist ausschliesslich
|
||||||
|
die Oberflaeche: LVGL v9 uebernimmt Display-Rendering und
|
||||||
|
Tasten-Navigation (Rauf/Runter/Auswahl -> LVGL-Keypad-Eingabegeraet).
|
||||||
|
|
||||||
Pinbelegung SD-Modul -> Pico (SPI0, alternatives Pinset):
|
Pinbelegung SD-Modul -> Pico (SPI0, alternatives Pinset):
|
||||||
3V3 -> 3V3 (out)
|
3V3 -> 3V3 (out), GND -> GND
|
||||||
GND -> GND
|
CS -> GP5, MOSI -> GP7, CLK -> GP6, MISO -> GP4
|
||||||
CS -> GP5
|
|
||||||
MOSI -> GP7
|
|
||||||
CLK -> GP6
|
|
||||||
MISO -> GP4
|
|
||||||
|
|
||||||
Hinweis: GP16-19 (Standard-SPI0-Pins) wurden bewusst NICHT verwendet.
|
Display: 160x128 ST7735S SPI-TFT an SPI1 (unveraendert).
|
||||||
GP16/17 waren im Frankenstein-Aufbau als UART-Verbindung zum Mega im
|
|
||||||
Einsatz (5V-Logik!) und haben sich beim Testen als unzuverlaessig fuer
|
|
||||||
SD-Kommunikation erwiesen - vermutlich durch die 5V-Belastung in
|
|
||||||
Mitleidenschaft gezogen. GP4-7 sind unbelastete, funktionierende Pins.
|
|
||||||
|
|
||||||
Das Display haengt weiterhin an SPI1 wie bisher, unveraendert.
|
WICHTIG - Vorbereitung, bevor das hier kompiliert:
|
||||||
Serial1 / UART wird nicht mehr gebraucht und ist komplett entfernt.
|
1. Library "lvgl" (von kisvegabor) Version 9.x ueber den Library
|
||||||
|
Manager installieren.
|
||||||
|
2. lv_conf.h bereitstellen: die Datei liegt als Vorlage
|
||||||
|
"lv_conf_template.h" im lvgl-Bibliotheksordner. Diese Vorlage
|
||||||
|
eine Ebene HOEHER als der "lvgl"-Ordner ablegen, z.B.:
|
||||||
|
Arduino/libraries/lv_conf.h (Ebene ueber .../libraries/lvgl/)
|
||||||
|
In der kopierten Datei ganz oben "#if 0" zu "#if 1" aendern,
|
||||||
|
damit sie ueberhaupt aktiv ist. Ansonsten reicht die Standard-
|
||||||
|
Vorlage unveraendert - Farbformat und Schriftgroesse (14) werden
|
||||||
|
im Code selbst erzwungen/verwendet, keine weiteren Anpassungen
|
||||||
|
an lv_conf.h noetig.
|
||||||
|
3. Display-Offset (tft.setColRowStart) ist fuer dieses konkrete
|
||||||
|
Panel empirisch auf (2, 1) kalibriert. Bei einem anderen Panel-
|
||||||
|
Exemplar/Batch kann das abweichen - falls nach dem Flashen ein
|
||||||
|
bunter Rand am Bildschirmrand auftaucht, hier nachjustieren.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <Adafruit_TinyUSB.h>
|
#include <Adafruit_TinyUSB.h>
|
||||||
@@ -27,6 +35,7 @@
|
|||||||
#include <Adafruit_ST7735.h>
|
#include <Adafruit_ST7735.h>
|
||||||
#include <SPI.h>
|
#include <SPI.h>
|
||||||
#include <SdFat.h>
|
#include <SdFat.h>
|
||||||
|
#include <lvgl.h>
|
||||||
|
|
||||||
#define TFT_CS_PIN 9
|
#define TFT_CS_PIN 9
|
||||||
#define TFT_DC_PIN 8
|
#define TFT_DC_PIN 8
|
||||||
@@ -43,11 +52,9 @@
|
|||||||
#define SD_MOSI_PIN 7
|
#define SD_MOSI_PIN 7
|
||||||
#define SD_SCK_PIN 6
|
#define SD_SCK_PIN 6
|
||||||
|
|
||||||
// Debug-Schalter: true = Serial-Ausgaben aktiv, false = komplett
|
#define ACTIVITY_LED_PIN LED_BUILTIN
|
||||||
// abgeschaltet (keine Ausgaben, kein Warten auf den seriellen Monitor
|
|
||||||
// beim Start). Das USB-CDC-Interface selbst bleibt so oder so im
|
#define DEBUG_ENABLED true
|
||||||
// USB-Deskriptor bestehen, das liegt an der TinyUSB-Konfiguration.
|
|
||||||
#define DEBUG_ENABLED false
|
|
||||||
|
|
||||||
#if DEBUG_ENABLED
|
#if DEBUG_ENABLED
|
||||||
#define DBG_PRINT(...) do { if (Serial) { Serial.print(__VA_ARGS__); } } while (0)
|
#define DBG_PRINT(...) do { if (Serial) { Serial.print(__VA_ARGS__); } } while (0)
|
||||||
@@ -58,6 +65,8 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define MAX_IMAGES 16
|
#define MAX_IMAGES 16
|
||||||
|
#define SCREEN_W 160
|
||||||
|
#define SCREEN_H 128
|
||||||
|
|
||||||
struct FloppyImage
|
struct FloppyImage
|
||||||
{
|
{
|
||||||
@@ -66,8 +75,6 @@ struct FloppyImage
|
|||||||
|
|
||||||
FloppyImage images[MAX_IMAGES];
|
FloppyImage images[MAX_IMAGES];
|
||||||
uint8_t imageCount = 0;
|
uint8_t imageCount = 0;
|
||||||
|
|
||||||
int8_t browseIndex = 0;
|
|
||||||
int8_t mountedIndex = -1;
|
int8_t mountedIndex = -1;
|
||||||
|
|
||||||
Adafruit_USBD_MSC usb_msc;
|
Adafruit_USBD_MSC usb_msc;
|
||||||
@@ -76,6 +83,8 @@ SdFat32 sd;
|
|||||||
File32 currentFile;
|
File32 currentFile;
|
||||||
String currentFilename = "";
|
String currentFilename = "";
|
||||||
|
|
||||||
|
volatile bool mscBusy = false;
|
||||||
|
|
||||||
class TftDisplay : public Adafruit_ST7735
|
class TftDisplay : public Adafruit_ST7735
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -98,6 +107,8 @@ Button btnPrev { BTN_PREV_PIN };
|
|||||||
Button btnNext { BTN_NEXT_PIN };
|
Button btnNext { BTN_NEXT_PIN };
|
||||||
Button btnSel { BTN_SEL_PIN };
|
Button btnSel { BTN_SEL_PIN };
|
||||||
|
|
||||||
|
bool comboActive = false;
|
||||||
|
|
||||||
bool buttonPressed(Button &b)
|
bool buttonPressed(Button &b)
|
||||||
{
|
{
|
||||||
bool reading = digitalRead(b.pin);
|
bool reading = digitalRead(b.pin);
|
||||||
@@ -117,12 +128,69 @@ bool buttonPressed(Button &b)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- USB MSC Callbacks: jetzt direkt auf die SD-Karte ---
|
// --- LVGL: Display-Flush + Tasten-Eingabegeraet ---
|
||||||
|
|
||||||
|
static lv_display_t *lvDisplay;
|
||||||
|
static lv_indev_t *lvKeypad;
|
||||||
|
static lv_group_t *lvGroup;
|
||||||
|
|
||||||
|
static lv_obj_t *imageList;
|
||||||
|
static lv_obj_t *statusLabel;
|
||||||
|
static lv_obj_t *activityDot;
|
||||||
|
static lv_obj_t *listButtons[MAX_IMAGES];
|
||||||
|
|
||||||
|
void disp_flush(lv_display_t *disp, const lv_area_t *area, uint8_t *px_map)
|
||||||
|
{
|
||||||
|
uint32_t w = area->x2 - area->x1 + 1;
|
||||||
|
uint32_t h = area->y2 - area->y1 + 1;
|
||||||
|
|
||||||
|
tft.drawRGBBitmap(area->x1, area->y1, (uint16_t *)px_map, w, h);
|
||||||
|
|
||||||
|
lv_display_flush_ready(disp);
|
||||||
|
}
|
||||||
|
|
||||||
|
void keypad_read_cb(lv_indev_t *indev, lv_indev_data_t *data)
|
||||||
|
{
|
||||||
|
static uint32_t lastKey = 0;
|
||||||
|
|
||||||
|
if (buttonPressed(btnPrev))
|
||||||
|
{
|
||||||
|
lastKey = LV_KEY_PREV;
|
||||||
|
data->state = LV_INDEV_STATE_PRESSED;
|
||||||
|
}
|
||||||
|
else if (buttonPressed(btnNext))
|
||||||
|
{
|
||||||
|
lastKey = LV_KEY_NEXT;
|
||||||
|
data->state = LV_INDEV_STATE_PRESSED;
|
||||||
|
}
|
||||||
|
else if (buttonPressed(btnSel))
|
||||||
|
{
|
||||||
|
lastKey = LV_KEY_ENTER;
|
||||||
|
data->state = LV_INDEV_STATE_PRESSED;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
data->state = LV_INDEV_STATE_RELEASED;
|
||||||
|
}
|
||||||
|
|
||||||
|
data->key = lastKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- USB MSC Callbacks ---
|
||||||
|
|
||||||
|
inline void setMscBusy(bool busy)
|
||||||
|
{
|
||||||
|
mscBusy = busy;
|
||||||
|
digitalWrite(ACTIVITY_LED_PIN, busy ? HIGH : LOW);
|
||||||
|
}
|
||||||
|
|
||||||
int32_t msc_read_cb(uint32_t lba, void *buffer, uint32_t bufsize)
|
int32_t msc_read_cb(uint32_t lba, void *buffer, uint32_t bufsize)
|
||||||
{
|
{
|
||||||
|
setMscBusy(true);
|
||||||
|
|
||||||
if (!currentFile)
|
if (!currentFile)
|
||||||
{
|
{
|
||||||
|
setMscBusy(false);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -130,6 +198,7 @@ int32_t msc_read_cb(uint32_t lba, void *buffer, uint32_t bufsize)
|
|||||||
{
|
{
|
||||||
DBG_PRINT("SEEK FEHLER (read) lba=");
|
DBG_PRINT("SEEK FEHLER (read) lba=");
|
||||||
DBG_PRINTLN(lba);
|
DBG_PRINTLN(lba);
|
||||||
|
setMscBusy(false);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -140,15 +209,20 @@ int32_t msc_read_cb(uint32_t lba, void *buffer, uint32_t bufsize)
|
|||||||
DBG_PRINT(lba);
|
DBG_PRINT(lba);
|
||||||
DBG_PRINT(" n=");
|
DBG_PRINT(" n=");
|
||||||
DBG_PRINTLN(n);
|
DBG_PRINTLN(n);
|
||||||
|
setMscBusy(false);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
setMscBusy(false);
|
||||||
return bufsize;
|
return bufsize;
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t msc_write_cb(uint32_t lba, uint8_t *buffer, uint32_t bufsize)
|
int32_t msc_write_cb(uint32_t lba, uint8_t *buffer, uint32_t bufsize)
|
||||||
{
|
{
|
||||||
|
setMscBusy(true);
|
||||||
|
|
||||||
if (!currentFile)
|
if (!currentFile)
|
||||||
{
|
{
|
||||||
|
setMscBusy(false);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -156,6 +230,7 @@ int32_t msc_write_cb(uint32_t lba, uint8_t *buffer, uint32_t bufsize)
|
|||||||
{
|
{
|
||||||
DBG_PRINT("SEEK FEHLER (write) lba=");
|
DBG_PRINT("SEEK FEHLER (write) lba=");
|
||||||
DBG_PRINTLN(lba);
|
DBG_PRINTLN(lba);
|
||||||
|
setMscBusy(false);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -164,8 +239,10 @@ int32_t msc_write_cb(uint32_t lba, uint8_t *buffer, uint32_t bufsize)
|
|||||||
{
|
{
|
||||||
DBG_PRINT("WRITE FEHLER lba=");
|
DBG_PRINT("WRITE FEHLER lba=");
|
||||||
DBG_PRINTLN(lba);
|
DBG_PRINTLN(lba);
|
||||||
|
setMscBusy(false);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
setMscBusy(false);
|
||||||
return bufsize;
|
return bufsize;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -177,40 +254,128 @@ void msc_flush_cb(void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Display ---
|
// --- UI-Aufbau ---
|
||||||
|
|
||||||
void updateDisplay()
|
void buildUi()
|
||||||
{
|
{
|
||||||
tft.fillScreen(ST77XX_BLACK);
|
lv_obj_t *scr = lv_scr_act();
|
||||||
|
lv_obj_set_style_bg_color(scr, lv_color_hex(0x000000), 0);
|
||||||
|
|
||||||
tft.setTextSize(1);
|
// Kopfzeile
|
||||||
tft.setTextColor(ST77XX_WHITE);
|
lv_obj_t *header = lv_obj_create(scr);
|
||||||
tft.setCursor(4, 4);
|
lv_obj_remove_style_all(header);
|
||||||
tft.println("Floppy A:");
|
lv_obj_set_size(header, SCREEN_W, 24);
|
||||||
|
lv_obj_set_pos(header, 0, 0);
|
||||||
|
lv_obj_set_style_bg_color(header, lv_color_hex(0x1b1f3a), 0);
|
||||||
|
lv_obj_set_style_bg_opa(header, LV_OPA_COVER, 0);
|
||||||
|
|
||||||
|
lv_obj_t *title = lv_label_create(header);
|
||||||
|
lv_label_set_text(title, LV_SYMBOL_SD_CARD " FLOPPINATOR");
|
||||||
|
lv_obj_set_style_text_color(title, lv_color_hex(0xFFC107), 0);
|
||||||
|
lv_obj_set_style_text_font(title, &lv_font_montserrat_14, 0);
|
||||||
|
lv_obj_center(title);
|
||||||
|
|
||||||
|
// Liste
|
||||||
|
imageList = lv_list_create(scr);
|
||||||
|
lv_obj_set_size(imageList, SCREEN_W, 86);
|
||||||
|
lv_obj_set_pos(imageList, 0, 24);
|
||||||
|
lv_obj_set_style_bg_color(imageList, lv_color_hex(0x101010), 0);
|
||||||
|
lv_obj_set_style_border_width(imageList, 0, 0);
|
||||||
|
lv_obj_set_style_pad_all(imageList, 2, 0);
|
||||||
|
lv_obj_set_style_pad_row(imageList, 2, 0);
|
||||||
|
|
||||||
|
// Fusszeile
|
||||||
|
lv_obj_t *footer = lv_obj_create(scr);
|
||||||
|
lv_obj_remove_style_all(footer);
|
||||||
|
lv_obj_set_size(footer, SCREEN_W, 18);
|
||||||
|
lv_obj_set_pos(footer, 0, 110);
|
||||||
|
lv_obj_set_style_bg_color(footer, lv_color_hex(0x1b1f3a), 0);
|
||||||
|
lv_obj_set_style_bg_opa(footer, LV_OPA_COVER, 0);
|
||||||
|
|
||||||
|
statusLabel = lv_label_create(footer);
|
||||||
|
lv_label_set_text(statusLabel, "kein Medium");
|
||||||
|
lv_obj_set_style_text_color(statusLabel, lv_color_hex(0xAAAAAA), 0);
|
||||||
|
lv_obj_set_style_text_font(statusLabel, &lv_font_montserrat_14, 0);
|
||||||
|
lv_obj_align(statusLabel, LV_ALIGN_LEFT_MID, 4, 0);
|
||||||
|
|
||||||
|
activityDot = lv_obj_create(footer);
|
||||||
|
lv_obj_remove_style_all(activityDot);
|
||||||
|
lv_obj_set_size(activityDot, 8, 8);
|
||||||
|
lv_obj_set_style_radius(activityDot, LV_RADIUS_CIRCLE, 0);
|
||||||
|
lv_obj_set_style_bg_color(activityDot, lv_color_hex(0x333333), 0);
|
||||||
|
lv_obj_set_style_bg_opa(activityDot, LV_OPA_COVER, 0);
|
||||||
|
lv_obj_align(activityDot, LV_ALIGN_RIGHT_MID, -4, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void listButtonEventCb(lv_event_t *e)
|
||||||
|
{
|
||||||
|
lv_obj_t *btn = (lv_obj_t *)lv_event_get_target(e);
|
||||||
|
int idx = (int)(uintptr_t)lv_obj_get_user_data(btn);
|
||||||
|
|
||||||
|
if (idx != mountedIndex)
|
||||||
|
{
|
||||||
|
mountImage(idx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void refreshStatusBar()
|
||||||
|
{
|
||||||
|
static char buf[40];
|
||||||
|
|
||||||
tft.setCursor(4, 24);
|
|
||||||
if (mountedIndex >= 0)
|
if (mountedIndex >= 0)
|
||||||
{
|
{
|
||||||
tft.setTextColor(ST77XX_GREEN);
|
snprintf(buf, sizeof(buf), LV_SYMBOL_OK " %s", images[mountedIndex].name);
|
||||||
tft.println(images[mountedIndex].name);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
tft.setTextColor(ST77XX_RED);
|
snprintf(buf, sizeof(buf), "kein Medium");
|
||||||
tft.println("-- leer --");
|
}
|
||||||
|
lv_label_set_text(statusLabel, buf);
|
||||||
|
|
||||||
|
for (uint8_t i = 0; i < imageCount; i++)
|
||||||
|
{
|
||||||
|
if (listButtons[i] == NULL)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ((int8_t)i == mountedIndex)
|
||||||
|
{
|
||||||
|
lv_obj_set_style_bg_color(listButtons[i], lv_color_hex(0x1b3a1b), 0);
|
||||||
|
lv_obj_set_style_bg_opa(listButtons[i], LV_OPA_COVER, 0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
lv_obj_set_style_bg_color(listButtons[i], lv_color_hex(0x202020), 0);
|
||||||
|
lv_obj_set_style_bg_opa(listButtons[i], LV_OPA_COVER, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void rebuildImageList()
|
||||||
|
{
|
||||||
|
lv_obj_clean(imageList);
|
||||||
|
for (uint8_t i = 0; i < MAX_IMAGES; i++)
|
||||||
|
{
|
||||||
|
listButtons[i] = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
tft.setCursor(4, 55);
|
if (imageCount == 0)
|
||||||
tft.setTextColor(ST77XX_YELLOW);
|
|
||||||
tft.print("> ");
|
|
||||||
if (imageCount > 0)
|
|
||||||
{
|
{
|
||||||
tft.println(images[browseIndex].name);
|
lv_list_add_text(imageList, "Keine Images gefunden");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
for (uint8_t i = 0; i < imageCount; i++)
|
||||||
{
|
{
|
||||||
tft.println("(Suche .img...)");
|
lv_obj_t *btn = lv_list_add_btn(imageList, LV_SYMBOL_FILE, images[i].name);
|
||||||
|
lv_obj_set_user_data(btn, (void *)(uintptr_t)i);
|
||||||
|
lv_obj_add_event_cb(btn, listButtonEventCb, LV_EVENT_CLICKED, NULL);
|
||||||
|
lv_group_add_obj(lvGroup, btn);
|
||||||
|
listButtons[i] = btn;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lv_group_focus_obj(listButtons[0]);
|
||||||
|
refreshStatusBar();
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- SD-Verzeichnis einlesen ---
|
// --- SD-Verzeichnis einlesen ---
|
||||||
@@ -240,7 +405,7 @@ void scanImages()
|
|||||||
String lowerName = String(nameBuf);
|
String lowerName = String(nameBuf);
|
||||||
lowerName.toLowerCase();
|
lowerName.toLowerCase();
|
||||||
|
|
||||||
if (lowerName.endsWith(".img") && imageCount < MAX_IMAGES)
|
if ((lowerName.endsWith(".img") || lowerName.endsWith(".adf")) && imageCount < MAX_IMAGES)
|
||||||
{
|
{
|
||||||
strncpy(images[imageCount].name, nameBuf, sizeof(images[imageCount].name) - 1);
|
strncpy(images[imageCount].name, nameBuf, sizeof(images[imageCount].name) - 1);
|
||||||
images[imageCount].name[sizeof(images[imageCount].name) - 1] = '\0';
|
images[imageCount].name[sizeof(images[imageCount].name) - 1] = '\0';
|
||||||
@@ -252,13 +417,67 @@ void scanImages()
|
|||||||
}
|
}
|
||||||
root.close();
|
root.close();
|
||||||
|
|
||||||
|
sortImages();
|
||||||
|
|
||||||
DBG_PRINT(imageCount);
|
DBG_PRINT(imageCount);
|
||||||
DBG_PRINTLN(" .img-Datei(en) gefunden.");
|
DBG_PRINTLN(" .img/.adf-Datei(en) gefunden.");
|
||||||
for (uint8_t i = 0; i < imageCount; i++)
|
for (uint8_t i = 0; i < imageCount; i++)
|
||||||
{
|
{
|
||||||
DBG_PRINT(" ["); DBG_PRINT(i); DBG_PRINT("] ");
|
DBG_PRINT(" ["); DBG_PRINT(i); DBG_PRINT("] ");
|
||||||
DBG_PRINTLN(images[i].name);
|
DBG_PRINTLN(images[i].name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rebuildImageList();
|
||||||
|
}
|
||||||
|
|
||||||
|
void sortImages()
|
||||||
|
{
|
||||||
|
for (uint8_t i = 1; i < imageCount; i++)
|
||||||
|
{
|
||||||
|
FloppyImage key = images[i];
|
||||||
|
int8_t j = i - 1;
|
||||||
|
|
||||||
|
while (j >= 0 && strcasecmp(images[j].name, key.name) > 0)
|
||||||
|
{
|
||||||
|
images[j + 1] = images[j];
|
||||||
|
j--;
|
||||||
|
}
|
||||||
|
images[j + 1] = key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void rescanSdCard()
|
||||||
|
{
|
||||||
|
if (mscBusy)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DBG_PRINTLN("Manueller Rescan angefordert...");
|
||||||
|
|
||||||
|
if (currentFile)
|
||||||
|
{
|
||||||
|
currentFile.close();
|
||||||
|
}
|
||||||
|
usb_msc.setUnitReady(false);
|
||||||
|
|
||||||
|
bool ok = sd.begin(SD_CS_PIN, SD_SCK_MHZ(4));
|
||||||
|
|
||||||
|
mountedIndex = -1;
|
||||||
|
|
||||||
|
if (ok)
|
||||||
|
{
|
||||||
|
DBG_PRINTLN("SD-Karte OK, Liste wird aktualisiert.");
|
||||||
|
scanImages(); // ruft rebuildImageList() intern auf
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DBG_PRINTLN("SD-Karte nicht gefunden!");
|
||||||
|
imageCount = 0;
|
||||||
|
rebuildImageList();
|
||||||
|
}
|
||||||
|
|
||||||
|
refreshStatusBar();
|
||||||
}
|
}
|
||||||
|
|
||||||
void mountImage(int8_t idx)
|
void mountImage(int8_t idx)
|
||||||
@@ -278,7 +497,6 @@ void mountImage(int8_t idx)
|
|||||||
DBG_PRINT(fname);
|
DBG_PRINT(fname);
|
||||||
DBG_PRINTLN("'");
|
DBG_PRINTLN("'");
|
||||||
|
|
||||||
// USB-Verbindung kurz trennen, damit das Betriebssystem das Medium neu einliest
|
|
||||||
usb_msc.setUnitReady(false);
|
usb_msc.setUnitReady(false);
|
||||||
TinyUSBDevice.detach();
|
TinyUSBDevice.detach();
|
||||||
delay(200);
|
delay(200);
|
||||||
@@ -303,21 +521,26 @@ void mountImage(int8_t idx)
|
|||||||
currentFilename = fname;
|
currentFilename = fname;
|
||||||
mountedIndex = idx;
|
mountedIndex = idx;
|
||||||
|
|
||||||
// 1.44 MB Standard Disketten-Geometrie (2880 Sektoren à 512 Byte)
|
uint32_t numSectors = currentFile.fileSize() / 512UL;
|
||||||
usb_msc.setCapacity(2880, 512);
|
DBG_PRINT("Dateigroesse: "); DBG_PRINT(currentFile.fileSize());
|
||||||
|
DBG_PRINT(" Bytes -> "); DBG_PRINT(numSectors); DBG_PRINTLN(" Sektoren");
|
||||||
|
|
||||||
|
usb_msc.setCapacity(numSectors, 512);
|
||||||
usb_msc.setUnitReady(true);
|
usb_msc.setUnitReady(true);
|
||||||
|
|
||||||
TinyUSBDevice.attach();
|
TinyUSBDevice.attach();
|
||||||
|
|
||||||
|
refreshStatusBar();
|
||||||
}
|
}
|
||||||
|
|
||||||
void setup()
|
void setup()
|
||||||
{
|
{
|
||||||
Serial.begin(115200);
|
Serial.begin(115200);
|
||||||
#if DEBUG_ENABLED
|
#if DEBUG_ENABLED
|
||||||
delay(2000); // Zeit, um den Seriellen Monitor rechtzeitig zu oeffnen
|
delay(2000);
|
||||||
#endif
|
#endif
|
||||||
DBG_PRINTLN();
|
DBG_PRINTLN();
|
||||||
DBG_PRINTLN("=== Bochser Floppy-Emulator (standalone) startet ===");
|
DBG_PRINTLN("=== Bochser Floppy-Emulator (LVGL) startet ===");
|
||||||
|
|
||||||
pinMode(BTN_PREV_PIN, INPUT_PULLUP);
|
pinMode(BTN_PREV_PIN, INPUT_PULLUP);
|
||||||
pinMode(BTN_NEXT_PIN, INPUT_PULLUP);
|
pinMode(BTN_NEXT_PIN, INPUT_PULLUP);
|
||||||
@@ -326,18 +549,40 @@ void setup()
|
|||||||
pinMode(TFT_BL_PIN, OUTPUT);
|
pinMode(TFT_BL_PIN, OUTPUT);
|
||||||
digitalWrite(TFT_BL_PIN, HIGH);
|
digitalWrite(TFT_BL_PIN, HIGH);
|
||||||
|
|
||||||
|
pinMode(ACTIVITY_LED_PIN, OUTPUT);
|
||||||
|
digitalWrite(ACTIVITY_LED_PIN, LOW);
|
||||||
|
|
||||||
DBG_PRINTLN("Initialisiere Display...");
|
DBG_PRINTLN("Initialisiere Display...");
|
||||||
SPI1.setRX(28);
|
SPI1.setRX(28);
|
||||||
SPI1.setSCK(10);
|
SPI1.setSCK(10);
|
||||||
SPI1.setTX(11);
|
SPI1.setTX(11);
|
||||||
|
|
||||||
tft.initR(INITR_BLACKTAB);
|
tft.initR(INITR_BLACKTAB);
|
||||||
tft.setColRowStart(1, 0);
|
tft.setColRowStart(2, 1); // Empirisch ermittelter Offset für dieses Panel
|
||||||
tft.setRotation(1);
|
tft.setRotation(1);
|
||||||
|
tft.fillScreen(ST77XX_BLACK);
|
||||||
updateDisplay();
|
|
||||||
DBG_PRINTLN("Display initialisiert.");
|
DBG_PRINTLN("Display initialisiert.");
|
||||||
|
|
||||||
|
DBG_PRINTLN("Initialisiere LVGL...");
|
||||||
|
lv_init();
|
||||||
|
lv_tick_set_cb(millis);
|
||||||
|
|
||||||
|
static uint16_t lvBuf[SCREEN_W * SCREEN_H]; // explizit 16-bit RGB565, unabhaengig von lv_color_t
|
||||||
|
lvDisplay = lv_display_create(SCREEN_W, SCREEN_H);
|
||||||
|
lv_display_set_color_format(lvDisplay, LV_COLOR_FORMAT_RGB565);
|
||||||
|
lv_display_set_flush_cb(lvDisplay, disp_flush);
|
||||||
|
lv_display_set_buffers(lvDisplay, lvBuf, NULL, sizeof(lvBuf), LV_DISPLAY_RENDER_MODE_PARTIAL);
|
||||||
|
|
||||||
|
lvKeypad = lv_indev_create();
|
||||||
|
lv_indev_set_type(lvKeypad, LV_INDEV_TYPE_KEYPAD);
|
||||||
|
lv_indev_set_read_cb(lvKeypad, keypad_read_cb);
|
||||||
|
|
||||||
|
lvGroup = lv_group_create();
|
||||||
|
lv_indev_set_group(lvKeypad, lvGroup);
|
||||||
|
|
||||||
|
buildUi();
|
||||||
|
DBG_PRINTLN("LVGL initialisiert.");
|
||||||
|
|
||||||
DBG_PRINTLN("Initialisiere SD-Karte (SPI0)...");
|
DBG_PRINTLN("Initialisiere SD-Karte (SPI0)...");
|
||||||
SPI.setRX(SD_MISO_PIN);
|
SPI.setRX(SD_MISO_PIN);
|
||||||
SPI.setTX(SD_MOSI_PIN);
|
SPI.setTX(SD_MOSI_PIN);
|
||||||
@@ -346,6 +591,7 @@ void setup()
|
|||||||
if (!sd.begin(SD_CS_PIN, SD_SCK_MHZ(4)))
|
if (!sd.begin(SD_CS_PIN, SD_SCK_MHZ(4)))
|
||||||
{
|
{
|
||||||
DBG_PRINTLN("SD-Karte nicht gefunden!");
|
DBG_PRINTLN("SD-Karte nicht gefunden!");
|
||||||
|
rebuildImageList();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -353,9 +599,6 @@ void setup()
|
|||||||
scanImages();
|
scanImages();
|
||||||
}
|
}
|
||||||
|
|
||||||
updateDisplay();
|
|
||||||
|
|
||||||
// USB MSC konfigurieren
|
|
||||||
DBG_PRINTLN("Initialisiere USB-MSC...");
|
DBG_PRINTLN("Initialisiere USB-MSC...");
|
||||||
usb_msc.setID("Hirnfrei", "Bochser", "1.0");
|
usb_msc.setID("Hirnfrei", "Bochser", "1.0");
|
||||||
usb_msc.setReadWriteCallback(msc_read_cb, msc_write_cb, msc_flush_cb);
|
usb_msc.setReadWriteCallback(msc_read_cb, msc_write_cb, msc_flush_cb);
|
||||||
@@ -367,44 +610,35 @@ void setup()
|
|||||||
|
|
||||||
void loop()
|
void loop()
|
||||||
{
|
{
|
||||||
if (imageCount == 0)
|
bool prevRaw = digitalRead(BTN_PREV_PIN) == LOW;
|
||||||
|
bool nextRaw = digitalRead(BTN_NEXT_PIN) == LOW;
|
||||||
|
|
||||||
|
if (prevRaw && nextRaw)
|
||||||
{
|
{
|
||||||
|
if (!comboActive)
|
||||||
|
{
|
||||||
|
comboActive = true;
|
||||||
|
rescanSdCard();
|
||||||
|
|
||||||
|
btnPrev.stableState = LOW; btnPrev.lastReading = LOW; btnPrev.lastChangeMs = millis();
|
||||||
|
btnNext.stableState = LOW; btnNext.lastReading = LOW; btnNext.lastChangeMs = millis();
|
||||||
|
}
|
||||||
|
lv_timer_handler();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
if (buttonPressed(btnPrev))
|
|
||||||
{
|
{
|
||||||
browseIndex = (browseIndex - 1 + imageCount) % imageCount;
|
comboActive = false;
|
||||||
if (Serial)
|
|
||||||
{
|
|
||||||
DBG_PRINT("Zurueck -> Index "); DBG_PRINT(browseIndex);
|
|
||||||
DBG_PRINT(" ("); DBG_PRINT(images[browseIndex].name); DBG_PRINTLN(")");
|
|
||||||
}
|
|
||||||
updateDisplay();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (buttonPressed(btnNext))
|
lv_timer_handler();
|
||||||
|
|
||||||
|
static bool lastMscBusyShown = false;
|
||||||
|
if (mscBusy != lastMscBusyShown)
|
||||||
{
|
{
|
||||||
browseIndex = (browseIndex + 1) % imageCount;
|
lastMscBusyShown = mscBusy;
|
||||||
if (Serial)
|
lv_obj_set_style_bg_color(activityDot, mscBusy ? lv_color_hex(0x00E000) : lv_color_hex(0x333333), 0);
|
||||||
{
|
|
||||||
DBG_PRINT("Weiter -> Index "); DBG_PRINT(browseIndex);
|
|
||||||
DBG_PRINT(" ("); DBG_PRINT(images[browseIndex].name); DBG_PRINTLN(")");
|
|
||||||
}
|
|
||||||
updateDisplay();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (buttonPressed(btnSel))
|
delay(5);
|
||||||
{
|
|
||||||
DBG_PRINT("Auswahl gedrueckt, Index "); DBG_PRINTLN(browseIndex);
|
|
||||||
if (browseIndex != mountedIndex)
|
|
||||||
{
|
|
||||||
mountImage(browseIndex);
|
|
||||||
updateDisplay();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
DBG_PRINTLN("Bereits gemountet, keine Aktion.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user