refactor: Display-Firmware neutral als P4_Display_Firmware führen
Der Ordner hieß JC_Display_Firmware, bedient seit 1.6.0 aber auch das Waveshare 7inch DSI LCD (H) am ESP32-P4-Pico. Der Name führte in die Irre. - JC_Display_Firmware/ -> P4_Display_Firmware/ (samt Sketch, den Arduino gleichnamig zum Ordner verlangt) - Doku/JC-Display_UART-Protokoll.md -> Doku/P4-Display_UART-Protokoll.md, Verweise und Titel angepasst - Verweise in CLAUDE.md, README und Quelltextköpfen nachgezogen Nur Namen und Pfade, keine Logikänderung. Der Vendor-Ordner JC_Display_Firmware_7zoll/ behält seinen Namen, ebenso die Bezeichner JC_PANEL_TYPE/jc_board_bringup im Quelltext. Enthält außerdem die bereits im Arbeitsverzeichnis liegende, noch nicht committete Ergänzung des Git-Workflows in CLAUDE.md (PRs über die Gitea-API statt der hängenden tea-CLI). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018QoLDQeUh1dCQz7yYZVb4Y
This commit is contained in:
co-authored by
Claude Opus 5
parent
e0c6784ecf
commit
4f9f686d81
@@ -0,0 +1,304 @@
|
||||
Version 1.6.1:
|
||||
- Waveshare 7" (H): Bild und Touch lassen sich um 180 Grad drehen, wenn das Panel auf dem
|
||||
Kopf eingebaut ist. Schalter WS7_ROTATE_180 in pins_config.h, Vorgabe ist gedreht.
|
||||
Die Drehung übernimmt der PPA-Grafikbeschleuniger des ESP32-P4 - die Software-Rotation
|
||||
des LVGL-Ports rechnet nur in RGB565 und wäre für dieses Panel unbrauchbar.
|
||||
- Der Touch dreht mit, sonst läge die Bedienung spiegelbildlich zum Bild.
|
||||
|
||||
Version 1.6.0:
|
||||
- Neu: Dieselbe Firmware läuft jetzt auch auf einem Waveshare ESP32-P4-Pico mit dem
|
||||
Waveshare 7inch DSI LCD (H) (1280x720). Umgeschaltet wird über „JC_PANEL_TYPE" in
|
||||
config.h; WS_PANEL_7H ist ab dieser Version die Vorgabe, JC_PANEL_43 und JC_PANEL_70
|
||||
bleiben unverändert verfügbar.
|
||||
- Das Waveshare-Panel hängt hinter einer Bridge, die ausschließlich RGB888 annimmt. LVGL
|
||||
rechnet für dieses Panel deshalb mit 24 statt 16 Bit Farbtiefe. lv_conf.h liest dazu
|
||||
JC_PANEL_TYPE aus config.h, damit Sketch und LVGL-Bibliothek garantiert dieselbe
|
||||
Farbtiefe benutzen; display_hal.cpp bricht den Build ab, falls sie auseinanderlaufen.
|
||||
Der Panelwechsel muss aus diesem Grund in config.h erfolgen und nicht per Compiler-Flag.
|
||||
- Bringup ohne Hersteller-Panel-Treiber: DSI-Bus, Kommandokanal und DPI-Panel werden direkt
|
||||
angelegt. Entscheidend ist die Legacy-Referenztaktquelle PLL_F20M - mit der Standardquelle
|
||||
bleibt der Bildschirm auf ESP32-P4 Rev. 1.3 / ECO2 schwarz. Die Zeitbasis (2 Lanes zu
|
||||
1250 Mbit/s, 80 MHz DPI-Takt, Austastlücken 64/64/64, rund 60 Hz) steht als WS7_*-Werte
|
||||
in pins_config.h und ist dort anpassbar, falls das Bild reißt.
|
||||
- Panel-Freigabe und Helligkeit laufen beim Waveshare nicht über einen PWM-Pin, sondern über
|
||||
Register der Bridge auf I2C-Adresse 0x45. Die Hintergrundbeleuchtung wird bewusst erst
|
||||
eingeschaltet, wenn die DSI-Ausgabe steht - sonst blitzt beim Start Bildrauschen auf.
|
||||
Standby-Abdunklung und „Uhrzeit im Standby" funktionieren wie bei den JC-Panels.
|
||||
- Touch: Der GT9271 des Waveshare-Panels benutzt dieselbe Registerbelegung wie der GT911,
|
||||
liegt aber auf I2C-Adresse 0x14 und meldet im Hochformat. X und Y werden deshalb getauscht
|
||||
und auf die Bildfläche begrenzt; Ausreißer des Controllers landen nicht mehr als Klick am
|
||||
Bildrand. Sollte die Bedienung spiegelverkehrt reagieren, lässt sich das mit
|
||||
WS7_TOUCH_MIRROR_RAW_X / _Y in pins_config.h umdrehen.
|
||||
- Der Framebuffer wächst auf 3 x 2,8 MB (rund 8,3 MB PSRAM). Auf dem P4-Pico mit 32 MB
|
||||
PSRAM ist das unkritisch.
|
||||
- Die UART-Pins zur Hauptplatine sind für das Waveshare-Board getrennt eingestellt, weil
|
||||
dessen 40-poliger Header eine andere Belegung hat als die JC-Panels: TX = GPIO17,
|
||||
RX = GPIO18 (JC-Panels unverändert TX = GPIO33, RX = GPIO31).
|
||||
|
||||
Version 1.5.0:
|
||||
- Neu: Dieselbe Firmware unterstützt jetzt auch das 7-Zoll-Display Guition JC1060P470C-I-W
|
||||
(JD9165, 1024x600) zusätzlich zum bisherigen 4,3-Zöller JC4880P443C-I-W (ST7701, 480x800).
|
||||
Umgeschaltet wird über „JC_PANEL_TYPE" in config.h (JC_PANEL_43 / JC_PANEL_70) oder per
|
||||
Compiler-Flag -DJC_PANEL_TYPE=70. Vorgabe bleibt das 4,3-Zoll-Panel, dessen Verhalten sich
|
||||
nicht ändert.
|
||||
- Panel-Treiber esp_lcd_jd9165.c/.h aus dem Hersteller-Paket übernommen; board_bringup.c legt
|
||||
je nach Panel den passenden DSI-Bus, die DPI-Zeitbasis und den Herstellertreiber an.
|
||||
- pins_config.h liefert Auflösung, LCD-Reset-Pin (4,3": GPIO5, 7,0": GPIO27) und Ausrichtung
|
||||
panelabhängig. Das 7-Zoll-Panel ist nativ Querformat, deshalb entfällt dort die 270-Grad-
|
||||
Rotation samt PPA-Beschleunigung.
|
||||
- Der GT911 des 7-Zöllers meldet Rohkoordinaten im Raster 800x480 statt 1024x600. Die
|
||||
Umrechnung hängt am process_coordinates-Hook von esp_lcd_touch, damit die Herstellerdatei
|
||||
esp_lcd_touch_gt911.c unverändert bleibt. Ohne sie wäre nur der linke obere Bildteil
|
||||
bedienbar.
|
||||
- Die Oberfläche rechnet nicht mehr mit fest verdrahteten 480 Pixeln Höhe, sondern mit
|
||||
DISP_VER_RES. Layout und Schriften bleiben sonst unverändert: Das Dashboard arbeitet fast
|
||||
durchgehend mit Prozentmaßen, und die feineren Pixelabstände des 7-Zöllers (170 statt 213 dpi)
|
||||
lassen dieselben Schriftgrößen physisch größer erscheinen.
|
||||
- Die UART-Verkabelung ist bei beiden Panels gleich (P4 GPIO33 = TX, GPIO31 = RX). Beim
|
||||
7-Zöller liegen diese Pins allerdings auf der FPC-Buchse FPC4 (1,0 mm, 16-polig) statt auf
|
||||
einer Stiftleiste - dort wird ein Adapterkabel gebraucht.
|
||||
|
||||
Version 1.4.2:
|
||||
- Bug-Fix: Beim Aufwecken über Antippen erschien die Auswahl „Espresso oder Cold Extraction"
|
||||
manchmal nicht. Der Dialog entschied nach einer anderen Bedingung, ob er die Auswahl ANZEIGT
|
||||
(verlangte zusätzlich cxArmable), als er beim Absenden benutzte, um die Rückfrage der S3 zu
|
||||
UNTERDRÜCKEN. In der Lücke dazwischen zeigte er nur „Aufwecken", sendete aber „wakeEspresso" -
|
||||
und damit war die Frage auch auf der S3 erledigt. Beide Stellen benutzen jetzt dieselbe Funktion
|
||||
wake_choice_offered().
|
||||
- Hat der Dialog die Auswahl nicht angeboten, sendet er wieder „deactivateStandby". Dann öffnet die
|
||||
S3 ihr eigenes Auswahlfenster, und die Frage kommt trotzdem - derselbe Weg wie beim Aufwecken über
|
||||
den Schalter. Die Auswahl kann damit nicht mehr ersatzlos entfallen.
|
||||
|
||||
Version 1.4.1:
|
||||
- Passend zu S3 5.6.2: Die Auswahl beim Aufwecken erscheint auch dann, wenn der Cold-Extraction-Modus
|
||||
bereits aktiv ist - sie geht in beide Richtungen. „Espresso" schaltet den Modus dann ab.
|
||||
- Der Cold-Extraction-Knopf zeigt den Ist-Zustand: Bei aktivem Modus ist er hervorgehoben und heißt
|
||||
„Cold Extraction (aktiv lassen)", sonst „Cold Extraction (Wasser bleibt kalt)".
|
||||
|
||||
Version 1.4.0:
|
||||
- Die Auswahl „Espresso oder Cold Extraction" erscheint jetzt auch beim Aufwecken über den Schalter
|
||||
(braucht S3-Firmware ab 5.6.0). Meldet die S3 ein offenes Auswahlfenster (cxWakeChoice), blendet das
|
||||
Display den Dialog von sich aus ein - ohne Antippen - und zeigt die verbleibende Bedenkzeit als
|
||||
Countdown. „Abbrechen" entfällt in diesem Fall, weil es nichts abzubrechen gibt: Läuft die Zeit ab,
|
||||
heizt die Maschine normal auf.
|
||||
- Der Espresso-Knopf sendet „wakeEspresso" statt „deactivateStandby", sobald die Auswahl relevant ist.
|
||||
Damit wird die Entscheidung auf der S3 vermerkt und nicht direkt danach erneut gefragt.
|
||||
- Der Cold-Extraction-Knopf beendet den Standby nur noch, wenn er überhaupt aktiv ist - im Auswahlfenster
|
||||
nach dem Aufwecken schaltet er nur den Modus.
|
||||
|
||||
Version 1.3.0:
|
||||
- Der Aufweck-Dialog wird zur Auswahl: „Womit aufwecken?" mit den Knöpfen „Espresso" (heizt normal auf)
|
||||
und „Cold Extraction (Wasser bleibt kalt)". Braucht S3-Firmware ab 5.5.0.
|
||||
Bei „Cold Extraction" wird erst der Modus scharf geschaltet und dann der Standby beendet, damit der
|
||||
Wasserkreis nicht zwischendurch anheizt.
|
||||
- Steuerbar über die neue Einstellung „Beim Aufwecken fragen" (Cold-Extraction-Seite, Abschnitt
|
||||
Grundeinstellung; identisch zur Web-UI). Ist sie aus, erscheint wieder der bisherige Dialog mit
|
||||
„Aufwecken" und „Abbrechen" - der Zwischenschritt entfällt dann komplett.
|
||||
|
||||
Version 1.2.1:
|
||||
- Bug-Fix: Während einer kalten Extraktion zeigten Waage-Kachel und Live-Bezugsschirm das Zielgewicht
|
||||
des normalen Espresso-Bezugs (bbwTarget) statt des Ziels der kalten Extraktion (cxTarget). Außerdem
|
||||
erschienen Gewichtsanzeige und Fortschrittsbalken nur, wenn Brew-by-Weight aktiv war - bei kalter
|
||||
Extraktion ist das typischerweise aus, obwohl ein Ziel existiert. Beide Anzeigen nutzen jetzt
|
||||
cxTarget, sobald cxShot gesetzt ist.
|
||||
|
||||
Version 1.2.0:
|
||||
- Neue Seite „Cold Extraction" mit eigenem Menüeintrag (braucht S3-Firmware ab 5.4.0). Dort stehen jetzt
|
||||
alle Parameter der kalten Extraktion und lassen sich direkt am Display ändern: Freigabetemperatur,
|
||||
Vorbenetzung (Dauer + Pumpenleistung), Hauptbezug (Pulsperiode + Pumpenleistung), Zielgewicht,
|
||||
maximale Bezugsdauer sowie der Pumpenschutz. Gespeichert wird über das erweiterte saveBrew-Kommando.
|
||||
- Der Modus-Schalter ist von der Temperatur-Seite auf die neue Seite umgezogen (eine Stelle statt zwei).
|
||||
Der Knopf „Aufwecken mit Cold Extraction" im Standby-Dialog bleibt unverändert.
|
||||
- Brew-Control-Seite neu gegliedert: Statt einer durchlaufenden Liste unter Zwischenüberschriften liegt
|
||||
jede Funktion (Pre-Infusion, Brew-by-Time, Brew-by-Weight, Dampf-Timer) jetzt in einer eigenen Karte
|
||||
mit Titel und einer Zeile, die erklärt, was sie tut. Die neue Cold-Extraction-Seite nutzt dieselbe
|
||||
Gliederung.
|
||||
- Eingabefelder werden nur beim Öffnen der Seite aus dem Zustand gefüllt, damit laufende State-Pushes
|
||||
eine begonnene Eingabe nicht überschreiben (gleiches Verhalten wie auf der Brew-Control-Seite).
|
||||
|
||||
Version 1.1.3:
|
||||
- Aufweck-Dialog hat einen dritten Knopf: „Aufwecken mit Cold Extraction" (braucht S3 ab 5.3.5).
|
||||
Er stellt den Modus scharf und beendet erst danach den Standby - in dieser Reihenfolge, damit
|
||||
der Wasserkreis gar nicht erst anheizt. Ohne diesen Weg musste man normal aufwecken (Maschine
|
||||
heizt an) und den Modus danach umschalten. Der Knopf erscheint nur, wenn die Funktion
|
||||
freigeschaltet und umschaltbar ist und der Modus nicht ohnehin schon läuft.
|
||||
- Der Schalter „Cold Extraction" auf der Temperatur-Seite hängt jetzt am neuen State-Feld
|
||||
cxArmable statt an cxAllowed: umschaltbar auch im Standby und bei warmem Kessel, gesperrt nur
|
||||
während eines laufenden kalten Bezugs. Bei älterer S3-Firmware fällt das Feld auf cxEnabled
|
||||
zurück, der Schalter bleibt also bedienbar.
|
||||
- Erklärzeile im Standby: „Nach dem Aufwecken bleibt der Wasserkreis aus."
|
||||
|
||||
Version 1.1.2:
|
||||
- Cold Extraction: Fehlt eine nutzbare Waage, weist das Display jetzt dauerhaft darauf hin.
|
||||
Ohne Waage greifen weder Zielgewicht noch Stillstands-Erkennung, der Bezug endet erst nach der
|
||||
maximalen Bezugsdauer (braucht S3-Firmware ab 5.3.4).
|
||||
- Statuszeile: „Cold Extraction aktiv - ohne Waage, Ende nach Zeit" (gelb) statt des blauen
|
||||
Normalhinweises; beim Aktivieren zusätzlich kurz die Meldung zum neuen cxNotice-Code 4.
|
||||
- Erklärzeile unter dem Schalter und die Parameter-Anzeige auf der Brew-Seite ergänzen den
|
||||
Hinweis; bei aktivem Modus ohne Waage nennt die Zeile die Zeit statt des Zielgewichts.
|
||||
|
||||
Version 1.1.1:
|
||||
- Compiler-Warnungen des P4-Builds aufgeräumt (rein technisch, keine Funktionsänderung):
|
||||
- „LV_FS_DEFAULT_DRIVE_LETTER is deprecated": Die Option heißt ab LVGL 9.3
|
||||
LV_FS_DEFAULT_DRIVER_LETTER. Der alte Name funktionierte zwar noch, löste aber in
|
||||
lv_api_map_v9_1.h ein #warning aus - und zwar in JEDER Übersetzungseinheit, die lvgl.h
|
||||
einbindet. Das allein waren rund 60 Warnungen pro Build, die alles andere zugedeckt haben.
|
||||
In lv_conf.h umbenannt.
|
||||
- „bitwise operation between different enumeration types is deprecated": LV_PART_* und
|
||||
LV_STATE_* sind in LVGL 9 zwei verschiedene Enum-Typen; sie direkt mit | zu verknüpfen ist
|
||||
in C++20 abgekündigt. Neues Makro TH_SEL(part, state) in theme.h castet beide auf
|
||||
lv_style_selector_t (ohnehin uint32_t). Betraf 5 Stellen in ui.cpp und theme.h.
|
||||
- Bug-Fix: Der Puffer für die Cold-Extraction-Infozeile auf der Brew-Seite war mit 192 Bytes
|
||||
zu klein - der Text ist rund 205 Zeichen lang (das „°" zählt als zwei Bytes), die letzte
|
||||
Zeile wurde also abgeschnitten. Jetzt 256 Bytes.
|
||||
|
||||
Version 1.1.0:
|
||||
- Cold Extraction am Display bedienbar (braucht S3-Firmware ab 5.3.2):
|
||||
- Neuer Schalter „Cold Extraction" auf der Seite „Temperaturen" unter „Modi", darunter eine
|
||||
Erklärzeile mit dem aktuellen Zustand bzw. dem Sperrgrund. Schalter und Zeile bleiben
|
||||
unsichtbar, solange die S3 die Funktion nicht freigeschaltet meldet (auch bei älterer
|
||||
S3-Firmware, die die Felder gar nicht sendet).
|
||||
- Einschalten ist nur bei erteilter Freigabe möglich (Wasserkessel kalt genug, kein Standby /
|
||||
Wartung / Tuning); ausschalten immer, außer während eines laufenden kalten Bezugs. Gesendet
|
||||
wird gezielt startColdExtraction/stopColdExtraction statt eines Toggles, damit ein
|
||||
abgewiesener Befehl den Schalter nicht in einen nie bestätigten Zustand bringt.
|
||||
- Statuszeile: eigener Slot direkt hinter der Sicherheitsmeldung. Zeigt dauerhaft
|
||||
„Cold Extraction aktiv - Wasserkreis heizt nicht", bei fehlender Freigabe den Grund,
|
||||
während der Pumpen-Zwangspause den Pumpenschutz und transient einen abgewiesenen Start
|
||||
bzw. einen Abbruch wegen fehlenden Zulaufs.
|
||||
- Wasser-Kachel im Dashboard zeigt bei aktivem Modus „Cold Extraction - Heizen aus" statt
|
||||
eines Sollwerts, der nicht erreicht wird; der Aufheiz-Countdown ist dann unterdrückt.
|
||||
- Phasenleiste und Live-Bezugsschirm benennen die erste Phase eines kalten Bezugs
|
||||
„Vorbenetzung" (keine Pause-Phase) und zeigen die Pumpenpause an.
|
||||
- Brew-Seite: Cold-Extraction-Parameter als Anzeige (Freigabeschwelle, Vorbenetzung,
|
||||
Zielgewicht, Timeout) mit Hinweis, dass sie in der Web-UI eingestellt werden.
|
||||
- Statistik-Seite: kalte Bezüge sind mit „(kalt)" markiert und gehen nicht in die mittlere
|
||||
Bezugsdauer ein - gleiche Regel wie in der S3-Statistik.
|
||||
- Bug-Fix: Das Limit für eingehende UART-Zeilen (PROTO_RX_LINE_MAX) lag bei 2048 Bytes, während
|
||||
die State-Zeile der S3 bereits rund 2,1 KB erreicht - mit langem Status-, Profil- oder
|
||||
SSID-Text konnte sie darüber liegen. In dem Fall wurde die GANZE Zeile verworfen und das
|
||||
Display fror auf dem letzten Stand ein, ohne dass die Verbindung als tot erkannt wurde.
|
||||
Das Limit ist jetzt 4096 Bytes; der RX-Puffer selbst bleibt bei 2 KB reserviert, weil der
|
||||
interne RAM knapp ist und eine Arduino-String nicht ins PSRAM alloziert werden kann.
|
||||
|
||||
Version 1.0.13:
|
||||
- Profil-Schnellwahl + Anzeige des aktiven Profils (braucht S3-Firmware ab 5.1.1):
|
||||
- Neuer Profil-Chip im Header (auf allen Seiten sichtbar): zeigt das zuletzt geladene bzw.
|
||||
gespeicherte Profil; ein „*" hinter dem Namen bedeutet, dass seitdem profil-relevante
|
||||
Einstellungen geändert wurden. Lange Namen werden gekürzt; ohne bekanntes Profil
|
||||
(oder mit älterer S3-Firmware) bleibt der Chip unsichtbar.
|
||||
- Antippen des Chips öffnet die Profil-Schnellwahl: ein Overlay mit allen gespeicherten
|
||||
Profilen, das aktive mit Häkchen und Akzentfarbe hervorgehoben. Ein Tap lädt das Profil
|
||||
direkt (Toast bestätigt, Chip aktualisiert sich mit dem nächsten State) — zwei Berührungen
|
||||
vom Dashboard zum Profilwechsel statt Umweg über die Profile-Seite.
|
||||
- Profile-Seite: das aktive Profil wird in der Liste mit Häkchen-Symbol und Akzentfarbe
|
||||
markiert.
|
||||
|
||||
Version 1.0.12:
|
||||
- Verlauf-Chart: Flackernde Heizleistungs-Kurven in den großen Zeitfenstern behoben. Die
|
||||
Dezimierung pickte pro Chart-Punkt ein Einzelsample, dessen Abtast-Raster mit jedem
|
||||
Sekundentick um eine Position wanderte — die schnell schaltende Heizleistung (PWM)
|
||||
sprang dadurch sichtbar zwischen zwei Kurvenbildern hin und her. Jetzt wird über den
|
||||
gesamten Zeitabschnitt eines Chart-Punkts gemittelt: das Bild steht ruhig, und die
|
||||
Leistungs-Kurve zeigt die tatsächliche mittlere Heizleistung statt zufälliger
|
||||
Momentwerte. Im 2-min-Fenster (1 Sample je Punkt) ändert sich nichts.
|
||||
|
||||
Version 1.0.11:
|
||||
- Verlauf-Seite ausgebaut (nur P4, keine S3-/Protokolländerung nötig):
|
||||
- Umschaltbares Zeitfenster: Buttons „2 min / 10 min / 30 min / 60 min" in der Kopfzeile.
|
||||
Intern läuft eine 1-s-Historie als Ringpuffer über volle 60 Minuten; das Chart zeigt
|
||||
weiterhin 120 Punkte und dezimiert je Fenster. Dadurch ist beim Fensterwechsel der
|
||||
komplette Verlauf sofort da (kein leeres Chart, kein Neuaufbau). Die Wahl wird P4-lokal
|
||||
im NVS gespeichert und überlebt Neustarts.
|
||||
- Gestrichelte Soll-Linien für Wasser und Dampf im Chart (Farbe der jeweiligen Kurve,
|
||||
gedimmt). Die Y-Skala bezieht die Soll-Werte mit ein, damit die Linien im Bild liegen —
|
||||
aber nur für Kreise, deren Ist-Kurve im Fenster sichtbar ist (ein kalter Dampfkessel
|
||||
zieht die Skala nicht auseinander).
|
||||
- Heizleistung zuschaltbar: Button „Leistung" blendet Duty-Cycle Wasser/Dampf als gedimmte
|
||||
Zusatzkurven auf einer zweiten Achse (0–100 %) ein, inkl. Live-Legende. Einstellung
|
||||
ebenfalls P4-lokal im NVS.
|
||||
- Feinere Kurven: Die Temperatur-Historie wird jetzt in Zehntelgrad geführt und gezeichnet
|
||||
(vorher ganzzahlig gerundet) — PID-Schwingungen um ±1 °C sind damit sauber ablesbar.
|
||||
- Die ~29 KB Historie liegen im PSRAM (heap_caps_malloc, wie die LVGL-Framebuffer) —
|
||||
als statische Arrays sprengten sie den internen RAM (Linker: 107 % belegt).
|
||||
- Shot-Zusammenfassung: Karte auf 560 px verbreitert und Spaltenabstand zwischen
|
||||
Dauer/Gewicht/Flow deutlich vergrößert (28 px statt 10 px) — die drei Werte standen
|
||||
zu nahe beisammen und waren schlecht lesbar.
|
||||
- PID-AutoTune sichtbar gemacht: Während eines Tuning-Laufs erscheint auf der Verlauf-Seite
|
||||
eine Live-Karte (mit Spinner) — je Kreis Laufzeit, aktuelle Phase (heizt/kühlt ab) und
|
||||
Schwingungshub samt Mittelpunkt (Min/Max der Ist-Temperatur seit Start). Nach dem Ende
|
||||
zeigt die Karte ~30 s das Ergebnis (Erfolg/Abbruch samt Grund, farbcodiert). Die
|
||||
PID-Tuning-Meldung in der Statuszeile ist jetzt antippbar und springt zur Verlauf-Seite,
|
||||
wo die Schwingung auch als Kurve zu sehen ist.
|
||||
|
||||
Version 1.0.10:
|
||||
- Live-Bezugsschirm (abschaltbar): Beim Start eines echten Bezugs blendet sich ein Vollbild-Overlay
|
||||
weich ein — große Bezugszeit, Live-Gewicht und Flow samt Flow-Verlaufskurve der letzten 30 Sekunden
|
||||
(nur bei verbundener Waage), Fortschrittsbalken Richtung Zielgewicht (Brew-by-Weight) bzw. Zielzeit
|
||||
(Brew-by-Time) und Pre-Infusion-Phasenhinweis mit Restzeit. Über das X oben rechts für den laufenden
|
||||
Bezug schließbar; beim nächsten Bezug erscheint er wieder. Ein/Aus-Schalter auf der Info-Seite unter
|
||||
„Darstellung", die Einstellung wird P4-lokal im NVS gespeichert und überlebt Neustarts. Während
|
||||
Flush und Reinigungsassistent erscheint das Overlay bewusst nicht.
|
||||
- Shot-Zusammenfassung aufgewertet: Die Karte nach Bezugsende blendet jetzt weich ein/aus (Fade),
|
||||
wirft einen Schlagschatten und zeigt Dauer, Endgewicht und mittleren Flow als große Werte mit
|
||||
gedimmter Beschriftung statt reiner Textzeilen (Gewicht/Flow nur bei aktiver Waage).
|
||||
- Pre-Infusion-Phasenanzeige auf dem Dashboard: Während eines Bezugs mit aktiver Pre-Infusion zeigt
|
||||
eine Schrittleiste die Phasen „Pre-Infusion → Pause → Extraktion" an — erledigte Schritte mit Haken,
|
||||
die aktive Phase hervorgehoben (Gold) inkl. Restzeit-Countdown (100-ms-Takt). Die aktive Phase kommt
|
||||
quelltreu von der S3 (neues State-Feld "piState", ab S3-Firmware 5.0.12); bei älterer S3-Firmware wird
|
||||
sie zeitbasiert aus den konfigurierten Phasendauern abgeleitet. Ist keine Pause konfiguriert (0 s),
|
||||
wird der Pause-Schritt ausgeblendet.
|
||||
- Temperatur-Seite: Solltemperaturen für Wasser und Dampf werden jetzt über radiale Drehregler (Arc,
|
||||
270°) mit großem Wert mittig und −/+-Feinjustage eingestellt. Gesendet wird entprellt (nach kurzer
|
||||
Bedienpause bzw. sofort beim Loslassen des Reglers oder Verlassen der Seite), damit Drag-Gesten die
|
||||
S3 nicht mit setTemp-Kommandos und EEPROM-Schreibzugriffen fluten. Tippen in die Reglermitte löst
|
||||
bewusst keinen Wertsprung aus (ADV_HITTEST: nur Ring/Griff reagieren).
|
||||
- Dashboard-Feinschliff (nur P4, keine S3-/Protokolländerung nötig):
|
||||
- Weiche Temperatur-Zustandsfarben: Der große Ist-Wert färbt sich stufenlos über den Abstand
|
||||
zum Soll (kontinuierliche Farbinterpolation statt harter Stufen) — Blau beim Aufheizen,
|
||||
Gold kurz vor dem Ziel, Grün im Zielband (±0,5 °C), Richtung Warngelb bei deutlichem
|
||||
Überschwinger. Dazu neue „BEREIT"-Pille im Header (auf allen Seiten sichtbar), wenn der
|
||||
Wasserkessel — und der Dampfkessel, sofern er heizt — im Zielband liegt, kein Fehler/
|
||||
Standby/Eco aktiv ist und kein Aufheiz-Countdown läuft; mit Hysterese gegen Flackern
|
||||
an der Bandgrenze, erscheint und verschwindet weich (Fade).
|
||||
- Umschaltbares Temperatur-Design: Die Dashboard-Temperaturanzeige gibt es jetzt in fünf
|
||||
Varianten — „Klassisch" (bisherige Karten mit Prozentbalken), „Instrumente" (freistehende
|
||||
Rundinstrumente im Manometer-Look: Metall-Bezel mit Verlauf und Schlagschatten für eine
|
||||
leichte 3D-Wirkung, 270°-Bogen füllt sich in der Zustandsfarbe, Soll-Wert in der
|
||||
Bogen-Lücke), „Minimal" (komplett rahmenlos, nur große Werte mit dünner Ist/Soll-Linie),
|
||||
„Soft-3D" (Neumorph: Fläche fast im Hintergrundton wirkt herausgedrückt — dunkler
|
||||
Schatten unten rechts, heller Lichtschein oben links, eingelassener Balken) und
|
||||
„Thermometer" (vertikale Säulen, die sich von unten in der Zustandsfarbe füllen, mit
|
||||
Glanzstreifen als Glas-Effekt und rundem Kolben unten). Außer bei „Klassisch" folgt
|
||||
auch die Füllfarbe von Balken/Bogen/Säule stufenlos der Zustandsfarbe des Ist-Werts.
|
||||
Umschalter auf der Info-Seite unter „Darstellung"; die Wahl wird P4-lokal im NVS
|
||||
gespeichert und überlebt Neustarts. Standard: Instrumente.
|
||||
- Konsolidierte Statuszeile: Die sechs gestapelten Dashboard-Banner (Sicherheit, Dampf,
|
||||
Reinigung, PID-Tuning, Wartung, Aufheiz-Countdown) teilen sich jetzt EINE feste einzeilige
|
||||
Meldung — mehrere aktive Hinweise rotieren alle 4 s weich durch (Fade) statt das Layout
|
||||
zu verschieben, Priorität Rot > Gelb > Blau. Wartungs- und Reinigungsmeldung bleiben
|
||||
antippbar (Serviceseite bzw. Reinigungs-Sub-Screen), Überlängen laufen als Laufschrift durch.
|
||||
|
||||
Version 1.0.9:
|
||||
- Weiche Übergänge (dezente Fade-Animationen) hinzugefügt:
|
||||
- Start-Animation: Boot-Splash mit Logo, Untertitel, Lade-Spinner ("Verbinde mit Maschine...")
|
||||
und Firmware-Stand; blendet sich beim Start weich ein und beim ersten State von der S3
|
||||
(Verbindung steht) wieder aus.
|
||||
- Einschlaf-Animation: Die Standby-Uhr wird beim Wechsel in den Standby weich eingeblendet.
|
||||
- Aufweck-Animation: Beim Aufwecken blendet das Standby-Overlay weich aus und gibt das
|
||||
Dashboard frei.
|
||||
- Umsetzung speicher-neutral über das 'opa'-Style (kein opa_layered/Vollbild-Puffer), um den
|
||||
LVGL-Speicherpool nicht zu belasten. Bei ausgeschalteter Standby-Uhr (Display aus) gibt es
|
||||
weiterhin keine Einschlaf-Animation (schwarzer Bildschirm bietet keine Anzeigefläche).
|
||||
|
||||
Version 1.0.8:
|
||||
- Statistik-Seite hinzugefügt
|
||||
|
||||
Version 1.0.7:
|
||||
- Brew-by-Weight-Gewichtseingabelimit auf 150g erhöht, um auch Heißwasserbzug für Americano oder ähnliches via BBW zu ermöglichen
|
||||
|
||||
Version 1.0.6:
|
||||
- Boot-Loop-Schutz implementiert:
|
||||
P4-Boot-Loop-Schutz + „Update-Modus", der den UART-OTA offenhält.
|
||||
Wenn die P4-Firmware in eine Absturzschleife läuft, erkennt sie das (RTC-Zähler wie beim S3) und
|
||||
startet in einen abgespeckten Modus, der bewusst das riskanteste überspringt — vor allem die Display-/LVGL-Init (der wahrscheinlichste P4-Absturzgrund, siehe unser LVGL-Mem-Pool-Thema) — und nur den UART-Empfang + OTA-Receiver laufen lässt. Dann kann der S3 die P4-Firmware jederzeit per UART neu flashen, auch wenn die normale P4-Oberfläche kaputt ist. (Optional: eine ganz simple statische „Update-Modus"-Bildschirmmeldung, falls das Panel-Bringup als sicher genug gilt.)
|
||||
Binary file not shown.
BIN
Binary file not shown.
@@ -0,0 +1 @@
|
||||
The sample program has a video playback function, and copying the MJPEG folder to the TF card can play videos. The speaker needs to be connected externally. The TF card capacity should be less than 32GB, formatted as FAT32.
|
||||
@@ -0,0 +1,2 @@
|
||||
# 请使用arduino_esp32_v3.2.1版本
|
||||
#lvgl v8.4.0
|
||||
@@ -0,0 +1,237 @@
|
||||
#pragma GCC push_options
|
||||
#pragma GCC optimize("O3")
|
||||
|
||||
#include <Arduino.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "lvgl.h"
|
||||
#include "WiFi.h"
|
||||
#include "driver/i2c_master.h"
|
||||
#include "demos/lv_demos.h"
|
||||
#include "pins_config.h"
|
||||
#include "src/lcd/st7701_lcd.h"
|
||||
#include "src/touch/gt911_touch.h"
|
||||
|
||||
bsp_lcd_handles_t lcd_panels;
|
||||
|
||||
st7701_lcd lcd = st7701_lcd(LCD_RST);
|
||||
gt911_touch touch = gt911_touch(TP_I2C_SDA, TP_I2C_SCL, TP_RST, TP_INT);
|
||||
|
||||
static lv_disp_draw_buf_t draw_buf;
|
||||
static lv_color_t *buf;
|
||||
static lv_color_t *buf1;
|
||||
|
||||
LV_FONT_DECLARE(weiruanyahei_14);
|
||||
|
||||
lv_obj_t *obj = NULL;
|
||||
lv_obj_t *label[25];
|
||||
|
||||
static bool lvgl_port_flush_dpi_panel_ready_callback(esp_lcd_panel_handle_t panel_io, esp_lcd_dpi_panel_event_data_t *edata, void *user_ctx)
|
||||
{
|
||||
lv_disp_drv_t *disp_drv = (lv_disp_drv_t *)user_ctx;
|
||||
assert(disp_drv != NULL);
|
||||
// lv_disp_flush_ready(disp_drv);
|
||||
lv_disp_flush_ready(disp_drv);
|
||||
|
||||
// if (disp_ctx->trans_size && disp_ctx->trans_sem) {
|
||||
// xSemaphoreGiveFromISR(disp_ctx->trans_sem, &taskAwake);
|
||||
// }
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// 显示刷新
|
||||
void my_disp_flush(lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *color_p)
|
||||
{
|
||||
const int offsetx1 = area->x1;
|
||||
const int offsetx2 = area->x2;
|
||||
const int offsety1 = area->y1;
|
||||
const int offsety2 = area->y2;
|
||||
lcd.lcd_draw_bitmap(offsetx1, offsety1, offsetx2 + 1, offsety2 + 1, &color_p->full);
|
||||
// lv_disp_flush_ready(disp); // 告诉lvgl刷新完成
|
||||
}
|
||||
|
||||
void my_touchpad_read(lv_indev_drv_t *indev_driver, lv_indev_data_t *data)
|
||||
{
|
||||
bool touched;
|
||||
uint16_t touchX, touchY;
|
||||
|
||||
touched = touch.getTouch(&touchX, &touchY);
|
||||
// touchX = 800 - touchX;
|
||||
|
||||
if (!touched)
|
||||
{
|
||||
data->state = LV_INDEV_STATE_REL;
|
||||
}
|
||||
else
|
||||
{
|
||||
data->state = LV_INDEV_STATE_PR;
|
||||
|
||||
// 设置坐标
|
||||
data->point.x = touchX;
|
||||
data->point.y = touchY;
|
||||
Serial.printf("x=%d,y=%d \r\n",touchX,touchY);
|
||||
}
|
||||
}
|
||||
|
||||
static void lvgl_port_update_callback(lv_disp_drv_t *drv)
|
||||
{
|
||||
switch (drv->rotated) {
|
||||
case LV_DISP_ROT_NONE:
|
||||
touch.set_rotation(0);
|
||||
break;
|
||||
case LV_DISP_ROT_90:
|
||||
touch.set_rotation(1);
|
||||
break;
|
||||
case LV_DISP_ROT_180:
|
||||
touch.set_rotation(2);
|
||||
break;
|
||||
case LV_DISP_ROT_270:
|
||||
touch.set_rotation(3);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void wifiScanTask(void *arg)
|
||||
{
|
||||
while(1)
|
||||
{
|
||||
printf("Scan start\r\n");
|
||||
|
||||
// WiFi.scanNetworks will return the number of networks found.
|
||||
int n = WiFi.scanNetworks();
|
||||
printf("Scan done\r\n");
|
||||
if(n > 25)
|
||||
n=25;
|
||||
if (n == 0) {
|
||||
printf("no networks found\r\n");
|
||||
} else {
|
||||
printf("%d",n);
|
||||
printf(" networks found\r\n");
|
||||
printf("Nr | SSID | RSSI | CH | Encryption\r\n");
|
||||
for (int i = 0; i < n; ++i) {
|
||||
// Print SSID and RSSI for each network found
|
||||
printf("%2d", i + 1);
|
||||
printf(" | ");
|
||||
printf("%-32.32s", WiFi.SSID(i).c_str());
|
||||
printf(" | ");
|
||||
printf("%4ld", WiFi.RSSI(i));
|
||||
printf(" | ");
|
||||
printf("%2ld", WiFi.channel(i));
|
||||
printf(" | ");
|
||||
switch (WiFi.encryptionType(i)) {
|
||||
case WIFI_AUTH_OPEN: printf("open"); break;
|
||||
case WIFI_AUTH_WEP: printf("WEP"); break;
|
||||
case WIFI_AUTH_WPA_PSK: printf("WPA"); break;
|
||||
case WIFI_AUTH_WPA2_PSK: printf("WPA2"); break;
|
||||
case WIFI_AUTH_WPA_WPA2_PSK: printf("WPA+WPA2"); break;
|
||||
case WIFI_AUTH_WPA2_ENTERPRISE: printf("WPA2-EAP"); break;
|
||||
case WIFI_AUTH_WPA3_PSK: printf("WPA3"); break;
|
||||
case WIFI_AUTH_WPA2_WPA3_PSK: printf("WPA2+WPA3"); break;
|
||||
case WIFI_AUTH_WAPI_PSK: printf("WAPI"); break;
|
||||
default: printf("unknown");
|
||||
}
|
||||
printf("\r\n");
|
||||
lv_label_set_text_fmt(label[i],"%-32s %4ld",WiFi.SSID(i).c_str(),WiFi.RSSI(i));
|
||||
delay(10);
|
||||
}
|
||||
}
|
||||
Serial.println("");
|
||||
|
||||
// Delete the scan result to free memory for code below.
|
||||
WiFi.scanDelete();
|
||||
|
||||
// Wait a bit before scanning again.
|
||||
delay(5000);
|
||||
}
|
||||
}
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
|
||||
// Set WiFi to station mode and disconnect from an AP if it was previously connected.
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFi.disconnect();
|
||||
delay(100);
|
||||
|
||||
i2c_master_bus_handle_t i2c_handle = NULL;
|
||||
|
||||
i2c_master_bus_config_t i2c_bus_conf = {
|
||||
.i2c_port = I2C_NUM_1,
|
||||
.sda_io_num = (gpio_num_t)TP_I2C_SDA,
|
||||
.scl_io_num = (gpio_num_t)TP_I2C_SCL,
|
||||
.clk_source = I2C_CLK_SRC_DEFAULT,
|
||||
.glitch_ignore_cnt = 7,
|
||||
.intr_priority = 0,
|
||||
.trans_queue_depth = 0,
|
||||
.flags = {
|
||||
.enable_internal_pullup = 1,
|
||||
},
|
||||
};
|
||||
i2c_new_master_bus(&i2c_bus_conf, &i2c_handle);
|
||||
|
||||
lcd.begin();
|
||||
touch.begin();
|
||||
|
||||
lcd.get_handle(&lcd_panels);
|
||||
|
||||
|
||||
lv_init();
|
||||
size_t buffer_size = sizeof(int16_t) * LCD_H_RES * LCD_V_RES;
|
||||
// buf = (int32_t *)heap_caps_malloc(buffer_size, MALLOC_CAP_SPIRAM);
|
||||
// buf1 = (int32_t *)heap_caps_malloc(buffer_size, MALLOC_CAP_SPIRAM);
|
||||
buf = (lv_color_t *)heap_caps_malloc(buffer_size, MALLOC_CAP_SPIRAM);
|
||||
buf1 = (lv_color_t *)heap_caps_malloc(buffer_size, MALLOC_CAP_SPIRAM);
|
||||
assert(buf);
|
||||
assert(buf1);
|
||||
|
||||
lv_disp_draw_buf_init(&draw_buf, buf, buf1, LCD_H_RES * LCD_V_RES);
|
||||
|
||||
static lv_disp_drv_t disp_drv;
|
||||
/*Initialize the display*/
|
||||
lv_disp_drv_init(&disp_drv);
|
||||
disp_drv.hor_res = LCD_H_RES;
|
||||
disp_drv.ver_res = LCD_V_RES;
|
||||
disp_drv.flush_cb = my_disp_flush;
|
||||
disp_drv.draw_buf = &draw_buf;
|
||||
disp_drv.full_refresh = false;
|
||||
lv_disp_drv_register(&disp_drv);
|
||||
|
||||
static lv_indev_drv_t indev_drv;
|
||||
lv_indev_drv_init(&indev_drv);
|
||||
indev_drv.type = LV_INDEV_TYPE_POINTER;
|
||||
indev_drv.read_cb = my_touchpad_read;
|
||||
lv_indev_drv_register(&indev_drv);
|
||||
|
||||
esp_lcd_dpi_panel_event_callbacks_t cbs = {0};
|
||||
cbs.on_color_trans_done = lvgl_port_flush_dpi_panel_ready_callback;
|
||||
/* Register done callback */
|
||||
esp_lcd_dpi_panel_register_event_callbacks(lcd_panels.panel, &cbs, &disp_drv);
|
||||
|
||||
// lv_disp_set_rotation(NULL, 0);
|
||||
Serial.println("start demo");
|
||||
|
||||
obj = lv_obj_create(lv_scr_act());
|
||||
lv_obj_set_size(obj,480,800);
|
||||
|
||||
|
||||
label[0] = lv_label_create(obj);
|
||||
lv_obj_align(label[0],LV_ALIGN_TOP_LEFT,0,0);
|
||||
lv_obj_set_style_text_font(label[0], &weiruanyahei_14, 0);
|
||||
for(int i =1;i<25;i++)
|
||||
{
|
||||
label[i] = lv_label_create(obj);
|
||||
lv_obj_set_style_text_font(label[i], &weiruanyahei_14, 0);
|
||||
lv_obj_align_to(label[i],label[i-1],LV_ALIGN_OUT_BOTTOM_LEFT,0,0);
|
||||
}
|
||||
|
||||
xTaskCreatePinnedToCore(wifiScanTask, "WiFi Scan", 4096, NULL, 4, NULL,1);
|
||||
Serial.println("Setup done");
|
||||
}
|
||||
|
||||
void loop() {
|
||||
|
||||
lv_timer_handler();
|
||||
delay(5);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,703 @@
|
||||
/**
|
||||
* @file lv_conf.h
|
||||
* Configuration file for v8.3.0-dev
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copy this file as `lv_conf.h`
|
||||
* 1. simply next to the `lvgl` folder
|
||||
* 2. or any other places and
|
||||
* - define `LV_CONF_INCLUDE_SIMPLE`
|
||||
* - add the path as include path
|
||||
*/
|
||||
|
||||
/* clang-format off */
|
||||
#if 1 /*Set it to "1" to enable content*/
|
||||
|
||||
#ifndef LV_CONF_H
|
||||
#define LV_CONF_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/*====================
|
||||
COLOR SETTINGS
|
||||
*====================*/
|
||||
|
||||
/*Color depth: 1 (1 byte per pixel), 8 (RGB332), 16 (RGB565), 32 (ARGB8888)*/
|
||||
#define LV_COLOR_DEPTH 16
|
||||
|
||||
/*Swap the 2 bytes of RGB565 color. Useful if the display has an 8-bit interface (e.g. SPI)*/
|
||||
#define LV_COLOR_16_SWAP 0
|
||||
|
||||
/*Enable more complex drawing routines to manage screens transparency.
|
||||
*Can be used if the UI is above another layer, e.g. an OSD menu or video player.
|
||||
*Requires `LV_COLOR_DEPTH = 32` colors and the screen's `bg_opa` should be set to non LV_OPA_COVER value*/
|
||||
#define LV_COLOR_SCREEN_TRANSP 0
|
||||
|
||||
/* Adjust color mix functions rounding. GPUs might calculate color mix (blending) differently.
|
||||
* 0: round down, 64: round up from x.75, 128: round up from half, 192: round up from x.25, 254: round up */
|
||||
#define LV_COLOR_MIX_ROUND_OFS (LV_COLOR_DEPTH == 32 ? 0: 128)
|
||||
|
||||
/*Images pixels with this color will not be drawn if they are chroma keyed)*/
|
||||
#define LV_COLOR_CHROMA_KEY lv_color_hex(0x00ff00) /*pure green*/
|
||||
|
||||
/*=========================
|
||||
MEMORY SETTINGS
|
||||
*=========================*/
|
||||
|
||||
/*1: use custom malloc/free, 0: use the built-in `lv_mem_alloc()` and `lv_mem_free()`*/
|
||||
#define LV_MEM_CUSTOM 1
|
||||
#if LV_MEM_CUSTOM == 0
|
||||
/*Size of the memory available for `lv_mem_alloc()` in bytes (>= 2kB)*/
|
||||
#define LV_MEM_SIZE (48U * 1024U) /*[bytes]*/
|
||||
|
||||
/*Set an address for the memory pool instead of allocating it as a normal array. Can be in external SRAM too.*/
|
||||
#define LV_MEM_ADR 0 /*0: unused*/
|
||||
/*Instead of an address give a memory allocator that will be called to get a memory pool for LVGL. E.g. my_malloc*/
|
||||
#if LV_MEM_ADR == 0
|
||||
//#define LV_MEM_POOL_INCLUDE your_alloc_library /* Uncomment if using an external allocator*/
|
||||
//#define LV_MEM_POOL_ALLOC your_alloc /* Uncomment if using an external allocator*/
|
||||
#endif
|
||||
|
||||
#else /*LV_MEM_CUSTOM*/
|
||||
#define LV_MEM_CUSTOM_INCLUDE <stdlib.h> /*Header for the dynamic memory function*/
|
||||
#define LV_MEM_CUSTOM_ALLOC malloc
|
||||
#define LV_MEM_CUSTOM_FREE free
|
||||
#define LV_MEM_CUSTOM_REALLOC realloc
|
||||
#endif /*LV_MEM_CUSTOM*/
|
||||
|
||||
/*Number of the intermediate memory buffer used during rendering and other internal processing mechanisms.
|
||||
*You will see an error log message if there wasn't enough buffers. */
|
||||
#define LV_MEM_BUF_MAX_NUM 16
|
||||
|
||||
/*Use the standard `memcpy` and `memset` instead of LVGL's own functions. (Might or might not be faster).*/
|
||||
#define LV_MEMCPY_MEMSET_STD 0
|
||||
|
||||
/*====================
|
||||
HAL SETTINGS
|
||||
*====================*/
|
||||
|
||||
/*Default display refresh period. LVG will redraw changed areas with this period time*/
|
||||
#define LV_DISP_DEF_REFR_PERIOD 15 /*[ms]*/ //15
|
||||
|
||||
/*Input device read period in milliseconds*/
|
||||
#define LV_INDEV_DEF_READ_PERIOD 30 /*[ms]*///30
|
||||
|
||||
/*Use a custom tick source that tells the elapsed time in milliseconds.
|
||||
*It removes the need to manually update the tick with `lv_tick_inc()`)*/
|
||||
#define LV_TICK_CUSTOM 1
|
||||
#if LV_TICK_CUSTOM
|
||||
#define LV_TICK_CUSTOM_INCLUDE "Arduino.h" /*Header for the system time function*/
|
||||
#define LV_TICK_CUSTOM_SYS_TIME_EXPR (millis()) /*Expression evaluating to current system time in ms*/
|
||||
#endif /*LV_TICK_CUSTOM*/
|
||||
|
||||
/*Default Dot Per Inch. Used to initialize default sizes such as widgets sized, style paddings.
|
||||
*(Not so important, you can adjust it to modify default sizes and spaces)*/
|
||||
#define LV_DPI_DEF 130 /*[px/inch]*/
|
||||
|
||||
/*=======================
|
||||
* FEATURE CONFIGURATION
|
||||
*=======================*/
|
||||
|
||||
/*-------------
|
||||
* Drawing
|
||||
*-----------*/
|
||||
|
||||
/*Enable complex draw engine.
|
||||
*Required to draw shadow, gradient, rounded corners, circles, arc, skew lines, image transformations or any masks*/
|
||||
#define LV_DRAW_COMPLEX 1
|
||||
#if LV_DRAW_COMPLEX != 0
|
||||
|
||||
/*Allow buffering some shadow calculation.
|
||||
*LV_SHADOW_CACHE_SIZE is the max. shadow size to buffer, where shadow size is `shadow_width + radius`
|
||||
*Caching has LV_SHADOW_CACHE_SIZE^2 RAM cost*/
|
||||
#define LV_SHADOW_CACHE_SIZE 0
|
||||
|
||||
/* Set number of maximally cached circle data.
|
||||
* The circumference of 1/4 circle are saved for anti-aliasing
|
||||
* radius * 4 bytes are used per circle (the most often used radiuses are saved)
|
||||
* 0: to disable caching */
|
||||
#define LV_CIRCLE_CACHE_SIZE 4
|
||||
#endif /*LV_DRAW_COMPLEX*/
|
||||
|
||||
/*Default image cache size. Image caching keeps the images opened.
|
||||
*If only the built-in image formats are used there is no real advantage of caching. (I.e. if no new image decoder is added)
|
||||
*With complex image decoders (e.g. PNG or JPG) caching can save the continuous open/decode of images.
|
||||
*However the opened images might consume additional RAM.
|
||||
*0: to disable caching*/
|
||||
#define LV_IMG_CACHE_DEF_SIZE 0
|
||||
|
||||
/*Number of stops allowed per gradient. Increase this to allow more stops.
|
||||
*This adds (sizeof(lv_color_t) + 1) bytes per additional stop*/
|
||||
#define LV_GRADIENT_MAX_STOPS 2
|
||||
|
||||
/*Default gradient buffer size.
|
||||
*When LVGL calculates the gradient "maps" it can save them into a cache to avoid calculating them again.
|
||||
*LV_GRAD_CACHE_DEF_SIZE sets the size of this cache in bytes.
|
||||
*If the cache is too small the map will be allocated only while it's required for the drawing.
|
||||
*0 mean no caching.*/
|
||||
#define LV_GRAD_CACHE_DEF_SIZE 0
|
||||
|
||||
/*Allow dithering the gradients (to achieve visual smooth color gradients on limited color depth display)
|
||||
*LV_DITHER_GRADIENT implies allocating one or two more lines of the object's rendering surface
|
||||
*The increase in memory consumption is (32 bits * object width) plus 24 bits * object width if using error diffusion */
|
||||
#define LV_DITHER_GRADIENT 0
|
||||
#if LV_DITHER_GRADIENT
|
||||
/*Add support for error diffusion dithering.
|
||||
*Error diffusion dithering gets a much better visual result, but implies more CPU consumption and memory when drawing.
|
||||
*The increase in memory consumption is (24 bits * object's width)*/
|
||||
#define LV_DITHER_ERROR_DIFFUSION 0
|
||||
#endif
|
||||
|
||||
/*Maximum buffer size to allocate for rotation.
|
||||
*Only used if software rotation is enabled in the display driver.*/
|
||||
#define LV_DISP_ROT_MAX_BUF (10*1024)
|
||||
|
||||
/*-------------
|
||||
* GPU
|
||||
*-----------*/
|
||||
|
||||
/*Use STM32's DMA2D (aka Chrom Art) GPU*/
|
||||
#define LV_USE_GPU_STM32_DMA2D 0
|
||||
#if LV_USE_GPU_STM32_DMA2D
|
||||
/*Must be defined to include path of CMSIS header of target processor
|
||||
e.g. "stm32f769xx.h" or "stm32f429xx.h"*/
|
||||
#define LV_GPU_DMA2D_CMSIS_INCLUDE
|
||||
#endif
|
||||
|
||||
/*Use NXP's PXP GPU iMX RTxxx platforms*/
|
||||
#define LV_USE_GPU_NXP_PXP 0
|
||||
#if LV_USE_GPU_NXP_PXP
|
||||
/*1: Add default bare metal and FreeRTOS interrupt handling routines for PXP (lv_gpu_nxp_pxp_osa.c)
|
||||
* and call lv_gpu_nxp_pxp_init() automatically during lv_init(). Note that symbol SDK_OS_FREE_RTOS
|
||||
* has to be defined in order to use FreeRTOS OSA, otherwise bare-metal implementation is selected.
|
||||
*0: lv_gpu_nxp_pxp_init() has to be called manually before lv_init()
|
||||
*/
|
||||
#define LV_USE_GPU_NXP_PXP_AUTO_INIT 0
|
||||
#endif
|
||||
|
||||
/*Use NXP's VG-Lite GPU iMX RTxxx platforms*/
|
||||
#define LV_USE_GPU_NXP_VG_LITE 0
|
||||
|
||||
/*Use SDL renderer API*/
|
||||
#define LV_USE_GPU_SDL 0
|
||||
#if LV_USE_GPU_SDL
|
||||
#define LV_GPU_SDL_INCLUDE_PATH <SDL2/SDL.h>
|
||||
/*Texture cache size, 8MB by default*/
|
||||
#define LV_GPU_SDL_LRU_SIZE (1024 * 1024 * 8)
|
||||
/*Custom blend mode for mask drawing, disable if you need to link with older SDL2 lib*/
|
||||
#define LV_GPU_SDL_CUSTOM_BLEND_MODE (SDL_VERSION_ATLEAST(2, 0, 6))
|
||||
#endif
|
||||
|
||||
/*-------------
|
||||
* Logging
|
||||
*-----------*/
|
||||
|
||||
/*Enable the log module*/
|
||||
#define LV_USE_LOG 0
|
||||
#if LV_USE_LOG
|
||||
|
||||
/*How important log should be added:
|
||||
*LV_LOG_LEVEL_TRACE A lot of logs to give detailed information
|
||||
*LV_LOG_LEVEL_INFO Log important events
|
||||
*LV_LOG_LEVEL_WARN Log if something unwanted happened but didn't cause a problem
|
||||
*LV_LOG_LEVEL_ERROR Only critical issue, when the system may fail
|
||||
*LV_LOG_LEVEL_USER Only logs added by the user
|
||||
*LV_LOG_LEVEL_NONE Do not log anything*/
|
||||
#define LV_LOG_LEVEL LV_LOG_LEVEL_WARN
|
||||
|
||||
/*1: Print the log with 'printf';
|
||||
*0: User need to register a callback with `lv_log_register_print_cb()`*/
|
||||
#define LV_LOG_PRINTF 0
|
||||
|
||||
/*Enable/disable LV_LOG_TRACE in modules that produces a huge number of logs*/
|
||||
#define LV_LOG_TRACE_MEM 1
|
||||
#define LV_LOG_TRACE_TIMER 1
|
||||
#define LV_LOG_TRACE_INDEV 1
|
||||
#define LV_LOG_TRACE_DISP_REFR 1
|
||||
#define LV_LOG_TRACE_EVENT 1
|
||||
#define LV_LOG_TRACE_OBJ_CREATE 1
|
||||
#define LV_LOG_TRACE_LAYOUT 1
|
||||
#define LV_LOG_TRACE_ANIM 1
|
||||
|
||||
#endif /*LV_USE_LOG*/
|
||||
|
||||
/*-------------
|
||||
* Asserts
|
||||
*-----------*/
|
||||
|
||||
/*Enable asserts if an operation is failed or an invalid data is found.
|
||||
*If LV_USE_LOG is enabled an error message will be printed on failure*/
|
||||
#define LV_USE_ASSERT_NULL 1 /*Check if the parameter is NULL. (Very fast, recommended)*/
|
||||
#define LV_USE_ASSERT_MALLOC 1 /*Checks is the memory is successfully allocated or no. (Very fast, recommended)*/
|
||||
#define LV_USE_ASSERT_STYLE 0 /*Check if the styles are properly initialized. (Very fast, recommended)*/
|
||||
#define LV_USE_ASSERT_MEM_INTEGRITY 0 /*Check the integrity of `lv_mem` after critical operations. (Slow)*/
|
||||
#define LV_USE_ASSERT_OBJ 0 /*Check the object's type and existence (e.g. not deleted). (Slow)*/
|
||||
|
||||
/*Add a custom handler when assert happens e.g. to restart the MCU*/
|
||||
#define LV_ASSERT_HANDLER_INCLUDE <stdint.h>
|
||||
#define LV_ASSERT_HANDLER while(1); /*Halt by default*/
|
||||
|
||||
/*-------------
|
||||
* Others
|
||||
*-----------*/
|
||||
|
||||
/*1: Show CPU usage and FPS count*/
|
||||
#define LV_USE_PERF_MONITOR 1
|
||||
#if LV_USE_PERF_MONITOR
|
||||
#define LV_USE_PERF_MONITOR_POS LV_ALIGN_BOTTOM_RIGHT
|
||||
#endif
|
||||
|
||||
/*1: Show the used memory and the memory fragmentation
|
||||
* Requires LV_MEM_CUSTOM = 0*/
|
||||
#define LV_USE_MEM_MONITOR 0
|
||||
#if LV_USE_MEM_MONITOR
|
||||
#define LV_USE_MEM_MONITOR_POS LV_ALIGN_BOTTOM_LEFT
|
||||
#endif
|
||||
|
||||
/*1: Draw random colored rectangles over the redrawn areas*/
|
||||
#define LV_USE_REFR_DEBUG 0
|
||||
|
||||
/*Change the built in (v)snprintf functions*/
|
||||
#define LV_SPRINTF_CUSTOM 0
|
||||
#if LV_SPRINTF_CUSTOM
|
||||
#define LV_SPRINTF_INCLUDE <stdio.h>
|
||||
#define lv_snprintf snprintf
|
||||
#define lv_vsnprintf vsnprintf
|
||||
#else /*LV_SPRINTF_CUSTOM*/
|
||||
#define LV_SPRINTF_USE_FLOAT 0
|
||||
#endif /*LV_SPRINTF_CUSTOM*/
|
||||
|
||||
#define LV_USE_USER_DATA 1
|
||||
|
||||
/*Garbage Collector settings
|
||||
*Used if lvgl is bound to higher level language and the memory is managed by that language*/
|
||||
#define LV_ENABLE_GC 0
|
||||
#if LV_ENABLE_GC != 0
|
||||
#define LV_GC_INCLUDE "gc.h" /*Include Garbage Collector related things*/
|
||||
#endif /*LV_ENABLE_GC*/
|
||||
|
||||
/*=====================
|
||||
* COMPILER SETTINGS
|
||||
*====================*/
|
||||
|
||||
/*For big endian systems set to 1*/
|
||||
#define LV_BIG_ENDIAN_SYSTEM 0
|
||||
|
||||
/*Define a custom attribute to `lv_tick_inc` function*/
|
||||
#define LV_ATTRIBUTE_TICK_INC
|
||||
|
||||
/*Define a custom attribute to `lv_timer_handler` function*/
|
||||
#define LV_ATTRIBUTE_TIMER_HANDLER
|
||||
|
||||
/*Define a custom attribute to `lv_disp_flush_ready` function*/
|
||||
#define LV_ATTRIBUTE_FLUSH_READY
|
||||
|
||||
/*Required alignment size for buffers*/
|
||||
#define LV_ATTRIBUTE_MEM_ALIGN_SIZE 1
|
||||
|
||||
/*Will be added where memories needs to be aligned (with -Os data might not be aligned to boundary by default).
|
||||
* E.g. __attribute__((aligned(4)))*/
|
||||
#define LV_ATTRIBUTE_MEM_ALIGN
|
||||
|
||||
/*Attribute to mark large constant arrays for example font's bitmaps*/
|
||||
#define LV_ATTRIBUTE_LARGE_CONST
|
||||
|
||||
/*Compiler prefix for a big array declaration in RAM*/
|
||||
#define LV_ATTRIBUTE_LARGE_RAM_ARRAY
|
||||
|
||||
/*Place performance critical functions into a faster memory (e.g RAM)*/
|
||||
#define LV_ATTRIBUTE_FAST_MEM
|
||||
|
||||
/*Prefix variables that are used in GPU accelerated operations, often these need to be placed in RAM sections that are DMA accessible*/
|
||||
#define LV_ATTRIBUTE_DMA
|
||||
|
||||
/*Export integer constant to binding. This macro is used with constants in the form of LV_<CONST> that
|
||||
*should also appear on LVGL binding API such as Micropython.*/
|
||||
#define LV_EXPORT_CONST_INT(int_value) struct _silence_gcc_warning /*The default value just prevents GCC warning*/
|
||||
|
||||
/*Extend the default -32k..32k coordinate range to -4M..4M by using int32_t for coordinates instead of int16_t*/
|
||||
#define LV_USE_LARGE_COORD 0
|
||||
|
||||
/*==================
|
||||
* FONT USAGE
|
||||
*===================*/
|
||||
|
||||
/*Montserrat fonts with ASCII range and some symbols using bpp = 4
|
||||
*https://fonts.google.com/specimen/Montserrat*/
|
||||
#define LV_FONT_MONTSERRAT_8 0
|
||||
#define LV_FONT_MONTSERRAT_10 0
|
||||
#define LV_FONT_MONTSERRAT_12 0
|
||||
#define LV_FONT_MONTSERRAT_14 1
|
||||
#define LV_FONT_MONTSERRAT_16 0
|
||||
#define LV_FONT_MONTSERRAT_18 1
|
||||
#define LV_FONT_MONTSERRAT_20 0
|
||||
#define LV_FONT_MONTSERRAT_22 0
|
||||
#define LV_FONT_MONTSERRAT_24 0
|
||||
#define LV_FONT_MONTSERRAT_26 0
|
||||
#define LV_FONT_MONTSERRAT_28 0
|
||||
#define LV_FONT_MONTSERRAT_30 0
|
||||
#define LV_FONT_MONTSERRAT_32 0
|
||||
#define LV_FONT_MONTSERRAT_34 0
|
||||
#define LV_FONT_MONTSERRAT_36 0
|
||||
#define LV_FONT_MONTSERRAT_38 0
|
||||
#define LV_FONT_MONTSERRAT_40 0
|
||||
#define LV_FONT_MONTSERRAT_42 0
|
||||
#define LV_FONT_MONTSERRAT_44 0
|
||||
#define LV_FONT_MONTSERRAT_46 0
|
||||
#define LV_FONT_MONTSERRAT_48 0
|
||||
|
||||
/*Demonstrate special features*/
|
||||
#define LV_FONT_MONTSERRAT_12_SUBPX 0
|
||||
#define LV_FONT_MONTSERRAT_28_COMPRESSED 0 /*bpp = 3*/
|
||||
#define LV_FONT_DEJAVU_16_PERSIAN_HEBREW 0 /*Hebrew, Arabic, Persian letters and all their forms*/
|
||||
#define LV_FONT_SIMSUN_16_CJK 0 /*1000 most common CJK radicals*/
|
||||
|
||||
/*Pixel perfect monospace fonts*/
|
||||
#define LV_FONT_UNSCII_8 0
|
||||
#define LV_FONT_UNSCII_16 0
|
||||
|
||||
/*Optionally declare custom fonts here.
|
||||
*You can use these fonts as default font too and they will be available globally.
|
||||
*E.g. #define LV_FONT_CUSTOM_DECLARE LV_FONT_DECLARE(my_font_1) LV_FONT_DECLARE(my_font_2)*/
|
||||
#define LV_FONT_CUSTOM_DECLARE
|
||||
|
||||
/*Always set a default font*/
|
||||
#define LV_FONT_DEFAULT &lv_font_montserrat_14
|
||||
|
||||
/*Enable handling large font and/or fonts with a lot of characters.
|
||||
*The limit depends on the font size, font face and bpp.
|
||||
*Compiler error will be triggered if a font needs it.*/
|
||||
#define LV_FONT_FMT_TXT_LARGE 1
|
||||
|
||||
/*Enables/disables support for compressed fonts.*/
|
||||
#define LV_USE_FONT_COMPRESSED 1
|
||||
|
||||
/*Enable subpixel rendering*/
|
||||
#define LV_USE_FONT_SUBPX 0
|
||||
#if LV_USE_FONT_SUBPX
|
||||
/*Set the pixel order of the display. Physical order of RGB channels. Doesn't matter with "normal" fonts.*/
|
||||
#define LV_FONT_SUBPX_BGR 0 /*0: RGB; 1:BGR order*/
|
||||
#endif
|
||||
|
||||
/*=================
|
||||
* TEXT SETTINGS
|
||||
*=================*/
|
||||
|
||||
/**
|
||||
* Select a character encoding for strings.
|
||||
* Your IDE or editor should have the same character encoding
|
||||
* - LV_TXT_ENC_UTF8
|
||||
* - LV_TXT_ENC_ASCII
|
||||
*/
|
||||
#define LV_TXT_ENC LV_TXT_ENC_UTF8
|
||||
|
||||
/*Can break (wrap) texts on these chars*/
|
||||
#define LV_TXT_BREAK_CHARS " ,.;:-_"
|
||||
|
||||
/*If a word is at least this long, will break wherever "prettiest"
|
||||
*To disable, set to a value <= 0*/
|
||||
#define LV_TXT_LINE_BREAK_LONG_LEN 0
|
||||
|
||||
/*Minimum number of characters in a long word to put on a line before a break.
|
||||
*Depends on LV_TXT_LINE_BREAK_LONG_LEN.*/
|
||||
#define LV_TXT_LINE_BREAK_LONG_PRE_MIN_LEN 3
|
||||
|
||||
/*Minimum number of characters in a long word to put on a line after a break.
|
||||
*Depends on LV_TXT_LINE_BREAK_LONG_LEN.*/
|
||||
#define LV_TXT_LINE_BREAK_LONG_POST_MIN_LEN 3
|
||||
|
||||
/*The control character to use for signalling text recoloring.*/
|
||||
#define LV_TXT_COLOR_CMD "#"
|
||||
|
||||
/*Support bidirectional texts. Allows mixing Left-to-Right and Right-to-Left texts.
|
||||
*The direction will be processed according to the Unicode Bidirectional Algorithm:
|
||||
*https://www.w3.org/International/articles/inline-bidi-markup/uba-basics*/
|
||||
#define LV_USE_BIDI 0
|
||||
#if LV_USE_BIDI
|
||||
/*Set the default direction. Supported values:
|
||||
*`LV_BASE_DIR_LTR` Left-to-Right
|
||||
*`LV_BASE_DIR_RTL` Right-to-Left
|
||||
*`LV_BASE_DIR_AUTO` detect texts base direction*/
|
||||
#define LV_BIDI_BASE_DIR_DEF LV_BASE_DIR_AUTO
|
||||
#endif
|
||||
|
||||
/*Enable Arabic/Persian processing
|
||||
*In these languages characters should be replaced with an other form based on their position in the text*/
|
||||
#define LV_USE_ARABIC_PERSIAN_CHARS 0
|
||||
|
||||
/*==================
|
||||
* WIDGET USAGE
|
||||
*================*/
|
||||
|
||||
/*Documentation of the widgets: https://docs.lvgl.io/latest/en/html/widgets/index.html*/
|
||||
|
||||
#define LV_USE_ARC 1
|
||||
|
||||
#define LV_USE_ANIMIMG 1
|
||||
|
||||
#define LV_USE_BAR 1
|
||||
|
||||
#define LV_USE_BTN 1
|
||||
|
||||
#define LV_USE_BTNMATRIX 1
|
||||
|
||||
#define LV_USE_CANVAS 1
|
||||
|
||||
#define LV_USE_CHECKBOX 1
|
||||
|
||||
#define LV_USE_DROPDOWN 1 /*Requires: lv_label*/
|
||||
|
||||
#define LV_USE_IMG 1 /*Requires: lv_label*/
|
||||
|
||||
#define LV_USE_LABEL 1
|
||||
#if LV_USE_LABEL
|
||||
#define LV_LABEL_TEXT_SELECTION 1 /*Enable selecting text of the label*/
|
||||
#define LV_LABEL_LONG_TXT_HINT 1 /*Store some extra info in labels to speed up drawing of very long texts*/
|
||||
#endif
|
||||
|
||||
#define LV_USE_LINE 1
|
||||
|
||||
#define LV_USE_ROLLER 1 /*Requires: lv_label*/
|
||||
#if LV_USE_ROLLER
|
||||
#define LV_ROLLER_INF_PAGES 7 /*Number of extra "pages" when the roller is infinite*/
|
||||
#endif
|
||||
|
||||
#define LV_USE_SLIDER 1 /*Requires: lv_bar*/
|
||||
|
||||
#define LV_USE_SWITCH 1
|
||||
|
||||
#define LV_USE_TEXTAREA 1 /*Requires: lv_label*/
|
||||
#if LV_USE_TEXTAREA != 0
|
||||
#define LV_TEXTAREA_DEF_PWD_SHOW_TIME 1500 /*ms*/
|
||||
#endif
|
||||
|
||||
#define LV_USE_TABLE 1
|
||||
|
||||
/*==================
|
||||
* EXTRA COMPONENTS
|
||||
*==================*/
|
||||
|
||||
/*-----------
|
||||
* Widgets
|
||||
*----------*/
|
||||
#define LV_USE_CALENDAR 1
|
||||
#if LV_USE_CALENDAR
|
||||
#define LV_CALENDAR_WEEK_STARTS_MONDAY 0
|
||||
#if LV_CALENDAR_WEEK_STARTS_MONDAY
|
||||
#define LV_CALENDAR_DEFAULT_DAY_NAMES {"Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"}
|
||||
#else
|
||||
#define LV_CALENDAR_DEFAULT_DAY_NAMES {"Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"}
|
||||
#endif
|
||||
|
||||
#define LV_CALENDAR_DEFAULT_MONTH_NAMES {"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"}
|
||||
#define LV_USE_CALENDAR_HEADER_ARROW 1
|
||||
#define LV_USE_CALENDAR_HEADER_DROPDOWN 1
|
||||
#endif /*LV_USE_CALENDAR*/
|
||||
|
||||
#define LV_USE_CHART 1
|
||||
|
||||
#define LV_USE_COLORWHEEL 1
|
||||
|
||||
#define LV_USE_IMGBTN 1
|
||||
|
||||
#define LV_USE_KEYBOARD 1
|
||||
|
||||
#define LV_USE_LED 1
|
||||
|
||||
#define LV_USE_LIST 1
|
||||
|
||||
#define LV_USE_MENU 1
|
||||
|
||||
#define LV_USE_METER 1
|
||||
|
||||
#define LV_USE_MSGBOX 1
|
||||
|
||||
#define LV_USE_SPINBOX 1
|
||||
|
||||
#define LV_USE_SPINNER 1
|
||||
|
||||
#define LV_USE_TABVIEW 1
|
||||
|
||||
#define LV_USE_TILEVIEW 1
|
||||
|
||||
#define LV_USE_WIN 1
|
||||
|
||||
#define LV_USE_SPAN 1
|
||||
#if LV_USE_SPAN
|
||||
/*A line text can contain maximum num of span descriptor */
|
||||
#define LV_SPAN_SNIPPET_STACK_SIZE 64
|
||||
#endif
|
||||
|
||||
/*-----------
|
||||
* Themes
|
||||
*----------*/
|
||||
|
||||
/*A simple, impressive and very complete theme*/
|
||||
#define LV_USE_THEME_DEFAULT 1
|
||||
#if LV_USE_THEME_DEFAULT
|
||||
|
||||
/*0: Light mode; 1: Dark mode*/
|
||||
#define LV_THEME_DEFAULT_DARK 0
|
||||
|
||||
/*1: Enable grow on press*/
|
||||
#define LV_THEME_DEFAULT_GROW 1
|
||||
|
||||
/*Default transition time in [ms]*/
|
||||
#define LV_THEME_DEFAULT_TRANSITION_TIME 80
|
||||
#endif /*LV_USE_THEME_DEFAULT*/
|
||||
|
||||
/*A very simple theme that is a good starting point for a custom theme*/
|
||||
#define LV_USE_THEME_BASIC 1
|
||||
|
||||
/*A theme designed for monochrome displays*/
|
||||
#define LV_USE_THEME_MONO 1
|
||||
|
||||
/*-----------
|
||||
* Layouts
|
||||
*----------*/
|
||||
|
||||
/*A layout similar to Flexbox in CSS.*/
|
||||
#define LV_USE_FLEX 1
|
||||
|
||||
/*A layout similar to Grid in CSS.*/
|
||||
#define LV_USE_GRID 1
|
||||
|
||||
/*---------------------
|
||||
* 3rd party libraries
|
||||
*--------------------*/
|
||||
|
||||
/*File system interfaces for common APIs */
|
||||
|
||||
/*API for fopen, fread, etc*/
|
||||
#define LV_USE_FS_STDIO 0
|
||||
#if LV_USE_FS_STDIO
|
||||
#define LV_FS_STDIO_LETTER '\0' /*Set an upper cased letter on which the drive will accessible (e.g. 'A')*/
|
||||
#define LV_FS_STDIO_PATH "" /*Set the working directory. File/directory paths will be appended to it.*/
|
||||
#define LV_FS_STDIO_CACHE_SIZE 0 /*>0 to cache this number of bytes in lv_fs_read()*/
|
||||
#endif
|
||||
|
||||
/*API for open, read, etc*/
|
||||
#define LV_USE_FS_POSIX 0
|
||||
#if LV_USE_FS_POSIX
|
||||
#define LV_FS_POSIX_LETTER '\0' /*Set an upper cased letter on which the drive will accessible (e.g. 'A')*/
|
||||
#define LV_FS_POSIX_PATH "" /*Set the working directory. File/directory paths will be appended to it.*/
|
||||
#define LV_FS_POSIX_CACHE_SIZE 0 /*>0 to cache this number of bytes in lv_fs_read()*/
|
||||
#endif
|
||||
|
||||
/*API for CreateFile, ReadFile, etc*/
|
||||
#define LV_USE_FS_WIN32 0
|
||||
#if LV_USE_FS_WIN32
|
||||
#define LV_FS_WIN32_LETTER '\0' /*Set an upper cased letter on which the drive will accessible (e.g. 'A')*/
|
||||
#define LV_FS_WIN32_PATH "" /*Set the working directory. File/directory paths will be appended to it.*/
|
||||
#define LV_FS_WIN32_CACHE_SIZE 0 /*>0 to cache this number of bytes in lv_fs_read()*/
|
||||
#endif
|
||||
|
||||
/*API for FATFS (needs to be added separately). Uses f_open, f_read, etc*/
|
||||
#define LV_USE_FS_FATFS 0
|
||||
#if LV_USE_FS_FATFS
|
||||
#define LV_FS_FATFS_LETTER '\0' /*Set an upper cased letter on which the drive will accessible (e.g. 'A')*/
|
||||
#define LV_FS_FATFS_CACHE_SIZE 0 /*>0 to cache this number of bytes in lv_fs_read()*/
|
||||
#endif
|
||||
|
||||
/*PNG decoder library*/
|
||||
#define LV_USE_PNG 0
|
||||
|
||||
/*BMP decoder library*/
|
||||
#define LV_USE_BMP 0
|
||||
|
||||
/* JPG + split JPG decoder library.
|
||||
* Split JPG is a custom format optimized for embedded systems. */
|
||||
#define LV_USE_SJPG 0
|
||||
|
||||
/*GIF decoder library*/
|
||||
#define LV_USE_GIF 0
|
||||
|
||||
/*QR code library*/
|
||||
#define LV_USE_QRCODE 0
|
||||
|
||||
/*FreeType library*/
|
||||
#define LV_USE_FREETYPE 0
|
||||
#if LV_USE_FREETYPE
|
||||
/*Memory used by FreeType to cache characters [bytes] (-1: no caching)*/
|
||||
#define LV_FREETYPE_CACHE_SIZE (16 * 1024)
|
||||
#if LV_FREETYPE_CACHE_SIZE >= 0
|
||||
/* 1: bitmap cache use the sbit cache, 0:bitmap cache use the image cache. */
|
||||
/* sbit cache:it is much more memory efficient for small bitmaps(font size < 256) */
|
||||
/* if font size >= 256, must be configured as image cache */
|
||||
#define LV_FREETYPE_SBIT_CACHE 0
|
||||
/* Maximum number of opened FT_Face/FT_Size objects managed by this cache instance. */
|
||||
/* (0:use system defaults) */
|
||||
#define LV_FREETYPE_CACHE_FT_FACES 0
|
||||
#define LV_FREETYPE_CACHE_FT_SIZES 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/*Rlottie library*/
|
||||
#define LV_USE_RLOTTIE 0
|
||||
|
||||
/*FFmpeg library for image decoding and playing videos
|
||||
*Supports all major image formats so do not enable other image decoder with it*/
|
||||
#define LV_USE_FFMPEG 0
|
||||
#if LV_USE_FFMPEG
|
||||
/*Dump input information to stderr*/
|
||||
#define LV_FFMPEG_AV_DUMP_FORMAT 0
|
||||
#endif
|
||||
|
||||
/*-----------
|
||||
* Others
|
||||
*----------*/
|
||||
|
||||
/*1: Enable API to take snapshot for object*/
|
||||
#define LV_USE_SNAPSHOT 0
|
||||
|
||||
/*1: Enable Monkey test*/
|
||||
#define LV_USE_MONKEY 0
|
||||
|
||||
/*1: Enable grid navigation*/
|
||||
#define LV_USE_GRIDNAV 0
|
||||
|
||||
/*1: Enable lv_obj fragment*/
|
||||
#define LV_USE_FRAGMENT 0
|
||||
|
||||
/*==================
|
||||
* EXAMPLES
|
||||
*==================*/
|
||||
|
||||
/*Enable the examples to be built with the library*/
|
||||
#define LV_BUILD_EXAMPLES 1
|
||||
|
||||
/*===================
|
||||
* DEMO USAGE
|
||||
====================*/
|
||||
|
||||
/*Show some widget. It might be required to increase `LV_MEM_SIZE` */
|
||||
#define LV_USE_DEMO_WIDGETS 1
|
||||
#if LV_USE_DEMO_WIDGETS
|
||||
#define LV_DEMO_WIDGETS_SLIDESHOW 1
|
||||
#endif
|
||||
|
||||
/*Demonstrate the usage of encoder and keyboard*/
|
||||
#define LV_USE_DEMO_KEYPAD_AND_ENCODER 1
|
||||
|
||||
/*Benchmark your system*/
|
||||
#define LV_USE_DEMO_BENCHMARK 1
|
||||
|
||||
/*Stress test for LVGL*/
|
||||
#define LV_USE_DEMO_STRESS 0
|
||||
|
||||
/*Music player demo*/
|
||||
#define LV_USE_DEMO_MUSIC 0
|
||||
#if LV_USE_DEMO_MUSIC
|
||||
# define LV_DEMO_MUSIC_SQUARE 0
|
||||
# define LV_DEMO_MUSIC_LANDSCAPE 0
|
||||
# define LV_DEMO_MUSIC_ROUND 0
|
||||
# define LV_DEMO_MUSIC_LARGE 0
|
||||
# define LV_DEMO_MUSIC_AUTO_PLAY 0
|
||||
#endif
|
||||
|
||||
/*--END OF LV_CONF_H--*/
|
||||
|
||||
#endif /*LV_CONF_H*/
|
||||
|
||||
#endif /*End of "Content enable"*/
|
||||
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#define LCD_H_RES 480
|
||||
#define LCD_V_RES 800
|
||||
|
||||
#define LCD_RST -1
|
||||
#define LCD_LED -1
|
||||
|
||||
#define TP_I2C_SDA 7
|
||||
#define TP_I2C_SCL 8
|
||||
#define TP_RST -1
|
||||
#define TP_INT -1
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "soc/soc_caps.h"
|
||||
#include "esp_check.h"
|
||||
#include "esp_lcd_types.h"
|
||||
|
||||
#include "esp_lcd_st7701_interface.h"
|
||||
#include "esp_lcd_st7701.h"
|
||||
|
||||
static const char *TAG = "st7701";
|
||||
|
||||
esp_err_t esp_lcd_new_panel_st7701(const esp_lcd_panel_io_handle_t io, const esp_lcd_panel_dev_config_t *panel_dev_config,
|
||||
esp_lcd_panel_handle_t *ret_panel)
|
||||
{
|
||||
// ESP_LOGI(TAG, "version: %d.%d.%d", ESP_LCD_ST7701_VER_MAJOR, ESP_LCD_ST7701_VER_MINOR, ESP_LCD_ST7701_VER_PATCH);
|
||||
ESP_RETURN_ON_FALSE(panel_dev_config && ret_panel, ESP_ERR_INVALID_ARG, TAG, "Invalid arguments");
|
||||
st7701_vendor_config_t *vendor_config = (st7701_vendor_config_t *)panel_dev_config->vendor_config;
|
||||
ESP_RETURN_ON_FALSE(vendor_config, ESP_ERR_INVALID_ARG, TAG, "`vendor_config` is necessary");
|
||||
|
||||
esp_err_t ret = ESP_ERR_NOT_SUPPORTED;
|
||||
|
||||
#if SOC_LCD_RGB_SUPPORTED
|
||||
if (!vendor_config->flags.use_mipi_interface) {
|
||||
ret = esp_lcd_new_panel_st7701_rgb(io, panel_dev_config, ret_panel);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if SOC_MIPI_DSI_SUPPORTED
|
||||
if (vendor_config->flags.use_mipi_interface) {
|
||||
ret = esp_lcd_new_panel_st7701_mipi(io, panel_dev_config, ret_panel);
|
||||
}
|
||||
#endif
|
||||
|
||||
return ret;
|
||||
}
|
||||
+199
@@ -0,0 +1,199 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "hal/lcd_types.h"
|
||||
#include "esp_lcd_panel_vendor.h"
|
||||
|
||||
#if SOC_LCD_RGB_SUPPORTED
|
||||
#include "esp_lcd_panel_rgb.h"
|
||||
#endif
|
||||
|
||||
#if SOC_MIPI_DSI_SUPPORTED
|
||||
#include "esp_lcd_mipi_dsi.h"
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief LCD panel initialization commands.
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
int cmd; /*<! The specific LCD command */
|
||||
const void *data; /*<! Buffer that holds the command specific data */
|
||||
size_t data_bytes; /*<! Size of `data` in memory, in bytes */
|
||||
unsigned int delay_ms; /*<! Delay in milliseconds after this command */
|
||||
} st7701_lcd_init_cmd_t;
|
||||
|
||||
/**
|
||||
* @brief LCD panel vendor configuration.
|
||||
*
|
||||
* @note This structure needs to be passed to the `vendor_config` field in `esp_lcd_panel_dev_config_t`.
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
const st7701_lcd_init_cmd_t *init_cmds; /*!< Pointer to initialization commands array. Set to NULL if using default commands.
|
||||
* The array should be declared as `static const` and positioned outside the function.
|
||||
* Please refer to `vendor_specific_init_default` in source file.
|
||||
*/
|
||||
uint16_t init_cmds_size; /*<! Number of commands in above array */
|
||||
union {
|
||||
#if SOC_LCD_RGB_SUPPORTED
|
||||
const esp_lcd_rgb_panel_config_t *rgb_config; /*!< RGB panel configuration */
|
||||
#endif
|
||||
#if SOC_MIPI_DSI_SUPPORTED
|
||||
struct {
|
||||
esp_lcd_dsi_bus_handle_t dsi_bus; /*!< MIPI-DSI bus configuration */
|
||||
const esp_lcd_dpi_panel_config_t *dpi_config; /*!< MIPI-DPI panel configuration */
|
||||
} mipi_config;
|
||||
#endif
|
||||
};
|
||||
struct {
|
||||
unsigned int use_mipi_interface: 1; /*<! Set to 1 if using MIPI interface, default is RGB interface */
|
||||
unsigned int mirror_by_cmd: 1; /*<! The `mirror()` function will be implemented by LCD command if set to 1.
|
||||
* Otherwise, the function will be implemented by software.
|
||||
* This flag is only valid for the RGB interface.
|
||||
*/
|
||||
|
||||
union {
|
||||
unsigned int auto_del_panel_io: 1;
|
||||
unsigned int enable_io_multiplex: 1;
|
||||
}; /*<! Delete the panel IO instance automatically if set to 1. All `*_by_cmd` flags will be invalid.
|
||||
* If the panel IO pins are sharing other pins of the RGB interface to save GPIOs,
|
||||
* Please set it to 1 to release the panel IO and its pins (except CS signal).
|
||||
* This flag is only valid for the RGB interface.
|
||||
*/
|
||||
} flags;
|
||||
} st7701_vendor_config_t;
|
||||
|
||||
/**
|
||||
* @brief Create LCD panel for model ST7701
|
||||
*
|
||||
* @note When `enable_io_multiplex` is set to 1, this function will first initialize the ST7701 with vendor specific initialization and then calls `esp_lcd_new_rgb_panel()` to create an RGB LCD panel. And the `esp_lcd_panel_init()` function will only initialize RGB.
|
||||
* @note When `enable_io_multiplex` is set to 0, this function will only call `esp_lcd_new_rgb_panel()` to create an RGB LCD panel. And the `esp_lcd_panel_init()` function will initialize both the ST7701 and RGB.
|
||||
* @note Vendor specific initialization can be different between manufacturers, should consult the LCD supplier for initialization sequence code.
|
||||
*
|
||||
* @param[in] io LCD panel IO handle
|
||||
* @param[in] panel_dev_config General panel device configuration (`vendor_config` and `rgb_config` are necessary)
|
||||
* @param[out] ret_panel Returned LCD panel handle
|
||||
* @return
|
||||
* - ESP_ERR_INVALID_ARG if parameter is invalid
|
||||
* - ESP_OK on success
|
||||
* - Otherwise on fail
|
||||
*/
|
||||
esp_err_t esp_lcd_new_panel_st7701(const esp_lcd_panel_io_handle_t io, const esp_lcd_panel_dev_config_t *panel_dev_config, esp_lcd_panel_handle_t *ret_panel);
|
||||
|
||||
/**
|
||||
* @brief 3-wire SPI panel IO configuration structure
|
||||
*
|
||||
* @param[in] line_cfg SPI line configuration
|
||||
* @param[in] scl_active_edge SCL signal active edge, 0: rising edge, 1: falling edge
|
||||
*
|
||||
*/
|
||||
#define ST7701_PANEL_IO_3WIRE_SPI_CONFIG(line_cfg, scl_active_edge) \
|
||||
{ \
|
||||
.line_config = line_cfg, \
|
||||
.expect_clk_speed = PANEL_IO_3WIRE_SPI_CLK_MAX, \
|
||||
.spi_mode = scl_active_edge ? 1 : 0, \
|
||||
.lcd_cmd_bytes = 1, \
|
||||
.lcd_param_bytes = 1, \
|
||||
.flags = { \
|
||||
.use_dc_bit = 1, \
|
||||
.dc_zero_on_data = 0, \
|
||||
.lsb_first = 0, \
|
||||
.cs_high_active = 0, \
|
||||
.del_keep_cs_inactive = 1, \
|
||||
}, \
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
/////////////////////////////// Default Configuration Macros for RGB Interface /////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
/**
|
||||
* @brief RGB timing structure
|
||||
*
|
||||
* @note refresh_rate = (pclk_hz * data_width) / (h_res + hsync_pulse_width + hsync_back_porch + hsync_front_porch)
|
||||
* / (v_res + vsync_pulse_width + vsync_back_porch + vsync_front_porch)
|
||||
* / bits_per_pixel
|
||||
*
|
||||
*/
|
||||
#define ST7701_480_480_PANEL_60HZ_RGB_TIMING() \
|
||||
{ \
|
||||
.pclk_hz = 16 * 1000 * 1000, \
|
||||
.h_res = 480, \
|
||||
.v_res = 480, \
|
||||
.hsync_pulse_width = 10, \
|
||||
.hsync_back_porch = 10, \
|
||||
.hsync_front_porch = 20, \
|
||||
.vsync_pulse_width = 10, \
|
||||
.vsync_back_porch = 10, \
|
||||
.vsync_front_porch = 10, \
|
||||
.flags.pclk_active_neg = false, \
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////// Default Configuration Macros for MIPI-DSI Interface //////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
/**
|
||||
* @brief MIPI-DSI bus configuration structure
|
||||
*/
|
||||
#define ST7701_PANEL_BUS_DSI_2CH_CONFIG() \
|
||||
{ \
|
||||
.bus_id = 0, \
|
||||
.num_data_lanes = 2, \
|
||||
.phy_clk_src = MIPI_DSI_PHY_CLK_SRC_DEFAULT, \
|
||||
.lane_bit_rate_mbps = 500, \
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief MIPI-DBI panel IO configuration structure
|
||||
*
|
||||
*/
|
||||
#define ST7701_PANEL_IO_DBI_CONFIG() \
|
||||
{ \
|
||||
.virtual_channel = 0, \
|
||||
.lcd_cmd_bits = 8, \
|
||||
.lcd_param_bits = 8, \
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief MIPI DPI configuration structure
|
||||
*
|
||||
* @note refresh_rate = (dpi_clock_freq_mhz * 1000000) / (h_res + hsync_pulse_width + hsync_back_porch + hsync_front_porch)
|
||||
* / (v_res + vsync_pulse_width + vsync_back_porch + vsync_front_porch)
|
||||
*
|
||||
* @param[in] px_format Pixel format of the panel
|
||||
*
|
||||
*/
|
||||
#define ST7701_480_360_PANEL_60HZ_DPI_CONFIG(px_format) \
|
||||
{ \
|
||||
.virtual_channel = 0, \
|
||||
.dpi_clk_src = MIPI_DSI_DPI_CLK_SRC_DEFAULT, \
|
||||
.dpi_clock_freq_mhz = 34, \
|
||||
.pixel_format = px_format, \
|
||||
.num_fbs = 1, \
|
||||
.video_timing = { \
|
||||
.h_size = 480, \
|
||||
.v_size = 800, \
|
||||
.hsync_pulse_width = 12, \
|
||||
.hsync_back_porch = 42, \
|
||||
.hsync_front_porch = 42, \
|
||||
.vsync_pulse_width = 2, \
|
||||
.vsync_back_porch = 8, \
|
||||
.vsync_front_porch = 166, \
|
||||
}, \
|
||||
.flags = {.use_dma2d = true,} \
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "esp_err.h"
|
||||
#include "esp_lcd_types.h"
|
||||
#include "esp_lcd_panel_vendor.h"
|
||||
#include "soc/soc_caps.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define ST7701_CMD_SDIR (0xC7)
|
||||
#define ST7701_CMD_SS_BIT (1 << 2)
|
||||
|
||||
#define ST7701_CMD_CND2BKxSEL (0xFF)
|
||||
#define ST7701_CMD_BKxSEL_BYTE0 (0x77)
|
||||
#define ST7701_CMD_BKxSEL_BYTE1 (0x01)
|
||||
#define ST7701_CMD_BKxSEL_BYTE2 (0x00)
|
||||
#define ST7701_CMD_BKxSEL_BYTE3 (0x00)
|
||||
#define ST7701_CMD_CN2_BIT (1 << 4)
|
||||
#define ST7701_CMD_BKxSEL_BK0 (0x00)
|
||||
|
||||
#if SOC_LCD_RGB_SUPPORTED
|
||||
/**
|
||||
* @brief Initialize ST7701 LCD panel with RGB interface
|
||||
*
|
||||
* @param[in] io LCD panel IO handle
|
||||
* @param[in] panel_dev_config LCD panel device configuration
|
||||
* @param[out] ret_panel LCD panel handle
|
||||
* @return
|
||||
* - ESP_OK: Success
|
||||
* - Otherwise: Fail
|
||||
*/
|
||||
esp_err_t esp_lcd_new_panel_st7701_rgb(const esp_lcd_panel_io_handle_t io, const esp_lcd_panel_dev_config_t *panel_dev_config, esp_lcd_panel_handle_t *ret_panel);
|
||||
#endif
|
||||
|
||||
#if SOC_MIPI_DSI_SUPPORTED
|
||||
/**
|
||||
* @brief Initialize ST7701 LCD panel with MIPI interface
|
||||
*
|
||||
* @param[in] io LCD panel IO handle
|
||||
* @param[in] panel_dev_config LCD panel device configuration
|
||||
* @param[out] ret_panel LCD panel handle
|
||||
* @return
|
||||
* - ESP_OK: Success
|
||||
* - Otherwise: Fail
|
||||
*/
|
||||
esp_err_t esp_lcd_new_panel_st7701_mipi(const esp_lcd_panel_io_handle_t io, const esp_lcd_panel_dev_config_t *panel_dev_config,
|
||||
esp_lcd_panel_handle_t *ret_panel);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
+478
@@ -0,0 +1,478 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include "soc/soc_caps.h"
|
||||
|
||||
#if SOC_MIPI_DSI_SUPPORTED
|
||||
#include <stdlib.h>
|
||||
#include <sys/cdefs.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "esp_lcd_panel_interface.h"
|
||||
#include "esp_lcd_panel_io.h"
|
||||
#include "esp_lcd_panel_vendor.h"
|
||||
#include "esp_lcd_panel_ops.h"
|
||||
#include "esp_lcd_panel_commands.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_check.h"
|
||||
#include "esp_lcd_st7701.h"
|
||||
#include "esp_lcd_st7701_interface.h"
|
||||
|
||||
static const char *TAG = "st7701_mipi";
|
||||
|
||||
static esp_err_t panel_st7701_del(esp_lcd_panel_t *panel);
|
||||
static esp_err_t panel_st7701_reset(esp_lcd_panel_t *panel);
|
||||
static esp_err_t panel_st7701_init(esp_lcd_panel_t *panel);
|
||||
static esp_err_t panel_st7701_invert_color(esp_lcd_panel_t *panel, bool invert_color_data);
|
||||
static esp_err_t panel_st7701_mirror(esp_lcd_panel_t *panel, bool mirror_x, bool mirror_y);
|
||||
static esp_err_t panel_st7701_disp_on_off(esp_lcd_panel_t *panel, bool off);
|
||||
static esp_err_t panel_st7701_sleep(esp_lcd_panel_t *panel, bool sleep);
|
||||
|
||||
typedef struct {
|
||||
esp_lcd_panel_io_handle_t io;
|
||||
int reset_gpio_num;
|
||||
uint8_t madctl_val; // save current value of LCD_CMD_MADCTL register
|
||||
uint8_t colmod_val; // save surrent value of LCD_CMD_COLMOD register
|
||||
const st7701_lcd_init_cmd_t *init_cmds;
|
||||
uint16_t init_cmds_size;
|
||||
struct {
|
||||
unsigned int reset_level: 1;
|
||||
} flags;
|
||||
// To save the original functions of MIPI DPI panel
|
||||
esp_err_t (*del)(esp_lcd_panel_t *panel);
|
||||
esp_err_t (*init)(esp_lcd_panel_t *panel);
|
||||
} st7701_panel_t;
|
||||
|
||||
esp_err_t esp_lcd_new_panel_st7701_mipi(const esp_lcd_panel_io_handle_t io, const esp_lcd_panel_dev_config_t *panel_dev_config, esp_lcd_panel_handle_t *ret_panel)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(io && panel_dev_config && ret_panel, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
st7701_vendor_config_t *vendor_config = (st7701_vendor_config_t *)panel_dev_config->vendor_config;
|
||||
ESP_RETURN_ON_FALSE(vendor_config && vendor_config->mipi_config.dpi_config && vendor_config->mipi_config.dsi_bus, ESP_ERR_INVALID_ARG, TAG,
|
||||
"invalid vendor config");
|
||||
|
||||
esp_err_t ret = ESP_OK;
|
||||
st7701_panel_t *st7701 = (st7701_panel_t *)calloc(1, sizeof(st7701_panel_t));
|
||||
ESP_RETURN_ON_FALSE(st7701, ESP_ERR_NO_MEM, TAG, "no mem for st7701 panel");
|
||||
|
||||
if (panel_dev_config->reset_gpio_num >= 0) {
|
||||
gpio_config_t io_conf = {
|
||||
.mode = GPIO_MODE_OUTPUT,
|
||||
.pin_bit_mask = 1ULL << panel_dev_config->reset_gpio_num,
|
||||
};
|
||||
ESP_GOTO_ON_ERROR(gpio_config(&io_conf), err, TAG, "configure GPIO for RST line failed");
|
||||
}
|
||||
|
||||
switch (panel_dev_config->color_space) {
|
||||
case LCD_RGB_ELEMENT_ORDER_RGB:
|
||||
st7701->madctl_val = 0;
|
||||
break;
|
||||
case LCD_RGB_ELEMENT_ORDER_BGR:
|
||||
st7701->madctl_val |= LCD_CMD_BGR_BIT;
|
||||
break;
|
||||
default:
|
||||
ESP_GOTO_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, err, TAG, "unsupported rgb element order");
|
||||
break;
|
||||
}
|
||||
|
||||
switch (panel_dev_config->bits_per_pixel) {
|
||||
case 16: // RGB565
|
||||
st7701->colmod_val = 0x55;
|
||||
break;
|
||||
case 18: // RGB666
|
||||
st7701->colmod_val = 0x66;
|
||||
break;
|
||||
case 24: // RGB888
|
||||
st7701->colmod_val = 0x77;
|
||||
break;
|
||||
default:
|
||||
ESP_GOTO_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, err, TAG, "unsupported pixel width");
|
||||
break;
|
||||
}
|
||||
|
||||
st7701->io = io;
|
||||
st7701->init_cmds = vendor_config->init_cmds;
|
||||
st7701->init_cmds_size = vendor_config->init_cmds_size;
|
||||
st7701->reset_gpio_num = panel_dev_config->reset_gpio_num;
|
||||
st7701->flags.reset_level = panel_dev_config->flags.reset_active_high;
|
||||
|
||||
// Create MIPI DPI panel
|
||||
esp_lcd_panel_handle_t panel_handle = NULL;
|
||||
ESP_GOTO_ON_ERROR(esp_lcd_new_panel_dpi(vendor_config->mipi_config.dsi_bus, vendor_config->mipi_config.dpi_config, &panel_handle), err, TAG,
|
||||
"create MIPI DPI panel failed");
|
||||
ESP_LOGD(TAG, "new MIPI DPI panel @%p", panel_handle);
|
||||
|
||||
// Save the original functions of MIPI DPI panel
|
||||
st7701->del = panel_handle->del;
|
||||
st7701->init = panel_handle->init;
|
||||
// Overwrite the functions of MIPI DPI panel
|
||||
panel_handle->del = panel_st7701_del;
|
||||
panel_handle->init = panel_st7701_init;
|
||||
panel_handle->reset = panel_st7701_reset;
|
||||
panel_handle->mirror = panel_st7701_mirror;
|
||||
panel_handle->invert_color = panel_st7701_invert_color;
|
||||
panel_handle->disp_on_off = panel_st7701_disp_on_off;
|
||||
panel_handle->disp_sleep = panel_st7701_sleep;
|
||||
panel_handle->user_data = st7701;
|
||||
*ret_panel = panel_handle;
|
||||
ESP_LOGD(TAG, "new st7701 panel @%p", st7701);
|
||||
|
||||
return ESP_OK;
|
||||
err:
|
||||
if (st7701) {
|
||||
if (panel_dev_config->reset_gpio_num >= 0) {
|
||||
gpio_reset_pin(panel_dev_config->reset_gpio_num);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t panel_st7701_del(esp_lcd_panel_t *panel)
|
||||
{
|
||||
st7701_panel_t *st7701 = (st7701_panel_t *)panel->user_data;
|
||||
|
||||
if (st7701->reset_gpio_num >= 0) {
|
||||
gpio_reset_pin(st7701->reset_gpio_num);
|
||||
}
|
||||
|
||||
// Delete MIPI DPI panel
|
||||
st7701->del(panel);
|
||||
ESP_LOGD(TAG, "del st7701 panel @%p", st7701);
|
||||
free(st7701);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t panel_st7701_reset(esp_lcd_panel_t *panel)
|
||||
{
|
||||
st7701_panel_t *st7701 = (st7701_panel_t *)panel->user_data;
|
||||
esp_lcd_panel_io_handle_t io = st7701->io;
|
||||
|
||||
// perform hardware reset
|
||||
if (st7701->reset_gpio_num >= 0) {
|
||||
gpio_set_level(st7701->reset_gpio_num, st7701->flags.reset_level);
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
gpio_set_level(st7701->reset_gpio_num, !st7701->flags.reset_level);
|
||||
vTaskDelay(pdMS_TO_TICKS(120));
|
||||
} else if (io) { // perform software reset
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_SWRESET, NULL, 0), TAG, "send command failed");
|
||||
vTaskDelay(pdMS_TO_TICKS(20)); // spec, wait at least 5ms before sending new command
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static const st7701_lcd_init_cmd_t vendor_specific_init_default[] = {
|
||||
// {0xFF, (uint8_t []){0x77, 0x01, 0x00, 0x00, 0x13}, 5, 0},
|
||||
// {0xEF, (uint8_t []){0x08}, 1, 0},
|
||||
// {0xFF, (uint8_t []){0x77, 0x01, 0x00, 0x00, 0x10}, 5, 0},
|
||||
// {0xC0, (uint8_t []){0x2c, 0x00}, 2, 0},
|
||||
// {0xC1, (uint8_t []){0x10, 0x0C}, 2, 0},
|
||||
// {0xC2, (uint8_t []){0x21, 0x0A}, 2, 0},
|
||||
// {0xCC, (uint8_t []){0x10}, 1, 0},
|
||||
// {0xB0, (uint8_t []){0x00, 0x0B, 0x12, 0x0D, 0x10, 0x06, 0x02, 0x08, 0x07, 0x1F, 0x04, 0x11, 0x0F, 0x29, 0x31, 0x1E}, 16, 0},
|
||||
// {0xB1, (uint8_t []){0x00, 0x0B, 0x13, 0x0D, 0x11, 0x06, 0x03, 0x08, 0x07, 0x20, 0x04, 0x12, 0x11, 0x29, 0x31, 0x1E}, 16, 0},
|
||||
// {0xFF, (uint8_t []){0x77, 0x01, 0x00, 0x00, 0x11}, 5, 0},
|
||||
// {0xB0, (uint8_t []){0x5D}, 1, 0},
|
||||
// {0xB1, (uint8_t []){0x72}, 1, 0},
|
||||
// {0xB2, (uint8_t []){0x84}, 1, 0},
|
||||
// {0xB3, (uint8_t []){0x80}, 1, 0},
|
||||
// {0xB5, (uint8_t []){0x4D}, 1, 0},
|
||||
// {0xB7, (uint8_t []){0x85}, 1, 0},
|
||||
// {0xB8, (uint8_t []){0x20}, 1, 0},
|
||||
// {0xC1, (uint8_t []){0x78}, 1, 0},
|
||||
// {0xC2, (uint8_t []){0x78}, 1, 0},
|
||||
// {0xD0, (uint8_t []){0x88}, 1, 0},
|
||||
// {0xE0, (uint8_t []){0x80, 0x00, 0x02}, 3, 0},
|
||||
// {0xE1, (uint8_t []){0x05, 0x00, 0x07, 0x00, 0x06, 0x00, 0x08, 0x00, 0x00, 0x33, 0x33}, 11, 0},
|
||||
// {0xE2, (uint8_t []){0x00, 0x00, 0x30, 0x30, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00}, 12, 0},
|
||||
// {0xE3, (uint8_t []){0x00, 0x00, 0x11, 0x11}, 4, 0},
|
||||
// {0xE4, (uint8_t []){0x44, 0x44}, 2, 0},
|
||||
// {0xE5, (uint8_t []){0x0C, 0x78, 0x00, 0xE0, 0x0E, 0x7A, 0x00, 0xE0, 0x08, 0x74, 0x00, 0xE0, 0x0A, 0x76, 0x00, 0xE0}, 16, 0},
|
||||
// {0xE6, (uint8_t []){0x00, 0x00, 0x11, 0x11}, 4, 0},
|
||||
// {0xE7, (uint8_t []){0x44, 0x44}, 2, 0},
|
||||
// {0xE8, (uint8_t []){0x0D, 0x79, 0x00, 0xE0, 0x0F, 0x7B, 0x00, 0xE0, 0x09, 0x75, 0x00, 0xE0, 0x0B, 0x77, 0x00, 0xE0}, 16, 0},
|
||||
// {0xE9, (uint8_t []){0x36, 0x00}, 2, 0},
|
||||
// {0xEB, (uint8_t []){0x00, 0x01, 0xE4, 0xE4, 0x44, 0x88, 0x40}, 7, 0},
|
||||
// {0xED, (uint8_t []){0xA1, 0xC2, 0xFB, 0x0F, 0x67, 0x45, 0xFF, 0xFF, 0xFF, 0xFF, 0x54, 0x76, 0xF0, 0xBF, 0x2C, 0x1A}, 16, 0},
|
||||
// {0xEF, (uint8_t []){0x10, 0x0D, 0x04, 0x08, 0x3F, 0x1F}, 6, 0},
|
||||
// {0xFF, (uint8_t []){0x77, 0x01, 0x00, 0x00, 0x13}, 5, 0},
|
||||
// {0xE8, (uint8_t []){0x00, 0x0E}, 2, 0},
|
||||
// {0xE8, (uint8_t []){0x00, 0x0C}, 2, 20},
|
||||
// {0xE8, (uint8_t []){0x00, 0x00}, 2, 0},
|
||||
// {0xFF, (uint8_t []){0x77, 0x01, 0x00, 0x00, 0x00}, 5, 0},
|
||||
// {0x11, (uint8_t []){0x00}, 0, 120},
|
||||
// {0x29, (uint8_t []){0x00}, 0, 0},
|
||||
|
||||
// {0xFF, (uint8_t []){0x77, 0x01, 0x00, 0x00, 0x12}, 5, 0}, /* This part of the parameters can be used for screen self-test */
|
||||
// {0xD1, (uint8_t []){0x81}, 1, 0},
|
||||
// {0xD2, (uint8_t []){0x08}, 1, 0},
|
||||
|
||||
/*
|
||||
|
||||
*/
|
||||
|
||||
{0xFF, (uint8_t []){0x77,0x01,0x00,0x00,0x13},5,0},
|
||||
{0xEF, (uint8_t []){0x08}, 1, 0},
|
||||
{0xFF, (uint8_t []){0x77,0x01,0x00,0x00,0x10},5,0},
|
||||
{0xC0, (uint8_t []){0x63, 0x00}, 2, 0},
|
||||
{0xC1, (uint8_t []){0x0D, 0x02}, 2, 0},
|
||||
{0xC2, (uint8_t []){0x10, 0x08}, 2, 0},
|
||||
{0xCC, (uint8_t []){0x10}, 1, 0},
|
||||
|
||||
{0xB0, (uint8_t []){0x80, 0x09, 0x53, 0x0C, 0xD0, 0x07, 0x0C, 0x09, 0x09, 0x28, 0x06, 0xD4, 0x13, 0x69, 0x2B, 0x71}, 16, 0},
|
||||
{0xB1, (uint8_t []){0x80, 0x94, 0x5A, 0x10, 0xD3, 0x06, 0x0A, 0x08, 0x08, 0x25, 0x03, 0xD3, 0x12, 0x66, 0x6A, 0x0D}, 16, 0},
|
||||
{0xFF, (uint8_t []){0x77, 0x01, 0x00, 0x00, 0x11}, 5, 0},
|
||||
|
||||
{0xB0, (uint8_t []){0x5D}, 1, 0},
|
||||
{0xB1, (uint8_t []){0x58}, 1, 0},
|
||||
{0xB2, (uint8_t []){0x87}, 1, 0},
|
||||
{0xB3, (uint8_t []){0x80}, 1, 0},
|
||||
{0xB5, (uint8_t []){0x4E}, 1, 0},
|
||||
{0xB7, (uint8_t []){0x85}, 1, 0},
|
||||
{0xB8, (uint8_t []){0x21}, 1, 0},
|
||||
{0xB9, (uint8_t []){0x10, 0x1F}, 2, 0},
|
||||
{0xBB, (uint8_t []){0x03}, 1,0},
|
||||
{0xBC, (uint8_t []){0x00}, 1,0},
|
||||
|
||||
{0xC1, (uint8_t []){0x78}, 1, 0},
|
||||
{0xC2, (uint8_t []){0x78}, 1, 0},
|
||||
{0xD0, (uint8_t []){0x88}, 1, 0},
|
||||
|
||||
{0xE0, (uint8_t []){0x00, 0x3A, 0x02}, 3, 0},
|
||||
{0xE1, (uint8_t []){0x04, 0xA0, 0x00, 0xA0, 0x05,0xA0, 0x00, 0xA0, 0x00, 0x40, 0x40}, 11, 0},
|
||||
{0xE2, (uint8_t []){0x30, 0x00, 0x40, 0x40, 0x32, 0xA0, 0x00, 0xA0, 0x00, 0xA0, 0x00, 0xA0, 0x00}, 13, 0},
|
||||
{0xE3, (uint8_t []){0x00, 0x00, 0x33, 0x33}, 4, 0},
|
||||
{0xE4, (uint8_t []){0x44, 0x44}, 2, 0},
|
||||
{0xE5, (uint8_t []){0x09, 0x2E, 0xA0, 0xA0, 0x0B, 0x30, 0xA0, 0xA0, 0x05, 0x2A, 0xA0, 0xA0, 0x07, 0x2C, 0xA0, 0xA0}, 16, 0},
|
||||
{0xE6, (uint8_t []){0x00, 0x00, 0x33, 0x33}, 4, 0},
|
||||
{0xE7, (uint8_t []){0x44, 0x44}, 2, 0},
|
||||
{0xE8, (uint8_t []){0x08, 0x2D, 0xA0, 0xA0, 0x0A, 0x2F, 0xA0, 0xA0, 0x04, 0x29, 0xA0, 0xA0, 0x06, 0x2B, 0xA0, 0xA0}, 16, 0},
|
||||
|
||||
{0xEB, (uint8_t []){0x00, 0x00, 0x4E, 0x4E, 0x00, 0x00, 0x00}, 7, 0},
|
||||
{0xEC, (uint8_t []){0x08, 0x01}, 2, 0},
|
||||
|
||||
{0xED, (uint8_t []){0xB0, 0x2B, 0x98, 0xA4, 0x56, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF7, 0x65, 0x4A, 0x89, 0xB2, 0x0B}, 16, 0},
|
||||
{0xEF, (uint8_t []){0x08, 0x08, 0x08, 0x45, 0x3F, 0x54}, 6, 0},
|
||||
{0xFF, (uint8_t []){0x77, 0x01, 0x00, 0x00, 0x00}, 5, 0},
|
||||
|
||||
// {0x3A, (uint8_t []){0x66}, 1, 0},
|
||||
{0x11, (uint8_t []){0x00}, 1, 120},
|
||||
{0x29, (uint8_t []){0x00}, 1, 20},
|
||||
|
||||
|
||||
|
||||
// {0xFF, (uint8_t []){0x77,0x01,0x00,0x00,0x13},5,0},
|
||||
// {0xEF, (uint8_t []){0x08}, 1, 0},
|
||||
// {0xFF, (uint8_t []){0x77,0x01,0x00,0x00,0x10},5,0},
|
||||
// {0xC0, (uint8_t []){0x63, 0x00}, 2, 0},
|
||||
// {0xC1, (uint8_t []){0x0D, 0x02}, 2, 0},
|
||||
// {0xC2, (uint8_t []){0x17, 0x08}, 2, 0},
|
||||
// {0xCC, (uint8_t []){0x10}, 1, 0},
|
||||
|
||||
// {0xB0, (uint8_t []){0x40, 0xC9, 0x94, 0x0E, 0x10, 0x05, 0x0B, 0x09, 0x08, 0x26, 0x04, 0x52, 0x10, 0x69, 0x6B, 0x69}, 16, 0},
|
||||
// {0xB1, (uint8_t []){0x40, 0xD2, 0x98, 0x0C, 0x92, 0x07, 0x09, 0x08, 0x07, 0x25, 0x02, 0x0E, 0x1C, 0x6E, 0x78, 0x55}, 16, 0},
|
||||
// {0xFF, (uint8_t []){0x77, 0x01, 0x00, 0x00, 0x11}, 5, 0},
|
||||
|
||||
// {0xB0, (uint8_t []){0x5D}, 1, 0},
|
||||
// {0xB1, (uint8_t []){0x4E}, 1, 0},
|
||||
// {0xB2, (uint8_t []){0x87}, 1, 0},
|
||||
// {0xB3, (uint8_t []){0x80}, 1, 0},
|
||||
// {0xB5, (uint8_t []){0x4E}, 1, 0},
|
||||
// {0xB7, (uint8_t []){0x85}, 1, 0},
|
||||
// {0xB8, (uint8_t []){0x21}, 1, 0},
|
||||
// {0xB9, (uint8_t []){0x10, 0x1F}, 2, 0},
|
||||
// {0xBB, (uint8_t []){0x03}, 1,0},
|
||||
// {0xBC, (uint8_t []){0x00}, 1,0},
|
||||
|
||||
// {0xC1, (uint8_t []){0x78}, 1, 0},
|
||||
// {0xC2, (uint8_t []){0x78}, 1, 0},
|
||||
// {0xD0, (uint8_t []){0x88}, 1, 0},
|
||||
|
||||
// {0xE0, (uint8_t []){0x00, 0x3A, 0x02}, 3, 0},
|
||||
// {0xE1, (uint8_t []){0x04, 0xA0, 0x00, 0xA0, 0x05,0xA0, 0x00, 0xA0, 0x00, 0x40, 0x40}, 11, 0},
|
||||
// {0xE2, (uint8_t []){0x30, 0x00, 0x40, 0x40, 0x32, 0xA0, 0x00, 0xA0, 0x00, 0xA0, 0x00, 0xA0, 0x00}, 13, 0},
|
||||
// {0xE3, (uint8_t []){0x00, 0x00, 0x33, 0x33}, 4, 0},
|
||||
// {0xE4, (uint8_t []){0x44, 0x44}, 2, 0},
|
||||
// {0xE5, (uint8_t []){0x09, 0x2E, 0xA0, 0xA0, 0x0B, 0x30, 0xA0, 0xA0, 0x05, 0x2A, 0xA0, 0xA0, 0x07, 0x2C, 0xA0, 0xA0}, 16, 0},
|
||||
// {0xE6, (uint8_t []){0x00, 0x00, 0x33, 0x33}, 4, 0},
|
||||
// {0xE7, (uint8_t []){0x44, 0x44}, 2, 0},
|
||||
// {0xE8, (uint8_t []){0x08, 0x2D, 0xA0, 0xA0, 0x0A, 0x2F, 0xA0, 0xA0, 0x04, 0x29, 0xA0, 0xA0, 0x06, 0x2B, 0xA0, 0xA0}, 16, 0},
|
||||
|
||||
// {0xEB, (uint8_t []){0x00, 0x00, 0x4E, 0x4E, 0x00, 0x00, 0x00}, 7, 0},
|
||||
// {0xEC, (uint8_t []){0x08, 0x01}, 2, 0},
|
||||
|
||||
// {0xED, (uint8_t []){0xB0, 0x2B, 0x98, 0xA4, 0x56, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF7, 0x65, 0x4A, 0x89, 0xB2, 0x0B}, 16, 0},
|
||||
// {0xEF, (uint8_t []){0x08, 0x08, 0x08, 0x45, 0x3F, 0x54}, 6, 0},
|
||||
// {0xFF, (uint8_t []){0x77, 0x01, 0x00, 0x00, 0x00}, 5, 0},
|
||||
|
||||
// // {0x3A, (uint8_t []){0x66}, 1, 0},
|
||||
// {0x11, (uint8_t []){0x00}, 1, 120},
|
||||
// {0x29, (uint8_t []){0x00}, 1, 20},
|
||||
|
||||
|
||||
};
|
||||
|
||||
static esp_err_t panel_st7701_init(esp_lcd_panel_t *panel)
|
||||
{
|
||||
st7701_panel_t *st7701 = (st7701_panel_t *)panel->user_data;
|
||||
esp_lcd_panel_io_handle_t io = st7701->io;
|
||||
const st7701_lcd_init_cmd_t *init_cmds = NULL;
|
||||
uint16_t init_cmds_size = 0;
|
||||
bool is_command2_disable = true;
|
||||
bool is_cmd_overwritten = false;
|
||||
|
||||
uint8_t ID[3];
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_rx_param(io, 0x04, ID, 3), TAG, "read ID failed");
|
||||
ESP_LOGI(TAG, "LCD ID: %02X %02X %02X", ID[0], ID[1], ID[2]);
|
||||
|
||||
// back to CMD_Page 0
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, ST7701_CMD_CND2BKxSEL, (uint8_t []) {
|
||||
ST7701_CMD_BKxSEL_BYTE0, ST7701_CMD_BKxSEL_BYTE1, ST7701_CMD_BKxSEL_BYTE2, ST7701_CMD_BKxSEL_BYTE3, 0x00
|
||||
}, 5), TAG, "Write cmd failed");
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL, (uint8_t[]) {
|
||||
st7701->madctl_val,
|
||||
}, 1), TAG, "send command failed");
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_COLMOD, (uint8_t[]) {
|
||||
st7701->colmod_val,
|
||||
}, 1), TAG, "send command failed");
|
||||
ESP_LOGI(TAG, " st7701->madctl_val: 0x%x, st7701->colmod_val: 0x%x", st7701->madctl_val, st7701->colmod_val);
|
||||
|
||||
// vendor specific initialization, it can be different between manufacturers
|
||||
// should consult the LCD supplier for initialization sequence code
|
||||
if (st7701->init_cmds) {
|
||||
init_cmds = st7701->init_cmds;
|
||||
init_cmds_size = st7701->init_cmds_size;
|
||||
} else {
|
||||
init_cmds = vendor_specific_init_default;
|
||||
init_cmds_size = sizeof(vendor_specific_init_default) / sizeof(st7701_lcd_init_cmd_t);
|
||||
}
|
||||
|
||||
for (int i = 0; i < init_cmds_size; i++) {
|
||||
// Check if the command has been used or conflicts with the internal only when command2 is disable
|
||||
if (is_command2_disable && (init_cmds[i].data_bytes > 0)) {
|
||||
switch (init_cmds[i].cmd) {
|
||||
case LCD_CMD_MADCTL:
|
||||
is_cmd_overwritten = true;
|
||||
st7701->madctl_val = ((uint8_t *)init_cmds[i].data)[0];
|
||||
break;
|
||||
case LCD_CMD_COLMOD:
|
||||
is_cmd_overwritten = true;
|
||||
st7701->colmod_val = ((uint8_t *)init_cmds[i].data)[0];
|
||||
break;
|
||||
default:
|
||||
is_cmd_overwritten = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (is_cmd_overwritten) {
|
||||
is_cmd_overwritten = false;
|
||||
ESP_LOGW(TAG, "The %02Xh command has been used and will be overwritten by external initialization sequence",
|
||||
init_cmds[i].cmd);
|
||||
}
|
||||
}
|
||||
|
||||
// Send command
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, init_cmds[i].cmd, init_cmds[i].data, init_cmds[i].data_bytes),
|
||||
TAG, "send command failed");
|
||||
vTaskDelay(pdMS_TO_TICKS(init_cmds[i].delay_ms));
|
||||
|
||||
// Check if the current cmd is the command2 disable cmd
|
||||
if ((init_cmds[i].cmd == ST7701_CMD_CND2BKxSEL) && (init_cmds[i].data_bytes > 4)) {
|
||||
is_command2_disable = !(((uint8_t *)init_cmds[i].data)[4] & ST7701_CMD_CN2_BIT);
|
||||
}
|
||||
}
|
||||
ESP_LOGD(TAG, "send init commands success");
|
||||
|
||||
ESP_RETURN_ON_ERROR(st7701->init(panel), TAG, "init MIPI DPI panel failed");
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t panel_st7701_invert_color(esp_lcd_panel_t *panel, bool invert_color_data)
|
||||
{
|
||||
st7701_panel_t *st7701 = (st7701_panel_t *)panel->user_data;
|
||||
esp_lcd_panel_io_handle_t io = st7701->io;
|
||||
int command = 0;
|
||||
if (invert_color_data) {
|
||||
command = LCD_CMD_INVON;
|
||||
} else {
|
||||
command = LCD_CMD_INVOFF;
|
||||
}
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, command, NULL, 0), TAG, "send command failed");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t panel_st7701_mirror(esp_lcd_panel_t *panel, bool mirror_x, bool mirror_y)
|
||||
{
|
||||
st7701_panel_t *st7701 = (st7701_panel_t *)panel->user_data;
|
||||
esp_lcd_panel_io_handle_t io = st7701->io;
|
||||
uint8_t sdir_val = 0;
|
||||
|
||||
ESP_RETURN_ON_FALSE(io, ESP_FAIL, TAG, "Panel IO is deleted, cannot send command");
|
||||
// Control mirror through LCD command
|
||||
if (mirror_x) {
|
||||
sdir_val = ST7701_CMD_SS_BIT;
|
||||
} else {
|
||||
sdir_val = 0;
|
||||
}
|
||||
if (mirror_y) {
|
||||
st7701->madctl_val |= LCD_CMD_ML_BIT;
|
||||
} else {
|
||||
st7701->madctl_val &= ~LCD_CMD_ML_BIT;
|
||||
}
|
||||
|
||||
// Enable the Command2 BK0
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, ST7701_CMD_CND2BKxSEL, (uint8_t []) {
|
||||
ST7701_CMD_BKxSEL_BYTE0, ST7701_CMD_BKxSEL_BYTE1, ST7701_CMD_BKxSEL_BYTE2, ST7701_CMD_BKxSEL_BYTE3,
|
||||
ST7701_CMD_BKxSEL_BK0 | ST7701_CMD_CN2_BIT,
|
||||
}, 5), TAG, "send command failed");
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, ST7701_CMD_SDIR, (uint8_t[]) {
|
||||
sdir_val,
|
||||
}, 1), TAG, "send command failed");;
|
||||
|
||||
// Disable Command2
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, ST7701_CMD_CND2BKxSEL, (uint8_t []) {
|
||||
ST7701_CMD_BKxSEL_BYTE0, ST7701_CMD_BKxSEL_BYTE1, ST7701_CMD_BKxSEL_BYTE2, ST7701_CMD_BKxSEL_BYTE3, 0,
|
||||
}, 5), TAG, "send command failed");
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL, (uint8_t[]) {
|
||||
st7701->madctl_val,
|
||||
}, 1), TAG, "send command failed");;
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t panel_st7701_disp_on_off(esp_lcd_panel_t *panel, bool on_off)
|
||||
{
|
||||
st7701_panel_t *st7701 = (st7701_panel_t *)panel->user_data;
|
||||
esp_lcd_panel_io_handle_t io = st7701->io;
|
||||
int command = 0;
|
||||
|
||||
if (on_off) {
|
||||
command = LCD_CMD_DISPON;
|
||||
} else {
|
||||
command = LCD_CMD_DISPOFF;
|
||||
}
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, command, NULL, 0), TAG, "send command failed");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t panel_st7701_sleep(esp_lcd_panel_t *panel, bool sleep)
|
||||
{
|
||||
st7701_panel_t *st7701 = (st7701_panel_t *)panel->user_data;
|
||||
esp_lcd_panel_io_handle_t io = st7701->io;
|
||||
int command = 0;
|
||||
if (sleep) {
|
||||
command = LCD_CMD_SLPIN;
|
||||
} else {
|
||||
command = LCD_CMD_SLPOUT;
|
||||
}
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, command, NULL, 0), TAG,
|
||||
"io tx param failed");
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
#endif
|
||||
+386
@@ -0,0 +1,386 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include "soc/soc_caps.h"
|
||||
|
||||
#if SOC_LCD_RGB_SUPPORTED
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "esp_check.h"
|
||||
#include "esp_lcd_panel_commands.h"
|
||||
#include "esp_lcd_panel_interface.h"
|
||||
#include "esp_lcd_panel_io.h"
|
||||
#include "esp_lcd_panel_rgb.h"
|
||||
#include "esp_lcd_panel_vendor.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_lcd_st7701.h"
|
||||
#include "esp_lcd_st7701_interface.h"
|
||||
typedef struct {
|
||||
esp_lcd_panel_io_handle_t io;
|
||||
int reset_gpio_num;
|
||||
uint8_t madctl_val; // Save current value of LCD_CMD_MADCTL register
|
||||
uint8_t colmod_val; // Save current value of LCD_CMD_COLMOD register
|
||||
const st7701_lcd_init_cmd_t *init_cmds;
|
||||
uint16_t init_cmds_size;
|
||||
struct {
|
||||
unsigned int mirror_by_cmd: 1;
|
||||
unsigned int enable_io_multiplex: 1;
|
||||
unsigned int display_on_off_use_cmd: 1;
|
||||
unsigned int reset_level: 1;
|
||||
} flags;
|
||||
// To save the original functions of RGB panel
|
||||
esp_err_t (*init)(esp_lcd_panel_t *panel);
|
||||
esp_err_t (*del)(esp_lcd_panel_t *panel);
|
||||
esp_err_t (*reset)(esp_lcd_panel_t *panel);
|
||||
esp_err_t (*mirror)(esp_lcd_panel_t *panel, bool x_axis, bool y_axis);
|
||||
esp_err_t (*disp_on_off)(esp_lcd_panel_t *panel, bool on_off);
|
||||
} st7701_panel_t;
|
||||
|
||||
static const char *TAG = "st7701_rgb";
|
||||
|
||||
static esp_err_t panel_st7701_send_init_cmds(st7701_panel_t *st7701);
|
||||
|
||||
static esp_err_t panel_st7701_init(esp_lcd_panel_t *panel);
|
||||
static esp_err_t panel_st7701_del(esp_lcd_panel_t *panel);
|
||||
static esp_err_t panel_st7701_reset(esp_lcd_panel_t *panel);
|
||||
static esp_err_t panel_st7701_mirror(esp_lcd_panel_t *panel, bool mirror_x, bool mirror_y);
|
||||
static esp_err_t panel_st7701_disp_on_off(esp_lcd_panel_t *panel, bool off);
|
||||
|
||||
esp_err_t esp_lcd_new_panel_st7701_rgb(const esp_lcd_panel_io_handle_t io, const esp_lcd_panel_dev_config_t *panel_dev_config,
|
||||
esp_lcd_panel_handle_t *ret_panel)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(io && panel_dev_config && ret_panel, ESP_ERR_INVALID_ARG, TAG, "invalid arguments");
|
||||
st7701_vendor_config_t *vendor_config = (st7701_vendor_config_t *)panel_dev_config->vendor_config;
|
||||
ESP_RETURN_ON_FALSE(vendor_config && vendor_config->rgb_config, ESP_ERR_INVALID_ARG, TAG, "`verndor_config` and `rgb_config` are necessary");
|
||||
ESP_RETURN_ON_FALSE(!vendor_config->flags.enable_io_multiplex || !vendor_config->flags.mirror_by_cmd,
|
||||
ESP_ERR_INVALID_ARG, TAG, "`mirror_by_cmd` and `enable_io_multiplex` cannot work together");
|
||||
|
||||
esp_err_t ret = ESP_OK;
|
||||
st7701_panel_t *st7701 = (st7701_panel_t *)calloc(1, sizeof(st7701_panel_t));
|
||||
ESP_RETURN_ON_FALSE(st7701, ESP_ERR_NO_MEM, TAG, "no mem for st7701 panel");
|
||||
|
||||
if (panel_dev_config->reset_gpio_num >= 0) {
|
||||
gpio_config_t io_conf = {
|
||||
.mode = GPIO_MODE_OUTPUT,
|
||||
.pin_bit_mask = 1ULL << panel_dev_config->reset_gpio_num,
|
||||
};
|
||||
ESP_GOTO_ON_ERROR(gpio_config(&io_conf), err, TAG, "configure GPIO for RST line failed");
|
||||
}
|
||||
|
||||
switch (panel_dev_config->rgb_ele_order) {
|
||||
case LCD_RGB_ELEMENT_ORDER_RGB:
|
||||
st7701->madctl_val = 0;
|
||||
break;
|
||||
case LCD_RGB_ELEMENT_ORDER_BGR:
|
||||
st7701->madctl_val |= LCD_CMD_BGR_BIT;
|
||||
break;
|
||||
default:
|
||||
ESP_GOTO_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, err, TAG, "unsupported color element order");
|
||||
break;
|
||||
}
|
||||
|
||||
st7701->colmod_val = 0;
|
||||
switch (panel_dev_config->bits_per_pixel) {
|
||||
case 16: // RGB565
|
||||
st7701->colmod_val = 0x50;
|
||||
break;
|
||||
case 18: // RGB666
|
||||
st7701->colmod_val = 0x60;
|
||||
break;
|
||||
case 24: // RGB888
|
||||
st7701->colmod_val = 0x70;
|
||||
break;
|
||||
default:
|
||||
ESP_GOTO_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, err, TAG, "unsupported pixel width");
|
||||
break;
|
||||
}
|
||||
|
||||
st7701->io = io;
|
||||
st7701->init_cmds = vendor_config->init_cmds;
|
||||
st7701->init_cmds_size = vendor_config->init_cmds_size;
|
||||
st7701->reset_gpio_num = panel_dev_config->reset_gpio_num;
|
||||
st7701->flags.mirror_by_cmd = vendor_config->flags.mirror_by_cmd;
|
||||
st7701->flags.display_on_off_use_cmd = (vendor_config->rgb_config->disp_gpio_num >= 0) ? 0 : 1;
|
||||
st7701->flags.enable_io_multiplex = vendor_config->flags.enable_io_multiplex;
|
||||
st7701->flags.reset_level = panel_dev_config->flags.reset_active_high;
|
||||
|
||||
if (st7701->flags.enable_io_multiplex) {
|
||||
if (st7701->reset_gpio_num >= 0) { // Perform hardware reset
|
||||
gpio_set_level(st7701->reset_gpio_num, st7701->flags.reset_level);
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
gpio_set_level(st7701->reset_gpio_num, !st7701->flags.reset_level);
|
||||
} else { // Perform software reset
|
||||
ESP_GOTO_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_SWRESET, NULL, 0), err, TAG, "send command failed");
|
||||
}
|
||||
vTaskDelay(pdMS_TO_TICKS(120));
|
||||
|
||||
/**
|
||||
* In order to enable the 3-wire SPI interface pins (such as SDA and SCK) to share other pins of the RGB interface
|
||||
* (such as HSYNC) and save GPIOs, we need to send LCD initialization commands via the 3-wire SPI interface before
|
||||
* `esp_lcd_new_rgb_panel()` is called.
|
||||
*/
|
||||
ESP_GOTO_ON_ERROR(panel_st7701_send_init_cmds(st7701), err, TAG, "send init commands failed");
|
||||
// After sending the initialization commands, the 3-wire SPI interface can be deleted
|
||||
ESP_GOTO_ON_ERROR(esp_lcd_panel_io_del(io), err, TAG, "delete panel IO failed");
|
||||
st7701->io = NULL;
|
||||
ESP_LOGD(TAG, "delete panel IO");
|
||||
}
|
||||
|
||||
// Create RGB panel
|
||||
ESP_GOTO_ON_ERROR(esp_lcd_new_rgb_panel(vendor_config->rgb_config, ret_panel), err, TAG, "create RGB panel failed");
|
||||
ESP_LOGD(TAG, "new RGB panel @%p", ret_panel);
|
||||
|
||||
// Save the original functions of RGB panel
|
||||
st7701->init = (*ret_panel)->init;
|
||||
st7701->del = (*ret_panel)->del;
|
||||
st7701->reset = (*ret_panel)->reset;
|
||||
st7701->mirror = (*ret_panel)->mirror;
|
||||
st7701->disp_on_off = (*ret_panel)->disp_on_off;
|
||||
// Overwrite the functions of RGB panel
|
||||
(*ret_panel)->init = panel_st7701_init;
|
||||
(*ret_panel)->del = panel_st7701_del;
|
||||
(*ret_panel)->reset = panel_st7701_reset;
|
||||
(*ret_panel)->mirror = panel_st7701_mirror;
|
||||
(*ret_panel)->disp_on_off = panel_st7701_disp_on_off;
|
||||
(*ret_panel)->user_data = st7701;
|
||||
ESP_LOGD(TAG, "new st7701 panel @%p", st7701);
|
||||
|
||||
return ESP_OK;
|
||||
|
||||
err:
|
||||
if (st7701) {
|
||||
if (panel_dev_config->reset_gpio_num >= 0) {
|
||||
gpio_reset_pin(panel_dev_config->reset_gpio_num);
|
||||
}
|
||||
free(st7701);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const st7701_lcd_init_cmd_t vendor_specific_init_default[] = {
|
||||
// {cmd, { data }, data_size, delay_ms}
|
||||
{0xFF, (uint8_t []){0x77, 0x01, 0x00, 0x00, 0x13}, 5, 0},
|
||||
{0xEF, (uint8_t []){0x08}, 1, 0},
|
||||
{0xFF, (uint8_t []){0x77, 0x01, 0x00, 0x00, 0x10}, 5, 0},
|
||||
{0xC0, (uint8_t []){0x3B, 0x00}, 2, 0},
|
||||
{0xC1, (uint8_t []){0x10, 0x02}, 2, 0},
|
||||
{0xC2, (uint8_t []){0x20, 0x06}, 2, 0},
|
||||
{0xCC, (uint8_t []){0x10}, 1, 0},
|
||||
{0xB0, (uint8_t []){0x00, 0x13, 0x5A, 0x0F, 0x12, 0x07, 0x09, 0x08, 0x08, 0x24, 0x07, 0x13, 0x12, 0x6B, 0x73, 0xFF}, 16, 0},
|
||||
{0xB1, (uint8_t []){0x00, 0x13, 0x5A, 0x0F, 0x12, 0x07, 0x09, 0x08, 0x08, 0x24, 0x07, 0x13, 0x12, 0x6B, 0x73, 0xFF}, 16, 0},
|
||||
{0xFF, (uint8_t []){0x77, 0x01, 0x00, 0x00, 0x11}, 5, 0},
|
||||
{0xB0, (uint8_t []){0x8D}, 1, 0},
|
||||
{0xB1, (uint8_t []){0x48}, 1, 0},
|
||||
{0xB2, (uint8_t []){0x89}, 1, 0},
|
||||
{0xB3, (uint8_t []){0x80}, 1, 0},
|
||||
{0xB5, (uint8_t []){0x49}, 1, 0},
|
||||
{0xB7, (uint8_t []){0x85}, 1, 0},
|
||||
{0xB8, (uint8_t []){0x32}, 1, 0},
|
||||
{0xC1, (uint8_t []){0x78}, 1, 0},
|
||||
{0xC2, (uint8_t []){0x78}, 1, 0},
|
||||
{0xD0, (uint8_t []){0x88}, 1, 100},
|
||||
{0xE0, (uint8_t []){0x00, 0x00, 0x02}, 3, 0},
|
||||
{0xE1, (uint8_t []){0x05, 0xC0, 0x07, 0xC0, 0x04, 0xC0, 0x06, 0xC0, 0x00, 0x44, 0x44}, 11, 0},
|
||||
{0xE2, (uint8_t []){0x00, 0x00, 0x33, 0x33, 0x01, 0xC0, 0x00, 0x00, 0x01, 0xC0, 0x00, 0x00, 0x00}, 13, 0},
|
||||
{0xE3, (uint8_t []){0x00, 0x00, 0x11, 0x11}, 4, 0},
|
||||
{0xE4, (uint8_t []){0x44, 0x44}, 2, 0},
|
||||
{0xE5, (uint8_t []){0x0D, 0xF1, 0x10, 0x98, 0x0F, 0xF3, 0x10, 0x98, 0x09, 0xED, 0x10, 0x98, 0x0B, 0xEF, 0x10, 0x98}, 16, 0},
|
||||
{0xE6, (uint8_t []){0x00, 0x00, 0x11, 0x11}, 4, 0},
|
||||
{0xE7, (uint8_t []){0x44, 0x44}, 2, 0},
|
||||
{0xE8, (uint8_t []){0x0C, 0xF0, 0x10, 0x98, 0x0E, 0xF2, 0x10, 0x98, 0x08, 0xEC, 0x10, 0x98, 0x0A, 0xEE, 0x10, 0x98}, 16, 0},
|
||||
{0xEB, (uint8_t []){0x00, 0x01, 0xE4, 0xE4, 0x44, 0x88, 0x00}, 7, 0},
|
||||
{0xED, (uint8_t []){0xFF, 0x04, 0x56, 0x7F, 0xBA, 0x2F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF2, 0xAB, 0xF7, 0x65, 0x40, 0xFF}, 16, 0},
|
||||
{0xEF, (uint8_t []){0x10, 0x0D, 0x04, 0x08, 0x3F, 0x1F}, 6, 0},
|
||||
{0xFF, (uint8_t []){0x77, 0x01, 0x00, 0x00, 0x00}, 5, 0},
|
||||
{0x11, (uint8_t []){0x00}, 0, 120},
|
||||
{0x29, (uint8_t []){0x00}, 0, 0},
|
||||
|
||||
// {0xFF, (uint8_t []){0x77, 0x01, 0x00, 0x00, 0x12}, 5, 0}, /* This part of the parameters can be used for screen self-test */
|
||||
// {0xD1, (uint8_t []){0x81}, 1, 0},
|
||||
// {0xD2, (uint8_t []){0x08}, 1, 0},
|
||||
};
|
||||
|
||||
static esp_err_t panel_st7701_send_init_cmds(st7701_panel_t *st7701)
|
||||
{
|
||||
esp_lcd_panel_io_handle_t io = st7701->io;
|
||||
const st7701_lcd_init_cmd_t *init_cmds = NULL;
|
||||
uint16_t init_cmds_size = 0;
|
||||
bool is_command2_disable = true;
|
||||
bool is_cmd_overwritten = false;
|
||||
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, ST7701_CMD_CND2BKxSEL, (uint8_t []) {
|
||||
ST7701_CMD_BKxSEL_BYTE0, ST7701_CMD_BKxSEL_BYTE1, ST7701_CMD_BKxSEL_BYTE2, ST7701_CMD_BKxSEL_BYTE3, 0x00
|
||||
}, 5), TAG, "Write cmd failed");
|
||||
// Set color format
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL, (uint8_t []) {
|
||||
st7701->madctl_val
|
||||
}, 1), TAG, "Write cmd failed");
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_COLMOD, (uint8_t []) {
|
||||
st7701->colmod_val
|
||||
}, 1), TAG, "Write cmd failed");
|
||||
|
||||
// vendor specific initialization, it can be different between manufacturers
|
||||
// should consult the LCD supplier for initialization sequence code
|
||||
if (st7701->init_cmds) {
|
||||
init_cmds = st7701->init_cmds;
|
||||
init_cmds_size = st7701->init_cmds_size;
|
||||
} else {
|
||||
init_cmds = vendor_specific_init_default;
|
||||
init_cmds_size = sizeof(vendor_specific_init_default) / sizeof(st7701_lcd_init_cmd_t);
|
||||
}
|
||||
|
||||
for (int i = 0; i < init_cmds_size; i++) {
|
||||
// Check if the command has been used or conflicts with the internal only when command2 is disable
|
||||
if (is_command2_disable && (init_cmds[i].data_bytes > 0)) {
|
||||
switch (init_cmds[i].cmd) {
|
||||
case LCD_CMD_MADCTL:
|
||||
is_cmd_overwritten = true;
|
||||
st7701->madctl_val = ((uint8_t *)init_cmds[i].data)[0];
|
||||
break;
|
||||
case LCD_CMD_COLMOD:
|
||||
is_cmd_overwritten = true;
|
||||
st7701->colmod_val = ((uint8_t *)init_cmds[i].data)[0];
|
||||
break;
|
||||
default:
|
||||
is_cmd_overwritten = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (is_cmd_overwritten) {
|
||||
is_cmd_overwritten = false;
|
||||
ESP_LOGW(TAG, "The %02Xh command has been used and will be overwritten by external initialization sequence",
|
||||
init_cmds[i].cmd);
|
||||
}
|
||||
}
|
||||
|
||||
// Send command
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, init_cmds[i].cmd, init_cmds[i].data, init_cmds[i].data_bytes),
|
||||
TAG, "send command failed");
|
||||
vTaskDelay(pdMS_TO_TICKS(init_cmds[i].delay_ms));
|
||||
|
||||
// Check if the current cmd is the command2 disable cmd
|
||||
if ((init_cmds[i].cmd == ST7701_CMD_CND2BKxSEL) && (init_cmds[i].data_bytes > 4)) {
|
||||
is_command2_disable = !(((uint8_t *)init_cmds[i].data)[4] & ST7701_CMD_CN2_BIT);
|
||||
}
|
||||
}
|
||||
ESP_LOGD(TAG, "send init commands success");
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t panel_st7701_init(esp_lcd_panel_t *panel)
|
||||
{
|
||||
st7701_panel_t *st7701 = (st7701_panel_t *)panel->user_data;
|
||||
|
||||
if (!st7701->flags.enable_io_multiplex) {
|
||||
ESP_RETURN_ON_ERROR(panel_st7701_send_init_cmds(st7701), TAG, "send init commands failed");
|
||||
}
|
||||
// Init RGB panel
|
||||
ESP_RETURN_ON_ERROR(st7701->init(panel), TAG, "init RGB panel failed");
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t panel_st7701_del(esp_lcd_panel_t *panel)
|
||||
{
|
||||
st7701_panel_t *st7701 = (st7701_panel_t *)panel->user_data;
|
||||
|
||||
if (st7701->reset_gpio_num >= 0) {
|
||||
gpio_reset_pin(st7701->reset_gpio_num);
|
||||
}
|
||||
// Delete RGB panel
|
||||
st7701->del(panel);
|
||||
free(st7701);
|
||||
ESP_LOGD(TAG, "del st7701 panel @%p", st7701);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t panel_st7701_reset(esp_lcd_panel_t *panel)
|
||||
{
|
||||
st7701_panel_t *st7701 = (st7701_panel_t *)panel->user_data;
|
||||
esp_lcd_panel_io_handle_t io = st7701->io;
|
||||
|
||||
// Perform hardware reset
|
||||
if (st7701->reset_gpio_num >= 0) {
|
||||
gpio_set_level(st7701->reset_gpio_num, st7701->flags.reset_level);
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
gpio_set_level(st7701->reset_gpio_num, !st7701->flags.reset_level);
|
||||
vTaskDelay(pdMS_TO_TICKS(120));
|
||||
} else if (io) { // Perform software reset
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_SWRESET, NULL, 0), TAG, "send command failed");
|
||||
vTaskDelay(pdMS_TO_TICKS(120));
|
||||
}
|
||||
// Reset RGB panel
|
||||
ESP_RETURN_ON_ERROR(st7701->reset(panel), TAG, "reset RGB panel failed");
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t panel_st7701_mirror(esp_lcd_panel_t *panel, bool mirror_x, bool mirror_y)
|
||||
{
|
||||
st7701_panel_t *st7701 = (st7701_panel_t *)panel->user_data;
|
||||
esp_lcd_panel_io_handle_t io = st7701->io;
|
||||
uint8_t sdir_val = 0;
|
||||
|
||||
if (st7701->flags.mirror_by_cmd) {
|
||||
ESP_RETURN_ON_FALSE(io, ESP_FAIL, TAG, "Panel IO is deleted, cannot send command");
|
||||
// Control mirror through LCD command
|
||||
if (mirror_x) {
|
||||
sdir_val = ST7701_CMD_SS_BIT;
|
||||
} else {
|
||||
sdir_val = 0;
|
||||
}
|
||||
if (mirror_y) {
|
||||
st7701->madctl_val |= LCD_CMD_ML_BIT;
|
||||
} else {
|
||||
st7701->madctl_val &= ~LCD_CMD_ML_BIT;
|
||||
}
|
||||
|
||||
// Enable the Command2 BK0
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, ST7701_CMD_CND2BKxSEL, (uint8_t []) {
|
||||
ST7701_CMD_BKxSEL_BYTE0, ST7701_CMD_BKxSEL_BYTE1, ST7701_CMD_BKxSEL_BYTE2, ST7701_CMD_BKxSEL_BYTE3,
|
||||
ST7701_CMD_BKxSEL_BK0 | ST7701_CMD_CN2_BIT,
|
||||
}, 5), TAG, "send command failed");
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, ST7701_CMD_SDIR, (uint8_t[]) {
|
||||
sdir_val,
|
||||
}, 1), TAG, "send command failed");;
|
||||
|
||||
// Disable Command2
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, ST7701_CMD_CND2BKxSEL, (uint8_t []) {
|
||||
ST7701_CMD_BKxSEL_BYTE0, ST7701_CMD_BKxSEL_BYTE1, ST7701_CMD_BKxSEL_BYTE2, ST7701_CMD_BKxSEL_BYTE3, 0,
|
||||
}, 5), TAG, "send command failed");
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL, (uint8_t[]) {
|
||||
st7701->madctl_val,
|
||||
}, 1), TAG, "send command failed");;
|
||||
} else {
|
||||
// Control mirror through RGB panel
|
||||
ESP_RETURN_ON_ERROR(st7701->mirror(panel, mirror_x, mirror_y), TAG, "RGB panel mirror failed");
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t panel_st7701_disp_on_off(esp_lcd_panel_t *panel, bool on_off)
|
||||
{
|
||||
st7701_panel_t *st7701 = (st7701_panel_t *)panel->user_data;
|
||||
esp_lcd_panel_io_handle_t io = st7701->io;
|
||||
int command = 0;
|
||||
|
||||
if (st7701->flags.display_on_off_use_cmd) {
|
||||
ESP_RETURN_ON_FALSE(io, ESP_FAIL, TAG, "Panel IO is deleted, cannot send command");
|
||||
// Control display on/off through LCD command
|
||||
if (on_off) {
|
||||
command = LCD_CMD_DISPON;
|
||||
} else {
|
||||
command = LCD_CMD_DISPOFF;
|
||||
}
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, command, NULL, 0), TAG, "send command failed");
|
||||
} else {
|
||||
// Control display on/off through display control signal
|
||||
ESP_RETURN_ON_ERROR(st7701->disp_on_off(panel, on_off), TAG, "RGB panel disp_on_off failed");
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
#endif
|
||||
+183
@@ -0,0 +1,183 @@
|
||||
#include "sdkconfig.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "esp_timer.h"
|
||||
#include "esp_lcd_panel_ops.h"
|
||||
#include "esp_lcd_mipi_dsi.h"
|
||||
#include "esp_lcd_panel_io.h"
|
||||
#include "esp_ldo_regulator.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "driver/i2c_master.h"
|
||||
#include "driver/ledc.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_log.h"
|
||||
#include "Arduino.h"
|
||||
|
||||
#include "esp_lcd_st7701.h"
|
||||
#include "st7701_lcd.h"
|
||||
|
||||
#define LCD_H_RES 480
|
||||
#define LCD_V_RES 800
|
||||
|
||||
#define MIPI_DPI_PX_FORMAT (LCD_COLOR_PIXEL_FORMAT_RGB565)
|
||||
#define LCD_BIT_PER_PIXEL (16)
|
||||
|
||||
// “VDD_MIPI_DPHY”应供电 2.5V,可从内部 LDO 稳压器或外部 LDO 芯片获取电源
|
||||
#define EXAMPLE_MIPI_DSI_PHY_PWR_LDO_CHAN 3 // LDO_VO3 连接至 VDD_MIPI_DPHY
|
||||
#define EXAMPLE_MIPI_DSI_PHY_PWR_LDO_VOLTAGE_MV 2500
|
||||
#define EXAMPLE_LCD_BK_LIGHT_ON_LEVEL 1
|
||||
#define EXAMPLE_LCD_BK_LIGHT_OFF_LEVEL (0)
|
||||
#define EXAMPLE_PIN_NUM_BK_LIGHT GPIO_NUM_23
|
||||
|
||||
#define BSP_LCD_BACKLIGHT GPIO_NUM_23
|
||||
#define LCD_LEDC_CH LEDC_CHANNEL_0
|
||||
|
||||
static const char *TAG = "example";
|
||||
esp_lcd_panel_handle_t panel_handle = NULL;
|
||||
esp_lcd_panel_io_handle_t io_handle = NULL;
|
||||
|
||||
st7701_lcd::st7701_lcd(int8_t lcd_rst)
|
||||
{
|
||||
_lcd_rst = lcd_rst;
|
||||
}
|
||||
|
||||
void st7701_lcd::example_bsp_enable_dsi_phy_power()
|
||||
{
|
||||
// 打开 MIPI DSI PHY 的电源,使其从“无电”状态进入“关机”状态
|
||||
esp_ldo_channel_handle_t ldo_mipi_phy = NULL;
|
||||
#ifdef EXAMPLE_MIPI_DSI_PHY_PWR_LDO_CHAN
|
||||
esp_ldo_channel_config_t ldo_mipi_phy_config = {
|
||||
.chan_id = EXAMPLE_MIPI_DSI_PHY_PWR_LDO_CHAN,
|
||||
.voltage_mv = EXAMPLE_MIPI_DSI_PHY_PWR_LDO_VOLTAGE_MV,
|
||||
};
|
||||
ESP_ERROR_CHECK(esp_ldo_acquire_channel(&ldo_mipi_phy_config, &ldo_mipi_phy));
|
||||
ESP_LOGI(TAG, "MIPI DSI PHY Powered on");
|
||||
#endif
|
||||
}
|
||||
|
||||
void st7701_lcd::example_bsp_init_lcd_backlight()
|
||||
{
|
||||
#if EXAMPLE_PIN_NUM_BK_LIGHT >= 0
|
||||
gpio_config_t bk_gpio_config = {
|
||||
.pin_bit_mask = 1ULL << EXAMPLE_PIN_NUM_BK_LIGHT,
|
||||
.mode = GPIO_MODE_OUTPUT
|
||||
};
|
||||
ESP_ERROR_CHECK(gpio_config(&bk_gpio_config));
|
||||
#endif
|
||||
}
|
||||
|
||||
void st7701_lcd::example_bsp_set_lcd_backlight(uint32_t level)
|
||||
{
|
||||
|
||||
#if EXAMPLE_PIN_NUM_BK_LIGHT >= 0
|
||||
gpio_set_level(EXAMPLE_PIN_NUM_BK_LIGHT, level);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
void st7701_lcd::begin()
|
||||
{
|
||||
example_bsp_enable_dsi_phy_power();
|
||||
example_bsp_init_lcd_backlight();
|
||||
// example_bsp_set_lcd_backlight(EXAMPLE_LCD_BK_LIGHT_OFF_LEVEL);
|
||||
|
||||
// 首先创建 MIPI DSI 总线,它还将初始化 DSI PHY
|
||||
esp_lcd_dsi_bus_handle_t mipi_dsi_bus;
|
||||
esp_lcd_dsi_bus_config_t bus_config = ST7701_PANEL_BUS_DSI_2CH_CONFIG();
|
||||
ESP_ERROR_CHECK(esp_lcd_new_dsi_bus(&bus_config, &mipi_dsi_bus));
|
||||
|
||||
ESP_LOGI(TAG, "Install MIPI DSI LCD control panel");
|
||||
// 我们使用DBI接口发送LCD命令和参数
|
||||
esp_lcd_dbi_io_config_t dbi_config = ST7701_PANEL_IO_DBI_CONFIG();
|
||||
|
||||
ESP_ERROR_CHECK(esp_lcd_new_panel_io_dbi(mipi_dsi_bus, &dbi_config, &io_handle));
|
||||
|
||||
// 创建JD9365控制面板
|
||||
esp_lcd_dpi_panel_config_t dpi_config = ST7701_480_360_PANEL_60HZ_DPI_CONFIG(MIPI_DPI_PX_FORMAT);
|
||||
|
||||
st7701_vendor_config_t vendor_config = {
|
||||
// .init_cmds = lcd_cmd,
|
||||
// .init_cmds_size = sizeof(lcd_cmd) / sizeof(st7701_lcd_init_cmd_t),
|
||||
.mipi_config = {
|
||||
.dsi_bus = mipi_dsi_bus,
|
||||
.dpi_config = &dpi_config,
|
||||
},
|
||||
.flags = {
|
||||
.use_mipi_interface = 1,
|
||||
}
|
||||
};
|
||||
|
||||
const esp_lcd_panel_dev_config_t panel_config = {
|
||||
.reset_gpio_num = GPIO_NUM_5,
|
||||
.rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB,
|
||||
.bits_per_pixel = 16,
|
||||
.vendor_config = &vendor_config,
|
||||
};
|
||||
ESP_ERROR_CHECK(esp_lcd_new_panel_st7701(io_handle, &panel_config, &panel_handle));
|
||||
ESP_ERROR_CHECK(esp_lcd_panel_reset(panel_handle));
|
||||
ESP_ERROR_CHECK(esp_lcd_panel_init(panel_handle));
|
||||
|
||||
// esp_lcd_dpi_panel_event_callbacks_t cbs = {0};
|
||||
// if (dsi_cfg->flags.avoid_tearing) {
|
||||
// cbs.on_refresh_done = lvgl_port_flush_dpi_vsync_ready_callback;
|
||||
// } else {
|
||||
// cbs.on_color_trans_done = lvgl_port_flush_dpi_panel_ready_callback;
|
||||
// }
|
||||
// /* Register done callback */
|
||||
// esp_lcd_dpi_panel_register_event_callbacks(disp_ctx->panel_handle, &cbs, &disp_ctx->disp_drv);
|
||||
|
||||
// 打开背光
|
||||
example_bsp_set_lcd_backlight(EXAMPLE_LCD_BK_LIGHT_ON_LEVEL);
|
||||
}
|
||||
|
||||
void st7701_lcd::lcd_draw_bitmap(uint16_t x_start, uint16_t y_start, uint16_t x_end, uint16_t y_end, uint16_t *color_data)
|
||||
{
|
||||
esp_lcd_panel_draw_bitmap(panel_handle, x_start, y_start, x_end, y_end, color_data);
|
||||
}
|
||||
|
||||
void st7701_lcd::draw16bitbergbbitmap(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t *color_data)
|
||||
{
|
||||
uint16_t x_start = x;
|
||||
uint16_t y_start = y;
|
||||
uint16_t x_end = w + x;
|
||||
uint16_t y_end = h + y;
|
||||
|
||||
esp_lcd_panel_draw_bitmap(panel_handle, x_start, y_start, x_end, y_end, color_data);
|
||||
}
|
||||
|
||||
void st7701_lcd::fillScreen(uint16_t color)
|
||||
{
|
||||
uint16_t *color_data = (uint16_t *)heap_caps_malloc(480 * 272 * 2, MALLOC_CAP_INTERNAL);
|
||||
memset(color_data, color, 480 * 272 * 2);
|
||||
draw16bitbergbbitmap(0, 0, 480, 272, color_data);
|
||||
free(color_data);
|
||||
}
|
||||
|
||||
void st7701_lcd::te_on()
|
||||
{
|
||||
esp_lcd_panel_io_tx_param(io_handle, 0x35,new (uint8_t[]){0x00}, 1);
|
||||
}
|
||||
|
||||
void st7701_lcd::te_off()
|
||||
{
|
||||
esp_lcd_panel_io_tx_param(io_handle, 0x34,new (uint8_t[]){0x00}, 0);
|
||||
}
|
||||
|
||||
uint16_t st7701_lcd::width()
|
||||
{
|
||||
return LCD_H_RES;
|
||||
}
|
||||
|
||||
uint16_t st7701_lcd::height()
|
||||
{
|
||||
return LCD_V_RES;
|
||||
}
|
||||
|
||||
void st7701_lcd::get_handle(bsp_lcd_handles_t *ret_handles)
|
||||
{
|
||||
ret_handles->io = io_handle;
|
||||
ret_handles->mipi_dsi_bus = NULL;
|
||||
ret_handles->panel = panel_handle;
|
||||
ret_handles->control = NULL;
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
#ifndef _ST7701_LCD_H
|
||||
#define _ST7701_LCD_H
|
||||
#include <stdio.h>
|
||||
#include "esp_lcd_types.h"
|
||||
#include "esp_lcd_mipi_dsi.h"
|
||||
|
||||
typedef struct {
|
||||
esp_lcd_dsi_bus_handle_t mipi_dsi_bus; /*!< MIPI DSI bus handle */
|
||||
esp_lcd_panel_io_handle_t io; /*!< ESP LCD IO handle */
|
||||
esp_lcd_panel_handle_t panel; /*!< ESP LCD panel (color) handle */
|
||||
esp_lcd_panel_handle_t control; /*!< ESP LCD panel (control) handle */
|
||||
} bsp_lcd_handles_t;
|
||||
|
||||
class st7701_lcd
|
||||
{
|
||||
public:
|
||||
st7701_lcd(int8_t lcd_rst);
|
||||
|
||||
void begin();
|
||||
void example_bsp_enable_dsi_phy_power();
|
||||
void example_bsp_init_lcd_backlight();
|
||||
void example_bsp_set_lcd_backlight(uint32_t level);
|
||||
void lcd_draw_bitmap(uint16_t x_start, uint16_t y_start,
|
||||
uint16_t x_end, uint16_t y_end, uint16_t *color_data);
|
||||
void draw16bitbergbbitmap(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t *color_data);
|
||||
void fillScreen(uint16_t color);
|
||||
void te_on();
|
||||
void te_off();
|
||||
uint16_t width();
|
||||
uint16_t height();
|
||||
void get_handle(bsp_lcd_handles_t *ret_handles);
|
||||
|
||||
private:
|
||||
int8_t _lcd_rst;
|
||||
};
|
||||
#endif
|
||||
+236
@@ -0,0 +1,236 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "esp_system.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_check.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_lcd_touch.h"
|
||||
|
||||
static const char *TAG = "TP";
|
||||
|
||||
/*******************************************************************************
|
||||
* Function definitions
|
||||
*******************************************************************************/
|
||||
|
||||
/*******************************************************************************
|
||||
* Local variables
|
||||
*******************************************************************************/
|
||||
|
||||
/*******************************************************************************
|
||||
* Public API functions
|
||||
*******************************************************************************/
|
||||
|
||||
esp_err_t esp_lcd_touch_read_data(esp_lcd_touch_handle_t tp)
|
||||
{
|
||||
assert(tp != NULL);
|
||||
assert(tp->read_data != NULL);
|
||||
|
||||
return tp->read_data(tp);
|
||||
}
|
||||
|
||||
bool esp_lcd_touch_get_coordinates(esp_lcd_touch_handle_t tp, uint16_t *x, uint16_t *y, uint16_t *strength, uint8_t *point_num, uint8_t max_point_num)
|
||||
{
|
||||
bool touched = false;
|
||||
|
||||
assert(tp != NULL);
|
||||
assert(x != NULL);
|
||||
assert(y != NULL);
|
||||
assert(tp->get_xy != NULL);
|
||||
|
||||
touched = tp->get_xy(tp, x, y, strength, point_num, max_point_num);
|
||||
if (!touched) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Process coordinates by user */
|
||||
if (tp->config.process_coordinates != NULL) {
|
||||
tp->config.process_coordinates(tp, x, y, strength, point_num, max_point_num);
|
||||
}
|
||||
|
||||
/* Software coordinates adjustment needed */
|
||||
bool sw_adj_needed = ((tp->config.flags.mirror_x && (tp->set_mirror_x == NULL)) ||
|
||||
(tp->config.flags.mirror_y && (tp->set_mirror_y == NULL)) ||
|
||||
(tp->config.flags.swap_xy && (tp->set_swap_xy == NULL)));
|
||||
|
||||
/* Adjust all coordinates */
|
||||
for (int i = 0; (sw_adj_needed && i < *point_num); i++) {
|
||||
|
||||
/* Mirror X coordinates (if not supported by HW) */
|
||||
if (tp->config.flags.mirror_x && tp->set_mirror_x == NULL) {
|
||||
x[i] = tp->config.x_max - x[i];
|
||||
}
|
||||
|
||||
/* Mirror Y coordinates (if not supported by HW) */
|
||||
if (tp->config.flags.mirror_y && tp->set_mirror_y == NULL) {
|
||||
y[i] = tp->config.y_max - y[i];
|
||||
}
|
||||
|
||||
/* Swap X and Y coordinates (if not supported by HW) */
|
||||
if (tp->config.flags.swap_xy && tp->set_swap_xy == NULL) {
|
||||
uint16_t tmp = x[i];
|
||||
x[i] = y[i];
|
||||
y[i] = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
return touched;
|
||||
}
|
||||
|
||||
#if (CONFIG_ESP_LCD_TOUCH_MAX_BUTTONS > 0)
|
||||
esp_err_t esp_lcd_touch_get_button_state(esp_lcd_touch_handle_t tp, uint8_t n, uint8_t *state)
|
||||
{
|
||||
assert(tp != NULL);
|
||||
assert(state != NULL);
|
||||
|
||||
*state = 0;
|
||||
|
||||
if (tp->get_button_state) {
|
||||
return tp->get_button_state(tp, n, state);
|
||||
} else {
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
esp_err_t esp_lcd_touch_set_swap_xy(esp_lcd_touch_handle_t tp, bool swap)
|
||||
{
|
||||
assert(tp != NULL);
|
||||
|
||||
tp->config.flags.swap_xy = swap;
|
||||
|
||||
/* Is swap supported by HW? */
|
||||
if (tp->set_swap_xy) {
|
||||
return tp->set_swap_xy(tp, swap);
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_lcd_touch_get_swap_xy(esp_lcd_touch_handle_t tp, bool *swap)
|
||||
{
|
||||
assert(tp != NULL);
|
||||
assert(swap != NULL);
|
||||
|
||||
/* Is swap supported by HW? */
|
||||
if (tp->get_swap_xy) {
|
||||
return tp->get_swap_xy(tp, swap);
|
||||
} else {
|
||||
*swap = tp->config.flags.swap_xy;
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_lcd_touch_set_mirror_x(esp_lcd_touch_handle_t tp, bool mirror)
|
||||
{
|
||||
assert(tp != NULL);
|
||||
|
||||
tp->config.flags.mirror_x = mirror;
|
||||
|
||||
/* Is mirror supported by HW? */
|
||||
if (tp->set_mirror_x) {
|
||||
return tp->set_mirror_x(tp, mirror);
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_lcd_touch_get_mirror_x(esp_lcd_touch_handle_t tp, bool *mirror)
|
||||
{
|
||||
assert(tp != NULL);
|
||||
assert(mirror != NULL);
|
||||
|
||||
/* Is swap supported by HW? */
|
||||
if (tp->get_mirror_x) {
|
||||
return tp->get_mirror_x(tp, mirror);
|
||||
} else {
|
||||
*mirror = tp->config.flags.mirror_x;
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_lcd_touch_set_mirror_y(esp_lcd_touch_handle_t tp, bool mirror)
|
||||
{
|
||||
assert(tp != NULL);
|
||||
|
||||
tp->config.flags.mirror_y = mirror;
|
||||
|
||||
/* Is mirror supported by HW? */
|
||||
if (tp->set_mirror_y) {
|
||||
return tp->set_mirror_y(tp, mirror);
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_lcd_touch_get_mirror_y(esp_lcd_touch_handle_t tp, bool *mirror)
|
||||
{
|
||||
assert(tp != NULL);
|
||||
assert(mirror != NULL);
|
||||
|
||||
/* Is swap supported by HW? */
|
||||
if (tp->get_mirror_y) {
|
||||
return tp->get_mirror_y(tp, mirror);
|
||||
} else {
|
||||
*mirror = tp->config.flags.mirror_y;
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_lcd_touch_del(esp_lcd_touch_handle_t tp)
|
||||
{
|
||||
assert(tp != NULL);
|
||||
|
||||
if (tp->del != NULL) {
|
||||
return tp->del(tp);
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_lcd_touch_register_interrupt_callback(esp_lcd_touch_handle_t tp, esp_lcd_touch_interrupt_callback_t callback)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
assert(tp != NULL);
|
||||
|
||||
/* Interrupt pin is not selected */
|
||||
if (tp->config.int_gpio_num == GPIO_NUM_NC) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
tp->config.interrupt_callback = callback;
|
||||
|
||||
if (callback != NULL) {
|
||||
ret = gpio_install_isr_service(0);
|
||||
/* ISR service can be installed from user before, then it returns invalid state */
|
||||
if (ret != ESP_OK && ret != ESP_ERR_INVALID_STATE) {
|
||||
ESP_LOGE(TAG, "GPIO ISR install failed");
|
||||
return ret;
|
||||
}
|
||||
/* Add GPIO ISR handler */
|
||||
ret = gpio_intr_enable(tp->config.int_gpio_num);
|
||||
ESP_RETURN_ON_ERROR(ret, TAG, "GPIO ISR install failed");
|
||||
ret = gpio_isr_handler_add(tp->config.int_gpio_num, (gpio_isr_t)tp->config.interrupt_callback, tp);
|
||||
ESP_RETURN_ON_ERROR(ret, TAG, "GPIO ISR install failed");
|
||||
} else {
|
||||
/* Remove GPIO ISR handler */
|
||||
ret = gpio_isr_handler_remove(tp->config.int_gpio_num);
|
||||
ESP_RETURN_ON_ERROR(ret, TAG, "GPIO ISR remove handler failed");
|
||||
ret = gpio_intr_disable(tp->config.int_gpio_num);
|
||||
ESP_RETURN_ON_ERROR(ret, TAG, "GPIO ISR disable failed");
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
+370
@@ -0,0 +1,370 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* @brief ESP LCD touch
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "sdkconfig.h"
|
||||
#include "esp_err.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "esp_lcd_panel_io.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/semphr.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define CONFIG_ESP_LCD_TOUCH_MAX_BUTTONS (1)
|
||||
#define CONFIG_ESP_LCD_TOUCH_MAX_POINTS (5)
|
||||
|
||||
/**
|
||||
* @brief Touch controller type
|
||||
*
|
||||
*/
|
||||
typedef struct esp_lcd_touch_s esp_lcd_touch_t;
|
||||
typedef esp_lcd_touch_t *esp_lcd_touch_handle_t;
|
||||
|
||||
/**
|
||||
* @brief Touch controller interrupt callback type
|
||||
*
|
||||
*/
|
||||
typedef void (*esp_lcd_touch_interrupt_callback_t)(esp_lcd_touch_handle_t tp);
|
||||
|
||||
/**
|
||||
* @brief Touch Configuration Type
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
uint16_t x_max; /*!< X coordinates max (for mirroring) */
|
||||
uint16_t y_max; /*!< Y coordinates max (for mirroring) */
|
||||
|
||||
gpio_num_t rst_gpio_num; /*!< GPIO number of reset pin */
|
||||
gpio_num_t int_gpio_num; /*!< GPIO number of interrupt pin */
|
||||
|
||||
struct {
|
||||
unsigned int reset: 1; /*!< Level of reset pin in reset */
|
||||
unsigned int interrupt: 1;/*!< Active Level of interrupt pin */
|
||||
} levels;
|
||||
|
||||
struct {
|
||||
unsigned int swap_xy: 1; /*!< Swap X and Y after read coordinates */
|
||||
unsigned int mirror_x: 1; /*!< Mirror X after read coordinates */
|
||||
unsigned int mirror_y: 1; /*!< Mirror Y after read coordinates */
|
||||
} flags;
|
||||
|
||||
/*!< User callback called after get coordinates from touch controller for apply user adjusting */
|
||||
void (*process_coordinates)(esp_lcd_touch_handle_t tp, uint16_t *x, uint16_t *y, uint16_t *strength, uint8_t *point_num, uint8_t max_point_num);
|
||||
/*!< User callback called after the touch interrupt occured */
|
||||
esp_lcd_touch_interrupt_callback_t interrupt_callback;
|
||||
} esp_lcd_touch_config_t;
|
||||
|
||||
typedef struct {
|
||||
uint8_t points; /*!< Count of touch points saved */
|
||||
|
||||
struct {
|
||||
uint16_t x; /*!< X coordinate */
|
||||
uint16_t y; /*!< Y coordinate */
|
||||
uint16_t strength; /*!< Strength */
|
||||
} coords[CONFIG_ESP_LCD_TOUCH_MAX_POINTS];
|
||||
|
||||
#if (CONFIG_ESP_LCD_TOUCH_MAX_BUTTONS > 0)
|
||||
uint8_t buttons; /*!< Count of buttons states saved */
|
||||
|
||||
struct {
|
||||
uint8_t status; /*!< Status of button */
|
||||
} button[CONFIG_ESP_LCD_TOUCH_MAX_BUTTONS];
|
||||
#endif
|
||||
|
||||
portMUX_TYPE lock; /*!< Lock for read/write */
|
||||
} esp_lcd_touch_data_t;
|
||||
|
||||
/**
|
||||
* @brief Declare of Touch Type
|
||||
*
|
||||
*/
|
||||
struct esp_lcd_touch_s {
|
||||
|
||||
/**
|
||||
* @brief Read data from touch controller (mandatory)
|
||||
*
|
||||
* @note This function is usually blocking.
|
||||
*
|
||||
* @param tp: Touch handler
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success, otherwise returns ESP_ERR_xxx
|
||||
*/
|
||||
esp_err_t (*read_data)(esp_lcd_touch_handle_t tp);
|
||||
|
||||
/**
|
||||
* @brief Get coordinates from touch controller (mandatory)
|
||||
*
|
||||
* @param tp: Touch handler
|
||||
* @param x: Array of X coordinates
|
||||
* @param y: Array of Y coordinates
|
||||
* @param strength: Array of strengths
|
||||
* @param point_num: Count of points touched (equals with count of items in x and y array)
|
||||
* @param max_point_num: Maximum count of touched points to return (equals with max size of x and y array)
|
||||
*
|
||||
* @return
|
||||
* - Returns true, when touched and coordinates readed. Otherwise returns false.
|
||||
*/
|
||||
bool (*get_xy)(esp_lcd_touch_handle_t tp, uint16_t *x, uint16_t *y, uint16_t *strength, uint8_t *point_num, uint8_t max_point_num);
|
||||
|
||||
|
||||
#if (CONFIG_ESP_LCD_TOUCH_MAX_BUTTONS > 0)
|
||||
/**
|
||||
* @brief Get button state (optional)
|
||||
*
|
||||
* @param tp: Touch handler
|
||||
* @param n: Button index
|
||||
* @param state: Button state
|
||||
*
|
||||
* @return
|
||||
* - Returns true, when touched and coordinates readed. Otherwise returns false.
|
||||
*/
|
||||
esp_err_t (*get_button_state)(esp_lcd_touch_handle_t tp, uint8_t n, uint8_t *state);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Swap X and Y after read coordinates (optional)
|
||||
* If set, then not used SW swapping.
|
||||
*
|
||||
* @param tp: Touch handler
|
||||
* @param swap: Set swap value
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success, otherwise returns ESP_ERR_xxx
|
||||
*/
|
||||
esp_err_t (*set_swap_xy)(esp_lcd_touch_handle_t tp, bool swap);
|
||||
|
||||
/**
|
||||
* @brief Are X and Y coordinates swapped (optional)
|
||||
*
|
||||
* @param tp: Touch handler
|
||||
* @param swap: Get swap value
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success, otherwise returns ESP_ERR_xxx
|
||||
*/
|
||||
esp_err_t (*get_swap_xy)(esp_lcd_touch_handle_t tp, bool *swap);
|
||||
|
||||
/**
|
||||
* @brief Mirror X after read coordinates
|
||||
* If set, then not used SW mirroring.
|
||||
*
|
||||
* @param tp: Touch handler
|
||||
* @param mirror: Set X mirror value
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success, otherwise returns ESP_ERR_xxx
|
||||
*/
|
||||
esp_err_t (*set_mirror_x)(esp_lcd_touch_handle_t tp, bool mirror);
|
||||
|
||||
/**
|
||||
* @brief Is mirrored X (optional)
|
||||
*
|
||||
* @param tp: Touch handler
|
||||
* @param mirror: Get X mirror value
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success, otherwise returns ESP_ERR_xxx
|
||||
*/
|
||||
esp_err_t (*get_mirror_x)(esp_lcd_touch_handle_t tp, bool *mirror);
|
||||
|
||||
/**
|
||||
* @brief Mirror Y after read coordinates
|
||||
* If set, then not used SW mirroring.
|
||||
*
|
||||
* @param tp: Touch handler
|
||||
* @param mirror: Set Y mirror value
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success, otherwise returns ESP_ERR_xxx
|
||||
*/
|
||||
esp_err_t (*set_mirror_y)(esp_lcd_touch_handle_t tp, bool mirror);
|
||||
|
||||
/**
|
||||
* @brief Is mirrored Y (optional)
|
||||
*
|
||||
* @param tp: Touch handler
|
||||
* @param mirror: Get Y mirror value
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success, otherwise returns ESP_ERR_xxx
|
||||
*/
|
||||
esp_err_t (*get_mirror_y)(esp_lcd_touch_handle_t tp, bool *mirror);
|
||||
|
||||
/**
|
||||
* @brief Delete Touch
|
||||
*
|
||||
* @param tp: Touch handler
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success, otherwise returns ESP_ERR_xxx
|
||||
*/
|
||||
esp_err_t (*del)(esp_lcd_touch_handle_t tp);
|
||||
|
||||
/**
|
||||
* @brief Configuration structure
|
||||
*/
|
||||
esp_lcd_touch_config_t config;
|
||||
|
||||
/**
|
||||
* @brief Communication interface
|
||||
*/
|
||||
esp_lcd_panel_io_handle_t io;
|
||||
|
||||
/**
|
||||
* @brief Data structure
|
||||
*/
|
||||
esp_lcd_touch_data_t data;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Read data from touch controller
|
||||
*
|
||||
* @note This function is usually blocking.
|
||||
*
|
||||
* @param tp: Touch handler
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_INVALID_ARG parameter error
|
||||
* - ESP_FAIL sending command error, slave hasn't ACK the transfer
|
||||
* - ESP_ERR_INVALID_STATE I2C driver not installed or not in master mode
|
||||
* - ESP_ERR_TIMEOUT operation timeout because the bus is busy
|
||||
*/
|
||||
esp_err_t esp_lcd_touch_read_data(esp_lcd_touch_handle_t tp);
|
||||
|
||||
/**
|
||||
* @brief Read coordinates from touch controller
|
||||
*
|
||||
* @param tp: Touch handler
|
||||
* @param x: Array of X coordinates
|
||||
* @param y: Array of Y coordinates
|
||||
* @param strength: Array of the strengths (can be NULL)
|
||||
* @param point_num: Count of points touched (equals with count of items in x and y array)
|
||||
* @param max_point_num: Maximum count of touched points to return (equals with max size of x and y array)
|
||||
*
|
||||
* @return
|
||||
* - Returns true, when touched and coordinates readed. Otherwise returns false.
|
||||
*/
|
||||
bool esp_lcd_touch_get_coordinates(esp_lcd_touch_handle_t tp, uint16_t *x, uint16_t *y, uint16_t *strength, uint8_t *point_num, uint8_t max_point_num);
|
||||
|
||||
|
||||
#if (CONFIG_ESP_LCD_TOUCH_MAX_BUTTONS > 0)
|
||||
/**
|
||||
* @brief Get button state
|
||||
*
|
||||
* @param tp: Touch handler
|
||||
* @param n: Button index
|
||||
* @param state: Button state
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_NOT_SUPPORTED if this function is not supported by controller
|
||||
* - ESP_ERR_INVALID_ARG if bad button index
|
||||
*/
|
||||
esp_err_t esp_lcd_touch_get_button_state(esp_lcd_touch_handle_t tp, uint8_t n, uint8_t *state);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Swap X and Y after read coordinates
|
||||
*
|
||||
* @param tp: Touch handler
|
||||
* @param swap: Set swap value
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
*/
|
||||
esp_err_t esp_lcd_touch_set_swap_xy(esp_lcd_touch_handle_t tp, bool swap);
|
||||
|
||||
/**
|
||||
* @brief Are X and Y coordinates swapped
|
||||
*
|
||||
* @param tp: Touch handler
|
||||
* @param swap: Get swap value
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
*/
|
||||
esp_err_t esp_lcd_touch_get_swap_xy(esp_lcd_touch_handle_t tp, bool *swap);
|
||||
|
||||
/**
|
||||
* @brief Mirror X after read coordinates
|
||||
*
|
||||
* @param tp: Touch handler
|
||||
* @param mirror: Set X mirror value
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
*/
|
||||
esp_err_t esp_lcd_touch_set_mirror_x(esp_lcd_touch_handle_t tp, bool mirror);
|
||||
|
||||
/**
|
||||
* @brief Is mirrored X
|
||||
*
|
||||
* @param tp: Touch handler
|
||||
* @param mirror: Get X mirror value
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
*/
|
||||
esp_err_t esp_lcd_touch_get_mirror_x(esp_lcd_touch_handle_t tp, bool *mirror);
|
||||
|
||||
/**
|
||||
* @brief Mirror Y after read coordinates
|
||||
*
|
||||
* @param tp: Touch handler
|
||||
* @param mirror: Set Y mirror value
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
*/
|
||||
esp_err_t esp_lcd_touch_set_mirror_y(esp_lcd_touch_handle_t tp, bool mirror);
|
||||
|
||||
/**
|
||||
* @brief Is mirrored Y
|
||||
*
|
||||
* @param tp: Touch handler
|
||||
* @param mirror: Get Y mirror value
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
*/
|
||||
esp_err_t esp_lcd_touch_get_mirror_y(esp_lcd_touch_handle_t tp, bool *mirror);
|
||||
|
||||
/**
|
||||
* @brief Delete touch (free all allocated memory and restart HW)
|
||||
*
|
||||
* @param tp: Touch handler
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
*/
|
||||
esp_err_t esp_lcd_touch_del(esp_lcd_touch_handle_t tp);
|
||||
|
||||
/**
|
||||
* @brief Register user callback called after the touch interrupt occured
|
||||
*
|
||||
* @param tp: Touch handler
|
||||
* @param callback: Interrupt callback
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
*/
|
||||
esp_err_t esp_lcd_touch_register_interrupt_callback(esp_lcd_touch_handle_t tp, esp_lcd_touch_interrupt_callback_t callback);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
+270
@@ -0,0 +1,270 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "esp_system.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_check.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "driver/i2c.h"
|
||||
#include "esp_lcd_panel_io.h"
|
||||
#include "esp_lcd_touch.h"
|
||||
|
||||
static const char *TAG = "GT911";
|
||||
|
||||
/* GT911 registers */
|
||||
#define ESP_LCD_TOUCH_GT911_READ_XY_REG (0x814E)
|
||||
#define ESP_LCD_TOUCH_GT911_CONFIG_REG (0x8047)
|
||||
#define ESP_LCD_TOUCH_GT911_PRODUCT_ID_REG (0x8140)
|
||||
|
||||
/*******************************************************************************
|
||||
* Function definitions
|
||||
*******************************************************************************/
|
||||
static esp_err_t esp_lcd_touch_gt911_read_data(esp_lcd_touch_handle_t tp);
|
||||
static bool esp_lcd_touch_gt911_get_xy(esp_lcd_touch_handle_t tp, uint16_t *x, uint16_t *y, uint16_t *strength, uint8_t *point_num, uint8_t max_point_num);
|
||||
static esp_err_t esp_lcd_touch_gt911_del(esp_lcd_touch_handle_t tp);
|
||||
|
||||
/* I2C read/write */
|
||||
static esp_err_t touch_gt911_i2c_read(esp_lcd_touch_handle_t tp, uint16_t reg, uint8_t *data, uint8_t len);
|
||||
static esp_err_t touch_gt911_i2c_write(esp_lcd_touch_handle_t tp, uint16_t reg, uint8_t data);
|
||||
|
||||
/* GT911 reset */
|
||||
static esp_err_t touch_gt911_reset(esp_lcd_touch_handle_t tp);
|
||||
/* Read status and config register */
|
||||
static esp_err_t touch_gt911_read_cfg(esp_lcd_touch_handle_t tp);
|
||||
|
||||
/*******************************************************************************
|
||||
* Public API functions
|
||||
*******************************************************************************/
|
||||
|
||||
esp_err_t esp_lcd_touch_new_i2c_gt911(const esp_lcd_panel_io_handle_t io, const esp_lcd_touch_config_t *config, esp_lcd_touch_handle_t *out_touch)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
|
||||
assert(io != NULL);
|
||||
assert(config != NULL);
|
||||
assert(out_touch != NULL);
|
||||
|
||||
/* Prepare main structure */
|
||||
esp_lcd_touch_handle_t esp_lcd_touch_gt911 = heap_caps_calloc(1, sizeof(esp_lcd_touch_t), MALLOC_CAP_DEFAULT);
|
||||
ESP_GOTO_ON_FALSE(esp_lcd_touch_gt911, ESP_ERR_NO_MEM, err, TAG, "no mem for GT911 controller");
|
||||
|
||||
/* Communication interface */
|
||||
esp_lcd_touch_gt911->io = io;
|
||||
|
||||
/* Only supported callbacks are set */
|
||||
esp_lcd_touch_gt911->read_data = esp_lcd_touch_gt911_read_data;
|
||||
esp_lcd_touch_gt911->get_xy = esp_lcd_touch_gt911_get_xy;
|
||||
esp_lcd_touch_gt911->del = esp_lcd_touch_gt911_del;
|
||||
|
||||
/* Mutex */
|
||||
esp_lcd_touch_gt911->data.lock.owner = portMUX_FREE_VAL;
|
||||
|
||||
/* Save config */
|
||||
memcpy(&esp_lcd_touch_gt911->config, config, sizeof(esp_lcd_touch_config_t));
|
||||
|
||||
/* Prepare pin for touch interrupt */
|
||||
if (esp_lcd_touch_gt911->config.int_gpio_num != GPIO_NUM_NC) {
|
||||
const gpio_config_t int_gpio_config = {
|
||||
.mode = GPIO_MODE_INPUT,
|
||||
.intr_type = GPIO_INTR_NEGEDGE,
|
||||
.pin_bit_mask = BIT64(esp_lcd_touch_gt911->config.int_gpio_num)
|
||||
};
|
||||
ret = gpio_config(&int_gpio_config);
|
||||
ESP_GOTO_ON_ERROR(ret, err, TAG, "GPIO config failed");
|
||||
|
||||
/* Register interrupt callback */
|
||||
if (esp_lcd_touch_gt911->config.interrupt_callback) {
|
||||
esp_lcd_touch_register_interrupt_callback(esp_lcd_touch_gt911, esp_lcd_touch_gt911->config.interrupt_callback);
|
||||
}
|
||||
}
|
||||
|
||||
/* Prepare pin for touch controller reset */
|
||||
if (esp_lcd_touch_gt911->config.rst_gpio_num != GPIO_NUM_NC) {
|
||||
const gpio_config_t rst_gpio_config = {
|
||||
.mode = GPIO_MODE_OUTPUT,
|
||||
.pin_bit_mask = BIT64(esp_lcd_touch_gt911->config.rst_gpio_num)
|
||||
};
|
||||
ret = gpio_config(&rst_gpio_config);
|
||||
ESP_GOTO_ON_ERROR(ret, err, TAG, "GPIO config failed");
|
||||
}
|
||||
|
||||
/* Reset controller */
|
||||
ret = touch_gt911_reset(esp_lcd_touch_gt911);
|
||||
ESP_GOTO_ON_ERROR(ret, err, TAG, "GT911 reset failed");
|
||||
|
||||
/* Read status and config info */
|
||||
ret = touch_gt911_read_cfg(esp_lcd_touch_gt911);
|
||||
ESP_GOTO_ON_ERROR(ret, err, TAG, "GT911 init failed");
|
||||
|
||||
err:
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Error (0x%x)! Touch controller GT911 initialization failed!", ret);
|
||||
if (esp_lcd_touch_gt911) {
|
||||
esp_lcd_touch_gt911_del(esp_lcd_touch_gt911);
|
||||
}
|
||||
}
|
||||
|
||||
*out_touch = esp_lcd_touch_gt911;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t esp_lcd_touch_gt911_read_data(esp_lcd_touch_handle_t tp)
|
||||
{
|
||||
esp_err_t err;
|
||||
uint8_t buf[41];
|
||||
uint8_t touch_cnt = 0;
|
||||
uint8_t clear = 0;
|
||||
size_t i = 0;
|
||||
|
||||
assert(tp != NULL);
|
||||
|
||||
err = touch_gt911_i2c_read(tp, ESP_LCD_TOUCH_GT911_READ_XY_REG, buf, 1);
|
||||
ESP_RETURN_ON_ERROR(err, TAG, "I2C read error!");
|
||||
|
||||
/* Any touch data? */
|
||||
if ((buf[0] & 0x80) == 0x00) {
|
||||
touch_gt911_i2c_write(tp, ESP_LCD_TOUCH_GT911_READ_XY_REG, clear);
|
||||
} else {
|
||||
/* Count of touched points */
|
||||
touch_cnt = buf[0] & 0x0f;
|
||||
if (touch_cnt > 5 || touch_cnt == 0) {
|
||||
touch_gt911_i2c_write(tp, ESP_LCD_TOUCH_GT911_READ_XY_REG, clear);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/* Read all points */
|
||||
err = touch_gt911_i2c_read(tp, ESP_LCD_TOUCH_GT911_READ_XY_REG + 1, &buf[1], touch_cnt * 8);
|
||||
ESP_RETURN_ON_ERROR(err, TAG, "I2C read error!");
|
||||
|
||||
/* Clear all */
|
||||
err = touch_gt911_i2c_write(tp, ESP_LCD_TOUCH_GT911_READ_XY_REG, clear);
|
||||
ESP_RETURN_ON_ERROR(err, TAG, "I2C read error!");
|
||||
|
||||
portENTER_CRITICAL(&tp->data.lock);
|
||||
|
||||
/* Number of touched points */
|
||||
touch_cnt = (touch_cnt > CONFIG_ESP_LCD_TOUCH_MAX_POINTS ? CONFIG_ESP_LCD_TOUCH_MAX_POINTS : touch_cnt);
|
||||
tp->data.points = touch_cnt;
|
||||
|
||||
/* Fill all coordinates */
|
||||
for (i = 0; i < touch_cnt; i++) {
|
||||
tp->data.coords[i].x = ((uint16_t)buf[(i * 8) + 3] << 8) + buf[(i * 8) + 2];
|
||||
tp->data.coords[i].y = (((uint16_t)buf[(i * 8) + 5] << 8) + buf[(i * 8) + 4]);
|
||||
tp->data.coords[i].strength = (((uint16_t)buf[(i * 8) + 7] << 8) + buf[(i * 8) + 6]);
|
||||
}
|
||||
|
||||
portEXIT_CRITICAL(&tp->data.lock);
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static bool esp_lcd_touch_gt911_get_xy(esp_lcd_touch_handle_t tp, uint16_t *x, uint16_t *y, uint16_t *strength, uint8_t *point_num, uint8_t max_point_num)
|
||||
{
|
||||
assert(tp != NULL);
|
||||
assert(x != NULL);
|
||||
assert(y != NULL);
|
||||
assert(point_num != NULL);
|
||||
assert(max_point_num > 0);
|
||||
|
||||
portENTER_CRITICAL(&tp->data.lock);
|
||||
|
||||
/* Count of points */
|
||||
*point_num = (tp->data.points > max_point_num ? max_point_num : tp->data.points);
|
||||
|
||||
for (size_t i = 0; i < *point_num; i++) {
|
||||
x[i] = tp->data.coords[i].x;
|
||||
y[i] = tp->data.coords[i].y;
|
||||
if (strength) {
|
||||
strength[i] = tp->data.coords[i].strength;
|
||||
}
|
||||
}
|
||||
|
||||
/* Invalidate */
|
||||
tp->data.points = 0;
|
||||
|
||||
portEXIT_CRITICAL(&tp->data.lock);
|
||||
|
||||
return (*point_num > 0);
|
||||
}
|
||||
|
||||
static esp_err_t esp_lcd_touch_gt911_del(esp_lcd_touch_handle_t tp)
|
||||
{
|
||||
assert(tp != NULL);
|
||||
|
||||
/* Reset GPIO pin settings */
|
||||
if (tp->config.int_gpio_num != GPIO_NUM_NC) {
|
||||
gpio_reset_pin(tp->config.int_gpio_num);
|
||||
}
|
||||
|
||||
/* Reset GPIO pin settings */
|
||||
if (tp->config.rst_gpio_num != GPIO_NUM_NC) {
|
||||
gpio_reset_pin(tp->config.rst_gpio_num);
|
||||
}
|
||||
|
||||
free(tp);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* Private API function
|
||||
*******************************************************************************/
|
||||
|
||||
/* Reset controller */
|
||||
static esp_err_t touch_gt911_reset(esp_lcd_touch_handle_t tp)
|
||||
{
|
||||
assert(tp != NULL);
|
||||
|
||||
if (tp->config.rst_gpio_num != GPIO_NUM_NC) {
|
||||
ESP_RETURN_ON_ERROR(gpio_set_level(tp->config.rst_gpio_num, tp->config.levels.reset), TAG, "GPIO set level error!");
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
ESP_RETURN_ON_ERROR(gpio_set_level(tp->config.rst_gpio_num, !tp->config.levels.reset), TAG, "GPIO set level error!");
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t touch_gt911_read_cfg(esp_lcd_touch_handle_t tp)
|
||||
{
|
||||
uint8_t buf[4];
|
||||
|
||||
assert(tp != NULL);
|
||||
|
||||
ESP_RETURN_ON_ERROR(touch_gt911_i2c_read(tp, ESP_LCD_TOUCH_GT911_PRODUCT_ID_REG, (uint8_t *)&buf[0], 3), TAG, "GT911 read error!");
|
||||
ESP_RETURN_ON_ERROR(touch_gt911_i2c_read(tp, ESP_LCD_TOUCH_GT911_CONFIG_REG, (uint8_t *)&buf[3], 1), TAG, "GT911 read error!");
|
||||
|
||||
ESP_LOGI(TAG, "TouchPad_ID:0x%02x,0x%02x,0x%02x", buf[0], buf[1], buf[2]);
|
||||
ESP_LOGI(TAG, "TouchPad_Config_Version:%d", buf[3]);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t touch_gt911_i2c_read(esp_lcd_touch_handle_t tp, uint16_t reg, uint8_t *data, uint8_t len)
|
||||
{
|
||||
assert(tp != NULL);
|
||||
assert(data != NULL);
|
||||
|
||||
/* Read data */
|
||||
return esp_lcd_panel_io_rx_param(tp->io, reg, data, len);
|
||||
}
|
||||
|
||||
static esp_err_t touch_gt911_i2c_write(esp_lcd_touch_handle_t tp, uint16_t reg, uint8_t data)
|
||||
{
|
||||
assert(tp != NULL);
|
||||
|
||||
// *INDENT-OFF*
|
||||
/* Write data */
|
||||
return esp_lcd_panel_io_tx_param(tp->io, reg, (uint8_t[]){data}, 1);
|
||||
// *INDENT-ON*
|
||||
}
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* @brief ESP LCD touch: GT911
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "esp_lcd_touch.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Create a new GT911 touch driver
|
||||
*
|
||||
* @note The I2C communication should be initialized before use this function.
|
||||
*
|
||||
* @param io LCD/Touch panel IO handle
|
||||
* @param config: Touch configuration
|
||||
* @param out_touch: Touch instance handle
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_NO_MEM if there is no memory for allocating main structure
|
||||
*/
|
||||
esp_err_t esp_lcd_touch_new_i2c_gt911(const esp_lcd_panel_io_handle_t io, const esp_lcd_touch_config_t *config, esp_lcd_touch_handle_t *out_touch);
|
||||
|
||||
/**
|
||||
* @brief I2C address of the GT911 controller
|
||||
*
|
||||
*/
|
||||
#define ESP_LCD_TOUCH_IO_I2C_GT911_ADDRESS (0x5D)
|
||||
|
||||
/**
|
||||
* @brief Touch IO configuration structure
|
||||
*
|
||||
*/
|
||||
#define ESP_LCD_TOUCH_IO_I2C_GT911_CONFIG() \
|
||||
{ \
|
||||
.dev_addr = ESP_LCD_TOUCH_IO_I2C_GT911_ADDRESS, \
|
||||
.control_phase_bytes = 1, \
|
||||
.dc_bit_offset = 0, \
|
||||
.lcd_cmd_bits = 16, \
|
||||
.flags = \
|
||||
{ \
|
||||
.disable_control_phase = 1, \
|
||||
} \
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
+103
@@ -0,0 +1,103 @@
|
||||
#include "sdkconfig.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_log.h"
|
||||
#include "driver/i2c_master.h"
|
||||
#include "esp_lcd_touch_gt911.h"
|
||||
#include "gt911_touch.h"
|
||||
|
||||
#define CONFIG_LCD_HRES 480
|
||||
#define CONFIG_LCD_VRES 800
|
||||
|
||||
static const char *TAG = "example";
|
||||
|
||||
esp_lcd_touch_handle_t tp;
|
||||
esp_lcd_panel_io_handle_t tp_io_handle;
|
||||
|
||||
uint16_t touch_strength[1];
|
||||
uint8_t touch_cnt = 0;
|
||||
|
||||
gt911_touch::gt911_touch(int8_t sda_pin, int8_t scl_pin, int8_t rst_pin, int8_t int_pin)
|
||||
{
|
||||
_sda = sda_pin;
|
||||
_scl = scl_pin;
|
||||
_rst = rst_pin;
|
||||
_int = int_pin;
|
||||
}
|
||||
|
||||
void gt911_touch::begin()
|
||||
{
|
||||
// i2c_config_t i2c_conf = {
|
||||
// .mode = I2C_MODE_MASTER,
|
||||
// .sda_io_num = (gpio_num_t)_sda,
|
||||
// .scl_io_num = (gpio_num_t)_scl,
|
||||
// .sda_pullup_en = GPIO_PULLUP_ENABLE,
|
||||
// .scl_pullup_en = GPIO_PULLUP_ENABLE,
|
||||
// };
|
||||
// i2c_conf.master.clk_speed = 400000; // 400kHz
|
||||
|
||||
|
||||
// ESP_ERROR_CHECK(i2c_param_config(I2C_NUM_0, &i2c_conf));
|
||||
// ESP_ERROR_CHECK(i2c_driver_install(I2C_NUM_0, i2c_conf.mode, 0, 0, 0));
|
||||
i2c_master_bus_handle_t i2c_handle = NULL;
|
||||
i2c_master_get_bus_handle(1,&i2c_handle);
|
||||
|
||||
esp_lcd_panel_io_i2c_config_t tp_io_config = ESP_LCD_TOUCH_IO_I2C_GT911_CONFIG();
|
||||
tp_io_config.scl_speed_hz = 100000;
|
||||
ESP_LOGI(TAG, "Initialize touch IO (I2C)");
|
||||
esp_lcd_new_panel_io_i2c(i2c_handle, &tp_io_config, &tp_io_handle);
|
||||
|
||||
esp_lcd_touch_config_t tp_cfg = {
|
||||
.x_max = CONFIG_LCD_HRES,
|
||||
.y_max = CONFIG_LCD_VRES,
|
||||
.rst_gpio_num = (gpio_num_t)_rst,
|
||||
.int_gpio_num = (gpio_num_t)_int,
|
||||
.levels = {
|
||||
.reset = 0,
|
||||
.interrupt = 0,
|
||||
},
|
||||
.flags = {
|
||||
.swap_xy = 0,
|
||||
.mirror_x = 0,
|
||||
.mirror_y = 0,
|
||||
},
|
||||
};
|
||||
|
||||
ESP_LOGI(TAG, "Initialize touch controller gt911");
|
||||
ESP_ERROR_CHECK(esp_lcd_touch_new_i2c_gt911(tp_io_handle, &tp_cfg, &tp));
|
||||
}
|
||||
|
||||
bool gt911_touch::getTouch(uint16_t *x, uint16_t *y)
|
||||
{
|
||||
esp_lcd_touch_read_data(tp);
|
||||
bool touchpad_pressed = esp_lcd_touch_get_coordinates(tp, x, y, touch_strength, &touch_cnt, 1);
|
||||
|
||||
return touchpad_pressed;
|
||||
}
|
||||
|
||||
void gt911_touch::set_rotation(uint8_t r){
|
||||
switch(r){
|
||||
case 0:
|
||||
esp_lcd_touch_set_swap_xy(tp, false);
|
||||
esp_lcd_touch_set_mirror_x(tp, false);
|
||||
esp_lcd_touch_set_mirror_y(tp, false);
|
||||
break;
|
||||
case 1:
|
||||
esp_lcd_touch_set_swap_xy(tp, false);
|
||||
esp_lcd_touch_set_mirror_x(tp, true);
|
||||
esp_lcd_touch_set_mirror_y(tp, true);
|
||||
break;
|
||||
case 2:
|
||||
esp_lcd_touch_set_swap_xy(tp, false);
|
||||
esp_lcd_touch_set_mirror_x(tp, false);
|
||||
esp_lcd_touch_set_mirror_y(tp, false);
|
||||
break;
|
||||
case 3:
|
||||
esp_lcd_touch_set_swap_xy(tp, false);
|
||||
esp_lcd_touch_set_mirror_x(tp, true);
|
||||
esp_lcd_touch_set_mirror_y(tp, true);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
#ifndef _GT911_TOUCH_H
|
||||
#define _GT911_TOUCH_H
|
||||
#include <stdio.h>
|
||||
|
||||
class gt911_touch
|
||||
{
|
||||
public:
|
||||
gt911_touch(int8_t sda_pin, int8_t scl_pin, int8_t rst_pin = -1, int8_t int_pin = -1);
|
||||
|
||||
void begin();
|
||||
bool getTouch(uint16_t *x, uint16_t *y);
|
||||
void set_rotation(uint8_t r);
|
||||
|
||||
private:
|
||||
int8_t _sda, _scl, _rst, _int;
|
||||
};
|
||||
|
||||
#endif
|
||||
+156416
File diff suppressed because it is too large
Load Diff
Binary file not shown.
|
After Width: | Height: | Size: 52 KiB |
@@ -0,0 +1,4 @@
|
||||
|
||||
# 请使用arduino_esp32_v3.2.1版本
|
||||
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
#include "esp_log.h"
|
||||
#include "esp_check.h"
|
||||
#include "esp_adc/adc_oneshot.h"
|
||||
#include "esp_adc/adc_cali.h"
|
||||
#include "esp_adc/adc_cali_scheme.h"
|
||||
|
||||
#define EXAMPLE_ADC2_CHAN0 ADC_CHANNEL_4
|
||||
#define EXAMPLE_ADC_ATTEN ADC_ATTEN_DB_12
|
||||
|
||||
#define V_C_MAX (2450) //电池满电时检测的值
|
||||
#define V_C_MIN (2250) //电池没电时检测的值
|
||||
|
||||
adc_oneshot_unit_handle_t adc2_handle;
|
||||
adc_cali_handle_t adc2_cali_handle = NULL;
|
||||
bool do_calibration2;
|
||||
|
||||
static int adc_raw;
|
||||
static int adc_raw_;
|
||||
static int voltage_;
|
||||
static int voltage;
|
||||
static int voltage_per;
|
||||
static int voltage_per_;
|
||||
static bool example_adc_calibration_init(adc_unit_t unit, adc_channel_t channel, adc_atten_t atten, adc_cali_handle_t *out_handle);
|
||||
static void example_adc_calibration_deinit(adc_cali_handle_t handle);
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200); // 初始化串口通信
|
||||
adc_oneshot_chan_cfg_t config = {
|
||||
.atten = EXAMPLE_ADC_ATTEN,
|
||||
.bitwidth = ADC_BITWIDTH_DEFAULT,
|
||||
};
|
||||
//-------------ADC2 Init---------------//
|
||||
adc_oneshot_unit_init_cfg_t init_config2 = {
|
||||
.unit_id = ADC_UNIT_2,
|
||||
.ulp_mode = ADC_ULP_MODE_DISABLE,
|
||||
};
|
||||
ESP_ERROR_CHECK(adc_oneshot_new_unit(&init_config2, &adc2_handle));
|
||||
|
||||
//-------------ADC2 Calibration Init---------------//
|
||||
do_calibration2 = example_adc_calibration_init(ADC_UNIT_2, EXAMPLE_ADC2_CHAN0, EXAMPLE_ADC_ATTEN, &adc2_cali_handle);
|
||||
|
||||
//-------------ADC2 Config---------------//
|
||||
ESP_ERROR_CHECK(adc_oneshot_config_channel(adc2_handle, EXAMPLE_ADC2_CHAN0, &config));
|
||||
|
||||
}
|
||||
|
||||
void loop() {
|
||||
for(int i=0;i<500;i++)
|
||||
{
|
||||
ESP_ERROR_CHECK(adc_oneshot_read(adc2_handle, EXAMPLE_ADC2_CHAN0,&adc_raw));
|
||||
adc_raw_ += adc_raw;
|
||||
}
|
||||
adc_raw_ = adc_raw_ / 500;
|
||||
Serial.printf("ADC%d Channel[%d] Raw Data: %d \r\n", ADC_UNIT_2 + 1, EXAMPLE_ADC2_CHAN0, adc_raw_);
|
||||
|
||||
if (do_calibration2) {
|
||||
ESP_ERROR_CHECK(adc_cali_raw_to_voltage(adc2_cali_handle, adc_raw_, &voltage));
|
||||
Serial.printf("ADC%d Channel[%d] Cali Voltage: %d mV \r\n", ADC_UNIT_2 + 1, EXAMPLE_ADC2_CHAN0, voltage);
|
||||
}
|
||||
|
||||
voltage_ = voltage - V_C_MIN;
|
||||
if(voltage_ < 0)
|
||||
voltage_ = 0;
|
||||
voltage_per_ = voltage_per;
|
||||
voltage_per = voltage_ * 10000 / (V_C_MAX - V_C_MIN) / 100 ;
|
||||
|
||||
voltage_per = (voltage_per_ + voltage_per) / 2;
|
||||
if(voltage_per > 100)
|
||||
voltage_per = 100;
|
||||
|
||||
Serial.printf("Battery charge: %d %% \r\n",voltage_per);
|
||||
|
||||
delay(1000); // 延迟1秒
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------------
|
||||
ADC Calibration
|
||||
---------------------------------------------------------------*/
|
||||
static bool example_adc_calibration_init(adc_unit_t unit, adc_channel_t channel, adc_atten_t atten, adc_cali_handle_t *out_handle)
|
||||
{
|
||||
adc_cali_handle_t handle = NULL;
|
||||
esp_err_t ret = ESP_FAIL;
|
||||
bool calibrated = false;
|
||||
|
||||
#if ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED
|
||||
if (!calibrated) {
|
||||
ESP_LOGI(TAG, "calibration scheme version is %s", "Curve Fitting");
|
||||
adc_cali_curve_fitting_config_t cali_config = {
|
||||
.unit_id = unit,
|
||||
.chan = channel,
|
||||
.atten = atten,
|
||||
.bitwidth = ADC_BITWIDTH_DEFAULT,
|
||||
};
|
||||
ret = adc_cali_create_scheme_curve_fitting(&cali_config, &handle);
|
||||
if (ret == ESP_OK) {
|
||||
calibrated = true;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ADC_CALI_SCHEME_LINE_FITTING_SUPPORTED
|
||||
if (!calibrated) {
|
||||
ESP_LOGI(TAG, "calibration scheme version is %s", "Line Fitting");
|
||||
adc_cali_line_fitting_config_t cali_config = {
|
||||
.unit_id = unit,
|
||||
.atten = atten,
|
||||
.bitwidth = ADC_BITWIDTH_DEFAULT,
|
||||
};
|
||||
ret = adc_cali_create_scheme_line_fitting(&cali_config, &handle);
|
||||
if (ret == ESP_OK) {
|
||||
calibrated = true;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
*out_handle = handle;
|
||||
if (ret == ESP_OK) {
|
||||
ESP_LOGI(TAG, "Calibration Success");
|
||||
} else if (ret == ESP_ERR_NOT_SUPPORTED || !calibrated) {
|
||||
ESP_LOGW(TAG, "eFuse not burnt, skip software calibration");
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Invalid arg or no memory");
|
||||
}
|
||||
|
||||
return calibrated;
|
||||
}
|
||||
|
||||
static void example_adc_calibration_deinit(adc_cali_handle_t handle)
|
||||
{
|
||||
#if ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED
|
||||
ESP_LOGI(TAG, "deregister %s calibration scheme", "Curve Fitting");
|
||||
ESP_ERROR_CHECK(adc_cali_delete_scheme_curve_fitting(handle));
|
||||
|
||||
#elif ADC_CALI_SCHEME_LINE_FITTING_SUPPORTED
|
||||
ESP_LOGI(TAG, "deregister %s calibration scheme", "Line Fitting");
|
||||
ESP_ERROR_CHECK(adc_cali_delete_scheme_line_fitting(handle));
|
||||
#endif
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 52 KiB |
@@ -0,0 +1,21 @@
|
||||
# 请使用arduino_esp32_v3.2版本
|
||||
#此样例使用的屏幕是微雪的10.1寸屏幕,具体型号位:10.1-DSI-TOUCH-A
|
||||
|
||||
# lvgl V8.4.0
|
||||
|
||||
# lv_conf.h文件必要设置(lvgl_v8)
|
||||
# 要将lvgl文件夹中的demos文件夹移动到同目录下的src文件夹中
|
||||
|
||||
```c
|
||||
#define LV_COLOR_DEPTH 16
|
||||
|
||||
#define LV_COLOR_16_SWAP 0
|
||||
|
||||
#define LV_MEM_CUSTOM 1
|
||||
|
||||
#define LV_TICK_CUSTOM 1
|
||||
|
||||
#define LV_MEMCPY_MEMSET_STD 1
|
||||
|
||||
#define LV_ATTRIBUTE_FAST_MEM IRAM_ATTR
|
||||
```
|
||||
+160
@@ -0,0 +1,160 @@
|
||||
#pragma GCC push_options
|
||||
#pragma GCC optimize("O3")
|
||||
|
||||
#include <Arduino.h>
|
||||
#include "lvgl.h"
|
||||
#include "driver/i2c_master.h"
|
||||
#include "demos/lv_demos.h"
|
||||
#include "pins_config.h"
|
||||
#include "src/lcd/st7701_lcd.h"
|
||||
#include "src/touch/gt911_touch.h"
|
||||
|
||||
bsp_lcd_handles_t lcd_panels;
|
||||
|
||||
st7701_lcd lcd = st7701_lcd(LCD_RST);
|
||||
gt911_touch touch = gt911_touch(TP_I2C_SDA, TP_I2C_SCL, TP_RST, TP_INT);
|
||||
|
||||
static lv_disp_draw_buf_t draw_buf;
|
||||
static lv_color_t *buf;
|
||||
static lv_color_t *buf1;
|
||||
|
||||
|
||||
static bool lvgl_port_flush_dpi_panel_ready_callback(esp_lcd_panel_handle_t panel_io, esp_lcd_dpi_panel_event_data_t *edata, void *user_ctx)
|
||||
{
|
||||
lv_disp_drv_t *disp_drv = (lv_disp_drv_t *)user_ctx;
|
||||
assert(disp_drv != NULL);
|
||||
// lv_disp_flush_ready(disp_drv);
|
||||
lv_disp_flush_ready(disp_drv);
|
||||
|
||||
// if (disp_ctx->trans_size && disp_ctx->trans_sem) {
|
||||
// xSemaphoreGiveFromISR(disp_ctx->trans_sem, &taskAwake);
|
||||
// }
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// 显示刷新
|
||||
void my_disp_flush(lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *color_p)
|
||||
{
|
||||
const int offsetx1 = area->x1;
|
||||
const int offsetx2 = area->x2;
|
||||
const int offsety1 = area->y1;
|
||||
const int offsety2 = area->y2;
|
||||
lcd.lcd_draw_bitmap(offsetx1, offsety1, offsetx2 + 1, offsety2 + 1, &color_p->full);
|
||||
// lv_disp_flush_ready(disp); // 告诉lvgl刷新完成
|
||||
}
|
||||
|
||||
void my_touchpad_read(lv_indev_drv_t *indev_driver, lv_indev_data_t *data)
|
||||
{
|
||||
bool touched;
|
||||
uint16_t touchX, touchY;
|
||||
|
||||
touched = touch.getTouch(&touchX, &touchY);
|
||||
// touchX = 800 - touchX;
|
||||
|
||||
if (!touched)
|
||||
{
|
||||
data->state = LV_INDEV_STATE_REL;
|
||||
}
|
||||
else
|
||||
{
|
||||
data->state = LV_INDEV_STATE_PR;
|
||||
|
||||
// 设置坐标
|
||||
data->point.x = touchX;
|
||||
data->point.y = touchY;
|
||||
Serial.printf("x=%d,y=%d \r\n",touchX,touchY);
|
||||
}
|
||||
}
|
||||
|
||||
static void lvgl_port_update_callback(lv_disp_drv_t *drv)
|
||||
{
|
||||
switch (drv->rotated) {
|
||||
case LV_DISP_ROT_NONE:
|
||||
touch.set_rotation(0);
|
||||
break;
|
||||
case LV_DISP_ROT_90:
|
||||
touch.set_rotation(1);
|
||||
break;
|
||||
case LV_DISP_ROT_180:
|
||||
touch.set_rotation(2);
|
||||
break;
|
||||
case LV_DISP_ROT_270:
|
||||
touch.set_rotation(3);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void setup()
|
||||
{
|
||||
Serial.begin(115200);
|
||||
Serial.println("ESP32P4 MIPI DSI LVGL");
|
||||
|
||||
i2c_master_bus_handle_t i2c_handle = NULL;
|
||||
|
||||
i2c_master_bus_config_t i2c_bus_conf = {
|
||||
.i2c_port = I2C_NUM_1,
|
||||
.sda_io_num = (gpio_num_t)TP_I2C_SDA,
|
||||
.scl_io_num = (gpio_num_t)TP_I2C_SCL,
|
||||
.clk_source = I2C_CLK_SRC_DEFAULT,
|
||||
.glitch_ignore_cnt = 7,
|
||||
.intr_priority = 0,
|
||||
.trans_queue_depth = 0,
|
||||
.flags = {
|
||||
.enable_internal_pullup = 1,
|
||||
},
|
||||
};
|
||||
i2c_new_master_bus(&i2c_bus_conf, &i2c_handle);
|
||||
|
||||
lcd.begin();
|
||||
touch.begin();
|
||||
|
||||
lcd.get_handle(&lcd_panels);
|
||||
|
||||
|
||||
lv_init();
|
||||
size_t buffer_size = sizeof(int16_t) * LCD_H_RES * LCD_V_RES;
|
||||
// buf = (int32_t *)heap_caps_malloc(buffer_size, MALLOC_CAP_SPIRAM);
|
||||
// buf1 = (int32_t *)heap_caps_malloc(buffer_size, MALLOC_CAP_SPIRAM);
|
||||
buf = (lv_color_t *)heap_caps_malloc(buffer_size, MALLOC_CAP_SPIRAM);
|
||||
buf1 = (lv_color_t *)heap_caps_malloc(buffer_size, MALLOC_CAP_SPIRAM);
|
||||
assert(buf);
|
||||
assert(buf1);
|
||||
|
||||
lv_disp_draw_buf_init(&draw_buf, buf, buf1, LCD_H_RES * LCD_V_RES);
|
||||
|
||||
static lv_disp_drv_t disp_drv;
|
||||
/*Initialize the display*/
|
||||
lv_disp_drv_init(&disp_drv);
|
||||
disp_drv.hor_res = LCD_H_RES;
|
||||
disp_drv.ver_res = LCD_V_RES;
|
||||
disp_drv.flush_cb = my_disp_flush;
|
||||
disp_drv.draw_buf = &draw_buf;
|
||||
disp_drv.full_refresh = false;
|
||||
lv_disp_drv_register(&disp_drv);
|
||||
|
||||
static lv_indev_drv_t indev_drv;
|
||||
lv_indev_drv_init(&indev_drv);
|
||||
indev_drv.type = LV_INDEV_TYPE_POINTER;
|
||||
indev_drv.read_cb = my_touchpad_read;
|
||||
lv_indev_drv_register(&indev_drv);
|
||||
|
||||
esp_lcd_dpi_panel_event_callbacks_t cbs = {0};
|
||||
cbs.on_color_trans_done = lvgl_port_flush_dpi_panel_ready_callback;
|
||||
/* Register done callback */
|
||||
esp_lcd_dpi_panel_register_event_callbacks(lcd_panels.panel, &cbs, &disp_drv);
|
||||
|
||||
// lv_disp_set_rotation(NULL, 0);
|
||||
Serial.println("start demo");
|
||||
|
||||
lv_demo_widgets(); /* 小部件示例 */
|
||||
// lv_demo_music(); /* 类似智能手机的现代音乐播放器演示 */
|
||||
// lv_demo_stress(); /* LVGL 压力测试 */
|
||||
// lv_demo_benchmark(); /* 用于测量 LVGL 性能或比较不同设置的演示 */
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
lv_timer_handler();
|
||||
delay(5);
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#define LCD_H_RES 480
|
||||
#define LCD_V_RES 800
|
||||
|
||||
#define LCD_RST -1
|
||||
#define LCD_LED -1
|
||||
|
||||
#define TP_I2C_SDA 7
|
||||
#define TP_I2C_SCL 8
|
||||
#define TP_RST -1
|
||||
#define TP_INT -1
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "soc/soc_caps.h"
|
||||
#include "esp_check.h"
|
||||
#include "esp_lcd_types.h"
|
||||
|
||||
#include "esp_lcd_st7701_interface.h"
|
||||
#include "esp_lcd_st7701.h"
|
||||
|
||||
static const char *TAG = "st7701";
|
||||
|
||||
esp_err_t esp_lcd_new_panel_st7701(const esp_lcd_panel_io_handle_t io, const esp_lcd_panel_dev_config_t *panel_dev_config,
|
||||
esp_lcd_panel_handle_t *ret_panel)
|
||||
{
|
||||
// ESP_LOGI(TAG, "version: %d.%d.%d", ESP_LCD_ST7701_VER_MAJOR, ESP_LCD_ST7701_VER_MINOR, ESP_LCD_ST7701_VER_PATCH);
|
||||
ESP_RETURN_ON_FALSE(panel_dev_config && ret_panel, ESP_ERR_INVALID_ARG, TAG, "Invalid arguments");
|
||||
st7701_vendor_config_t *vendor_config = (st7701_vendor_config_t *)panel_dev_config->vendor_config;
|
||||
ESP_RETURN_ON_FALSE(vendor_config, ESP_ERR_INVALID_ARG, TAG, "`vendor_config` is necessary");
|
||||
|
||||
esp_err_t ret = ESP_ERR_NOT_SUPPORTED;
|
||||
|
||||
#if SOC_LCD_RGB_SUPPORTED
|
||||
if (!vendor_config->flags.use_mipi_interface) {
|
||||
ret = esp_lcd_new_panel_st7701_rgb(io, panel_dev_config, ret_panel);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if SOC_MIPI_DSI_SUPPORTED
|
||||
if (vendor_config->flags.use_mipi_interface) {
|
||||
ret = esp_lcd_new_panel_st7701_mipi(io, panel_dev_config, ret_panel);
|
||||
}
|
||||
#endif
|
||||
|
||||
return ret;
|
||||
}
|
||||
+199
@@ -0,0 +1,199 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "hal/lcd_types.h"
|
||||
#include "esp_lcd_panel_vendor.h"
|
||||
|
||||
#if SOC_LCD_RGB_SUPPORTED
|
||||
#include "esp_lcd_panel_rgb.h"
|
||||
#endif
|
||||
|
||||
#if SOC_MIPI_DSI_SUPPORTED
|
||||
#include "esp_lcd_mipi_dsi.h"
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief LCD panel initialization commands.
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
int cmd; /*<! The specific LCD command */
|
||||
const void *data; /*<! Buffer that holds the command specific data */
|
||||
size_t data_bytes; /*<! Size of `data` in memory, in bytes */
|
||||
unsigned int delay_ms; /*<! Delay in milliseconds after this command */
|
||||
} st7701_lcd_init_cmd_t;
|
||||
|
||||
/**
|
||||
* @brief LCD panel vendor configuration.
|
||||
*
|
||||
* @note This structure needs to be passed to the `vendor_config` field in `esp_lcd_panel_dev_config_t`.
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
const st7701_lcd_init_cmd_t *init_cmds; /*!< Pointer to initialization commands array. Set to NULL if using default commands.
|
||||
* The array should be declared as `static const` and positioned outside the function.
|
||||
* Please refer to `vendor_specific_init_default` in source file.
|
||||
*/
|
||||
uint16_t init_cmds_size; /*<! Number of commands in above array */
|
||||
union {
|
||||
#if SOC_LCD_RGB_SUPPORTED
|
||||
const esp_lcd_rgb_panel_config_t *rgb_config; /*!< RGB panel configuration */
|
||||
#endif
|
||||
#if SOC_MIPI_DSI_SUPPORTED
|
||||
struct {
|
||||
esp_lcd_dsi_bus_handle_t dsi_bus; /*!< MIPI-DSI bus configuration */
|
||||
const esp_lcd_dpi_panel_config_t *dpi_config; /*!< MIPI-DPI panel configuration */
|
||||
} mipi_config;
|
||||
#endif
|
||||
};
|
||||
struct {
|
||||
unsigned int use_mipi_interface: 1; /*<! Set to 1 if using MIPI interface, default is RGB interface */
|
||||
unsigned int mirror_by_cmd: 1; /*<! The `mirror()` function will be implemented by LCD command if set to 1.
|
||||
* Otherwise, the function will be implemented by software.
|
||||
* This flag is only valid for the RGB interface.
|
||||
*/
|
||||
|
||||
union {
|
||||
unsigned int auto_del_panel_io: 1;
|
||||
unsigned int enable_io_multiplex: 1;
|
||||
}; /*<! Delete the panel IO instance automatically if set to 1. All `*_by_cmd` flags will be invalid.
|
||||
* If the panel IO pins are sharing other pins of the RGB interface to save GPIOs,
|
||||
* Please set it to 1 to release the panel IO and its pins (except CS signal).
|
||||
* This flag is only valid for the RGB interface.
|
||||
*/
|
||||
} flags;
|
||||
} st7701_vendor_config_t;
|
||||
|
||||
/**
|
||||
* @brief Create LCD panel for model ST7701
|
||||
*
|
||||
* @note When `enable_io_multiplex` is set to 1, this function will first initialize the ST7701 with vendor specific initialization and then calls `esp_lcd_new_rgb_panel()` to create an RGB LCD panel. And the `esp_lcd_panel_init()` function will only initialize RGB.
|
||||
* @note When `enable_io_multiplex` is set to 0, this function will only call `esp_lcd_new_rgb_panel()` to create an RGB LCD panel. And the `esp_lcd_panel_init()` function will initialize both the ST7701 and RGB.
|
||||
* @note Vendor specific initialization can be different between manufacturers, should consult the LCD supplier for initialization sequence code.
|
||||
*
|
||||
* @param[in] io LCD panel IO handle
|
||||
* @param[in] panel_dev_config General panel device configuration (`vendor_config` and `rgb_config` are necessary)
|
||||
* @param[out] ret_panel Returned LCD panel handle
|
||||
* @return
|
||||
* - ESP_ERR_INVALID_ARG if parameter is invalid
|
||||
* - ESP_OK on success
|
||||
* - Otherwise on fail
|
||||
*/
|
||||
esp_err_t esp_lcd_new_panel_st7701(const esp_lcd_panel_io_handle_t io, const esp_lcd_panel_dev_config_t *panel_dev_config, esp_lcd_panel_handle_t *ret_panel);
|
||||
|
||||
/**
|
||||
* @brief 3-wire SPI panel IO configuration structure
|
||||
*
|
||||
* @param[in] line_cfg SPI line configuration
|
||||
* @param[in] scl_active_edge SCL signal active edge, 0: rising edge, 1: falling edge
|
||||
*
|
||||
*/
|
||||
#define ST7701_PANEL_IO_3WIRE_SPI_CONFIG(line_cfg, scl_active_edge) \
|
||||
{ \
|
||||
.line_config = line_cfg, \
|
||||
.expect_clk_speed = PANEL_IO_3WIRE_SPI_CLK_MAX, \
|
||||
.spi_mode = scl_active_edge ? 1 : 0, \
|
||||
.lcd_cmd_bytes = 1, \
|
||||
.lcd_param_bytes = 1, \
|
||||
.flags = { \
|
||||
.use_dc_bit = 1, \
|
||||
.dc_zero_on_data = 0, \
|
||||
.lsb_first = 0, \
|
||||
.cs_high_active = 0, \
|
||||
.del_keep_cs_inactive = 1, \
|
||||
}, \
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
/////////////////////////////// Default Configuration Macros for RGB Interface /////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
/**
|
||||
* @brief RGB timing structure
|
||||
*
|
||||
* @note refresh_rate = (pclk_hz * data_width) / (h_res + hsync_pulse_width + hsync_back_porch + hsync_front_porch)
|
||||
* / (v_res + vsync_pulse_width + vsync_back_porch + vsync_front_porch)
|
||||
* / bits_per_pixel
|
||||
*
|
||||
*/
|
||||
#define ST7701_480_480_PANEL_60HZ_RGB_TIMING() \
|
||||
{ \
|
||||
.pclk_hz = 16 * 1000 * 1000, \
|
||||
.h_res = 480, \
|
||||
.v_res = 480, \
|
||||
.hsync_pulse_width = 10, \
|
||||
.hsync_back_porch = 10, \
|
||||
.hsync_front_porch = 20, \
|
||||
.vsync_pulse_width = 10, \
|
||||
.vsync_back_porch = 10, \
|
||||
.vsync_front_porch = 10, \
|
||||
.flags.pclk_active_neg = false, \
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////// Default Configuration Macros for MIPI-DSI Interface //////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
/**
|
||||
* @brief MIPI-DSI bus configuration structure
|
||||
*/
|
||||
#define ST7701_PANEL_BUS_DSI_2CH_CONFIG() \
|
||||
{ \
|
||||
.bus_id = 0, \
|
||||
.num_data_lanes = 2, \
|
||||
.phy_clk_src = MIPI_DSI_PHY_CLK_SRC_DEFAULT, \
|
||||
.lane_bit_rate_mbps = 500, \
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief MIPI-DBI panel IO configuration structure
|
||||
*
|
||||
*/
|
||||
#define ST7701_PANEL_IO_DBI_CONFIG() \
|
||||
{ \
|
||||
.virtual_channel = 0, \
|
||||
.lcd_cmd_bits = 8, \
|
||||
.lcd_param_bits = 8, \
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief MIPI DPI configuration structure
|
||||
*
|
||||
* @note refresh_rate = (dpi_clock_freq_mhz * 1000000) / (h_res + hsync_pulse_width + hsync_back_porch + hsync_front_porch)
|
||||
* / (v_res + vsync_pulse_width + vsync_back_porch + vsync_front_porch)
|
||||
*
|
||||
* @param[in] px_format Pixel format of the panel
|
||||
*
|
||||
*/
|
||||
#define ST7701_480_360_PANEL_60HZ_DPI_CONFIG(px_format) \
|
||||
{ \
|
||||
.virtual_channel = 0, \
|
||||
.dpi_clk_src = MIPI_DSI_DPI_CLK_SRC_DEFAULT, \
|
||||
.dpi_clock_freq_mhz = 34, \
|
||||
.pixel_format = px_format, \
|
||||
.num_fbs = 1, \
|
||||
.video_timing = { \
|
||||
.h_size = 480, \
|
||||
.v_size = 800, \
|
||||
.hsync_pulse_width = 12, \
|
||||
.hsync_back_porch = 42, \
|
||||
.hsync_front_porch = 42, \
|
||||
.vsync_pulse_width = 2, \
|
||||
.vsync_back_porch = 8, \
|
||||
.vsync_front_porch = 166, \
|
||||
}, \
|
||||
.flags = {.use_dma2d = true,} \
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "esp_err.h"
|
||||
#include "esp_lcd_types.h"
|
||||
#include "esp_lcd_panel_vendor.h"
|
||||
#include "soc/soc_caps.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define ST7701_CMD_SDIR (0xC7)
|
||||
#define ST7701_CMD_SS_BIT (1 << 2)
|
||||
|
||||
#define ST7701_CMD_CND2BKxSEL (0xFF)
|
||||
#define ST7701_CMD_BKxSEL_BYTE0 (0x77)
|
||||
#define ST7701_CMD_BKxSEL_BYTE1 (0x01)
|
||||
#define ST7701_CMD_BKxSEL_BYTE2 (0x00)
|
||||
#define ST7701_CMD_BKxSEL_BYTE3 (0x00)
|
||||
#define ST7701_CMD_CN2_BIT (1 << 4)
|
||||
#define ST7701_CMD_BKxSEL_BK0 (0x00)
|
||||
|
||||
#if SOC_LCD_RGB_SUPPORTED
|
||||
/**
|
||||
* @brief Initialize ST7701 LCD panel with RGB interface
|
||||
*
|
||||
* @param[in] io LCD panel IO handle
|
||||
* @param[in] panel_dev_config LCD panel device configuration
|
||||
* @param[out] ret_panel LCD panel handle
|
||||
* @return
|
||||
* - ESP_OK: Success
|
||||
* - Otherwise: Fail
|
||||
*/
|
||||
esp_err_t esp_lcd_new_panel_st7701_rgb(const esp_lcd_panel_io_handle_t io, const esp_lcd_panel_dev_config_t *panel_dev_config, esp_lcd_panel_handle_t *ret_panel);
|
||||
#endif
|
||||
|
||||
#if SOC_MIPI_DSI_SUPPORTED
|
||||
/**
|
||||
* @brief Initialize ST7701 LCD panel with MIPI interface
|
||||
*
|
||||
* @param[in] io LCD panel IO handle
|
||||
* @param[in] panel_dev_config LCD panel device configuration
|
||||
* @param[out] ret_panel LCD panel handle
|
||||
* @return
|
||||
* - ESP_OK: Success
|
||||
* - Otherwise: Fail
|
||||
*/
|
||||
esp_err_t esp_lcd_new_panel_st7701_mipi(const esp_lcd_panel_io_handle_t io, const esp_lcd_panel_dev_config_t *panel_dev_config,
|
||||
esp_lcd_panel_handle_t *ret_panel);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
+478
@@ -0,0 +1,478 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include "soc/soc_caps.h"
|
||||
|
||||
#if SOC_MIPI_DSI_SUPPORTED
|
||||
#include <stdlib.h>
|
||||
#include <sys/cdefs.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "esp_lcd_panel_interface.h"
|
||||
#include "esp_lcd_panel_io.h"
|
||||
#include "esp_lcd_panel_vendor.h"
|
||||
#include "esp_lcd_panel_ops.h"
|
||||
#include "esp_lcd_panel_commands.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_check.h"
|
||||
#include "esp_lcd_st7701.h"
|
||||
#include "esp_lcd_st7701_interface.h"
|
||||
|
||||
static const char *TAG = "st7701_mipi";
|
||||
|
||||
static esp_err_t panel_st7701_del(esp_lcd_panel_t *panel);
|
||||
static esp_err_t panel_st7701_reset(esp_lcd_panel_t *panel);
|
||||
static esp_err_t panel_st7701_init(esp_lcd_panel_t *panel);
|
||||
static esp_err_t panel_st7701_invert_color(esp_lcd_panel_t *panel, bool invert_color_data);
|
||||
static esp_err_t panel_st7701_mirror(esp_lcd_panel_t *panel, bool mirror_x, bool mirror_y);
|
||||
static esp_err_t panel_st7701_disp_on_off(esp_lcd_panel_t *panel, bool off);
|
||||
static esp_err_t panel_st7701_sleep(esp_lcd_panel_t *panel, bool sleep);
|
||||
|
||||
typedef struct {
|
||||
esp_lcd_panel_io_handle_t io;
|
||||
int reset_gpio_num;
|
||||
uint8_t madctl_val; // save current value of LCD_CMD_MADCTL register
|
||||
uint8_t colmod_val; // save surrent value of LCD_CMD_COLMOD register
|
||||
const st7701_lcd_init_cmd_t *init_cmds;
|
||||
uint16_t init_cmds_size;
|
||||
struct {
|
||||
unsigned int reset_level: 1;
|
||||
} flags;
|
||||
// To save the original functions of MIPI DPI panel
|
||||
esp_err_t (*del)(esp_lcd_panel_t *panel);
|
||||
esp_err_t (*init)(esp_lcd_panel_t *panel);
|
||||
} st7701_panel_t;
|
||||
|
||||
esp_err_t esp_lcd_new_panel_st7701_mipi(const esp_lcd_panel_io_handle_t io, const esp_lcd_panel_dev_config_t *panel_dev_config, esp_lcd_panel_handle_t *ret_panel)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(io && panel_dev_config && ret_panel, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
st7701_vendor_config_t *vendor_config = (st7701_vendor_config_t *)panel_dev_config->vendor_config;
|
||||
ESP_RETURN_ON_FALSE(vendor_config && vendor_config->mipi_config.dpi_config && vendor_config->mipi_config.dsi_bus, ESP_ERR_INVALID_ARG, TAG,
|
||||
"invalid vendor config");
|
||||
|
||||
esp_err_t ret = ESP_OK;
|
||||
st7701_panel_t *st7701 = (st7701_panel_t *)calloc(1, sizeof(st7701_panel_t));
|
||||
ESP_RETURN_ON_FALSE(st7701, ESP_ERR_NO_MEM, TAG, "no mem for st7701 panel");
|
||||
|
||||
if (panel_dev_config->reset_gpio_num >= 0) {
|
||||
gpio_config_t io_conf = {
|
||||
.mode = GPIO_MODE_OUTPUT,
|
||||
.pin_bit_mask = 1ULL << panel_dev_config->reset_gpio_num,
|
||||
};
|
||||
ESP_GOTO_ON_ERROR(gpio_config(&io_conf), err, TAG, "configure GPIO for RST line failed");
|
||||
}
|
||||
|
||||
switch (panel_dev_config->color_space) {
|
||||
case LCD_RGB_ELEMENT_ORDER_RGB:
|
||||
st7701->madctl_val = 0;
|
||||
break;
|
||||
case LCD_RGB_ELEMENT_ORDER_BGR:
|
||||
st7701->madctl_val |= LCD_CMD_BGR_BIT;
|
||||
break;
|
||||
default:
|
||||
ESP_GOTO_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, err, TAG, "unsupported rgb element order");
|
||||
break;
|
||||
}
|
||||
|
||||
switch (panel_dev_config->bits_per_pixel) {
|
||||
case 16: // RGB565
|
||||
st7701->colmod_val = 0x55;
|
||||
break;
|
||||
case 18: // RGB666
|
||||
st7701->colmod_val = 0x66;
|
||||
break;
|
||||
case 24: // RGB888
|
||||
st7701->colmod_val = 0x77;
|
||||
break;
|
||||
default:
|
||||
ESP_GOTO_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, err, TAG, "unsupported pixel width");
|
||||
break;
|
||||
}
|
||||
|
||||
st7701->io = io;
|
||||
st7701->init_cmds = vendor_config->init_cmds;
|
||||
st7701->init_cmds_size = vendor_config->init_cmds_size;
|
||||
st7701->reset_gpio_num = panel_dev_config->reset_gpio_num;
|
||||
st7701->flags.reset_level = panel_dev_config->flags.reset_active_high;
|
||||
|
||||
// Create MIPI DPI panel
|
||||
esp_lcd_panel_handle_t panel_handle = NULL;
|
||||
ESP_GOTO_ON_ERROR(esp_lcd_new_panel_dpi(vendor_config->mipi_config.dsi_bus, vendor_config->mipi_config.dpi_config, &panel_handle), err, TAG,
|
||||
"create MIPI DPI panel failed");
|
||||
ESP_LOGD(TAG, "new MIPI DPI panel @%p", panel_handle);
|
||||
|
||||
// Save the original functions of MIPI DPI panel
|
||||
st7701->del = panel_handle->del;
|
||||
st7701->init = panel_handle->init;
|
||||
// Overwrite the functions of MIPI DPI panel
|
||||
panel_handle->del = panel_st7701_del;
|
||||
panel_handle->init = panel_st7701_init;
|
||||
panel_handle->reset = panel_st7701_reset;
|
||||
panel_handle->mirror = panel_st7701_mirror;
|
||||
panel_handle->invert_color = panel_st7701_invert_color;
|
||||
panel_handle->disp_on_off = panel_st7701_disp_on_off;
|
||||
panel_handle->disp_sleep = panel_st7701_sleep;
|
||||
panel_handle->user_data = st7701;
|
||||
*ret_panel = panel_handle;
|
||||
ESP_LOGD(TAG, "new st7701 panel @%p", st7701);
|
||||
|
||||
return ESP_OK;
|
||||
err:
|
||||
if (st7701) {
|
||||
if (panel_dev_config->reset_gpio_num >= 0) {
|
||||
gpio_reset_pin(panel_dev_config->reset_gpio_num);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t panel_st7701_del(esp_lcd_panel_t *panel)
|
||||
{
|
||||
st7701_panel_t *st7701 = (st7701_panel_t *)panel->user_data;
|
||||
|
||||
if (st7701->reset_gpio_num >= 0) {
|
||||
gpio_reset_pin(st7701->reset_gpio_num);
|
||||
}
|
||||
|
||||
// Delete MIPI DPI panel
|
||||
st7701->del(panel);
|
||||
ESP_LOGD(TAG, "del st7701 panel @%p", st7701);
|
||||
free(st7701);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t panel_st7701_reset(esp_lcd_panel_t *panel)
|
||||
{
|
||||
st7701_panel_t *st7701 = (st7701_panel_t *)panel->user_data;
|
||||
esp_lcd_panel_io_handle_t io = st7701->io;
|
||||
|
||||
// perform hardware reset
|
||||
if (st7701->reset_gpio_num >= 0) {
|
||||
gpio_set_level(st7701->reset_gpio_num, st7701->flags.reset_level);
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
gpio_set_level(st7701->reset_gpio_num, !st7701->flags.reset_level);
|
||||
vTaskDelay(pdMS_TO_TICKS(120));
|
||||
} else if (io) { // perform software reset
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_SWRESET, NULL, 0), TAG, "send command failed");
|
||||
vTaskDelay(pdMS_TO_TICKS(20)); // spec, wait at least 5ms before sending new command
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static const st7701_lcd_init_cmd_t vendor_specific_init_default[] = {
|
||||
// {0xFF, (uint8_t []){0x77, 0x01, 0x00, 0x00, 0x13}, 5, 0},
|
||||
// {0xEF, (uint8_t []){0x08}, 1, 0},
|
||||
// {0xFF, (uint8_t []){0x77, 0x01, 0x00, 0x00, 0x10}, 5, 0},
|
||||
// {0xC0, (uint8_t []){0x2c, 0x00}, 2, 0},
|
||||
// {0xC1, (uint8_t []){0x10, 0x0C}, 2, 0},
|
||||
// {0xC2, (uint8_t []){0x21, 0x0A}, 2, 0},
|
||||
// {0xCC, (uint8_t []){0x10}, 1, 0},
|
||||
// {0xB0, (uint8_t []){0x00, 0x0B, 0x12, 0x0D, 0x10, 0x06, 0x02, 0x08, 0x07, 0x1F, 0x04, 0x11, 0x0F, 0x29, 0x31, 0x1E}, 16, 0},
|
||||
// {0xB1, (uint8_t []){0x00, 0x0B, 0x13, 0x0D, 0x11, 0x06, 0x03, 0x08, 0x07, 0x20, 0x04, 0x12, 0x11, 0x29, 0x31, 0x1E}, 16, 0},
|
||||
// {0xFF, (uint8_t []){0x77, 0x01, 0x00, 0x00, 0x11}, 5, 0},
|
||||
// {0xB0, (uint8_t []){0x5D}, 1, 0},
|
||||
// {0xB1, (uint8_t []){0x72}, 1, 0},
|
||||
// {0xB2, (uint8_t []){0x84}, 1, 0},
|
||||
// {0xB3, (uint8_t []){0x80}, 1, 0},
|
||||
// {0xB5, (uint8_t []){0x4D}, 1, 0},
|
||||
// {0xB7, (uint8_t []){0x85}, 1, 0},
|
||||
// {0xB8, (uint8_t []){0x20}, 1, 0},
|
||||
// {0xC1, (uint8_t []){0x78}, 1, 0},
|
||||
// {0xC2, (uint8_t []){0x78}, 1, 0},
|
||||
// {0xD0, (uint8_t []){0x88}, 1, 0},
|
||||
// {0xE0, (uint8_t []){0x80, 0x00, 0x02}, 3, 0},
|
||||
// {0xE1, (uint8_t []){0x05, 0x00, 0x07, 0x00, 0x06, 0x00, 0x08, 0x00, 0x00, 0x33, 0x33}, 11, 0},
|
||||
// {0xE2, (uint8_t []){0x00, 0x00, 0x30, 0x30, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00}, 12, 0},
|
||||
// {0xE3, (uint8_t []){0x00, 0x00, 0x11, 0x11}, 4, 0},
|
||||
// {0xE4, (uint8_t []){0x44, 0x44}, 2, 0},
|
||||
// {0xE5, (uint8_t []){0x0C, 0x78, 0x00, 0xE0, 0x0E, 0x7A, 0x00, 0xE0, 0x08, 0x74, 0x00, 0xE0, 0x0A, 0x76, 0x00, 0xE0}, 16, 0},
|
||||
// {0xE6, (uint8_t []){0x00, 0x00, 0x11, 0x11}, 4, 0},
|
||||
// {0xE7, (uint8_t []){0x44, 0x44}, 2, 0},
|
||||
// {0xE8, (uint8_t []){0x0D, 0x79, 0x00, 0xE0, 0x0F, 0x7B, 0x00, 0xE0, 0x09, 0x75, 0x00, 0xE0, 0x0B, 0x77, 0x00, 0xE0}, 16, 0},
|
||||
// {0xE9, (uint8_t []){0x36, 0x00}, 2, 0},
|
||||
// {0xEB, (uint8_t []){0x00, 0x01, 0xE4, 0xE4, 0x44, 0x88, 0x40}, 7, 0},
|
||||
// {0xED, (uint8_t []){0xA1, 0xC2, 0xFB, 0x0F, 0x67, 0x45, 0xFF, 0xFF, 0xFF, 0xFF, 0x54, 0x76, 0xF0, 0xBF, 0x2C, 0x1A}, 16, 0},
|
||||
// {0xEF, (uint8_t []){0x10, 0x0D, 0x04, 0x08, 0x3F, 0x1F}, 6, 0},
|
||||
// {0xFF, (uint8_t []){0x77, 0x01, 0x00, 0x00, 0x13}, 5, 0},
|
||||
// {0xE8, (uint8_t []){0x00, 0x0E}, 2, 0},
|
||||
// {0xE8, (uint8_t []){0x00, 0x0C}, 2, 20},
|
||||
// {0xE8, (uint8_t []){0x00, 0x00}, 2, 0},
|
||||
// {0xFF, (uint8_t []){0x77, 0x01, 0x00, 0x00, 0x00}, 5, 0},
|
||||
// {0x11, (uint8_t []){0x00}, 0, 120},
|
||||
// {0x29, (uint8_t []){0x00}, 0, 0},
|
||||
|
||||
// {0xFF, (uint8_t []){0x77, 0x01, 0x00, 0x00, 0x12}, 5, 0}, /* This part of the parameters can be used for screen self-test */
|
||||
// {0xD1, (uint8_t []){0x81}, 1, 0},
|
||||
// {0xD2, (uint8_t []){0x08}, 1, 0},
|
||||
|
||||
/*
|
||||
|
||||
*/
|
||||
|
||||
{0xFF, (uint8_t []){0x77,0x01,0x00,0x00,0x13},5,0},
|
||||
{0xEF, (uint8_t []){0x08}, 1, 0},
|
||||
{0xFF, (uint8_t []){0x77,0x01,0x00,0x00,0x10},5,0},
|
||||
{0xC0, (uint8_t []){0x63, 0x00}, 2, 0},
|
||||
{0xC1, (uint8_t []){0x0D, 0x02}, 2, 0},
|
||||
{0xC2, (uint8_t []){0x10, 0x08}, 2, 0},
|
||||
{0xCC, (uint8_t []){0x10}, 1, 0},
|
||||
|
||||
{0xB0, (uint8_t []){0x80, 0x09, 0x53, 0x0C, 0xD0, 0x07, 0x0C, 0x09, 0x09, 0x28, 0x06, 0xD4, 0x13, 0x69, 0x2B, 0x71}, 16, 0},
|
||||
{0xB1, (uint8_t []){0x80, 0x94, 0x5A, 0x10, 0xD3, 0x06, 0x0A, 0x08, 0x08, 0x25, 0x03, 0xD3, 0x12, 0x66, 0x6A, 0x0D}, 16, 0},
|
||||
{0xFF, (uint8_t []){0x77, 0x01, 0x00, 0x00, 0x11}, 5, 0},
|
||||
|
||||
{0xB0, (uint8_t []){0x5D}, 1, 0},
|
||||
{0xB1, (uint8_t []){0x58}, 1, 0},
|
||||
{0xB2, (uint8_t []){0x87}, 1, 0},
|
||||
{0xB3, (uint8_t []){0x80}, 1, 0},
|
||||
{0xB5, (uint8_t []){0x4E}, 1, 0},
|
||||
{0xB7, (uint8_t []){0x85}, 1, 0},
|
||||
{0xB8, (uint8_t []){0x21}, 1, 0},
|
||||
{0xB9, (uint8_t []){0x10, 0x1F}, 2, 0},
|
||||
{0xBB, (uint8_t []){0x03}, 1,0},
|
||||
{0xBC, (uint8_t []){0x00}, 1,0},
|
||||
|
||||
{0xC1, (uint8_t []){0x78}, 1, 0},
|
||||
{0xC2, (uint8_t []){0x78}, 1, 0},
|
||||
{0xD0, (uint8_t []){0x88}, 1, 0},
|
||||
|
||||
{0xE0, (uint8_t []){0x00, 0x3A, 0x02}, 3, 0},
|
||||
{0xE1, (uint8_t []){0x04, 0xA0, 0x00, 0xA0, 0x05,0xA0, 0x00, 0xA0, 0x00, 0x40, 0x40}, 11, 0},
|
||||
{0xE2, (uint8_t []){0x30, 0x00, 0x40, 0x40, 0x32, 0xA0, 0x00, 0xA0, 0x00, 0xA0, 0x00, 0xA0, 0x00}, 13, 0},
|
||||
{0xE3, (uint8_t []){0x00, 0x00, 0x33, 0x33}, 4, 0},
|
||||
{0xE4, (uint8_t []){0x44, 0x44}, 2, 0},
|
||||
{0xE5, (uint8_t []){0x09, 0x2E, 0xA0, 0xA0, 0x0B, 0x30, 0xA0, 0xA0, 0x05, 0x2A, 0xA0, 0xA0, 0x07, 0x2C, 0xA0, 0xA0}, 16, 0},
|
||||
{0xE6, (uint8_t []){0x00, 0x00, 0x33, 0x33}, 4, 0},
|
||||
{0xE7, (uint8_t []){0x44, 0x44}, 2, 0},
|
||||
{0xE8, (uint8_t []){0x08, 0x2D, 0xA0, 0xA0, 0x0A, 0x2F, 0xA0, 0xA0, 0x04, 0x29, 0xA0, 0xA0, 0x06, 0x2B, 0xA0, 0xA0}, 16, 0},
|
||||
|
||||
{0xEB, (uint8_t []){0x00, 0x00, 0x4E, 0x4E, 0x00, 0x00, 0x00}, 7, 0},
|
||||
{0xEC, (uint8_t []){0x08, 0x01}, 2, 0},
|
||||
|
||||
{0xED, (uint8_t []){0xB0, 0x2B, 0x98, 0xA4, 0x56, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF7, 0x65, 0x4A, 0x89, 0xB2, 0x0B}, 16, 0},
|
||||
{0xEF, (uint8_t []){0x08, 0x08, 0x08, 0x45, 0x3F, 0x54}, 6, 0},
|
||||
{0xFF, (uint8_t []){0x77, 0x01, 0x00, 0x00, 0x00}, 5, 0},
|
||||
|
||||
// {0x3A, (uint8_t []){0x66}, 1, 0},
|
||||
{0x11, (uint8_t []){0x00}, 1, 120},
|
||||
{0x29, (uint8_t []){0x00}, 1, 20},
|
||||
|
||||
|
||||
|
||||
// {0xFF, (uint8_t []){0x77,0x01,0x00,0x00,0x13},5,0},
|
||||
// {0xEF, (uint8_t []){0x08}, 1, 0},
|
||||
// {0xFF, (uint8_t []){0x77,0x01,0x00,0x00,0x10},5,0},
|
||||
// {0xC0, (uint8_t []){0x63, 0x00}, 2, 0},
|
||||
// {0xC1, (uint8_t []){0x0D, 0x02}, 2, 0},
|
||||
// {0xC2, (uint8_t []){0x17, 0x08}, 2, 0},
|
||||
// {0xCC, (uint8_t []){0x10}, 1, 0},
|
||||
|
||||
// {0xB0, (uint8_t []){0x40, 0xC9, 0x94, 0x0E, 0x10, 0x05, 0x0B, 0x09, 0x08, 0x26, 0x04, 0x52, 0x10, 0x69, 0x6B, 0x69}, 16, 0},
|
||||
// {0xB1, (uint8_t []){0x40, 0xD2, 0x98, 0x0C, 0x92, 0x07, 0x09, 0x08, 0x07, 0x25, 0x02, 0x0E, 0x1C, 0x6E, 0x78, 0x55}, 16, 0},
|
||||
// {0xFF, (uint8_t []){0x77, 0x01, 0x00, 0x00, 0x11}, 5, 0},
|
||||
|
||||
// {0xB0, (uint8_t []){0x5D}, 1, 0},
|
||||
// {0xB1, (uint8_t []){0x4E}, 1, 0},
|
||||
// {0xB2, (uint8_t []){0x87}, 1, 0},
|
||||
// {0xB3, (uint8_t []){0x80}, 1, 0},
|
||||
// {0xB5, (uint8_t []){0x4E}, 1, 0},
|
||||
// {0xB7, (uint8_t []){0x85}, 1, 0},
|
||||
// {0xB8, (uint8_t []){0x21}, 1, 0},
|
||||
// {0xB9, (uint8_t []){0x10, 0x1F}, 2, 0},
|
||||
// {0xBB, (uint8_t []){0x03}, 1,0},
|
||||
// {0xBC, (uint8_t []){0x00}, 1,0},
|
||||
|
||||
// {0xC1, (uint8_t []){0x78}, 1, 0},
|
||||
// {0xC2, (uint8_t []){0x78}, 1, 0},
|
||||
// {0xD0, (uint8_t []){0x88}, 1, 0},
|
||||
|
||||
// {0xE0, (uint8_t []){0x00, 0x3A, 0x02}, 3, 0},
|
||||
// {0xE1, (uint8_t []){0x04, 0xA0, 0x00, 0xA0, 0x05,0xA0, 0x00, 0xA0, 0x00, 0x40, 0x40}, 11, 0},
|
||||
// {0xE2, (uint8_t []){0x30, 0x00, 0x40, 0x40, 0x32, 0xA0, 0x00, 0xA0, 0x00, 0xA0, 0x00, 0xA0, 0x00}, 13, 0},
|
||||
// {0xE3, (uint8_t []){0x00, 0x00, 0x33, 0x33}, 4, 0},
|
||||
// {0xE4, (uint8_t []){0x44, 0x44}, 2, 0},
|
||||
// {0xE5, (uint8_t []){0x09, 0x2E, 0xA0, 0xA0, 0x0B, 0x30, 0xA0, 0xA0, 0x05, 0x2A, 0xA0, 0xA0, 0x07, 0x2C, 0xA0, 0xA0}, 16, 0},
|
||||
// {0xE6, (uint8_t []){0x00, 0x00, 0x33, 0x33}, 4, 0},
|
||||
// {0xE7, (uint8_t []){0x44, 0x44}, 2, 0},
|
||||
// {0xE8, (uint8_t []){0x08, 0x2D, 0xA0, 0xA0, 0x0A, 0x2F, 0xA0, 0xA0, 0x04, 0x29, 0xA0, 0xA0, 0x06, 0x2B, 0xA0, 0xA0}, 16, 0},
|
||||
|
||||
// {0xEB, (uint8_t []){0x00, 0x00, 0x4E, 0x4E, 0x00, 0x00, 0x00}, 7, 0},
|
||||
// {0xEC, (uint8_t []){0x08, 0x01}, 2, 0},
|
||||
|
||||
// {0xED, (uint8_t []){0xB0, 0x2B, 0x98, 0xA4, 0x56, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF7, 0x65, 0x4A, 0x89, 0xB2, 0x0B}, 16, 0},
|
||||
// {0xEF, (uint8_t []){0x08, 0x08, 0x08, 0x45, 0x3F, 0x54}, 6, 0},
|
||||
// {0xFF, (uint8_t []){0x77, 0x01, 0x00, 0x00, 0x00}, 5, 0},
|
||||
|
||||
// // {0x3A, (uint8_t []){0x66}, 1, 0},
|
||||
// {0x11, (uint8_t []){0x00}, 1, 120},
|
||||
// {0x29, (uint8_t []){0x00}, 1, 20},
|
||||
|
||||
|
||||
};
|
||||
|
||||
static esp_err_t panel_st7701_init(esp_lcd_panel_t *panel)
|
||||
{
|
||||
st7701_panel_t *st7701 = (st7701_panel_t *)panel->user_data;
|
||||
esp_lcd_panel_io_handle_t io = st7701->io;
|
||||
const st7701_lcd_init_cmd_t *init_cmds = NULL;
|
||||
uint16_t init_cmds_size = 0;
|
||||
bool is_command2_disable = true;
|
||||
bool is_cmd_overwritten = false;
|
||||
|
||||
uint8_t ID[3];
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_rx_param(io, 0x04, ID, 3), TAG, "read ID failed");
|
||||
ESP_LOGI(TAG, "LCD ID: %02X %02X %02X", ID[0], ID[1], ID[2]);
|
||||
|
||||
// back to CMD_Page 0
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, ST7701_CMD_CND2BKxSEL, (uint8_t []) {
|
||||
ST7701_CMD_BKxSEL_BYTE0, ST7701_CMD_BKxSEL_BYTE1, ST7701_CMD_BKxSEL_BYTE2, ST7701_CMD_BKxSEL_BYTE3, 0x00
|
||||
}, 5), TAG, "Write cmd failed");
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL, (uint8_t[]) {
|
||||
st7701->madctl_val,
|
||||
}, 1), TAG, "send command failed");
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_COLMOD, (uint8_t[]) {
|
||||
st7701->colmod_val,
|
||||
}, 1), TAG, "send command failed");
|
||||
ESP_LOGI(TAG, " st7701->madctl_val: 0x%x, st7701->colmod_val: 0x%x", st7701->madctl_val, st7701->colmod_val);
|
||||
|
||||
// vendor specific initialization, it can be different between manufacturers
|
||||
// should consult the LCD supplier for initialization sequence code
|
||||
if (st7701->init_cmds) {
|
||||
init_cmds = st7701->init_cmds;
|
||||
init_cmds_size = st7701->init_cmds_size;
|
||||
} else {
|
||||
init_cmds = vendor_specific_init_default;
|
||||
init_cmds_size = sizeof(vendor_specific_init_default) / sizeof(st7701_lcd_init_cmd_t);
|
||||
}
|
||||
|
||||
for (int i = 0; i < init_cmds_size; i++) {
|
||||
// Check if the command has been used or conflicts with the internal only when command2 is disable
|
||||
if (is_command2_disable && (init_cmds[i].data_bytes > 0)) {
|
||||
switch (init_cmds[i].cmd) {
|
||||
case LCD_CMD_MADCTL:
|
||||
is_cmd_overwritten = true;
|
||||
st7701->madctl_val = ((uint8_t *)init_cmds[i].data)[0];
|
||||
break;
|
||||
case LCD_CMD_COLMOD:
|
||||
is_cmd_overwritten = true;
|
||||
st7701->colmod_val = ((uint8_t *)init_cmds[i].data)[0];
|
||||
break;
|
||||
default:
|
||||
is_cmd_overwritten = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (is_cmd_overwritten) {
|
||||
is_cmd_overwritten = false;
|
||||
ESP_LOGW(TAG, "The %02Xh command has been used and will be overwritten by external initialization sequence",
|
||||
init_cmds[i].cmd);
|
||||
}
|
||||
}
|
||||
|
||||
// Send command
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, init_cmds[i].cmd, init_cmds[i].data, init_cmds[i].data_bytes),
|
||||
TAG, "send command failed");
|
||||
vTaskDelay(pdMS_TO_TICKS(init_cmds[i].delay_ms));
|
||||
|
||||
// Check if the current cmd is the command2 disable cmd
|
||||
if ((init_cmds[i].cmd == ST7701_CMD_CND2BKxSEL) && (init_cmds[i].data_bytes > 4)) {
|
||||
is_command2_disable = !(((uint8_t *)init_cmds[i].data)[4] & ST7701_CMD_CN2_BIT);
|
||||
}
|
||||
}
|
||||
ESP_LOGD(TAG, "send init commands success");
|
||||
|
||||
ESP_RETURN_ON_ERROR(st7701->init(panel), TAG, "init MIPI DPI panel failed");
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t panel_st7701_invert_color(esp_lcd_panel_t *panel, bool invert_color_data)
|
||||
{
|
||||
st7701_panel_t *st7701 = (st7701_panel_t *)panel->user_data;
|
||||
esp_lcd_panel_io_handle_t io = st7701->io;
|
||||
int command = 0;
|
||||
if (invert_color_data) {
|
||||
command = LCD_CMD_INVON;
|
||||
} else {
|
||||
command = LCD_CMD_INVOFF;
|
||||
}
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, command, NULL, 0), TAG, "send command failed");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t panel_st7701_mirror(esp_lcd_panel_t *panel, bool mirror_x, bool mirror_y)
|
||||
{
|
||||
st7701_panel_t *st7701 = (st7701_panel_t *)panel->user_data;
|
||||
esp_lcd_panel_io_handle_t io = st7701->io;
|
||||
uint8_t sdir_val = 0;
|
||||
|
||||
ESP_RETURN_ON_FALSE(io, ESP_FAIL, TAG, "Panel IO is deleted, cannot send command");
|
||||
// Control mirror through LCD command
|
||||
if (mirror_x) {
|
||||
sdir_val = ST7701_CMD_SS_BIT;
|
||||
} else {
|
||||
sdir_val = 0;
|
||||
}
|
||||
if (mirror_y) {
|
||||
st7701->madctl_val |= LCD_CMD_ML_BIT;
|
||||
} else {
|
||||
st7701->madctl_val &= ~LCD_CMD_ML_BIT;
|
||||
}
|
||||
|
||||
// Enable the Command2 BK0
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, ST7701_CMD_CND2BKxSEL, (uint8_t []) {
|
||||
ST7701_CMD_BKxSEL_BYTE0, ST7701_CMD_BKxSEL_BYTE1, ST7701_CMD_BKxSEL_BYTE2, ST7701_CMD_BKxSEL_BYTE3,
|
||||
ST7701_CMD_BKxSEL_BK0 | ST7701_CMD_CN2_BIT,
|
||||
}, 5), TAG, "send command failed");
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, ST7701_CMD_SDIR, (uint8_t[]) {
|
||||
sdir_val,
|
||||
}, 1), TAG, "send command failed");;
|
||||
|
||||
// Disable Command2
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, ST7701_CMD_CND2BKxSEL, (uint8_t []) {
|
||||
ST7701_CMD_BKxSEL_BYTE0, ST7701_CMD_BKxSEL_BYTE1, ST7701_CMD_BKxSEL_BYTE2, ST7701_CMD_BKxSEL_BYTE3, 0,
|
||||
}, 5), TAG, "send command failed");
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL, (uint8_t[]) {
|
||||
st7701->madctl_val,
|
||||
}, 1), TAG, "send command failed");;
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t panel_st7701_disp_on_off(esp_lcd_panel_t *panel, bool on_off)
|
||||
{
|
||||
st7701_panel_t *st7701 = (st7701_panel_t *)panel->user_data;
|
||||
esp_lcd_panel_io_handle_t io = st7701->io;
|
||||
int command = 0;
|
||||
|
||||
if (on_off) {
|
||||
command = LCD_CMD_DISPON;
|
||||
} else {
|
||||
command = LCD_CMD_DISPOFF;
|
||||
}
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, command, NULL, 0), TAG, "send command failed");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t panel_st7701_sleep(esp_lcd_panel_t *panel, bool sleep)
|
||||
{
|
||||
st7701_panel_t *st7701 = (st7701_panel_t *)panel->user_data;
|
||||
esp_lcd_panel_io_handle_t io = st7701->io;
|
||||
int command = 0;
|
||||
if (sleep) {
|
||||
command = LCD_CMD_SLPIN;
|
||||
} else {
|
||||
command = LCD_CMD_SLPOUT;
|
||||
}
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, command, NULL, 0), TAG,
|
||||
"io tx param failed");
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
#endif
|
||||
+386
@@ -0,0 +1,386 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include "soc/soc_caps.h"
|
||||
|
||||
#if SOC_LCD_RGB_SUPPORTED
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "esp_check.h"
|
||||
#include "esp_lcd_panel_commands.h"
|
||||
#include "esp_lcd_panel_interface.h"
|
||||
#include "esp_lcd_panel_io.h"
|
||||
#include "esp_lcd_panel_rgb.h"
|
||||
#include "esp_lcd_panel_vendor.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_lcd_st7701.h"
|
||||
#include "esp_lcd_st7701_interface.h"
|
||||
typedef struct {
|
||||
esp_lcd_panel_io_handle_t io;
|
||||
int reset_gpio_num;
|
||||
uint8_t madctl_val; // Save current value of LCD_CMD_MADCTL register
|
||||
uint8_t colmod_val; // Save current value of LCD_CMD_COLMOD register
|
||||
const st7701_lcd_init_cmd_t *init_cmds;
|
||||
uint16_t init_cmds_size;
|
||||
struct {
|
||||
unsigned int mirror_by_cmd: 1;
|
||||
unsigned int enable_io_multiplex: 1;
|
||||
unsigned int display_on_off_use_cmd: 1;
|
||||
unsigned int reset_level: 1;
|
||||
} flags;
|
||||
// To save the original functions of RGB panel
|
||||
esp_err_t (*init)(esp_lcd_panel_t *panel);
|
||||
esp_err_t (*del)(esp_lcd_panel_t *panel);
|
||||
esp_err_t (*reset)(esp_lcd_panel_t *panel);
|
||||
esp_err_t (*mirror)(esp_lcd_panel_t *panel, bool x_axis, bool y_axis);
|
||||
esp_err_t (*disp_on_off)(esp_lcd_panel_t *panel, bool on_off);
|
||||
} st7701_panel_t;
|
||||
|
||||
static const char *TAG = "st7701_rgb";
|
||||
|
||||
static esp_err_t panel_st7701_send_init_cmds(st7701_panel_t *st7701);
|
||||
|
||||
static esp_err_t panel_st7701_init(esp_lcd_panel_t *panel);
|
||||
static esp_err_t panel_st7701_del(esp_lcd_panel_t *panel);
|
||||
static esp_err_t panel_st7701_reset(esp_lcd_panel_t *panel);
|
||||
static esp_err_t panel_st7701_mirror(esp_lcd_panel_t *panel, bool mirror_x, bool mirror_y);
|
||||
static esp_err_t panel_st7701_disp_on_off(esp_lcd_panel_t *panel, bool off);
|
||||
|
||||
esp_err_t esp_lcd_new_panel_st7701_rgb(const esp_lcd_panel_io_handle_t io, const esp_lcd_panel_dev_config_t *panel_dev_config,
|
||||
esp_lcd_panel_handle_t *ret_panel)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(io && panel_dev_config && ret_panel, ESP_ERR_INVALID_ARG, TAG, "invalid arguments");
|
||||
st7701_vendor_config_t *vendor_config = (st7701_vendor_config_t *)panel_dev_config->vendor_config;
|
||||
ESP_RETURN_ON_FALSE(vendor_config && vendor_config->rgb_config, ESP_ERR_INVALID_ARG, TAG, "`verndor_config` and `rgb_config` are necessary");
|
||||
ESP_RETURN_ON_FALSE(!vendor_config->flags.enable_io_multiplex || !vendor_config->flags.mirror_by_cmd,
|
||||
ESP_ERR_INVALID_ARG, TAG, "`mirror_by_cmd` and `enable_io_multiplex` cannot work together");
|
||||
|
||||
esp_err_t ret = ESP_OK;
|
||||
st7701_panel_t *st7701 = (st7701_panel_t *)calloc(1, sizeof(st7701_panel_t));
|
||||
ESP_RETURN_ON_FALSE(st7701, ESP_ERR_NO_MEM, TAG, "no mem for st7701 panel");
|
||||
|
||||
if (panel_dev_config->reset_gpio_num >= 0) {
|
||||
gpio_config_t io_conf = {
|
||||
.mode = GPIO_MODE_OUTPUT,
|
||||
.pin_bit_mask = 1ULL << panel_dev_config->reset_gpio_num,
|
||||
};
|
||||
ESP_GOTO_ON_ERROR(gpio_config(&io_conf), err, TAG, "configure GPIO for RST line failed");
|
||||
}
|
||||
|
||||
switch (panel_dev_config->rgb_ele_order) {
|
||||
case LCD_RGB_ELEMENT_ORDER_RGB:
|
||||
st7701->madctl_val = 0;
|
||||
break;
|
||||
case LCD_RGB_ELEMENT_ORDER_BGR:
|
||||
st7701->madctl_val |= LCD_CMD_BGR_BIT;
|
||||
break;
|
||||
default:
|
||||
ESP_GOTO_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, err, TAG, "unsupported color element order");
|
||||
break;
|
||||
}
|
||||
|
||||
st7701->colmod_val = 0;
|
||||
switch (panel_dev_config->bits_per_pixel) {
|
||||
case 16: // RGB565
|
||||
st7701->colmod_val = 0x50;
|
||||
break;
|
||||
case 18: // RGB666
|
||||
st7701->colmod_val = 0x60;
|
||||
break;
|
||||
case 24: // RGB888
|
||||
st7701->colmod_val = 0x70;
|
||||
break;
|
||||
default:
|
||||
ESP_GOTO_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, err, TAG, "unsupported pixel width");
|
||||
break;
|
||||
}
|
||||
|
||||
st7701->io = io;
|
||||
st7701->init_cmds = vendor_config->init_cmds;
|
||||
st7701->init_cmds_size = vendor_config->init_cmds_size;
|
||||
st7701->reset_gpio_num = panel_dev_config->reset_gpio_num;
|
||||
st7701->flags.mirror_by_cmd = vendor_config->flags.mirror_by_cmd;
|
||||
st7701->flags.display_on_off_use_cmd = (vendor_config->rgb_config->disp_gpio_num >= 0) ? 0 : 1;
|
||||
st7701->flags.enable_io_multiplex = vendor_config->flags.enable_io_multiplex;
|
||||
st7701->flags.reset_level = panel_dev_config->flags.reset_active_high;
|
||||
|
||||
if (st7701->flags.enable_io_multiplex) {
|
||||
if (st7701->reset_gpio_num >= 0) { // Perform hardware reset
|
||||
gpio_set_level(st7701->reset_gpio_num, st7701->flags.reset_level);
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
gpio_set_level(st7701->reset_gpio_num, !st7701->flags.reset_level);
|
||||
} else { // Perform software reset
|
||||
ESP_GOTO_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_SWRESET, NULL, 0), err, TAG, "send command failed");
|
||||
}
|
||||
vTaskDelay(pdMS_TO_TICKS(120));
|
||||
|
||||
/**
|
||||
* In order to enable the 3-wire SPI interface pins (such as SDA and SCK) to share other pins of the RGB interface
|
||||
* (such as HSYNC) and save GPIOs, we need to send LCD initialization commands via the 3-wire SPI interface before
|
||||
* `esp_lcd_new_rgb_panel()` is called.
|
||||
*/
|
||||
ESP_GOTO_ON_ERROR(panel_st7701_send_init_cmds(st7701), err, TAG, "send init commands failed");
|
||||
// After sending the initialization commands, the 3-wire SPI interface can be deleted
|
||||
ESP_GOTO_ON_ERROR(esp_lcd_panel_io_del(io), err, TAG, "delete panel IO failed");
|
||||
st7701->io = NULL;
|
||||
ESP_LOGD(TAG, "delete panel IO");
|
||||
}
|
||||
|
||||
// Create RGB panel
|
||||
ESP_GOTO_ON_ERROR(esp_lcd_new_rgb_panel(vendor_config->rgb_config, ret_panel), err, TAG, "create RGB panel failed");
|
||||
ESP_LOGD(TAG, "new RGB panel @%p", ret_panel);
|
||||
|
||||
// Save the original functions of RGB panel
|
||||
st7701->init = (*ret_panel)->init;
|
||||
st7701->del = (*ret_panel)->del;
|
||||
st7701->reset = (*ret_panel)->reset;
|
||||
st7701->mirror = (*ret_panel)->mirror;
|
||||
st7701->disp_on_off = (*ret_panel)->disp_on_off;
|
||||
// Overwrite the functions of RGB panel
|
||||
(*ret_panel)->init = panel_st7701_init;
|
||||
(*ret_panel)->del = panel_st7701_del;
|
||||
(*ret_panel)->reset = panel_st7701_reset;
|
||||
(*ret_panel)->mirror = panel_st7701_mirror;
|
||||
(*ret_panel)->disp_on_off = panel_st7701_disp_on_off;
|
||||
(*ret_panel)->user_data = st7701;
|
||||
ESP_LOGD(TAG, "new st7701 panel @%p", st7701);
|
||||
|
||||
return ESP_OK;
|
||||
|
||||
err:
|
||||
if (st7701) {
|
||||
if (panel_dev_config->reset_gpio_num >= 0) {
|
||||
gpio_reset_pin(panel_dev_config->reset_gpio_num);
|
||||
}
|
||||
free(st7701);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const st7701_lcd_init_cmd_t vendor_specific_init_default[] = {
|
||||
// {cmd, { data }, data_size, delay_ms}
|
||||
{0xFF, (uint8_t []){0x77, 0x01, 0x00, 0x00, 0x13}, 5, 0},
|
||||
{0xEF, (uint8_t []){0x08}, 1, 0},
|
||||
{0xFF, (uint8_t []){0x77, 0x01, 0x00, 0x00, 0x10}, 5, 0},
|
||||
{0xC0, (uint8_t []){0x3B, 0x00}, 2, 0},
|
||||
{0xC1, (uint8_t []){0x10, 0x02}, 2, 0},
|
||||
{0xC2, (uint8_t []){0x20, 0x06}, 2, 0},
|
||||
{0xCC, (uint8_t []){0x10}, 1, 0},
|
||||
{0xB0, (uint8_t []){0x00, 0x13, 0x5A, 0x0F, 0x12, 0x07, 0x09, 0x08, 0x08, 0x24, 0x07, 0x13, 0x12, 0x6B, 0x73, 0xFF}, 16, 0},
|
||||
{0xB1, (uint8_t []){0x00, 0x13, 0x5A, 0x0F, 0x12, 0x07, 0x09, 0x08, 0x08, 0x24, 0x07, 0x13, 0x12, 0x6B, 0x73, 0xFF}, 16, 0},
|
||||
{0xFF, (uint8_t []){0x77, 0x01, 0x00, 0x00, 0x11}, 5, 0},
|
||||
{0xB0, (uint8_t []){0x8D}, 1, 0},
|
||||
{0xB1, (uint8_t []){0x48}, 1, 0},
|
||||
{0xB2, (uint8_t []){0x89}, 1, 0},
|
||||
{0xB3, (uint8_t []){0x80}, 1, 0},
|
||||
{0xB5, (uint8_t []){0x49}, 1, 0},
|
||||
{0xB7, (uint8_t []){0x85}, 1, 0},
|
||||
{0xB8, (uint8_t []){0x32}, 1, 0},
|
||||
{0xC1, (uint8_t []){0x78}, 1, 0},
|
||||
{0xC2, (uint8_t []){0x78}, 1, 0},
|
||||
{0xD0, (uint8_t []){0x88}, 1, 100},
|
||||
{0xE0, (uint8_t []){0x00, 0x00, 0x02}, 3, 0},
|
||||
{0xE1, (uint8_t []){0x05, 0xC0, 0x07, 0xC0, 0x04, 0xC0, 0x06, 0xC0, 0x00, 0x44, 0x44}, 11, 0},
|
||||
{0xE2, (uint8_t []){0x00, 0x00, 0x33, 0x33, 0x01, 0xC0, 0x00, 0x00, 0x01, 0xC0, 0x00, 0x00, 0x00}, 13, 0},
|
||||
{0xE3, (uint8_t []){0x00, 0x00, 0x11, 0x11}, 4, 0},
|
||||
{0xE4, (uint8_t []){0x44, 0x44}, 2, 0},
|
||||
{0xE5, (uint8_t []){0x0D, 0xF1, 0x10, 0x98, 0x0F, 0xF3, 0x10, 0x98, 0x09, 0xED, 0x10, 0x98, 0x0B, 0xEF, 0x10, 0x98}, 16, 0},
|
||||
{0xE6, (uint8_t []){0x00, 0x00, 0x11, 0x11}, 4, 0},
|
||||
{0xE7, (uint8_t []){0x44, 0x44}, 2, 0},
|
||||
{0xE8, (uint8_t []){0x0C, 0xF0, 0x10, 0x98, 0x0E, 0xF2, 0x10, 0x98, 0x08, 0xEC, 0x10, 0x98, 0x0A, 0xEE, 0x10, 0x98}, 16, 0},
|
||||
{0xEB, (uint8_t []){0x00, 0x01, 0xE4, 0xE4, 0x44, 0x88, 0x00}, 7, 0},
|
||||
{0xED, (uint8_t []){0xFF, 0x04, 0x56, 0x7F, 0xBA, 0x2F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF2, 0xAB, 0xF7, 0x65, 0x40, 0xFF}, 16, 0},
|
||||
{0xEF, (uint8_t []){0x10, 0x0D, 0x04, 0x08, 0x3F, 0x1F}, 6, 0},
|
||||
{0xFF, (uint8_t []){0x77, 0x01, 0x00, 0x00, 0x00}, 5, 0},
|
||||
{0x11, (uint8_t []){0x00}, 0, 120},
|
||||
{0x29, (uint8_t []){0x00}, 0, 0},
|
||||
|
||||
// {0xFF, (uint8_t []){0x77, 0x01, 0x00, 0x00, 0x12}, 5, 0}, /* This part of the parameters can be used for screen self-test */
|
||||
// {0xD1, (uint8_t []){0x81}, 1, 0},
|
||||
// {0xD2, (uint8_t []){0x08}, 1, 0},
|
||||
};
|
||||
|
||||
static esp_err_t panel_st7701_send_init_cmds(st7701_panel_t *st7701)
|
||||
{
|
||||
esp_lcd_panel_io_handle_t io = st7701->io;
|
||||
const st7701_lcd_init_cmd_t *init_cmds = NULL;
|
||||
uint16_t init_cmds_size = 0;
|
||||
bool is_command2_disable = true;
|
||||
bool is_cmd_overwritten = false;
|
||||
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, ST7701_CMD_CND2BKxSEL, (uint8_t []) {
|
||||
ST7701_CMD_BKxSEL_BYTE0, ST7701_CMD_BKxSEL_BYTE1, ST7701_CMD_BKxSEL_BYTE2, ST7701_CMD_BKxSEL_BYTE3, 0x00
|
||||
}, 5), TAG, "Write cmd failed");
|
||||
// Set color format
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL, (uint8_t []) {
|
||||
st7701->madctl_val
|
||||
}, 1), TAG, "Write cmd failed");
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_COLMOD, (uint8_t []) {
|
||||
st7701->colmod_val
|
||||
}, 1), TAG, "Write cmd failed");
|
||||
|
||||
// vendor specific initialization, it can be different between manufacturers
|
||||
// should consult the LCD supplier for initialization sequence code
|
||||
if (st7701->init_cmds) {
|
||||
init_cmds = st7701->init_cmds;
|
||||
init_cmds_size = st7701->init_cmds_size;
|
||||
} else {
|
||||
init_cmds = vendor_specific_init_default;
|
||||
init_cmds_size = sizeof(vendor_specific_init_default) / sizeof(st7701_lcd_init_cmd_t);
|
||||
}
|
||||
|
||||
for (int i = 0; i < init_cmds_size; i++) {
|
||||
// Check if the command has been used or conflicts with the internal only when command2 is disable
|
||||
if (is_command2_disable && (init_cmds[i].data_bytes > 0)) {
|
||||
switch (init_cmds[i].cmd) {
|
||||
case LCD_CMD_MADCTL:
|
||||
is_cmd_overwritten = true;
|
||||
st7701->madctl_val = ((uint8_t *)init_cmds[i].data)[0];
|
||||
break;
|
||||
case LCD_CMD_COLMOD:
|
||||
is_cmd_overwritten = true;
|
||||
st7701->colmod_val = ((uint8_t *)init_cmds[i].data)[0];
|
||||
break;
|
||||
default:
|
||||
is_cmd_overwritten = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (is_cmd_overwritten) {
|
||||
is_cmd_overwritten = false;
|
||||
ESP_LOGW(TAG, "The %02Xh command has been used and will be overwritten by external initialization sequence",
|
||||
init_cmds[i].cmd);
|
||||
}
|
||||
}
|
||||
|
||||
// Send command
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, init_cmds[i].cmd, init_cmds[i].data, init_cmds[i].data_bytes),
|
||||
TAG, "send command failed");
|
||||
vTaskDelay(pdMS_TO_TICKS(init_cmds[i].delay_ms));
|
||||
|
||||
// Check if the current cmd is the command2 disable cmd
|
||||
if ((init_cmds[i].cmd == ST7701_CMD_CND2BKxSEL) && (init_cmds[i].data_bytes > 4)) {
|
||||
is_command2_disable = !(((uint8_t *)init_cmds[i].data)[4] & ST7701_CMD_CN2_BIT);
|
||||
}
|
||||
}
|
||||
ESP_LOGD(TAG, "send init commands success");
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t panel_st7701_init(esp_lcd_panel_t *panel)
|
||||
{
|
||||
st7701_panel_t *st7701 = (st7701_panel_t *)panel->user_data;
|
||||
|
||||
if (!st7701->flags.enable_io_multiplex) {
|
||||
ESP_RETURN_ON_ERROR(panel_st7701_send_init_cmds(st7701), TAG, "send init commands failed");
|
||||
}
|
||||
// Init RGB panel
|
||||
ESP_RETURN_ON_ERROR(st7701->init(panel), TAG, "init RGB panel failed");
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t panel_st7701_del(esp_lcd_panel_t *panel)
|
||||
{
|
||||
st7701_panel_t *st7701 = (st7701_panel_t *)panel->user_data;
|
||||
|
||||
if (st7701->reset_gpio_num >= 0) {
|
||||
gpio_reset_pin(st7701->reset_gpio_num);
|
||||
}
|
||||
// Delete RGB panel
|
||||
st7701->del(panel);
|
||||
free(st7701);
|
||||
ESP_LOGD(TAG, "del st7701 panel @%p", st7701);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t panel_st7701_reset(esp_lcd_panel_t *panel)
|
||||
{
|
||||
st7701_panel_t *st7701 = (st7701_panel_t *)panel->user_data;
|
||||
esp_lcd_panel_io_handle_t io = st7701->io;
|
||||
|
||||
// Perform hardware reset
|
||||
if (st7701->reset_gpio_num >= 0) {
|
||||
gpio_set_level(st7701->reset_gpio_num, st7701->flags.reset_level);
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
gpio_set_level(st7701->reset_gpio_num, !st7701->flags.reset_level);
|
||||
vTaskDelay(pdMS_TO_TICKS(120));
|
||||
} else if (io) { // Perform software reset
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_SWRESET, NULL, 0), TAG, "send command failed");
|
||||
vTaskDelay(pdMS_TO_TICKS(120));
|
||||
}
|
||||
// Reset RGB panel
|
||||
ESP_RETURN_ON_ERROR(st7701->reset(panel), TAG, "reset RGB panel failed");
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t panel_st7701_mirror(esp_lcd_panel_t *panel, bool mirror_x, bool mirror_y)
|
||||
{
|
||||
st7701_panel_t *st7701 = (st7701_panel_t *)panel->user_data;
|
||||
esp_lcd_panel_io_handle_t io = st7701->io;
|
||||
uint8_t sdir_val = 0;
|
||||
|
||||
if (st7701->flags.mirror_by_cmd) {
|
||||
ESP_RETURN_ON_FALSE(io, ESP_FAIL, TAG, "Panel IO is deleted, cannot send command");
|
||||
// Control mirror through LCD command
|
||||
if (mirror_x) {
|
||||
sdir_val = ST7701_CMD_SS_BIT;
|
||||
} else {
|
||||
sdir_val = 0;
|
||||
}
|
||||
if (mirror_y) {
|
||||
st7701->madctl_val |= LCD_CMD_ML_BIT;
|
||||
} else {
|
||||
st7701->madctl_val &= ~LCD_CMD_ML_BIT;
|
||||
}
|
||||
|
||||
// Enable the Command2 BK0
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, ST7701_CMD_CND2BKxSEL, (uint8_t []) {
|
||||
ST7701_CMD_BKxSEL_BYTE0, ST7701_CMD_BKxSEL_BYTE1, ST7701_CMD_BKxSEL_BYTE2, ST7701_CMD_BKxSEL_BYTE3,
|
||||
ST7701_CMD_BKxSEL_BK0 | ST7701_CMD_CN2_BIT,
|
||||
}, 5), TAG, "send command failed");
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, ST7701_CMD_SDIR, (uint8_t[]) {
|
||||
sdir_val,
|
||||
}, 1), TAG, "send command failed");;
|
||||
|
||||
// Disable Command2
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, ST7701_CMD_CND2BKxSEL, (uint8_t []) {
|
||||
ST7701_CMD_BKxSEL_BYTE0, ST7701_CMD_BKxSEL_BYTE1, ST7701_CMD_BKxSEL_BYTE2, ST7701_CMD_BKxSEL_BYTE3, 0,
|
||||
}, 5), TAG, "send command failed");
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL, (uint8_t[]) {
|
||||
st7701->madctl_val,
|
||||
}, 1), TAG, "send command failed");;
|
||||
} else {
|
||||
// Control mirror through RGB panel
|
||||
ESP_RETURN_ON_ERROR(st7701->mirror(panel, mirror_x, mirror_y), TAG, "RGB panel mirror failed");
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t panel_st7701_disp_on_off(esp_lcd_panel_t *panel, bool on_off)
|
||||
{
|
||||
st7701_panel_t *st7701 = (st7701_panel_t *)panel->user_data;
|
||||
esp_lcd_panel_io_handle_t io = st7701->io;
|
||||
int command = 0;
|
||||
|
||||
if (st7701->flags.display_on_off_use_cmd) {
|
||||
ESP_RETURN_ON_FALSE(io, ESP_FAIL, TAG, "Panel IO is deleted, cannot send command");
|
||||
// Control display on/off through LCD command
|
||||
if (on_off) {
|
||||
command = LCD_CMD_DISPON;
|
||||
} else {
|
||||
command = LCD_CMD_DISPOFF;
|
||||
}
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, command, NULL, 0), TAG, "send command failed");
|
||||
} else {
|
||||
// Control display on/off through display control signal
|
||||
ESP_RETURN_ON_ERROR(st7701->disp_on_off(panel, on_off), TAG, "RGB panel disp_on_off failed");
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
#endif
|
||||
+183
@@ -0,0 +1,183 @@
|
||||
#include "sdkconfig.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "esp_timer.h"
|
||||
#include "esp_lcd_panel_ops.h"
|
||||
#include "esp_lcd_mipi_dsi.h"
|
||||
#include "esp_lcd_panel_io.h"
|
||||
#include "esp_ldo_regulator.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "driver/i2c_master.h"
|
||||
#include "driver/ledc.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_log.h"
|
||||
#include "Arduino.h"
|
||||
|
||||
#include "esp_lcd_st7701.h"
|
||||
#include "st7701_lcd.h"
|
||||
|
||||
#define LCD_H_RES 480
|
||||
#define LCD_V_RES 800
|
||||
|
||||
#define MIPI_DPI_PX_FORMAT (LCD_COLOR_PIXEL_FORMAT_RGB565)
|
||||
#define LCD_BIT_PER_PIXEL (16)
|
||||
|
||||
// “VDD_MIPI_DPHY”应供电 2.5V,可从内部 LDO 稳压器或外部 LDO 芯片获取电源
|
||||
#define EXAMPLE_MIPI_DSI_PHY_PWR_LDO_CHAN 3 // LDO_VO3 连接至 VDD_MIPI_DPHY
|
||||
#define EXAMPLE_MIPI_DSI_PHY_PWR_LDO_VOLTAGE_MV 2500
|
||||
#define EXAMPLE_LCD_BK_LIGHT_ON_LEVEL 1
|
||||
#define EXAMPLE_LCD_BK_LIGHT_OFF_LEVEL (0)
|
||||
#define EXAMPLE_PIN_NUM_BK_LIGHT GPIO_NUM_23
|
||||
|
||||
#define BSP_LCD_BACKLIGHT GPIO_NUM_23
|
||||
#define LCD_LEDC_CH LEDC_CHANNEL_0
|
||||
|
||||
static const char *TAG = "example";
|
||||
esp_lcd_panel_handle_t panel_handle = NULL;
|
||||
esp_lcd_panel_io_handle_t io_handle = NULL;
|
||||
|
||||
st7701_lcd::st7701_lcd(int8_t lcd_rst)
|
||||
{
|
||||
_lcd_rst = lcd_rst;
|
||||
}
|
||||
|
||||
void st7701_lcd::example_bsp_enable_dsi_phy_power()
|
||||
{
|
||||
// 打开 MIPI DSI PHY 的电源,使其从“无电”状态进入“关机”状态
|
||||
esp_ldo_channel_handle_t ldo_mipi_phy = NULL;
|
||||
#ifdef EXAMPLE_MIPI_DSI_PHY_PWR_LDO_CHAN
|
||||
esp_ldo_channel_config_t ldo_mipi_phy_config = {
|
||||
.chan_id = EXAMPLE_MIPI_DSI_PHY_PWR_LDO_CHAN,
|
||||
.voltage_mv = EXAMPLE_MIPI_DSI_PHY_PWR_LDO_VOLTAGE_MV,
|
||||
};
|
||||
ESP_ERROR_CHECK(esp_ldo_acquire_channel(&ldo_mipi_phy_config, &ldo_mipi_phy));
|
||||
ESP_LOGI(TAG, "MIPI DSI PHY Powered on");
|
||||
#endif
|
||||
}
|
||||
|
||||
void st7701_lcd::example_bsp_init_lcd_backlight()
|
||||
{
|
||||
#if EXAMPLE_PIN_NUM_BK_LIGHT >= 0
|
||||
gpio_config_t bk_gpio_config = {
|
||||
.pin_bit_mask = 1ULL << EXAMPLE_PIN_NUM_BK_LIGHT,
|
||||
.mode = GPIO_MODE_OUTPUT
|
||||
};
|
||||
ESP_ERROR_CHECK(gpio_config(&bk_gpio_config));
|
||||
#endif
|
||||
}
|
||||
|
||||
void st7701_lcd::example_bsp_set_lcd_backlight(uint32_t level)
|
||||
{
|
||||
|
||||
#if EXAMPLE_PIN_NUM_BK_LIGHT >= 0
|
||||
gpio_set_level(EXAMPLE_PIN_NUM_BK_LIGHT, level);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
void st7701_lcd::begin()
|
||||
{
|
||||
example_bsp_enable_dsi_phy_power();
|
||||
example_bsp_init_lcd_backlight();
|
||||
// example_bsp_set_lcd_backlight(EXAMPLE_LCD_BK_LIGHT_OFF_LEVEL);
|
||||
|
||||
// 首先创建 MIPI DSI 总线,它还将初始化 DSI PHY
|
||||
esp_lcd_dsi_bus_handle_t mipi_dsi_bus;
|
||||
esp_lcd_dsi_bus_config_t bus_config = ST7701_PANEL_BUS_DSI_2CH_CONFIG();
|
||||
ESP_ERROR_CHECK(esp_lcd_new_dsi_bus(&bus_config, &mipi_dsi_bus));
|
||||
|
||||
ESP_LOGI(TAG, "Install MIPI DSI LCD control panel");
|
||||
// 我们使用DBI接口发送LCD命令和参数
|
||||
esp_lcd_dbi_io_config_t dbi_config = ST7701_PANEL_IO_DBI_CONFIG();
|
||||
|
||||
ESP_ERROR_CHECK(esp_lcd_new_panel_io_dbi(mipi_dsi_bus, &dbi_config, &io_handle));
|
||||
|
||||
// 创建JD9365控制面板
|
||||
esp_lcd_dpi_panel_config_t dpi_config = ST7701_480_360_PANEL_60HZ_DPI_CONFIG(MIPI_DPI_PX_FORMAT);
|
||||
|
||||
st7701_vendor_config_t vendor_config = {
|
||||
// .init_cmds = lcd_cmd,
|
||||
// .init_cmds_size = sizeof(lcd_cmd) / sizeof(st7701_lcd_init_cmd_t),
|
||||
.mipi_config = {
|
||||
.dsi_bus = mipi_dsi_bus,
|
||||
.dpi_config = &dpi_config,
|
||||
},
|
||||
.flags = {
|
||||
.use_mipi_interface = 1,
|
||||
}
|
||||
};
|
||||
|
||||
const esp_lcd_panel_dev_config_t panel_config = {
|
||||
.reset_gpio_num = GPIO_NUM_5,
|
||||
.rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB,
|
||||
.bits_per_pixel = 16,
|
||||
.vendor_config = &vendor_config,
|
||||
};
|
||||
ESP_ERROR_CHECK(esp_lcd_new_panel_st7701(io_handle, &panel_config, &panel_handle));
|
||||
ESP_ERROR_CHECK(esp_lcd_panel_reset(panel_handle));
|
||||
ESP_ERROR_CHECK(esp_lcd_panel_init(panel_handle));
|
||||
|
||||
// esp_lcd_dpi_panel_event_callbacks_t cbs = {0};
|
||||
// if (dsi_cfg->flags.avoid_tearing) {
|
||||
// cbs.on_refresh_done = lvgl_port_flush_dpi_vsync_ready_callback;
|
||||
// } else {
|
||||
// cbs.on_color_trans_done = lvgl_port_flush_dpi_panel_ready_callback;
|
||||
// }
|
||||
// /* Register done callback */
|
||||
// esp_lcd_dpi_panel_register_event_callbacks(disp_ctx->panel_handle, &cbs, &disp_ctx->disp_drv);
|
||||
|
||||
// 打开背光
|
||||
example_bsp_set_lcd_backlight(EXAMPLE_LCD_BK_LIGHT_ON_LEVEL);
|
||||
}
|
||||
|
||||
void st7701_lcd::lcd_draw_bitmap(uint16_t x_start, uint16_t y_start, uint16_t x_end, uint16_t y_end, uint16_t *color_data)
|
||||
{
|
||||
esp_lcd_panel_draw_bitmap(panel_handle, x_start, y_start, x_end, y_end, color_data);
|
||||
}
|
||||
|
||||
void st7701_lcd::draw16bitbergbbitmap(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t *color_data)
|
||||
{
|
||||
uint16_t x_start = x;
|
||||
uint16_t y_start = y;
|
||||
uint16_t x_end = w + x;
|
||||
uint16_t y_end = h + y;
|
||||
|
||||
esp_lcd_panel_draw_bitmap(panel_handle, x_start, y_start, x_end, y_end, color_data);
|
||||
}
|
||||
|
||||
void st7701_lcd::fillScreen(uint16_t color)
|
||||
{
|
||||
uint16_t *color_data = (uint16_t *)heap_caps_malloc(480 * 272 * 2, MALLOC_CAP_INTERNAL);
|
||||
memset(color_data, color, 480 * 272 * 2);
|
||||
draw16bitbergbbitmap(0, 0, 480, 272, color_data);
|
||||
free(color_data);
|
||||
}
|
||||
|
||||
void st7701_lcd::te_on()
|
||||
{
|
||||
esp_lcd_panel_io_tx_param(io_handle, 0x35,new (uint8_t[]){0x00}, 1);
|
||||
}
|
||||
|
||||
void st7701_lcd::te_off()
|
||||
{
|
||||
esp_lcd_panel_io_tx_param(io_handle, 0x34,new (uint8_t[]){0x00}, 0);
|
||||
}
|
||||
|
||||
uint16_t st7701_lcd::width()
|
||||
{
|
||||
return LCD_H_RES;
|
||||
}
|
||||
|
||||
uint16_t st7701_lcd::height()
|
||||
{
|
||||
return LCD_V_RES;
|
||||
}
|
||||
|
||||
void st7701_lcd::get_handle(bsp_lcd_handles_t *ret_handles)
|
||||
{
|
||||
ret_handles->io = io_handle;
|
||||
ret_handles->mipi_dsi_bus = NULL;
|
||||
ret_handles->panel = panel_handle;
|
||||
ret_handles->control = NULL;
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
#ifndef _ST7701_LCD_H
|
||||
#define _ST7701_LCD_H
|
||||
#include <stdio.h>
|
||||
#include "esp_lcd_types.h"
|
||||
#include "esp_lcd_mipi_dsi.h"
|
||||
|
||||
typedef struct {
|
||||
esp_lcd_dsi_bus_handle_t mipi_dsi_bus; /*!< MIPI DSI bus handle */
|
||||
esp_lcd_panel_io_handle_t io; /*!< ESP LCD IO handle */
|
||||
esp_lcd_panel_handle_t panel; /*!< ESP LCD panel (color) handle */
|
||||
esp_lcd_panel_handle_t control; /*!< ESP LCD panel (control) handle */
|
||||
} bsp_lcd_handles_t;
|
||||
|
||||
class st7701_lcd
|
||||
{
|
||||
public:
|
||||
st7701_lcd(int8_t lcd_rst);
|
||||
|
||||
void begin();
|
||||
void example_bsp_enable_dsi_phy_power();
|
||||
void example_bsp_init_lcd_backlight();
|
||||
void example_bsp_set_lcd_backlight(uint32_t level);
|
||||
void lcd_draw_bitmap(uint16_t x_start, uint16_t y_start,
|
||||
uint16_t x_end, uint16_t y_end, uint16_t *color_data);
|
||||
void draw16bitbergbbitmap(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t *color_data);
|
||||
void fillScreen(uint16_t color);
|
||||
void te_on();
|
||||
void te_off();
|
||||
uint16_t width();
|
||||
uint16_t height();
|
||||
void get_handle(bsp_lcd_handles_t *ret_handles);
|
||||
|
||||
private:
|
||||
int8_t _lcd_rst;
|
||||
};
|
||||
#endif
|
||||
+236
@@ -0,0 +1,236 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "esp_system.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_check.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_lcd_touch.h"
|
||||
|
||||
static const char *TAG = "TP";
|
||||
|
||||
/*******************************************************************************
|
||||
* Function definitions
|
||||
*******************************************************************************/
|
||||
|
||||
/*******************************************************************************
|
||||
* Local variables
|
||||
*******************************************************************************/
|
||||
|
||||
/*******************************************************************************
|
||||
* Public API functions
|
||||
*******************************************************************************/
|
||||
|
||||
esp_err_t esp_lcd_touch_read_data(esp_lcd_touch_handle_t tp)
|
||||
{
|
||||
assert(tp != NULL);
|
||||
assert(tp->read_data != NULL);
|
||||
|
||||
return tp->read_data(tp);
|
||||
}
|
||||
|
||||
bool esp_lcd_touch_get_coordinates(esp_lcd_touch_handle_t tp, uint16_t *x, uint16_t *y, uint16_t *strength, uint8_t *point_num, uint8_t max_point_num)
|
||||
{
|
||||
bool touched = false;
|
||||
|
||||
assert(tp != NULL);
|
||||
assert(x != NULL);
|
||||
assert(y != NULL);
|
||||
assert(tp->get_xy != NULL);
|
||||
|
||||
touched = tp->get_xy(tp, x, y, strength, point_num, max_point_num);
|
||||
if (!touched) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Process coordinates by user */
|
||||
if (tp->config.process_coordinates != NULL) {
|
||||
tp->config.process_coordinates(tp, x, y, strength, point_num, max_point_num);
|
||||
}
|
||||
|
||||
/* Software coordinates adjustment needed */
|
||||
bool sw_adj_needed = ((tp->config.flags.mirror_x && (tp->set_mirror_x == NULL)) ||
|
||||
(tp->config.flags.mirror_y && (tp->set_mirror_y == NULL)) ||
|
||||
(tp->config.flags.swap_xy && (tp->set_swap_xy == NULL)));
|
||||
|
||||
/* Adjust all coordinates */
|
||||
for (int i = 0; (sw_adj_needed && i < *point_num); i++) {
|
||||
|
||||
/* Mirror X coordinates (if not supported by HW) */
|
||||
if (tp->config.flags.mirror_x && tp->set_mirror_x == NULL) {
|
||||
x[i] = tp->config.x_max - x[i];
|
||||
}
|
||||
|
||||
/* Mirror Y coordinates (if not supported by HW) */
|
||||
if (tp->config.flags.mirror_y && tp->set_mirror_y == NULL) {
|
||||
y[i] = tp->config.y_max - y[i];
|
||||
}
|
||||
|
||||
/* Swap X and Y coordinates (if not supported by HW) */
|
||||
if (tp->config.flags.swap_xy && tp->set_swap_xy == NULL) {
|
||||
uint16_t tmp = x[i];
|
||||
x[i] = y[i];
|
||||
y[i] = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
return touched;
|
||||
}
|
||||
|
||||
#if (CONFIG_ESP_LCD_TOUCH_MAX_BUTTONS > 0)
|
||||
esp_err_t esp_lcd_touch_get_button_state(esp_lcd_touch_handle_t tp, uint8_t n, uint8_t *state)
|
||||
{
|
||||
assert(tp != NULL);
|
||||
assert(state != NULL);
|
||||
|
||||
*state = 0;
|
||||
|
||||
if (tp->get_button_state) {
|
||||
return tp->get_button_state(tp, n, state);
|
||||
} else {
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
esp_err_t esp_lcd_touch_set_swap_xy(esp_lcd_touch_handle_t tp, bool swap)
|
||||
{
|
||||
assert(tp != NULL);
|
||||
|
||||
tp->config.flags.swap_xy = swap;
|
||||
|
||||
/* Is swap supported by HW? */
|
||||
if (tp->set_swap_xy) {
|
||||
return tp->set_swap_xy(tp, swap);
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_lcd_touch_get_swap_xy(esp_lcd_touch_handle_t tp, bool *swap)
|
||||
{
|
||||
assert(tp != NULL);
|
||||
assert(swap != NULL);
|
||||
|
||||
/* Is swap supported by HW? */
|
||||
if (tp->get_swap_xy) {
|
||||
return tp->get_swap_xy(tp, swap);
|
||||
} else {
|
||||
*swap = tp->config.flags.swap_xy;
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_lcd_touch_set_mirror_x(esp_lcd_touch_handle_t tp, bool mirror)
|
||||
{
|
||||
assert(tp != NULL);
|
||||
|
||||
tp->config.flags.mirror_x = mirror;
|
||||
|
||||
/* Is mirror supported by HW? */
|
||||
if (tp->set_mirror_x) {
|
||||
return tp->set_mirror_x(tp, mirror);
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_lcd_touch_get_mirror_x(esp_lcd_touch_handle_t tp, bool *mirror)
|
||||
{
|
||||
assert(tp != NULL);
|
||||
assert(mirror != NULL);
|
||||
|
||||
/* Is swap supported by HW? */
|
||||
if (tp->get_mirror_x) {
|
||||
return tp->get_mirror_x(tp, mirror);
|
||||
} else {
|
||||
*mirror = tp->config.flags.mirror_x;
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_lcd_touch_set_mirror_y(esp_lcd_touch_handle_t tp, bool mirror)
|
||||
{
|
||||
assert(tp != NULL);
|
||||
|
||||
tp->config.flags.mirror_y = mirror;
|
||||
|
||||
/* Is mirror supported by HW? */
|
||||
if (tp->set_mirror_y) {
|
||||
return tp->set_mirror_y(tp, mirror);
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_lcd_touch_get_mirror_y(esp_lcd_touch_handle_t tp, bool *mirror)
|
||||
{
|
||||
assert(tp != NULL);
|
||||
assert(mirror != NULL);
|
||||
|
||||
/* Is swap supported by HW? */
|
||||
if (tp->get_mirror_y) {
|
||||
return tp->get_mirror_y(tp, mirror);
|
||||
} else {
|
||||
*mirror = tp->config.flags.mirror_y;
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_lcd_touch_del(esp_lcd_touch_handle_t tp)
|
||||
{
|
||||
assert(tp != NULL);
|
||||
|
||||
if (tp->del != NULL) {
|
||||
return tp->del(tp);
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_lcd_touch_register_interrupt_callback(esp_lcd_touch_handle_t tp, esp_lcd_touch_interrupt_callback_t callback)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
assert(tp != NULL);
|
||||
|
||||
/* Interrupt pin is not selected */
|
||||
if (tp->config.int_gpio_num == GPIO_NUM_NC) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
tp->config.interrupt_callback = callback;
|
||||
|
||||
if (callback != NULL) {
|
||||
ret = gpio_install_isr_service(0);
|
||||
/* ISR service can be installed from user before, then it returns invalid state */
|
||||
if (ret != ESP_OK && ret != ESP_ERR_INVALID_STATE) {
|
||||
ESP_LOGE(TAG, "GPIO ISR install failed");
|
||||
return ret;
|
||||
}
|
||||
/* Add GPIO ISR handler */
|
||||
ret = gpio_intr_enable(tp->config.int_gpio_num);
|
||||
ESP_RETURN_ON_ERROR(ret, TAG, "GPIO ISR install failed");
|
||||
ret = gpio_isr_handler_add(tp->config.int_gpio_num, (gpio_isr_t)tp->config.interrupt_callback, tp);
|
||||
ESP_RETURN_ON_ERROR(ret, TAG, "GPIO ISR install failed");
|
||||
} else {
|
||||
/* Remove GPIO ISR handler */
|
||||
ret = gpio_isr_handler_remove(tp->config.int_gpio_num);
|
||||
ESP_RETURN_ON_ERROR(ret, TAG, "GPIO ISR remove handler failed");
|
||||
ret = gpio_intr_disable(tp->config.int_gpio_num);
|
||||
ESP_RETURN_ON_ERROR(ret, TAG, "GPIO ISR disable failed");
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
+370
@@ -0,0 +1,370 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* @brief ESP LCD touch
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "sdkconfig.h"
|
||||
#include "esp_err.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "esp_lcd_panel_io.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/semphr.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define CONFIG_ESP_LCD_TOUCH_MAX_BUTTONS (1)
|
||||
#define CONFIG_ESP_LCD_TOUCH_MAX_POINTS (5)
|
||||
|
||||
/**
|
||||
* @brief Touch controller type
|
||||
*
|
||||
*/
|
||||
typedef struct esp_lcd_touch_s esp_lcd_touch_t;
|
||||
typedef esp_lcd_touch_t *esp_lcd_touch_handle_t;
|
||||
|
||||
/**
|
||||
* @brief Touch controller interrupt callback type
|
||||
*
|
||||
*/
|
||||
typedef void (*esp_lcd_touch_interrupt_callback_t)(esp_lcd_touch_handle_t tp);
|
||||
|
||||
/**
|
||||
* @brief Touch Configuration Type
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
uint16_t x_max; /*!< X coordinates max (for mirroring) */
|
||||
uint16_t y_max; /*!< Y coordinates max (for mirroring) */
|
||||
|
||||
gpio_num_t rst_gpio_num; /*!< GPIO number of reset pin */
|
||||
gpio_num_t int_gpio_num; /*!< GPIO number of interrupt pin */
|
||||
|
||||
struct {
|
||||
unsigned int reset: 1; /*!< Level of reset pin in reset */
|
||||
unsigned int interrupt: 1;/*!< Active Level of interrupt pin */
|
||||
} levels;
|
||||
|
||||
struct {
|
||||
unsigned int swap_xy: 1; /*!< Swap X and Y after read coordinates */
|
||||
unsigned int mirror_x: 1; /*!< Mirror X after read coordinates */
|
||||
unsigned int mirror_y: 1; /*!< Mirror Y after read coordinates */
|
||||
} flags;
|
||||
|
||||
/*!< User callback called after get coordinates from touch controller for apply user adjusting */
|
||||
void (*process_coordinates)(esp_lcd_touch_handle_t tp, uint16_t *x, uint16_t *y, uint16_t *strength, uint8_t *point_num, uint8_t max_point_num);
|
||||
/*!< User callback called after the touch interrupt occured */
|
||||
esp_lcd_touch_interrupt_callback_t interrupt_callback;
|
||||
} esp_lcd_touch_config_t;
|
||||
|
||||
typedef struct {
|
||||
uint8_t points; /*!< Count of touch points saved */
|
||||
|
||||
struct {
|
||||
uint16_t x; /*!< X coordinate */
|
||||
uint16_t y; /*!< Y coordinate */
|
||||
uint16_t strength; /*!< Strength */
|
||||
} coords[CONFIG_ESP_LCD_TOUCH_MAX_POINTS];
|
||||
|
||||
#if (CONFIG_ESP_LCD_TOUCH_MAX_BUTTONS > 0)
|
||||
uint8_t buttons; /*!< Count of buttons states saved */
|
||||
|
||||
struct {
|
||||
uint8_t status; /*!< Status of button */
|
||||
} button[CONFIG_ESP_LCD_TOUCH_MAX_BUTTONS];
|
||||
#endif
|
||||
|
||||
portMUX_TYPE lock; /*!< Lock for read/write */
|
||||
} esp_lcd_touch_data_t;
|
||||
|
||||
/**
|
||||
* @brief Declare of Touch Type
|
||||
*
|
||||
*/
|
||||
struct esp_lcd_touch_s {
|
||||
|
||||
/**
|
||||
* @brief Read data from touch controller (mandatory)
|
||||
*
|
||||
* @note This function is usually blocking.
|
||||
*
|
||||
* @param tp: Touch handler
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success, otherwise returns ESP_ERR_xxx
|
||||
*/
|
||||
esp_err_t (*read_data)(esp_lcd_touch_handle_t tp);
|
||||
|
||||
/**
|
||||
* @brief Get coordinates from touch controller (mandatory)
|
||||
*
|
||||
* @param tp: Touch handler
|
||||
* @param x: Array of X coordinates
|
||||
* @param y: Array of Y coordinates
|
||||
* @param strength: Array of strengths
|
||||
* @param point_num: Count of points touched (equals with count of items in x and y array)
|
||||
* @param max_point_num: Maximum count of touched points to return (equals with max size of x and y array)
|
||||
*
|
||||
* @return
|
||||
* - Returns true, when touched and coordinates readed. Otherwise returns false.
|
||||
*/
|
||||
bool (*get_xy)(esp_lcd_touch_handle_t tp, uint16_t *x, uint16_t *y, uint16_t *strength, uint8_t *point_num, uint8_t max_point_num);
|
||||
|
||||
|
||||
#if (CONFIG_ESP_LCD_TOUCH_MAX_BUTTONS > 0)
|
||||
/**
|
||||
* @brief Get button state (optional)
|
||||
*
|
||||
* @param tp: Touch handler
|
||||
* @param n: Button index
|
||||
* @param state: Button state
|
||||
*
|
||||
* @return
|
||||
* - Returns true, when touched and coordinates readed. Otherwise returns false.
|
||||
*/
|
||||
esp_err_t (*get_button_state)(esp_lcd_touch_handle_t tp, uint8_t n, uint8_t *state);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Swap X and Y after read coordinates (optional)
|
||||
* If set, then not used SW swapping.
|
||||
*
|
||||
* @param tp: Touch handler
|
||||
* @param swap: Set swap value
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success, otherwise returns ESP_ERR_xxx
|
||||
*/
|
||||
esp_err_t (*set_swap_xy)(esp_lcd_touch_handle_t tp, bool swap);
|
||||
|
||||
/**
|
||||
* @brief Are X and Y coordinates swapped (optional)
|
||||
*
|
||||
* @param tp: Touch handler
|
||||
* @param swap: Get swap value
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success, otherwise returns ESP_ERR_xxx
|
||||
*/
|
||||
esp_err_t (*get_swap_xy)(esp_lcd_touch_handle_t tp, bool *swap);
|
||||
|
||||
/**
|
||||
* @brief Mirror X after read coordinates
|
||||
* If set, then not used SW mirroring.
|
||||
*
|
||||
* @param tp: Touch handler
|
||||
* @param mirror: Set X mirror value
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success, otherwise returns ESP_ERR_xxx
|
||||
*/
|
||||
esp_err_t (*set_mirror_x)(esp_lcd_touch_handle_t tp, bool mirror);
|
||||
|
||||
/**
|
||||
* @brief Is mirrored X (optional)
|
||||
*
|
||||
* @param tp: Touch handler
|
||||
* @param mirror: Get X mirror value
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success, otherwise returns ESP_ERR_xxx
|
||||
*/
|
||||
esp_err_t (*get_mirror_x)(esp_lcd_touch_handle_t tp, bool *mirror);
|
||||
|
||||
/**
|
||||
* @brief Mirror Y after read coordinates
|
||||
* If set, then not used SW mirroring.
|
||||
*
|
||||
* @param tp: Touch handler
|
||||
* @param mirror: Set Y mirror value
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success, otherwise returns ESP_ERR_xxx
|
||||
*/
|
||||
esp_err_t (*set_mirror_y)(esp_lcd_touch_handle_t tp, bool mirror);
|
||||
|
||||
/**
|
||||
* @brief Is mirrored Y (optional)
|
||||
*
|
||||
* @param tp: Touch handler
|
||||
* @param mirror: Get Y mirror value
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success, otherwise returns ESP_ERR_xxx
|
||||
*/
|
||||
esp_err_t (*get_mirror_y)(esp_lcd_touch_handle_t tp, bool *mirror);
|
||||
|
||||
/**
|
||||
* @brief Delete Touch
|
||||
*
|
||||
* @param tp: Touch handler
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success, otherwise returns ESP_ERR_xxx
|
||||
*/
|
||||
esp_err_t (*del)(esp_lcd_touch_handle_t tp);
|
||||
|
||||
/**
|
||||
* @brief Configuration structure
|
||||
*/
|
||||
esp_lcd_touch_config_t config;
|
||||
|
||||
/**
|
||||
* @brief Communication interface
|
||||
*/
|
||||
esp_lcd_panel_io_handle_t io;
|
||||
|
||||
/**
|
||||
* @brief Data structure
|
||||
*/
|
||||
esp_lcd_touch_data_t data;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Read data from touch controller
|
||||
*
|
||||
* @note This function is usually blocking.
|
||||
*
|
||||
* @param tp: Touch handler
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_INVALID_ARG parameter error
|
||||
* - ESP_FAIL sending command error, slave hasn't ACK the transfer
|
||||
* - ESP_ERR_INVALID_STATE I2C driver not installed or not in master mode
|
||||
* - ESP_ERR_TIMEOUT operation timeout because the bus is busy
|
||||
*/
|
||||
esp_err_t esp_lcd_touch_read_data(esp_lcd_touch_handle_t tp);
|
||||
|
||||
/**
|
||||
* @brief Read coordinates from touch controller
|
||||
*
|
||||
* @param tp: Touch handler
|
||||
* @param x: Array of X coordinates
|
||||
* @param y: Array of Y coordinates
|
||||
* @param strength: Array of the strengths (can be NULL)
|
||||
* @param point_num: Count of points touched (equals with count of items in x and y array)
|
||||
* @param max_point_num: Maximum count of touched points to return (equals with max size of x and y array)
|
||||
*
|
||||
* @return
|
||||
* - Returns true, when touched and coordinates readed. Otherwise returns false.
|
||||
*/
|
||||
bool esp_lcd_touch_get_coordinates(esp_lcd_touch_handle_t tp, uint16_t *x, uint16_t *y, uint16_t *strength, uint8_t *point_num, uint8_t max_point_num);
|
||||
|
||||
|
||||
#if (CONFIG_ESP_LCD_TOUCH_MAX_BUTTONS > 0)
|
||||
/**
|
||||
* @brief Get button state
|
||||
*
|
||||
* @param tp: Touch handler
|
||||
* @param n: Button index
|
||||
* @param state: Button state
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_NOT_SUPPORTED if this function is not supported by controller
|
||||
* - ESP_ERR_INVALID_ARG if bad button index
|
||||
*/
|
||||
esp_err_t esp_lcd_touch_get_button_state(esp_lcd_touch_handle_t tp, uint8_t n, uint8_t *state);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Swap X and Y after read coordinates
|
||||
*
|
||||
* @param tp: Touch handler
|
||||
* @param swap: Set swap value
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
*/
|
||||
esp_err_t esp_lcd_touch_set_swap_xy(esp_lcd_touch_handle_t tp, bool swap);
|
||||
|
||||
/**
|
||||
* @brief Are X and Y coordinates swapped
|
||||
*
|
||||
* @param tp: Touch handler
|
||||
* @param swap: Get swap value
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
*/
|
||||
esp_err_t esp_lcd_touch_get_swap_xy(esp_lcd_touch_handle_t tp, bool *swap);
|
||||
|
||||
/**
|
||||
* @brief Mirror X after read coordinates
|
||||
*
|
||||
* @param tp: Touch handler
|
||||
* @param mirror: Set X mirror value
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
*/
|
||||
esp_err_t esp_lcd_touch_set_mirror_x(esp_lcd_touch_handle_t tp, bool mirror);
|
||||
|
||||
/**
|
||||
* @brief Is mirrored X
|
||||
*
|
||||
* @param tp: Touch handler
|
||||
* @param mirror: Get X mirror value
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
*/
|
||||
esp_err_t esp_lcd_touch_get_mirror_x(esp_lcd_touch_handle_t tp, bool *mirror);
|
||||
|
||||
/**
|
||||
* @brief Mirror Y after read coordinates
|
||||
*
|
||||
* @param tp: Touch handler
|
||||
* @param mirror: Set Y mirror value
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
*/
|
||||
esp_err_t esp_lcd_touch_set_mirror_y(esp_lcd_touch_handle_t tp, bool mirror);
|
||||
|
||||
/**
|
||||
* @brief Is mirrored Y
|
||||
*
|
||||
* @param tp: Touch handler
|
||||
* @param mirror: Get Y mirror value
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
*/
|
||||
esp_err_t esp_lcd_touch_get_mirror_y(esp_lcd_touch_handle_t tp, bool *mirror);
|
||||
|
||||
/**
|
||||
* @brief Delete touch (free all allocated memory and restart HW)
|
||||
*
|
||||
* @param tp: Touch handler
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
*/
|
||||
esp_err_t esp_lcd_touch_del(esp_lcd_touch_handle_t tp);
|
||||
|
||||
/**
|
||||
* @brief Register user callback called after the touch interrupt occured
|
||||
*
|
||||
* @param tp: Touch handler
|
||||
* @param callback: Interrupt callback
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
*/
|
||||
esp_err_t esp_lcd_touch_register_interrupt_callback(esp_lcd_touch_handle_t tp, esp_lcd_touch_interrupt_callback_t callback);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
+270
@@ -0,0 +1,270 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "esp_system.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_check.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "driver/i2c.h"
|
||||
#include "esp_lcd_panel_io.h"
|
||||
#include "esp_lcd_touch.h"
|
||||
|
||||
static const char *TAG = "GT911";
|
||||
|
||||
/* GT911 registers */
|
||||
#define ESP_LCD_TOUCH_GT911_READ_XY_REG (0x814E)
|
||||
#define ESP_LCD_TOUCH_GT911_CONFIG_REG (0x8047)
|
||||
#define ESP_LCD_TOUCH_GT911_PRODUCT_ID_REG (0x8140)
|
||||
|
||||
/*******************************************************************************
|
||||
* Function definitions
|
||||
*******************************************************************************/
|
||||
static esp_err_t esp_lcd_touch_gt911_read_data(esp_lcd_touch_handle_t tp);
|
||||
static bool esp_lcd_touch_gt911_get_xy(esp_lcd_touch_handle_t tp, uint16_t *x, uint16_t *y, uint16_t *strength, uint8_t *point_num, uint8_t max_point_num);
|
||||
static esp_err_t esp_lcd_touch_gt911_del(esp_lcd_touch_handle_t tp);
|
||||
|
||||
/* I2C read/write */
|
||||
static esp_err_t touch_gt911_i2c_read(esp_lcd_touch_handle_t tp, uint16_t reg, uint8_t *data, uint8_t len);
|
||||
static esp_err_t touch_gt911_i2c_write(esp_lcd_touch_handle_t tp, uint16_t reg, uint8_t data);
|
||||
|
||||
/* GT911 reset */
|
||||
static esp_err_t touch_gt911_reset(esp_lcd_touch_handle_t tp);
|
||||
/* Read status and config register */
|
||||
static esp_err_t touch_gt911_read_cfg(esp_lcd_touch_handle_t tp);
|
||||
|
||||
/*******************************************************************************
|
||||
* Public API functions
|
||||
*******************************************************************************/
|
||||
|
||||
esp_err_t esp_lcd_touch_new_i2c_gt911(const esp_lcd_panel_io_handle_t io, const esp_lcd_touch_config_t *config, esp_lcd_touch_handle_t *out_touch)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
|
||||
assert(io != NULL);
|
||||
assert(config != NULL);
|
||||
assert(out_touch != NULL);
|
||||
|
||||
/* Prepare main structure */
|
||||
esp_lcd_touch_handle_t esp_lcd_touch_gt911 = heap_caps_calloc(1, sizeof(esp_lcd_touch_t), MALLOC_CAP_DEFAULT);
|
||||
ESP_GOTO_ON_FALSE(esp_lcd_touch_gt911, ESP_ERR_NO_MEM, err, TAG, "no mem for GT911 controller");
|
||||
|
||||
/* Communication interface */
|
||||
esp_lcd_touch_gt911->io = io;
|
||||
|
||||
/* Only supported callbacks are set */
|
||||
esp_lcd_touch_gt911->read_data = esp_lcd_touch_gt911_read_data;
|
||||
esp_lcd_touch_gt911->get_xy = esp_lcd_touch_gt911_get_xy;
|
||||
esp_lcd_touch_gt911->del = esp_lcd_touch_gt911_del;
|
||||
|
||||
/* Mutex */
|
||||
esp_lcd_touch_gt911->data.lock.owner = portMUX_FREE_VAL;
|
||||
|
||||
/* Save config */
|
||||
memcpy(&esp_lcd_touch_gt911->config, config, sizeof(esp_lcd_touch_config_t));
|
||||
|
||||
/* Prepare pin for touch interrupt */
|
||||
if (esp_lcd_touch_gt911->config.int_gpio_num != GPIO_NUM_NC) {
|
||||
const gpio_config_t int_gpio_config = {
|
||||
.mode = GPIO_MODE_INPUT,
|
||||
.intr_type = GPIO_INTR_NEGEDGE,
|
||||
.pin_bit_mask = BIT64(esp_lcd_touch_gt911->config.int_gpio_num)
|
||||
};
|
||||
ret = gpio_config(&int_gpio_config);
|
||||
ESP_GOTO_ON_ERROR(ret, err, TAG, "GPIO config failed");
|
||||
|
||||
/* Register interrupt callback */
|
||||
if (esp_lcd_touch_gt911->config.interrupt_callback) {
|
||||
esp_lcd_touch_register_interrupt_callback(esp_lcd_touch_gt911, esp_lcd_touch_gt911->config.interrupt_callback);
|
||||
}
|
||||
}
|
||||
|
||||
/* Prepare pin for touch controller reset */
|
||||
if (esp_lcd_touch_gt911->config.rst_gpio_num != GPIO_NUM_NC) {
|
||||
const gpio_config_t rst_gpio_config = {
|
||||
.mode = GPIO_MODE_OUTPUT,
|
||||
.pin_bit_mask = BIT64(esp_lcd_touch_gt911->config.rst_gpio_num)
|
||||
};
|
||||
ret = gpio_config(&rst_gpio_config);
|
||||
ESP_GOTO_ON_ERROR(ret, err, TAG, "GPIO config failed");
|
||||
}
|
||||
|
||||
/* Reset controller */
|
||||
ret = touch_gt911_reset(esp_lcd_touch_gt911);
|
||||
ESP_GOTO_ON_ERROR(ret, err, TAG, "GT911 reset failed");
|
||||
|
||||
/* Read status and config info */
|
||||
ret = touch_gt911_read_cfg(esp_lcd_touch_gt911);
|
||||
ESP_GOTO_ON_ERROR(ret, err, TAG, "GT911 init failed");
|
||||
|
||||
err:
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Error (0x%x)! Touch controller GT911 initialization failed!", ret);
|
||||
if (esp_lcd_touch_gt911) {
|
||||
esp_lcd_touch_gt911_del(esp_lcd_touch_gt911);
|
||||
}
|
||||
}
|
||||
|
||||
*out_touch = esp_lcd_touch_gt911;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t esp_lcd_touch_gt911_read_data(esp_lcd_touch_handle_t tp)
|
||||
{
|
||||
esp_err_t err;
|
||||
uint8_t buf[41];
|
||||
uint8_t touch_cnt = 0;
|
||||
uint8_t clear = 0;
|
||||
size_t i = 0;
|
||||
|
||||
assert(tp != NULL);
|
||||
|
||||
err = touch_gt911_i2c_read(tp, ESP_LCD_TOUCH_GT911_READ_XY_REG, buf, 1);
|
||||
ESP_RETURN_ON_ERROR(err, TAG, "I2C read error!");
|
||||
|
||||
/* Any touch data? */
|
||||
if ((buf[0] & 0x80) == 0x00) {
|
||||
touch_gt911_i2c_write(tp, ESP_LCD_TOUCH_GT911_READ_XY_REG, clear);
|
||||
} else {
|
||||
/* Count of touched points */
|
||||
touch_cnt = buf[0] & 0x0f;
|
||||
if (touch_cnt > 5 || touch_cnt == 0) {
|
||||
touch_gt911_i2c_write(tp, ESP_LCD_TOUCH_GT911_READ_XY_REG, clear);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/* Read all points */
|
||||
err = touch_gt911_i2c_read(tp, ESP_LCD_TOUCH_GT911_READ_XY_REG + 1, &buf[1], touch_cnt * 8);
|
||||
ESP_RETURN_ON_ERROR(err, TAG, "I2C read error!");
|
||||
|
||||
/* Clear all */
|
||||
err = touch_gt911_i2c_write(tp, ESP_LCD_TOUCH_GT911_READ_XY_REG, clear);
|
||||
ESP_RETURN_ON_ERROR(err, TAG, "I2C read error!");
|
||||
|
||||
portENTER_CRITICAL(&tp->data.lock);
|
||||
|
||||
/* Number of touched points */
|
||||
touch_cnt = (touch_cnt > CONFIG_ESP_LCD_TOUCH_MAX_POINTS ? CONFIG_ESP_LCD_TOUCH_MAX_POINTS : touch_cnt);
|
||||
tp->data.points = touch_cnt;
|
||||
|
||||
/* Fill all coordinates */
|
||||
for (i = 0; i < touch_cnt; i++) {
|
||||
tp->data.coords[i].x = ((uint16_t)buf[(i * 8) + 3] << 8) + buf[(i * 8) + 2];
|
||||
tp->data.coords[i].y = (((uint16_t)buf[(i * 8) + 5] << 8) + buf[(i * 8) + 4]);
|
||||
tp->data.coords[i].strength = (((uint16_t)buf[(i * 8) + 7] << 8) + buf[(i * 8) + 6]);
|
||||
}
|
||||
|
||||
portEXIT_CRITICAL(&tp->data.lock);
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static bool esp_lcd_touch_gt911_get_xy(esp_lcd_touch_handle_t tp, uint16_t *x, uint16_t *y, uint16_t *strength, uint8_t *point_num, uint8_t max_point_num)
|
||||
{
|
||||
assert(tp != NULL);
|
||||
assert(x != NULL);
|
||||
assert(y != NULL);
|
||||
assert(point_num != NULL);
|
||||
assert(max_point_num > 0);
|
||||
|
||||
portENTER_CRITICAL(&tp->data.lock);
|
||||
|
||||
/* Count of points */
|
||||
*point_num = (tp->data.points > max_point_num ? max_point_num : tp->data.points);
|
||||
|
||||
for (size_t i = 0; i < *point_num; i++) {
|
||||
x[i] = tp->data.coords[i].x;
|
||||
y[i] = tp->data.coords[i].y;
|
||||
if (strength) {
|
||||
strength[i] = tp->data.coords[i].strength;
|
||||
}
|
||||
}
|
||||
|
||||
/* Invalidate */
|
||||
tp->data.points = 0;
|
||||
|
||||
portEXIT_CRITICAL(&tp->data.lock);
|
||||
|
||||
return (*point_num > 0);
|
||||
}
|
||||
|
||||
static esp_err_t esp_lcd_touch_gt911_del(esp_lcd_touch_handle_t tp)
|
||||
{
|
||||
assert(tp != NULL);
|
||||
|
||||
/* Reset GPIO pin settings */
|
||||
if (tp->config.int_gpio_num != GPIO_NUM_NC) {
|
||||
gpio_reset_pin(tp->config.int_gpio_num);
|
||||
}
|
||||
|
||||
/* Reset GPIO pin settings */
|
||||
if (tp->config.rst_gpio_num != GPIO_NUM_NC) {
|
||||
gpio_reset_pin(tp->config.rst_gpio_num);
|
||||
}
|
||||
|
||||
free(tp);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* Private API function
|
||||
*******************************************************************************/
|
||||
|
||||
/* Reset controller */
|
||||
static esp_err_t touch_gt911_reset(esp_lcd_touch_handle_t tp)
|
||||
{
|
||||
assert(tp != NULL);
|
||||
|
||||
if (tp->config.rst_gpio_num != GPIO_NUM_NC) {
|
||||
ESP_RETURN_ON_ERROR(gpio_set_level(tp->config.rst_gpio_num, tp->config.levels.reset), TAG, "GPIO set level error!");
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
ESP_RETURN_ON_ERROR(gpio_set_level(tp->config.rst_gpio_num, !tp->config.levels.reset), TAG, "GPIO set level error!");
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t touch_gt911_read_cfg(esp_lcd_touch_handle_t tp)
|
||||
{
|
||||
uint8_t buf[4];
|
||||
|
||||
assert(tp != NULL);
|
||||
|
||||
ESP_RETURN_ON_ERROR(touch_gt911_i2c_read(tp, ESP_LCD_TOUCH_GT911_PRODUCT_ID_REG, (uint8_t *)&buf[0], 3), TAG, "GT911 read error!");
|
||||
ESP_RETURN_ON_ERROR(touch_gt911_i2c_read(tp, ESP_LCD_TOUCH_GT911_CONFIG_REG, (uint8_t *)&buf[3], 1), TAG, "GT911 read error!");
|
||||
|
||||
ESP_LOGI(TAG, "TouchPad_ID:0x%02x,0x%02x,0x%02x", buf[0], buf[1], buf[2]);
|
||||
ESP_LOGI(TAG, "TouchPad_Config_Version:%d", buf[3]);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t touch_gt911_i2c_read(esp_lcd_touch_handle_t tp, uint16_t reg, uint8_t *data, uint8_t len)
|
||||
{
|
||||
assert(tp != NULL);
|
||||
assert(data != NULL);
|
||||
|
||||
/* Read data */
|
||||
return esp_lcd_panel_io_rx_param(tp->io, reg, data, len);
|
||||
}
|
||||
|
||||
static esp_err_t touch_gt911_i2c_write(esp_lcd_touch_handle_t tp, uint16_t reg, uint8_t data)
|
||||
{
|
||||
assert(tp != NULL);
|
||||
|
||||
// *INDENT-OFF*
|
||||
/* Write data */
|
||||
return esp_lcd_panel_io_tx_param(tp->io, reg, (uint8_t[]){data}, 1);
|
||||
// *INDENT-ON*
|
||||
}
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* @brief ESP LCD touch: GT911
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "esp_lcd_touch.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Create a new GT911 touch driver
|
||||
*
|
||||
* @note The I2C communication should be initialized before use this function.
|
||||
*
|
||||
* @param io LCD/Touch panel IO handle
|
||||
* @param config: Touch configuration
|
||||
* @param out_touch: Touch instance handle
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_NO_MEM if there is no memory for allocating main structure
|
||||
*/
|
||||
esp_err_t esp_lcd_touch_new_i2c_gt911(const esp_lcd_panel_io_handle_t io, const esp_lcd_touch_config_t *config, esp_lcd_touch_handle_t *out_touch);
|
||||
|
||||
/**
|
||||
* @brief I2C address of the GT911 controller
|
||||
*
|
||||
*/
|
||||
#define ESP_LCD_TOUCH_IO_I2C_GT911_ADDRESS (0x5D)
|
||||
|
||||
/**
|
||||
* @brief Touch IO configuration structure
|
||||
*
|
||||
*/
|
||||
#define ESP_LCD_TOUCH_IO_I2C_GT911_CONFIG() \
|
||||
{ \
|
||||
.dev_addr = ESP_LCD_TOUCH_IO_I2C_GT911_ADDRESS, \
|
||||
.control_phase_bytes = 1, \
|
||||
.dc_bit_offset = 0, \
|
||||
.lcd_cmd_bits = 16, \
|
||||
.flags = \
|
||||
{ \
|
||||
.disable_control_phase = 1, \
|
||||
} \
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
+103
@@ -0,0 +1,103 @@
|
||||
#include "sdkconfig.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_log.h"
|
||||
#include "driver/i2c_master.h"
|
||||
#include "esp_lcd_touch_gt911.h"
|
||||
#include "gt911_touch.h"
|
||||
|
||||
#define CONFIG_LCD_HRES 480
|
||||
#define CONFIG_LCD_VRES 800
|
||||
|
||||
static const char *TAG = "example";
|
||||
|
||||
esp_lcd_touch_handle_t tp;
|
||||
esp_lcd_panel_io_handle_t tp_io_handle;
|
||||
|
||||
uint16_t touch_strength[1];
|
||||
uint8_t touch_cnt = 0;
|
||||
|
||||
gt911_touch::gt911_touch(int8_t sda_pin, int8_t scl_pin, int8_t rst_pin, int8_t int_pin)
|
||||
{
|
||||
_sda = sda_pin;
|
||||
_scl = scl_pin;
|
||||
_rst = rst_pin;
|
||||
_int = int_pin;
|
||||
}
|
||||
|
||||
void gt911_touch::begin()
|
||||
{
|
||||
// i2c_config_t i2c_conf = {
|
||||
// .mode = I2C_MODE_MASTER,
|
||||
// .sda_io_num = (gpio_num_t)_sda,
|
||||
// .scl_io_num = (gpio_num_t)_scl,
|
||||
// .sda_pullup_en = GPIO_PULLUP_ENABLE,
|
||||
// .scl_pullup_en = GPIO_PULLUP_ENABLE,
|
||||
// };
|
||||
// i2c_conf.master.clk_speed = 400000; // 400kHz
|
||||
|
||||
|
||||
// ESP_ERROR_CHECK(i2c_param_config(I2C_NUM_0, &i2c_conf));
|
||||
// ESP_ERROR_CHECK(i2c_driver_install(I2C_NUM_0, i2c_conf.mode, 0, 0, 0));
|
||||
i2c_master_bus_handle_t i2c_handle = NULL;
|
||||
i2c_master_get_bus_handle(1,&i2c_handle);
|
||||
|
||||
esp_lcd_panel_io_i2c_config_t tp_io_config = ESP_LCD_TOUCH_IO_I2C_GT911_CONFIG();
|
||||
tp_io_config.scl_speed_hz = 100000;
|
||||
ESP_LOGI(TAG, "Initialize touch IO (I2C)");
|
||||
esp_lcd_new_panel_io_i2c(i2c_handle, &tp_io_config, &tp_io_handle);
|
||||
|
||||
esp_lcd_touch_config_t tp_cfg = {
|
||||
.x_max = CONFIG_LCD_HRES,
|
||||
.y_max = CONFIG_LCD_VRES,
|
||||
.rst_gpio_num = (gpio_num_t)_rst,
|
||||
.int_gpio_num = (gpio_num_t)_int,
|
||||
.levels = {
|
||||
.reset = 0,
|
||||
.interrupt = 0,
|
||||
},
|
||||
.flags = {
|
||||
.swap_xy = 0,
|
||||
.mirror_x = 0,
|
||||
.mirror_y = 0,
|
||||
},
|
||||
};
|
||||
|
||||
ESP_LOGI(TAG, "Initialize touch controller gt911");
|
||||
ESP_ERROR_CHECK(esp_lcd_touch_new_i2c_gt911(tp_io_handle, &tp_cfg, &tp));
|
||||
}
|
||||
|
||||
bool gt911_touch::getTouch(uint16_t *x, uint16_t *y)
|
||||
{
|
||||
esp_lcd_touch_read_data(tp);
|
||||
bool touchpad_pressed = esp_lcd_touch_get_coordinates(tp, x, y, touch_strength, &touch_cnt, 1);
|
||||
|
||||
return touchpad_pressed;
|
||||
}
|
||||
|
||||
void gt911_touch::set_rotation(uint8_t r){
|
||||
switch(r){
|
||||
case 0:
|
||||
esp_lcd_touch_set_swap_xy(tp, false);
|
||||
esp_lcd_touch_set_mirror_x(tp, false);
|
||||
esp_lcd_touch_set_mirror_y(tp, false);
|
||||
break;
|
||||
case 1:
|
||||
esp_lcd_touch_set_swap_xy(tp, false);
|
||||
esp_lcd_touch_set_mirror_x(tp, true);
|
||||
esp_lcd_touch_set_mirror_y(tp, true);
|
||||
break;
|
||||
case 2:
|
||||
esp_lcd_touch_set_swap_xy(tp, false);
|
||||
esp_lcd_touch_set_mirror_x(tp, false);
|
||||
esp_lcd_touch_set_mirror_y(tp, false);
|
||||
break;
|
||||
case 3:
|
||||
esp_lcd_touch_set_swap_xy(tp, false);
|
||||
esp_lcd_touch_set_mirror_x(tp, true);
|
||||
esp_lcd_touch_set_mirror_y(tp, true);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
#ifndef _GT911_TOUCH_H
|
||||
#define _GT911_TOUCH_H
|
||||
#include <stdio.h>
|
||||
|
||||
class gt911_touch
|
||||
{
|
||||
public:
|
||||
gt911_touch(int8_t sda_pin, int8_t scl_pin, int8_t rst_pin = -1, int8_t int_pin = -1);
|
||||
|
||||
void begin();
|
||||
bool getTouch(uint16_t *x, uint16_t *y);
|
||||
void set_rotation(uint8_t r);
|
||||
|
||||
private:
|
||||
int8_t _sda, _scl, _rst, _int;
|
||||
};
|
||||
|
||||
#endif
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 53 KiB |
+8
@@ -0,0 +1,8 @@
|
||||
#此样例使用的屏幕是微雪的10.1寸屏幕,具体型号位:10.1-DSI-TOUCH-A
|
||||
# 请使用arduino_esp32_v3.2.1版本
|
||||
|
||||
#lvgl v9.2.2
|
||||
|
||||
|
||||
# 要将lvgl文件夹中的demos文件夹移动到同目录下的src文件夹中
|
||||
|
||||
+1127
File diff suppressed because it is too large
Load Diff
+708
@@ -0,0 +1,708 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "soc/soc_caps.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "freertos/task.h"
|
||||
#include "esp_heap_caps.h"
|
||||
#include "esp_lcd_panel_ops.h"
|
||||
#if SOC_LCDCAM_RGB_LCD_SUPPORTED
|
||||
#include "esp_lcd_panel_rgb.h"
|
||||
#endif
|
||||
#if SOC_MIPI_DSI_SUPPORTED
|
||||
#include "esp_lcd_mipi_dsi.h"
|
||||
#endif
|
||||
#include "src/touch/esp_lcd_touch.h"
|
||||
#include "esp_timer.h"
|
||||
#include "esp_log.h"
|
||||
#if CONFIG_IDF_TARGET_ESP32P4
|
||||
#include "esp_private/esp_cache_private.h"
|
||||
#include "driver/ppa.h"
|
||||
#endif
|
||||
#include "lvgl.h"
|
||||
#include "lvgl_private.h"
|
||||
#include "lvgl_port_v9.h"
|
||||
|
||||
#define ALIGN_UP_BY(num, align) (((num) + ((align) - 1)) & ~((align) - 1))
|
||||
#define BLOCK_SIZE_SMALL (32)
|
||||
#define BLOCK_SIZE_LARGE (256)
|
||||
|
||||
static const char *TAG = "lv_port";
|
||||
|
||||
typedef struct {
|
||||
esp_lcd_panel_handle_t lcd_handle;
|
||||
esp_lcd_touch_handle_t tp_handle;
|
||||
bool is_init;
|
||||
} lvgl_port_task_param_t;
|
||||
|
||||
typedef esp_err_t (*get_lcd_frame_buffer_cb_t)(esp_lcd_panel_handle_t panel, uint32_t fb_num, void **fb0, ...);
|
||||
|
||||
#if LVGL_PORT_PPA_ROTATION_ENABLE
|
||||
static ppa_client_handle_t ppa_srm_handle = NULL;
|
||||
static size_t data_cache_line_size = 0;
|
||||
#endif
|
||||
|
||||
static SemaphoreHandle_t lvgl_mux; // LVGL mutex
|
||||
static TaskHandle_t lvgl_task_handle = NULL;
|
||||
static lvgl_port_interface_t lvgl_port_interface = LVGL_PORT_INTERFACE_RGB;
|
||||
|
||||
#if LVGL_PORT_AVOID_TEAR_ENABLE
|
||||
static get_lcd_frame_buffer_cb_t lvgl_get_lcd_frame_buffer = NULL;
|
||||
#endif
|
||||
|
||||
#if EXAMPLE_LVGL_PORT_ROTATION_DEGREE != 0
|
||||
static void *get_next_frame_buffer(esp_lcd_panel_handle_t panel_handle)
|
||||
{
|
||||
static void *next_fb = NULL;
|
||||
static void *fb[2] = { NULL };
|
||||
if (next_fb == NULL) {
|
||||
ESP_ERROR_CHECK(lvgl_get_lcd_frame_buffer(panel_handle, 2, &fb[0], &fb[1]));
|
||||
next_fb = fb[1];
|
||||
} else {
|
||||
next_fb = (next_fb == fb[0]) ? fb[1] : fb[0];
|
||||
}
|
||||
return next_fb;
|
||||
}
|
||||
|
||||
#if !LVGL_PORT_PPA_ROTATION_ENABLE
|
||||
static void rotate_image(const void *src, void *dst, int width, int height, int rotation, int bpp)
|
||||
{
|
||||
int bytes_per_pixel = bpp / 8;
|
||||
int block_w = rotation == 90 || rotation == 270 ? BLOCK_SIZE_SMALL : BLOCK_SIZE_LARGE;
|
||||
int block_h = rotation == 90 || rotation == 270 ? BLOCK_SIZE_LARGE : BLOCK_SIZE_SMALL;
|
||||
|
||||
for (int i = 0; i < height; i += block_h) {
|
||||
int max_height = i + block_h > height ? height : i + block_h;
|
||||
|
||||
for (int j = 0; j < width; j += block_w) {
|
||||
int max_width = j + block_w > width ? width : j + block_w;
|
||||
|
||||
for (int x = i; x < max_height; x++) {
|
||||
for (int y = j; y < max_width; y++) {
|
||||
void *src_pixel = (uint8_t *)src + (x * width + y) * bytes_per_pixel;
|
||||
void *dst_pixel;
|
||||
|
||||
switch (rotation) {
|
||||
case 270:
|
||||
dst_pixel = (uint8_t *)dst + ((width - 1 - y) * height + x) * bytes_per_pixel;
|
||||
break;
|
||||
case 180:
|
||||
dst_pixel = (uint8_t *)dst + ((height - 1 - x) * width + (width - 1 - y)) * bytes_per_pixel;
|
||||
break;
|
||||
case 90:
|
||||
dst_pixel = (uint8_t *)dst + (y * height + (height - 1 - x)) * bytes_per_pixel;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
if (bpp == 16) {
|
||||
*(uint16_t *)dst_pixel = *(uint16_t *)src_pixel;
|
||||
} else if (bpp == 24) {
|
||||
((uint8_t *)dst_pixel)[0] = ((uint8_t *)src_pixel)[0];
|
||||
((uint8_t *)dst_pixel)[1] = ((uint8_t *)src_pixel)[1];
|
||||
((uint8_t *)dst_pixel)[2] = ((uint8_t *)src_pixel)[2];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
IRAM_ATTR static void rotate_copy_pixel(const uint16_t *from, uint16_t *to, uint16_t x_start, uint16_t y_start, uint16_t x_end, uint16_t y_end, uint16_t w, uint16_t h, uint16_t rotation)
|
||||
{
|
||||
#if LVGL_PORT_PPA_ROTATION_ENABLE
|
||||
ppa_srm_rotation_angle_t ppa_rotation;
|
||||
int x_offset = 0, y_offset = 0;
|
||||
|
||||
// Determine rotation settings once and reuse
|
||||
switch (rotation) {
|
||||
case 90:
|
||||
ppa_rotation = PPA_SRM_ROTATION_ANGLE_270;
|
||||
x_offset = h - y_end - 1;
|
||||
y_offset = x_start;
|
||||
break;
|
||||
case 180:
|
||||
ppa_rotation = PPA_SRM_ROTATION_ANGLE_180;
|
||||
x_offset = w - x_end - 1;
|
||||
y_offset = h - y_end - 1;
|
||||
break;
|
||||
case 270:
|
||||
ppa_rotation = PPA_SRM_ROTATION_ANGLE_90;
|
||||
x_offset = y_start;
|
||||
y_offset = w - x_end - 1;
|
||||
break;
|
||||
default:
|
||||
ppa_rotation = PPA_SRM_ROTATION_ANGLE_0;
|
||||
break;
|
||||
}
|
||||
|
||||
// Fill operation config for PPA rotation, without recalculating each time
|
||||
ppa_srm_oper_config_t oper_config = {
|
||||
.in.buffer = from,
|
||||
.in.pic_w = w,
|
||||
.in.pic_h = h,
|
||||
.in.block_w = x_end - x_start + 1,
|
||||
.in.block_h = y_end - y_start + 1,
|
||||
.in.block_offset_x = x_start,
|
||||
.in.block_offset_y = y_start,
|
||||
.in.srm_cm = (LV_COLOR_DEPTH == 24) ? PPA_SRM_COLOR_MODE_RGB888 : PPA_SRM_COLOR_MODE_RGB565,
|
||||
|
||||
.out.buffer = to,
|
||||
.out.buffer_size = ALIGN_UP_BY(sizeof(lv_color_t) * w * h, data_cache_line_size),
|
||||
.out.pic_w = (ppa_rotation == PPA_SRM_ROTATION_ANGLE_90 || ppa_rotation == PPA_SRM_ROTATION_ANGLE_270) ? h : w,
|
||||
.out.pic_h = (ppa_rotation == PPA_SRM_ROTATION_ANGLE_90 || ppa_rotation == PPA_SRM_ROTATION_ANGLE_270) ? w : h,
|
||||
.out.block_offset_x = x_offset,
|
||||
.out.block_offset_y = y_offset,
|
||||
.out.srm_cm = (LV_COLOR_DEPTH == 24) ? PPA_SRM_COLOR_MODE_RGB888 : PPA_SRM_COLOR_MODE_RGB565,
|
||||
|
||||
.rotation_angle = ppa_rotation,
|
||||
.scale_x = 1.0,
|
||||
.scale_y = 1.0,
|
||||
.rgb_swap = 0,
|
||||
.byte_swap = 0,
|
||||
.mode = PPA_TRANS_MODE_BLOCKING,
|
||||
};
|
||||
|
||||
ESP_ERROR_CHECK(ppa_do_scale_rotate_mirror(ppa_srm_handle, &oper_config));
|
||||
|
||||
#else
|
||||
// Fallback: optimized transpose for non-PPA systems
|
||||
rotate_image(from, to, w, h, rotation, LV_COLOR_DEPTH);
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif /* EXAMPLE_LVGL_PORT_ROTATION_DEGREE */
|
||||
|
||||
#if LVGL_PORT_AVOID_TEAR_ENABLE
|
||||
|
||||
static void switch_lcd_frame_buffer_to(esp_lcd_panel_handle_t panel_handle, void *fb)
|
||||
{
|
||||
esp_lcd_panel_draw_bitmap(panel_handle, 0, 0, LVGL_PORT_H_RES, LVGL_PORT_V_RES, fb);
|
||||
}
|
||||
|
||||
#if LVGL_PORT_DIRECT_MODE
|
||||
#if EXAMPLE_LVGL_PORT_ROTATION_DEGREE != 0
|
||||
typedef struct {
|
||||
uint16_t inv_p;
|
||||
uint8_t inv_area_joined[LV_INV_BUF_SIZE];
|
||||
lv_area_t inv_areas[LV_INV_BUF_SIZE];
|
||||
} lv_port_dirty_area_t;
|
||||
|
||||
typedef enum {
|
||||
FLUSH_STATUS_PART,
|
||||
FLUSH_STATUS_FULL
|
||||
} lv_port_flush_status_t;
|
||||
|
||||
typedef enum {
|
||||
FLUSH_PROBE_PART_COPY,
|
||||
FLUSH_PROBE_SKIP_COPY,
|
||||
FLUSH_PROBE_FULL_COPY,
|
||||
} lv_port_flush_probe_t;
|
||||
|
||||
static lv_port_dirty_area_t dirty_area;
|
||||
|
||||
static void flush_dirty_save(lv_port_dirty_area_t *dirty_area)
|
||||
{
|
||||
lv_disp_t *disp = lv_refr_get_disp_refreshing();
|
||||
dirty_area->inv_p = disp->inv_p;
|
||||
for (int i = 0; i < disp->inv_p; i++) {
|
||||
dirty_area->inv_area_joined[i] = disp->inv_area_joined[i];
|
||||
dirty_area->inv_areas[i] = disp->inv_areas[i];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Probe dirty area to copy
|
||||
*
|
||||
* @note This function is used to avoid tearing effect, and only work with LVGL direct-mode.
|
||||
*
|
||||
*/
|
||||
static lv_port_flush_probe_t flush_copy_probe(lv_display_t *disp)
|
||||
{
|
||||
static lv_port_flush_status_t prev_status = FLUSH_STATUS_PART;
|
||||
lv_port_flush_status_t cur_status;
|
||||
lv_port_flush_probe_t probe_result;
|
||||
lv_disp_t *disp_refr = lv_refr_get_disp_refreshing();
|
||||
|
||||
uint32_t flush_ver = 0;
|
||||
uint32_t flush_hor = 0;
|
||||
for (int i = 0; i < disp_refr->inv_p; i++) {
|
||||
if (disp_refr->inv_area_joined[i] == 0) {
|
||||
flush_ver = (disp_refr->inv_areas[i].y2 + 1 - disp_refr->inv_areas[i].y1);
|
||||
flush_hor = (disp_refr->inv_areas[i].x2 + 1 - disp_refr->inv_areas[i].x1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* Check if the current full screen refreshes */
|
||||
cur_status = ((flush_ver == disp->ver_res) && (flush_hor == disp->hor_res)) ? (FLUSH_STATUS_FULL) : (FLUSH_STATUS_PART);
|
||||
|
||||
if (prev_status == FLUSH_STATUS_FULL) {
|
||||
if ((cur_status == FLUSH_STATUS_PART)) {
|
||||
probe_result = FLUSH_PROBE_FULL_COPY;
|
||||
} else {
|
||||
probe_result = FLUSH_PROBE_SKIP_COPY;
|
||||
}
|
||||
} else {
|
||||
probe_result = FLUSH_PROBE_PART_COPY;
|
||||
}
|
||||
prev_status = cur_status;
|
||||
|
||||
return probe_result;
|
||||
}
|
||||
|
||||
static inline void *flush_get_next_buf(void *panel_handle)
|
||||
{
|
||||
return get_next_frame_buffer(panel_handle);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Copy dirty area
|
||||
*
|
||||
* @note This function is used to avoid tearing effect, and only work with LVGL direct-mode.
|
||||
*
|
||||
*/
|
||||
static void flush_dirty_copy(void *dst, void *src, lv_port_dirty_area_t *dirty_area)
|
||||
{
|
||||
lv_coord_t x_start, x_end, y_start, y_end;
|
||||
for (int i = 0; i < dirty_area->inv_p; i++) {
|
||||
/* Refresh the unjoined areas*/
|
||||
if (dirty_area->inv_area_joined[i] == 0) {
|
||||
x_start = dirty_area->inv_areas[i].x1;
|
||||
x_end = dirty_area->inv_areas[i].x2;
|
||||
y_start = dirty_area->inv_areas[i].y1;
|
||||
y_end = dirty_area->inv_areas[i].y2;
|
||||
|
||||
rotate_copy_pixel(src, dst, x_start, y_start, x_end, y_end, LV_HOR_RES, LV_VER_RES, EXAMPLE_LVGL_PORT_ROTATION_DEGREE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void flush_callback(lv_display_t *disp, const lv_area_t *area, uint8_t *color_map)
|
||||
{
|
||||
esp_lcd_panel_handle_t panel_handle = (esp_lcd_panel_handle_t)lv_display_get_user_data(disp);
|
||||
const int offsetx1 = area->x1;
|
||||
const int offsetx2 = area->x2;
|
||||
const int offsety1 = area->y1;
|
||||
const int offsety2 = area->y2;
|
||||
void *next_fb = NULL;
|
||||
lv_port_flush_probe_t probe_result = FLUSH_PROBE_PART_COPY;
|
||||
|
||||
/* Action after last area refresh */
|
||||
if (lv_disp_flush_is_last(disp)) {
|
||||
/* Check if the `full_refresh` flag has been triggered */
|
||||
if (disp->render_mode == LV_DISPLAY_RENDER_MODE_FULL) {
|
||||
/* Reset flag */
|
||||
disp->render_mode = LV_DISPLAY_RENDER_MODE_DIRECT;
|
||||
|
||||
// Rotate and copy data from the whole screen LVGL's buffer to the next frame buffer
|
||||
next_fb = flush_get_next_buf(panel_handle);
|
||||
rotate_copy_pixel((uint16_t *)color_map, next_fb, offsetx1, offsety1, offsetx2, offsety2, LV_HOR_RES, LV_VER_RES, EXAMPLE_LVGL_PORT_ROTATION_DEGREE);
|
||||
|
||||
/* Switch the current LCD frame buffer to `next_fb` */
|
||||
switch_lcd_frame_buffer_to(panel_handle, next_fb);
|
||||
|
||||
/* Waiting for the current frame buffer to complete transmission */
|
||||
ulTaskNotifyValueClear(NULL, ULONG_MAX);
|
||||
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
|
||||
|
||||
/* Synchronously update the dirty area for another frame buffer */
|
||||
flush_dirty_copy(flush_get_next_buf(panel_handle), color_map, &dirty_area);
|
||||
flush_get_next_buf(panel_handle);
|
||||
} else {
|
||||
/* Probe the copy method for the current dirty area */
|
||||
probe_result = flush_copy_probe(disp);
|
||||
|
||||
if (probe_result == FLUSH_PROBE_FULL_COPY) {
|
||||
/* Save current dirty area for next frame buffer */
|
||||
flush_dirty_save(&dirty_area);
|
||||
|
||||
/* Set LVGL full-refresh flag and set flush ready in advance */
|
||||
disp->render_mode = LV_DISPLAY_RENDER_MODE_FULL;
|
||||
disp->rendering_in_progress = false;
|
||||
lv_disp_flush_ready(disp);
|
||||
|
||||
/* Force to refresh whole screen, and will invoke `flush_callback` recursively */
|
||||
lv_refr_now(lv_refr_get_disp_refreshing());
|
||||
} else {
|
||||
/* Update current dirty area for next frame buffer */
|
||||
next_fb = flush_get_next_buf(panel_handle);
|
||||
flush_dirty_save(&dirty_area);
|
||||
flush_dirty_copy(next_fb, color_map, &dirty_area);
|
||||
|
||||
/* Switch the current LCD frame buffer to `next_fb` */
|
||||
switch_lcd_frame_buffer_to(panel_handle, next_fb);
|
||||
|
||||
/* Waiting for the current frame buffer to complete transmission */
|
||||
ulTaskNotifyValueClear(NULL, ULONG_MAX);
|
||||
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
|
||||
|
||||
if (probe_result == FLUSH_PROBE_PART_COPY) {
|
||||
/* Synchronously update the dirty area for another frame buffer */
|
||||
flush_dirty_save(&dirty_area);
|
||||
flush_dirty_copy(flush_get_next_buf(panel_handle), color_map, &dirty_area);
|
||||
flush_get_next_buf(panel_handle);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lv_disp_flush_ready(disp);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static void flush_callback(lv_display_t *disp, const lv_area_t *area, uint8_t *color_map)
|
||||
{
|
||||
esp_lcd_panel_handle_t panel_handle = (esp_lcd_panel_handle_t)lv_display_get_user_data(disp);
|
||||
|
||||
/* Action after last area refresh */
|
||||
if (lv_disp_flush_is_last(disp)) {
|
||||
/* Switch the current LCD frame buffer to `color_map` */
|
||||
switch_lcd_frame_buffer_to(panel_handle, color_map);
|
||||
|
||||
/* Waiting for the last frame buffer to complete transmission */
|
||||
ulTaskNotifyValueClear(NULL, ULONG_MAX);
|
||||
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
|
||||
}
|
||||
|
||||
lv_disp_flush_ready(disp);
|
||||
}
|
||||
#endif /* EXAMPLE_LVGL_PORT_ROTATION_DEGREE */
|
||||
|
||||
#elif LVGL_PORT_FULL_REFRESH && LVGL_PORT_LCD_BUFFER_NUMS == 2
|
||||
|
||||
static void flush_callback(lv_display_t *disp, const lv_area_t *area, uint8_t *color_map)
|
||||
{
|
||||
esp_lcd_panel_handle_t panel_handle = (esp_lcd_panel_handle_t)lv_display_get_user_data(disp);
|
||||
|
||||
/* Switch the current LCD frame buffer to `color_map` */
|
||||
switch_lcd_frame_buffer_to(panel_handle, color_map);
|
||||
|
||||
/* Waiting for the last frame buffer to complete transmission */
|
||||
ulTaskNotifyValueClear(NULL, ULONG_MAX);
|
||||
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
|
||||
|
||||
lv_disp_flush_ready(disp);
|
||||
}
|
||||
|
||||
#elif LVGL_PORT_FULL_REFRESH && LVGL_PORT_LCD_BUFFER_NUMS == 3
|
||||
|
||||
#if EXAMPLE_LVGL_PORT_ROTATION_DEGREE == 0
|
||||
static void *lvgl_port_rgb_last_buf = NULL;
|
||||
static void *lvgl_port_rgb_next_buf = NULL;
|
||||
static void *lvgl_port_flush_next_buf = NULL;
|
||||
#endif
|
||||
|
||||
void flush_callback(lv_display_t *disp, const lv_area_t *area, uint8_t *color_map)
|
||||
{
|
||||
esp_lcd_panel_handle_t panel_handle = (esp_lcd_panel_handle_t)lv_display_get_user_data(disp);
|
||||
|
||||
#if EXAMPLE_LVGL_PORT_ROTATION_DEGREE != 0
|
||||
const int offsetx1 = area->x1;
|
||||
const int offsetx2 = area->x2;
|
||||
const int offsety1 = area->y1;
|
||||
const int offsety2 = area->y2;
|
||||
void *next_fb = get_next_frame_buffer(panel_handle);
|
||||
|
||||
/* Rotate and copy dirty area from the current LVGL's buffer to the next LCD frame buffer */
|
||||
rotate_copy_pixel((uint16_t *)color_map, next_fb, offsetx1, offsety1, offsetx2, offsety2, LV_HOR_RES, LV_VER_RES, EXAMPLE_LVGL_PORT_ROTATION_DEGREE);
|
||||
|
||||
/* Switch the current LCD frame buffer to `next_fb` */
|
||||
switch_lcd_frame_buffer_to(panel_handle, next_fb);
|
||||
#else
|
||||
if (disp->buf_act == disp->buf_1) {
|
||||
disp->buf_2->data = lvgl_port_flush_next_buf;
|
||||
} else {
|
||||
disp->buf_1->data = lvgl_port_flush_next_buf;
|
||||
}
|
||||
lvgl_port_flush_next_buf = color_map;
|
||||
|
||||
/* Switch the current LCD frame buffer to `color_map` */
|
||||
switch_lcd_frame_buffer_to(panel_handle, color_map);
|
||||
|
||||
lvgl_port_rgb_next_buf = color_map;
|
||||
#endif
|
||||
|
||||
lv_disp_flush_ready(disp);
|
||||
}
|
||||
#endif
|
||||
|
||||
#else
|
||||
|
||||
void flush_callback(lv_display_t *disp, const lv_area_t *area, uint8_t *color_map)
|
||||
{
|
||||
esp_lcd_panel_handle_t panel_handle = (esp_lcd_panel_handle_t)lv_display_get_user_data(disp);
|
||||
const int offsetx1 = area->x1;
|
||||
const int offsetx2 = area->x2;
|
||||
const int offsety1 = area->y1;
|
||||
const int offsety2 = area->y2;
|
||||
|
||||
/* Just copy data from the color map to the LCD frame buffer */
|
||||
esp_lcd_panel_draw_bitmap(panel_handle, offsetx1, offsety1, offsetx2 + 1, offsety2 + 1, color_map);
|
||||
|
||||
if (lvgl_port_interface != LVGL_PORT_INTERFACE_MIPI_DSI_DMA) {
|
||||
lv_disp_flush_ready(disp);
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* LVGL_PORT_AVOID_TEAR_ENABLE */
|
||||
|
||||
static lv_display_t *display_init(esp_lcd_panel_handle_t panel_handle)
|
||||
{
|
||||
#if LVGL_PORT_PPA_ROTATION_ENABLE
|
||||
// Initialize the PPA
|
||||
ppa_client_config_t ppa_srm_config = {
|
||||
.oper_type = PPA_OPERATION_SRM,
|
||||
};
|
||||
ESP_ERROR_CHECK(ppa_register_client(&ppa_srm_config, &ppa_srm_handle));
|
||||
ESP_ERROR_CHECK(esp_cache_get_alignment(MALLOC_CAP_DMA|MALLOC_CAP_SPIRAM, &data_cache_line_size));
|
||||
#endif
|
||||
|
||||
assert(panel_handle);
|
||||
|
||||
// alloc draw buffers used by LVGL
|
||||
void *buf1 = NULL;
|
||||
void *buf2 = NULL;
|
||||
int buffer_size = 0;
|
||||
|
||||
ESP_LOGD(TAG, "Malloc memory for LVGL buffer");
|
||||
#if LVGL_PORT_AVOID_TEAR_ENABLE
|
||||
// To avoid the tearing effect, we should use at least two frame buffers: one for LVGL rendering and another for RGB output
|
||||
buffer_size = LVGL_PORT_H_RES * LVGL_PORT_V_RES;
|
||||
#if (LVGL_PORT_LCD_BUFFER_NUMS == 3) && (EXAMPLE_LVGL_PORT_ROTATION_DEGREE == 0) && LVGL_PORT_FULL_REFRESH
|
||||
// With the usage of three buffers and full-refresh, we always have one buffer available for rendering, eliminating the need to wait for the RGB's sync signal
|
||||
ESP_ERROR_CHECK(lvgl_get_lcd_frame_buffer(panel_handle, 3, &lvgl_port_rgb_last_buf, &buf1, &buf2));
|
||||
lvgl_port_rgb_next_buf = lvgl_port_rgb_last_buf;
|
||||
lvgl_port_flush_next_buf = buf2;
|
||||
#elif (LVGL_PORT_LCD_BUFFER_NUMS == 3) && (EXAMPLE_LVGL_PORT_ROTATION_DEGREE != 0)
|
||||
// Here we are using three frame buffers, one for LVGL rendering, and the other two for RGB driver (one of them is used for rotation)
|
||||
void *fbs[3];
|
||||
ESP_ERROR_CHECK(lvgl_get_lcd_frame_buffer(panel_handle, 3, &fbs[0], &fbs[1], &fbs[2]));
|
||||
buf1 = fbs[2];
|
||||
#else
|
||||
ESP_ERROR_CHECK(lvgl_get_lcd_frame_buffer(panel_handle, 2, &buf1, &buf2));
|
||||
#endif
|
||||
#else
|
||||
// Normmaly, for RGB LCD, we just use one buffer for LVGL rendering
|
||||
buffer_size = LVGL_PORT_H_RES * LVGL_PORT_BUFFER_HEIGHT;
|
||||
buf1 = heap_caps_malloc(buffer_size * sizeof(lv_color_t), LVGL_PORT_BUFFER_MALLOC_CAPS);
|
||||
// buffer_size = LVGL_PORT_H_RES * LVGL_PORT_BUFFER_HEIGHT;
|
||||
// buf1 = heap_caps_malloc(buffer_size * sizeof(lv_color_t), MALLOC_CAP_DMA);
|
||||
assert(buf1);
|
||||
ESP_LOGI(TAG, "LVGL buffer size: %dKB", buffer_size * sizeof(lv_color_t) / 1024);
|
||||
#endif /* LVGL_PORT_AVOID_TEAR_ENABLE */
|
||||
|
||||
ESP_LOGD(TAG, "Register display driver to LVGL");
|
||||
lv_display_t *display = lv_display_create(
|
||||
#if (EXAMPLE_LVGL_PORT_ROTATION_DEGREE != 90) && (EXAMPLE_LVGL_PORT_ROTATION_DEGREE != 270)
|
||||
LVGL_PORT_H_RES, LVGL_PORT_V_RES
|
||||
#else
|
||||
LVGL_PORT_V_RES, LVGL_PORT_H_RES
|
||||
#endif
|
||||
);
|
||||
|
||||
lv_display_set_buffers(
|
||||
display, buf1, buf2, buffer_size * sizeof(lv_color_t),
|
||||
#if LVGL_PORT_FULL_REFRESH
|
||||
LV_DISPLAY_RENDER_MODE_FULL
|
||||
#elif LVGL_PORT_DIRECT_MODE
|
||||
LV_DISPLAY_RENDER_MODE_DIRECT
|
||||
#else
|
||||
LV_DISPLAY_RENDER_MODE_PARTIAL
|
||||
#endif
|
||||
);
|
||||
lv_display_set_flush_cb(display, flush_callback);
|
||||
lv_display_set_user_data(display, panel_handle);
|
||||
|
||||
return display;
|
||||
}
|
||||
|
||||
static void touchpad_read(lv_indev_t *indev_drv, lv_indev_data_t *data)
|
||||
{
|
||||
esp_lcd_touch_handle_t tp = (esp_lcd_touch_handle_t)lv_indev_get_user_data(indev_drv);
|
||||
assert(tp);
|
||||
|
||||
uint16_t touchpad_x;
|
||||
uint16_t touchpad_y;
|
||||
uint8_t touchpad_cnt = 0;
|
||||
/* Read data from touch controller into memory */
|
||||
esp_lcd_touch_read_data(tp);
|
||||
|
||||
/* Read data from touch controller */
|
||||
bool touchpad_pressed = esp_lcd_touch_get_coordinates(tp, &touchpad_x, &touchpad_y, NULL, &touchpad_cnt, 1);
|
||||
if (touchpad_pressed && touchpad_cnt > 0) {
|
||||
data->point.x = touchpad_x;
|
||||
data->point.y = touchpad_y;
|
||||
data->state = LV_INDEV_STATE_PRESSED;
|
||||
|
||||
ESP_LOGD(TAG, "Touch position: %d,%d", touchpad_x, touchpad_y);
|
||||
} else {
|
||||
data->state = LV_INDEV_STATE_RELEASED;
|
||||
}
|
||||
}
|
||||
|
||||
static lv_indev_t *indev_init(esp_lcd_touch_handle_t tp)
|
||||
{
|
||||
assert(tp);
|
||||
|
||||
lv_indev_t *indev = lv_indev_create();
|
||||
lv_indev_set_type(indev, LV_INDEV_TYPE_POINTER); /*See below.*/
|
||||
lv_indev_set_user_data(indev, tp);
|
||||
lv_indev_set_read_cb(indev, touchpad_read); /*See below.*/
|
||||
|
||||
return indev;
|
||||
}
|
||||
|
||||
static void tick_increment(void *arg)
|
||||
{
|
||||
/* Tell LVGL how many milliseconds have elapsed */
|
||||
lv_tick_inc(LVGL_PORT_TICK_PERIOD_MS);
|
||||
}
|
||||
|
||||
static esp_err_t tick_init(void)
|
||||
{
|
||||
// Tick interface for LVGL (using esp_timer to generate 2ms periodic event)
|
||||
const esp_timer_create_args_t lvgl_tick_timer_args = {
|
||||
.callback = &tick_increment,
|
||||
.name = "LVGL tick"
|
||||
};
|
||||
esp_timer_handle_t lvgl_tick_timer = NULL;
|
||||
ESP_ERROR_CHECK(esp_timer_create(&lvgl_tick_timer_args, &lvgl_tick_timer));
|
||||
return esp_timer_start_periodic(lvgl_tick_timer, LVGL_PORT_TICK_PERIOD_MS * 1000);
|
||||
}
|
||||
|
||||
static void lvgl_port_task(void *arg)
|
||||
{
|
||||
ESP_LOGD(TAG, "Starting LVGL task");
|
||||
|
||||
lvgl_port_task_param_t *param = (lvgl_port_task_param_t *)arg;
|
||||
|
||||
lv_init();
|
||||
ESP_ERROR_CHECK(tick_init());
|
||||
|
||||
lv_display_t *disp = display_init(param->lcd_handle);
|
||||
assert(disp);
|
||||
|
||||
if (param->tp_handle) {
|
||||
lv_indev_t *indev = indev_init(param->tp_handle);
|
||||
assert(indev);
|
||||
|
||||
#if EXAMPLE_LVGL_PORT_ROTATION_90
|
||||
esp_lcd_touch_set_swap_xy(param->tp_handle, true);
|
||||
esp_lcd_touch_set_mirror_x(param->tp_handle, true);
|
||||
#elif EXAMPLE_LVGL_PORT_ROTATION_180
|
||||
esp_lcd_touch_set_mirror_x(param->tp_handle, false);
|
||||
esp_lcd_touch_set_mirror_y(param->tp_handle, false);
|
||||
#elif EXAMPLE_LVGL_PORT_ROTATION_270
|
||||
esp_lcd_touch_set_swap_xy(param->tp_handle, true);
|
||||
esp_lcd_touch_set_mirror_y(param->tp_handle, false);
|
||||
#endif
|
||||
}
|
||||
|
||||
param->is_init = true;
|
||||
|
||||
uint32_t task_delay_ms = LVGL_PORT_TASK_MAX_DELAY_MS;
|
||||
while (1) {
|
||||
if (lvgl_port_lock(-1)) {
|
||||
task_delay_ms = lv_timer_handler();
|
||||
lvgl_port_unlock();
|
||||
}
|
||||
if (task_delay_ms > LVGL_PORT_TASK_MAX_DELAY_MS) {
|
||||
task_delay_ms = LVGL_PORT_TASK_MAX_DELAY_MS;
|
||||
} else if (task_delay_ms < LVGL_PORT_TASK_MIN_DELAY_MS) {
|
||||
task_delay_ms = LVGL_PORT_TASK_MIN_DELAY_MS;
|
||||
}
|
||||
vTaskDelay(pdMS_TO_TICKS(task_delay_ms));
|
||||
}
|
||||
}
|
||||
|
||||
esp_err_t lvgl_port_init(esp_lcd_panel_handle_t lcd_handle, esp_lcd_touch_handle_t tp_handle, lvgl_port_interface_t interface)
|
||||
{
|
||||
lvgl_port_task_param_t lvgl_task_param = {
|
||||
.lcd_handle = lcd_handle,
|
||||
.tp_handle = tp_handle,
|
||||
.is_init = false
|
||||
};
|
||||
|
||||
lvgl_port_interface = interface;
|
||||
#if LVGL_PORT_AVOID_TEAR_ENABLE
|
||||
switch (interface) {
|
||||
#if SOC_LCDCAM_RGB_LCD_SUPPORTED
|
||||
case LVGL_PORT_INTERFACE_RGB:
|
||||
lvgl_get_lcd_frame_buffer = esp_lcd_rgb_panel_get_frame_buffer;
|
||||
break;
|
||||
#endif
|
||||
|
||||
#if SOC_MIPI_DSI_SUPPORTED
|
||||
case LVGL_PORT_INTERFACE_MIPI_DSI_DMA:
|
||||
case LVGL_PORT_INTERFACE_MIPI_DSI_NO_DMA:
|
||||
lvgl_get_lcd_frame_buffer = esp_lcd_dpi_panel_get_frame_buffer;
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
ESP_LOGE(TAG, "Invalid interface type");
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
#endif
|
||||
|
||||
lvgl_mux = xSemaphoreCreateRecursiveMutex();
|
||||
assert(lvgl_mux);
|
||||
|
||||
ESP_LOGI(TAG, "Create LVGL task");
|
||||
BaseType_t core_id = (LVGL_PORT_TASK_CORE < 0) ? tskNO_AFFINITY : LVGL_PORT_TASK_CORE;
|
||||
BaseType_t ret = xTaskCreatePinnedToCore(lvgl_port_task, "lvgl", LVGL_PORT_TASK_STACK_SIZE, &lvgl_task_param,
|
||||
LVGL_PORT_TASK_PRIORITY, &lvgl_task_handle, core_id);
|
||||
if (ret != pdPASS) {
|
||||
ESP_LOGE(TAG, "Failed to create LVGL task");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
while (!lvgl_task_param.is_init) {
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
bool lvgl_port_lock(int timeout_ms)
|
||||
{
|
||||
assert(lvgl_mux && "lvgl_port_init must be called first");
|
||||
|
||||
const TickType_t timeout_ticks = (timeout_ms < 0) ? portMAX_DELAY : pdMS_TO_TICKS(timeout_ms);
|
||||
return xSemaphoreTakeRecursive(lvgl_mux, timeout_ticks) == pdTRUE;
|
||||
}
|
||||
|
||||
void lvgl_port_unlock(void)
|
||||
{
|
||||
assert(lvgl_mux && "lvgl_port_init must be called first");
|
||||
xSemaphoreGiveRecursive(lvgl_mux);
|
||||
}
|
||||
|
||||
bool lvgl_port_notify_lcd_vsync(void)
|
||||
{
|
||||
BaseType_t need_yield = pdFALSE;
|
||||
#if LVGL_PORT_FULL_REFRESH && (LVGL_PORT_LCD_RGB_BUFFER_NUMS == 3) && (EXAMPLE_LVGL_PORT_ROTATION_DEGREE == 0)
|
||||
if (lvgl_port_rgb_next_buf != lvgl_port_rgb_last_buf) {
|
||||
lvgl_port_flush_next_buf = lvgl_port_rgb_last_buf;
|
||||
lvgl_port_rgb_last_buf = lvgl_port_rgb_next_buf;
|
||||
}
|
||||
#elif LVGL_PORT_AVOID_TEAR_ENABLE
|
||||
// Notify that the current LCD frame buffer has been transmitted
|
||||
if (lvgl_task_handle) {
|
||||
xTaskNotifyFromISR(lvgl_task_handle, ULONG_MAX, eNoAction, &need_yield);
|
||||
}
|
||||
#else
|
||||
if (lvgl_port_interface == LVGL_PORT_INTERFACE_MIPI_DSI_DMA) {
|
||||
lv_display_t *disp = lv_disp_get_default();
|
||||
lv_disp_flush_ready(disp);
|
||||
}
|
||||
#endif
|
||||
return (need_yield == pdTRUE);
|
||||
}
|
||||
+181
@@ -0,0 +1,181 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "esp_err.h"
|
||||
#include "esp_lcd_types.h"
|
||||
#include "src/touch/esp_lcd_touch.h"
|
||||
#include "lvgl.h"
|
||||
#include "pins_config.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief LVGL port interface type
|
||||
*
|
||||
*/
|
||||
typedef enum {
|
||||
LVGL_PORT_INTERFACE_RGB,
|
||||
LVGL_PORT_INTERFACE_MIPI_DSI_DMA,
|
||||
LVGL_PORT_INTERFACE_MIPI_DSI_NO_DMA,
|
||||
LVGL_PORT_INTERFACE_MAX,
|
||||
} lvgl_port_interface_t;
|
||||
|
||||
/**
|
||||
* LVGL related parameters, can be adjusted by users
|
||||
*
|
||||
*/
|
||||
#define LVGL_PORT_H_RES (480)
|
||||
#define LVGL_PORT_V_RES (800)
|
||||
#define LVGL_PORT_TICK_PERIOD_MS (EXAMPLE_LVGL_PORT_TICK)
|
||||
|
||||
/**
|
||||
* LVGL timer handle task related parameters, can be adjusted by users
|
||||
*
|
||||
*/
|
||||
#define LVGL_PORT_TASK_MAX_DELAY_MS (EXAMPLE_LVGL_PORT_TASK_MAX_DELAY_MS) // The maximum delay of the LVGL timer task, in milliseconds
|
||||
#define LVGL_PORT_TASK_MIN_DELAY_MS (EXAMPLE_LVGL_PORT_TASK_MIN_DELAY_MS) // The minimum delay of the LVGL timer task, in milliseconds
|
||||
#define LVGL_PORT_TASK_STACK_SIZE (EXAMPLE_LVGL_PORT_TASK_STACK_SIZE_KB * 1024) // The stack size of the LVGL timer task, in bytes
|
||||
#define LVGL_PORT_TASK_PRIORITY (EXAMPLE_LVGL_PORT_TASK_PRIORITY) // The priority of the LVGL timer task
|
||||
#define LVGL_PORT_TASK_CORE (EXAMPLE_LVGL_PORT_TASK_CORE) // The core of the LVGL timer task,
|
||||
// `-1` means the don't specify the core
|
||||
/**
|
||||
*
|
||||
* LVGL buffer related parameters, can be adjusted by users:
|
||||
* (These parameters will be useless if the avoid tearing function is enabled)
|
||||
*
|
||||
* - Memory type for buffer allocation:
|
||||
* - MALLOC_CAP_SPIRAM: Allocate LVGL buffer in PSRAM
|
||||
* - MALLOC_CAP_INTERNAL: Allocate LVGL buffer in SRAM
|
||||
* (The SRAM is faster than PSRAM, but the PSRAM has a larger capacity)
|
||||
*
|
||||
*/
|
||||
#if CONFIG_EXAMPLE_LVGL_PORT_BUF_PSRAM
|
||||
#define LVGL_PORT_BUFFER_MALLOC_CAPS (MALLOC_CAP_SPIRAM)
|
||||
#elif CONFIG_EXAMPLE_LVGL_PORT_BUF_INTERNAL
|
||||
#define LVGL_PORT_BUFFER_MALLOC_CAPS (MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT)
|
||||
#endif
|
||||
#define LVGL_PORT_BUFFER_HEIGHT (CONFIG_EXAMPLE_LVGL_PORT_BUF_HEIGHT)
|
||||
|
||||
/**
|
||||
* Avoid tering related configurations, can be adjusted by users.
|
||||
*
|
||||
*/
|
||||
#define LVGL_PORT_AVOID_TEAR_ENABLE (EXAMPLE_LVGL_PORT_AVOID_TEAR_ENABLE) // Set to 1 to enable
|
||||
#if LVGL_PORT_AVOID_TEAR_ENABLE
|
||||
/**
|
||||
* Set the avoid tearing mode:
|
||||
* - 0: Disable avoid tearing function
|
||||
* - 1: LCD double-buffer & LVGL full-refresh
|
||||
* - 2: LCD triple-buffer & LVGL full-refresh
|
||||
* - 3: LCD double-buffer & LVGL direct-mode (recommended)
|
||||
*
|
||||
*/
|
||||
#define LVGL_PORT_AVOID_TEAR_MODE (EXAMPLE_LVGL_PORT_AVOID_TEAR_MODE)
|
||||
|
||||
/**
|
||||
* Set the PPA rotation enable:
|
||||
* - 0: Disable PPA rotation
|
||||
* - 1: Enable PPA rotation
|
||||
*
|
||||
*/
|
||||
#define LVGL_PORT_PPA_ROTATION_ENABLE (EXAMPLE_LVGL_PORT_PPA_ROTATION_ENABLE)
|
||||
|
||||
/**
|
||||
* Set the rotation degree of the LCD panel when the avoid tearing function is enabled:
|
||||
* - 0: 0 degree
|
||||
* - 90: 90 degree
|
||||
* - 180: 180 degree
|
||||
* - 270: 270 degree
|
||||
*
|
||||
*/
|
||||
#define EXAMPLE_LVGL_PORT_ROTATION_DEGREE (EXAMPLE_LVGL_PORT_ROTATION_DEGREE_)
|
||||
|
||||
/**
|
||||
* Below configurations are automatically set according to the above configurations, users do not need to modify them.
|
||||
*
|
||||
*/
|
||||
#if LVGL_PORT_AVOID_TEAR_MODE == 1
|
||||
#define LVGL_PORT_LCD_BUFFER_NUMS (2)
|
||||
#define LVGL_PORT_FULL_REFRESH (1)
|
||||
#elif LVGL_PORT_AVOID_TEAR_MODE == 2
|
||||
#define LVGL_PORT_LCD_BUFFER_NUMS (3)
|
||||
#define LVGL_PORT_FULL_REFRESH (1)
|
||||
#elif LVGL_PORT_AVOID_TEAR_MODE == 3
|
||||
#define LVGL_PORT_LCD_BUFFER_NUMS (2)
|
||||
#define LVGL_PORT_DIRECT_MODE (1)
|
||||
#endif /* LVGL_PORT_AVOID_TEAR_MODE */
|
||||
|
||||
#if EXAMPLE_LVGL_PORT_ROTATION_DEGREE == 0
|
||||
#define EXAMPLE_LVGL_PORT_ROTATION_0 (1)
|
||||
#else
|
||||
#if EXAMPLE_LVGL_PORT_ROTATION_DEGREE == 90
|
||||
#define EXAMPLE_LVGL_PORT_ROTATION_90 (1)
|
||||
#elif EXAMPLE_LVGL_PORT_ROTATION_DEGREE == 180
|
||||
#define EXAMPLE_LVGL_PORT_ROTATION_180 (1)
|
||||
#elif EXAMPLE_LVGL_PORT_ROTATION_DEGREE == 270
|
||||
#define EXAMPLE_LVGL_PORT_ROTATION_270 (1)
|
||||
#endif
|
||||
#ifdef LVGL_PORT_LCD_BUFFER_NUMS
|
||||
#undef LVGL_PORT_LCD_BUFFER_NUMS
|
||||
#define LVGL_PORT_LCD_BUFFER_NUMS (3)
|
||||
#endif
|
||||
#endif /* EXAMPLE_LVGL_PORT_ROTATION_DEGREE */
|
||||
#else
|
||||
#define LVGL_PORT_LCD_BUFFER_NUMS (1)
|
||||
#define LVGL_PORT_FULL_REFRESH (0)
|
||||
#define LVGL_PORT_DIRECT_MODE (0)
|
||||
#endif /* LVGL_PORT_AVOID_TEAR_ENABLE */
|
||||
|
||||
/**
|
||||
* @brief Initialize LVGL port
|
||||
*
|
||||
* @param[in] lcd_handle: LCD panel handle
|
||||
* @param[in] tp_handle: Touch panel handle
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Success
|
||||
* - ESP_ERR_INVALID_ARG: Invalid argument
|
||||
* - Others: Fail
|
||||
*/
|
||||
esp_err_t lvgl_port_init(esp_lcd_panel_handle_t lcd_handle, esp_lcd_touch_handle_t tp_handle, lvgl_port_interface_t interface);
|
||||
|
||||
/**
|
||||
* @brief Take LVGL mutex
|
||||
*
|
||||
* @param[in] timeout_ms: Timeout in [ms]. 0 will block indefinitely.
|
||||
*
|
||||
* @return
|
||||
* - true: Mutex was taken
|
||||
* - false: Mutex was NOT taken
|
||||
*/
|
||||
bool lvgl_port_lock(int timeout_ms);
|
||||
|
||||
/**
|
||||
* @brief Give LVGL mutex
|
||||
*
|
||||
*/
|
||||
void lvgl_port_unlock(void);
|
||||
|
||||
/**
|
||||
* @brief Notifies the LVGL task when the transmission of the RGB frame buffer is completed.
|
||||
*
|
||||
* @return
|
||||
* - true: The tasks need to be re-scheduled
|
||||
* - false: The tasks don't need to be re-scheduled
|
||||
*/
|
||||
bool lvgl_port_notify_lcd_vsync(void);
|
||||
|
||||
void lvgl_sw_rotation_main(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
+206
@@ -0,0 +1,206 @@
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "driver/ledc.h"
|
||||
#include "driver/i2c_master.h"
|
||||
#include "driver/spi_master.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_timer.h"
|
||||
#include "esp_heap_caps.h"
|
||||
#include "esp_ldo_regulator.h"
|
||||
#include "esp_lcd_panel_io.h"
|
||||
#include "esp_lcd_panel_ops.h"
|
||||
#include "esp_lcd_mipi_dsi.h"
|
||||
#include "esp_cache.h"
|
||||
#include "esp_heap_caps.h"
|
||||
#include "esp_private/esp_cache_private.h"
|
||||
#include "src/touch/esp_lcd_touch_gt911.h"
|
||||
#include "src/lcd/esp_lcd_st7701.h"
|
||||
#include "lvgl_port_v9.h"
|
||||
#include "demos/lv_demos.h"
|
||||
#include "driver/ppa.h"
|
||||
|
||||
#define TAG "main"
|
||||
|
||||
|
||||
#define BSP_MIPI_DSI_PHY_PWR_LDO_CHAN (3) // LDO_VO3 is connected to VDD_MIPI_DPHY
|
||||
#define BSP_MIPI_DSI_PHY_PWR_LDO_VOLTAGE_MV (2500)
|
||||
|
||||
#define BSP_LCD_DPI_BUFFER_NUMS (1)
|
||||
|
||||
#define BSP_LCD_H_RES (480)
|
||||
#define BSP_LCD_V_RES (800)
|
||||
|
||||
#define BSP_I2C_NUM (I2C_NUM_1)
|
||||
#define BSP_I2C_SDA (GPIO_NUM_7)
|
||||
#define BSP_I2C_SCL (GPIO_NUM_8)
|
||||
|
||||
#define BSP_LCD_TOUCH_RST (GPIO_NUM_NC)
|
||||
#define BSP_LCD_TOUCH_INT (GPIO_NUM_NC)
|
||||
|
||||
#define BSP_LCD_RST (GPIO_NUM_5)
|
||||
|
||||
i2c_master_bus_handle_t i2c_handle = NULL;
|
||||
|
||||
|
||||
#define BSP_LCD_BACKLIGHT GPIO_NUM_23
|
||||
#define LCD_LEDC_CH LEDC_CHANNEL_0
|
||||
static esp_err_t bsp_display_brightness_init(void)
|
||||
{
|
||||
// Setup LEDC peripheral for PWM backlight control
|
||||
const ledc_channel_config_t LCD_backlight_channel = {
|
||||
.gpio_num = BSP_LCD_BACKLIGHT,
|
||||
.speed_mode = LEDC_LOW_SPEED_MODE,
|
||||
.channel = LCD_LEDC_CH,
|
||||
.intr_type = LEDC_INTR_DISABLE,
|
||||
.timer_sel = 1,
|
||||
.duty = 0,
|
||||
.hpoint = 0
|
||||
};
|
||||
const ledc_timer_config_t LCD_backlight_timer = {
|
||||
.speed_mode = LEDC_LOW_SPEED_MODE,
|
||||
.duty_resolution = LEDC_TIMER_10_BIT,
|
||||
.timer_num = 1,
|
||||
.freq_hz = 5000,
|
||||
.clk_cfg = LEDC_AUTO_CLK
|
||||
};
|
||||
|
||||
ESP_ERROR_CHECK(ledc_timer_config(&LCD_backlight_timer));
|
||||
ESP_ERROR_CHECK(ledc_channel_config(&LCD_backlight_channel));
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t bsp_display_brightness_set(int brightness_percent)
|
||||
{
|
||||
if (brightness_percent > 100) {
|
||||
brightness_percent = 100;
|
||||
}
|
||||
if (brightness_percent < 0) {
|
||||
brightness_percent = 0;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "Setting LCD backlight: %d%%", brightness_percent);
|
||||
uint32_t duty_cycle = (1023 * brightness_percent) / 100; // LEDC resolution set to 10bits, thus: 100% = 1023
|
||||
ESP_ERROR_CHECK(ledc_set_duty(LEDC_LOW_SPEED_MODE, LCD_LEDC_CH, duty_cycle));
|
||||
ESP_ERROR_CHECK(ledc_update_duty(LEDC_LOW_SPEED_MODE, LCD_LEDC_CH));
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t bsp_display_backlight_off(void)
|
||||
{
|
||||
return bsp_display_brightness_set(0);
|
||||
}
|
||||
|
||||
static esp_err_t bsp_display_backlight_on(void)
|
||||
{
|
||||
return bsp_display_brightness_set(100);
|
||||
}
|
||||
|
||||
IRAM_ATTR static bool mipi_dsi_lcd_on_vsync_event(esp_lcd_panel_handle_t panel, esp_lcd_dpi_panel_event_data_t *edata, void *user_ctx)
|
||||
{
|
||||
return lvgl_port_notify_lcd_vsync();
|
||||
}
|
||||
|
||||
|
||||
void lvgl_sw_rotation_main(void)
|
||||
{
|
||||
bsp_display_brightness_init();
|
||||
|
||||
i2c_master_bus_config_t i2c_bus_conf = {
|
||||
.clk_source = I2C_CLK_SRC_DEFAULT,
|
||||
.sda_io_num = BSP_I2C_SDA,
|
||||
.scl_io_num = BSP_I2C_SCL,
|
||||
.i2c_port = BSP_I2C_NUM,
|
||||
};
|
||||
i2c_new_master_bus(&i2c_bus_conf, &i2c_handle);
|
||||
|
||||
static esp_ldo_channel_handle_t phy_pwr_chan = NULL;
|
||||
esp_ldo_channel_config_t ldo_cfg = {
|
||||
.chan_id = BSP_MIPI_DSI_PHY_PWR_LDO_CHAN,
|
||||
.voltage_mv = BSP_MIPI_DSI_PHY_PWR_LDO_VOLTAGE_MV,
|
||||
};
|
||||
esp_ldo_acquire_channel(&ldo_cfg, &phy_pwr_chan);
|
||||
ESP_LOGI(TAG, "MIPI DSI PHY Powered on");
|
||||
|
||||
esp_lcd_dsi_bus_handle_t mipi_dsi_bus;
|
||||
esp_lcd_dsi_bus_config_t bus_config = ST7701_PANEL_BUS_DSI_2CH_CONFIG();
|
||||
|
||||
esp_lcd_new_dsi_bus(&bus_config, &mipi_dsi_bus);
|
||||
|
||||
ESP_LOGI(TAG, "Install MIPI DSI LCD control panel");
|
||||
// we use DBI interface to send LCD commands and parameters
|
||||
esp_lcd_panel_io_handle_t io = NULL;
|
||||
esp_lcd_dbi_io_config_t dbi_config =ST7701_PANEL_IO_DBI_CONFIG();
|
||||
|
||||
esp_lcd_new_panel_io_dbi(mipi_dsi_bus, &dbi_config, &io);
|
||||
|
||||
esp_lcd_panel_handle_t disp_panel = NULL;
|
||||
esp_lcd_dpi_panel_config_t dpi_config = ST7701_480_360_PANEL_60HZ_DPI_CONFIG(LCD_COLOR_PIXEL_FORMAT_RGB565);
|
||||
|
||||
dpi_config.num_fbs = LVGL_PORT_LCD_BUFFER_NUMS;
|
||||
|
||||
st7701_vendor_config_t vendor_config = {
|
||||
.mipi_config = {
|
||||
.dsi_bus = mipi_dsi_bus,
|
||||
.dpi_config = &dpi_config,
|
||||
},
|
||||
.flags = {
|
||||
.use_mipi_interface = 1,
|
||||
},
|
||||
};
|
||||
esp_lcd_panel_dev_config_t lcd_dev_config = {
|
||||
.bits_per_pixel = 16,
|
||||
.rgb_ele_order = ESP_LCD_COLOR_SPACE_RGB,
|
||||
.reset_gpio_num = BSP_LCD_RST,
|
||||
.vendor_config = &vendor_config,
|
||||
};
|
||||
esp_lcd_new_panel_st7701(io, &lcd_dev_config, &disp_panel);
|
||||
esp_lcd_panel_reset(disp_panel);
|
||||
esp_lcd_panel_init(disp_panel);
|
||||
|
||||
esp_lcd_dpi_panel_event_callbacks_t cbs = {
|
||||
#if LVGL_PORT_AVOID_TEAR_MODE
|
||||
.on_refresh_done = mipi_dsi_lcd_on_vsync_event,
|
||||
#else
|
||||
.on_color_trans_done = mipi_dsi_lcd_on_vsync_event,
|
||||
#endif
|
||||
};
|
||||
esp_lcd_dpi_panel_register_event_callbacks(disp_panel, &cbs, NULL);
|
||||
esp_lcd_panel_io_handle_t tp_io_handle = NULL;
|
||||
esp_lcd_touch_handle_t tp_handle;
|
||||
esp_lcd_panel_io_i2c_config_t tp_io_config = ESP_LCD_TOUCH_IO_I2C_GT911_CONFIG();
|
||||
tp_io_config.scl_speed_hz = 100000;
|
||||
esp_lcd_new_panel_io_i2c(i2c_handle, &tp_io_config, &tp_io_handle);
|
||||
const esp_lcd_touch_config_t tp_cfg = {
|
||||
.x_max = BSP_LCD_H_RES,
|
||||
.y_max = BSP_LCD_V_RES,
|
||||
.rst_gpio_num = BSP_LCD_TOUCH_RST, // Shared with LCD reset
|
||||
.int_gpio_num = BSP_LCD_TOUCH_INT,
|
||||
.levels = {
|
||||
.reset = 0,
|
||||
.interrupt = 0,
|
||||
},
|
||||
.flags = {
|
||||
.swap_xy = 0,
|
||||
.mirror_x = 0,
|
||||
.mirror_y = 0,
|
||||
},
|
||||
};
|
||||
|
||||
esp_lcd_touch_new_i2c_gt911(tp_io_handle, &tp_cfg, &tp_handle);
|
||||
|
||||
lvgl_port_interface_t interface = (dpi_config.flags.use_dma2d) ? LVGL_PORT_INTERFACE_MIPI_DSI_DMA : LVGL_PORT_INTERFACE_MIPI_DSI_NO_DMA;
|
||||
ESP_LOGI(TAG,"interface is %d",interface);
|
||||
ESP_ERROR_CHECK(lvgl_port_init(disp_panel, tp_handle, interface));
|
||||
|
||||
bsp_display_brightness_set(100);
|
||||
|
||||
if(lvgl_port_lock(-1))
|
||||
{
|
||||
// lv_demo_music();
|
||||
// lv_demo_benchmark();
|
||||
lv_demo_widgets();
|
||||
|
||||
lvgl_port_unlock();
|
||||
}
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
#pragma GCC push_options
|
||||
#pragma GCC optimize("O3")
|
||||
|
||||
#include <Arduino.h>
|
||||
#include "lvgl.h"
|
||||
#include "demos/lv_demos.h"
|
||||
#include "pins_config.h"
|
||||
#include "lvgl_port_v9.h"
|
||||
|
||||
void setup()
|
||||
{
|
||||
Serial.begin(115200);
|
||||
Serial.println("ESP32P4 MIPI DSI LVGL");
|
||||
|
||||
lvgl_sw_rotation_main();
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#define EXAMPLE_LVGL_PORT_TASK_MAX_DELAY_MS 500 //range 2 to 2000
|
||||
#define EXAMPLE_LVGL_PORT_TASK_MIN_DELAY_MS 5 //range 1 to 100
|
||||
#define EXAMPLE_LVGL_PORT_TASK_PRIORITY 4
|
||||
#define EXAMPLE_LVGL_PORT_TASK_STACK_SIZE_KB 6 //KB
|
||||
#define EXAMPLE_LVGL_PORT_TASK_CORE -1 //range -1 to 1
|
||||
#define EXAMPLE_LVGL_PORT_TICK 2 //ragne 1 to 100
|
||||
|
||||
#define EXAMPLE_LVGL_PORT_AVOID_TEAR_ENABLE 1
|
||||
|
||||
#ifdef EXAMPLE_LVGL_PORT_AVOID_TEAR_ENABLE
|
||||
#define EXAMPLE_LVGL_PORT_AVOID_TEAR_MODE 3 //range 1 to 3
|
||||
|
||||
#define EXAMPLE_LVGL_PORT_ROTATION_DEGREE_ 90 // 0,90,180 or 270
|
||||
#define EXAMPLE_LVGL_PORT_PPA_ROTATION_ENABLE 1
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
#define LCD_H_RES 480
|
||||
#define LCD_V_RES 800
|
||||
|
||||
#define LCD_RST -1
|
||||
#define LCD_LED -1
|
||||
|
||||
#define TP_I2C_SDA 7
|
||||
#define TP_I2C_SCL 8
|
||||
#define TP_RST -1
|
||||
#define TP_INT -1
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "soc/soc_caps.h"
|
||||
#include "esp_check.h"
|
||||
#include "esp_lcd_types.h"
|
||||
|
||||
#include "esp_lcd_st7701_interface.h"
|
||||
#include "esp_lcd_st7701.h"
|
||||
|
||||
static const char *TAG = "st7701";
|
||||
|
||||
esp_err_t esp_lcd_new_panel_st7701(const esp_lcd_panel_io_handle_t io, const esp_lcd_panel_dev_config_t *panel_dev_config,
|
||||
esp_lcd_panel_handle_t *ret_panel)
|
||||
{
|
||||
// ESP_LOGI(TAG, "version: %d.%d.%d", ESP_LCD_ST7701_VER_MAJOR, ESP_LCD_ST7701_VER_MINOR, ESP_LCD_ST7701_VER_PATCH);
|
||||
ESP_RETURN_ON_FALSE(panel_dev_config && ret_panel, ESP_ERR_INVALID_ARG, TAG, "Invalid arguments");
|
||||
st7701_vendor_config_t *vendor_config = (st7701_vendor_config_t *)panel_dev_config->vendor_config;
|
||||
ESP_RETURN_ON_FALSE(vendor_config, ESP_ERR_INVALID_ARG, TAG, "`vendor_config` is necessary");
|
||||
|
||||
esp_err_t ret = ESP_ERR_NOT_SUPPORTED;
|
||||
|
||||
#if SOC_LCD_RGB_SUPPORTED
|
||||
if (!vendor_config->flags.use_mipi_interface) {
|
||||
ret = esp_lcd_new_panel_st7701_rgb(io, panel_dev_config, ret_panel);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if SOC_MIPI_DSI_SUPPORTED
|
||||
if (vendor_config->flags.use_mipi_interface) {
|
||||
ret = esp_lcd_new_panel_st7701_mipi(io, panel_dev_config, ret_panel);
|
||||
}
|
||||
#endif
|
||||
|
||||
return ret;
|
||||
}
|
||||
+199
@@ -0,0 +1,199 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "hal/lcd_types.h"
|
||||
#include "esp_lcd_panel_vendor.h"
|
||||
|
||||
#if SOC_LCD_RGB_SUPPORTED
|
||||
#include "esp_lcd_panel_rgb.h"
|
||||
#endif
|
||||
|
||||
#if SOC_MIPI_DSI_SUPPORTED
|
||||
#include "esp_lcd_mipi_dsi.h"
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief LCD panel initialization commands.
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
int cmd; /*<! The specific LCD command */
|
||||
const void *data; /*<! Buffer that holds the command specific data */
|
||||
size_t data_bytes; /*<! Size of `data` in memory, in bytes */
|
||||
unsigned int delay_ms; /*<! Delay in milliseconds after this command */
|
||||
} st7701_lcd_init_cmd_t;
|
||||
|
||||
/**
|
||||
* @brief LCD panel vendor configuration.
|
||||
*
|
||||
* @note This structure needs to be passed to the `vendor_config` field in `esp_lcd_panel_dev_config_t`.
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
const st7701_lcd_init_cmd_t *init_cmds; /*!< Pointer to initialization commands array. Set to NULL if using default commands.
|
||||
* The array should be declared as `static const` and positioned outside the function.
|
||||
* Please refer to `vendor_specific_init_default` in source file.
|
||||
*/
|
||||
uint16_t init_cmds_size; /*<! Number of commands in above array */
|
||||
union {
|
||||
#if SOC_LCD_RGB_SUPPORTED
|
||||
const esp_lcd_rgb_panel_config_t *rgb_config; /*!< RGB panel configuration */
|
||||
#endif
|
||||
#if SOC_MIPI_DSI_SUPPORTED
|
||||
struct {
|
||||
esp_lcd_dsi_bus_handle_t dsi_bus; /*!< MIPI-DSI bus configuration */
|
||||
const esp_lcd_dpi_panel_config_t *dpi_config; /*!< MIPI-DPI panel configuration */
|
||||
} mipi_config;
|
||||
#endif
|
||||
};
|
||||
struct {
|
||||
unsigned int use_mipi_interface: 1; /*<! Set to 1 if using MIPI interface, default is RGB interface */
|
||||
unsigned int mirror_by_cmd: 1; /*<! The `mirror()` function will be implemented by LCD command if set to 1.
|
||||
* Otherwise, the function will be implemented by software.
|
||||
* This flag is only valid for the RGB interface.
|
||||
*/
|
||||
|
||||
union {
|
||||
unsigned int auto_del_panel_io: 1;
|
||||
unsigned int enable_io_multiplex: 1;
|
||||
}; /*<! Delete the panel IO instance automatically if set to 1. All `*_by_cmd` flags will be invalid.
|
||||
* If the panel IO pins are sharing other pins of the RGB interface to save GPIOs,
|
||||
* Please set it to 1 to release the panel IO and its pins (except CS signal).
|
||||
* This flag is only valid for the RGB interface.
|
||||
*/
|
||||
} flags;
|
||||
} st7701_vendor_config_t;
|
||||
|
||||
/**
|
||||
* @brief Create LCD panel for model ST7701
|
||||
*
|
||||
* @note When `enable_io_multiplex` is set to 1, this function will first initialize the ST7701 with vendor specific initialization and then calls `esp_lcd_new_rgb_panel()` to create an RGB LCD panel. And the `esp_lcd_panel_init()` function will only initialize RGB.
|
||||
* @note When `enable_io_multiplex` is set to 0, this function will only call `esp_lcd_new_rgb_panel()` to create an RGB LCD panel. And the `esp_lcd_panel_init()` function will initialize both the ST7701 and RGB.
|
||||
* @note Vendor specific initialization can be different between manufacturers, should consult the LCD supplier for initialization sequence code.
|
||||
*
|
||||
* @param[in] io LCD panel IO handle
|
||||
* @param[in] panel_dev_config General panel device configuration (`vendor_config` and `rgb_config` are necessary)
|
||||
* @param[out] ret_panel Returned LCD panel handle
|
||||
* @return
|
||||
* - ESP_ERR_INVALID_ARG if parameter is invalid
|
||||
* - ESP_OK on success
|
||||
* - Otherwise on fail
|
||||
*/
|
||||
esp_err_t esp_lcd_new_panel_st7701(const esp_lcd_panel_io_handle_t io, const esp_lcd_panel_dev_config_t *panel_dev_config, esp_lcd_panel_handle_t *ret_panel);
|
||||
|
||||
/**
|
||||
* @brief 3-wire SPI panel IO configuration structure
|
||||
*
|
||||
* @param[in] line_cfg SPI line configuration
|
||||
* @param[in] scl_active_edge SCL signal active edge, 0: rising edge, 1: falling edge
|
||||
*
|
||||
*/
|
||||
#define ST7701_PANEL_IO_3WIRE_SPI_CONFIG(line_cfg, scl_active_edge) \
|
||||
{ \
|
||||
.line_config = line_cfg, \
|
||||
.expect_clk_speed = PANEL_IO_3WIRE_SPI_CLK_MAX, \
|
||||
.spi_mode = scl_active_edge ? 1 : 0, \
|
||||
.lcd_cmd_bytes = 1, \
|
||||
.lcd_param_bytes = 1, \
|
||||
.flags = { \
|
||||
.use_dc_bit = 1, \
|
||||
.dc_zero_on_data = 0, \
|
||||
.lsb_first = 0, \
|
||||
.cs_high_active = 0, \
|
||||
.del_keep_cs_inactive = 1, \
|
||||
}, \
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
/////////////////////////////// Default Configuration Macros for RGB Interface /////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
/**
|
||||
* @brief RGB timing structure
|
||||
*
|
||||
* @note refresh_rate = (pclk_hz * data_width) / (h_res + hsync_pulse_width + hsync_back_porch + hsync_front_porch)
|
||||
* / (v_res + vsync_pulse_width + vsync_back_porch + vsync_front_porch)
|
||||
* / bits_per_pixel
|
||||
*
|
||||
*/
|
||||
#define ST7701_480_480_PANEL_60HZ_RGB_TIMING() \
|
||||
{ \
|
||||
.pclk_hz = 16 * 1000 * 1000, \
|
||||
.h_res = 480, \
|
||||
.v_res = 480, \
|
||||
.hsync_pulse_width = 10, \
|
||||
.hsync_back_porch = 10, \
|
||||
.hsync_front_porch = 20, \
|
||||
.vsync_pulse_width = 10, \
|
||||
.vsync_back_porch = 10, \
|
||||
.vsync_front_porch = 10, \
|
||||
.flags.pclk_active_neg = false, \
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////// Default Configuration Macros for MIPI-DSI Interface //////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
/**
|
||||
* @brief MIPI-DSI bus configuration structure
|
||||
*/
|
||||
#define ST7701_PANEL_BUS_DSI_2CH_CONFIG() \
|
||||
{ \
|
||||
.bus_id = 0, \
|
||||
.num_data_lanes = 2, \
|
||||
.phy_clk_src = MIPI_DSI_PHY_CLK_SRC_DEFAULT, \
|
||||
.lane_bit_rate_mbps = 500, \
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief MIPI-DBI panel IO configuration structure
|
||||
*
|
||||
*/
|
||||
#define ST7701_PANEL_IO_DBI_CONFIG() \
|
||||
{ \
|
||||
.virtual_channel = 0, \
|
||||
.lcd_cmd_bits = 8, \
|
||||
.lcd_param_bits = 8, \
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief MIPI DPI configuration structure
|
||||
*
|
||||
* @note refresh_rate = (dpi_clock_freq_mhz * 1000000) / (h_res + hsync_pulse_width + hsync_back_porch + hsync_front_porch)
|
||||
* / (v_res + vsync_pulse_width + vsync_back_porch + vsync_front_porch)
|
||||
*
|
||||
* @param[in] px_format Pixel format of the panel
|
||||
*
|
||||
*/
|
||||
#define ST7701_480_360_PANEL_60HZ_DPI_CONFIG(px_format) \
|
||||
{ \
|
||||
.virtual_channel = 0, \
|
||||
.dpi_clk_src = MIPI_DSI_DPI_CLK_SRC_DEFAULT, \
|
||||
.dpi_clock_freq_mhz = 34, \
|
||||
.pixel_format = px_format, \
|
||||
.num_fbs = 1, \
|
||||
.video_timing = { \
|
||||
.h_size = 480, \
|
||||
.v_size = 800, \
|
||||
.hsync_pulse_width = 12, \
|
||||
.hsync_back_porch = 42, \
|
||||
.hsync_front_porch = 42, \
|
||||
.vsync_pulse_width = 2, \
|
||||
.vsync_back_porch = 8, \
|
||||
.vsync_front_porch = 166, \
|
||||
}, \
|
||||
.flags = {.use_dma2d = true,} \
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "esp_err.h"
|
||||
#include "esp_lcd_types.h"
|
||||
#include "esp_lcd_panel_vendor.h"
|
||||
#include "soc/soc_caps.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define ST7701_CMD_SDIR (0xC7)
|
||||
#define ST7701_CMD_SS_BIT (1 << 2)
|
||||
|
||||
#define ST7701_CMD_CND2BKxSEL (0xFF)
|
||||
#define ST7701_CMD_BKxSEL_BYTE0 (0x77)
|
||||
#define ST7701_CMD_BKxSEL_BYTE1 (0x01)
|
||||
#define ST7701_CMD_BKxSEL_BYTE2 (0x00)
|
||||
#define ST7701_CMD_BKxSEL_BYTE3 (0x00)
|
||||
#define ST7701_CMD_CN2_BIT (1 << 4)
|
||||
#define ST7701_CMD_BKxSEL_BK0 (0x00)
|
||||
|
||||
#if SOC_LCD_RGB_SUPPORTED
|
||||
/**
|
||||
* @brief Initialize ST7701 LCD panel with RGB interface
|
||||
*
|
||||
* @param[in] io LCD panel IO handle
|
||||
* @param[in] panel_dev_config LCD panel device configuration
|
||||
* @param[out] ret_panel LCD panel handle
|
||||
* @return
|
||||
* - ESP_OK: Success
|
||||
* - Otherwise: Fail
|
||||
*/
|
||||
esp_err_t esp_lcd_new_panel_st7701_rgb(const esp_lcd_panel_io_handle_t io, const esp_lcd_panel_dev_config_t *panel_dev_config, esp_lcd_panel_handle_t *ret_panel);
|
||||
#endif
|
||||
|
||||
#if SOC_MIPI_DSI_SUPPORTED
|
||||
/**
|
||||
* @brief Initialize ST7701 LCD panel with MIPI interface
|
||||
*
|
||||
* @param[in] io LCD panel IO handle
|
||||
* @param[in] panel_dev_config LCD panel device configuration
|
||||
* @param[out] ret_panel LCD panel handle
|
||||
* @return
|
||||
* - ESP_OK: Success
|
||||
* - Otherwise: Fail
|
||||
*/
|
||||
esp_err_t esp_lcd_new_panel_st7701_mipi(const esp_lcd_panel_io_handle_t io, const esp_lcd_panel_dev_config_t *panel_dev_config,
|
||||
esp_lcd_panel_handle_t *ret_panel);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
+478
@@ -0,0 +1,478 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include "soc/soc_caps.h"
|
||||
|
||||
#if SOC_MIPI_DSI_SUPPORTED
|
||||
#include <stdlib.h>
|
||||
#include <sys/cdefs.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "esp_lcd_panel_interface.h"
|
||||
#include "esp_lcd_panel_io.h"
|
||||
#include "esp_lcd_panel_vendor.h"
|
||||
#include "esp_lcd_panel_ops.h"
|
||||
#include "esp_lcd_panel_commands.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_check.h"
|
||||
#include "esp_lcd_st7701.h"
|
||||
#include "esp_lcd_st7701_interface.h"
|
||||
|
||||
static const char *TAG = "st7701_mipi";
|
||||
|
||||
static esp_err_t panel_st7701_del(esp_lcd_panel_t *panel);
|
||||
static esp_err_t panel_st7701_reset(esp_lcd_panel_t *panel);
|
||||
static esp_err_t panel_st7701_init(esp_lcd_panel_t *panel);
|
||||
static esp_err_t panel_st7701_invert_color(esp_lcd_panel_t *panel, bool invert_color_data);
|
||||
static esp_err_t panel_st7701_mirror(esp_lcd_panel_t *panel, bool mirror_x, bool mirror_y);
|
||||
static esp_err_t panel_st7701_disp_on_off(esp_lcd_panel_t *panel, bool off);
|
||||
static esp_err_t panel_st7701_sleep(esp_lcd_panel_t *panel, bool sleep);
|
||||
|
||||
typedef struct {
|
||||
esp_lcd_panel_io_handle_t io;
|
||||
int reset_gpio_num;
|
||||
uint8_t madctl_val; // save current value of LCD_CMD_MADCTL register
|
||||
uint8_t colmod_val; // save surrent value of LCD_CMD_COLMOD register
|
||||
const st7701_lcd_init_cmd_t *init_cmds;
|
||||
uint16_t init_cmds_size;
|
||||
struct {
|
||||
unsigned int reset_level: 1;
|
||||
} flags;
|
||||
// To save the original functions of MIPI DPI panel
|
||||
esp_err_t (*del)(esp_lcd_panel_t *panel);
|
||||
esp_err_t (*init)(esp_lcd_panel_t *panel);
|
||||
} st7701_panel_t;
|
||||
|
||||
esp_err_t esp_lcd_new_panel_st7701_mipi(const esp_lcd_panel_io_handle_t io, const esp_lcd_panel_dev_config_t *panel_dev_config, esp_lcd_panel_handle_t *ret_panel)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(io && panel_dev_config && ret_panel, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
st7701_vendor_config_t *vendor_config = (st7701_vendor_config_t *)panel_dev_config->vendor_config;
|
||||
ESP_RETURN_ON_FALSE(vendor_config && vendor_config->mipi_config.dpi_config && vendor_config->mipi_config.dsi_bus, ESP_ERR_INVALID_ARG, TAG,
|
||||
"invalid vendor config");
|
||||
|
||||
esp_err_t ret = ESP_OK;
|
||||
st7701_panel_t *st7701 = (st7701_panel_t *)calloc(1, sizeof(st7701_panel_t));
|
||||
ESP_RETURN_ON_FALSE(st7701, ESP_ERR_NO_MEM, TAG, "no mem for st7701 panel");
|
||||
|
||||
if (panel_dev_config->reset_gpio_num >= 0) {
|
||||
gpio_config_t io_conf = {
|
||||
.mode = GPIO_MODE_OUTPUT,
|
||||
.pin_bit_mask = 1ULL << panel_dev_config->reset_gpio_num,
|
||||
};
|
||||
ESP_GOTO_ON_ERROR(gpio_config(&io_conf), err, TAG, "configure GPIO for RST line failed");
|
||||
}
|
||||
|
||||
switch (panel_dev_config->color_space) {
|
||||
case LCD_RGB_ELEMENT_ORDER_RGB:
|
||||
st7701->madctl_val = 0;
|
||||
break;
|
||||
case LCD_RGB_ELEMENT_ORDER_BGR:
|
||||
st7701->madctl_val |= LCD_CMD_BGR_BIT;
|
||||
break;
|
||||
default:
|
||||
ESP_GOTO_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, err, TAG, "unsupported rgb element order");
|
||||
break;
|
||||
}
|
||||
|
||||
switch (panel_dev_config->bits_per_pixel) {
|
||||
case 16: // RGB565
|
||||
st7701->colmod_val = 0x55;
|
||||
break;
|
||||
case 18: // RGB666
|
||||
st7701->colmod_val = 0x66;
|
||||
break;
|
||||
case 24: // RGB888
|
||||
st7701->colmod_val = 0x77;
|
||||
break;
|
||||
default:
|
||||
ESP_GOTO_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, err, TAG, "unsupported pixel width");
|
||||
break;
|
||||
}
|
||||
|
||||
st7701->io = io;
|
||||
st7701->init_cmds = vendor_config->init_cmds;
|
||||
st7701->init_cmds_size = vendor_config->init_cmds_size;
|
||||
st7701->reset_gpio_num = panel_dev_config->reset_gpio_num;
|
||||
st7701->flags.reset_level = panel_dev_config->flags.reset_active_high;
|
||||
|
||||
// Create MIPI DPI panel
|
||||
esp_lcd_panel_handle_t panel_handle = NULL;
|
||||
ESP_GOTO_ON_ERROR(esp_lcd_new_panel_dpi(vendor_config->mipi_config.dsi_bus, vendor_config->mipi_config.dpi_config, &panel_handle), err, TAG,
|
||||
"create MIPI DPI panel failed");
|
||||
ESP_LOGD(TAG, "new MIPI DPI panel @%p", panel_handle);
|
||||
|
||||
// Save the original functions of MIPI DPI panel
|
||||
st7701->del = panel_handle->del;
|
||||
st7701->init = panel_handle->init;
|
||||
// Overwrite the functions of MIPI DPI panel
|
||||
panel_handle->del = panel_st7701_del;
|
||||
panel_handle->init = panel_st7701_init;
|
||||
panel_handle->reset = panel_st7701_reset;
|
||||
panel_handle->mirror = panel_st7701_mirror;
|
||||
panel_handle->invert_color = panel_st7701_invert_color;
|
||||
panel_handle->disp_on_off = panel_st7701_disp_on_off;
|
||||
panel_handle->disp_sleep = panel_st7701_sleep;
|
||||
panel_handle->user_data = st7701;
|
||||
*ret_panel = panel_handle;
|
||||
ESP_LOGD(TAG, "new st7701 panel @%p", st7701);
|
||||
|
||||
return ESP_OK;
|
||||
err:
|
||||
if (st7701) {
|
||||
if (panel_dev_config->reset_gpio_num >= 0) {
|
||||
gpio_reset_pin(panel_dev_config->reset_gpio_num);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t panel_st7701_del(esp_lcd_panel_t *panel)
|
||||
{
|
||||
st7701_panel_t *st7701 = (st7701_panel_t *)panel->user_data;
|
||||
|
||||
if (st7701->reset_gpio_num >= 0) {
|
||||
gpio_reset_pin(st7701->reset_gpio_num);
|
||||
}
|
||||
|
||||
// Delete MIPI DPI panel
|
||||
st7701->del(panel);
|
||||
ESP_LOGD(TAG, "del st7701 panel @%p", st7701);
|
||||
free(st7701);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t panel_st7701_reset(esp_lcd_panel_t *panel)
|
||||
{
|
||||
st7701_panel_t *st7701 = (st7701_panel_t *)panel->user_data;
|
||||
esp_lcd_panel_io_handle_t io = st7701->io;
|
||||
|
||||
// perform hardware reset
|
||||
if (st7701->reset_gpio_num >= 0) {
|
||||
gpio_set_level(st7701->reset_gpio_num, st7701->flags.reset_level);
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
gpio_set_level(st7701->reset_gpio_num, !st7701->flags.reset_level);
|
||||
vTaskDelay(pdMS_TO_TICKS(120));
|
||||
} else if (io) { // perform software reset
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_SWRESET, NULL, 0), TAG, "send command failed");
|
||||
vTaskDelay(pdMS_TO_TICKS(20)); // spec, wait at least 5ms before sending new command
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static const st7701_lcd_init_cmd_t vendor_specific_init_default[] = {
|
||||
// {0xFF, (uint8_t []){0x77, 0x01, 0x00, 0x00, 0x13}, 5, 0},
|
||||
// {0xEF, (uint8_t []){0x08}, 1, 0},
|
||||
// {0xFF, (uint8_t []){0x77, 0x01, 0x00, 0x00, 0x10}, 5, 0},
|
||||
// {0xC0, (uint8_t []){0x2c, 0x00}, 2, 0},
|
||||
// {0xC1, (uint8_t []){0x10, 0x0C}, 2, 0},
|
||||
// {0xC2, (uint8_t []){0x21, 0x0A}, 2, 0},
|
||||
// {0xCC, (uint8_t []){0x10}, 1, 0},
|
||||
// {0xB0, (uint8_t []){0x00, 0x0B, 0x12, 0x0D, 0x10, 0x06, 0x02, 0x08, 0x07, 0x1F, 0x04, 0x11, 0x0F, 0x29, 0x31, 0x1E}, 16, 0},
|
||||
// {0xB1, (uint8_t []){0x00, 0x0B, 0x13, 0x0D, 0x11, 0x06, 0x03, 0x08, 0x07, 0x20, 0x04, 0x12, 0x11, 0x29, 0x31, 0x1E}, 16, 0},
|
||||
// {0xFF, (uint8_t []){0x77, 0x01, 0x00, 0x00, 0x11}, 5, 0},
|
||||
// {0xB0, (uint8_t []){0x5D}, 1, 0},
|
||||
// {0xB1, (uint8_t []){0x72}, 1, 0},
|
||||
// {0xB2, (uint8_t []){0x84}, 1, 0},
|
||||
// {0xB3, (uint8_t []){0x80}, 1, 0},
|
||||
// {0xB5, (uint8_t []){0x4D}, 1, 0},
|
||||
// {0xB7, (uint8_t []){0x85}, 1, 0},
|
||||
// {0xB8, (uint8_t []){0x20}, 1, 0},
|
||||
// {0xC1, (uint8_t []){0x78}, 1, 0},
|
||||
// {0xC2, (uint8_t []){0x78}, 1, 0},
|
||||
// {0xD0, (uint8_t []){0x88}, 1, 0},
|
||||
// {0xE0, (uint8_t []){0x80, 0x00, 0x02}, 3, 0},
|
||||
// {0xE1, (uint8_t []){0x05, 0x00, 0x07, 0x00, 0x06, 0x00, 0x08, 0x00, 0x00, 0x33, 0x33}, 11, 0},
|
||||
// {0xE2, (uint8_t []){0x00, 0x00, 0x30, 0x30, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00}, 12, 0},
|
||||
// {0xE3, (uint8_t []){0x00, 0x00, 0x11, 0x11}, 4, 0},
|
||||
// {0xE4, (uint8_t []){0x44, 0x44}, 2, 0},
|
||||
// {0xE5, (uint8_t []){0x0C, 0x78, 0x00, 0xE0, 0x0E, 0x7A, 0x00, 0xE0, 0x08, 0x74, 0x00, 0xE0, 0x0A, 0x76, 0x00, 0xE0}, 16, 0},
|
||||
// {0xE6, (uint8_t []){0x00, 0x00, 0x11, 0x11}, 4, 0},
|
||||
// {0xE7, (uint8_t []){0x44, 0x44}, 2, 0},
|
||||
// {0xE8, (uint8_t []){0x0D, 0x79, 0x00, 0xE0, 0x0F, 0x7B, 0x00, 0xE0, 0x09, 0x75, 0x00, 0xE0, 0x0B, 0x77, 0x00, 0xE0}, 16, 0},
|
||||
// {0xE9, (uint8_t []){0x36, 0x00}, 2, 0},
|
||||
// {0xEB, (uint8_t []){0x00, 0x01, 0xE4, 0xE4, 0x44, 0x88, 0x40}, 7, 0},
|
||||
// {0xED, (uint8_t []){0xA1, 0xC2, 0xFB, 0x0F, 0x67, 0x45, 0xFF, 0xFF, 0xFF, 0xFF, 0x54, 0x76, 0xF0, 0xBF, 0x2C, 0x1A}, 16, 0},
|
||||
// {0xEF, (uint8_t []){0x10, 0x0D, 0x04, 0x08, 0x3F, 0x1F}, 6, 0},
|
||||
// {0xFF, (uint8_t []){0x77, 0x01, 0x00, 0x00, 0x13}, 5, 0},
|
||||
// {0xE8, (uint8_t []){0x00, 0x0E}, 2, 0},
|
||||
// {0xE8, (uint8_t []){0x00, 0x0C}, 2, 20},
|
||||
// {0xE8, (uint8_t []){0x00, 0x00}, 2, 0},
|
||||
// {0xFF, (uint8_t []){0x77, 0x01, 0x00, 0x00, 0x00}, 5, 0},
|
||||
// {0x11, (uint8_t []){0x00}, 0, 120},
|
||||
// {0x29, (uint8_t []){0x00}, 0, 0},
|
||||
|
||||
// {0xFF, (uint8_t []){0x77, 0x01, 0x00, 0x00, 0x12}, 5, 0}, /* This part of the parameters can be used for screen self-test */
|
||||
// {0xD1, (uint8_t []){0x81}, 1, 0},
|
||||
// {0xD2, (uint8_t []){0x08}, 1, 0},
|
||||
|
||||
/*
|
||||
|
||||
*/
|
||||
|
||||
{0xFF, (uint8_t []){0x77,0x01,0x00,0x00,0x13},5,0},
|
||||
{0xEF, (uint8_t []){0x08}, 1, 0},
|
||||
{0xFF, (uint8_t []){0x77,0x01,0x00,0x00,0x10},5,0},
|
||||
{0xC0, (uint8_t []){0x63, 0x00}, 2, 0},
|
||||
{0xC1, (uint8_t []){0x0D, 0x02}, 2, 0},
|
||||
{0xC2, (uint8_t []){0x10, 0x08}, 2, 0},
|
||||
{0xCC, (uint8_t []){0x10}, 1, 0},
|
||||
|
||||
{0xB0, (uint8_t []){0x80, 0x09, 0x53, 0x0C, 0xD0, 0x07, 0x0C, 0x09, 0x09, 0x28, 0x06, 0xD4, 0x13, 0x69, 0x2B, 0x71}, 16, 0},
|
||||
{0xB1, (uint8_t []){0x80, 0x94, 0x5A, 0x10, 0xD3, 0x06, 0x0A, 0x08, 0x08, 0x25, 0x03, 0xD3, 0x12, 0x66, 0x6A, 0x0D}, 16, 0},
|
||||
{0xFF, (uint8_t []){0x77, 0x01, 0x00, 0x00, 0x11}, 5, 0},
|
||||
|
||||
{0xB0, (uint8_t []){0x5D}, 1, 0},
|
||||
{0xB1, (uint8_t []){0x58}, 1, 0},
|
||||
{0xB2, (uint8_t []){0x87}, 1, 0},
|
||||
{0xB3, (uint8_t []){0x80}, 1, 0},
|
||||
{0xB5, (uint8_t []){0x4E}, 1, 0},
|
||||
{0xB7, (uint8_t []){0x85}, 1, 0},
|
||||
{0xB8, (uint8_t []){0x21}, 1, 0},
|
||||
{0xB9, (uint8_t []){0x10, 0x1F}, 2, 0},
|
||||
{0xBB, (uint8_t []){0x03}, 1,0},
|
||||
{0xBC, (uint8_t []){0x00}, 1,0},
|
||||
|
||||
{0xC1, (uint8_t []){0x78}, 1, 0},
|
||||
{0xC2, (uint8_t []){0x78}, 1, 0},
|
||||
{0xD0, (uint8_t []){0x88}, 1, 0},
|
||||
|
||||
{0xE0, (uint8_t []){0x00, 0x3A, 0x02}, 3, 0},
|
||||
{0xE1, (uint8_t []){0x04, 0xA0, 0x00, 0xA0, 0x05,0xA0, 0x00, 0xA0, 0x00, 0x40, 0x40}, 11, 0},
|
||||
{0xE2, (uint8_t []){0x30, 0x00, 0x40, 0x40, 0x32, 0xA0, 0x00, 0xA0, 0x00, 0xA0, 0x00, 0xA0, 0x00}, 13, 0},
|
||||
{0xE3, (uint8_t []){0x00, 0x00, 0x33, 0x33}, 4, 0},
|
||||
{0xE4, (uint8_t []){0x44, 0x44}, 2, 0},
|
||||
{0xE5, (uint8_t []){0x09, 0x2E, 0xA0, 0xA0, 0x0B, 0x30, 0xA0, 0xA0, 0x05, 0x2A, 0xA0, 0xA0, 0x07, 0x2C, 0xA0, 0xA0}, 16, 0},
|
||||
{0xE6, (uint8_t []){0x00, 0x00, 0x33, 0x33}, 4, 0},
|
||||
{0xE7, (uint8_t []){0x44, 0x44}, 2, 0},
|
||||
{0xE8, (uint8_t []){0x08, 0x2D, 0xA0, 0xA0, 0x0A, 0x2F, 0xA0, 0xA0, 0x04, 0x29, 0xA0, 0xA0, 0x06, 0x2B, 0xA0, 0xA0}, 16, 0},
|
||||
|
||||
{0xEB, (uint8_t []){0x00, 0x00, 0x4E, 0x4E, 0x00, 0x00, 0x00}, 7, 0},
|
||||
{0xEC, (uint8_t []){0x08, 0x01}, 2, 0},
|
||||
|
||||
{0xED, (uint8_t []){0xB0, 0x2B, 0x98, 0xA4, 0x56, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF7, 0x65, 0x4A, 0x89, 0xB2, 0x0B}, 16, 0},
|
||||
{0xEF, (uint8_t []){0x08, 0x08, 0x08, 0x45, 0x3F, 0x54}, 6, 0},
|
||||
{0xFF, (uint8_t []){0x77, 0x01, 0x00, 0x00, 0x00}, 5, 0},
|
||||
|
||||
// {0x3A, (uint8_t []){0x66}, 1, 0},
|
||||
{0x11, (uint8_t []){0x00}, 1, 120},
|
||||
{0x29, (uint8_t []){0x00}, 1, 20},
|
||||
|
||||
|
||||
|
||||
// {0xFF, (uint8_t []){0x77,0x01,0x00,0x00,0x13},5,0},
|
||||
// {0xEF, (uint8_t []){0x08}, 1, 0},
|
||||
// {0xFF, (uint8_t []){0x77,0x01,0x00,0x00,0x10},5,0},
|
||||
// {0xC0, (uint8_t []){0x63, 0x00}, 2, 0},
|
||||
// {0xC1, (uint8_t []){0x0D, 0x02}, 2, 0},
|
||||
// {0xC2, (uint8_t []){0x17, 0x08}, 2, 0},
|
||||
// {0xCC, (uint8_t []){0x10}, 1, 0},
|
||||
|
||||
// {0xB0, (uint8_t []){0x40, 0xC9, 0x94, 0x0E, 0x10, 0x05, 0x0B, 0x09, 0x08, 0x26, 0x04, 0x52, 0x10, 0x69, 0x6B, 0x69}, 16, 0},
|
||||
// {0xB1, (uint8_t []){0x40, 0xD2, 0x98, 0x0C, 0x92, 0x07, 0x09, 0x08, 0x07, 0x25, 0x02, 0x0E, 0x1C, 0x6E, 0x78, 0x55}, 16, 0},
|
||||
// {0xFF, (uint8_t []){0x77, 0x01, 0x00, 0x00, 0x11}, 5, 0},
|
||||
|
||||
// {0xB0, (uint8_t []){0x5D}, 1, 0},
|
||||
// {0xB1, (uint8_t []){0x4E}, 1, 0},
|
||||
// {0xB2, (uint8_t []){0x87}, 1, 0},
|
||||
// {0xB3, (uint8_t []){0x80}, 1, 0},
|
||||
// {0xB5, (uint8_t []){0x4E}, 1, 0},
|
||||
// {0xB7, (uint8_t []){0x85}, 1, 0},
|
||||
// {0xB8, (uint8_t []){0x21}, 1, 0},
|
||||
// {0xB9, (uint8_t []){0x10, 0x1F}, 2, 0},
|
||||
// {0xBB, (uint8_t []){0x03}, 1,0},
|
||||
// {0xBC, (uint8_t []){0x00}, 1,0},
|
||||
|
||||
// {0xC1, (uint8_t []){0x78}, 1, 0},
|
||||
// {0xC2, (uint8_t []){0x78}, 1, 0},
|
||||
// {0xD0, (uint8_t []){0x88}, 1, 0},
|
||||
|
||||
// {0xE0, (uint8_t []){0x00, 0x3A, 0x02}, 3, 0},
|
||||
// {0xE1, (uint8_t []){0x04, 0xA0, 0x00, 0xA0, 0x05,0xA0, 0x00, 0xA0, 0x00, 0x40, 0x40}, 11, 0},
|
||||
// {0xE2, (uint8_t []){0x30, 0x00, 0x40, 0x40, 0x32, 0xA0, 0x00, 0xA0, 0x00, 0xA0, 0x00, 0xA0, 0x00}, 13, 0},
|
||||
// {0xE3, (uint8_t []){0x00, 0x00, 0x33, 0x33}, 4, 0},
|
||||
// {0xE4, (uint8_t []){0x44, 0x44}, 2, 0},
|
||||
// {0xE5, (uint8_t []){0x09, 0x2E, 0xA0, 0xA0, 0x0B, 0x30, 0xA0, 0xA0, 0x05, 0x2A, 0xA0, 0xA0, 0x07, 0x2C, 0xA0, 0xA0}, 16, 0},
|
||||
// {0xE6, (uint8_t []){0x00, 0x00, 0x33, 0x33}, 4, 0},
|
||||
// {0xE7, (uint8_t []){0x44, 0x44}, 2, 0},
|
||||
// {0xE8, (uint8_t []){0x08, 0x2D, 0xA0, 0xA0, 0x0A, 0x2F, 0xA0, 0xA0, 0x04, 0x29, 0xA0, 0xA0, 0x06, 0x2B, 0xA0, 0xA0}, 16, 0},
|
||||
|
||||
// {0xEB, (uint8_t []){0x00, 0x00, 0x4E, 0x4E, 0x00, 0x00, 0x00}, 7, 0},
|
||||
// {0xEC, (uint8_t []){0x08, 0x01}, 2, 0},
|
||||
|
||||
// {0xED, (uint8_t []){0xB0, 0x2B, 0x98, 0xA4, 0x56, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF7, 0x65, 0x4A, 0x89, 0xB2, 0x0B}, 16, 0},
|
||||
// {0xEF, (uint8_t []){0x08, 0x08, 0x08, 0x45, 0x3F, 0x54}, 6, 0},
|
||||
// {0xFF, (uint8_t []){0x77, 0x01, 0x00, 0x00, 0x00}, 5, 0},
|
||||
|
||||
// // {0x3A, (uint8_t []){0x66}, 1, 0},
|
||||
// {0x11, (uint8_t []){0x00}, 1, 120},
|
||||
// {0x29, (uint8_t []){0x00}, 1, 20},
|
||||
|
||||
|
||||
};
|
||||
|
||||
static esp_err_t panel_st7701_init(esp_lcd_panel_t *panel)
|
||||
{
|
||||
st7701_panel_t *st7701 = (st7701_panel_t *)panel->user_data;
|
||||
esp_lcd_panel_io_handle_t io = st7701->io;
|
||||
const st7701_lcd_init_cmd_t *init_cmds = NULL;
|
||||
uint16_t init_cmds_size = 0;
|
||||
bool is_command2_disable = true;
|
||||
bool is_cmd_overwritten = false;
|
||||
|
||||
uint8_t ID[3];
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_rx_param(io, 0x04, ID, 3), TAG, "read ID failed");
|
||||
ESP_LOGI(TAG, "LCD ID: %02X %02X %02X", ID[0], ID[1], ID[2]);
|
||||
|
||||
// back to CMD_Page 0
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, ST7701_CMD_CND2BKxSEL, (uint8_t []) {
|
||||
ST7701_CMD_BKxSEL_BYTE0, ST7701_CMD_BKxSEL_BYTE1, ST7701_CMD_BKxSEL_BYTE2, ST7701_CMD_BKxSEL_BYTE3, 0x00
|
||||
}, 5), TAG, "Write cmd failed");
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL, (uint8_t[]) {
|
||||
st7701->madctl_val,
|
||||
}, 1), TAG, "send command failed");
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_COLMOD, (uint8_t[]) {
|
||||
st7701->colmod_val,
|
||||
}, 1), TAG, "send command failed");
|
||||
ESP_LOGI(TAG, " st7701->madctl_val: 0x%x, st7701->colmod_val: 0x%x", st7701->madctl_val, st7701->colmod_val);
|
||||
|
||||
// vendor specific initialization, it can be different between manufacturers
|
||||
// should consult the LCD supplier for initialization sequence code
|
||||
if (st7701->init_cmds) {
|
||||
init_cmds = st7701->init_cmds;
|
||||
init_cmds_size = st7701->init_cmds_size;
|
||||
} else {
|
||||
init_cmds = vendor_specific_init_default;
|
||||
init_cmds_size = sizeof(vendor_specific_init_default) / sizeof(st7701_lcd_init_cmd_t);
|
||||
}
|
||||
|
||||
for (int i = 0; i < init_cmds_size; i++) {
|
||||
// Check if the command has been used or conflicts with the internal only when command2 is disable
|
||||
if (is_command2_disable && (init_cmds[i].data_bytes > 0)) {
|
||||
switch (init_cmds[i].cmd) {
|
||||
case LCD_CMD_MADCTL:
|
||||
is_cmd_overwritten = true;
|
||||
st7701->madctl_val = ((uint8_t *)init_cmds[i].data)[0];
|
||||
break;
|
||||
case LCD_CMD_COLMOD:
|
||||
is_cmd_overwritten = true;
|
||||
st7701->colmod_val = ((uint8_t *)init_cmds[i].data)[0];
|
||||
break;
|
||||
default:
|
||||
is_cmd_overwritten = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (is_cmd_overwritten) {
|
||||
is_cmd_overwritten = false;
|
||||
ESP_LOGW(TAG, "The %02Xh command has been used and will be overwritten by external initialization sequence",
|
||||
init_cmds[i].cmd);
|
||||
}
|
||||
}
|
||||
|
||||
// Send command
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, init_cmds[i].cmd, init_cmds[i].data, init_cmds[i].data_bytes),
|
||||
TAG, "send command failed");
|
||||
vTaskDelay(pdMS_TO_TICKS(init_cmds[i].delay_ms));
|
||||
|
||||
// Check if the current cmd is the command2 disable cmd
|
||||
if ((init_cmds[i].cmd == ST7701_CMD_CND2BKxSEL) && (init_cmds[i].data_bytes > 4)) {
|
||||
is_command2_disable = !(((uint8_t *)init_cmds[i].data)[4] & ST7701_CMD_CN2_BIT);
|
||||
}
|
||||
}
|
||||
ESP_LOGD(TAG, "send init commands success");
|
||||
|
||||
ESP_RETURN_ON_ERROR(st7701->init(panel), TAG, "init MIPI DPI panel failed");
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t panel_st7701_invert_color(esp_lcd_panel_t *panel, bool invert_color_data)
|
||||
{
|
||||
st7701_panel_t *st7701 = (st7701_panel_t *)panel->user_data;
|
||||
esp_lcd_panel_io_handle_t io = st7701->io;
|
||||
int command = 0;
|
||||
if (invert_color_data) {
|
||||
command = LCD_CMD_INVON;
|
||||
} else {
|
||||
command = LCD_CMD_INVOFF;
|
||||
}
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, command, NULL, 0), TAG, "send command failed");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t panel_st7701_mirror(esp_lcd_panel_t *panel, bool mirror_x, bool mirror_y)
|
||||
{
|
||||
st7701_panel_t *st7701 = (st7701_panel_t *)panel->user_data;
|
||||
esp_lcd_panel_io_handle_t io = st7701->io;
|
||||
uint8_t sdir_val = 0;
|
||||
|
||||
ESP_RETURN_ON_FALSE(io, ESP_FAIL, TAG, "Panel IO is deleted, cannot send command");
|
||||
// Control mirror through LCD command
|
||||
if (mirror_x) {
|
||||
sdir_val = ST7701_CMD_SS_BIT;
|
||||
} else {
|
||||
sdir_val = 0;
|
||||
}
|
||||
if (mirror_y) {
|
||||
st7701->madctl_val |= LCD_CMD_ML_BIT;
|
||||
} else {
|
||||
st7701->madctl_val &= ~LCD_CMD_ML_BIT;
|
||||
}
|
||||
|
||||
// Enable the Command2 BK0
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, ST7701_CMD_CND2BKxSEL, (uint8_t []) {
|
||||
ST7701_CMD_BKxSEL_BYTE0, ST7701_CMD_BKxSEL_BYTE1, ST7701_CMD_BKxSEL_BYTE2, ST7701_CMD_BKxSEL_BYTE3,
|
||||
ST7701_CMD_BKxSEL_BK0 | ST7701_CMD_CN2_BIT,
|
||||
}, 5), TAG, "send command failed");
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, ST7701_CMD_SDIR, (uint8_t[]) {
|
||||
sdir_val,
|
||||
}, 1), TAG, "send command failed");;
|
||||
|
||||
// Disable Command2
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, ST7701_CMD_CND2BKxSEL, (uint8_t []) {
|
||||
ST7701_CMD_BKxSEL_BYTE0, ST7701_CMD_BKxSEL_BYTE1, ST7701_CMD_BKxSEL_BYTE2, ST7701_CMD_BKxSEL_BYTE3, 0,
|
||||
}, 5), TAG, "send command failed");
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL, (uint8_t[]) {
|
||||
st7701->madctl_val,
|
||||
}, 1), TAG, "send command failed");;
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t panel_st7701_disp_on_off(esp_lcd_panel_t *panel, bool on_off)
|
||||
{
|
||||
st7701_panel_t *st7701 = (st7701_panel_t *)panel->user_data;
|
||||
esp_lcd_panel_io_handle_t io = st7701->io;
|
||||
int command = 0;
|
||||
|
||||
if (on_off) {
|
||||
command = LCD_CMD_DISPON;
|
||||
} else {
|
||||
command = LCD_CMD_DISPOFF;
|
||||
}
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, command, NULL, 0), TAG, "send command failed");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t panel_st7701_sleep(esp_lcd_panel_t *panel, bool sleep)
|
||||
{
|
||||
st7701_panel_t *st7701 = (st7701_panel_t *)panel->user_data;
|
||||
esp_lcd_panel_io_handle_t io = st7701->io;
|
||||
int command = 0;
|
||||
if (sleep) {
|
||||
command = LCD_CMD_SLPIN;
|
||||
} else {
|
||||
command = LCD_CMD_SLPOUT;
|
||||
}
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, command, NULL, 0), TAG,
|
||||
"io tx param failed");
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
#endif
|
||||
+386
@@ -0,0 +1,386 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include "soc/soc_caps.h"
|
||||
|
||||
#if SOC_LCD_RGB_SUPPORTED
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "esp_check.h"
|
||||
#include "esp_lcd_panel_commands.h"
|
||||
#include "esp_lcd_panel_interface.h"
|
||||
#include "esp_lcd_panel_io.h"
|
||||
#include "esp_lcd_panel_rgb.h"
|
||||
#include "esp_lcd_panel_vendor.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_lcd_st7701.h"
|
||||
#include "esp_lcd_st7701_interface.h"
|
||||
typedef struct {
|
||||
esp_lcd_panel_io_handle_t io;
|
||||
int reset_gpio_num;
|
||||
uint8_t madctl_val; // Save current value of LCD_CMD_MADCTL register
|
||||
uint8_t colmod_val; // Save current value of LCD_CMD_COLMOD register
|
||||
const st7701_lcd_init_cmd_t *init_cmds;
|
||||
uint16_t init_cmds_size;
|
||||
struct {
|
||||
unsigned int mirror_by_cmd: 1;
|
||||
unsigned int enable_io_multiplex: 1;
|
||||
unsigned int display_on_off_use_cmd: 1;
|
||||
unsigned int reset_level: 1;
|
||||
} flags;
|
||||
// To save the original functions of RGB panel
|
||||
esp_err_t (*init)(esp_lcd_panel_t *panel);
|
||||
esp_err_t (*del)(esp_lcd_panel_t *panel);
|
||||
esp_err_t (*reset)(esp_lcd_panel_t *panel);
|
||||
esp_err_t (*mirror)(esp_lcd_panel_t *panel, bool x_axis, bool y_axis);
|
||||
esp_err_t (*disp_on_off)(esp_lcd_panel_t *panel, bool on_off);
|
||||
} st7701_panel_t;
|
||||
|
||||
static const char *TAG = "st7701_rgb";
|
||||
|
||||
static esp_err_t panel_st7701_send_init_cmds(st7701_panel_t *st7701);
|
||||
|
||||
static esp_err_t panel_st7701_init(esp_lcd_panel_t *panel);
|
||||
static esp_err_t panel_st7701_del(esp_lcd_panel_t *panel);
|
||||
static esp_err_t panel_st7701_reset(esp_lcd_panel_t *panel);
|
||||
static esp_err_t panel_st7701_mirror(esp_lcd_panel_t *panel, bool mirror_x, bool mirror_y);
|
||||
static esp_err_t panel_st7701_disp_on_off(esp_lcd_panel_t *panel, bool off);
|
||||
|
||||
esp_err_t esp_lcd_new_panel_st7701_rgb(const esp_lcd_panel_io_handle_t io, const esp_lcd_panel_dev_config_t *panel_dev_config,
|
||||
esp_lcd_panel_handle_t *ret_panel)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(io && panel_dev_config && ret_panel, ESP_ERR_INVALID_ARG, TAG, "invalid arguments");
|
||||
st7701_vendor_config_t *vendor_config = (st7701_vendor_config_t *)panel_dev_config->vendor_config;
|
||||
ESP_RETURN_ON_FALSE(vendor_config && vendor_config->rgb_config, ESP_ERR_INVALID_ARG, TAG, "`verndor_config` and `rgb_config` are necessary");
|
||||
ESP_RETURN_ON_FALSE(!vendor_config->flags.enable_io_multiplex || !vendor_config->flags.mirror_by_cmd,
|
||||
ESP_ERR_INVALID_ARG, TAG, "`mirror_by_cmd` and `enable_io_multiplex` cannot work together");
|
||||
|
||||
esp_err_t ret = ESP_OK;
|
||||
st7701_panel_t *st7701 = (st7701_panel_t *)calloc(1, sizeof(st7701_panel_t));
|
||||
ESP_RETURN_ON_FALSE(st7701, ESP_ERR_NO_MEM, TAG, "no mem for st7701 panel");
|
||||
|
||||
if (panel_dev_config->reset_gpio_num >= 0) {
|
||||
gpio_config_t io_conf = {
|
||||
.mode = GPIO_MODE_OUTPUT,
|
||||
.pin_bit_mask = 1ULL << panel_dev_config->reset_gpio_num,
|
||||
};
|
||||
ESP_GOTO_ON_ERROR(gpio_config(&io_conf), err, TAG, "configure GPIO for RST line failed");
|
||||
}
|
||||
|
||||
switch (panel_dev_config->rgb_ele_order) {
|
||||
case LCD_RGB_ELEMENT_ORDER_RGB:
|
||||
st7701->madctl_val = 0;
|
||||
break;
|
||||
case LCD_RGB_ELEMENT_ORDER_BGR:
|
||||
st7701->madctl_val |= LCD_CMD_BGR_BIT;
|
||||
break;
|
||||
default:
|
||||
ESP_GOTO_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, err, TAG, "unsupported color element order");
|
||||
break;
|
||||
}
|
||||
|
||||
st7701->colmod_val = 0;
|
||||
switch (panel_dev_config->bits_per_pixel) {
|
||||
case 16: // RGB565
|
||||
st7701->colmod_val = 0x50;
|
||||
break;
|
||||
case 18: // RGB666
|
||||
st7701->colmod_val = 0x60;
|
||||
break;
|
||||
case 24: // RGB888
|
||||
st7701->colmod_val = 0x70;
|
||||
break;
|
||||
default:
|
||||
ESP_GOTO_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, err, TAG, "unsupported pixel width");
|
||||
break;
|
||||
}
|
||||
|
||||
st7701->io = io;
|
||||
st7701->init_cmds = vendor_config->init_cmds;
|
||||
st7701->init_cmds_size = vendor_config->init_cmds_size;
|
||||
st7701->reset_gpio_num = panel_dev_config->reset_gpio_num;
|
||||
st7701->flags.mirror_by_cmd = vendor_config->flags.mirror_by_cmd;
|
||||
st7701->flags.display_on_off_use_cmd = (vendor_config->rgb_config->disp_gpio_num >= 0) ? 0 : 1;
|
||||
st7701->flags.enable_io_multiplex = vendor_config->flags.enable_io_multiplex;
|
||||
st7701->flags.reset_level = panel_dev_config->flags.reset_active_high;
|
||||
|
||||
if (st7701->flags.enable_io_multiplex) {
|
||||
if (st7701->reset_gpio_num >= 0) { // Perform hardware reset
|
||||
gpio_set_level(st7701->reset_gpio_num, st7701->flags.reset_level);
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
gpio_set_level(st7701->reset_gpio_num, !st7701->flags.reset_level);
|
||||
} else { // Perform software reset
|
||||
ESP_GOTO_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_SWRESET, NULL, 0), err, TAG, "send command failed");
|
||||
}
|
||||
vTaskDelay(pdMS_TO_TICKS(120));
|
||||
|
||||
/**
|
||||
* In order to enable the 3-wire SPI interface pins (such as SDA and SCK) to share other pins of the RGB interface
|
||||
* (such as HSYNC) and save GPIOs, we need to send LCD initialization commands via the 3-wire SPI interface before
|
||||
* `esp_lcd_new_rgb_panel()` is called.
|
||||
*/
|
||||
ESP_GOTO_ON_ERROR(panel_st7701_send_init_cmds(st7701), err, TAG, "send init commands failed");
|
||||
// After sending the initialization commands, the 3-wire SPI interface can be deleted
|
||||
ESP_GOTO_ON_ERROR(esp_lcd_panel_io_del(io), err, TAG, "delete panel IO failed");
|
||||
st7701->io = NULL;
|
||||
ESP_LOGD(TAG, "delete panel IO");
|
||||
}
|
||||
|
||||
// Create RGB panel
|
||||
ESP_GOTO_ON_ERROR(esp_lcd_new_rgb_panel(vendor_config->rgb_config, ret_panel), err, TAG, "create RGB panel failed");
|
||||
ESP_LOGD(TAG, "new RGB panel @%p", ret_panel);
|
||||
|
||||
// Save the original functions of RGB panel
|
||||
st7701->init = (*ret_panel)->init;
|
||||
st7701->del = (*ret_panel)->del;
|
||||
st7701->reset = (*ret_panel)->reset;
|
||||
st7701->mirror = (*ret_panel)->mirror;
|
||||
st7701->disp_on_off = (*ret_panel)->disp_on_off;
|
||||
// Overwrite the functions of RGB panel
|
||||
(*ret_panel)->init = panel_st7701_init;
|
||||
(*ret_panel)->del = panel_st7701_del;
|
||||
(*ret_panel)->reset = panel_st7701_reset;
|
||||
(*ret_panel)->mirror = panel_st7701_mirror;
|
||||
(*ret_panel)->disp_on_off = panel_st7701_disp_on_off;
|
||||
(*ret_panel)->user_data = st7701;
|
||||
ESP_LOGD(TAG, "new st7701 panel @%p", st7701);
|
||||
|
||||
return ESP_OK;
|
||||
|
||||
err:
|
||||
if (st7701) {
|
||||
if (panel_dev_config->reset_gpio_num >= 0) {
|
||||
gpio_reset_pin(panel_dev_config->reset_gpio_num);
|
||||
}
|
||||
free(st7701);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const st7701_lcd_init_cmd_t vendor_specific_init_default[] = {
|
||||
// {cmd, { data }, data_size, delay_ms}
|
||||
{0xFF, (uint8_t []){0x77, 0x01, 0x00, 0x00, 0x13}, 5, 0},
|
||||
{0xEF, (uint8_t []){0x08}, 1, 0},
|
||||
{0xFF, (uint8_t []){0x77, 0x01, 0x00, 0x00, 0x10}, 5, 0},
|
||||
{0xC0, (uint8_t []){0x3B, 0x00}, 2, 0},
|
||||
{0xC1, (uint8_t []){0x10, 0x02}, 2, 0},
|
||||
{0xC2, (uint8_t []){0x20, 0x06}, 2, 0},
|
||||
{0xCC, (uint8_t []){0x10}, 1, 0},
|
||||
{0xB0, (uint8_t []){0x00, 0x13, 0x5A, 0x0F, 0x12, 0x07, 0x09, 0x08, 0x08, 0x24, 0x07, 0x13, 0x12, 0x6B, 0x73, 0xFF}, 16, 0},
|
||||
{0xB1, (uint8_t []){0x00, 0x13, 0x5A, 0x0F, 0x12, 0x07, 0x09, 0x08, 0x08, 0x24, 0x07, 0x13, 0x12, 0x6B, 0x73, 0xFF}, 16, 0},
|
||||
{0xFF, (uint8_t []){0x77, 0x01, 0x00, 0x00, 0x11}, 5, 0},
|
||||
{0xB0, (uint8_t []){0x8D}, 1, 0},
|
||||
{0xB1, (uint8_t []){0x48}, 1, 0},
|
||||
{0xB2, (uint8_t []){0x89}, 1, 0},
|
||||
{0xB3, (uint8_t []){0x80}, 1, 0},
|
||||
{0xB5, (uint8_t []){0x49}, 1, 0},
|
||||
{0xB7, (uint8_t []){0x85}, 1, 0},
|
||||
{0xB8, (uint8_t []){0x32}, 1, 0},
|
||||
{0xC1, (uint8_t []){0x78}, 1, 0},
|
||||
{0xC2, (uint8_t []){0x78}, 1, 0},
|
||||
{0xD0, (uint8_t []){0x88}, 1, 100},
|
||||
{0xE0, (uint8_t []){0x00, 0x00, 0x02}, 3, 0},
|
||||
{0xE1, (uint8_t []){0x05, 0xC0, 0x07, 0xC0, 0x04, 0xC0, 0x06, 0xC0, 0x00, 0x44, 0x44}, 11, 0},
|
||||
{0xE2, (uint8_t []){0x00, 0x00, 0x33, 0x33, 0x01, 0xC0, 0x00, 0x00, 0x01, 0xC0, 0x00, 0x00, 0x00}, 13, 0},
|
||||
{0xE3, (uint8_t []){0x00, 0x00, 0x11, 0x11}, 4, 0},
|
||||
{0xE4, (uint8_t []){0x44, 0x44}, 2, 0},
|
||||
{0xE5, (uint8_t []){0x0D, 0xF1, 0x10, 0x98, 0x0F, 0xF3, 0x10, 0x98, 0x09, 0xED, 0x10, 0x98, 0x0B, 0xEF, 0x10, 0x98}, 16, 0},
|
||||
{0xE6, (uint8_t []){0x00, 0x00, 0x11, 0x11}, 4, 0},
|
||||
{0xE7, (uint8_t []){0x44, 0x44}, 2, 0},
|
||||
{0xE8, (uint8_t []){0x0C, 0xF0, 0x10, 0x98, 0x0E, 0xF2, 0x10, 0x98, 0x08, 0xEC, 0x10, 0x98, 0x0A, 0xEE, 0x10, 0x98}, 16, 0},
|
||||
{0xEB, (uint8_t []){0x00, 0x01, 0xE4, 0xE4, 0x44, 0x88, 0x00}, 7, 0},
|
||||
{0xED, (uint8_t []){0xFF, 0x04, 0x56, 0x7F, 0xBA, 0x2F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF2, 0xAB, 0xF7, 0x65, 0x40, 0xFF}, 16, 0},
|
||||
{0xEF, (uint8_t []){0x10, 0x0D, 0x04, 0x08, 0x3F, 0x1F}, 6, 0},
|
||||
{0xFF, (uint8_t []){0x77, 0x01, 0x00, 0x00, 0x00}, 5, 0},
|
||||
{0x11, (uint8_t []){0x00}, 0, 120},
|
||||
{0x29, (uint8_t []){0x00}, 0, 0},
|
||||
|
||||
// {0xFF, (uint8_t []){0x77, 0x01, 0x00, 0x00, 0x12}, 5, 0}, /* This part of the parameters can be used for screen self-test */
|
||||
// {0xD1, (uint8_t []){0x81}, 1, 0},
|
||||
// {0xD2, (uint8_t []){0x08}, 1, 0},
|
||||
};
|
||||
|
||||
static esp_err_t panel_st7701_send_init_cmds(st7701_panel_t *st7701)
|
||||
{
|
||||
esp_lcd_panel_io_handle_t io = st7701->io;
|
||||
const st7701_lcd_init_cmd_t *init_cmds = NULL;
|
||||
uint16_t init_cmds_size = 0;
|
||||
bool is_command2_disable = true;
|
||||
bool is_cmd_overwritten = false;
|
||||
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, ST7701_CMD_CND2BKxSEL, (uint8_t []) {
|
||||
ST7701_CMD_BKxSEL_BYTE0, ST7701_CMD_BKxSEL_BYTE1, ST7701_CMD_BKxSEL_BYTE2, ST7701_CMD_BKxSEL_BYTE3, 0x00
|
||||
}, 5), TAG, "Write cmd failed");
|
||||
// Set color format
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL, (uint8_t []) {
|
||||
st7701->madctl_val
|
||||
}, 1), TAG, "Write cmd failed");
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_COLMOD, (uint8_t []) {
|
||||
st7701->colmod_val
|
||||
}, 1), TAG, "Write cmd failed");
|
||||
|
||||
// vendor specific initialization, it can be different between manufacturers
|
||||
// should consult the LCD supplier for initialization sequence code
|
||||
if (st7701->init_cmds) {
|
||||
init_cmds = st7701->init_cmds;
|
||||
init_cmds_size = st7701->init_cmds_size;
|
||||
} else {
|
||||
init_cmds = vendor_specific_init_default;
|
||||
init_cmds_size = sizeof(vendor_specific_init_default) / sizeof(st7701_lcd_init_cmd_t);
|
||||
}
|
||||
|
||||
for (int i = 0; i < init_cmds_size; i++) {
|
||||
// Check if the command has been used or conflicts with the internal only when command2 is disable
|
||||
if (is_command2_disable && (init_cmds[i].data_bytes > 0)) {
|
||||
switch (init_cmds[i].cmd) {
|
||||
case LCD_CMD_MADCTL:
|
||||
is_cmd_overwritten = true;
|
||||
st7701->madctl_val = ((uint8_t *)init_cmds[i].data)[0];
|
||||
break;
|
||||
case LCD_CMD_COLMOD:
|
||||
is_cmd_overwritten = true;
|
||||
st7701->colmod_val = ((uint8_t *)init_cmds[i].data)[0];
|
||||
break;
|
||||
default:
|
||||
is_cmd_overwritten = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (is_cmd_overwritten) {
|
||||
is_cmd_overwritten = false;
|
||||
ESP_LOGW(TAG, "The %02Xh command has been used and will be overwritten by external initialization sequence",
|
||||
init_cmds[i].cmd);
|
||||
}
|
||||
}
|
||||
|
||||
// Send command
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, init_cmds[i].cmd, init_cmds[i].data, init_cmds[i].data_bytes),
|
||||
TAG, "send command failed");
|
||||
vTaskDelay(pdMS_TO_TICKS(init_cmds[i].delay_ms));
|
||||
|
||||
// Check if the current cmd is the command2 disable cmd
|
||||
if ((init_cmds[i].cmd == ST7701_CMD_CND2BKxSEL) && (init_cmds[i].data_bytes > 4)) {
|
||||
is_command2_disable = !(((uint8_t *)init_cmds[i].data)[4] & ST7701_CMD_CN2_BIT);
|
||||
}
|
||||
}
|
||||
ESP_LOGD(TAG, "send init commands success");
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t panel_st7701_init(esp_lcd_panel_t *panel)
|
||||
{
|
||||
st7701_panel_t *st7701 = (st7701_panel_t *)panel->user_data;
|
||||
|
||||
if (!st7701->flags.enable_io_multiplex) {
|
||||
ESP_RETURN_ON_ERROR(panel_st7701_send_init_cmds(st7701), TAG, "send init commands failed");
|
||||
}
|
||||
// Init RGB panel
|
||||
ESP_RETURN_ON_ERROR(st7701->init(panel), TAG, "init RGB panel failed");
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t panel_st7701_del(esp_lcd_panel_t *panel)
|
||||
{
|
||||
st7701_panel_t *st7701 = (st7701_panel_t *)panel->user_data;
|
||||
|
||||
if (st7701->reset_gpio_num >= 0) {
|
||||
gpio_reset_pin(st7701->reset_gpio_num);
|
||||
}
|
||||
// Delete RGB panel
|
||||
st7701->del(panel);
|
||||
free(st7701);
|
||||
ESP_LOGD(TAG, "del st7701 panel @%p", st7701);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t panel_st7701_reset(esp_lcd_panel_t *panel)
|
||||
{
|
||||
st7701_panel_t *st7701 = (st7701_panel_t *)panel->user_data;
|
||||
esp_lcd_panel_io_handle_t io = st7701->io;
|
||||
|
||||
// Perform hardware reset
|
||||
if (st7701->reset_gpio_num >= 0) {
|
||||
gpio_set_level(st7701->reset_gpio_num, st7701->flags.reset_level);
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
gpio_set_level(st7701->reset_gpio_num, !st7701->flags.reset_level);
|
||||
vTaskDelay(pdMS_TO_TICKS(120));
|
||||
} else if (io) { // Perform software reset
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_SWRESET, NULL, 0), TAG, "send command failed");
|
||||
vTaskDelay(pdMS_TO_TICKS(120));
|
||||
}
|
||||
// Reset RGB panel
|
||||
ESP_RETURN_ON_ERROR(st7701->reset(panel), TAG, "reset RGB panel failed");
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t panel_st7701_mirror(esp_lcd_panel_t *panel, bool mirror_x, bool mirror_y)
|
||||
{
|
||||
st7701_panel_t *st7701 = (st7701_panel_t *)panel->user_data;
|
||||
esp_lcd_panel_io_handle_t io = st7701->io;
|
||||
uint8_t sdir_val = 0;
|
||||
|
||||
if (st7701->flags.mirror_by_cmd) {
|
||||
ESP_RETURN_ON_FALSE(io, ESP_FAIL, TAG, "Panel IO is deleted, cannot send command");
|
||||
// Control mirror through LCD command
|
||||
if (mirror_x) {
|
||||
sdir_val = ST7701_CMD_SS_BIT;
|
||||
} else {
|
||||
sdir_val = 0;
|
||||
}
|
||||
if (mirror_y) {
|
||||
st7701->madctl_val |= LCD_CMD_ML_BIT;
|
||||
} else {
|
||||
st7701->madctl_val &= ~LCD_CMD_ML_BIT;
|
||||
}
|
||||
|
||||
// Enable the Command2 BK0
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, ST7701_CMD_CND2BKxSEL, (uint8_t []) {
|
||||
ST7701_CMD_BKxSEL_BYTE0, ST7701_CMD_BKxSEL_BYTE1, ST7701_CMD_BKxSEL_BYTE2, ST7701_CMD_BKxSEL_BYTE3,
|
||||
ST7701_CMD_BKxSEL_BK0 | ST7701_CMD_CN2_BIT,
|
||||
}, 5), TAG, "send command failed");
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, ST7701_CMD_SDIR, (uint8_t[]) {
|
||||
sdir_val,
|
||||
}, 1), TAG, "send command failed");;
|
||||
|
||||
// Disable Command2
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, ST7701_CMD_CND2BKxSEL, (uint8_t []) {
|
||||
ST7701_CMD_BKxSEL_BYTE0, ST7701_CMD_BKxSEL_BYTE1, ST7701_CMD_BKxSEL_BYTE2, ST7701_CMD_BKxSEL_BYTE3, 0,
|
||||
}, 5), TAG, "send command failed");
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL, (uint8_t[]) {
|
||||
st7701->madctl_val,
|
||||
}, 1), TAG, "send command failed");;
|
||||
} else {
|
||||
// Control mirror through RGB panel
|
||||
ESP_RETURN_ON_ERROR(st7701->mirror(panel, mirror_x, mirror_y), TAG, "RGB panel mirror failed");
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t panel_st7701_disp_on_off(esp_lcd_panel_t *panel, bool on_off)
|
||||
{
|
||||
st7701_panel_t *st7701 = (st7701_panel_t *)panel->user_data;
|
||||
esp_lcd_panel_io_handle_t io = st7701->io;
|
||||
int command = 0;
|
||||
|
||||
if (st7701->flags.display_on_off_use_cmd) {
|
||||
ESP_RETURN_ON_FALSE(io, ESP_FAIL, TAG, "Panel IO is deleted, cannot send command");
|
||||
// Control display on/off through LCD command
|
||||
if (on_off) {
|
||||
command = LCD_CMD_DISPON;
|
||||
} else {
|
||||
command = LCD_CMD_DISPOFF;
|
||||
}
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, command, NULL, 0), TAG, "send command failed");
|
||||
} else {
|
||||
// Control display on/off through display control signal
|
||||
ESP_RETURN_ON_ERROR(st7701->disp_on_off(panel, on_off), TAG, "RGB panel disp_on_off failed");
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
#endif
|
||||
+183
@@ -0,0 +1,183 @@
|
||||
#include "sdkconfig.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "esp_timer.h"
|
||||
#include "esp_lcd_panel_ops.h"
|
||||
#include "esp_lcd_mipi_dsi.h"
|
||||
#include "esp_lcd_panel_io.h"
|
||||
#include "esp_ldo_regulator.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "driver/i2c_master.h"
|
||||
#include "driver/ledc.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_log.h"
|
||||
#include "Arduino.h"
|
||||
|
||||
#include "esp_lcd_st7701.h"
|
||||
#include "st7701_lcd.h"
|
||||
|
||||
#define LCD_H_RES 480
|
||||
#define LCD_V_RES 800
|
||||
|
||||
#define MIPI_DPI_PX_FORMAT (LCD_COLOR_PIXEL_FORMAT_RGB565)
|
||||
#define LCD_BIT_PER_PIXEL (16)
|
||||
|
||||
// “VDD_MIPI_DPHY”应供电 2.5V,可从内部 LDO 稳压器或外部 LDO 芯片获取电源
|
||||
#define EXAMPLE_MIPI_DSI_PHY_PWR_LDO_CHAN 3 // LDO_VO3 连接至 VDD_MIPI_DPHY
|
||||
#define EXAMPLE_MIPI_DSI_PHY_PWR_LDO_VOLTAGE_MV 2500
|
||||
#define EXAMPLE_LCD_BK_LIGHT_ON_LEVEL 1
|
||||
#define EXAMPLE_LCD_BK_LIGHT_OFF_LEVEL (0)
|
||||
#define EXAMPLE_PIN_NUM_BK_LIGHT GPIO_NUM_23
|
||||
|
||||
#define BSP_LCD_BACKLIGHT GPIO_NUM_23
|
||||
#define LCD_LEDC_CH LEDC_CHANNEL_0
|
||||
|
||||
static const char *TAG = "example";
|
||||
esp_lcd_panel_handle_t panel_handle = NULL;
|
||||
esp_lcd_panel_io_handle_t io_handle = NULL;
|
||||
|
||||
st7701_lcd::st7701_lcd(int8_t lcd_rst)
|
||||
{
|
||||
_lcd_rst = lcd_rst;
|
||||
}
|
||||
|
||||
void st7701_lcd::example_bsp_enable_dsi_phy_power()
|
||||
{
|
||||
// 打开 MIPI DSI PHY 的电源,使其从“无电”状态进入“关机”状态
|
||||
esp_ldo_channel_handle_t ldo_mipi_phy = NULL;
|
||||
#ifdef EXAMPLE_MIPI_DSI_PHY_PWR_LDO_CHAN
|
||||
esp_ldo_channel_config_t ldo_mipi_phy_config = {
|
||||
.chan_id = EXAMPLE_MIPI_DSI_PHY_PWR_LDO_CHAN,
|
||||
.voltage_mv = EXAMPLE_MIPI_DSI_PHY_PWR_LDO_VOLTAGE_MV,
|
||||
};
|
||||
ESP_ERROR_CHECK(esp_ldo_acquire_channel(&ldo_mipi_phy_config, &ldo_mipi_phy));
|
||||
ESP_LOGI(TAG, "MIPI DSI PHY Powered on");
|
||||
#endif
|
||||
}
|
||||
|
||||
void st7701_lcd::example_bsp_init_lcd_backlight()
|
||||
{
|
||||
#if EXAMPLE_PIN_NUM_BK_LIGHT >= 0
|
||||
gpio_config_t bk_gpio_config = {
|
||||
.pin_bit_mask = 1ULL << EXAMPLE_PIN_NUM_BK_LIGHT,
|
||||
.mode = GPIO_MODE_OUTPUT
|
||||
};
|
||||
ESP_ERROR_CHECK(gpio_config(&bk_gpio_config));
|
||||
#endif
|
||||
}
|
||||
|
||||
void st7701_lcd::example_bsp_set_lcd_backlight(uint32_t level)
|
||||
{
|
||||
|
||||
#if EXAMPLE_PIN_NUM_BK_LIGHT >= 0
|
||||
gpio_set_level(EXAMPLE_PIN_NUM_BK_LIGHT, level);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
void st7701_lcd::begin()
|
||||
{
|
||||
example_bsp_enable_dsi_phy_power();
|
||||
example_bsp_init_lcd_backlight();
|
||||
// example_bsp_set_lcd_backlight(EXAMPLE_LCD_BK_LIGHT_OFF_LEVEL);
|
||||
|
||||
// 首先创建 MIPI DSI 总线,它还将初始化 DSI PHY
|
||||
esp_lcd_dsi_bus_handle_t mipi_dsi_bus;
|
||||
esp_lcd_dsi_bus_config_t bus_config = ST7701_PANEL_BUS_DSI_2CH_CONFIG();
|
||||
ESP_ERROR_CHECK(esp_lcd_new_dsi_bus(&bus_config, &mipi_dsi_bus));
|
||||
|
||||
ESP_LOGI(TAG, "Install MIPI DSI LCD control panel");
|
||||
// 我们使用DBI接口发送LCD命令和参数
|
||||
esp_lcd_dbi_io_config_t dbi_config = ST7701_PANEL_IO_DBI_CONFIG();
|
||||
|
||||
ESP_ERROR_CHECK(esp_lcd_new_panel_io_dbi(mipi_dsi_bus, &dbi_config, &io_handle));
|
||||
|
||||
// 创建JD9365控制面板
|
||||
esp_lcd_dpi_panel_config_t dpi_config = ST7701_480_360_PANEL_60HZ_DPI_CONFIG(MIPI_DPI_PX_FORMAT);
|
||||
|
||||
st7701_vendor_config_t vendor_config = {
|
||||
// .init_cmds = lcd_cmd,
|
||||
// .init_cmds_size = sizeof(lcd_cmd) / sizeof(st7701_lcd_init_cmd_t),
|
||||
.mipi_config = {
|
||||
.dsi_bus = mipi_dsi_bus,
|
||||
.dpi_config = &dpi_config,
|
||||
},
|
||||
.flags = {
|
||||
.use_mipi_interface = 1,
|
||||
}
|
||||
};
|
||||
|
||||
const esp_lcd_panel_dev_config_t panel_config = {
|
||||
.reset_gpio_num = GPIO_NUM_5,
|
||||
.rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB,
|
||||
.bits_per_pixel = 16,
|
||||
.vendor_config = &vendor_config,
|
||||
};
|
||||
ESP_ERROR_CHECK(esp_lcd_new_panel_st7701(io_handle, &panel_config, &panel_handle));
|
||||
ESP_ERROR_CHECK(esp_lcd_panel_reset(panel_handle));
|
||||
ESP_ERROR_CHECK(esp_lcd_panel_init(panel_handle));
|
||||
|
||||
// esp_lcd_dpi_panel_event_callbacks_t cbs = {0};
|
||||
// if (dsi_cfg->flags.avoid_tearing) {
|
||||
// cbs.on_refresh_done = lvgl_port_flush_dpi_vsync_ready_callback;
|
||||
// } else {
|
||||
// cbs.on_color_trans_done = lvgl_port_flush_dpi_panel_ready_callback;
|
||||
// }
|
||||
// /* Register done callback */
|
||||
// esp_lcd_dpi_panel_register_event_callbacks(disp_ctx->panel_handle, &cbs, &disp_ctx->disp_drv);
|
||||
|
||||
// 打开背光
|
||||
example_bsp_set_lcd_backlight(EXAMPLE_LCD_BK_LIGHT_ON_LEVEL);
|
||||
}
|
||||
|
||||
void st7701_lcd::lcd_draw_bitmap(uint16_t x_start, uint16_t y_start, uint16_t x_end, uint16_t y_end, uint16_t *color_data)
|
||||
{
|
||||
esp_lcd_panel_draw_bitmap(panel_handle, x_start, y_start, x_end, y_end, color_data);
|
||||
}
|
||||
|
||||
void st7701_lcd::draw16bitbergbbitmap(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t *color_data)
|
||||
{
|
||||
uint16_t x_start = x;
|
||||
uint16_t y_start = y;
|
||||
uint16_t x_end = w + x;
|
||||
uint16_t y_end = h + y;
|
||||
|
||||
esp_lcd_panel_draw_bitmap(panel_handle, x_start, y_start, x_end, y_end, color_data);
|
||||
}
|
||||
|
||||
void st7701_lcd::fillScreen(uint16_t color)
|
||||
{
|
||||
uint16_t *color_data = (uint16_t *)heap_caps_malloc(480 * 272 * 2, MALLOC_CAP_INTERNAL);
|
||||
memset(color_data, color, 480 * 272 * 2);
|
||||
draw16bitbergbbitmap(0, 0, 480, 272, color_data);
|
||||
free(color_data);
|
||||
}
|
||||
|
||||
void st7701_lcd::te_on()
|
||||
{
|
||||
esp_lcd_panel_io_tx_param(io_handle, 0x35,new (uint8_t[]){0x00}, 1);
|
||||
}
|
||||
|
||||
void st7701_lcd::te_off()
|
||||
{
|
||||
esp_lcd_panel_io_tx_param(io_handle, 0x34,new (uint8_t[]){0x00}, 0);
|
||||
}
|
||||
|
||||
uint16_t st7701_lcd::width()
|
||||
{
|
||||
return LCD_H_RES;
|
||||
}
|
||||
|
||||
uint16_t st7701_lcd::height()
|
||||
{
|
||||
return LCD_V_RES;
|
||||
}
|
||||
|
||||
void st7701_lcd::get_handle(bsp_lcd_handles_t *ret_handles)
|
||||
{
|
||||
ret_handles->io = io_handle;
|
||||
ret_handles->mipi_dsi_bus = NULL;
|
||||
ret_handles->panel = panel_handle;
|
||||
ret_handles->control = NULL;
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
#ifndef _ST7701_LCD_H
|
||||
#define _ST7701_LCD_H
|
||||
#include <stdio.h>
|
||||
#include "esp_lcd_types.h"
|
||||
#include "esp_lcd_mipi_dsi.h"
|
||||
|
||||
typedef struct {
|
||||
esp_lcd_dsi_bus_handle_t mipi_dsi_bus; /*!< MIPI DSI bus handle */
|
||||
esp_lcd_panel_io_handle_t io; /*!< ESP LCD IO handle */
|
||||
esp_lcd_panel_handle_t panel; /*!< ESP LCD panel (color) handle */
|
||||
esp_lcd_panel_handle_t control; /*!< ESP LCD panel (control) handle */
|
||||
} bsp_lcd_handles_t;
|
||||
|
||||
class st7701_lcd
|
||||
{
|
||||
public:
|
||||
st7701_lcd(int8_t lcd_rst);
|
||||
|
||||
void begin();
|
||||
void example_bsp_enable_dsi_phy_power();
|
||||
void example_bsp_init_lcd_backlight();
|
||||
void example_bsp_set_lcd_backlight(uint32_t level);
|
||||
void lcd_draw_bitmap(uint16_t x_start, uint16_t y_start,
|
||||
uint16_t x_end, uint16_t y_end, uint16_t *color_data);
|
||||
void draw16bitbergbbitmap(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t *color_data);
|
||||
void fillScreen(uint16_t color);
|
||||
void te_on();
|
||||
void te_off();
|
||||
uint16_t width();
|
||||
uint16_t height();
|
||||
void get_handle(bsp_lcd_handles_t *ret_handles);
|
||||
|
||||
private:
|
||||
int8_t _lcd_rst;
|
||||
};
|
||||
#endif
|
||||
+236
@@ -0,0 +1,236 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "esp_system.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_check.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_lcd_touch.h"
|
||||
|
||||
static const char *TAG = "TP";
|
||||
|
||||
/*******************************************************************************
|
||||
* Function definitions
|
||||
*******************************************************************************/
|
||||
|
||||
/*******************************************************************************
|
||||
* Local variables
|
||||
*******************************************************************************/
|
||||
|
||||
/*******************************************************************************
|
||||
* Public API functions
|
||||
*******************************************************************************/
|
||||
|
||||
esp_err_t esp_lcd_touch_read_data(esp_lcd_touch_handle_t tp)
|
||||
{
|
||||
assert(tp != NULL);
|
||||
assert(tp->read_data != NULL);
|
||||
|
||||
return tp->read_data(tp);
|
||||
}
|
||||
|
||||
bool esp_lcd_touch_get_coordinates(esp_lcd_touch_handle_t tp, uint16_t *x, uint16_t *y, uint16_t *strength, uint8_t *point_num, uint8_t max_point_num)
|
||||
{
|
||||
bool touched = false;
|
||||
|
||||
assert(tp != NULL);
|
||||
assert(x != NULL);
|
||||
assert(y != NULL);
|
||||
assert(tp->get_xy != NULL);
|
||||
|
||||
touched = tp->get_xy(tp, x, y, strength, point_num, max_point_num);
|
||||
if (!touched) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Process coordinates by user */
|
||||
if (tp->config.process_coordinates != NULL) {
|
||||
tp->config.process_coordinates(tp, x, y, strength, point_num, max_point_num);
|
||||
}
|
||||
|
||||
/* Software coordinates adjustment needed */
|
||||
bool sw_adj_needed = ((tp->config.flags.mirror_x && (tp->set_mirror_x == NULL)) ||
|
||||
(tp->config.flags.mirror_y && (tp->set_mirror_y == NULL)) ||
|
||||
(tp->config.flags.swap_xy && (tp->set_swap_xy == NULL)));
|
||||
|
||||
/* Adjust all coordinates */
|
||||
for (int i = 0; (sw_adj_needed && i < *point_num); i++) {
|
||||
|
||||
/* Mirror X coordinates (if not supported by HW) */
|
||||
if (tp->config.flags.mirror_x && tp->set_mirror_x == NULL) {
|
||||
x[i] = tp->config.x_max - x[i];
|
||||
}
|
||||
|
||||
/* Mirror Y coordinates (if not supported by HW) */
|
||||
if (tp->config.flags.mirror_y && tp->set_mirror_y == NULL) {
|
||||
y[i] = tp->config.y_max - y[i];
|
||||
}
|
||||
|
||||
/* Swap X and Y coordinates (if not supported by HW) */
|
||||
if (tp->config.flags.swap_xy && tp->set_swap_xy == NULL) {
|
||||
uint16_t tmp = x[i];
|
||||
x[i] = y[i];
|
||||
y[i] = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
return touched;
|
||||
}
|
||||
|
||||
#if (CONFIG_ESP_LCD_TOUCH_MAX_BUTTONS > 0)
|
||||
esp_err_t esp_lcd_touch_get_button_state(esp_lcd_touch_handle_t tp, uint8_t n, uint8_t *state)
|
||||
{
|
||||
assert(tp != NULL);
|
||||
assert(state != NULL);
|
||||
|
||||
*state = 0;
|
||||
|
||||
if (tp->get_button_state) {
|
||||
return tp->get_button_state(tp, n, state);
|
||||
} else {
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
esp_err_t esp_lcd_touch_set_swap_xy(esp_lcd_touch_handle_t tp, bool swap)
|
||||
{
|
||||
assert(tp != NULL);
|
||||
|
||||
tp->config.flags.swap_xy = swap;
|
||||
|
||||
/* Is swap supported by HW? */
|
||||
if (tp->set_swap_xy) {
|
||||
return tp->set_swap_xy(tp, swap);
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_lcd_touch_get_swap_xy(esp_lcd_touch_handle_t tp, bool *swap)
|
||||
{
|
||||
assert(tp != NULL);
|
||||
assert(swap != NULL);
|
||||
|
||||
/* Is swap supported by HW? */
|
||||
if (tp->get_swap_xy) {
|
||||
return tp->get_swap_xy(tp, swap);
|
||||
} else {
|
||||
*swap = tp->config.flags.swap_xy;
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_lcd_touch_set_mirror_x(esp_lcd_touch_handle_t tp, bool mirror)
|
||||
{
|
||||
assert(tp != NULL);
|
||||
|
||||
tp->config.flags.mirror_x = mirror;
|
||||
|
||||
/* Is mirror supported by HW? */
|
||||
if (tp->set_mirror_x) {
|
||||
return tp->set_mirror_x(tp, mirror);
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_lcd_touch_get_mirror_x(esp_lcd_touch_handle_t tp, bool *mirror)
|
||||
{
|
||||
assert(tp != NULL);
|
||||
assert(mirror != NULL);
|
||||
|
||||
/* Is swap supported by HW? */
|
||||
if (tp->get_mirror_x) {
|
||||
return tp->get_mirror_x(tp, mirror);
|
||||
} else {
|
||||
*mirror = tp->config.flags.mirror_x;
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_lcd_touch_set_mirror_y(esp_lcd_touch_handle_t tp, bool mirror)
|
||||
{
|
||||
assert(tp != NULL);
|
||||
|
||||
tp->config.flags.mirror_y = mirror;
|
||||
|
||||
/* Is mirror supported by HW? */
|
||||
if (tp->set_mirror_y) {
|
||||
return tp->set_mirror_y(tp, mirror);
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_lcd_touch_get_mirror_y(esp_lcd_touch_handle_t tp, bool *mirror)
|
||||
{
|
||||
assert(tp != NULL);
|
||||
assert(mirror != NULL);
|
||||
|
||||
/* Is swap supported by HW? */
|
||||
if (tp->get_mirror_y) {
|
||||
return tp->get_mirror_y(tp, mirror);
|
||||
} else {
|
||||
*mirror = tp->config.flags.mirror_y;
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_lcd_touch_del(esp_lcd_touch_handle_t tp)
|
||||
{
|
||||
assert(tp != NULL);
|
||||
|
||||
if (tp->del != NULL) {
|
||||
return tp->del(tp);
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_lcd_touch_register_interrupt_callback(esp_lcd_touch_handle_t tp, esp_lcd_touch_interrupt_callback_t callback)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
assert(tp != NULL);
|
||||
|
||||
/* Interrupt pin is not selected */
|
||||
if (tp->config.int_gpio_num == GPIO_NUM_NC) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
tp->config.interrupt_callback = callback;
|
||||
|
||||
if (callback != NULL) {
|
||||
ret = gpio_install_isr_service(0);
|
||||
/* ISR service can be installed from user before, then it returns invalid state */
|
||||
if (ret != ESP_OK && ret != ESP_ERR_INVALID_STATE) {
|
||||
ESP_LOGE(TAG, "GPIO ISR install failed");
|
||||
return ret;
|
||||
}
|
||||
/* Add GPIO ISR handler */
|
||||
ret = gpio_intr_enable(tp->config.int_gpio_num);
|
||||
ESP_RETURN_ON_ERROR(ret, TAG, "GPIO ISR install failed");
|
||||
ret = gpio_isr_handler_add(tp->config.int_gpio_num, (gpio_isr_t)tp->config.interrupt_callback, tp);
|
||||
ESP_RETURN_ON_ERROR(ret, TAG, "GPIO ISR install failed");
|
||||
} else {
|
||||
/* Remove GPIO ISR handler */
|
||||
ret = gpio_isr_handler_remove(tp->config.int_gpio_num);
|
||||
ESP_RETURN_ON_ERROR(ret, TAG, "GPIO ISR remove handler failed");
|
||||
ret = gpio_intr_disable(tp->config.int_gpio_num);
|
||||
ESP_RETURN_ON_ERROR(ret, TAG, "GPIO ISR disable failed");
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
+370
@@ -0,0 +1,370 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* @brief ESP LCD touch
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "sdkconfig.h"
|
||||
#include "esp_err.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "esp_lcd_panel_io.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/semphr.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define CONFIG_ESP_LCD_TOUCH_MAX_BUTTONS (1)
|
||||
#define CONFIG_ESP_LCD_TOUCH_MAX_POINTS (5)
|
||||
|
||||
/**
|
||||
* @brief Touch controller type
|
||||
*
|
||||
*/
|
||||
typedef struct esp_lcd_touch_s esp_lcd_touch_t;
|
||||
typedef esp_lcd_touch_t *esp_lcd_touch_handle_t;
|
||||
|
||||
/**
|
||||
* @brief Touch controller interrupt callback type
|
||||
*
|
||||
*/
|
||||
typedef void (*esp_lcd_touch_interrupt_callback_t)(esp_lcd_touch_handle_t tp);
|
||||
|
||||
/**
|
||||
* @brief Touch Configuration Type
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
uint16_t x_max; /*!< X coordinates max (for mirroring) */
|
||||
uint16_t y_max; /*!< Y coordinates max (for mirroring) */
|
||||
|
||||
gpio_num_t rst_gpio_num; /*!< GPIO number of reset pin */
|
||||
gpio_num_t int_gpio_num; /*!< GPIO number of interrupt pin */
|
||||
|
||||
struct {
|
||||
unsigned int reset: 1; /*!< Level of reset pin in reset */
|
||||
unsigned int interrupt: 1;/*!< Active Level of interrupt pin */
|
||||
} levels;
|
||||
|
||||
struct {
|
||||
unsigned int swap_xy: 1; /*!< Swap X and Y after read coordinates */
|
||||
unsigned int mirror_x: 1; /*!< Mirror X after read coordinates */
|
||||
unsigned int mirror_y: 1; /*!< Mirror Y after read coordinates */
|
||||
} flags;
|
||||
|
||||
/*!< User callback called after get coordinates from touch controller for apply user adjusting */
|
||||
void (*process_coordinates)(esp_lcd_touch_handle_t tp, uint16_t *x, uint16_t *y, uint16_t *strength, uint8_t *point_num, uint8_t max_point_num);
|
||||
/*!< User callback called after the touch interrupt occured */
|
||||
esp_lcd_touch_interrupt_callback_t interrupt_callback;
|
||||
} esp_lcd_touch_config_t;
|
||||
|
||||
typedef struct {
|
||||
uint8_t points; /*!< Count of touch points saved */
|
||||
|
||||
struct {
|
||||
uint16_t x; /*!< X coordinate */
|
||||
uint16_t y; /*!< Y coordinate */
|
||||
uint16_t strength; /*!< Strength */
|
||||
} coords[CONFIG_ESP_LCD_TOUCH_MAX_POINTS];
|
||||
|
||||
#if (CONFIG_ESP_LCD_TOUCH_MAX_BUTTONS > 0)
|
||||
uint8_t buttons; /*!< Count of buttons states saved */
|
||||
|
||||
struct {
|
||||
uint8_t status; /*!< Status of button */
|
||||
} button[CONFIG_ESP_LCD_TOUCH_MAX_BUTTONS];
|
||||
#endif
|
||||
|
||||
portMUX_TYPE lock; /*!< Lock for read/write */
|
||||
} esp_lcd_touch_data_t;
|
||||
|
||||
/**
|
||||
* @brief Declare of Touch Type
|
||||
*
|
||||
*/
|
||||
struct esp_lcd_touch_s {
|
||||
|
||||
/**
|
||||
* @brief Read data from touch controller (mandatory)
|
||||
*
|
||||
* @note This function is usually blocking.
|
||||
*
|
||||
* @param tp: Touch handler
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success, otherwise returns ESP_ERR_xxx
|
||||
*/
|
||||
esp_err_t (*read_data)(esp_lcd_touch_handle_t tp);
|
||||
|
||||
/**
|
||||
* @brief Get coordinates from touch controller (mandatory)
|
||||
*
|
||||
* @param tp: Touch handler
|
||||
* @param x: Array of X coordinates
|
||||
* @param y: Array of Y coordinates
|
||||
* @param strength: Array of strengths
|
||||
* @param point_num: Count of points touched (equals with count of items in x and y array)
|
||||
* @param max_point_num: Maximum count of touched points to return (equals with max size of x and y array)
|
||||
*
|
||||
* @return
|
||||
* - Returns true, when touched and coordinates readed. Otherwise returns false.
|
||||
*/
|
||||
bool (*get_xy)(esp_lcd_touch_handle_t tp, uint16_t *x, uint16_t *y, uint16_t *strength, uint8_t *point_num, uint8_t max_point_num);
|
||||
|
||||
|
||||
#if (CONFIG_ESP_LCD_TOUCH_MAX_BUTTONS > 0)
|
||||
/**
|
||||
* @brief Get button state (optional)
|
||||
*
|
||||
* @param tp: Touch handler
|
||||
* @param n: Button index
|
||||
* @param state: Button state
|
||||
*
|
||||
* @return
|
||||
* - Returns true, when touched and coordinates readed. Otherwise returns false.
|
||||
*/
|
||||
esp_err_t (*get_button_state)(esp_lcd_touch_handle_t tp, uint8_t n, uint8_t *state);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Swap X and Y after read coordinates (optional)
|
||||
* If set, then not used SW swapping.
|
||||
*
|
||||
* @param tp: Touch handler
|
||||
* @param swap: Set swap value
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success, otherwise returns ESP_ERR_xxx
|
||||
*/
|
||||
esp_err_t (*set_swap_xy)(esp_lcd_touch_handle_t tp, bool swap);
|
||||
|
||||
/**
|
||||
* @brief Are X and Y coordinates swapped (optional)
|
||||
*
|
||||
* @param tp: Touch handler
|
||||
* @param swap: Get swap value
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success, otherwise returns ESP_ERR_xxx
|
||||
*/
|
||||
esp_err_t (*get_swap_xy)(esp_lcd_touch_handle_t tp, bool *swap);
|
||||
|
||||
/**
|
||||
* @brief Mirror X after read coordinates
|
||||
* If set, then not used SW mirroring.
|
||||
*
|
||||
* @param tp: Touch handler
|
||||
* @param mirror: Set X mirror value
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success, otherwise returns ESP_ERR_xxx
|
||||
*/
|
||||
esp_err_t (*set_mirror_x)(esp_lcd_touch_handle_t tp, bool mirror);
|
||||
|
||||
/**
|
||||
* @brief Is mirrored X (optional)
|
||||
*
|
||||
* @param tp: Touch handler
|
||||
* @param mirror: Get X mirror value
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success, otherwise returns ESP_ERR_xxx
|
||||
*/
|
||||
esp_err_t (*get_mirror_x)(esp_lcd_touch_handle_t tp, bool *mirror);
|
||||
|
||||
/**
|
||||
* @brief Mirror Y after read coordinates
|
||||
* If set, then not used SW mirroring.
|
||||
*
|
||||
* @param tp: Touch handler
|
||||
* @param mirror: Set Y mirror value
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success, otherwise returns ESP_ERR_xxx
|
||||
*/
|
||||
esp_err_t (*set_mirror_y)(esp_lcd_touch_handle_t tp, bool mirror);
|
||||
|
||||
/**
|
||||
* @brief Is mirrored Y (optional)
|
||||
*
|
||||
* @param tp: Touch handler
|
||||
* @param mirror: Get Y mirror value
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success, otherwise returns ESP_ERR_xxx
|
||||
*/
|
||||
esp_err_t (*get_mirror_y)(esp_lcd_touch_handle_t tp, bool *mirror);
|
||||
|
||||
/**
|
||||
* @brief Delete Touch
|
||||
*
|
||||
* @param tp: Touch handler
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success, otherwise returns ESP_ERR_xxx
|
||||
*/
|
||||
esp_err_t (*del)(esp_lcd_touch_handle_t tp);
|
||||
|
||||
/**
|
||||
* @brief Configuration structure
|
||||
*/
|
||||
esp_lcd_touch_config_t config;
|
||||
|
||||
/**
|
||||
* @brief Communication interface
|
||||
*/
|
||||
esp_lcd_panel_io_handle_t io;
|
||||
|
||||
/**
|
||||
* @brief Data structure
|
||||
*/
|
||||
esp_lcd_touch_data_t data;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Read data from touch controller
|
||||
*
|
||||
* @note This function is usually blocking.
|
||||
*
|
||||
* @param tp: Touch handler
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_INVALID_ARG parameter error
|
||||
* - ESP_FAIL sending command error, slave hasn't ACK the transfer
|
||||
* - ESP_ERR_INVALID_STATE I2C driver not installed or not in master mode
|
||||
* - ESP_ERR_TIMEOUT operation timeout because the bus is busy
|
||||
*/
|
||||
esp_err_t esp_lcd_touch_read_data(esp_lcd_touch_handle_t tp);
|
||||
|
||||
/**
|
||||
* @brief Read coordinates from touch controller
|
||||
*
|
||||
* @param tp: Touch handler
|
||||
* @param x: Array of X coordinates
|
||||
* @param y: Array of Y coordinates
|
||||
* @param strength: Array of the strengths (can be NULL)
|
||||
* @param point_num: Count of points touched (equals with count of items in x and y array)
|
||||
* @param max_point_num: Maximum count of touched points to return (equals with max size of x and y array)
|
||||
*
|
||||
* @return
|
||||
* - Returns true, when touched and coordinates readed. Otherwise returns false.
|
||||
*/
|
||||
bool esp_lcd_touch_get_coordinates(esp_lcd_touch_handle_t tp, uint16_t *x, uint16_t *y, uint16_t *strength, uint8_t *point_num, uint8_t max_point_num);
|
||||
|
||||
|
||||
#if (CONFIG_ESP_LCD_TOUCH_MAX_BUTTONS > 0)
|
||||
/**
|
||||
* @brief Get button state
|
||||
*
|
||||
* @param tp: Touch handler
|
||||
* @param n: Button index
|
||||
* @param state: Button state
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_NOT_SUPPORTED if this function is not supported by controller
|
||||
* - ESP_ERR_INVALID_ARG if bad button index
|
||||
*/
|
||||
esp_err_t esp_lcd_touch_get_button_state(esp_lcd_touch_handle_t tp, uint8_t n, uint8_t *state);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Swap X and Y after read coordinates
|
||||
*
|
||||
* @param tp: Touch handler
|
||||
* @param swap: Set swap value
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
*/
|
||||
esp_err_t esp_lcd_touch_set_swap_xy(esp_lcd_touch_handle_t tp, bool swap);
|
||||
|
||||
/**
|
||||
* @brief Are X and Y coordinates swapped
|
||||
*
|
||||
* @param tp: Touch handler
|
||||
* @param swap: Get swap value
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
*/
|
||||
esp_err_t esp_lcd_touch_get_swap_xy(esp_lcd_touch_handle_t tp, bool *swap);
|
||||
|
||||
/**
|
||||
* @brief Mirror X after read coordinates
|
||||
*
|
||||
* @param tp: Touch handler
|
||||
* @param mirror: Set X mirror value
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
*/
|
||||
esp_err_t esp_lcd_touch_set_mirror_x(esp_lcd_touch_handle_t tp, bool mirror);
|
||||
|
||||
/**
|
||||
* @brief Is mirrored X
|
||||
*
|
||||
* @param tp: Touch handler
|
||||
* @param mirror: Get X mirror value
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
*/
|
||||
esp_err_t esp_lcd_touch_get_mirror_x(esp_lcd_touch_handle_t tp, bool *mirror);
|
||||
|
||||
/**
|
||||
* @brief Mirror Y after read coordinates
|
||||
*
|
||||
* @param tp: Touch handler
|
||||
* @param mirror: Set Y mirror value
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
*/
|
||||
esp_err_t esp_lcd_touch_set_mirror_y(esp_lcd_touch_handle_t tp, bool mirror);
|
||||
|
||||
/**
|
||||
* @brief Is mirrored Y
|
||||
*
|
||||
* @param tp: Touch handler
|
||||
* @param mirror: Get Y mirror value
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
*/
|
||||
esp_err_t esp_lcd_touch_get_mirror_y(esp_lcd_touch_handle_t tp, bool *mirror);
|
||||
|
||||
/**
|
||||
* @brief Delete touch (free all allocated memory and restart HW)
|
||||
*
|
||||
* @param tp: Touch handler
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
*/
|
||||
esp_err_t esp_lcd_touch_del(esp_lcd_touch_handle_t tp);
|
||||
|
||||
/**
|
||||
* @brief Register user callback called after the touch interrupt occured
|
||||
*
|
||||
* @param tp: Touch handler
|
||||
* @param callback: Interrupt callback
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
*/
|
||||
esp_err_t esp_lcd_touch_register_interrupt_callback(esp_lcd_touch_handle_t tp, esp_lcd_touch_interrupt_callback_t callback);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
+270
@@ -0,0 +1,270 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "esp_system.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_check.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "driver/i2c.h"
|
||||
#include "esp_lcd_panel_io.h"
|
||||
#include "esp_lcd_touch.h"
|
||||
|
||||
static const char *TAG = "GT911";
|
||||
|
||||
/* GT911 registers */
|
||||
#define ESP_LCD_TOUCH_GT911_READ_XY_REG (0x814E)
|
||||
#define ESP_LCD_TOUCH_GT911_CONFIG_REG (0x8047)
|
||||
#define ESP_LCD_TOUCH_GT911_PRODUCT_ID_REG (0x8140)
|
||||
|
||||
/*******************************************************************************
|
||||
* Function definitions
|
||||
*******************************************************************************/
|
||||
static esp_err_t esp_lcd_touch_gt911_read_data(esp_lcd_touch_handle_t tp);
|
||||
static bool esp_lcd_touch_gt911_get_xy(esp_lcd_touch_handle_t tp, uint16_t *x, uint16_t *y, uint16_t *strength, uint8_t *point_num, uint8_t max_point_num);
|
||||
static esp_err_t esp_lcd_touch_gt911_del(esp_lcd_touch_handle_t tp);
|
||||
|
||||
/* I2C read/write */
|
||||
static esp_err_t touch_gt911_i2c_read(esp_lcd_touch_handle_t tp, uint16_t reg, uint8_t *data, uint8_t len);
|
||||
static esp_err_t touch_gt911_i2c_write(esp_lcd_touch_handle_t tp, uint16_t reg, uint8_t data);
|
||||
|
||||
/* GT911 reset */
|
||||
static esp_err_t touch_gt911_reset(esp_lcd_touch_handle_t tp);
|
||||
/* Read status and config register */
|
||||
static esp_err_t touch_gt911_read_cfg(esp_lcd_touch_handle_t tp);
|
||||
|
||||
/*******************************************************************************
|
||||
* Public API functions
|
||||
*******************************************************************************/
|
||||
|
||||
esp_err_t esp_lcd_touch_new_i2c_gt911(const esp_lcd_panel_io_handle_t io, const esp_lcd_touch_config_t *config, esp_lcd_touch_handle_t *out_touch)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
|
||||
assert(io != NULL);
|
||||
assert(config != NULL);
|
||||
assert(out_touch != NULL);
|
||||
|
||||
/* Prepare main structure */
|
||||
esp_lcd_touch_handle_t esp_lcd_touch_gt911 = heap_caps_calloc(1, sizeof(esp_lcd_touch_t), MALLOC_CAP_DEFAULT);
|
||||
ESP_GOTO_ON_FALSE(esp_lcd_touch_gt911, ESP_ERR_NO_MEM, err, TAG, "no mem for GT911 controller");
|
||||
|
||||
/* Communication interface */
|
||||
esp_lcd_touch_gt911->io = io;
|
||||
|
||||
/* Only supported callbacks are set */
|
||||
esp_lcd_touch_gt911->read_data = esp_lcd_touch_gt911_read_data;
|
||||
esp_lcd_touch_gt911->get_xy = esp_lcd_touch_gt911_get_xy;
|
||||
esp_lcd_touch_gt911->del = esp_lcd_touch_gt911_del;
|
||||
|
||||
/* Mutex */
|
||||
esp_lcd_touch_gt911->data.lock.owner = portMUX_FREE_VAL;
|
||||
|
||||
/* Save config */
|
||||
memcpy(&esp_lcd_touch_gt911->config, config, sizeof(esp_lcd_touch_config_t));
|
||||
|
||||
/* Prepare pin for touch interrupt */
|
||||
if (esp_lcd_touch_gt911->config.int_gpio_num != GPIO_NUM_NC) {
|
||||
const gpio_config_t int_gpio_config = {
|
||||
.mode = GPIO_MODE_INPUT,
|
||||
.intr_type = GPIO_INTR_NEGEDGE,
|
||||
.pin_bit_mask = BIT64(esp_lcd_touch_gt911->config.int_gpio_num)
|
||||
};
|
||||
ret = gpio_config(&int_gpio_config);
|
||||
ESP_GOTO_ON_ERROR(ret, err, TAG, "GPIO config failed");
|
||||
|
||||
/* Register interrupt callback */
|
||||
if (esp_lcd_touch_gt911->config.interrupt_callback) {
|
||||
esp_lcd_touch_register_interrupt_callback(esp_lcd_touch_gt911, esp_lcd_touch_gt911->config.interrupt_callback);
|
||||
}
|
||||
}
|
||||
|
||||
/* Prepare pin for touch controller reset */
|
||||
if (esp_lcd_touch_gt911->config.rst_gpio_num != GPIO_NUM_NC) {
|
||||
const gpio_config_t rst_gpio_config = {
|
||||
.mode = GPIO_MODE_OUTPUT,
|
||||
.pin_bit_mask = BIT64(esp_lcd_touch_gt911->config.rst_gpio_num)
|
||||
};
|
||||
ret = gpio_config(&rst_gpio_config);
|
||||
ESP_GOTO_ON_ERROR(ret, err, TAG, "GPIO config failed");
|
||||
}
|
||||
|
||||
/* Reset controller */
|
||||
ret = touch_gt911_reset(esp_lcd_touch_gt911);
|
||||
ESP_GOTO_ON_ERROR(ret, err, TAG, "GT911 reset failed");
|
||||
|
||||
/* Read status and config info */
|
||||
ret = touch_gt911_read_cfg(esp_lcd_touch_gt911);
|
||||
ESP_GOTO_ON_ERROR(ret, err, TAG, "GT911 init failed");
|
||||
|
||||
err:
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Error (0x%x)! Touch controller GT911 initialization failed!", ret);
|
||||
if (esp_lcd_touch_gt911) {
|
||||
esp_lcd_touch_gt911_del(esp_lcd_touch_gt911);
|
||||
}
|
||||
}
|
||||
|
||||
*out_touch = esp_lcd_touch_gt911;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t esp_lcd_touch_gt911_read_data(esp_lcd_touch_handle_t tp)
|
||||
{
|
||||
esp_err_t err;
|
||||
uint8_t buf[41];
|
||||
uint8_t touch_cnt = 0;
|
||||
uint8_t clear = 0;
|
||||
size_t i = 0;
|
||||
|
||||
assert(tp != NULL);
|
||||
|
||||
err = touch_gt911_i2c_read(tp, ESP_LCD_TOUCH_GT911_READ_XY_REG, buf, 1);
|
||||
ESP_RETURN_ON_ERROR(err, TAG, "I2C read error!");
|
||||
|
||||
/* Any touch data? */
|
||||
if ((buf[0] & 0x80) == 0x00) {
|
||||
touch_gt911_i2c_write(tp, ESP_LCD_TOUCH_GT911_READ_XY_REG, clear);
|
||||
} else {
|
||||
/* Count of touched points */
|
||||
touch_cnt = buf[0] & 0x0f;
|
||||
if (touch_cnt > 5 || touch_cnt == 0) {
|
||||
touch_gt911_i2c_write(tp, ESP_LCD_TOUCH_GT911_READ_XY_REG, clear);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/* Read all points */
|
||||
err = touch_gt911_i2c_read(tp, ESP_LCD_TOUCH_GT911_READ_XY_REG + 1, &buf[1], touch_cnt * 8);
|
||||
ESP_RETURN_ON_ERROR(err, TAG, "I2C read error!");
|
||||
|
||||
/* Clear all */
|
||||
err = touch_gt911_i2c_write(tp, ESP_LCD_TOUCH_GT911_READ_XY_REG, clear);
|
||||
ESP_RETURN_ON_ERROR(err, TAG, "I2C read error!");
|
||||
|
||||
portENTER_CRITICAL(&tp->data.lock);
|
||||
|
||||
/* Number of touched points */
|
||||
touch_cnt = (touch_cnt > CONFIG_ESP_LCD_TOUCH_MAX_POINTS ? CONFIG_ESP_LCD_TOUCH_MAX_POINTS : touch_cnt);
|
||||
tp->data.points = touch_cnt;
|
||||
|
||||
/* Fill all coordinates */
|
||||
for (i = 0; i < touch_cnt; i++) {
|
||||
tp->data.coords[i].x = ((uint16_t)buf[(i * 8) + 3] << 8) + buf[(i * 8) + 2];
|
||||
tp->data.coords[i].y = (((uint16_t)buf[(i * 8) + 5] << 8) + buf[(i * 8) + 4]);
|
||||
tp->data.coords[i].strength = (((uint16_t)buf[(i * 8) + 7] << 8) + buf[(i * 8) + 6]);
|
||||
}
|
||||
|
||||
portEXIT_CRITICAL(&tp->data.lock);
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static bool esp_lcd_touch_gt911_get_xy(esp_lcd_touch_handle_t tp, uint16_t *x, uint16_t *y, uint16_t *strength, uint8_t *point_num, uint8_t max_point_num)
|
||||
{
|
||||
assert(tp != NULL);
|
||||
assert(x != NULL);
|
||||
assert(y != NULL);
|
||||
assert(point_num != NULL);
|
||||
assert(max_point_num > 0);
|
||||
|
||||
portENTER_CRITICAL(&tp->data.lock);
|
||||
|
||||
/* Count of points */
|
||||
*point_num = (tp->data.points > max_point_num ? max_point_num : tp->data.points);
|
||||
|
||||
for (size_t i = 0; i < *point_num; i++) {
|
||||
x[i] = tp->data.coords[i].x;
|
||||
y[i] = tp->data.coords[i].y;
|
||||
if (strength) {
|
||||
strength[i] = tp->data.coords[i].strength;
|
||||
}
|
||||
}
|
||||
|
||||
/* Invalidate */
|
||||
tp->data.points = 0;
|
||||
|
||||
portEXIT_CRITICAL(&tp->data.lock);
|
||||
|
||||
return (*point_num > 0);
|
||||
}
|
||||
|
||||
static esp_err_t esp_lcd_touch_gt911_del(esp_lcd_touch_handle_t tp)
|
||||
{
|
||||
assert(tp != NULL);
|
||||
|
||||
/* Reset GPIO pin settings */
|
||||
if (tp->config.int_gpio_num != GPIO_NUM_NC) {
|
||||
gpio_reset_pin(tp->config.int_gpio_num);
|
||||
}
|
||||
|
||||
/* Reset GPIO pin settings */
|
||||
if (tp->config.rst_gpio_num != GPIO_NUM_NC) {
|
||||
gpio_reset_pin(tp->config.rst_gpio_num);
|
||||
}
|
||||
|
||||
free(tp);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* Private API function
|
||||
*******************************************************************************/
|
||||
|
||||
/* Reset controller */
|
||||
static esp_err_t touch_gt911_reset(esp_lcd_touch_handle_t tp)
|
||||
{
|
||||
assert(tp != NULL);
|
||||
|
||||
if (tp->config.rst_gpio_num != GPIO_NUM_NC) {
|
||||
ESP_RETURN_ON_ERROR(gpio_set_level(tp->config.rst_gpio_num, tp->config.levels.reset), TAG, "GPIO set level error!");
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
ESP_RETURN_ON_ERROR(gpio_set_level(tp->config.rst_gpio_num, !tp->config.levels.reset), TAG, "GPIO set level error!");
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t touch_gt911_read_cfg(esp_lcd_touch_handle_t tp)
|
||||
{
|
||||
uint8_t buf[4];
|
||||
|
||||
assert(tp != NULL);
|
||||
|
||||
ESP_RETURN_ON_ERROR(touch_gt911_i2c_read(tp, ESP_LCD_TOUCH_GT911_PRODUCT_ID_REG, (uint8_t *)&buf[0], 3), TAG, "GT911 read error!");
|
||||
ESP_RETURN_ON_ERROR(touch_gt911_i2c_read(tp, ESP_LCD_TOUCH_GT911_CONFIG_REG, (uint8_t *)&buf[3], 1), TAG, "GT911 read error!");
|
||||
|
||||
ESP_LOGI(TAG, "TouchPad_ID:0x%02x,0x%02x,0x%02x", buf[0], buf[1], buf[2]);
|
||||
ESP_LOGI(TAG, "TouchPad_Config_Version:%d", buf[3]);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t touch_gt911_i2c_read(esp_lcd_touch_handle_t tp, uint16_t reg, uint8_t *data, uint8_t len)
|
||||
{
|
||||
assert(tp != NULL);
|
||||
assert(data != NULL);
|
||||
|
||||
/* Read data */
|
||||
return esp_lcd_panel_io_rx_param(tp->io, reg, data, len);
|
||||
}
|
||||
|
||||
static esp_err_t touch_gt911_i2c_write(esp_lcd_touch_handle_t tp, uint16_t reg, uint8_t data)
|
||||
{
|
||||
assert(tp != NULL);
|
||||
|
||||
// *INDENT-OFF*
|
||||
/* Write data */
|
||||
return esp_lcd_panel_io_tx_param(tp->io, reg, (uint8_t[]){data}, 1);
|
||||
// *INDENT-ON*
|
||||
}
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* @brief ESP LCD touch: GT911
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "esp_lcd_touch.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Create a new GT911 touch driver
|
||||
*
|
||||
* @note The I2C communication should be initialized before use this function.
|
||||
*
|
||||
* @param io LCD/Touch panel IO handle
|
||||
* @param config: Touch configuration
|
||||
* @param out_touch: Touch instance handle
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_NO_MEM if there is no memory for allocating main structure
|
||||
*/
|
||||
esp_err_t esp_lcd_touch_new_i2c_gt911(const esp_lcd_panel_io_handle_t io, const esp_lcd_touch_config_t *config, esp_lcd_touch_handle_t *out_touch);
|
||||
|
||||
/**
|
||||
* @brief I2C address of the GT911 controller
|
||||
*
|
||||
*/
|
||||
#define ESP_LCD_TOUCH_IO_I2C_GT911_ADDRESS (0x5D)
|
||||
|
||||
/**
|
||||
* @brief Touch IO configuration structure
|
||||
*
|
||||
*/
|
||||
#define ESP_LCD_TOUCH_IO_I2C_GT911_CONFIG() \
|
||||
{ \
|
||||
.dev_addr = ESP_LCD_TOUCH_IO_I2C_GT911_ADDRESS, \
|
||||
.control_phase_bytes = 1, \
|
||||
.dc_bit_offset = 0, \
|
||||
.lcd_cmd_bits = 16, \
|
||||
.flags = \
|
||||
{ \
|
||||
.disable_control_phase = 1, \
|
||||
} \
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
+103
@@ -0,0 +1,103 @@
|
||||
#include "sdkconfig.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_log.h"
|
||||
#include "driver/i2c_master.h"
|
||||
#include "esp_lcd_touch_gt911.h"
|
||||
#include "gt911_touch.h"
|
||||
|
||||
#define CONFIG_LCD_HRES 800
|
||||
#define CONFIG_LCD_VRES 1280
|
||||
|
||||
static const char *TAG = "example";
|
||||
|
||||
esp_lcd_touch_handle_t tp;
|
||||
esp_lcd_panel_io_handle_t tp_io_handle;
|
||||
|
||||
uint16_t touch_strength[1];
|
||||
uint8_t touch_cnt = 0;
|
||||
|
||||
gt911_touch::gt911_touch(int8_t sda_pin, int8_t scl_pin, int8_t rst_pin, int8_t int_pin)
|
||||
{
|
||||
_sda = sda_pin;
|
||||
_scl = scl_pin;
|
||||
_rst = rst_pin;
|
||||
_int = int_pin;
|
||||
}
|
||||
|
||||
void gt911_touch::begin()
|
||||
{
|
||||
// i2c_config_t i2c_conf = {
|
||||
// .mode = I2C_MODE_MASTER,
|
||||
// .sda_io_num = (gpio_num_t)_sda,
|
||||
// .scl_io_num = (gpio_num_t)_scl,
|
||||
// .sda_pullup_en = GPIO_PULLUP_ENABLE,
|
||||
// .scl_pullup_en = GPIO_PULLUP_ENABLE,
|
||||
// };
|
||||
// i2c_conf.master.clk_speed = 400000; // 400kHz
|
||||
|
||||
|
||||
// ESP_ERROR_CHECK(i2c_param_config(I2C_NUM_0, &i2c_conf));
|
||||
// ESP_ERROR_CHECK(i2c_driver_install(I2C_NUM_0, i2c_conf.mode, 0, 0, 0));
|
||||
i2c_master_bus_handle_t i2c_handle = NULL;
|
||||
i2c_master_get_bus_handle(1,&i2c_handle);
|
||||
|
||||
esp_lcd_panel_io_i2c_config_t tp_io_config = ESP_LCD_TOUCH_IO_I2C_GT911_CONFIG();
|
||||
tp_io_config.scl_speed_hz = 100000;
|
||||
ESP_LOGI(TAG, "Initialize touch IO (I2C)");
|
||||
esp_lcd_new_panel_io_i2c(i2c_handle, &tp_io_config, &tp_io_handle);
|
||||
|
||||
esp_lcd_touch_config_t tp_cfg = {
|
||||
.x_max = CONFIG_LCD_HRES,
|
||||
.y_max = CONFIG_LCD_VRES,
|
||||
.rst_gpio_num = (gpio_num_t)_rst,
|
||||
.int_gpio_num = (gpio_num_t)_int,
|
||||
.levels = {
|
||||
.reset = 0,
|
||||
.interrupt = 0,
|
||||
},
|
||||
.flags = {
|
||||
.swap_xy = 0,
|
||||
.mirror_x = 0,
|
||||
.mirror_y = 0,
|
||||
},
|
||||
};
|
||||
|
||||
ESP_LOGI(TAG, "Initialize touch controller gt911");
|
||||
ESP_ERROR_CHECK(esp_lcd_touch_new_i2c_gt911(tp_io_handle, &tp_cfg, &tp));
|
||||
}
|
||||
|
||||
bool gt911_touch::getTouch(uint16_t *x, uint16_t *y)
|
||||
{
|
||||
esp_lcd_touch_read_data(tp);
|
||||
bool touchpad_pressed = esp_lcd_touch_get_coordinates(tp, x, y, touch_strength, &touch_cnt, 1);
|
||||
|
||||
return touchpad_pressed;
|
||||
}
|
||||
|
||||
void gt911_touch::set_rotation(uint8_t r){
|
||||
switch(r){
|
||||
case 0:
|
||||
esp_lcd_touch_set_swap_xy(tp, false);
|
||||
esp_lcd_touch_set_mirror_x(tp, false);
|
||||
esp_lcd_touch_set_mirror_y(tp, false);
|
||||
break;
|
||||
case 1:
|
||||
esp_lcd_touch_set_swap_xy(tp, false);
|
||||
esp_lcd_touch_set_mirror_x(tp, true);
|
||||
esp_lcd_touch_set_mirror_y(tp, true);
|
||||
break;
|
||||
case 2:
|
||||
esp_lcd_touch_set_swap_xy(tp, false);
|
||||
esp_lcd_touch_set_mirror_x(tp, false);
|
||||
esp_lcd_touch_set_mirror_y(tp, false);
|
||||
break;
|
||||
case 3:
|
||||
esp_lcd_touch_set_swap_xy(tp, false);
|
||||
esp_lcd_touch_set_mirror_x(tp, true);
|
||||
esp_lcd_touch_set_mirror_y(tp, true);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
#ifndef _GT911_TOUCH_H
|
||||
#define _GT911_TOUCH_H
|
||||
#include <stdio.h>
|
||||
|
||||
class gt911_touch
|
||||
{
|
||||
public:
|
||||
gt911_touch(int8_t sda_pin, int8_t scl_pin, int8_t rst_pin = -1, int8_t int_pin = -1);
|
||||
|
||||
void begin();
|
||||
bool getTouch(uint16_t *x, uint16_t *y);
|
||||
void set_rotation(uint8_t r);
|
||||
|
||||
private:
|
||||
int8_t _sda, _scl, _rst, _int;
|
||||
};
|
||||
|
||||
#endif
|
||||
BIN
Binary file not shown.
|
After Width: | Height: | Size: 53 KiB |
@@ -0,0 +1,4 @@
|
||||
# 请使用arduino_esp32_v3.2.1版本
|
||||
# ESP32-audioI2S V3.3.0
|
||||
# arduino-audio-driver https://github.com/pschatzmann/arduino-audio-driver
|
||||
|
||||
+140
@@ -0,0 +1,140 @@
|
||||
#include "string.h"
|
||||
#include "AudioBoard.h" //https://github.com/pschatzmann/arduino-audio-driver
|
||||
#include "Audio.h" //https://github.com/schreibfaul1/ESP32-audioI2S
|
||||
#include "SD_MMC.h"
|
||||
|
||||
Audio audio;
|
||||
DriverPins my_pins;
|
||||
AudioBoard board(AudioDriverES8311, my_pins);
|
||||
|
||||
//SD_MMC
|
||||
#define SD_D0 39
|
||||
#define SD_D1 40
|
||||
#define SD_D2 41
|
||||
#define SD_D3 42
|
||||
#define SD_CMD 44
|
||||
#define SD_CLK 43
|
||||
|
||||
#define I2S_MCK_IO 13
|
||||
#define I2S_BCK_IO 12
|
||||
#define I2S_DI_IO 48
|
||||
#define I2S_WS_IO 10
|
||||
#define I2S_DO_IO 9
|
||||
#define ES8311_PA 11
|
||||
|
||||
#define I2C_SDA 7
|
||||
#define I2C_SCL 8
|
||||
#define ES8311_ADDRESS 0x18
|
||||
|
||||
static char file_list[20][256];
|
||||
static int file_cnt = 0;
|
||||
static int cnt = 0;
|
||||
|
||||
|
||||
void listDir(fs::FS &fs, const char *dirname, uint8_t levels) {
|
||||
Serial.printf("Listing directory: %s\n", dirname);
|
||||
|
||||
File root = fs.open(dirname);
|
||||
if (!root) {
|
||||
Serial.println("Failed to open directory");
|
||||
return;
|
||||
}
|
||||
if (!root.isDirectory()) {
|
||||
Serial.println("Not a directory");
|
||||
return;
|
||||
}
|
||||
|
||||
File file = root.openNextFile();
|
||||
while (file) {
|
||||
if (file.isDirectory()) {
|
||||
Serial.print(" DIR : ");
|
||||
Serial.println(file.name());
|
||||
if (levels) {
|
||||
listDir(fs, file.path(), levels - 1);
|
||||
}
|
||||
} else {
|
||||
Serial.print(" FILE: ");
|
||||
Serial.print(file.name());
|
||||
snprintf(&file_list[file_cnt][0],256,"/music/%s",file.name());
|
||||
file_cnt++;
|
||||
Serial.print(" SIZE: ");
|
||||
Serial.println(file.size());
|
||||
}
|
||||
file = root.openNextFile();
|
||||
}
|
||||
}
|
||||
|
||||
void setup() {
|
||||
|
||||
Serial.begin(115200);
|
||||
Serial.println("start");
|
||||
if(! SD_MMC.setPins(SD_CLK, SD_CMD, SD_D0, SD_D1, SD_D2, SD_D3)){
|
||||
Serial.println("Pin change failed!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!SD_MMC.begin()) {
|
||||
Serial.println("Card Mount Failed");
|
||||
return;
|
||||
}
|
||||
|
||||
listDir(SD_MMC,"/music/",0);
|
||||
|
||||
// pinMode(ES8311_PA, OUTPUT);
|
||||
// digitalWrite(ES8311_PA, HIGH);
|
||||
|
||||
my_pins.addI2C(PinFunction::CODEC,I2C_SCL,I2C_SDA,ES8311_ADDRESS);
|
||||
my_pins.addPin(PinFunction::PA,ES8311_PA,PinLogic::Output);
|
||||
|
||||
CodecConfig cfg;
|
||||
cfg.input_device = ADC_INPUT_LINE1;
|
||||
cfg.output_device = DAC_OUTPUT_ALL;
|
||||
cfg.i2s.bits = BIT_LENGTH_16BITS;
|
||||
cfg.i2s.rate = RATE_44K;
|
||||
// cfg.i2s.fmt = I2S_NORMAL;
|
||||
// cfg.i2s.mode = MODE_SLAVE;
|
||||
board.begin(cfg);
|
||||
|
||||
audio.setPinout(I2S_BCK_IO, I2S_WS_IO, I2S_DO_IO,I2S_MCK_IO);
|
||||
audio.setVolume(4); // 0...21
|
||||
audio.connecttoFS(SD_MMC, file_list[cnt]);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
audio.loop();
|
||||
}
|
||||
|
||||
// optional
|
||||
void audio_info(const char *info){
|
||||
Serial.print("info "); Serial.println(info);
|
||||
}
|
||||
void audio_id3data(const char *info){ //id3 metadata
|
||||
Serial.print("id3data ");Serial.println(info);
|
||||
}
|
||||
void audio_eof_mp3(const char *info){ //end of file
|
||||
Serial.print("eof_mp3 ");Serial.println(info);
|
||||
cnt++;
|
||||
if(cnt > file_cnt - 1)
|
||||
cnt = 0;
|
||||
|
||||
audio.connecttoFS(SD_MMC, file_list[cnt]);
|
||||
|
||||
}
|
||||
void audio_showstation(const char *info){
|
||||
Serial.print("station ");Serial.println(info);
|
||||
}
|
||||
void audio_showstreamtitle(const char *info){
|
||||
Serial.print("streamtitle ");Serial.println(info);
|
||||
}
|
||||
void audio_bitrate(const char *info){
|
||||
Serial.print("bitrate ");Serial.println(info);
|
||||
}
|
||||
void audio_commercial(const char *info){ //duration in sec
|
||||
Serial.print("commercial ");Serial.println(info);
|
||||
}
|
||||
void audio_icyurl(const char *info){ //homepage
|
||||
Serial.print("icyurl ");Serial.println(info);
|
||||
}
|
||||
void audio_lasthost(const char *info){ //stream URL played
|
||||
Serial.print("lasthost ");Serial.println(info);
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 52 KiB |
+20
@@ -0,0 +1,20 @@
|
||||
set(SRCS "")
|
||||
list(APPEND SRCS
|
||||
"src/bsp_board_extra.c"
|
||||
)
|
||||
|
||||
set(INCLUDE_DIRS "")
|
||||
list(APPEND INCLUDE_DIRS "include")
|
||||
|
||||
foreach(SRC_DIR IN LISTS SRC_DIRS)
|
||||
file(GLOB_RECURSE SRC ${SRC_DIR}/*.c)
|
||||
list(APPEND SRCS ${SRC})
|
||||
list(APPEND INCLUDE_DIRS ${SRC_DIR}/include)
|
||||
endforeach()
|
||||
|
||||
idf_component_register(
|
||||
SRCS ${SRCS}
|
||||
INCLUDE_DIRS ${INCLUDE_DIRS}
|
||||
REQUIRES driver
|
||||
PRIV_REQUIRES esp_timer fatfs esp_psram esp_mm
|
||||
)
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
menu "Board Support Package"
|
||||
config BSP_I2S_NUM
|
||||
int "I2S peripheral index"
|
||||
default 1
|
||||
range 0 1
|
||||
help
|
||||
ESP32S3 has two I2S peripherals, pick the one you want to use.
|
||||
endmenu
|
||||
+202
@@ -0,0 +1,202 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
dependencies:
|
||||
idf:
|
||||
version: '>=5.3.0'
|
||||
|
||||
espressif/esp32_p4_function_ev_board:
|
||||
path: ../espressif__esp32_p4_function_ev_board
|
||||
|
||||
chmorgan/esp-audio-player:
|
||||
version: "1.0.*"
|
||||
public: true
|
||||
|
||||
chmorgan/esp-file-iterator:
|
||||
version: "1.0.0"
|
||||
public: true
|
||||
|
||||
description: Board Support Package for ESP32-P4-Function-ev-board
|
||||
targets:
|
||||
- esp32p4
|
||||
version: 0.0.2
|
||||
+220
@@ -0,0 +1,220 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
#include <stdbool.h>
|
||||
#include "esp_codec_dev.h"
|
||||
#include "esp_err.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "driver/i2s_std.h"
|
||||
#include "audio_player.h"
|
||||
#include "file_iterator.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define CODEC_DEFAULT_SAMPLE_RATE (44100)
|
||||
#define CODEC_DEFAULT_BIT_WIDTH (16)
|
||||
#define CODEC_DEFAULT_ADC_VOLUME (24.0)
|
||||
#define CODEC_DEFAULT_CHANNEL (2)
|
||||
#define CODEC_DEFAULT_VOLUME (60)
|
||||
|
||||
#define BSP_LCD_BACKLIGHT_BRIGHTNESS_MAX (95)
|
||||
#define BSP_LCD_BACKLIGHT_BRIGHTNESS_MIN (0)
|
||||
#define LCD_LEDC_CH (CONFIG_BSP_DISPLAY_BRIGHTNESS_LEDC_CH)
|
||||
|
||||
/**************************************************************************************************
|
||||
* BSP Extra interface
|
||||
* Mainly provided some I2S Codec interfaces.
|
||||
**************************************************************************************************/
|
||||
/**
|
||||
* @brief Player set mute.
|
||||
*
|
||||
* @param enable: true or false
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Success
|
||||
* - Others: Fail
|
||||
*/
|
||||
esp_err_t bsp_extra_codec_mute_set(bool enable);
|
||||
|
||||
/**
|
||||
* @brief Player set volume.
|
||||
*
|
||||
* @param volume: volume set
|
||||
* @param volume_set: volume set response
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Success
|
||||
* - Others: Fail
|
||||
*/
|
||||
esp_err_t bsp_extra_codec_volume_set(int volume, int *volume_set);
|
||||
|
||||
/**
|
||||
* @brief Player get volume.
|
||||
*
|
||||
* @return
|
||||
* - volume: volume get
|
||||
*/
|
||||
int bsp_extra_codec_volume_get(void);
|
||||
|
||||
/**
|
||||
* @brief Stop I2S function.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Success
|
||||
* - Others: Fail
|
||||
*/
|
||||
esp_err_t bsp_extra_codec_dev_stop(void);
|
||||
|
||||
/**
|
||||
* @brief Resume I2S function.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Success
|
||||
* - Others: Fail
|
||||
*/
|
||||
esp_err_t bsp_extra_codec_dev_resume(void);
|
||||
|
||||
/**
|
||||
* @brief Set I2S format to codec.
|
||||
*
|
||||
* @param rate: Sample rate of sample
|
||||
* @param bits_cfg: Bit lengths of one channel data
|
||||
* @param ch: Channels of sample
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Success
|
||||
* - Others: Fail
|
||||
*/
|
||||
esp_err_t bsp_extra_codec_set_fs(uint32_t rate, uint32_t bits_cfg, i2s_slot_mode_t ch);
|
||||
esp_err_t bsp_extra_codec_set_fs_play(uint32_t rate, uint32_t bits_cfg, i2s_slot_mode_t ch);
|
||||
|
||||
/**
|
||||
* @brief Read data from recoder.
|
||||
*
|
||||
* @param audio_buffer: The pointer of receiving data buffer
|
||||
* @param len: Max data buffer length
|
||||
* @param bytes_read: Byte number that actually be read, can be NULL if not needed
|
||||
* @param timeout_ms: Max block time
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Success
|
||||
* - Others: Fail
|
||||
*/
|
||||
esp_err_t bsp_extra_i2s_read(void *audio_buffer, size_t len, size_t *bytes_read, uint32_t timeout_ms);
|
||||
|
||||
/**
|
||||
* @brief Write data to player.
|
||||
*
|
||||
* @param audio_buffer: The pointer of sent data buffer
|
||||
* @param len: Max data buffer length
|
||||
* @param bytes_written: Byte number that actually be sent, can be NULL if not needed
|
||||
* @param timeout_ms: Max block time
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Success
|
||||
* - Others: Fail
|
||||
*/
|
||||
esp_err_t bsp_extra_i2s_write(void *audio_buffer, size_t len, size_t *bytes_written, uint32_t timeout_ms);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Initialize codec play and record handle.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Success
|
||||
* - Others: Fail
|
||||
*/
|
||||
esp_err_t bsp_extra_codec_init();
|
||||
|
||||
/**
|
||||
* @brief Initialize audio player task.
|
||||
*
|
||||
* @param path file path
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Success
|
||||
* - Others: Fail
|
||||
*/
|
||||
esp_err_t bsp_extra_player_init(void);
|
||||
|
||||
/**
|
||||
* @brief Delete audio player task.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Success
|
||||
* - Others: Fail
|
||||
*/
|
||||
esp_err_t bsp_extra_player_del(void);
|
||||
|
||||
/**
|
||||
* @brief Initialize a file iterator instance
|
||||
*
|
||||
* @param path The file path for the iterator.
|
||||
* @param ret_instance A pointer to the file iterator instance to be returned.
|
||||
* @return
|
||||
* - ESP_OK: Successfully initialized the file iterator instance.
|
||||
* - ESP_FAIL: Failed to initialize the file iterator instance due to invalid parameters or memory allocation failure.
|
||||
*/
|
||||
esp_err_t bsp_extra_file_instance_init(const char *path, file_iterator_instance_t **ret_instance);
|
||||
|
||||
/**
|
||||
* @brief Play the audio file at the specified index in the file iterator
|
||||
*
|
||||
* @param instance The file iterator instance.
|
||||
* @param index The index of the file to play within the iterator.
|
||||
* @return
|
||||
* - ESP_OK: Successfully started playing the audio file.
|
||||
* - ESP_FAIL: Failed to play the audio file due to invalid parameters or file access issues.
|
||||
*/
|
||||
esp_err_t bsp_extra_player_play_index(file_iterator_instance_t *instance, int index);
|
||||
|
||||
/**
|
||||
* @brief Play the audio file specified by the file path
|
||||
*
|
||||
* @param file_path The path to the audio file to be played.
|
||||
* @return
|
||||
* - ESP_OK: Successfully started playing the audio file.
|
||||
* - ESP_FAIL: Failed to play the audio file due to file access issues.
|
||||
*/
|
||||
esp_err_t bsp_extra_player_play_file(const char *file_path);
|
||||
|
||||
/**
|
||||
* @brief Register a callback function for the audio player
|
||||
*
|
||||
* @param cb The callback function to be registered.
|
||||
* @param user_data User data to be passed to the callback function.
|
||||
*/
|
||||
void bsp_extra_player_register_callback(audio_player_cb_t cb, void *user_data);
|
||||
|
||||
/**
|
||||
* @brief Check if the specified audio file is currently playing
|
||||
*
|
||||
* @param file_path The path to the audio file to check.
|
||||
* @return
|
||||
* - true: The specified audio file is currently playing.
|
||||
* - false: The specified audio file is not currently playing.
|
||||
*/
|
||||
bool bsp_extra_player_is_playing_by_path(const char *file_path);
|
||||
|
||||
/**
|
||||
* @brief Check if the audio file at the specified index is currently playing
|
||||
*
|
||||
* @param instance The file iterator instance.
|
||||
* @param index The index of the file to check.
|
||||
* @return
|
||||
* - true: The audio file at the specified index is currently playing.
|
||||
* - false: The audio file at the specified index is not currently playing.
|
||||
*/
|
||||
bool bsp_extra_player_is_playing_by_index(file_iterator_instance_t *instance, int index);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
+285
@@ -0,0 +1,285 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include "esp_log.h"
|
||||
#include "esp_check.h"
|
||||
#include "esp_codec_dev_defaults.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_vfs_fat.h"
|
||||
#include "driver/i2c.h"
|
||||
#include "driver/i2s_std.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "driver/ledc.h"
|
||||
|
||||
#include "bsp/esp-bsp.h"
|
||||
#include "bsp_board_extra.h"
|
||||
|
||||
static const char *TAG = "bsp_extra_board";
|
||||
|
||||
static esp_codec_dev_handle_t play_dev_handle;
|
||||
static esp_codec_dev_handle_t record_dev_handle;
|
||||
|
||||
static bool _is_audio_init = false;
|
||||
static bool _is_player_init = false;
|
||||
static int _vloume_intensity = CODEC_DEFAULT_VOLUME;
|
||||
|
||||
static audio_player_cb_t audio_idle_callback = NULL;
|
||||
static void *audio_idle_cb_user_data = NULL;
|
||||
static char audio_file_path[128];
|
||||
|
||||
/**************************************************************************************************
|
||||
*
|
||||
* Extra Board Function
|
||||
*
|
||||
**************************************************************************************************/
|
||||
|
||||
static esp_err_t audio_mute_function(AUDIO_PLAYER_MUTE_SETTING setting)
|
||||
{
|
||||
// Volume saved when muting and restored when unmuting. Restoring volume is necessary
|
||||
// as es8311_set_voice_mute(true) results in voice volume (REG32) being set to zero.
|
||||
|
||||
bsp_extra_codec_mute_set(setting == AUDIO_PLAYER_MUTE ? true : false);
|
||||
|
||||
// restore the voice volume upon unmuting
|
||||
if (setting == AUDIO_PLAYER_UNMUTE) {
|
||||
ESP_RETURN_ON_ERROR(esp_codec_dev_set_out_vol(play_dev_handle, _vloume_intensity), TAG, "Set Codec volume failed");
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static void audio_callback(audio_player_cb_ctx_t *ctx)
|
||||
{
|
||||
if (audio_idle_callback) {
|
||||
ctx->user_ctx = audio_idle_cb_user_data;
|
||||
audio_idle_callback(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
esp_err_t bsp_extra_i2s_read(void *audio_buffer, size_t len, size_t *bytes_read, uint32_t timeout_ms)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
ret = esp_codec_dev_read(record_dev_handle, audio_buffer, len);
|
||||
*bytes_read = len;
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t bsp_extra_i2s_write(void *audio_buffer, size_t len, size_t *bytes_written, uint32_t timeout_ms)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
ret = esp_codec_dev_write(play_dev_handle, audio_buffer, len);
|
||||
*bytes_written = len;
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t bsp_extra_codec_set_fs(uint32_t rate, uint32_t bits_cfg, i2s_slot_mode_t ch)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
ESP_LOGI(TAG,"rate = %" PRId32 "bits_cfg = %" PRId32, rate,bits_cfg);
|
||||
|
||||
esp_codec_dev_sample_info_t fs = {
|
||||
.sample_rate = rate,
|
||||
.channel = ch,
|
||||
.bits_per_sample = bits_cfg,
|
||||
};
|
||||
|
||||
if (play_dev_handle) {
|
||||
ret = esp_codec_dev_close(play_dev_handle);
|
||||
}
|
||||
if (record_dev_handle) {
|
||||
ret |= esp_codec_dev_close(record_dev_handle);
|
||||
ret |= esp_codec_dev_set_in_gain(record_dev_handle, CODEC_DEFAULT_ADC_VOLUME);
|
||||
}
|
||||
|
||||
if (play_dev_handle) {
|
||||
ret |= esp_codec_dev_open(play_dev_handle, &fs);
|
||||
}
|
||||
if (record_dev_handle) {
|
||||
ret |= esp_codec_dev_open(record_dev_handle, &fs);
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG,"ret = 0x%x , %s",ret,strerror(ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t bsp_extra_codec_set_fs_play(uint32_t rate, uint32_t bits_cfg, i2s_slot_mode_t ch)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
ESP_LOGI(TAG,"rate = %" PRId32 "bits_cfg = %" PRId32, rate,bits_cfg);
|
||||
|
||||
esp_codec_dev_sample_info_t fs = {
|
||||
.sample_rate = rate,
|
||||
.channel = CODEC_DEFAULT_CHANNEL,
|
||||
.bits_per_sample = bits_cfg,
|
||||
};
|
||||
|
||||
if (play_dev_handle) {
|
||||
ret = esp_codec_dev_close(play_dev_handle);
|
||||
}
|
||||
|
||||
if (play_dev_handle) {
|
||||
ret |= esp_codec_dev_open(play_dev_handle, &fs);
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG,"ret = 0x%x , %s",ret,strerror(ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t bsp_extra_codec_volume_set(int volume, int *volume_set)
|
||||
{
|
||||
ESP_RETURN_ON_ERROR(esp_codec_dev_set_out_vol(play_dev_handle, volume), TAG, "Set Codec volume failed");
|
||||
_vloume_intensity = volume;
|
||||
|
||||
ESP_LOGI(TAG, "Setting volume: %d", volume);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
int bsp_extra_codec_volume_get(void)
|
||||
{
|
||||
return _vloume_intensity;
|
||||
}
|
||||
|
||||
esp_err_t bsp_extra_codec_mute_set(bool enable)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
ret = esp_codec_dev_set_out_mute(play_dev_handle, enable);
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t bsp_extra_codec_dev_stop(void)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
|
||||
if (play_dev_handle) {
|
||||
ret = esp_codec_dev_close(play_dev_handle);
|
||||
}
|
||||
|
||||
if (record_dev_handle) {
|
||||
ret = esp_codec_dev_close(record_dev_handle);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t bsp_extra_codec_dev_resume(void)
|
||||
{
|
||||
return bsp_extra_codec_set_fs(CODEC_DEFAULT_SAMPLE_RATE, CODEC_DEFAULT_BIT_WIDTH, CODEC_DEFAULT_CHANNEL);
|
||||
}
|
||||
|
||||
esp_err_t bsp_extra_codec_init()
|
||||
{
|
||||
if (_is_audio_init) {
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
play_dev_handle = bsp_audio_codec_speaker_init();
|
||||
assert((play_dev_handle) && "play_dev_handle not initialized");
|
||||
|
||||
record_dev_handle = bsp_audio_codec_microphone_init();
|
||||
assert((record_dev_handle) && "record_dev_handle not initialized");
|
||||
|
||||
bsp_extra_codec_set_fs(CODEC_DEFAULT_SAMPLE_RATE, CODEC_DEFAULT_BIT_WIDTH, CODEC_DEFAULT_CHANNEL);
|
||||
|
||||
_is_audio_init = true;
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t bsp_extra_player_init(void)
|
||||
{
|
||||
if (_is_player_init) {
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
audio_player_config_t config = { .mute_fn = audio_mute_function,
|
||||
.write_fn = bsp_extra_i2s_write,
|
||||
.clk_set_fn = bsp_extra_codec_set_fs,
|
||||
.priority = 5
|
||||
};
|
||||
ESP_RETURN_ON_ERROR(audio_player_new(config), TAG, "audio_player_init failed");
|
||||
audio_player_callback_register(audio_callback, NULL);
|
||||
|
||||
_is_player_init = true;
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t bsp_extra_player_del(void)
|
||||
{
|
||||
_is_player_init = false;
|
||||
|
||||
ESP_RETURN_ON_ERROR(audio_player_delete(), TAG, "audio_player_delete failed");
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t bsp_extra_file_instance_init(const char *path, file_iterator_instance_t **ret_instance)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(path, ESP_FAIL, TAG, "path is NULL");
|
||||
ESP_RETURN_ON_FALSE(ret_instance, ESP_FAIL, TAG, "ret_instance is NULL");
|
||||
|
||||
file_iterator_instance_t *file_iterator = file_iterator_new(path);
|
||||
ESP_RETURN_ON_FALSE(file_iterator, ESP_FAIL, TAG, "file_iterator_new failed, %s", path);
|
||||
|
||||
*ret_instance = file_iterator;
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t bsp_extra_player_play_index(file_iterator_instance_t *instance, int index)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(instance, ESP_FAIL, TAG, "instance is NULL");
|
||||
|
||||
ESP_LOGI(TAG, "play_index(%d)", index);
|
||||
char filename[128];
|
||||
int retval = file_iterator_get_full_path_from_index(instance, index, filename, sizeof(filename));
|
||||
ESP_RETURN_ON_FALSE(retval != 0, ESP_FAIL, TAG, "file_iterator_get_full_path_from_index failed");
|
||||
|
||||
ESP_LOGI(TAG, "opening file '%s'", filename);
|
||||
FILE *fp = fopen(filename, "rb");
|
||||
ESP_RETURN_ON_FALSE(fp, ESP_FAIL, TAG, "unable to open file");
|
||||
|
||||
ESP_LOGI(TAG, "Playing '%s'", filename);
|
||||
ESP_RETURN_ON_ERROR(audio_player_play(fp), TAG, "audio_player_play failed");
|
||||
|
||||
memcpy(audio_file_path, filename, sizeof(audio_file_path));
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t bsp_extra_player_play_file(const char *file_path)
|
||||
{
|
||||
ESP_LOGI(TAG, "opening file '%s'", file_path);
|
||||
FILE *fp = fopen(file_path, "rb");
|
||||
ESP_RETURN_ON_FALSE(fp, ESP_FAIL, TAG, "unable to open file");
|
||||
|
||||
ESP_LOGI(TAG, "Playing '%s'", file_path);
|
||||
ESP_RETURN_ON_ERROR(audio_player_play(fp), TAG, "audio_player_play failed");
|
||||
|
||||
memcpy(audio_file_path, file_path, sizeof(audio_file_path));
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
void bsp_extra_player_register_callback(audio_player_cb_t cb, void *user_data)
|
||||
{
|
||||
audio_idle_callback = cb;
|
||||
audio_idle_cb_user_data = user_data;
|
||||
}
|
||||
|
||||
bool bsp_extra_player_is_playing_by_path(const char *file_path)
|
||||
{
|
||||
return (strcmp(audio_file_path, file_path) == 0);
|
||||
}
|
||||
|
||||
bool bsp_extra_player_is_playing_by_index(file_iterator_instance_t *instance, int index)
|
||||
{
|
||||
return (index == file_iterator_get_index(instance));
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
1e0436b3d220275d6b7930330b1a9b828fedf0dbc81006531e592b059842641e
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
|
||||
idf_component_register(
|
||||
SRCS "esp32_p4_function_ev_board.c"
|
||||
INCLUDE_DIRS "include"
|
||||
PRIV_INCLUDE_DIRS "priv_include"
|
||||
REQUIRES driver
|
||||
PRIV_REQUIRES esp_lcd usb spiffs fatfs
|
||||
)
|
||||
+138
@@ -0,0 +1,138 @@
|
||||
menu "Board Support Package(ESP32-P4)"
|
||||
|
||||
config BSP_ERROR_CHECK
|
||||
bool "Enable error check in BSP"
|
||||
default y
|
||||
help
|
||||
Error check assert the application before returning the error code.
|
||||
|
||||
menu "I2C"
|
||||
config BSP_I2C_NUM
|
||||
int "I2C peripheral index"
|
||||
default 1
|
||||
range 0 1
|
||||
help
|
||||
ESP32P4 has two I2C peripherals, pick the one you want to use.
|
||||
|
||||
config BSP_I2C_FAST_MODE
|
||||
bool "Enable I2C fast mode"
|
||||
default y
|
||||
help
|
||||
I2C has two speed modes: normal (100kHz) and fast (400kHz).
|
||||
|
||||
config BSP_I2C_CLK_SPEED_HZ
|
||||
int
|
||||
default 400000 if BSP_I2C_FAST_MODE
|
||||
default 100000
|
||||
endmenu
|
||||
|
||||
menu "I2S"
|
||||
config BSP_I2S_NUM
|
||||
int "I2S peripheral index"
|
||||
default 1
|
||||
range 0 2
|
||||
help
|
||||
ESP32P4 has three I2S peripherals, pick the one you want to use.
|
||||
endmenu
|
||||
|
||||
menu "uSD card - Virtual File System"
|
||||
config BSP_SD_FORMAT_ON_MOUNT_FAIL
|
||||
bool "Format uSD card if mounting fails"
|
||||
default n
|
||||
help
|
||||
The SDMMC host will format (FAT) the uSD card if it fails to mount the filesystem.
|
||||
|
||||
config BSP_SD_MOUNT_POINT
|
||||
string "uSD card mount point"
|
||||
default "/sdcard"
|
||||
help
|
||||
Mount point of the uSD card in the Virtual File System
|
||||
|
||||
endmenu
|
||||
|
||||
menu "SPIFFS - Virtual File System"
|
||||
config BSP_SPIFFS_FORMAT_ON_MOUNT_FAIL
|
||||
bool "Format SPIFFS if mounting fails"
|
||||
default n
|
||||
help
|
||||
Format SPIFFS if it fails to mount the filesystem.
|
||||
|
||||
config BSP_SPIFFS_MOUNT_POINT
|
||||
string "SPIFFS mount point"
|
||||
default "/spiffs"
|
||||
help
|
||||
Mount point of SPIFFS in the Virtual File System.
|
||||
|
||||
config BSP_SPIFFS_PARTITION_LABEL
|
||||
string "Partition label of SPIFFS"
|
||||
default "storage"
|
||||
help
|
||||
Partition label which stores SPIFFS.
|
||||
|
||||
config BSP_SPIFFS_MAX_FILES
|
||||
int "Max files supported for SPIFFS VFS"
|
||||
default 5
|
||||
help
|
||||
Supported max files for SPIFFS in the Virtual File System.
|
||||
endmenu
|
||||
|
||||
menu "Display"
|
||||
config BSP_LCD_DPI_BUFFER_NUMS
|
||||
int "Set number of frame buffers"
|
||||
default 1
|
||||
range 1 3
|
||||
help
|
||||
Let DPI LCD driver create a specified number of frame-size buffers. Only when it is set to multiple can the avoiding tearing be turned on.
|
||||
|
||||
config BSP_DISPLAY_LVGL_AVOID_TEAR
|
||||
bool "Avoid tearing effect"
|
||||
depends on BSP_LCD_DPI_BUFFER_NUMS > 1
|
||||
default "n"
|
||||
help
|
||||
Avoid tearing effect through LVGL buffer mode and double frame buffers of RGB LCD. This feature is only available for RGB LCD.
|
||||
|
||||
choice BSP_DISPLAY_LVGL_MODE
|
||||
depends on BSP_DISPLAY_LVGL_AVOID_TEAR
|
||||
prompt "Select LVGL buffer mode"
|
||||
default BSP_DISPLAY_LVGL_FULL_REFRESH
|
||||
config BSP_DISPLAY_LVGL_FULL_REFRESH
|
||||
bool "Full refresh"
|
||||
config BSP_DISPLAY_LVGL_DIRECT_MODE
|
||||
bool "Direct mode"
|
||||
endchoice
|
||||
|
||||
config BSP_DISPLAY_BRIGHTNESS_LEDC_CH
|
||||
int "LEDC channel index"
|
||||
default 1
|
||||
range 0 7
|
||||
help
|
||||
LEDC channel is used to generate PWM signal that controls display brightness.
|
||||
Set LEDC index that should be used.
|
||||
|
||||
choice BSP_LCD_COLOR_FORMAT
|
||||
prompt "Select LCD color format"
|
||||
default BSP_LCD_COLOR_FORMAT_RGB565
|
||||
help
|
||||
Select the LCD color format RGB565/RGB888.
|
||||
|
||||
config BSP_LCD_COLOR_FORMAT_RGB565
|
||||
bool "RGB565"
|
||||
config BSP_LCD_COLOR_FORMAT_RGB888
|
||||
bool "RGB888"
|
||||
endchoice
|
||||
|
||||
choice BSP_LCD_TYPE
|
||||
prompt "Select LCD type"
|
||||
default BSP_LCD_TYPE_1024_600
|
||||
help
|
||||
Select the LCD.
|
||||
|
||||
config BSP_LCD_TYPE_1024_600
|
||||
bool "LCD 7-inch 1024x600 - ek79007"
|
||||
config BSP_LCD_TYPE_1280_800
|
||||
bool "LCD 1280x800 - ili9881c"
|
||||
endchoice
|
||||
|
||||
endmenu
|
||||
|
||||
endmenu
|
||||
+202
@@ -0,0 +1,202 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
# BSP: ESP32-P4 Function EV Board
|
||||
|
||||
[](https://components.espressif.com/components/espressif/esp32_p4_function_ev_board)
|
||||
|
||||
ESP32-P4 Function EV Board is internal Espressif board for testing features on ESP32P4 chip.
|
||||
|
||||
| HW version | BSP Version |
|
||||
| :--------: | :---------: |
|
||||
| V1.0 | ^1 |
|
||||
| V1.2 | ^2 |
|
||||
| [V1.4](https://docs.espressif.com/projects/esp-dev-kits/en/latest/esp32p4/esp32-p4-function-ev-board/user_guide.html) | ^3 |
|
||||
|
||||
## Configuration
|
||||
|
||||
Configuration in `menuconfig`.
|
||||
|
||||
Selection LCD display `Board Support Package(ESP32-P4) --> Display --> Select LCD type`
|
||||
- LCD 7-inch 1280x800 - ili9881c (default)
|
||||
- LCD 1024x600 - ek79007
|
||||
|
||||
Selection color format `Board Support Package(ESP32-P4) --> Display --> Select LCD color format`
|
||||
- RGB565 (default)
|
||||
- RGB888
|
||||
|
||||
|
||||
<!-- Autogenerated start: Dependencies -->
|
||||
### Capabilities and dependencies
|
||||
| Capability | Available | Component |Version|
|
||||
|-------------|------------------|----------------------------------------------------------------------------------------------------------|-------|
|
||||
| DISPLAY |:heavy_check_mark:| [espressif/esp_lcd_ek79007](https://components.espressif.com/components/espressif/esp_lcd_ek79007) | 1.* |
|
||||
| LVGL_PORT |:heavy_check_mark:| [espressif/esp_lvgl_port](https://components.espressif.com/components/espressif/esp_lvgl_port) | ^2 |
|
||||
| TOUCH |:heavy_check_mark:|[espressif/esp_lcd_touch_gt911](https://components.espressif.com/components/espressif/esp_lcd_touch_gt911)| ^1 |
|
||||
| BUTTONS | :x: | | |
|
||||
| AUDIO |:heavy_check_mark:| [espressif/esp_codec_dev](https://components.espressif.com/components/espressif/esp_codec_dev) | 1.2.* |
|
||||
|AUDIO_SPEAKER|:heavy_check_mark:| | |
|
||||
| AUDIO_MIC |:heavy_check_mark:| | |
|
||||
| SDCARD |:heavy_check_mark:| idf | >=5.3 |
|
||||
| IMU | :x: | | |
|
||||
<!-- Autogenerated end: Dependencies -->
|
||||
+784
@@ -0,0 +1,784 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "sdkconfig.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "driver/ledc.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_check.h"
|
||||
#include "esp_spiffs.h"
|
||||
#include "esp_lcd_panel_ops.h"
|
||||
#include "esp_lcd_mipi_dsi.h"
|
||||
#include "esp_ldo_regulator.h"
|
||||
#include "esp_vfs_fat.h"
|
||||
#include "usb/usb_host.h"
|
||||
#include "sd_pwr_ctrl_by_on_chip_ldo.h"
|
||||
|
||||
|
||||
#if CONFIG_BSP_LCD_TYPE_1024_600
|
||||
#include "esp_lcd_ek79007.h"
|
||||
#include "esp_lcd_st7701.h"
|
||||
#else
|
||||
#include "esp_lcd_ili9881c.h"
|
||||
#endif
|
||||
|
||||
#include "bsp/esp32_p4_function_ev_board.h"
|
||||
#include "bsp/display.h"
|
||||
#include "bsp/touch.h"
|
||||
#include "esp_lcd_touch_gt911.h"
|
||||
#include "bsp_err_check.h"
|
||||
#include "esp_codec_dev_defaults.h"
|
||||
#include "lvgl.h"
|
||||
|
||||
static const char *TAG = "ESP32_P4_EV";
|
||||
|
||||
#if (BSP_CONFIG_NO_GRAPHIC_LIB == 0)
|
||||
static lv_indev_t *disp_indev = NULL;
|
||||
#endif // (BSP_CONFIG_NO_GRAPHIC_LIB == 0)
|
||||
|
||||
sdmmc_card_t *bsp_sdcard = NULL; // Global uSD card handler
|
||||
static bool i2c_initialized = false;
|
||||
static TaskHandle_t usb_host_task; // USB Host Library task
|
||||
#if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0))
|
||||
static i2c_master_bus_handle_t i2c_handle = NULL; // I2C Handle
|
||||
#endif
|
||||
static i2s_chan_handle_t i2s_tx_chan = NULL;
|
||||
static i2s_chan_handle_t i2s_rx_chan = NULL;
|
||||
static const audio_codec_data_if_t *i2s_data_if = NULL; /* Codec data interface */
|
||||
|
||||
/* Can be used for `i2s_std_gpio_config_t` and/or `i2s_std_config_t` initialization */
|
||||
#define BSP_I2S_GPIO_CFG \
|
||||
{ \
|
||||
.mclk = BSP_I2S_MCLK, \
|
||||
.bclk = BSP_I2S_SCLK, \
|
||||
.ws = BSP_I2S_LCLK, \
|
||||
.dout = BSP_I2S_DOUT, \
|
||||
.din = BSP_I2S_DSIN, \
|
||||
.invert_flags = { \
|
||||
.mclk_inv = false, \
|
||||
.bclk_inv = false, \
|
||||
.ws_inv = false, \
|
||||
}, \
|
||||
}
|
||||
|
||||
/* This configuration is used by default in `bsp_extra_audio_init()` */
|
||||
#define BSP_I2S_DUPLEX_MONO_CFG(_sample_rate) \
|
||||
{ \
|
||||
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(_sample_rate), \
|
||||
.slot_cfg = I2S_STD_PHILIP_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_STEREO), \
|
||||
.gpio_cfg = BSP_I2S_GPIO_CFG, \
|
||||
}
|
||||
|
||||
esp_err_t bsp_i2c_init(void)
|
||||
{
|
||||
/* I2C was initialized before */
|
||||
if (i2c_initialized) {
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
i2c_master_bus_config_t i2c_bus_conf = {
|
||||
.clk_source = I2C_CLK_SRC_DEFAULT,
|
||||
.sda_io_num = BSP_I2C_SDA,
|
||||
.scl_io_num = BSP_I2C_SCL,
|
||||
.i2c_port = BSP_I2C_NUM,
|
||||
};
|
||||
BSP_ERROR_CHECK_RETURN_ERR(i2c_new_master_bus(&i2c_bus_conf, &i2c_handle));
|
||||
|
||||
i2c_initialized = true;
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t bsp_i2c_deinit(void)
|
||||
{
|
||||
BSP_ERROR_CHECK_RETURN_ERR(i2c_del_master_bus(i2c_handle));
|
||||
i2c_initialized = false;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
i2c_master_bus_handle_t bsp_i2c_get_handle(void)
|
||||
{
|
||||
return i2c_handle;
|
||||
}
|
||||
|
||||
esp_err_t bsp_sdcard_mount(void)
|
||||
{
|
||||
const esp_vfs_fat_sdmmc_mount_config_t mount_config = {
|
||||
#ifdef CONFIG_BSP_SD_FORMAT_ON_MOUNT_FAIL
|
||||
.format_if_mount_failed = true,
|
||||
#else
|
||||
.format_if_mount_failed = false,
|
||||
#endif
|
||||
.max_files = 5,
|
||||
.allocation_unit_size = 64 * 1024
|
||||
};
|
||||
|
||||
sdmmc_host_t host = SDMMC_HOST_DEFAULT();
|
||||
host.slot = SDMMC_HOST_SLOT_0;
|
||||
host.max_freq_khz = SDMMC_FREQ_HIGHSPEED;
|
||||
|
||||
sd_pwr_ctrl_ldo_config_t ldo_config = {
|
||||
.ldo_chan_id = 4,
|
||||
};
|
||||
sd_pwr_ctrl_handle_t pwr_ctrl_handle = NULL;
|
||||
esp_err_t ret = sd_pwr_ctrl_new_on_chip_ldo(&ldo_config, &pwr_ctrl_handle);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to create a new on-chip LDO power control driver");
|
||||
return ret;
|
||||
}
|
||||
host.pwr_ctrl_handle = pwr_ctrl_handle;
|
||||
|
||||
const sdmmc_slot_config_t slot_config = {
|
||||
/* SD card is connected to Slot 0 pins. Slot 0 uses IO MUX, so not specifying the pins here */
|
||||
.cd = SDMMC_SLOT_NO_CD,
|
||||
.wp = SDMMC_SLOT_NO_WP,
|
||||
.width = 4,
|
||||
.flags = 0,
|
||||
};
|
||||
|
||||
return esp_vfs_fat_sdmmc_mount(BSP_SD_MOUNT_POINT, &host, &slot_config, &mount_config, &bsp_sdcard);
|
||||
}
|
||||
|
||||
esp_err_t bsp_sdcard_unmount(void)
|
||||
{
|
||||
return esp_vfs_fat_sdcard_unmount(BSP_SD_MOUNT_POINT, bsp_sdcard);
|
||||
}
|
||||
|
||||
esp_err_t bsp_spiffs_mount(void)
|
||||
{
|
||||
esp_vfs_spiffs_conf_t conf = {
|
||||
.base_path = CONFIG_BSP_SPIFFS_MOUNT_POINT,
|
||||
.partition_label = CONFIG_BSP_SPIFFS_PARTITION_LABEL,
|
||||
.max_files = CONFIG_BSP_SPIFFS_MAX_FILES,
|
||||
#ifdef CONFIG_BSP_SPIFFS_FORMAT_ON_MOUNT_FAIL
|
||||
.format_if_mount_failed = true,
|
||||
#else
|
||||
.format_if_mount_failed = false,
|
||||
#endif
|
||||
};
|
||||
|
||||
esp_err_t ret_val = esp_vfs_spiffs_register(&conf);
|
||||
|
||||
BSP_ERROR_CHECK_RETURN_ERR(ret_val);
|
||||
|
||||
size_t total = 0, used = 0;
|
||||
ret_val = esp_spiffs_info(conf.partition_label, &total, &used);
|
||||
if (ret_val != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to get SPIFFS partition information (%s)", esp_err_to_name(ret_val));
|
||||
} else {
|
||||
ESP_LOGI(TAG, "Partition size: total: %d, used: %d", total, used);
|
||||
}
|
||||
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
esp_err_t bsp_spiffs_unmount(void)
|
||||
{
|
||||
return esp_vfs_spiffs_unregister(CONFIG_BSP_SPIFFS_PARTITION_LABEL);
|
||||
}
|
||||
|
||||
esp_err_t bsp_audio_init(const i2s_std_config_t *i2s_config)
|
||||
{
|
||||
if (i2s_tx_chan && i2s_rx_chan) {
|
||||
/* Audio was initialized before */
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/* Setup I2S peripheral */
|
||||
i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(CONFIG_BSP_I2S_NUM, I2S_ROLE_MASTER);
|
||||
chan_cfg.auto_clear = true; // Auto clear the legacy data in the DMA buffer
|
||||
ESP_ERROR_CHECK(i2s_new_channel(&chan_cfg, &i2s_tx_chan, &i2s_rx_chan));
|
||||
|
||||
/* Setup I2S channels */
|
||||
const i2s_std_config_t std_cfg_default = BSP_I2S_DUPLEX_MONO_CFG(44100);
|
||||
const i2s_std_config_t *p_i2s_cfg = &std_cfg_default;
|
||||
if (i2s_config != NULL) {
|
||||
p_i2s_cfg = i2s_config;
|
||||
}
|
||||
|
||||
if (i2s_tx_chan != NULL) {
|
||||
ESP_ERROR_CHECK(i2s_channel_init_std_mode(i2s_tx_chan, p_i2s_cfg));
|
||||
ESP_ERROR_CHECK(i2s_channel_enable(i2s_tx_chan));
|
||||
}
|
||||
|
||||
if (i2s_rx_chan != NULL) {
|
||||
ESP_ERROR_CHECK(i2s_channel_init_std_mode(i2s_rx_chan, p_i2s_cfg));
|
||||
ESP_ERROR_CHECK(i2s_channel_enable(i2s_rx_chan));
|
||||
}
|
||||
|
||||
audio_codec_i2s_cfg_t i2s_cfg = {
|
||||
.port = CONFIG_BSP_I2S_NUM,
|
||||
.tx_handle = i2s_tx_chan,
|
||||
.rx_handle = i2s_rx_chan,
|
||||
};
|
||||
i2s_data_if = audio_codec_new_i2s_data(&i2s_cfg);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_codec_dev_handle_t bsp_audio_codec_speaker_init(void)
|
||||
{
|
||||
if (i2s_data_if == NULL) {
|
||||
/* Initilize I2C */
|
||||
ESP_ERROR_CHECK(bsp_i2c_init());
|
||||
/* Configure I2S peripheral and Power Amplifier */
|
||||
ESP_ERROR_CHECK(bsp_audio_init(NULL));
|
||||
}
|
||||
assert(i2s_data_if);
|
||||
|
||||
const audio_codec_gpio_if_t *gpio_if = audio_codec_new_gpio();
|
||||
|
||||
audio_codec_i2c_cfg_t i2c_cfg = {
|
||||
.port = BSP_I2C_NUM,
|
||||
.addr = ES8311_CODEC_DEFAULT_ADDR,
|
||||
.bus_handle = i2c_handle,
|
||||
};
|
||||
const audio_codec_ctrl_if_t *i2c_ctrl_if = audio_codec_new_i2c_ctrl(&i2c_cfg);
|
||||
assert(i2c_ctrl_if);
|
||||
|
||||
esp_codec_dev_hw_gain_t gain = {
|
||||
.pa_voltage = 5.0,
|
||||
.codec_dac_voltage = 3.3,
|
||||
};
|
||||
|
||||
es8311_codec_cfg_t es8311_cfg = {
|
||||
.ctrl_if = i2c_ctrl_if,
|
||||
.gpio_if = gpio_if,
|
||||
.codec_mode = ESP_CODEC_DEV_TYPE_OUT,
|
||||
.pa_pin = BSP_POWER_AMP_IO,
|
||||
.pa_reverted = false,
|
||||
.master_mode = false,
|
||||
.use_mclk = true,
|
||||
.digital_mic = false,
|
||||
.invert_mclk = false,
|
||||
.invert_sclk = false,
|
||||
.hw_gain = gain,
|
||||
};
|
||||
const audio_codec_if_t *es8311_dev = es8311_codec_new(&es8311_cfg);
|
||||
assert(es8311_dev);
|
||||
|
||||
esp_codec_dev_cfg_t codec_dev_cfg = {
|
||||
.dev_type = ESP_CODEC_DEV_TYPE_IN_OUT,
|
||||
.codec_if = es8311_dev,
|
||||
.data_if = i2s_data_if,
|
||||
};
|
||||
return esp_codec_dev_new(&codec_dev_cfg);
|
||||
}
|
||||
|
||||
esp_codec_dev_handle_t bsp_audio_codec_microphone_init(void)
|
||||
{
|
||||
if (i2s_data_if == NULL) {
|
||||
/* Initilize I2C */
|
||||
ESP_ERROR_CHECK(bsp_i2c_init());
|
||||
/* Configure I2S peripheral and Power Amplifier */
|
||||
ESP_ERROR_CHECK(bsp_audio_init(NULL));
|
||||
}
|
||||
assert(i2s_data_if);
|
||||
|
||||
const audio_codec_gpio_if_t *gpio_if = audio_codec_new_gpio();
|
||||
|
||||
audio_codec_i2c_cfg_t i2c_cfg = {
|
||||
.port = BSP_I2C_NUM,
|
||||
.addr = ES8311_CODEC_DEFAULT_ADDR,
|
||||
.bus_handle = i2c_handle,
|
||||
};
|
||||
const audio_codec_ctrl_if_t *i2c_ctrl_if = audio_codec_new_i2c_ctrl(&i2c_cfg);
|
||||
assert(i2c_ctrl_if);
|
||||
|
||||
esp_codec_dev_hw_gain_t gain = {
|
||||
.pa_voltage = 5.0,
|
||||
.codec_dac_voltage = 3.3,
|
||||
};
|
||||
|
||||
es8311_codec_cfg_t es8311_cfg = {
|
||||
.ctrl_if = i2c_ctrl_if,
|
||||
.gpio_if = gpio_if,
|
||||
.codec_mode = ESP_CODEC_DEV_WORK_MODE_BOTH,
|
||||
.pa_pin = BSP_POWER_AMP_IO,
|
||||
.pa_reverted = false,
|
||||
.master_mode = false,
|
||||
.use_mclk = true,
|
||||
.digital_mic = false,
|
||||
.invert_mclk = false,
|
||||
.invert_sclk = false,
|
||||
.hw_gain = gain,
|
||||
};
|
||||
|
||||
const audio_codec_if_t *es8311_dev = es8311_codec_new(&es8311_cfg);
|
||||
assert(es8311_dev);
|
||||
|
||||
esp_codec_dev_cfg_t codec_es8311_dev_cfg = {
|
||||
.dev_type = ESP_CODEC_DEV_TYPE_IN,
|
||||
.codec_if = es8311_dev,
|
||||
.data_if = i2s_data_if,
|
||||
};
|
||||
return esp_codec_dev_new(&codec_es8311_dev_cfg);
|
||||
}
|
||||
|
||||
// Bit number used to represent command and parameter
|
||||
#define LCD_LEDC_CH CONFIG_BSP_DISPLAY_BRIGHTNESS_LEDC_CH
|
||||
|
||||
esp_err_t bsp_display_brightness_init(void)
|
||||
{
|
||||
// Setup LEDC peripheral for PWM backlight control
|
||||
const ledc_channel_config_t LCD_backlight_channel = {
|
||||
.gpio_num = BSP_LCD_BACKLIGHT,
|
||||
.speed_mode = LEDC_LOW_SPEED_MODE,
|
||||
.channel = LCD_LEDC_CH,
|
||||
.intr_type = LEDC_INTR_DISABLE,
|
||||
.timer_sel = 1,
|
||||
.duty = 0,
|
||||
.hpoint = 0
|
||||
};
|
||||
const ledc_timer_config_t LCD_backlight_timer = {
|
||||
.speed_mode = LEDC_LOW_SPEED_MODE,
|
||||
.duty_resolution = LEDC_TIMER_10_BIT,
|
||||
.timer_num = 1,
|
||||
.freq_hz = 20000,
|
||||
.clk_cfg = LEDC_AUTO_CLK
|
||||
};
|
||||
|
||||
BSP_ERROR_CHECK_RETURN_ERR(ledc_timer_config(&LCD_backlight_timer));
|
||||
BSP_ERROR_CHECK_RETURN_ERR(ledc_channel_config(&LCD_backlight_channel));
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t bsp_display_brightness_set(int brightness_percent)
|
||||
{
|
||||
if (brightness_percent > 100) {
|
||||
brightness_percent = 100;
|
||||
}
|
||||
if (brightness_percent < 0) {
|
||||
brightness_percent = 0;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "Setting LCD backlight: %d%%", brightness_percent);
|
||||
uint32_t duty_cycle = (1023 * brightness_percent) / 100; // LEDC resolution set to 10bits, thus: 100% = 1023
|
||||
BSP_ERROR_CHECK_RETURN_ERR(ledc_set_duty(LEDC_LOW_SPEED_MODE, LCD_LEDC_CH, duty_cycle));
|
||||
BSP_ERROR_CHECK_RETURN_ERR(ledc_update_duty(LEDC_LOW_SPEED_MODE, LCD_LEDC_CH));
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t bsp_display_backlight_off(void)
|
||||
{
|
||||
return bsp_display_brightness_set(0);
|
||||
}
|
||||
|
||||
esp_err_t bsp_display_backlight_on(void)
|
||||
{
|
||||
return bsp_display_brightness_set(100);
|
||||
}
|
||||
|
||||
static esp_err_t bsp_enable_dsi_phy_power(void)
|
||||
{
|
||||
#if BSP_MIPI_DSI_PHY_PWR_LDO_CHAN > 0
|
||||
// Turn on the power for MIPI DSI PHY, so it can go from "No Power" state to "Shutdown" state
|
||||
static esp_ldo_channel_handle_t phy_pwr_chan = NULL;
|
||||
esp_ldo_channel_config_t ldo_cfg = {
|
||||
.chan_id = BSP_MIPI_DSI_PHY_PWR_LDO_CHAN,
|
||||
.voltage_mv = BSP_MIPI_DSI_PHY_PWR_LDO_VOLTAGE_MV,
|
||||
};
|
||||
ESP_RETURN_ON_ERROR(esp_ldo_acquire_channel(&ldo_cfg, &phy_pwr_chan), TAG, "Acquire LDO channel for DPHY failed");
|
||||
ESP_LOGI(TAG, "MIPI DSI PHY Powered on");
|
||||
#endif // BSP_MIPI_DSI_PHY_PWR_LDO_CHAN > 0
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t bsp_display_new(const bsp_display_config_t *config, esp_lcd_panel_handle_t *ret_panel, esp_lcd_panel_io_handle_t *ret_io)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
bsp_lcd_handles_t handles;
|
||||
ret = bsp_display_new_with_handles(config, &handles);
|
||||
|
||||
*ret_panel = handles.panel;
|
||||
*ret_io = handles.io;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// static const st7701_lcd_init_cmd_t lcd_cmd[] = {
|
||||
|
||||
// {0xFF, (uint8_t []){0x77,0x01,0x00,0x00,0x13},5,0},
|
||||
// {0xEF, (uint8_t []){0x08}, 1, 0},
|
||||
// {0xFF, (uint8_t []){0x77,0x01,0x00,0x00,0x10},5,0},
|
||||
// {0xC0, (uint8_t []){0x63, 0x00}, 2, 0},
|
||||
// {0xC1, (uint8_t []){0x0D, 0x02}, 2, 0},
|
||||
// {0xC2, (uint8_t []){0x10, 0x08}, 2, 0},
|
||||
// {0xCC, (uint8_t []){0x10}, 1, 0},
|
||||
|
||||
// {0xB0, (uint8_t []){0x80, 0x09, 0x53, 0x0C, 0xD0, 0x07, 0x0C, 0x09, 0x09, 0x28, 0x06, 0xD4, 0x13, 0x69, 0x2B, 0x71}, 16, 0},
|
||||
// {0xB1, (uint8_t []){0x80, 0x94, 0x5A, 0x10, 0xD3, 0x06, 0x0A, 0x08, 0x08, 0x25, 0x03, 0xD3, 0x12, 0x66, 0x6A, 0x0D}, 16, 0},
|
||||
// {0xFF, (uint8_t []){0x77, 0x01, 0x00, 0x00, 0x11}, 5, 0},
|
||||
|
||||
// {0xB0, (uint8_t []){0x5D}, 1, 0},
|
||||
// {0xB1, (uint8_t []){0x58}, 1, 0},
|
||||
// {0xB2, (uint8_t []){0x87}, 1, 0},
|
||||
// {0xB3, (uint8_t []){0x80}, 1, 0},
|
||||
// {0xB5, (uint8_t []){0x4E}, 1, 0},
|
||||
// {0xB7, (uint8_t []){0x85}, 1, 0},
|
||||
// {0xB8, (uint8_t []){0x21}, 1, 0},
|
||||
// {0xB9, (uint8_t []){0x10, 0x1F}, 2, 0},
|
||||
// {0xBB, (uint8_t []){0x03}, 1,0},
|
||||
// {0xBC, (uint8_t []){0x00}, 1,0},
|
||||
|
||||
// {0xC1, (uint8_t []){0x78}, 1, 0},
|
||||
// {0xC2, (uint8_t []){0x78}, 1, 0},
|
||||
// {0xD0, (uint8_t []){0x88}, 1, 0},
|
||||
|
||||
// {0xE0, (uint8_t []){0x00, 0x3A, 0x02}, 3, 0},
|
||||
// {0xE1, (uint8_t []){0x04, 0xA0, 0x00, 0xA0, 0x05,0xA0, 0x00, 0xA0, 0x00, 0x40, 0x40}, 11, 0},
|
||||
// {0xE2, (uint8_t []){0x30, 0x00, 0x40, 0x40, 0x32, 0xA0, 0x00, 0xA0, 0x00, 0xA0, 0x00, 0xA0, 0x00}, 13, 0},
|
||||
// {0xE3, (uint8_t []){0x00, 0x00, 0x33, 0x33}, 4, 0},
|
||||
// {0xE4, (uint8_t []){0x44, 0x44}, 2, 0},
|
||||
// {0xE5, (uint8_t []){0x09, 0x2E, 0xA0, 0xA0, 0x0B, 0x30, 0xA0, 0xA0, 0x05, 0x2A, 0xA0, 0xA0, 0x07, 0x2C, 0xA0, 0xA0}, 16, 0},
|
||||
// {0xE6, (uint8_t []){0x00, 0x00, 0x33, 0x33}, 4, 0},
|
||||
// {0xE7, (uint8_t []){0x44, 0x44}, 2, 0},
|
||||
// {0xE8, (uint8_t []){0x08, 0x2D, 0xA0, 0xA0, 0x0A, 0x2F, 0xA0, 0xA0, 0x04, 0x29, 0xA0, 0xA0, 0x06, 0x2B, 0xA0, 0xA0}, 16, 0},
|
||||
|
||||
// {0xEB, (uint8_t []){0x00, 0x00, 0x4E, 0x4E, 0x00, 0x00, 0x00}, 7, 0},
|
||||
// {0xEC, (uint8_t []){0x08, 0x01}, 2, 0},
|
||||
|
||||
// {0xED, (uint8_t []){0xB0, 0x2B, 0x98, 0xA4, 0x56, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF7, 0x65, 0x4A, 0x89, 0xB2, 0x0B}, 16, 0},
|
||||
// {0xEF, (uint8_t []){0x08, 0x08, 0x08, 0x45, 0x3F, 0x54}, 6, 0},
|
||||
// {0xFF, (uint8_t []){0x77, 0x01, 0x00, 0x00, 0x00}, 5, 0},
|
||||
|
||||
// {0x3A, (uint8_t []){0x66}, 1, 0},
|
||||
// {0x11, (uint8_t []){0x00}, 1, 120},
|
||||
// {0x29, (uint8_t []){0x00}, 1, 20},
|
||||
// };
|
||||
|
||||
esp_err_t bsp_display_new_with_handles(const bsp_display_config_t *config, bsp_lcd_handles_t *ret_handles)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
|
||||
ESP_RETURN_ON_ERROR(bsp_display_brightness_init(), TAG, "Brightness init failed");
|
||||
ESP_RETURN_ON_ERROR(bsp_enable_dsi_phy_power(), TAG, "DSI PHY power failed");
|
||||
|
||||
/* create MIPI DSI bus first, it will initialize the DSI PHY as well */
|
||||
esp_lcd_dsi_bus_handle_t mipi_dsi_bus;
|
||||
esp_lcd_dsi_bus_config_t bus_config = {
|
||||
.bus_id = 0,
|
||||
.num_data_lanes = BSP_LCD_MIPI_DSI_LANE_NUM,
|
||||
.phy_clk_src = MIPI_DSI_PHY_CLK_SRC_DEFAULT,
|
||||
.lane_bit_rate_mbps = 750,
|
||||
};
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_new_dsi_bus(&bus_config, &mipi_dsi_bus), TAG, "New DSI bus init failed");
|
||||
|
||||
ESP_LOGI(TAG, "Install MIPI DSI LCD control panel");
|
||||
// we use DBI interface to send LCD commands and parameters
|
||||
esp_lcd_panel_io_handle_t io;
|
||||
esp_lcd_dbi_io_config_t dbi_config = {
|
||||
.virtual_channel = 0,
|
||||
.lcd_cmd_bits = 8, // according to the LCD ILI9881C spec
|
||||
.lcd_param_bits = 8, // according to the LCD ILI9881C spec
|
||||
};
|
||||
ESP_GOTO_ON_ERROR(esp_lcd_new_panel_io_dbi(mipi_dsi_bus, &dbi_config, &io), err, TAG, "New panel IO failed");
|
||||
|
||||
esp_lcd_panel_handle_t disp_panel = NULL;
|
||||
#if CONFIG_BSP_LCD_TYPE_1024_600
|
||||
// create EK79007 control panel
|
||||
ESP_LOGI(TAG, "Install EK79007 LCD control panel");
|
||||
|
||||
#if CONFIG_BSP_LCD_COLOR_FORMAT_RGB888
|
||||
esp_lcd_dpi_panel_config_t dpi_config = ST7701_480_360_PANEL_60HZ_DPI_CONFIG(LCD_COLOR_PIXEL_FORMAT_RGB888);
|
||||
#else
|
||||
esp_lcd_dpi_panel_config_t dpi_config = ST7701_480_360_PANEL_60HZ_DPI_CONFIG(LCD_COLOR_PIXEL_FORMAT_RGB565);
|
||||
#endif
|
||||
dpi_config.num_fbs = CONFIG_BSP_LCD_DPI_BUFFER_NUMS;
|
||||
|
||||
st7701_vendor_config_t vendor_config = {
|
||||
// .init_cmds = lcd_cmd,
|
||||
// .init_cmds_size = sizeof(lcd_cmd) / sizeof(st7701_lcd_init_cmd_t),
|
||||
.mipi_config = {
|
||||
.dsi_bus = mipi_dsi_bus,
|
||||
.dpi_config = &dpi_config,
|
||||
},
|
||||
.flags = {
|
||||
.use_mipi_interface = 1,
|
||||
}
|
||||
};
|
||||
esp_lcd_panel_dev_config_t lcd_dev_config = {
|
||||
.bits_per_pixel = 16,
|
||||
.rgb_ele_order = BSP_LCD_COLOR_SPACE,
|
||||
.reset_gpio_num = BSP_LCD_RST,
|
||||
.vendor_config = &vendor_config,
|
||||
};
|
||||
ESP_GOTO_ON_ERROR(esp_lcd_new_panel_st7701(io, &lcd_dev_config, &disp_panel), err, TAG, "New LCD panel ST7701 failed");
|
||||
ESP_GOTO_ON_ERROR(esp_lcd_panel_reset(disp_panel), err, TAG, "LCD panel reset failed");
|
||||
ESP_GOTO_ON_ERROR(esp_lcd_panel_init(disp_panel), err, TAG, "LCD panel init failed");
|
||||
// ESP_GOTO_ON_ERROR(esp_lcd_panel_disp_on_off(disp_panel, true), err, TAG, "LCD panel ON failed");
|
||||
// ESP_GOTO_ON_ERROR(esp_lcd_panel_disp_sleep(disp_panel, false), err, TAG, "LCD Sleep ON failed");
|
||||
|
||||
#else
|
||||
// create ILI9881C control panel
|
||||
ESP_LOGI(TAG, "Install ILI9881C LCD control panel");
|
||||
#if CONFIG_BSP_LCD_COLOR_FORMAT_RGB888
|
||||
esp_lcd_dpi_panel_config_t dpi_config = ILI9881C_800_1280_PANEL_60HZ_DPI_CONFIG(LCD_COLOR_PIXEL_FORMAT_RGB888);
|
||||
#else
|
||||
esp_lcd_dpi_panel_config_t dpi_config = ILI9881C_800_1280_PANEL_60HZ_DPI_CONFIG(LCD_COLOR_PIXEL_FORMAT_RGB565);
|
||||
#endif
|
||||
dpi_config.num_fbs = CONFIG_BSP_LCD_DPI_BUFFER_NUMS;
|
||||
|
||||
ili9881c_vendor_config_t vendor_config = {
|
||||
.mipi_config = {
|
||||
.dsi_bus = mipi_dsi_bus,
|
||||
.dpi_config = &dpi_config,
|
||||
.lane_num = BSP_LCD_MIPI_DSI_LANE_NUM,
|
||||
},
|
||||
};
|
||||
const esp_lcd_panel_dev_config_t lcd_dev_config = {
|
||||
.reset_gpio_num = BSP_LCD_RST,
|
||||
.rgb_ele_order = BSP_LCD_COLOR_SPACE,
|
||||
.bits_per_pixel = 16,
|
||||
.vendor_config = &vendor_config,
|
||||
};
|
||||
ESP_GOTO_ON_ERROR(esp_lcd_new_panel_ili9881c(io, &lcd_dev_config, &disp_panel), err, TAG, "New LCD panel ILI9881C failed");
|
||||
ESP_GOTO_ON_ERROR(esp_lcd_panel_reset(disp_panel), err, TAG, "LCD panel reset failed");
|
||||
ESP_GOTO_ON_ERROR(esp_lcd_panel_init(disp_panel), err, TAG, "LCD panel init failed");
|
||||
ESP_GOTO_ON_ERROR(esp_lcd_panel_disp_on_off(disp_panel, true), err, TAG, "LCD panel ON failed");
|
||||
#endif
|
||||
|
||||
/* Return all handles */
|
||||
ret_handles->io = io;
|
||||
ret_handles->mipi_dsi_bus = mipi_dsi_bus;
|
||||
ret_handles->panel = disp_panel;
|
||||
ret_handles->control = NULL;
|
||||
|
||||
ESP_LOGI(TAG, "Display initialized");
|
||||
|
||||
return ret;
|
||||
|
||||
err:
|
||||
if (disp_panel) {
|
||||
esp_lcd_panel_del(disp_panel);
|
||||
}
|
||||
if (io) {
|
||||
esp_lcd_panel_io_del(io);
|
||||
}
|
||||
if (mipi_dsi_bus) {
|
||||
esp_lcd_del_dsi_bus(mipi_dsi_bus);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t bsp_touch_new(const bsp_touch_config_t *config, esp_lcd_touch_handle_t *ret_touch)
|
||||
{
|
||||
/* Initilize I2C */
|
||||
BSP_ERROR_CHECK_RETURN_ERR(bsp_i2c_init());
|
||||
|
||||
/* Initialize touch */
|
||||
const esp_lcd_touch_config_t tp_cfg = {
|
||||
.x_max = BSP_LCD_H_RES,
|
||||
.y_max = BSP_LCD_V_RES,
|
||||
.rst_gpio_num = BSP_LCD_TOUCH_RST, // Shared with LCD reset
|
||||
.int_gpio_num = BSP_LCD_TOUCH_INT,
|
||||
.levels = {
|
||||
.reset = 0,
|
||||
.interrupt = 0,
|
||||
},
|
||||
.flags = {
|
||||
.swap_xy = 0,
|
||||
#if CONFIG_BSP_LCD_TYPE_1024_600
|
||||
.mirror_x = 0,
|
||||
.mirror_y = 0,
|
||||
#else
|
||||
.mirror_x = 0,
|
||||
.mirror_y = 0,
|
||||
#endif
|
||||
},
|
||||
};
|
||||
esp_lcd_panel_io_handle_t tp_io_handle = NULL;
|
||||
esp_lcd_panel_io_i2c_config_t tp_io_config = ESP_LCD_TOUCH_IO_I2C_GT911_CONFIG();
|
||||
tp_io_config.scl_speed_hz = CONFIG_BSP_I2C_CLK_SPEED_HZ;
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_new_panel_io_i2c(i2c_handle, &tp_io_config, &tp_io_handle), TAG, "");
|
||||
return esp_lcd_touch_new_i2c_gt911(tp_io_handle, &tp_cfg, ret_touch);
|
||||
}
|
||||
|
||||
#if (BSP_CONFIG_NO_GRAPHIC_LIB == 0)
|
||||
static lv_display_t *bsp_display_lcd_init(const bsp_display_cfg_t *cfg)
|
||||
{
|
||||
assert(cfg != NULL);
|
||||
bsp_lcd_handles_t lcd_panels;
|
||||
BSP_ERROR_CHECK_RETURN_NULL(bsp_display_new_with_handles(NULL, &lcd_panels));
|
||||
|
||||
/* Add LCD screen */
|
||||
ESP_LOGD(TAG, "Add LCD screen");
|
||||
const lvgl_port_display_cfg_t disp_cfg = {
|
||||
.io_handle = lcd_panels.io,
|
||||
.panel_handle = lcd_panels.panel,
|
||||
.control_handle = lcd_panels.control,
|
||||
.buffer_size = cfg->buffer_size,
|
||||
.double_buffer = cfg->double_buffer,
|
||||
.hres = BSP_LCD_H_RES,
|
||||
.vres = BSP_LCD_V_RES,
|
||||
.monochrome = false,
|
||||
/* Rotation values must be same as used in esp_lcd for initial settings of the screen */
|
||||
.rotation = {
|
||||
.swap_xy = true,
|
||||
.mirror_x = false,
|
||||
.mirror_y = false,
|
||||
},
|
||||
#if LVGL_VERSION_MAJOR >= 9
|
||||
#if CONFIG_BSP_LCD_COLOR_FORMAT_RGB888
|
||||
.color_format = LV_COLOR_FORMAT_RGB888,
|
||||
#else
|
||||
.color_format = LV_COLOR_FORMAT_RGB565,
|
||||
#endif
|
||||
#endif
|
||||
.flags = {
|
||||
.buff_dma = cfg->flags.buff_dma,
|
||||
.buff_spiram = cfg->flags.buff_spiram,
|
||||
#if LVGL_VERSION_MAJOR >= 9
|
||||
.swap_bytes = (BSP_LCD_BIGENDIAN ? true : false),
|
||||
#endif
|
||||
#if CONFIG_BSP_DISPLAY_LVGL_AVOID_TEAR
|
||||
.sw_rotate = false, /* Avoid tearing is not supported for SW rotation */
|
||||
#else
|
||||
.sw_rotate = cfg->flags.sw_rotate, /* Only SW rotation is supported for 90° and 270° */
|
||||
#endif
|
||||
#if CONFIG_BSP_DISPLAY_LVGL_FULL_REFRESH
|
||||
.full_refresh = true,
|
||||
#elif CONFIG_BSP_DISPLAY_LVGL_DIRECT_MODE
|
||||
.direct_mode = true,
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
const lvgl_port_display_dsi_cfg_t dpi_cfg = {
|
||||
.flags = {
|
||||
#if CONFIG_BSP_DISPLAY_LVGL_AVOID_TEAR
|
||||
.avoid_tearing = true,
|
||||
#else
|
||||
.avoid_tearing = false,
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
return lvgl_port_add_disp_dsi(&disp_cfg, &dpi_cfg);
|
||||
}
|
||||
|
||||
static lv_indev_t *bsp_display_indev_init(lv_display_t *disp)
|
||||
{
|
||||
esp_lcd_touch_handle_t tp;
|
||||
BSP_ERROR_CHECK_RETURN_NULL(bsp_touch_new(NULL, &tp));
|
||||
assert(tp);
|
||||
|
||||
/* Add touch input (for selected screen) */
|
||||
const lvgl_port_touch_cfg_t touch_cfg = {
|
||||
.disp = disp,
|
||||
.handle = tp,
|
||||
};
|
||||
|
||||
return lvgl_port_add_touch(&touch_cfg);
|
||||
}
|
||||
|
||||
lv_display_t *bsp_display_start(void)
|
||||
{
|
||||
bsp_display_cfg_t cfg = {
|
||||
.lvgl_port_cfg = ESP_LVGL_PORT_INIT_CONFIG(),
|
||||
.buffer_size = BSP_LCD_DRAW_BUFF_SIZE,
|
||||
.double_buffer = BSP_LCD_DRAW_BUFF_DOUBLE,
|
||||
.flags = {
|
||||
#if CONFIG_BSP_LCD_COLOR_FORMAT_RGB888
|
||||
.buff_dma = false,
|
||||
#else
|
||||
.buff_dma = true,
|
||||
#endif
|
||||
.buff_spiram = false,
|
||||
.sw_rotate = true,
|
||||
}
|
||||
};
|
||||
return bsp_display_start_with_config(&cfg);
|
||||
}
|
||||
|
||||
lv_display_t *bsp_display_start_with_config(const bsp_display_cfg_t *cfg)
|
||||
{
|
||||
lv_display_t *disp;
|
||||
|
||||
assert(cfg != NULL);
|
||||
BSP_ERROR_CHECK_RETURN_NULL(lvgl_port_init(&cfg->lvgl_port_cfg));
|
||||
|
||||
BSP_ERROR_CHECK_RETURN_NULL(bsp_display_brightness_init());
|
||||
|
||||
BSP_NULL_CHECK(disp = bsp_display_lcd_init(cfg), NULL);
|
||||
// bsp_display_lock(-1);
|
||||
// lv_disp_set_rotation(disp,LV_DISPLAY_ROTATION_90);
|
||||
// bsp_display_unlock();
|
||||
|
||||
BSP_NULL_CHECK(disp_indev = bsp_display_indev_init(disp), NULL);
|
||||
|
||||
return disp;
|
||||
}
|
||||
|
||||
lv_indev_t *bsp_display_get_input_dev(void)
|
||||
{
|
||||
return disp_indev;
|
||||
}
|
||||
|
||||
void bsp_display_rotate(lv_display_t *disp, lv_disp_rotation_t rotation)
|
||||
{
|
||||
lv_disp_set_rotation(disp, rotation);
|
||||
}
|
||||
|
||||
bool bsp_display_lock(uint32_t timeout_ms)
|
||||
{
|
||||
return lvgl_port_lock(timeout_ms);
|
||||
}
|
||||
|
||||
void bsp_display_unlock(void)
|
||||
{
|
||||
lvgl_port_unlock();
|
||||
}
|
||||
|
||||
#endif // (BSP_CONFIG_NO_GRAPHIC_LIB == 0)
|
||||
|
||||
static void usb_lib_task(void *arg)
|
||||
{
|
||||
while (1) {
|
||||
// Start handling system events
|
||||
uint32_t event_flags;
|
||||
usb_host_lib_handle_events(portMAX_DELAY, &event_flags);
|
||||
if (event_flags & USB_HOST_LIB_EVENT_FLAGS_NO_CLIENTS) {
|
||||
ESP_ERROR_CHECK(usb_host_device_free_all());
|
||||
}
|
||||
if (event_flags & USB_HOST_LIB_EVENT_FLAGS_ALL_FREE) {
|
||||
ESP_LOGI(TAG, "USB: All devices freed");
|
||||
// Continue handling USB events to allow device reconnection
|
||||
// The only way this task can be stopped is by calling bsp_usb_host_stop()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
esp_err_t bsp_usb_host_start(bsp_usb_host_power_mode_t mode, bool limit_500mA)
|
||||
{
|
||||
//Install USB Host driver. Should only be called once in entire application
|
||||
ESP_LOGI(TAG, "Installing USB Host");
|
||||
const usb_host_config_t host_config = {
|
||||
.skip_phy_setup = false,
|
||||
.intr_flags = ESP_INTR_FLAG_LEVEL1,
|
||||
};
|
||||
BSP_ERROR_CHECK_RETURN_ERR(usb_host_install(&host_config));
|
||||
|
||||
// Create a task that will handle USB library events
|
||||
if (xTaskCreate(usb_lib_task, "usb_lib", 4096, NULL, 10, &usb_host_task) != pdTRUE) {
|
||||
ESP_LOGE(TAG, "Creating USB host lib task failed");
|
||||
abort();
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t bsp_usb_host_stop(void)
|
||||
{
|
||||
usb_host_uninstall();
|
||||
if (usb_host_task) {
|
||||
vTaskSuspend(usb_host_task);
|
||||
vTaskDelete(usb_host_task);
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
dependencies:
|
||||
esp_codec_dev:
|
||||
public: true
|
||||
version: 1.2.*
|
||||
esp_lcd_ek79007: 1.*
|
||||
esp_lcd_ili9881c: 1.*
|
||||
esp_lcd_touch_gt911: ^1
|
||||
esp_lcd_st7701:
|
||||
path: ../espressif__esp_lcd_st7701
|
||||
espressif/esp_lvgl_port:
|
||||
public: true
|
||||
version: ^2
|
||||
idf: '>=5.3'
|
||||
lvgl/lvgl: '>=8,<10'
|
||||
description: Board Support Package (BSP) for ESP32-P4 Function EV Board (preview)
|
||||
repository: git://github.com/espressif/esp-bsp.git
|
||||
repository_info:
|
||||
commit_sha: 0064ab931c38699fc993ee161713ac1d1c0943a5
|
||||
path: bsp/esp32_p4_function_ev_board
|
||||
tags:
|
||||
- bsp
|
||||
targets:
|
||||
- esp32p4
|
||||
url: https://github.com/espressif/esp-bsp/tree/master/bsp/esp32_p4_function_ev_board
|
||||
version: 4.1.1
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/**************************************************************************************************
|
||||
* BSP configuration
|
||||
**************************************************************************************************/
|
||||
// By default, this BSP is shipped with LVGL graphical library. Enabling this option will exclude it.
|
||||
// If you want to use BSP without LVGL, select BSP version with 'noglib' suffix.
|
||||
#if !defined(BSP_CONFIG_NO_GRAPHIC_LIB) // Check if the symbol is not coming from compiler definitions (-D...)
|
||||
#define BSP_CONFIG_NO_GRAPHIC_LIB (0)
|
||||
#endif
|
||||
+192
@@ -0,0 +1,192 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* @brief BSP LCD
|
||||
*
|
||||
* This file offers API for basic LCD control.
|
||||
* It is useful for users who want to use the LCD without the default Graphical Library LVGL.
|
||||
*
|
||||
* For standard LCD initialization with LVGL graphical library, you can call all-in-one function bsp_display_start().
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "esp_lcd_types.h"
|
||||
#include "esp_lcd_mipi_dsi.h"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
/* LCD color formats */
|
||||
#define ESP_LCD_COLOR_FORMAT_RGB565 (1)
|
||||
#define ESP_LCD_COLOR_FORMAT_RGB888 (2)
|
||||
|
||||
/* LCD display color format */
|
||||
#if CONFIG_BSP_LCD_COLOR_FORMAT_RGB888
|
||||
#define BSP_LCD_COLOR_FORMAT (ESP_LCD_COLOR_FORMAT_RGB888)
|
||||
#else
|
||||
#define BSP_LCD_COLOR_FORMAT (ESP_LCD_COLOR_FORMAT_RGB565)
|
||||
#endif
|
||||
/* LCD display color bytes endianess */
|
||||
#define BSP_LCD_BIGENDIAN (0)
|
||||
/* LCD display color bits */
|
||||
#define BSP_LCD_BITS_PER_PIXEL (16)
|
||||
/* LCD display color space */
|
||||
#define BSP_LCD_COLOR_SPACE (ESP_LCD_COLOR_SPACE_RGB)
|
||||
|
||||
#if CONFIG_BSP_LCD_TYPE_1024_600
|
||||
/* LCD display definition 1024x600 */
|
||||
#define BSP_LCD_H_RES (480)
|
||||
#define BSP_LCD_V_RES (800)
|
||||
|
||||
#define BSP_LCD_MIPI_DSI_LCD_HSYNC (1344)
|
||||
#define BSP_LCD_MIPI_DSI_LCD_HBP (160)
|
||||
#define BSP_LCD_MIPI_DSI_LCD_HFP (160)
|
||||
#define BSP_LCD_MIPI_DSI_LCD_VSYNC (635)
|
||||
#define BSP_LCD_MIPI_DSI_LCD_VBP (23)
|
||||
#define BSP_LCD_MIPI_DSI_LCD_VFP (12)
|
||||
#else
|
||||
/* LCD display definition 1280x800 */
|
||||
#define BSP_LCD_H_RES (800)
|
||||
#define BSP_LCD_V_RES (1280)
|
||||
|
||||
#define BSP_LCD_MIPI_DSI_LCD_HSYNC (40)
|
||||
#define BSP_LCD_MIPI_DSI_LCD_HBP (140)
|
||||
#define BSP_LCD_MIPI_DSI_LCD_HFP (40)
|
||||
#define BSP_LCD_MIPI_DSI_LCD_VSYNC (4)
|
||||
#define BSP_LCD_MIPI_DSI_LCD_VBP (16)
|
||||
#define BSP_LCD_MIPI_DSI_LCD_VFP (16)
|
||||
#endif
|
||||
|
||||
#define BSP_LCD_MIPI_DSI_LANE_NUM (2) // 2 data lanes
|
||||
#define BSP_LCD_MIPI_DSI_LANE_BITRATE_MBPS (1000) // 1Gbps
|
||||
|
||||
#define BSP_MIPI_DSI_PHY_PWR_LDO_CHAN (3) // LDO_VO3 is connected to VDD_MIPI_DPHY
|
||||
#define BSP_MIPI_DSI_PHY_PWR_LDO_VOLTAGE_MV (2500)
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief BSP display configuration structure
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
int dummy;
|
||||
} bsp_display_config_t;
|
||||
|
||||
/**
|
||||
* @brief BSP display return handles
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
esp_lcd_dsi_bus_handle_t mipi_dsi_bus; /*!< MIPI DSI bus handle */
|
||||
esp_lcd_panel_io_handle_t io; /*!< ESP LCD IO handle */
|
||||
esp_lcd_panel_handle_t panel; /*!< ESP LCD panel (color) handle */
|
||||
esp_lcd_panel_handle_t control; /*!< ESP LCD panel (control) handle */
|
||||
} bsp_lcd_handles_t;
|
||||
|
||||
/**
|
||||
* @brief Create new display panel
|
||||
*
|
||||
* For maximum flexibility, this function performs only reset and initialization of the display.
|
||||
* You must turn on the display explicitly by calling esp_lcd_panel_disp_on_off().
|
||||
* The display's backlight is not turned on either. You can use bsp_display_backlight_on/off(),
|
||||
* bsp_display_brightness_set() (on supported boards) or implement your own backlight control.
|
||||
*
|
||||
* If you want to free resources allocated by this function, you can use esp_lcd API, ie.:
|
||||
*
|
||||
* \code{.c}
|
||||
* esp_lcd_panel_del(panel);
|
||||
* esp_lcd_panel_io_del(io);
|
||||
* esp_lcd_del_dsi_bus(mipi_dsi_bus);
|
||||
* \endcode
|
||||
*
|
||||
* @param[in] config display configuration
|
||||
* @param[out] ret_panel esp_lcd panel handle
|
||||
* @param[out] ret_io esp_lcd IO handle
|
||||
* @return
|
||||
* - ESP_OK On success
|
||||
* - Else esp_lcd failure
|
||||
*/
|
||||
esp_err_t bsp_display_new(const bsp_display_config_t *config, esp_lcd_panel_handle_t *ret_panel, esp_lcd_panel_io_handle_t *ret_io);
|
||||
|
||||
/**
|
||||
* @brief Create new display panel
|
||||
*
|
||||
* For maximum flexibility, this function performs only reset and initialization of the display.
|
||||
* You must turn on the display explicitly by calling esp_lcd_panel_disp_on_off().
|
||||
* The display's backlight is not turned on either. You can use bsp_display_backlight_on/off(),
|
||||
* bsp_display_brightness_set() (on supported boards) or implement your own backlight control.
|
||||
*
|
||||
* If you want to free resources allocated by this function, you can use esp_lcd API, ie.:
|
||||
*
|
||||
* \code{.c}
|
||||
* esp_lcd_panel_del(panel);
|
||||
* esp_lcd_panel_del(control);
|
||||
* esp_lcd_panel_io_del(io);
|
||||
* esp_lcd_del_dsi_bus(mipi_dsi_bus);
|
||||
* \endcode
|
||||
*
|
||||
* @param[in] config display configuration
|
||||
* @param[out] ret_handles all esp_lcd handles in one structure
|
||||
* @return
|
||||
* - ESP_OK On success
|
||||
* - Else esp_lcd failure
|
||||
*/
|
||||
esp_err_t bsp_display_new_with_handles(const bsp_display_config_t *config, bsp_lcd_handles_t *ret_handles);
|
||||
|
||||
/**
|
||||
* @brief Initialize display's brightness
|
||||
*
|
||||
* Brightness is controlled with PWM signal to a pin controlling backlight.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK On success
|
||||
* - ESP_ERR_INVALID_ARG Parameter error
|
||||
*/
|
||||
esp_err_t bsp_display_brightness_init(void);
|
||||
|
||||
/**
|
||||
* @brief Set display's brightness
|
||||
*
|
||||
* Brightness is controlled with PWM signal to a pin controlling backlight.
|
||||
* Brightness must be already initialized by calling bsp_display_brightness_init() or bsp_display_new()
|
||||
*
|
||||
* @param[in] brightness_percent Brightness in [%]
|
||||
* @return
|
||||
* - ESP_OK On success
|
||||
* - ESP_ERR_INVALID_ARG Parameter error
|
||||
*/
|
||||
esp_err_t bsp_display_brightness_set(int brightness_percent);
|
||||
|
||||
/**
|
||||
* @brief Turn on display backlight
|
||||
*
|
||||
* Brightness is controlled with PWM signal to a pin controlling backlight.
|
||||
* Brightness must be already initialized by calling bsp_display_brightness_init() or bsp_display_new()
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK On success
|
||||
* - ESP_ERR_INVALID_ARG Parameter error
|
||||
*/
|
||||
esp_err_t bsp_display_backlight_on(void);
|
||||
|
||||
/**
|
||||
* @brief Turn off display backlight
|
||||
*
|
||||
* Brightness is controlled with PWM signal to a pin controlling backlight.
|
||||
* Brightness must be already initialized by calling bsp_display_brightness_init() or bsp_display_new()
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK On success
|
||||
* - ESP_ERR_INVALID_ARG Parameter error
|
||||
*/
|
||||
esp_err_t bsp_display_backlight_off(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "bsp/esp32_p4_function_ev_board.h"
|
||||
+382
@@ -0,0 +1,382 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* @brief ESP BSP: ESP32-P4 Function EV Board
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "sdkconfig.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "driver/i2c_master.h"
|
||||
#include "driver/sdmmc_host.h"
|
||||
#include "driver/i2s_std.h"
|
||||
#include "bsp/config.h"
|
||||
#include "bsp/display.h"
|
||||
#include "esp_codec_dev.h"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#if (BSP_CONFIG_NO_GRAPHIC_LIB == 0)
|
||||
#include "lvgl.h"
|
||||
#include "esp_lvgl_port.h"
|
||||
#endif // BSP_CONFIG_NO_GRAPHIC_LIB == 0
|
||||
|
||||
/**************************************************************************************************
|
||||
* BSP Capabilities
|
||||
**************************************************************************************************/
|
||||
|
||||
#define BSP_CAPS_DISPLAY 1
|
||||
#define BSP_CAPS_TOUCH 1
|
||||
#define BSP_CAPS_BUTTONS 0
|
||||
#define BSP_CAPS_AUDIO 1
|
||||
#define BSP_CAPS_AUDIO_SPEAKER 1
|
||||
#define BSP_CAPS_AUDIO_MIC 1
|
||||
#define BSP_CAPS_SDCARD 1
|
||||
#define BSP_CAPS_IMU 0
|
||||
|
||||
/**************************************************************************************************
|
||||
* ESP-BOX pinout
|
||||
**************************************************************************************************/
|
||||
/* I2C */
|
||||
#define BSP_I2C_SCL (GPIO_NUM_8)
|
||||
#define BSP_I2C_SDA (GPIO_NUM_7)
|
||||
|
||||
/* Audio */
|
||||
#define BSP_I2S_SCLK (GPIO_NUM_12)
|
||||
#define BSP_I2S_MCLK (GPIO_NUM_13)
|
||||
#define BSP_I2S_LCLK (GPIO_NUM_10)
|
||||
#define BSP_I2S_DOUT (GPIO_NUM_9)
|
||||
#define BSP_I2S_DSIN (GPIO_NUM_48)
|
||||
#define BSP_POWER_AMP_IO (GPIO_NUM_11)
|
||||
|
||||
/* Display */
|
||||
#if CONFIG_BSP_LCD_TYPE_1024_600
|
||||
#define BSP_LCD_BACKLIGHT (GPIO_NUM_23)
|
||||
#define BSP_LCD_RST (GPIO_NUM_5)
|
||||
#define BSP_LCD_TOUCH_RST (GPIO_NUM_NC)
|
||||
#define BSP_LCD_TOUCH_INT (GPIO_NUM_NC)
|
||||
#else
|
||||
#define BSP_LCD_BACKLIGHT (GPIO_NUM_23)
|
||||
#define BSP_LCD_RST (GPIO_NUM_NC)
|
||||
#define BSP_LCD_TOUCH_RST (GPIO_NUM_NC)
|
||||
#define BSP_LCD_TOUCH_INT (GPIO_NUM_NC)
|
||||
#endif
|
||||
|
||||
/* uSD card */
|
||||
#define BSP_SD_D0 (GPIO_NUM_39)
|
||||
#define BSP_SD_D1 (GPIO_NUM_40)
|
||||
#define BSP_SD_D2 (GPIO_NUM_41)
|
||||
#define BSP_SD_D3 (GPIO_NUM_42)
|
||||
#define BSP_SD_CMD (GPIO_NUM_44)
|
||||
#define BSP_SD_CLK (GPIO_NUM_43)
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**************************************************************************************************
|
||||
*
|
||||
* I2C interface
|
||||
*
|
||||
* There are multiple devices connected to I2C peripheral:
|
||||
* - Codec ES8311 (configuration only)
|
||||
* - LCD Touch controller
|
||||
**************************************************************************************************/
|
||||
#define BSP_I2C_NUM CONFIG_BSP_I2C_NUM
|
||||
|
||||
/**
|
||||
* @brief Init I2C driver
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK On success
|
||||
* - ESP_ERR_INVALID_ARG I2C parameter error
|
||||
* - ESP_FAIL I2C driver installation error
|
||||
*
|
||||
*/
|
||||
esp_err_t bsp_i2c_init(void);
|
||||
|
||||
/**
|
||||
* @brief Deinit I2C driver and free its resources
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK On success
|
||||
* - ESP_ERR_INVALID_ARG I2C parameter error
|
||||
*
|
||||
*/
|
||||
esp_err_t bsp_i2c_deinit(void);
|
||||
|
||||
/**
|
||||
* @brief Get I2C driver handle
|
||||
*
|
||||
* @return
|
||||
* - I2C handle
|
||||
*
|
||||
*/
|
||||
i2c_master_bus_handle_t bsp_i2c_get_handle(void);
|
||||
|
||||
/**************************************************************************************************
|
||||
*
|
||||
* I2S audio interface
|
||||
*
|
||||
* There are two devices connected to the I2S peripheral:
|
||||
* - Codec ES8311 for output(playback) and input(recording) path
|
||||
*
|
||||
* For speaker initialization use bsp_audio_codec_speaker_init() which is inside initialize I2S with bsp_audio_init().
|
||||
* For microphone initialization use bsp_audio_codec_microphone_init() which is inside initialize I2S with bsp_audio_init().
|
||||
* After speaker or microphone initialization, use functions from esp_codec_dev for play/record audio.
|
||||
* Example audio play:
|
||||
* \code{.c}
|
||||
* esp_codec_dev_set_out_vol(spk_codec_dev, DEFAULT_VOLUME);
|
||||
* esp_codec_dev_open(spk_codec_dev, &fs);
|
||||
* esp_codec_dev_write(spk_codec_dev, wav_bytes, bytes_read_from_spiffs);
|
||||
* esp_codec_dev_close(spk_codec_dev);
|
||||
* \endcode
|
||||
**************************************************************************************************/
|
||||
|
||||
/**
|
||||
* @brief Init audio
|
||||
*
|
||||
* @note There is no deinit audio function. Users can free audio resources by calling i2s_del_channel()
|
||||
* @warning The type of i2s_config param is depending on IDF version.
|
||||
* @param[in] i2s_config I2S configuration. Pass NULL to use default values (Mono, duplex, 16bit, 22050 Hz)
|
||||
* @return
|
||||
* - ESP_OK On success
|
||||
* - ESP_ERR_NOT_SUPPORTED The communication mode is not supported on the current chip
|
||||
* - ESP_ERR_INVALID_ARG NULL pointer or invalid configuration
|
||||
* - ESP_ERR_NOT_FOUND No available I2S channel found
|
||||
* - ESP_ERR_NO_MEM No memory for storing the channel information
|
||||
* - ESP_ERR_INVALID_STATE This channel has not initialized or already started
|
||||
*/
|
||||
esp_err_t bsp_audio_init(const i2s_std_config_t *i2s_config);
|
||||
|
||||
/**
|
||||
* @brief Initialize speaker codec device
|
||||
*
|
||||
* @return Pointer to codec device handle or NULL when error occurred
|
||||
*/
|
||||
esp_codec_dev_handle_t bsp_audio_codec_speaker_init(void);
|
||||
|
||||
/**
|
||||
* @brief Initialize microphone codec device
|
||||
*
|
||||
* @return Pointer to codec device handle or NULL when error occurred
|
||||
*/
|
||||
esp_codec_dev_handle_t bsp_audio_codec_microphone_init(void);
|
||||
|
||||
/**************************************************************************************************
|
||||
*
|
||||
* SPIFFS
|
||||
*
|
||||
* After mounting the SPIFFS, it can be accessed with stdio functions ie.:
|
||||
* \code{.c}
|
||||
* FILE* f = fopen(BSP_SPIFFS_MOUNT_POINT"/hello.txt", "w");
|
||||
* fprintf(f, "Hello World!\n");
|
||||
* fclose(f);
|
||||
* \endcode
|
||||
**************************************************************************************************/
|
||||
#define BSP_SPIFFS_MOUNT_POINT CONFIG_BSP_SPIFFS_MOUNT_POINT
|
||||
|
||||
/**
|
||||
* @brief Mount SPIFFS to virtual file system
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_INVALID_STATE if esp_vfs_spiffs_register was already called
|
||||
* - ESP_ERR_NO_MEM if memory can not be allocated
|
||||
* - ESP_FAIL if partition can not be mounted
|
||||
* - other error codes
|
||||
*/
|
||||
esp_err_t bsp_spiffs_mount(void);
|
||||
|
||||
/**
|
||||
* @brief Unmount SPIFFS from virtual file system
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_NOT_FOUND if the partition table does not contain SPIFFS partition with given label
|
||||
* - ESP_ERR_INVALID_STATE if esp_vfs_spiffs_unregister was already called
|
||||
* - ESP_ERR_NO_MEM if memory can not be allocated
|
||||
* - ESP_FAIL if partition can not be mounted
|
||||
* - other error codes
|
||||
*/
|
||||
esp_err_t bsp_spiffs_unmount(void);
|
||||
|
||||
/**************************************************************************************************
|
||||
*
|
||||
* uSD card
|
||||
*
|
||||
* After mounting the uSD card, it can be accessed with stdio functions ie.:
|
||||
* \code{.c}
|
||||
* FILE* f = fopen(BSP_MOUNT_POINT"/hello.txt", "w");
|
||||
* fprintf(f, "Hello %s!\n", bsp_sdcard->cid.name);
|
||||
* fclose(f);
|
||||
* \endcode
|
||||
**************************************************************************************************/
|
||||
#define BSP_SD_MOUNT_POINT CONFIG_BSP_SD_MOUNT_POINT
|
||||
extern sdmmc_card_t *bsp_sdcard;
|
||||
|
||||
/**
|
||||
* @brief Mount microSD card to virtual file system
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_INVALID_STATE if esp_vfs_fat_sdmmc_mount was already called
|
||||
* - ESP_ERR_NO_MEM if memory cannot be allocated
|
||||
* - ESP_FAIL if partition cannot be mounted
|
||||
* - other error codes from SDMMC or SPI drivers, SDMMC protocol, or FATFS drivers
|
||||
*/
|
||||
esp_err_t bsp_sdcard_mount(void);
|
||||
|
||||
/**
|
||||
* @brief Unmount microSD card from virtual file system
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_NOT_FOUND if the partition table does not contain FATFS partition with given label
|
||||
* - ESP_ERR_INVALID_STATE if esp_vfs_fat_spiflash_mount was already called
|
||||
* - ESP_ERR_NO_MEM if memory can not be allocated
|
||||
* - ESP_FAIL if partition can not be mounted
|
||||
* - other error codes from wear levelling library, SPI flash driver, or FATFS drivers
|
||||
*/
|
||||
esp_err_t bsp_sdcard_unmount(void);
|
||||
|
||||
/**************************************************************************************************
|
||||
*
|
||||
* LCD interface
|
||||
*
|
||||
* ESP-BOX is shipped with 2.4inch ST7789 display controller.
|
||||
* It features 16-bit colors, 320x240 resolution and capacitive touch controller.
|
||||
*
|
||||
* LVGL is used as graphics library. LVGL is NOT thread safe, therefore the user must take LVGL mutex
|
||||
* by calling bsp_display_lock() before calling and LVGL API (lv_...) and then give the mutex with
|
||||
* bsp_display_unlock().
|
||||
*
|
||||
* Display's backlight must be enabled explicitly by calling bsp_display_backlight_on()
|
||||
**************************************************************************************************/
|
||||
#define BSP_LCD_PIXEL_CLOCK_MHZ (80)
|
||||
|
||||
#if (BSP_CONFIG_NO_GRAPHIC_LIB == 0)
|
||||
|
||||
#define BSP_LCD_DRAW_BUFF_SIZE (BSP_LCD_H_RES * 50) // Frame buffer size in pixels
|
||||
#define BSP_LCD_DRAW_BUFF_DOUBLE (0)
|
||||
|
||||
/**
|
||||
* @brief BSP display configuration structure
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
lvgl_port_cfg_t lvgl_port_cfg; /*!< LVGL port configuration */
|
||||
uint32_t buffer_size; /*!< Size of the buffer for the screen in pixels */
|
||||
bool double_buffer; /*!< True, if should be allocated two buffers */
|
||||
struct {
|
||||
unsigned int buff_dma: 1; /*!< Allocated LVGL buffer will be DMA capable */
|
||||
unsigned int buff_spiram: 1; /*!< Allocated LVGL buffer will be in PSRAM */
|
||||
unsigned int sw_rotate: 1; /*!< Use software rotation (slower), The feature is unavailable under avoid-tear mode */
|
||||
} flags;
|
||||
} bsp_display_cfg_t;
|
||||
|
||||
/**
|
||||
* @brief Initialize display
|
||||
*
|
||||
* This function initializes SPI, display controller and starts LVGL handling task.
|
||||
* LCD backlight must be enabled separately by calling bsp_display_brightness_set()
|
||||
*
|
||||
* @return Pointer to LVGL display or NULL when error occured
|
||||
*/
|
||||
lv_display_t *bsp_display_start(void);
|
||||
|
||||
/**
|
||||
* @brief Initialize display
|
||||
*
|
||||
* This function initializes SPI, display controller and starts LVGL handling task.
|
||||
* LCD backlight must be enabled separately by calling bsp_display_brightness_set()
|
||||
*
|
||||
* @param cfg display configuration
|
||||
*
|
||||
* @return Pointer to LVGL display or NULL when error occured
|
||||
*/
|
||||
lv_display_t *bsp_display_start_with_config(const bsp_display_cfg_t *cfg);
|
||||
|
||||
/**
|
||||
* @brief Get pointer to input device (touch, buttons, ...)
|
||||
*
|
||||
* @note The LVGL input device is initialized in bsp_display_start() function.
|
||||
*
|
||||
* @return Pointer to LVGL input device or NULL when not initialized
|
||||
*/
|
||||
lv_indev_t *bsp_display_get_input_dev(void);
|
||||
|
||||
/**
|
||||
* @brief Take LVGL mutex
|
||||
*
|
||||
* @param timeout_ms Timeout in [ms]. 0 will block indefinitely.
|
||||
* @return true Mutex was taken
|
||||
* @return false Mutex was NOT taken
|
||||
*/
|
||||
bool bsp_display_lock(uint32_t timeout_ms);
|
||||
|
||||
/**
|
||||
* @brief Give LVGL mutex
|
||||
*
|
||||
*/
|
||||
void bsp_display_unlock(void);
|
||||
|
||||
/**
|
||||
* @brief Rotate screen
|
||||
*
|
||||
* Display must be already initialized by calling bsp_display_start()
|
||||
*
|
||||
* @param[in] disp Pointer to LVGL display
|
||||
* @param[in] rotation Angle of the display rotation
|
||||
*/
|
||||
void bsp_display_rotate(lv_display_t *disp, lv_disp_rotation_t rotation);
|
||||
#endif // BSP_CONFIG_NO_GRAPHIC_LIB == 0
|
||||
|
||||
/**************************************************************************************************
|
||||
*
|
||||
* USB
|
||||
*
|
||||
**************************************************************************************************/
|
||||
|
||||
|
||||
/**
|
||||
* @brief Power modes of USB Host connector
|
||||
*/
|
||||
typedef enum bsp_usb_host_power_mode_t {
|
||||
BSP_USB_HOST_POWER_MODE_USB_DEV, //!< Power from USB DEV port
|
||||
} bsp_usb_host_power_mode_t;
|
||||
|
||||
/**
|
||||
* @brief Start USB host
|
||||
*
|
||||
* This is a one-stop-shop function that will configure the board for USB Host mode
|
||||
* and start USB Host library
|
||||
*
|
||||
* @param[in] mode USB Host connector power mode (Not used on this board)
|
||||
* @param[in] limit_500mA Limit output current to 500mA (Not used on this board)
|
||||
* @return
|
||||
* - ESP_OK On success
|
||||
* - ESP_ERR_INVALID_ARG Parameter error
|
||||
* - ESP_ERR_NO_MEM Memory cannot be allocated
|
||||
*/
|
||||
esp_err_t bsp_usb_host_start(bsp_usb_host_power_mode_t mode, bool limit_500mA);
|
||||
|
||||
/**
|
||||
* @brief Stop USB host
|
||||
*
|
||||
* USB Host lib will be uninstalled and power from connector removed.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK On success
|
||||
* - ESP_ERR_INVALID_ARG Parameter error
|
||||
*/
|
||||
esp_err_t bsp_usb_host_stop(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* @brief BSP Touchscreen
|
||||
*
|
||||
* This file offers API for basic touchscreen initialization.
|
||||
* It is useful for users who want to use the touchscreen without the default Graphical Library LVGL.
|
||||
*
|
||||
* For standard LCD initialization with LVGL graphical library, you can call all-in-one function bsp_display_start().
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "esp_lcd_touch.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief BSP touch configuration structure
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
void *dummy; /*!< Prepared for future use. */
|
||||
} bsp_touch_config_t;
|
||||
|
||||
/**
|
||||
* @brief Create new touchscreen
|
||||
*
|
||||
* If you want to free resources allocated by this function, you can use esp_lcd_touch API, ie.:
|
||||
*
|
||||
* \code{.c}
|
||||
* esp_lcd_touch_del(tp);
|
||||
* \endcode
|
||||
*
|
||||
* @param[in] config touch configuration
|
||||
* @param[out] ret_touch esp_lcd_touch touchscreen handle
|
||||
* @return
|
||||
* - ESP_OK On success
|
||||
* - Else esp_lcd_touch failure
|
||||
*/
|
||||
esp_err_t bsp_touch_new(const bsp_touch_config_t *config, esp_lcd_touch_handle_t *ret_touch);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "esp_check.h"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Assert on error, if selected in menuconfig. Otherwise return error code. */
|
||||
#if CONFIG_BSP_ERROR_CHECK
|
||||
#define BSP_ERROR_CHECK_RETURN_ERR(x) ESP_ERROR_CHECK(x)
|
||||
#define BSP_ERROR_CHECK_RETURN_NULL(x) ESP_ERROR_CHECK(x)
|
||||
#define BSP_ERROR_CHECK(x, ret) ESP_ERROR_CHECK(x)
|
||||
#define BSP_NULL_CHECK(x, ret) assert(x)
|
||||
#define BSP_NULL_CHECK_GOTO(x, goto_tag) assert(x)
|
||||
#else
|
||||
#define BSP_ERROR_CHECK_RETURN_ERR(x) do { \
|
||||
esp_err_t err_rc_ = (x); \
|
||||
if (unlikely(err_rc_ != ESP_OK)) { \
|
||||
return err_rc_; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
#define BSP_ERROR_CHECK_RETURN_NULL(x) do { \
|
||||
if (unlikely((x) != ESP_OK)) { \
|
||||
return NULL; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
#define BSP_NULL_CHECK(x, ret) do { \
|
||||
if ((x) == NULL) { \
|
||||
return ret; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
#define BSP_ERROR_CHECK(x, ret) do { \
|
||||
if (unlikely((x) != ESP_OK)) { \
|
||||
return ret; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
#define BSP_NULL_CHECK_GOTO(x, goto_tag) do { \
|
||||
if ((x) == NULL) { \
|
||||
goto goto_tag; \
|
||||
} \
|
||||
} while(0)
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
+1
@@ -0,0 +1 @@
|
||||
39cea941e1e0e6c47408395d79834c04eac2e308309432af36d94e035b9239d5
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
# ChangeLog
|
||||
|
||||
## v1.1.3 - 2025-6-23
|
||||
|
||||
### bugfix:
|
||||
|
||||
* Fixed the log TAG issue
|
||||
|
||||
## v1.1.2 - 2025-04-03
|
||||
|
||||
### bugfix:
|
||||
|
||||
* Fixed the issue where the ST7701 RGB interface mirror and swap were ineffective
|
||||
|
||||
## v1.1.1 - 2024-11-10
|
||||
|
||||
### bugfix:
|
||||
|
||||
* Modified the order of reading the ID register
|
||||
|
||||
## v1.1.0 - 2024-05-06
|
||||
|
||||
### Enhancements:
|
||||
|
||||
* Support MIPI-DSI interface
|
||||
|
||||
## v1.0.1 - 2024-03-25
|
||||
|
||||
### bugfix
|
||||
|
||||
* Fix the `mirror` function
|
||||
|
||||
## v1.0.0 - 2023-11-03
|
||||
|
||||
### bugfix
|
||||
|
||||
* Fix the incompatible dependent version of ESP-IDF
|
||||
* Fix missing dependency of `cmake_utilities` component
|
||||
* Check conflicting commands between initialization sequence and driver
|
||||
|
||||
## v0.0.2 - 2023-10-26
|
||||
|
||||
### Enhancements:
|
||||
|
||||
* Support to mirror by command
|
||||
|
||||
## v0.0.1 - 2023-09-12
|
||||
|
||||
### Enhancements:
|
||||
|
||||
* Implement the driver for the ST7701(S) LCD controller
|
||||
* Support RGB interface
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
idf_component_register(SRCS "esp_lcd_st7701.c" "esp_lcd_st7701_rgb.c" "esp_lcd_st7701_mipi.c"
|
||||
INCLUDE_DIRS "include"
|
||||
PRIV_INCLUDE_DIRS "priv_include"
|
||||
REQUIRES "esp_lcd" "driver")
|
||||
|
||||
include(package_manager)
|
||||
cu_pkg_define_version(${CMAKE_CURRENT_LIST_DIR})
|
||||
+174
@@ -0,0 +1,174 @@
|
||||
# ESP LCD ST7701(S)
|
||||
|
||||
[](https://components.espressif.com/components/espressif/esp_lcd_st7701)
|
||||
|
||||
Implementation of the ST7701(S) LCD controller with esp_lcd component.
|
||||
|
||||
| LCD controller | Communication interface | Component name | Link to datasheet |
|
||||
| :------------: | :---------------------: | :------------: | :--------------------------------------------------------------------------: |
|
||||
| ST7701(S) | 3-wire SPI + RGB / MIPI-DSI | esp_lcd_st7701 | [PDF](https://dl.espressif.com/AE/esp-iot-solution/ST7701S_SPEC_%20V1.4.pdf) |
|
||||
|
||||
**Note**: MIPI-DSI interface only supports ESP-IDF v5.3 and above versions.
|
||||
|
||||
For more information on LCD, please refer to the [LCD documentation](https://docs.espressif.com/projects/esp-iot-solution/en/latest/display/lcd/index.html).
|
||||
|
||||
## Add to project
|
||||
|
||||
Packages from this repository are uploaded to [Espressif's component service](https://components.espressif.com/).
|
||||
You can add them to your project via `idf.py add-dependancy`, e.g.
|
||||
|
||||
```
|
||||
idf.py add-dependency "espressif/esp_lcd_st7701"
|
||||
```
|
||||
|
||||
Alternatively, you can create `idf_component.yml`. More is in [Espressif's documentation](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/tools/idf-component-manager.html).
|
||||
|
||||
## Example use
|
||||
|
||||
### RGB Interface
|
||||
|
||||
For most RGB LCDs, they typically use a "3-Wire SPI + Parallel RGB" interface. The "3-Wire SPI" interface is used for transmitting command data and the "Parallel RGB" interface is used for sending pixel data.
|
||||
|
||||
It's recommended to use the [esp_lcd_panel_io_additions](https://components.espressif.com/components/espressif/esp_lcd_panel_io_additions) component to bit-bang the "3-Wire SPI" interface through **GPIO** or an **IO expander** (like [TCA9554](https://components.espressif.com/components/espressif/esp_io_expander_tca9554)). To do this, please first add this component to your project manually. Then, refer to the following code to initialize the GC9503 controller.
|
||||
|
||||
```c
|
||||
ESP_LOGI(TAG, "Install 3-wire SPI panel IO");
|
||||
spi_line_config_t line_config = {
|
||||
.cs_io_type = IO_TYPE_EXPANDER, // Set to `IO_TYPE_GPIO` if using GPIO, same to below
|
||||
.cs_gpio_num = EXAMPLE_LCD_IO_SPI_CS,
|
||||
.scl_io_type = IO_TYPE_GPIO,
|
||||
.scl_gpio_num = EXAMPLE_LCD_IO_SPI_SCK,
|
||||
.sda_io_type = IO_TYPE_GPIO,
|
||||
.sda_gpio_num = EXAMPLE_LCD_IO_SPI_SDO,
|
||||
.io_expander = NULL, // Set to NULL if not using IO expander
|
||||
};
|
||||
esp_lcd_panel_io_3wire_spi_config_t io_config = ST7701_PANEL_IO_3WIRE_SPI_CONFIG(line_config, 0);
|
||||
esp_lcd_panel_io_handle_t io_handle = NULL;
|
||||
ESP_ERROR_CHECK(esp_lcd_new_panel_io_3wire_spi(&io_config, &io_handle));
|
||||
|
||||
/**
|
||||
* Uncomment these line if use custom initialization commands.
|
||||
* The array should be declared as static const and positioned outside the function.
|
||||
*/
|
||||
// static const st7701_lcd_init_cmd_t lcd_init_cmds[] = {
|
||||
// // cmd data data_size delay_ms
|
||||
// {0xFF, (uint8_t []){0x77, 0x01, 0x00, 0x00, 0x13}, 5, 0},
|
||||
// {0xEF, (uint8_t []){0x08}, 1, 0},
|
||||
// {0xFF, (uint8_t []){0x77, 0x01, 0x00, 0x00, 0x10}, 5, 0},
|
||||
// {0xC0, (uint8_t []){0x3B, 0x00}, 2, 0},
|
||||
// ...
|
||||
// };
|
||||
|
||||
ESP_LOGI(TAG, "Install ST7701 panel driver");
|
||||
esp_lcd_rgb_panel_config_t rgb_config = {
|
||||
.clk_src = LCD_CLK_SRC_DEFAULT,
|
||||
.psram_trans_align = 64,
|
||||
.data_width = 16,
|
||||
.bits_per_pixel = 16,
|
||||
.de_gpio_num = EXAMPLE_LCD_IO_RGB_DE,
|
||||
.pclk_gpio_num = EXAMPLE_LCD_IO_RGB_PCLK,
|
||||
.vsync_gpio_num = EXAMPLE_LCD_IO_RGB_VSYNC,
|
||||
.hsync_gpio_num = EXAMPLE_LCD_IO_RGB_HSYNC,
|
||||
.disp_gpio_num = EXAMPLE_LCD_IO_RGB_DISP,
|
||||
.data_gpio_nums = {
|
||||
EXAMPLE_LCD_IO_RGB_DATA0,
|
||||
EXAMPLE_LCD_IO_RGB_DATA1,
|
||||
EXAMPLE_LCD_IO_RGB_DATA2,
|
||||
EXAMPLE_LCD_IO_RGB_DATA3,
|
||||
EXAMPLE_LCD_IO_RGB_DATA4,
|
||||
EXAMPLE_LCD_IO_RGB_DATA5,
|
||||
EXAMPLE_LCD_IO_RGB_DATA6,
|
||||
EXAMPLE_LCD_IO_RGB_DATA7,
|
||||
EXAMPLE_LCD_IO_RGB_DATA8,
|
||||
EXAMPLE_LCD_IO_RGB_DATA9,
|
||||
EXAMPLE_LCD_IO_RGB_DATA10,
|
||||
EXAMPLE_LCD_IO_RGB_DATA11,
|
||||
EXAMPLE_LCD_IO_RGB_DATA12,
|
||||
EXAMPLE_LCD_IO_RGB_DATA13,
|
||||
EXAMPLE_LCD_IO_RGB_DATA14,
|
||||
EXAMPLE_LCD_IO_RGB_DATA15,
|
||||
},
|
||||
.timings = ST7701_480_480_PANEL_60HZ_RGB_TIMING(),
|
||||
...
|
||||
};
|
||||
st7701_vendor_config_t vendor_config = {
|
||||
.rgb_config = &rgb_config,
|
||||
// .init_cmds = lcd_init_cmds, // Uncomment these line if use custom initialization commands
|
||||
// .init_cmds_size = sizeof(lcd_init_cmds) / sizeof(st7701_lcd_init_cmd_t),
|
||||
.flags = {
|
||||
.mirror_by_cmd = 1, // Only work when `enable_io_multiplex` is set to 0
|
||||
.enable_io_multiplex = 0, /**
|
||||
* Set to 1 if panel IO is no longer needed after LCD initialization.
|
||||
* If the panel IO pins are sharing other pins of the RGB interface to save GPIOs,
|
||||
* Please set it to 1 to release the pins.
|
||||
*/
|
||||
},
|
||||
};
|
||||
const esp_lcd_panel_dev_config_t panel_config = {
|
||||
.reset_gpio_num = EXAMPLE_LCD_IO_RST, // Set to -1 if not use
|
||||
.rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB, // Implemented by LCD command `36h`
|
||||
.bits_per_pixel = EXAMPLE_LCD_BIT_PER_PIXEL, // Implemented by LCD command `3Ah` (16/18/24)
|
||||
.vendor_config = &vendor_config,
|
||||
};
|
||||
esp_lcd_panel_handle_t panel_handle = NULL;
|
||||
ESP_ERROR_CHECK(esp_lcd_new_panel_st7701(io_handle, &panel_config, &panel_handle)); /**
|
||||
* Only create RGB when `enable_io_multiplex` is set to 0,
|
||||
* or initialize st7701 meanwhile
|
||||
*/
|
||||
ESP_ERROR_CHECK(esp_lcd_panel_reset(panel_handle)); // Only reset RGB when `enable_io_multiplex` is set to 1, or reset st7701 meanwhile
|
||||
ESP_ERROR_CHECK(esp_lcd_panel_init(panel_handle)); // Only initialize RGB when `enable_io_multiplex` is set to 1, or initialize st7701 meanwhile
|
||||
```
|
||||
|
||||
### MIPI Interface
|
||||
|
||||
```c
|
||||
/**
|
||||
* Uncomment these line if use custom initialization commands.
|
||||
* The array should be declared as static const and positioned outside the function.
|
||||
*/
|
||||
// static const st7701_lcd_init_cmd_t lcd_init_cmds[] = {
|
||||
// // cmd data data_size delay_ms
|
||||
// {0xFF, (uint8_t []){0x77, 0x01, 0x00, 0x00, 0x13}, 5, 0},
|
||||
// {0xEF, (uint8_t []){0x08}, 1, 0},
|
||||
// {0xFF, (uint8_t []){0x77, 0x01, 0x00, 0x00, 0x10}, 5, 0},
|
||||
// {0xC0, (uint8_t []){0x3B, 0x00}, 2, 0},
|
||||
// ...
|
||||
// };
|
||||
ESP_LOGI(TAG, "MIPI DSI PHY Powered on");
|
||||
esp_ldo_channel_config_t ldo_mipi_phy_config = {
|
||||
.chan_id = EXAMPLE_MIPI_DSI_PHY_PWR_LDO_CHAN,
|
||||
.voltage_mv = EXAMPLE_MIPI_DSI_PHY_PWR_LDO_VOLTAGE_MV,
|
||||
};
|
||||
ESP_ERROR_CHECK(esp_ldo_acquire_channel(&ldo_mipi_phy_config, &ldo_mipi_phy));
|
||||
|
||||
ESP_LOGI(TAG, "Initialize MIPI DSI bus");
|
||||
esp_lcd_dsi_bus_config_t bus_config = ST7701_PANEL_BUS_DSI_2CH_CONFIG();
|
||||
ESP_ERROR_CHECK(esp_lcd_new_dsi_bus(&bus_config, &mipi_dsi_bus));
|
||||
|
||||
ESP_LOGI(TAG, "Install panel IO");
|
||||
esp_lcd_dbi_io_config_t dbi_config = ST7701_PANEL_IO_DBI_CONFIG();
|
||||
ESP_ERROR_CHECK(esp_lcd_new_panel_io_dbi(mipi_dsi_bus, &dbi_config, &mipi_dbi_io));
|
||||
|
||||
ESP_LOGI(TAG, "Install LCD driver of st7701");
|
||||
esp_lcd_panel_handle_t panel_handle = NULL;
|
||||
esp_lcd_dpi_panel_config_t dpi_config = ST7701_480_360_PANEL_60HZ_DPI_CONFIG(EXAMPLE_MIPI_DPI_PX_FORMAT);
|
||||
st7701_vendor_config_t vendor_config = {
|
||||
// .init_cmds = lcd_init_cmds, // Uncomment these line if use custom initialization commands
|
||||
// .init_cmds_size = sizeof(lcd_init_cmds) / sizeof(st7701_lcd_init_cmd_t),
|
||||
.flags.use_mipi_interface = 1,
|
||||
.mipi_config = {
|
||||
.dsi_bus = mipi_dsi_bus,
|
||||
.dpi_config = &dpi_config,
|
||||
},
|
||||
};
|
||||
const esp_lcd_panel_dev_config_t panel_config = {
|
||||
.reset_gpio_num = EXAMPLE_PIN_NUM_LCD_RST,
|
||||
.rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB,
|
||||
.bits_per_pixel = EXAMPLE_LCD_BIT_PER_PIXEL,
|
||||
.vendor_config = &vendor_config,
|
||||
};
|
||||
ESP_ERROR_CHECK(esp_lcd_new_panel_st7701(mipi_dbi_io, &panel_config, &panel_handle));
|
||||
ESP_ERROR_CHECK(esp_lcd_panel_reset(panel_handle));
|
||||
ESP_ERROR_CHECK(esp_lcd_panel_init(panel_handle));
|
||||
ESP_ERROR_CHECK(esp_lcd_panel_disp_on_off(panel_handle, true));
|
||||
```
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "soc/soc_caps.h"
|
||||
#include "esp_check.h"
|
||||
#include "esp_lcd_types.h"
|
||||
|
||||
#include "esp_lcd_st7701_interface.h"
|
||||
#include "esp_lcd_st7701.h"
|
||||
|
||||
static const char *TAG = "st7701";
|
||||
|
||||
esp_err_t esp_lcd_new_panel_st7701(const esp_lcd_panel_io_handle_t io, const esp_lcd_panel_dev_config_t *panel_dev_config,
|
||||
esp_lcd_panel_handle_t *ret_panel)
|
||||
{
|
||||
ESP_LOGI(TAG, "version: %d.%d.%d", ESP_LCD_ST7701_VER_MAJOR, ESP_LCD_ST7701_VER_MINOR, ESP_LCD_ST7701_VER_PATCH);
|
||||
ESP_RETURN_ON_FALSE(panel_dev_config && ret_panel, ESP_ERR_INVALID_ARG, TAG, "Invalid arguments");
|
||||
st7701_vendor_config_t *vendor_config = (st7701_vendor_config_t *)panel_dev_config->vendor_config;
|
||||
ESP_RETURN_ON_FALSE(vendor_config, ESP_ERR_INVALID_ARG, TAG, "`vendor_config` is necessary");
|
||||
|
||||
esp_err_t ret = ESP_ERR_NOT_SUPPORTED;
|
||||
|
||||
#if SOC_LCD_RGB_SUPPORTED
|
||||
if (!vendor_config->flags.use_mipi_interface) {
|
||||
ret = esp_lcd_new_panel_st7701_rgb(io, panel_dev_config, ret_panel);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if SOC_MIPI_DSI_SUPPORTED
|
||||
if (vendor_config->flags.use_mipi_interface) {
|
||||
ret = esp_lcd_new_panel_st7701_mipi(io, panel_dev_config, ret_panel);
|
||||
}
|
||||
#endif
|
||||
|
||||
return ret;
|
||||
}
|
||||
+478
@@ -0,0 +1,478 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include "soc/soc_caps.h"
|
||||
|
||||
#if SOC_MIPI_DSI_SUPPORTED
|
||||
#include <stdlib.h>
|
||||
#include <sys/cdefs.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "esp_lcd_panel_interface.h"
|
||||
#include "esp_lcd_panel_io.h"
|
||||
#include "esp_lcd_panel_vendor.h"
|
||||
#include "esp_lcd_panel_ops.h"
|
||||
#include "esp_lcd_panel_commands.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_check.h"
|
||||
#include "esp_lcd_st7701.h"
|
||||
#include "esp_lcd_st7701_interface.h"
|
||||
|
||||
static const char *TAG = "st7701_mipi";
|
||||
|
||||
static esp_err_t panel_st7701_del(esp_lcd_panel_t *panel);
|
||||
static esp_err_t panel_st7701_reset(esp_lcd_panel_t *panel);
|
||||
static esp_err_t panel_st7701_init(esp_lcd_panel_t *panel);
|
||||
static esp_err_t panel_st7701_invert_color(esp_lcd_panel_t *panel, bool invert_color_data);
|
||||
static esp_err_t panel_st7701_mirror(esp_lcd_panel_t *panel, bool mirror_x, bool mirror_y);
|
||||
static esp_err_t panel_st7701_disp_on_off(esp_lcd_panel_t *panel, bool off);
|
||||
static esp_err_t panel_st7701_sleep(esp_lcd_panel_t *panel, bool sleep);
|
||||
|
||||
typedef struct {
|
||||
esp_lcd_panel_io_handle_t io;
|
||||
int reset_gpio_num;
|
||||
uint8_t madctl_val; // save current value of LCD_CMD_MADCTL register
|
||||
uint8_t colmod_val; // save surrent value of LCD_CMD_COLMOD register
|
||||
const st7701_lcd_init_cmd_t *init_cmds;
|
||||
uint16_t init_cmds_size;
|
||||
struct {
|
||||
unsigned int reset_level: 1;
|
||||
} flags;
|
||||
// To save the original functions of MIPI DPI panel
|
||||
esp_err_t (*del)(esp_lcd_panel_t *panel);
|
||||
esp_err_t (*init)(esp_lcd_panel_t *panel);
|
||||
} st7701_panel_t;
|
||||
|
||||
esp_err_t esp_lcd_new_panel_st7701_mipi(const esp_lcd_panel_io_handle_t io, const esp_lcd_panel_dev_config_t *panel_dev_config, esp_lcd_panel_handle_t *ret_panel)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(io && panel_dev_config && ret_panel, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
st7701_vendor_config_t *vendor_config = (st7701_vendor_config_t *)panel_dev_config->vendor_config;
|
||||
ESP_RETURN_ON_FALSE(vendor_config && vendor_config->mipi_config.dpi_config && vendor_config->mipi_config.dsi_bus, ESP_ERR_INVALID_ARG, TAG,
|
||||
"invalid vendor config");
|
||||
|
||||
esp_err_t ret = ESP_OK;
|
||||
st7701_panel_t *st7701 = (st7701_panel_t *)calloc(1, sizeof(st7701_panel_t));
|
||||
ESP_RETURN_ON_FALSE(st7701, ESP_ERR_NO_MEM, TAG, "no mem for st7701 panel");
|
||||
|
||||
if (panel_dev_config->reset_gpio_num >= 0) {
|
||||
gpio_config_t io_conf = {
|
||||
.mode = GPIO_MODE_OUTPUT,
|
||||
.pin_bit_mask = 1ULL << panel_dev_config->reset_gpio_num,
|
||||
};
|
||||
ESP_GOTO_ON_ERROR(gpio_config(&io_conf), err, TAG, "configure GPIO for RST line failed");
|
||||
}
|
||||
|
||||
switch (panel_dev_config->color_space) {
|
||||
case LCD_RGB_ELEMENT_ORDER_RGB:
|
||||
st7701->madctl_val = 0;
|
||||
break;
|
||||
case LCD_RGB_ELEMENT_ORDER_BGR:
|
||||
st7701->madctl_val |= LCD_CMD_BGR_BIT;
|
||||
break;
|
||||
default:
|
||||
ESP_GOTO_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, err, TAG, "unsupported rgb element order");
|
||||
break;
|
||||
}
|
||||
|
||||
switch (panel_dev_config->bits_per_pixel) {
|
||||
case 16: // RGB565
|
||||
st7701->colmod_val = 0x55;
|
||||
break;
|
||||
case 18: // RGB666
|
||||
st7701->colmod_val = 0x66;
|
||||
break;
|
||||
case 24: // RGB888
|
||||
st7701->colmod_val = 0x77;
|
||||
break;
|
||||
default:
|
||||
ESP_GOTO_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, err, TAG, "unsupported pixel width");
|
||||
break;
|
||||
}
|
||||
|
||||
st7701->io = io;
|
||||
st7701->init_cmds = vendor_config->init_cmds;
|
||||
st7701->init_cmds_size = vendor_config->init_cmds_size;
|
||||
st7701->reset_gpio_num = panel_dev_config->reset_gpio_num;
|
||||
st7701->flags.reset_level = panel_dev_config->flags.reset_active_high;
|
||||
|
||||
// Create MIPI DPI panel
|
||||
esp_lcd_panel_handle_t panel_handle = NULL;
|
||||
ESP_GOTO_ON_ERROR(esp_lcd_new_panel_dpi(vendor_config->mipi_config.dsi_bus, vendor_config->mipi_config.dpi_config, &panel_handle), err, TAG,
|
||||
"create MIPI DPI panel failed");
|
||||
ESP_LOGD(TAG, "new MIPI DPI panel @%p", panel_handle);
|
||||
|
||||
// Save the original functions of MIPI DPI panel
|
||||
st7701->del = panel_handle->del;
|
||||
st7701->init = panel_handle->init;
|
||||
// Overwrite the functions of MIPI DPI panel
|
||||
panel_handle->del = panel_st7701_del;
|
||||
panel_handle->init = panel_st7701_init;
|
||||
panel_handle->reset = panel_st7701_reset;
|
||||
panel_handle->mirror = panel_st7701_mirror;
|
||||
panel_handle->invert_color = panel_st7701_invert_color;
|
||||
panel_handle->disp_on_off = panel_st7701_disp_on_off;
|
||||
panel_handle->disp_sleep = panel_st7701_sleep;
|
||||
panel_handle->user_data = st7701;
|
||||
*ret_panel = panel_handle;
|
||||
ESP_LOGD(TAG, "new st7701 panel @%p", st7701);
|
||||
|
||||
return ESP_OK;
|
||||
err:
|
||||
if (st7701) {
|
||||
if (panel_dev_config->reset_gpio_num >= 0) {
|
||||
gpio_reset_pin(panel_dev_config->reset_gpio_num);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t panel_st7701_del(esp_lcd_panel_t *panel)
|
||||
{
|
||||
st7701_panel_t *st7701 = (st7701_panel_t *)panel->user_data;
|
||||
|
||||
if (st7701->reset_gpio_num >= 0) {
|
||||
gpio_reset_pin(st7701->reset_gpio_num);
|
||||
}
|
||||
|
||||
// Delete MIPI DPI panel
|
||||
st7701->del(panel);
|
||||
ESP_LOGD(TAG, "del st7701 panel @%p", st7701);
|
||||
free(st7701);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t panel_st7701_reset(esp_lcd_panel_t *panel)
|
||||
{
|
||||
st7701_panel_t *st7701 = (st7701_panel_t *)panel->user_data;
|
||||
esp_lcd_panel_io_handle_t io = st7701->io;
|
||||
|
||||
// perform hardware reset
|
||||
if (st7701->reset_gpio_num >= 0) {
|
||||
gpio_set_level(st7701->reset_gpio_num, st7701->flags.reset_level);
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
gpio_set_level(st7701->reset_gpio_num, !st7701->flags.reset_level);
|
||||
vTaskDelay(pdMS_TO_TICKS(120));
|
||||
} else if (io) { // perform software reset
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_SWRESET, NULL, 0), TAG, "send command failed");
|
||||
vTaskDelay(pdMS_TO_TICKS(20)); // spec, wait at least 5ms before sending new command
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static const st7701_lcd_init_cmd_t vendor_specific_init_default[] = {
|
||||
// {0xFF, (uint8_t []){0x77, 0x01, 0x00, 0x00, 0x13}, 5, 0},
|
||||
// {0xEF, (uint8_t []){0x08}, 1, 0},
|
||||
// {0xFF, (uint8_t []){0x77, 0x01, 0x00, 0x00, 0x10}, 5, 0},
|
||||
// {0xC0, (uint8_t []){0x2c, 0x00}, 2, 0},
|
||||
// {0xC1, (uint8_t []){0x10, 0x0C}, 2, 0},
|
||||
// {0xC2, (uint8_t []){0x21, 0x0A}, 2, 0},
|
||||
// {0xCC, (uint8_t []){0x10}, 1, 0},
|
||||
// {0xB0, (uint8_t []){0x00, 0x0B, 0x12, 0x0D, 0x10, 0x06, 0x02, 0x08, 0x07, 0x1F, 0x04, 0x11, 0x0F, 0x29, 0x31, 0x1E}, 16, 0},
|
||||
// {0xB1, (uint8_t []){0x00, 0x0B, 0x13, 0x0D, 0x11, 0x06, 0x03, 0x08, 0x07, 0x20, 0x04, 0x12, 0x11, 0x29, 0x31, 0x1E}, 16, 0},
|
||||
// {0xFF, (uint8_t []){0x77, 0x01, 0x00, 0x00, 0x11}, 5, 0},
|
||||
// {0xB0, (uint8_t []){0x5D}, 1, 0},
|
||||
// {0xB1, (uint8_t []){0x72}, 1, 0},
|
||||
// {0xB2, (uint8_t []){0x84}, 1, 0},
|
||||
// {0xB3, (uint8_t []){0x80}, 1, 0},
|
||||
// {0xB5, (uint8_t []){0x4D}, 1, 0},
|
||||
// {0xB7, (uint8_t []){0x85}, 1, 0},
|
||||
// {0xB8, (uint8_t []){0x20}, 1, 0},
|
||||
// {0xC1, (uint8_t []){0x78}, 1, 0},
|
||||
// {0xC2, (uint8_t []){0x78}, 1, 0},
|
||||
// {0xD0, (uint8_t []){0x88}, 1, 0},
|
||||
// {0xE0, (uint8_t []){0x80, 0x00, 0x02}, 3, 0},
|
||||
// {0xE1, (uint8_t []){0x05, 0x00, 0x07, 0x00, 0x06, 0x00, 0x08, 0x00, 0x00, 0x33, 0x33}, 11, 0},
|
||||
// {0xE2, (uint8_t []){0x00, 0x00, 0x30, 0x30, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00}, 12, 0},
|
||||
// {0xE3, (uint8_t []){0x00, 0x00, 0x11, 0x11}, 4, 0},
|
||||
// {0xE4, (uint8_t []){0x44, 0x44}, 2, 0},
|
||||
// {0xE5, (uint8_t []){0x0C, 0x78, 0x00, 0xE0, 0x0E, 0x7A, 0x00, 0xE0, 0x08, 0x74, 0x00, 0xE0, 0x0A, 0x76, 0x00, 0xE0}, 16, 0},
|
||||
// {0xE6, (uint8_t []){0x00, 0x00, 0x11, 0x11}, 4, 0},
|
||||
// {0xE7, (uint8_t []){0x44, 0x44}, 2, 0},
|
||||
// {0xE8, (uint8_t []){0x0D, 0x79, 0x00, 0xE0, 0x0F, 0x7B, 0x00, 0xE0, 0x09, 0x75, 0x00, 0xE0, 0x0B, 0x77, 0x00, 0xE0}, 16, 0},
|
||||
// {0xE9, (uint8_t []){0x36, 0x00}, 2, 0},
|
||||
// {0xEB, (uint8_t []){0x00, 0x01, 0xE4, 0xE4, 0x44, 0x88, 0x40}, 7, 0},
|
||||
// {0xED, (uint8_t []){0xA1, 0xC2, 0xFB, 0x0F, 0x67, 0x45, 0xFF, 0xFF, 0xFF, 0xFF, 0x54, 0x76, 0xF0, 0xBF, 0x2C, 0x1A}, 16, 0},
|
||||
// {0xEF, (uint8_t []){0x10, 0x0D, 0x04, 0x08, 0x3F, 0x1F}, 6, 0},
|
||||
// {0xFF, (uint8_t []){0x77, 0x01, 0x00, 0x00, 0x13}, 5, 0},
|
||||
// {0xE8, (uint8_t []){0x00, 0x0E}, 2, 0},
|
||||
// {0xE8, (uint8_t []){0x00, 0x0C}, 2, 20},
|
||||
// {0xE8, (uint8_t []){0x00, 0x00}, 2, 0},
|
||||
// {0xFF, (uint8_t []){0x77, 0x01, 0x00, 0x00, 0x00}, 5, 0},
|
||||
// {0x11, (uint8_t []){0x00}, 0, 120},
|
||||
// {0x29, (uint8_t []){0x00}, 0, 0},
|
||||
|
||||
// {0xFF, (uint8_t []){0x77, 0x01, 0x00, 0x00, 0x12}, 5, 0}, /* This part of the parameters can be used for screen self-test */
|
||||
// {0xD1, (uint8_t []){0x81}, 1, 0},
|
||||
// {0xD2, (uint8_t []){0x08}, 1, 0},
|
||||
|
||||
/*
|
||||
|
||||
*/
|
||||
|
||||
{0xFF, (uint8_t []){0x77,0x01,0x00,0x00,0x13},5,0},
|
||||
{0xEF, (uint8_t []){0x08}, 1, 0},
|
||||
{0xFF, (uint8_t []){0x77,0x01,0x00,0x00,0x10},5,0},
|
||||
{0xC0, (uint8_t []){0x63, 0x00}, 2, 0},
|
||||
{0xC1, (uint8_t []){0x0D, 0x02}, 2, 0},
|
||||
{0xC2, (uint8_t []){0x10, 0x08}, 2, 0},
|
||||
{0xCC, (uint8_t []){0x10}, 1, 0},
|
||||
|
||||
{0xB0, (uint8_t []){0x80, 0x09, 0x53, 0x0C, 0xD0, 0x07, 0x0C, 0x09, 0x09, 0x28, 0x06, 0xD4, 0x13, 0x69, 0x2B, 0x71}, 16, 0},
|
||||
{0xB1, (uint8_t []){0x80, 0x94, 0x5A, 0x10, 0xD3, 0x06, 0x0A, 0x08, 0x08, 0x25, 0x03, 0xD3, 0x12, 0x66, 0x6A, 0x0D}, 16, 0},
|
||||
{0xFF, (uint8_t []){0x77, 0x01, 0x00, 0x00, 0x11}, 5, 0},
|
||||
|
||||
{0xB0, (uint8_t []){0x5D}, 1, 0},
|
||||
{0xB1, (uint8_t []){0x58}, 1, 0},
|
||||
{0xB2, (uint8_t []){0x87}, 1, 0},
|
||||
{0xB3, (uint8_t []){0x80}, 1, 0},
|
||||
{0xB5, (uint8_t []){0x4E}, 1, 0},
|
||||
{0xB7, (uint8_t []){0x85}, 1, 0},
|
||||
{0xB8, (uint8_t []){0x21}, 1, 0},
|
||||
{0xB9, (uint8_t []){0x10, 0x1F}, 2, 0},
|
||||
{0xBB, (uint8_t []){0x03}, 1,0},
|
||||
{0xBC, (uint8_t []){0x00}, 1,0},
|
||||
|
||||
{0xC1, (uint8_t []){0x78}, 1, 0},
|
||||
{0xC2, (uint8_t []){0x78}, 1, 0},
|
||||
{0xD0, (uint8_t []){0x88}, 1, 0},
|
||||
|
||||
{0xE0, (uint8_t []){0x00, 0x3A, 0x02}, 3, 0},
|
||||
{0xE1, (uint8_t []){0x04, 0xA0, 0x00, 0xA0, 0x05,0xA0, 0x00, 0xA0, 0x00, 0x40, 0x40}, 11, 0},
|
||||
{0xE2, (uint8_t []){0x30, 0x00, 0x40, 0x40, 0x32, 0xA0, 0x00, 0xA0, 0x00, 0xA0, 0x00, 0xA0, 0x00}, 13, 0},
|
||||
{0xE3, (uint8_t []){0x00, 0x00, 0x33, 0x33}, 4, 0},
|
||||
{0xE4, (uint8_t []){0x44, 0x44}, 2, 0},
|
||||
{0xE5, (uint8_t []){0x09, 0x2E, 0xA0, 0xA0, 0x0B, 0x30, 0xA0, 0xA0, 0x05, 0x2A, 0xA0, 0xA0, 0x07, 0x2C, 0xA0, 0xA0}, 16, 0},
|
||||
{0xE6, (uint8_t []){0x00, 0x00, 0x33, 0x33}, 4, 0},
|
||||
{0xE7, (uint8_t []){0x44, 0x44}, 2, 0},
|
||||
{0xE8, (uint8_t []){0x08, 0x2D, 0xA0, 0xA0, 0x0A, 0x2F, 0xA0, 0xA0, 0x04, 0x29, 0xA0, 0xA0, 0x06, 0x2B, 0xA0, 0xA0}, 16, 0},
|
||||
|
||||
{0xEB, (uint8_t []){0x00, 0x00, 0x4E, 0x4E, 0x00, 0x00, 0x00}, 7, 0},
|
||||
{0xEC, (uint8_t []){0x08, 0x01}, 2, 0},
|
||||
|
||||
{0xED, (uint8_t []){0xB0, 0x2B, 0x98, 0xA4, 0x56, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF7, 0x65, 0x4A, 0x89, 0xB2, 0x0B}, 16, 0},
|
||||
{0xEF, (uint8_t []){0x08, 0x08, 0x08, 0x45, 0x3F, 0x54}, 6, 0},
|
||||
{0xFF, (uint8_t []){0x77, 0x01, 0x00, 0x00, 0x00}, 5, 0},
|
||||
|
||||
// {0x3A, (uint8_t []){0x66}, 1, 0},
|
||||
{0x11, (uint8_t []){0x00}, 1, 120},
|
||||
{0x29, (uint8_t []){0x00}, 1, 20},
|
||||
|
||||
|
||||
|
||||
// {0xFF, (uint8_t []){0x77,0x01,0x00,0x00,0x13},5,0},
|
||||
// {0xEF, (uint8_t []){0x08}, 1, 0},
|
||||
// {0xFF, (uint8_t []){0x77,0x01,0x00,0x00,0x10},5,0},
|
||||
// {0xC0, (uint8_t []){0x63, 0x00}, 2, 0},
|
||||
// {0xC1, (uint8_t []){0x0D, 0x02}, 2, 0},
|
||||
// {0xC2, (uint8_t []){0x17, 0x08}, 2, 0},
|
||||
// {0xCC, (uint8_t []){0x10}, 1, 0},
|
||||
|
||||
// {0xB0, (uint8_t []){0x40, 0xC9, 0x94, 0x0E, 0x10, 0x05, 0x0B, 0x09, 0x08, 0x26, 0x04, 0x52, 0x10, 0x69, 0x6B, 0x69}, 16, 0},
|
||||
// {0xB1, (uint8_t []){0x40, 0xD2, 0x98, 0x0C, 0x92, 0x07, 0x09, 0x08, 0x07, 0x25, 0x02, 0x0E, 0x1C, 0x6E, 0x78, 0x55}, 16, 0},
|
||||
// {0xFF, (uint8_t []){0x77, 0x01, 0x00, 0x00, 0x11}, 5, 0},
|
||||
|
||||
// {0xB0, (uint8_t []){0x5D}, 1, 0},
|
||||
// {0xB1, (uint8_t []){0x4E}, 1, 0},
|
||||
// {0xB2, (uint8_t []){0x87}, 1, 0},
|
||||
// {0xB3, (uint8_t []){0x80}, 1, 0},
|
||||
// {0xB5, (uint8_t []){0x4E}, 1, 0},
|
||||
// {0xB7, (uint8_t []){0x85}, 1, 0},
|
||||
// {0xB8, (uint8_t []){0x21}, 1, 0},
|
||||
// {0xB9, (uint8_t []){0x10, 0x1F}, 2, 0},
|
||||
// {0xBB, (uint8_t []){0x03}, 1,0},
|
||||
// {0xBC, (uint8_t []){0x00}, 1,0},
|
||||
|
||||
// {0xC1, (uint8_t []){0x78}, 1, 0},
|
||||
// {0xC2, (uint8_t []){0x78}, 1, 0},
|
||||
// {0xD0, (uint8_t []){0x88}, 1, 0},
|
||||
|
||||
// {0xE0, (uint8_t []){0x00, 0x3A, 0x02}, 3, 0},
|
||||
// {0xE1, (uint8_t []){0x04, 0xA0, 0x00, 0xA0, 0x05,0xA0, 0x00, 0xA0, 0x00, 0x40, 0x40}, 11, 0},
|
||||
// {0xE2, (uint8_t []){0x30, 0x00, 0x40, 0x40, 0x32, 0xA0, 0x00, 0xA0, 0x00, 0xA0, 0x00, 0xA0, 0x00}, 13, 0},
|
||||
// {0xE3, (uint8_t []){0x00, 0x00, 0x33, 0x33}, 4, 0},
|
||||
// {0xE4, (uint8_t []){0x44, 0x44}, 2, 0},
|
||||
// {0xE5, (uint8_t []){0x09, 0x2E, 0xA0, 0xA0, 0x0B, 0x30, 0xA0, 0xA0, 0x05, 0x2A, 0xA0, 0xA0, 0x07, 0x2C, 0xA0, 0xA0}, 16, 0},
|
||||
// {0xE6, (uint8_t []){0x00, 0x00, 0x33, 0x33}, 4, 0},
|
||||
// {0xE7, (uint8_t []){0x44, 0x44}, 2, 0},
|
||||
// {0xE8, (uint8_t []){0x08, 0x2D, 0xA0, 0xA0, 0x0A, 0x2F, 0xA0, 0xA0, 0x04, 0x29, 0xA0, 0xA0, 0x06, 0x2B, 0xA0, 0xA0}, 16, 0},
|
||||
|
||||
// {0xEB, (uint8_t []){0x00, 0x00, 0x4E, 0x4E, 0x00, 0x00, 0x00}, 7, 0},
|
||||
// {0xEC, (uint8_t []){0x08, 0x01}, 2, 0},
|
||||
|
||||
// {0xED, (uint8_t []){0xB0, 0x2B, 0x98, 0xA4, 0x56, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF7, 0x65, 0x4A, 0x89, 0xB2, 0x0B}, 16, 0},
|
||||
// {0xEF, (uint8_t []){0x08, 0x08, 0x08, 0x45, 0x3F, 0x54}, 6, 0},
|
||||
// {0xFF, (uint8_t []){0x77, 0x01, 0x00, 0x00, 0x00}, 5, 0},
|
||||
|
||||
// // {0x3A, (uint8_t []){0x66}, 1, 0},
|
||||
// {0x11, (uint8_t []){0x00}, 1, 120},
|
||||
// {0x29, (uint8_t []){0x00}, 1, 20},
|
||||
|
||||
|
||||
};
|
||||
|
||||
static esp_err_t panel_st7701_init(esp_lcd_panel_t *panel)
|
||||
{
|
||||
st7701_panel_t *st7701 = (st7701_panel_t *)panel->user_data;
|
||||
esp_lcd_panel_io_handle_t io = st7701->io;
|
||||
const st7701_lcd_init_cmd_t *init_cmds = NULL;
|
||||
uint16_t init_cmds_size = 0;
|
||||
bool is_command2_disable = true;
|
||||
bool is_cmd_overwritten = false;
|
||||
|
||||
uint8_t ID[3];
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_rx_param(io, 0x04, ID, 3), TAG, "read ID failed");
|
||||
ESP_LOGI(TAG, "LCD ID: %02X %02X %02X", ID[0], ID[1], ID[2]);
|
||||
|
||||
// back to CMD_Page 0
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, ST7701_CMD_CND2BKxSEL, (uint8_t []) {
|
||||
ST7701_CMD_BKxSEL_BYTE0, ST7701_CMD_BKxSEL_BYTE1, ST7701_CMD_BKxSEL_BYTE2, ST7701_CMD_BKxSEL_BYTE3, 0x00
|
||||
}, 5), TAG, "Write cmd failed");
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL, (uint8_t[]) {
|
||||
st7701->madctl_val,
|
||||
}, 1), TAG, "send command failed");
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_COLMOD, (uint8_t[]) {
|
||||
st7701->colmod_val,
|
||||
}, 1), TAG, "send command failed");
|
||||
ESP_LOGI(TAG, " st7701->madctl_val: 0x%x, st7701->colmod_val: 0x%x", st7701->madctl_val, st7701->colmod_val);
|
||||
|
||||
// vendor specific initialization, it can be different between manufacturers
|
||||
// should consult the LCD supplier for initialization sequence code
|
||||
if (st7701->init_cmds) {
|
||||
init_cmds = st7701->init_cmds;
|
||||
init_cmds_size = st7701->init_cmds_size;
|
||||
} else {
|
||||
init_cmds = vendor_specific_init_default;
|
||||
init_cmds_size = sizeof(vendor_specific_init_default) / sizeof(st7701_lcd_init_cmd_t);
|
||||
}
|
||||
|
||||
for (int i = 0; i < init_cmds_size; i++) {
|
||||
// Check if the command has been used or conflicts with the internal only when command2 is disable
|
||||
if (is_command2_disable && (init_cmds[i].data_bytes > 0)) {
|
||||
switch (init_cmds[i].cmd) {
|
||||
case LCD_CMD_MADCTL:
|
||||
is_cmd_overwritten = true;
|
||||
st7701->madctl_val = ((uint8_t *)init_cmds[i].data)[0];
|
||||
break;
|
||||
case LCD_CMD_COLMOD:
|
||||
is_cmd_overwritten = true;
|
||||
st7701->colmod_val = ((uint8_t *)init_cmds[i].data)[0];
|
||||
break;
|
||||
default:
|
||||
is_cmd_overwritten = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (is_cmd_overwritten) {
|
||||
is_cmd_overwritten = false;
|
||||
ESP_LOGW(TAG, "The %02Xh command has been used and will be overwritten by external initialization sequence",
|
||||
init_cmds[i].cmd);
|
||||
}
|
||||
}
|
||||
|
||||
// Send command
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, init_cmds[i].cmd, init_cmds[i].data, init_cmds[i].data_bytes),
|
||||
TAG, "send command failed");
|
||||
vTaskDelay(pdMS_TO_TICKS(init_cmds[i].delay_ms));
|
||||
|
||||
// Check if the current cmd is the command2 disable cmd
|
||||
if ((init_cmds[i].cmd == ST7701_CMD_CND2BKxSEL) && (init_cmds[i].data_bytes > 4)) {
|
||||
is_command2_disable = !(((uint8_t *)init_cmds[i].data)[4] & ST7701_CMD_CN2_BIT);
|
||||
}
|
||||
}
|
||||
ESP_LOGD(TAG, "send init commands success");
|
||||
|
||||
ESP_RETURN_ON_ERROR(st7701->init(panel), TAG, "init MIPI DPI panel failed");
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t panel_st7701_invert_color(esp_lcd_panel_t *panel, bool invert_color_data)
|
||||
{
|
||||
st7701_panel_t *st7701 = (st7701_panel_t *)panel->user_data;
|
||||
esp_lcd_panel_io_handle_t io = st7701->io;
|
||||
int command = 0;
|
||||
if (invert_color_data) {
|
||||
command = LCD_CMD_INVON;
|
||||
} else {
|
||||
command = LCD_CMD_INVOFF;
|
||||
}
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, command, NULL, 0), TAG, "send command failed");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t panel_st7701_mirror(esp_lcd_panel_t *panel, bool mirror_x, bool mirror_y)
|
||||
{
|
||||
st7701_panel_t *st7701 = (st7701_panel_t *)panel->user_data;
|
||||
esp_lcd_panel_io_handle_t io = st7701->io;
|
||||
uint8_t sdir_val = 0;
|
||||
|
||||
ESP_RETURN_ON_FALSE(io, ESP_FAIL, TAG, "Panel IO is deleted, cannot send command");
|
||||
// Control mirror through LCD command
|
||||
if (mirror_x) {
|
||||
sdir_val = ST7701_CMD_SS_BIT;
|
||||
} else {
|
||||
sdir_val = 0;
|
||||
}
|
||||
if (mirror_y) {
|
||||
st7701->madctl_val |= LCD_CMD_ML_BIT;
|
||||
} else {
|
||||
st7701->madctl_val &= ~LCD_CMD_ML_BIT;
|
||||
}
|
||||
|
||||
// Enable the Command2 BK0
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, ST7701_CMD_CND2BKxSEL, (uint8_t []) {
|
||||
ST7701_CMD_BKxSEL_BYTE0, ST7701_CMD_BKxSEL_BYTE1, ST7701_CMD_BKxSEL_BYTE2, ST7701_CMD_BKxSEL_BYTE3,
|
||||
ST7701_CMD_BKxSEL_BK0 | ST7701_CMD_CN2_BIT,
|
||||
}, 5), TAG, "send command failed");
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, ST7701_CMD_SDIR, (uint8_t[]) {
|
||||
sdir_val,
|
||||
}, 1), TAG, "send command failed");;
|
||||
|
||||
// Disable Command2
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, ST7701_CMD_CND2BKxSEL, (uint8_t []) {
|
||||
ST7701_CMD_BKxSEL_BYTE0, ST7701_CMD_BKxSEL_BYTE1, ST7701_CMD_BKxSEL_BYTE2, ST7701_CMD_BKxSEL_BYTE3, 0,
|
||||
}, 5), TAG, "send command failed");
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL, (uint8_t[]) {
|
||||
st7701->madctl_val,
|
||||
}, 1), TAG, "send command failed");;
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t panel_st7701_disp_on_off(esp_lcd_panel_t *panel, bool on_off)
|
||||
{
|
||||
st7701_panel_t *st7701 = (st7701_panel_t *)panel->user_data;
|
||||
esp_lcd_panel_io_handle_t io = st7701->io;
|
||||
int command = 0;
|
||||
|
||||
if (on_off) {
|
||||
command = LCD_CMD_DISPON;
|
||||
} else {
|
||||
command = LCD_CMD_DISPOFF;
|
||||
}
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, command, NULL, 0), TAG, "send command failed");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t panel_st7701_sleep(esp_lcd_panel_t *panel, bool sleep)
|
||||
{
|
||||
st7701_panel_t *st7701 = (st7701_panel_t *)panel->user_data;
|
||||
esp_lcd_panel_io_handle_t io = st7701->io;
|
||||
int command = 0;
|
||||
if (sleep) {
|
||||
command = LCD_CMD_SLPIN;
|
||||
} else {
|
||||
command = LCD_CMD_SLPOUT;
|
||||
}
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, command, NULL, 0), TAG,
|
||||
"io tx param failed");
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
#endif
|
||||
+386
@@ -0,0 +1,386 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include "soc/soc_caps.h"
|
||||
|
||||
#if SOC_LCD_RGB_SUPPORTED
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "esp_check.h"
|
||||
#include "esp_lcd_panel_commands.h"
|
||||
#include "esp_lcd_panel_interface.h"
|
||||
#include "esp_lcd_panel_io.h"
|
||||
#include "esp_lcd_panel_rgb.h"
|
||||
#include "esp_lcd_panel_vendor.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_lcd_st7701.h"
|
||||
#include "esp_lcd_st7701_interface.h"
|
||||
typedef struct {
|
||||
esp_lcd_panel_io_handle_t io;
|
||||
int reset_gpio_num;
|
||||
uint8_t madctl_val; // Save current value of LCD_CMD_MADCTL register
|
||||
uint8_t colmod_val; // Save current value of LCD_CMD_COLMOD register
|
||||
const st7701_lcd_init_cmd_t *init_cmds;
|
||||
uint16_t init_cmds_size;
|
||||
struct {
|
||||
unsigned int mirror_by_cmd: 1;
|
||||
unsigned int enable_io_multiplex: 1;
|
||||
unsigned int display_on_off_use_cmd: 1;
|
||||
unsigned int reset_level: 1;
|
||||
} flags;
|
||||
// To save the original functions of RGB panel
|
||||
esp_err_t (*init)(esp_lcd_panel_t *panel);
|
||||
esp_err_t (*del)(esp_lcd_panel_t *panel);
|
||||
esp_err_t (*reset)(esp_lcd_panel_t *panel);
|
||||
esp_err_t (*mirror)(esp_lcd_panel_t *panel, bool x_axis, bool y_axis);
|
||||
esp_err_t (*disp_on_off)(esp_lcd_panel_t *panel, bool on_off);
|
||||
} st7701_panel_t;
|
||||
|
||||
static const char *TAG = "st7701_rgb";
|
||||
|
||||
static esp_err_t panel_st7701_send_init_cmds(st7701_panel_t *st7701);
|
||||
|
||||
static esp_err_t panel_st7701_init(esp_lcd_panel_t *panel);
|
||||
static esp_err_t panel_st7701_del(esp_lcd_panel_t *panel);
|
||||
static esp_err_t panel_st7701_reset(esp_lcd_panel_t *panel);
|
||||
static esp_err_t panel_st7701_mirror(esp_lcd_panel_t *panel, bool mirror_x, bool mirror_y);
|
||||
static esp_err_t panel_st7701_disp_on_off(esp_lcd_panel_t *panel, bool off);
|
||||
|
||||
esp_err_t esp_lcd_new_panel_st7701_rgb(const esp_lcd_panel_io_handle_t io, const esp_lcd_panel_dev_config_t *panel_dev_config,
|
||||
esp_lcd_panel_handle_t *ret_panel)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(io && panel_dev_config && ret_panel, ESP_ERR_INVALID_ARG, TAG, "invalid arguments");
|
||||
st7701_vendor_config_t *vendor_config = (st7701_vendor_config_t *)panel_dev_config->vendor_config;
|
||||
ESP_RETURN_ON_FALSE(vendor_config && vendor_config->rgb_config, ESP_ERR_INVALID_ARG, TAG, "`verndor_config` and `rgb_config` are necessary");
|
||||
ESP_RETURN_ON_FALSE(!vendor_config->flags.enable_io_multiplex || !vendor_config->flags.mirror_by_cmd,
|
||||
ESP_ERR_INVALID_ARG, TAG, "`mirror_by_cmd` and `enable_io_multiplex` cannot work together");
|
||||
|
||||
esp_err_t ret = ESP_OK;
|
||||
st7701_panel_t *st7701 = (st7701_panel_t *)calloc(1, sizeof(st7701_panel_t));
|
||||
ESP_RETURN_ON_FALSE(st7701, ESP_ERR_NO_MEM, TAG, "no mem for st7701 panel");
|
||||
|
||||
if (panel_dev_config->reset_gpio_num >= 0) {
|
||||
gpio_config_t io_conf = {
|
||||
.mode = GPIO_MODE_OUTPUT,
|
||||
.pin_bit_mask = 1ULL << panel_dev_config->reset_gpio_num,
|
||||
};
|
||||
ESP_GOTO_ON_ERROR(gpio_config(&io_conf), err, TAG, "configure GPIO for RST line failed");
|
||||
}
|
||||
|
||||
switch (panel_dev_config->rgb_ele_order) {
|
||||
case LCD_RGB_ELEMENT_ORDER_RGB:
|
||||
st7701->madctl_val = 0;
|
||||
break;
|
||||
case LCD_RGB_ELEMENT_ORDER_BGR:
|
||||
st7701->madctl_val |= LCD_CMD_BGR_BIT;
|
||||
break;
|
||||
default:
|
||||
ESP_GOTO_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, err, TAG, "unsupported color element order");
|
||||
break;
|
||||
}
|
||||
|
||||
st7701->colmod_val = 0;
|
||||
switch (panel_dev_config->bits_per_pixel) {
|
||||
case 16: // RGB565
|
||||
st7701->colmod_val = 0x50;
|
||||
break;
|
||||
case 18: // RGB666
|
||||
st7701->colmod_val = 0x60;
|
||||
break;
|
||||
case 24: // RGB888
|
||||
st7701->colmod_val = 0x70;
|
||||
break;
|
||||
default:
|
||||
ESP_GOTO_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, err, TAG, "unsupported pixel width");
|
||||
break;
|
||||
}
|
||||
|
||||
st7701->io = io;
|
||||
st7701->init_cmds = vendor_config->init_cmds;
|
||||
st7701->init_cmds_size = vendor_config->init_cmds_size;
|
||||
st7701->reset_gpio_num = panel_dev_config->reset_gpio_num;
|
||||
st7701->flags.mirror_by_cmd = vendor_config->flags.mirror_by_cmd;
|
||||
st7701->flags.display_on_off_use_cmd = (vendor_config->rgb_config->disp_gpio_num >= 0) ? 0 : 1;
|
||||
st7701->flags.enable_io_multiplex = vendor_config->flags.enable_io_multiplex;
|
||||
st7701->flags.reset_level = panel_dev_config->flags.reset_active_high;
|
||||
|
||||
if (st7701->flags.enable_io_multiplex) {
|
||||
if (st7701->reset_gpio_num >= 0) { // Perform hardware reset
|
||||
gpio_set_level(st7701->reset_gpio_num, st7701->flags.reset_level);
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
gpio_set_level(st7701->reset_gpio_num, !st7701->flags.reset_level);
|
||||
} else { // Perform software reset
|
||||
ESP_GOTO_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_SWRESET, NULL, 0), err, TAG, "send command failed");
|
||||
}
|
||||
vTaskDelay(pdMS_TO_TICKS(120));
|
||||
|
||||
/**
|
||||
* In order to enable the 3-wire SPI interface pins (such as SDA and SCK) to share other pins of the RGB interface
|
||||
* (such as HSYNC) and save GPIOs, we need to send LCD initialization commands via the 3-wire SPI interface before
|
||||
* `esp_lcd_new_rgb_panel()` is called.
|
||||
*/
|
||||
ESP_GOTO_ON_ERROR(panel_st7701_send_init_cmds(st7701), err, TAG, "send init commands failed");
|
||||
// After sending the initialization commands, the 3-wire SPI interface can be deleted
|
||||
ESP_GOTO_ON_ERROR(esp_lcd_panel_io_del(io), err, TAG, "delete panel IO failed");
|
||||
st7701->io = NULL;
|
||||
ESP_LOGD(TAG, "delete panel IO");
|
||||
}
|
||||
|
||||
// Create RGB panel
|
||||
ESP_GOTO_ON_ERROR(esp_lcd_new_rgb_panel(vendor_config->rgb_config, ret_panel), err, TAG, "create RGB panel failed");
|
||||
ESP_LOGD(TAG, "new RGB panel @%p", ret_panel);
|
||||
|
||||
// Save the original functions of RGB panel
|
||||
st7701->init = (*ret_panel)->init;
|
||||
st7701->del = (*ret_panel)->del;
|
||||
st7701->reset = (*ret_panel)->reset;
|
||||
st7701->mirror = (*ret_panel)->mirror;
|
||||
st7701->disp_on_off = (*ret_panel)->disp_on_off;
|
||||
// Overwrite the functions of RGB panel
|
||||
(*ret_panel)->init = panel_st7701_init;
|
||||
(*ret_panel)->del = panel_st7701_del;
|
||||
(*ret_panel)->reset = panel_st7701_reset;
|
||||
(*ret_panel)->mirror = panel_st7701_mirror;
|
||||
(*ret_panel)->disp_on_off = panel_st7701_disp_on_off;
|
||||
(*ret_panel)->user_data = st7701;
|
||||
ESP_LOGD(TAG, "new st7701 panel @%p", st7701);
|
||||
|
||||
return ESP_OK;
|
||||
|
||||
err:
|
||||
if (st7701) {
|
||||
if (panel_dev_config->reset_gpio_num >= 0) {
|
||||
gpio_reset_pin(panel_dev_config->reset_gpio_num);
|
||||
}
|
||||
free(st7701);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const st7701_lcd_init_cmd_t vendor_specific_init_default[] = {
|
||||
// {cmd, { data }, data_size, delay_ms}
|
||||
{0xFF, (uint8_t []){0x77, 0x01, 0x00, 0x00, 0x13}, 5, 0},
|
||||
{0xEF, (uint8_t []){0x08}, 1, 0},
|
||||
{0xFF, (uint8_t []){0x77, 0x01, 0x00, 0x00, 0x10}, 5, 0},
|
||||
{0xC0, (uint8_t []){0x3B, 0x00}, 2, 0},
|
||||
{0xC1, (uint8_t []){0x10, 0x02}, 2, 0},
|
||||
{0xC2, (uint8_t []){0x20, 0x06}, 2, 0},
|
||||
{0xCC, (uint8_t []){0x10}, 1, 0},
|
||||
{0xB0, (uint8_t []){0x00, 0x13, 0x5A, 0x0F, 0x12, 0x07, 0x09, 0x08, 0x08, 0x24, 0x07, 0x13, 0x12, 0x6B, 0x73, 0xFF}, 16, 0},
|
||||
{0xB1, (uint8_t []){0x00, 0x13, 0x5A, 0x0F, 0x12, 0x07, 0x09, 0x08, 0x08, 0x24, 0x07, 0x13, 0x12, 0x6B, 0x73, 0xFF}, 16, 0},
|
||||
{0xFF, (uint8_t []){0x77, 0x01, 0x00, 0x00, 0x11}, 5, 0},
|
||||
{0xB0, (uint8_t []){0x8D}, 1, 0},
|
||||
{0xB1, (uint8_t []){0x48}, 1, 0},
|
||||
{0xB2, (uint8_t []){0x89}, 1, 0},
|
||||
{0xB3, (uint8_t []){0x80}, 1, 0},
|
||||
{0xB5, (uint8_t []){0x49}, 1, 0},
|
||||
{0xB7, (uint8_t []){0x85}, 1, 0},
|
||||
{0xB8, (uint8_t []){0x32}, 1, 0},
|
||||
{0xC1, (uint8_t []){0x78}, 1, 0},
|
||||
{0xC2, (uint8_t []){0x78}, 1, 0},
|
||||
{0xD0, (uint8_t []){0x88}, 1, 100},
|
||||
{0xE0, (uint8_t []){0x00, 0x00, 0x02}, 3, 0},
|
||||
{0xE1, (uint8_t []){0x05, 0xC0, 0x07, 0xC0, 0x04, 0xC0, 0x06, 0xC0, 0x00, 0x44, 0x44}, 11, 0},
|
||||
{0xE2, (uint8_t []){0x00, 0x00, 0x33, 0x33, 0x01, 0xC0, 0x00, 0x00, 0x01, 0xC0, 0x00, 0x00, 0x00}, 13, 0},
|
||||
{0xE3, (uint8_t []){0x00, 0x00, 0x11, 0x11}, 4, 0},
|
||||
{0xE4, (uint8_t []){0x44, 0x44}, 2, 0},
|
||||
{0xE5, (uint8_t []){0x0D, 0xF1, 0x10, 0x98, 0x0F, 0xF3, 0x10, 0x98, 0x09, 0xED, 0x10, 0x98, 0x0B, 0xEF, 0x10, 0x98}, 16, 0},
|
||||
{0xE6, (uint8_t []){0x00, 0x00, 0x11, 0x11}, 4, 0},
|
||||
{0xE7, (uint8_t []){0x44, 0x44}, 2, 0},
|
||||
{0xE8, (uint8_t []){0x0C, 0xF0, 0x10, 0x98, 0x0E, 0xF2, 0x10, 0x98, 0x08, 0xEC, 0x10, 0x98, 0x0A, 0xEE, 0x10, 0x98}, 16, 0},
|
||||
{0xEB, (uint8_t []){0x00, 0x01, 0xE4, 0xE4, 0x44, 0x88, 0x00}, 7, 0},
|
||||
{0xED, (uint8_t []){0xFF, 0x04, 0x56, 0x7F, 0xBA, 0x2F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF2, 0xAB, 0xF7, 0x65, 0x40, 0xFF}, 16, 0},
|
||||
{0xEF, (uint8_t []){0x10, 0x0D, 0x04, 0x08, 0x3F, 0x1F}, 6, 0},
|
||||
{0xFF, (uint8_t []){0x77, 0x01, 0x00, 0x00, 0x00}, 5, 0},
|
||||
{0x11, (uint8_t []){0x00}, 0, 120},
|
||||
{0x29, (uint8_t []){0x00}, 0, 0},
|
||||
|
||||
// {0xFF, (uint8_t []){0x77, 0x01, 0x00, 0x00, 0x12}, 5, 0}, /* This part of the parameters can be used for screen self-test */
|
||||
// {0xD1, (uint8_t []){0x81}, 1, 0},
|
||||
// {0xD2, (uint8_t []){0x08}, 1, 0},
|
||||
};
|
||||
|
||||
static esp_err_t panel_st7701_send_init_cmds(st7701_panel_t *st7701)
|
||||
{
|
||||
esp_lcd_panel_io_handle_t io = st7701->io;
|
||||
const st7701_lcd_init_cmd_t *init_cmds = NULL;
|
||||
uint16_t init_cmds_size = 0;
|
||||
bool is_command2_disable = true;
|
||||
bool is_cmd_overwritten = false;
|
||||
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, ST7701_CMD_CND2BKxSEL, (uint8_t []) {
|
||||
ST7701_CMD_BKxSEL_BYTE0, ST7701_CMD_BKxSEL_BYTE1, ST7701_CMD_BKxSEL_BYTE2, ST7701_CMD_BKxSEL_BYTE3, 0x00
|
||||
}, 5), TAG, "Write cmd failed");
|
||||
// Set color format
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL, (uint8_t []) {
|
||||
st7701->madctl_val
|
||||
}, 1), TAG, "Write cmd failed");
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_COLMOD, (uint8_t []) {
|
||||
st7701->colmod_val
|
||||
}, 1), TAG, "Write cmd failed");
|
||||
|
||||
// vendor specific initialization, it can be different between manufacturers
|
||||
// should consult the LCD supplier for initialization sequence code
|
||||
if (st7701->init_cmds) {
|
||||
init_cmds = st7701->init_cmds;
|
||||
init_cmds_size = st7701->init_cmds_size;
|
||||
} else {
|
||||
init_cmds = vendor_specific_init_default;
|
||||
init_cmds_size = sizeof(vendor_specific_init_default) / sizeof(st7701_lcd_init_cmd_t);
|
||||
}
|
||||
|
||||
for (int i = 0; i < init_cmds_size; i++) {
|
||||
// Check if the command has been used or conflicts with the internal only when command2 is disable
|
||||
if (is_command2_disable && (init_cmds[i].data_bytes > 0)) {
|
||||
switch (init_cmds[i].cmd) {
|
||||
case LCD_CMD_MADCTL:
|
||||
is_cmd_overwritten = true;
|
||||
st7701->madctl_val = ((uint8_t *)init_cmds[i].data)[0];
|
||||
break;
|
||||
case LCD_CMD_COLMOD:
|
||||
is_cmd_overwritten = true;
|
||||
st7701->colmod_val = ((uint8_t *)init_cmds[i].data)[0];
|
||||
break;
|
||||
default:
|
||||
is_cmd_overwritten = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (is_cmd_overwritten) {
|
||||
is_cmd_overwritten = false;
|
||||
ESP_LOGW(TAG, "The %02Xh command has been used and will be overwritten by external initialization sequence",
|
||||
init_cmds[i].cmd);
|
||||
}
|
||||
}
|
||||
|
||||
// Send command
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, init_cmds[i].cmd, init_cmds[i].data, init_cmds[i].data_bytes),
|
||||
TAG, "send command failed");
|
||||
vTaskDelay(pdMS_TO_TICKS(init_cmds[i].delay_ms));
|
||||
|
||||
// Check if the current cmd is the command2 disable cmd
|
||||
if ((init_cmds[i].cmd == ST7701_CMD_CND2BKxSEL) && (init_cmds[i].data_bytes > 4)) {
|
||||
is_command2_disable = !(((uint8_t *)init_cmds[i].data)[4] & ST7701_CMD_CN2_BIT);
|
||||
}
|
||||
}
|
||||
ESP_LOGD(TAG, "send init commands success");
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t panel_st7701_init(esp_lcd_panel_t *panel)
|
||||
{
|
||||
st7701_panel_t *st7701 = (st7701_panel_t *)panel->user_data;
|
||||
|
||||
if (!st7701->flags.enable_io_multiplex) {
|
||||
ESP_RETURN_ON_ERROR(panel_st7701_send_init_cmds(st7701), TAG, "send init commands failed");
|
||||
}
|
||||
// Init RGB panel
|
||||
ESP_RETURN_ON_ERROR(st7701->init(panel), TAG, "init RGB panel failed");
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t panel_st7701_del(esp_lcd_panel_t *panel)
|
||||
{
|
||||
st7701_panel_t *st7701 = (st7701_panel_t *)panel->user_data;
|
||||
|
||||
if (st7701->reset_gpio_num >= 0) {
|
||||
gpio_reset_pin(st7701->reset_gpio_num);
|
||||
}
|
||||
// Delete RGB panel
|
||||
st7701->del(panel);
|
||||
free(st7701);
|
||||
ESP_LOGD(TAG, "del st7701 panel @%p", st7701);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t panel_st7701_reset(esp_lcd_panel_t *panel)
|
||||
{
|
||||
st7701_panel_t *st7701 = (st7701_panel_t *)panel->user_data;
|
||||
esp_lcd_panel_io_handle_t io = st7701->io;
|
||||
|
||||
// Perform hardware reset
|
||||
if (st7701->reset_gpio_num >= 0) {
|
||||
gpio_set_level(st7701->reset_gpio_num, st7701->flags.reset_level);
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
gpio_set_level(st7701->reset_gpio_num, !st7701->flags.reset_level);
|
||||
vTaskDelay(pdMS_TO_TICKS(120));
|
||||
} else if (io) { // Perform software reset
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_SWRESET, NULL, 0), TAG, "send command failed");
|
||||
vTaskDelay(pdMS_TO_TICKS(120));
|
||||
}
|
||||
// Reset RGB panel
|
||||
ESP_RETURN_ON_ERROR(st7701->reset(panel), TAG, "reset RGB panel failed");
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t panel_st7701_mirror(esp_lcd_panel_t *panel, bool mirror_x, bool mirror_y)
|
||||
{
|
||||
st7701_panel_t *st7701 = (st7701_panel_t *)panel->user_data;
|
||||
esp_lcd_panel_io_handle_t io = st7701->io;
|
||||
uint8_t sdir_val = 0;
|
||||
|
||||
if (st7701->flags.mirror_by_cmd) {
|
||||
ESP_RETURN_ON_FALSE(io, ESP_FAIL, TAG, "Panel IO is deleted, cannot send command");
|
||||
// Control mirror through LCD command
|
||||
if (mirror_x) {
|
||||
sdir_val = ST7701_CMD_SS_BIT;
|
||||
} else {
|
||||
sdir_val = 0;
|
||||
}
|
||||
if (mirror_y) {
|
||||
st7701->madctl_val |= LCD_CMD_ML_BIT;
|
||||
} else {
|
||||
st7701->madctl_val &= ~LCD_CMD_ML_BIT;
|
||||
}
|
||||
|
||||
// Enable the Command2 BK0
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, ST7701_CMD_CND2BKxSEL, (uint8_t []) {
|
||||
ST7701_CMD_BKxSEL_BYTE0, ST7701_CMD_BKxSEL_BYTE1, ST7701_CMD_BKxSEL_BYTE2, ST7701_CMD_BKxSEL_BYTE3,
|
||||
ST7701_CMD_BKxSEL_BK0 | ST7701_CMD_CN2_BIT,
|
||||
}, 5), TAG, "send command failed");
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, ST7701_CMD_SDIR, (uint8_t[]) {
|
||||
sdir_val,
|
||||
}, 1), TAG, "send command failed");;
|
||||
|
||||
// Disable Command2
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, ST7701_CMD_CND2BKxSEL, (uint8_t []) {
|
||||
ST7701_CMD_BKxSEL_BYTE0, ST7701_CMD_BKxSEL_BYTE1, ST7701_CMD_BKxSEL_BYTE2, ST7701_CMD_BKxSEL_BYTE3, 0,
|
||||
}, 5), TAG, "send command failed");
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL, (uint8_t[]) {
|
||||
st7701->madctl_val,
|
||||
}, 1), TAG, "send command failed");;
|
||||
} else {
|
||||
// Control mirror through RGB panel
|
||||
ESP_RETURN_ON_ERROR(st7701->mirror(panel, mirror_x, mirror_y), TAG, "RGB panel mirror failed");
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t panel_st7701_disp_on_off(esp_lcd_panel_t *panel, bool on_off)
|
||||
{
|
||||
st7701_panel_t *st7701 = (st7701_panel_t *)panel->user_data;
|
||||
esp_lcd_panel_io_handle_t io = st7701->io;
|
||||
int command = 0;
|
||||
|
||||
if (st7701->flags.display_on_off_use_cmd) {
|
||||
ESP_RETURN_ON_FALSE(io, ESP_FAIL, TAG, "Panel IO is deleted, cannot send command");
|
||||
// Control display on/off through LCD command
|
||||
if (on_off) {
|
||||
command = LCD_CMD_DISPON;
|
||||
} else {
|
||||
command = LCD_CMD_DISPOFF;
|
||||
}
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, command, NULL, 0), TAG, "send command failed");
|
||||
} else {
|
||||
// Control display on/off through display control signal
|
||||
ESP_RETURN_ON_ERROR(st7701->disp_on_off(panel, on_off), TAG, "RGB panel disp_on_off failed");
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
#endif
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
# For more information about build system see
|
||||
# https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/build-system.html
|
||||
# The following five lines of boilerplate have to be in your project's
|
||||
# CMakeLists in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
add_compile_options("-Wno-attributes")
|
||||
project(rgb_avoid_tearing)
|
||||
+81
@@ -0,0 +1,81 @@
|
||||
| Supported Targets | ESP32-S3 |
|
||||
| ----------------- | -------- |
|
||||
|
||||
| Supported LCD Controller | ST7701 |
|
||||
| ----------------------------| -------|
|
||||
|
||||
| Supported Touch Controller | GT911 |
|
||||
| ----------------------------| -------|
|
||||
|
||||
# RGB Avoid Tearing Example
|
||||
|
||||
[esp_lcd](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/lcd.html) provides several panel drivers out-of box, e.g. ST7789, SSD1306, NT35510. However, there're a lot of other panels on the market, it's beyond `esp_lcd` component's responsibility to include them all.
|
||||
|
||||
`esp_lcd` allows user to add their own panel drivers in the project scope (i.e. panel driver can live outside of esp-idf), so that the upper layer code like LVGL porting code can be reused without any modifications, as long as user-implemented panel driver follows the interface defined in the `esp_lcd` component.
|
||||
|
||||
This example demonstrates how to avoid tearing when using LVGL with RGB interface screens in an esp-idf project. The example will use the LVGL library to draw a stylish music player.
|
||||
|
||||
The LVGL-related parameter configurations, such as LVGL's registered resolution, LVGL task-related parameters, and tearing prevention methods, can be configured in lvgl_port_v8.h.
|
||||
|
||||
This example uses the [esp_timer](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/system/esp_timer.html) to generate the ticks needed by LVGL and uses a dedicated task to run the `lv_timer_handler()`. Since the LVGL APIs are not thread-safe, this example uses a mutex which be invoked before the call of `lv_timer_handler()` and released after it. The same mutex needs to be used in other tasks and threads around every LVGL (lv_...) related function call and code. For more porting guides, please refer to [LVGL porting doc](https://docs.lvgl.io/master/porting/index.html).
|
||||
|
||||
## How to use the example
|
||||
|
||||
## ESP-IDF Required
|
||||
|
||||
### Hardware Required
|
||||
|
||||
* An ESP32-S3R8 development board
|
||||
* A ST7701 LCD panel, with RGB interface
|
||||
* An USB cable for power supply and programming
|
||||
|
||||
### Hardware Connection
|
||||
|
||||
The connection between ESP Board and the LCD is as follows:
|
||||
|
||||
```
|
||||
ESP Board RGB Panel
|
||||
+-----------------------+ +-------------------+
|
||||
| GND +--------------+GND |
|
||||
| | | |
|
||||
| 3V3 +--------------+VCC |
|
||||
| | | |
|
||||
| PCLK+--------------+PCLK |
|
||||
| | | |
|
||||
| DATA[15:0]+--------------+DATA[15:0] |
|
||||
| | | |
|
||||
| HSYNC+--------------+HSYNC |
|
||||
| | | |
|
||||
| VSYNC+--------------+VSYNC |
|
||||
| | | |
|
||||
| DE+--------------+DE |
|
||||
| | | |
|
||||
| BK_LIGHT+--------------+BLK |
|
||||
+-----------------------+ | |
|
||||
3V3-----+DISP_EN |
|
||||
| |
|
||||
+-------------------+
|
||||
```
|
||||
|
||||
* The LCD parameters and GPIO number used by this example can be changed in [example_rgb_avoid_tearing.c](main/example_rgb_avoid_tearing.c). Especially, please pay attention to the **vendor specific initialization**, it can be different between manufacturers and should consult the LCD supplier for initialization sequence code.
|
||||
* The LVGL parameters can be changed not only through `menuconfig` but also directly in `lvgl_conf.h`
|
||||
|
||||
### Configure the Project
|
||||
|
||||
Run `idf.py menuconfig` and navigate to `Example Configuration` menu.
|
||||
|
||||
### Build and Flash
|
||||
|
||||
Run `idf.py set-target esp32s3` to select the target chip.
|
||||
|
||||
Run `idf.py -p PORT build flash monitor` to build, flash and monitor the project. A fancy animation will show up on the LCD as expected.
|
||||
|
||||
The first time you run `idf.py` for the example will cost extra time as the build system needs to address the component dependencies and downloads the missing components from registry into `managed_components` folder.
|
||||
|
||||
(To exit the serial monitor, type ``Ctrl-]``.)
|
||||
|
||||
See the [Getting Started Guide](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/index.html) for full steps to configure and use ESP-IDF to build projects.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
For any technical queries, please open an [issue](https://github.com/espressif/esp-iot-solution/issues) on GitHub. We will get back to you soon.
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
idf_component_register(
|
||||
SRCS "example_rgb_avoid_tearing.c" "lvgl_port_v8.c"
|
||||
INCLUDE_DIRS ".")
|
||||
|
||||
idf_component_get_property(lvgl_lib lvgl__lvgl COMPONENT_LIB)
|
||||
target_compile_options(${lvgl_lib} PRIVATE -Wno-format)
|
||||
+132
@@ -0,0 +1,132 @@
|
||||
menu "Example Configuration"
|
||||
menu "Touch Controller"
|
||||
config EXAMPLE_LCD_TOUCH_CONTROLLER_GT911
|
||||
bool "Enable LCD GT911 Touch"
|
||||
default n
|
||||
help
|
||||
Enable this option if you wish to use display touch.
|
||||
endmenu
|
||||
|
||||
menu "Display"
|
||||
config EXAMPLE_LCD_RGB_BOUNCE_BUFFER_HEIGHT
|
||||
int "RGB Bounce buffer height"
|
||||
default 20
|
||||
help
|
||||
Height of bounce buffer. The width of the buffer is the same as that of the LCD.
|
||||
|
||||
config EXAMPLE_LVGL_PORT_TASK_MAX_DELAY_MS
|
||||
int "LVGL timer task maximum delay (ms)"
|
||||
default 500
|
||||
range 2 2000 # Example range, adjust as needed
|
||||
help
|
||||
The maximum delay of the LVGL timer task, in milliseconds.
|
||||
|
||||
config EXAMPLE_LVGL_PORT_TASK_MIN_DELAY_MS
|
||||
int "LVGL timer task minimum delay (ms)"
|
||||
default 5
|
||||
range 1 100 # Example range, adjust as needed
|
||||
help
|
||||
The minimum delay of the LVGL timer task, in milliseconds.
|
||||
|
||||
config EXAMPLE_LVGL_PORT_TASK_PRIORITY
|
||||
int "LVGL task priority"
|
||||
default 4
|
||||
help
|
||||
The Board Support Package will create a task that will periodically handle LVGL operation in lv_timer_handler().
|
||||
|
||||
config EXAMPLE_LVGL_PORT_TASK_STACK_SIZE_KB
|
||||
int "LVGL task stack size (KB)"
|
||||
default 6
|
||||
help
|
||||
Size(KB) of LVGL task stack.
|
||||
|
||||
config EXAMPLE_LVGL_PORT_TASK_CORE
|
||||
int "LVGL timer task core"
|
||||
default -1
|
||||
range -1 1
|
||||
help
|
||||
The core of the LVGL timer task.
|
||||
Set to -1 to not specify the core.
|
||||
Set to 1 only if the SoCs support dual-core, otherwise set to -1 or 0.
|
||||
|
||||
config EXAMPLE_LVGL_PORT_TICK
|
||||
int "LVGL tick period"
|
||||
default 2
|
||||
range 1 100
|
||||
help
|
||||
Period of LVGL tick timer.
|
||||
|
||||
config EXAMPLE_LVGL_PORT_AVOID_TEAR_ENABLE
|
||||
bool "Avoid tearing effect"
|
||||
default "n"
|
||||
help
|
||||
Avoid tearing effect through LVGL buffer mode and double frame buffers of RGB LCD. This feature is only available for RGB LCD.
|
||||
|
||||
choice
|
||||
depends on EXAMPLE_LVGL_PORT_AVOID_TEAR_ENABLE
|
||||
prompt "Select Avoid Tearing Mode"
|
||||
default EXAMPLE_LVGL_PORT_AVOID_TEAR_MODE_3
|
||||
config EXAMPLE_LVGL_PORT_AVOID_TEAR_MODE_1
|
||||
bool "Mode1: LCD double-buffer & LVGL full-refresh"
|
||||
config EXAMPLE_LVGL_PORT_AVOID_TEAR_MODE_2
|
||||
bool "Mode2: LCD triple-buffer & LVGL full-refresh"
|
||||
config EXAMPLE_LVGL_PORT_AVOID_TEAR_MODE_3
|
||||
bool "Mode3: LCD double-buffer & LVGL direct-mode"
|
||||
help
|
||||
The current tearing prevention mode supports both full refresh mode and direct mode. Tearing prevention mode may consume more PSRAM space
|
||||
endchoice
|
||||
|
||||
config EXAMPLE_LVGL_PORT_AVOID_TEAR_MODE
|
||||
depends on EXAMPLE_LVGL_PORT_AVOID_TEAR_ENABLE
|
||||
int
|
||||
default 1 if EXAMPLE_LVGL_PORT_AVOID_TEAR_MODE_1
|
||||
default 2 if EXAMPLE_LVGL_PORT_AVOID_TEAR_MODE_2
|
||||
default 3 if EXAMPLE_LVGL_PORT_AVOID_TEAR_MODE_3
|
||||
|
||||
choice
|
||||
depends on EXAMPLE_LVGL_PORT_AVOID_TEAR_ENABLE
|
||||
prompt "Select rotation"
|
||||
default EXAMPLE_LVGL_PORT_ROTATION_0
|
||||
config EXAMPLE_LVGL_PORT_ROTATION_0
|
||||
bool "Rotation 0"
|
||||
config EXAMPLE_LVGL_PORT_ROTATION_90
|
||||
bool "Rotation 90"
|
||||
config EXAMPLE_LVGL_PORT_ROTATION_180
|
||||
bool "Rotation 180"
|
||||
config EXAMPLE_LVGL_PORT_ROTATION_270
|
||||
bool "Rotation 270"
|
||||
endchoice
|
||||
|
||||
config EXAMPLE_LVGL_PORT_PPA_ROTATION_ENABLE
|
||||
depends on EXAMPLE_LVGL_PORT_AVOID_TEAR_ENABLE && IDF_TARGET_ESP32P4 && !EXAMPLE_LVGL_PORT_ROTATION_0
|
||||
bool "Enable PPA Rotation"
|
||||
default n
|
||||
help
|
||||
Enable this option to use PPA (Pixel Processor Assembly) for display rotation.
|
||||
This feature allows hardware-based rotation for improved performance
|
||||
|
||||
config EXAMPLE_LVGL_PORT_ROTATION_DEGREE
|
||||
int
|
||||
default 0 if EXAMPLE_LVGL_PORT_ROTATION_0
|
||||
default 90 if EXAMPLE_LVGL_PORT_ROTATION_90
|
||||
default 180 if EXAMPLE_LVGL_PORT_ROTATION_180
|
||||
default 270 if EXAMPLE_LVGL_PORT_ROTATION_270
|
||||
|
||||
choice
|
||||
depends on !EXAMPLE_LVGL_PORT_AVOID_TEAR_ENABLE
|
||||
prompt "Select LVGL buffer memory capability"
|
||||
default EXAMPLE_LVGL_PORT_BUF_INTERNAL
|
||||
config EXAMPLE_LVGL_PORT_BUF_PSRAM
|
||||
bool "PSRAM memory"
|
||||
config EXAMPLE_LVGL_PORT_BUF_INTERNAL
|
||||
bool "Internal memory"
|
||||
endchoice
|
||||
|
||||
config EXAMPLE_LVGL_PORT_BUF_HEIGHT
|
||||
depends on !EXAMPLE_LVGL_PORT_AVOID_TEAR_ENABLE
|
||||
int "LVGL buffer height"
|
||||
default 100
|
||||
help
|
||||
Height of LVGL buffer. The width of the buffer is the same as that of the LCD.
|
||||
endmenu
|
||||
endmenu
|
||||
+268
@@ -0,0 +1,268 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: CC0-1.0
|
||||
*/
|
||||
|
||||
#include "esp_log.h"
|
||||
#include "esp_heap_caps.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "driver/i2c.h"
|
||||
#include "driver/spi_master.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "esp_lcd_panel_ops.h"
|
||||
#include "esp_lcd_panel_io.h"
|
||||
#include "esp_lcd_panel_io_additions.h"
|
||||
#include "esp_lcd_touch_gt911.h"
|
||||
#include "esp_lcd_st7701.h"
|
||||
#include "lv_demos.h"
|
||||
#include "lvgl_port_v8.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////// Please update the following configuration according to your LCD spec //////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#define EXAMPLE_LCD_H_RES (480)
|
||||
#define EXAMPLE_LCD_V_RES (480)
|
||||
#define EXAMPLE_LCD_BIT_PER_PIXEL (18)
|
||||
#define EXAMPLE_RGB_BIT_PER_PIXEL (16)
|
||||
#define EXAMPLE_RGB_DATA_WIDTH (16)
|
||||
#define EXAMPLE_RGB_BOUNCE_BUFFER_SIZE (EXAMPLE_LCD_H_RES * CONFIG_EXAMPLE_LCD_RGB_BOUNCE_BUFFER_HEIGHT)
|
||||
#define EXAMPLE_LCD_IO_RGB_DISP (-1) // -1 if not used
|
||||
#define EXAMPLE_LCD_IO_RGB_VSYNC (GPIO_NUM_17)
|
||||
#define EXAMPLE_LCD_IO_RGB_HSYNC (GPIO_NUM_16)
|
||||
#define EXAMPLE_LCD_IO_RGB_DE (GPIO_NUM_18)
|
||||
#define EXAMPLE_LCD_IO_RGB_PCLK (GPIO_NUM_21)
|
||||
#define EXAMPLE_LCD_IO_RGB_DATA0 (GPIO_NUM_4)
|
||||
#define EXAMPLE_LCD_IO_RGB_DATA1 (GPIO_NUM_5)
|
||||
#define EXAMPLE_LCD_IO_RGB_DATA2 (GPIO_NUM_6)
|
||||
#define EXAMPLE_LCD_IO_RGB_DATA3 (GPIO_NUM_7)
|
||||
#define EXAMPLE_LCD_IO_RGB_DATA4 (GPIO_NUM_15)
|
||||
#define EXAMPLE_LCD_IO_RGB_DATA5 (GPIO_NUM_8)
|
||||
#define EXAMPLE_LCD_IO_RGB_DATA6 (GPIO_NUM_20)
|
||||
#define EXAMPLE_LCD_IO_RGB_DATA7 (GPIO_NUM_3)
|
||||
#define EXAMPLE_LCD_IO_RGB_DATA8 (GPIO_NUM_46)
|
||||
#define EXAMPLE_LCD_IO_RGB_DATA9 (GPIO_NUM_9)
|
||||
#define EXAMPLE_LCD_IO_RGB_DATA10 (GPIO_NUM_10)
|
||||
#define EXAMPLE_LCD_IO_RGB_DATA11 (GPIO_NUM_11)
|
||||
#define EXAMPLE_LCD_IO_RGB_DATA12 (GPIO_NUM_12)
|
||||
#define EXAMPLE_LCD_IO_RGB_DATA13 (GPIO_NUM_13)
|
||||
#define EXAMPLE_LCD_IO_RGB_DATA14 (GPIO_NUM_14)
|
||||
#define EXAMPLE_LCD_IO_RGB_DATA15 (GPIO_NUM_0)
|
||||
#define EXAMPLE_LCD_IO_SPI_CS (GPIO_NUM_39)
|
||||
#define EXAMPLE_LCD_IO_SPI_SCL (GPIO_NUM_48)
|
||||
#define EXAMPLE_LCD_IO_SPI_SDA (GPIO_NUM_47)
|
||||
#define EXAMPLE_LCD_IO_RST (-1) // -1 if not used
|
||||
#define EXAMPLE_PIN_NUM_BK_LIGHT (GPIO_NUM_38) // -1 if not used
|
||||
#define EXAMPLE_LCD_BK_LIGHT_ON_LEVEL (1)
|
||||
#define EXAMPLE_LCD_BK_LIGHT_OFF_LEVEL !EXAMPLE_LCD_BK_LIGHT_ON_LEVEL
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////// Please update the following configuration according to your touch spec ////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#if CONFIG_EXAMPLE_LCD_TOUCH_CONTROLLER_GT911
|
||||
#define TOUCH_HOST (I2C_NUM_0)
|
||||
#define EXAMPLE_PIN_NUM_TOUCH_SCL (GPIO_NUM_45)
|
||||
#define EXAMPLE_PIN_NUM_TOUCH_SDA (GPIO_NUM_19)
|
||||
#define EXAMPLE_PIN_NUM_TOUCH_RST (-1) // -1 if not used
|
||||
#define EXAMPLE_PIN_NUM_TOUCH_INT (-1) // -1 if not used
|
||||
#endif
|
||||
|
||||
static const char *TAG = "example";
|
||||
|
||||
IRAM_ATTR static bool rgb_lcd_on_vsync_event(esp_lcd_panel_handle_t panel, const esp_lcd_rgb_panel_event_data_t *edata, void *user_ctx)
|
||||
{
|
||||
return lvgl_port_notify_lcd_vsync();
|
||||
}
|
||||
|
||||
static const st7701_lcd_init_cmd_t lcd_init_cmds[] = {
|
||||
// {cmd, { data }, data_size, delay_ms}
|
||||
{0xFF, (uint8_t []){0x77, 0x01, 0x00, 0x00, 0x10}, 5, 0},
|
||||
{0xC0, (uint8_t []){0x3B, 0x00}, 2, 0},
|
||||
{0xC1, (uint8_t []){0x0D, 0x02}, 2, 0},
|
||||
{0xC2, (uint8_t []){0x31, 0x05}, 2, 0},
|
||||
{0xCD, (uint8_t []){0x00}, 1, 0},
|
||||
{0xB0, (uint8_t []){0x00, 0x11, 0x18, 0x0E, 0x11, 0x06, 0x07, 0x08, 0x07, 0x22, 0x04, 0x12, 0x0F, 0xAA, 0x31, 0x18}, 16, 0},
|
||||
{0xB1, (uint8_t []){0x00, 0x11, 0x19, 0x0E, 0x12, 0x07, 0x08, 0x08, 0x08, 0x22, 0x04, 0x11, 0x11, 0xA9, 0x32, 0x18}, 16, 0},
|
||||
{0xFF, (uint8_t []){0x77, 0x01, 0x00, 0x00, 0x11}, 5, 0},
|
||||
{0xB0, (uint8_t []){0x60}, 1, 0},
|
||||
{0xB1, (uint8_t []){0x32}, 1, 0},
|
||||
{0xB2, (uint8_t []){0x07}, 1, 0},
|
||||
{0xB3, (uint8_t []){0x80}, 1, 0},
|
||||
{0xB5, (uint8_t []){0x49}, 1, 0},
|
||||
{0xB7, (uint8_t []){0x85}, 1, 0},
|
||||
{0xB8, (uint8_t []){0x21}, 1, 0},
|
||||
{0xC1, (uint8_t []){0x78}, 1, 0},
|
||||
{0xC2, (uint8_t []){0x78}, 1, 0},
|
||||
{0xE0, (uint8_t []){0x00, 0x1B, 0x02}, 3, 0},
|
||||
{0xE1, (uint8_t []){0x08, 0xA0, 0x00, 0x00, 0x07, 0xA0, 0x00, 0x00, 0x00, 0x44, 0x44}, 11, 0},
|
||||
{0xE2, (uint8_t []){0x11, 0x11, 0x44, 0x44, 0xED, 0xA0, 0x00, 0x00, 0xEC, 0xA0, 0x00, 0x00}, 12, 0},
|
||||
{0xE3, (uint8_t []){0x00, 0x00, 0x11, 0x11}, 4, 0},
|
||||
{0xE4, (uint8_t []){0x44, 0x44}, 2, 0},
|
||||
{0xE5, (uint8_t []){0x0A, 0xE9, 0xD8, 0xA0, 0x0C, 0xEB, 0xD8, 0xA0, 0x0E, 0xED, 0xD8, 0xA0, 0x10, 0xEF, 0xD8, 0xA0}, 16, 0},
|
||||
{0xE6, (uint8_t []){0x00, 0x00, 0x11, 0x11}, 4, 0},
|
||||
{0xE7, (uint8_t []){0x44, 0x44}, 2, 0},
|
||||
{0xE8, (uint8_t []){0x09, 0xE8, 0xD8, 0xA0, 0x0B, 0xEA, 0xD8, 0xA0, 0x0D, 0xEC, 0xD8, 0xA0, 0x0F, 0xEE, 0xD8, 0xA0}, 16, 0},
|
||||
{0xEB, (uint8_t []){0x02, 0x00, 0xE4, 0xE4, 0x88, 0x00, 0x40}, 7, 0},
|
||||
{0xEC, (uint8_t []){0x3C, 0x00}, 2, 0},
|
||||
{0xED, (uint8_t []){0xAB, 0x89, 0x76, 0x54, 0x02, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x20, 0x45, 0x67, 0x98, 0xBA}, 16, 0},
|
||||
{0xFF, (uint8_t []){0x77, 0x01, 0x00, 0x00, 0x13}, 5, 0},
|
||||
{0xE5, (uint8_t []){0xE4}, 1, 0},
|
||||
{0xFF, (uint8_t []){0x77, 0x01, 0x00, 0x00, 0x00}, 5, 0},
|
||||
{0x11, (uint8_t []){0x00}, 0, 120},
|
||||
{0x29, (uint8_t []){0x00}, 0, 0},
|
||||
};
|
||||
|
||||
void app_main()
|
||||
{
|
||||
#if EXAMPLE_PIN_NUM_BK_LIGHT >= 0
|
||||
ESP_LOGI(TAG, "Turn off LCD backlight");
|
||||
gpio_config_t bk_gpio_config = {
|
||||
.mode = GPIO_MODE_OUTPUT,
|
||||
.pin_bit_mask = 1ULL << EXAMPLE_PIN_NUM_BK_LIGHT
|
||||
};
|
||||
ESP_ERROR_CHECK(gpio_config(&bk_gpio_config));
|
||||
#endif
|
||||
|
||||
ESP_LOGI(TAG, "Install 3-wire SPI panel IO");
|
||||
spi_line_config_t line_config = {
|
||||
.cs_io_type = IO_TYPE_GPIO,
|
||||
.cs_gpio_num = EXAMPLE_LCD_IO_SPI_CS,
|
||||
.scl_io_type = IO_TYPE_GPIO,
|
||||
.scl_gpio_num = EXAMPLE_LCD_IO_SPI_SCL,
|
||||
.sda_io_type = IO_TYPE_GPIO,
|
||||
.sda_gpio_num = EXAMPLE_LCD_IO_SPI_SDA,
|
||||
.io_expander = NULL,
|
||||
};
|
||||
esp_lcd_panel_io_3wire_spi_config_t io_config = ST7701_PANEL_IO_3WIRE_SPI_CONFIG(line_config, 0);
|
||||
esp_lcd_panel_io_handle_t io_handle = NULL;
|
||||
ESP_ERROR_CHECK(esp_lcd_new_panel_io_3wire_spi(&io_config, &io_handle));
|
||||
|
||||
ESP_LOGI(TAG, "Install ST7701 panel driver");
|
||||
esp_lcd_panel_handle_t lcd_handle = NULL;
|
||||
esp_lcd_rgb_panel_config_t rgb_config = {
|
||||
.clk_src = LCD_CLK_SRC_DEFAULT,
|
||||
.psram_trans_align = 64,
|
||||
.data_width = EXAMPLE_RGB_DATA_WIDTH,
|
||||
.bits_per_pixel = EXAMPLE_RGB_BIT_PER_PIXEL,
|
||||
.de_gpio_num = EXAMPLE_LCD_IO_RGB_DE,
|
||||
.pclk_gpio_num = EXAMPLE_LCD_IO_RGB_PCLK,
|
||||
.vsync_gpio_num = EXAMPLE_LCD_IO_RGB_VSYNC,
|
||||
.hsync_gpio_num = EXAMPLE_LCD_IO_RGB_HSYNC,
|
||||
.disp_gpio_num = EXAMPLE_LCD_IO_RGB_DISP,
|
||||
.data_gpio_nums = {
|
||||
EXAMPLE_LCD_IO_RGB_DATA0,
|
||||
EXAMPLE_LCD_IO_RGB_DATA1,
|
||||
EXAMPLE_LCD_IO_RGB_DATA2,
|
||||
EXAMPLE_LCD_IO_RGB_DATA3,
|
||||
EXAMPLE_LCD_IO_RGB_DATA4,
|
||||
EXAMPLE_LCD_IO_RGB_DATA5,
|
||||
EXAMPLE_LCD_IO_RGB_DATA6,
|
||||
EXAMPLE_LCD_IO_RGB_DATA7,
|
||||
EXAMPLE_LCD_IO_RGB_DATA8,
|
||||
EXAMPLE_LCD_IO_RGB_DATA9,
|
||||
EXAMPLE_LCD_IO_RGB_DATA10,
|
||||
EXAMPLE_LCD_IO_RGB_DATA11,
|
||||
EXAMPLE_LCD_IO_RGB_DATA12,
|
||||
EXAMPLE_LCD_IO_RGB_DATA13,
|
||||
EXAMPLE_LCD_IO_RGB_DATA14,
|
||||
EXAMPLE_LCD_IO_RGB_DATA15,
|
||||
},
|
||||
.timings = ST7701_480_480_PANEL_60HZ_RGB_TIMING(),
|
||||
.flags.fb_in_psram = 1,
|
||||
.num_fbs = LVGL_PORT_LCD_BUFFER_NUMS,
|
||||
.bounce_buffer_size_px = EXAMPLE_RGB_BOUNCE_BUFFER_SIZE,
|
||||
};
|
||||
rgb_config.timings.h_res = EXAMPLE_LCD_H_RES;
|
||||
rgb_config.timings.v_res = EXAMPLE_LCD_V_RES;
|
||||
st7701_vendor_config_t vendor_config = {
|
||||
.rgb_config = &rgb_config,
|
||||
.init_cmds = lcd_init_cmds, // Uncomment these line if use custom initialization commands
|
||||
.init_cmds_size = sizeof(lcd_init_cmds) / sizeof(lcd_init_cmds[0]),
|
||||
.flags = {
|
||||
.auto_del_panel_io = 0, /**
|
||||
* Set to 1 if panel IO is no longer needed after LCD initialization.
|
||||
* If the panel IO pins are sharing other pins of the RGB interface to save GPIOs,
|
||||
* Please set it to 1 to release the pins.
|
||||
*/
|
||||
.mirror_by_cmd = 1, // Set to 0 if `auto_del_panel_io` is enabled
|
||||
},
|
||||
};
|
||||
const esp_lcd_panel_dev_config_t panel_config = {
|
||||
.reset_gpio_num = EXAMPLE_LCD_IO_RST,
|
||||
.rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB,
|
||||
.bits_per_pixel = EXAMPLE_LCD_BIT_PER_PIXEL,
|
||||
.vendor_config = &vendor_config,
|
||||
};
|
||||
ESP_ERROR_CHECK(esp_lcd_new_panel_st7701(io_handle, &panel_config, &lcd_handle));
|
||||
ESP_ERROR_CHECK(esp_lcd_panel_reset(lcd_handle));
|
||||
ESP_ERROR_CHECK(esp_lcd_panel_init(lcd_handle));
|
||||
ESP_ERROR_CHECK(esp_lcd_panel_disp_on_off(lcd_handle, true));
|
||||
|
||||
esp_lcd_rgb_panel_event_callbacks_t cbs = {
|
||||
#if EXAMPLE_RGB_BOUNCE_BUFFER_SIZE > 0
|
||||
.on_bounce_frame_finish = rgb_lcd_on_vsync_event,
|
||||
#else
|
||||
.on_vsync = rgb_lcd_on_vsync_event,
|
||||
#endif
|
||||
};
|
||||
esp_lcd_rgb_panel_register_event_callbacks(lcd_handle, &cbs, NULL);
|
||||
|
||||
esp_lcd_touch_handle_t tp_handle = NULL;
|
||||
#if CONFIG_EXAMPLE_LCD_TOUCH_CONTROLLER_GT911
|
||||
ESP_LOGI(TAG, "Initialize I2C bus");
|
||||
const i2c_config_t i2c_conf = {
|
||||
.mode = I2C_MODE_MASTER,
|
||||
.sda_io_num = EXAMPLE_PIN_NUM_TOUCH_SDA,
|
||||
.sda_pullup_en = GPIO_PULLUP_ENABLE,
|
||||
.scl_io_num = EXAMPLE_PIN_NUM_TOUCH_SCL,
|
||||
.scl_pullup_en = GPIO_PULLUP_ENABLE,
|
||||
.master.clk_speed = 400 * 1000,
|
||||
};
|
||||
ESP_ERROR_CHECK(i2c_param_config(TOUCH_HOST, &i2c_conf));
|
||||
ESP_ERROR_CHECK(i2c_driver_install(TOUCH_HOST, i2c_conf.mode, 0, 0, 0));
|
||||
|
||||
esp_lcd_panel_io_handle_t tp_io_handle = NULL;
|
||||
const esp_lcd_panel_io_i2c_config_t tp_io_config = ESP_LCD_TOUCH_IO_I2C_GT911_CONFIG();
|
||||
|
||||
ESP_LOGI(TAG, "Initialize I2C panel IO");
|
||||
ESP_ERROR_CHECK(esp_lcd_new_panel_io_i2c((esp_lcd_i2c_bus_handle_t)TOUCH_HOST, &tp_io_config, &tp_io_handle));
|
||||
|
||||
ESP_LOGI(TAG, "Initialize touch controller GT911");
|
||||
const esp_lcd_touch_config_t tp_cfg = {
|
||||
.x_max = EXAMPLE_LCD_H_RES,
|
||||
.y_max = EXAMPLE_LCD_V_RES,
|
||||
.rst_gpio_num = EXAMPLE_PIN_NUM_TOUCH_RST,
|
||||
.int_gpio_num = EXAMPLE_PIN_NUM_TOUCH_INT,
|
||||
.levels = {
|
||||
.reset = 0,
|
||||
.interrupt = 0,
|
||||
},
|
||||
.flags = {
|
||||
.swap_xy = 0,
|
||||
.mirror_x = 0,
|
||||
.mirror_y = 0,
|
||||
},
|
||||
};
|
||||
ESP_ERROR_CHECK(esp_lcd_touch_new_i2c_gt911(tp_io_handle, &tp_cfg, &tp_handle));
|
||||
#endif // CONFIG_EXAMPLE_LCD_TOUCH_CONTROLLER_GT911
|
||||
|
||||
ESP_ERROR_CHECK(lvgl_port_init(lcd_handle, tp_handle, LVGL_PORT_INTERFACE_RGB));
|
||||
|
||||
#if EXAMPLE_PIN_NUM_BK_LIGHT >= 0
|
||||
ESP_LOGI(TAG, "Turn on LCD backlight");
|
||||
gpio_set_level(EXAMPLE_PIN_NUM_BK_LIGHT, EXAMPLE_LCD_BK_LIGHT_ON_LEVEL);
|
||||
#endif
|
||||
|
||||
ESP_LOGI(TAG, "Display LVGL demos");
|
||||
// Lock the mutex due to the LVGL APIs are not thread-safe
|
||||
if (lvgl_port_lock(-1)) {
|
||||
// lv_demo_stress();
|
||||
// lv_demo_benchmark();
|
||||
lv_demo_music();
|
||||
// lv_demo_widgets();
|
||||
|
||||
// Release the mutex
|
||||
lvgl_port_unlock();
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
dependencies:
|
||||
esp_lcd_panel_io_additions:
|
||||
version: '*'
|
||||
esp_lcd_st7701:
|
||||
version: '*'
|
||||
esp_lcd_touch_gt911: ^1
|
||||
idf:
|
||||
version: '>=5.3.0'
|
||||
lvgl/lvgl:
|
||||
public: true
|
||||
version: '>8.3.9,<9'
|
||||
+686
@@ -0,0 +1,686 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "soc/soc_caps.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "freertos/task.h"
|
||||
#include "esp_heap_caps.h"
|
||||
#include "esp_lcd_panel_ops.h"
|
||||
#if SOC_LCDCAM_RGB_LCD_SUPPORTED
|
||||
#include "esp_lcd_panel_rgb.h"
|
||||
#endif
|
||||
#if SOC_MIPI_DSI_SUPPORTED
|
||||
#include "esp_lcd_mipi_dsi.h"
|
||||
#endif
|
||||
#include "esp_lcd_touch.h"
|
||||
#include "esp_timer.h"
|
||||
#include "esp_log.h"
|
||||
#if CONFIG_IDF_TARGET_ESP32P4
|
||||
#include "esp_private/esp_cache_private.h"
|
||||
#include "driver/ppa.h"
|
||||
#endif
|
||||
#include "lvgl.h"
|
||||
#include "lvgl_port_v8.h"
|
||||
|
||||
#define ALIGN_UP_BY(num, align) (((num) + ((align) - 1)) & ~((align) - 1))
|
||||
#define BLOCK_SIZE_SMALL (32)
|
||||
#define BLOCK_SIZE_LARGE (256)
|
||||
|
||||
static const char *TAG = "lv_port";
|
||||
|
||||
typedef esp_err_t (*get_lcd_frame_buffer_cb_t)(esp_lcd_panel_handle_t panel, uint32_t fb_num, void **fb0, ...);
|
||||
|
||||
#if LVGL_PORT_PPA_ROTATION_ENABLE
|
||||
static ppa_client_handle_t ppa_srm_handle = NULL;
|
||||
static size_t data_cache_line_size = 0;
|
||||
#endif
|
||||
|
||||
static SemaphoreHandle_t lvgl_mux; // LVGL mutex
|
||||
static TaskHandle_t lvgl_task_handle = NULL;
|
||||
static lvgl_port_interface_t lvgl_port_interface = LVGL_PORT_INTERFACE_RGB;
|
||||
|
||||
#if LVGL_PORT_AVOID_TEAR_ENABLE
|
||||
static get_lcd_frame_buffer_cb_t lvgl_get_lcd_frame_buffer = NULL;
|
||||
#endif
|
||||
|
||||
#if EXAMPLE_LVGL_PORT_ROTATION_DEGREE != 0
|
||||
static void *get_next_frame_buffer(esp_lcd_panel_handle_t panel_handle)
|
||||
{
|
||||
static void *next_fb = NULL;
|
||||
static void *fb[2] = { NULL };
|
||||
if (next_fb == NULL) {
|
||||
ESP_ERROR_CHECK(lvgl_get_lcd_frame_buffer(panel_handle, 2, &fb[0], &fb[1]));
|
||||
next_fb = fb[1];
|
||||
} else {
|
||||
next_fb = (next_fb == fb[0]) ? fb[1] : fb[0];
|
||||
}
|
||||
return next_fb;
|
||||
}
|
||||
|
||||
#if !LVGL_PORT_PPA_ROTATION_ENABLE
|
||||
static void rotate_image(const void *src, void *dst, int width, int height, int rotation, int bpp)
|
||||
{
|
||||
int bytes_per_pixel = bpp / 8;
|
||||
int block_w = rotation == 90 || rotation == 270 ? BLOCK_SIZE_SMALL : BLOCK_SIZE_LARGE;
|
||||
int block_h = rotation == 90 || rotation == 270 ? BLOCK_SIZE_LARGE : BLOCK_SIZE_SMALL;
|
||||
|
||||
for (int i = 0; i < height; i += block_h) {
|
||||
int max_height = i + block_h > height ? height : i + block_h;
|
||||
|
||||
for (int j = 0; j < width; j += block_w) {
|
||||
int max_width = j + block_w > width ? width : j + block_w;
|
||||
|
||||
for (int x = i; x < max_height; x++) {
|
||||
for (int y = j; y < max_width; y++) {
|
||||
void *src_pixel = (uint8_t *)src + (x * width + y) * bytes_per_pixel;
|
||||
void *dst_pixel;
|
||||
|
||||
switch (rotation) {
|
||||
case 270:
|
||||
dst_pixel = (uint8_t *)dst + ((width - 1 - y) * height + x) * bytes_per_pixel;
|
||||
break;
|
||||
case 180:
|
||||
dst_pixel = (uint8_t *)dst + ((height - 1 - x) * width + (width - 1 - y)) * bytes_per_pixel;
|
||||
break;
|
||||
case 90:
|
||||
dst_pixel = (uint8_t *)dst + (y * height + (height - 1 - x)) * bytes_per_pixel;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
if (bpp == 16) {
|
||||
*(uint16_t *)dst_pixel = *(uint16_t *)src_pixel;
|
||||
} else if (bpp == 24) {
|
||||
((uint8_t *)dst_pixel)[0] = ((uint8_t *)src_pixel)[0];
|
||||
((uint8_t *)dst_pixel)[1] = ((uint8_t *)src_pixel)[1];
|
||||
((uint8_t *)dst_pixel)[2] = ((uint8_t *)src_pixel)[2];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
IRAM_ATTR static void rotate_copy_pixel(const uint16_t *from, uint16_t *to, uint16_t x_start, uint16_t y_start, uint16_t x_end, uint16_t y_end, uint16_t w, uint16_t h, uint16_t rotation)
|
||||
{
|
||||
#if LVGL_PORT_PPA_ROTATION_ENABLE
|
||||
ppa_srm_rotation_angle_t ppa_rotation;
|
||||
int x_offset = 0, y_offset = 0;
|
||||
|
||||
// Determine rotation settings once and reuse
|
||||
switch (rotation) {
|
||||
case 90:
|
||||
ppa_rotation = PPA_SRM_ROTATION_ANGLE_270;
|
||||
x_offset = h - y_end - 1;
|
||||
y_offset = x_start;
|
||||
break;
|
||||
case 180:
|
||||
ppa_rotation = PPA_SRM_ROTATION_ANGLE_180;
|
||||
x_offset = w - x_end - 1;
|
||||
y_offset = h - y_end - 1;
|
||||
break;
|
||||
case 270:
|
||||
ppa_rotation = PPA_SRM_ROTATION_ANGLE_90;
|
||||
x_offset = y_start;
|
||||
y_offset = w - x_end - 1;
|
||||
break;
|
||||
default:
|
||||
ppa_rotation = PPA_SRM_ROTATION_ANGLE_0;
|
||||
break;
|
||||
}
|
||||
|
||||
// Fill operation config for PPA rotation, without recalculating each time
|
||||
ppa_srm_oper_config_t oper_config = {
|
||||
.in.buffer = from,
|
||||
.in.pic_w = w,
|
||||
.in.pic_h = h,
|
||||
.in.block_w = x_end - x_start + 1,
|
||||
.in.block_h = y_end - y_start + 1,
|
||||
.in.block_offset_x = x_start,
|
||||
.in.block_offset_y = y_start,
|
||||
.in.srm_cm = (LV_COLOR_DEPTH == 24) ? PPA_SRM_COLOR_MODE_RGB888 : PPA_SRM_COLOR_MODE_RGB565,
|
||||
|
||||
.out.buffer = to,
|
||||
.out.buffer_size = ALIGN_UP_BY(sizeof(lv_color_t) * w * h, data_cache_line_size),
|
||||
.out.pic_w = (ppa_rotation == PPA_SRM_ROTATION_ANGLE_90 || ppa_rotation == PPA_SRM_ROTATION_ANGLE_270) ? h : w,
|
||||
.out.pic_h = (ppa_rotation == PPA_SRM_ROTATION_ANGLE_90 || ppa_rotation == PPA_SRM_ROTATION_ANGLE_270) ? w : h,
|
||||
.out.block_offset_x = x_offset,
|
||||
.out.block_offset_y = y_offset,
|
||||
.out.srm_cm = (LV_COLOR_DEPTH == 24) ? PPA_SRM_COLOR_MODE_RGB888 : PPA_SRM_COLOR_MODE_RGB565,
|
||||
|
||||
.rotation_angle = ppa_rotation,
|
||||
.scale_x = 1.0,
|
||||
.scale_y = 1.0,
|
||||
.rgb_swap = 0,
|
||||
.byte_swap = 0,
|
||||
.mode = PPA_TRANS_MODE_BLOCKING,
|
||||
};
|
||||
|
||||
ESP_ERROR_CHECK(ppa_do_scale_rotate_mirror(ppa_srm_handle, &oper_config));
|
||||
|
||||
#else
|
||||
// Fallback: optimized transpose for non-PPA systems
|
||||
rotate_image(from, to, w, h, rotation, LV_COLOR_DEPTH);
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif /* EXAMPLE_LVGL_PORT_ROTATION_DEGREE */
|
||||
|
||||
#if LVGL_PORT_AVOID_TEAR_ENABLE
|
||||
|
||||
static void switch_lcd_frame_buffer_to(esp_lcd_panel_handle_t panel_handle, void *fb)
|
||||
{
|
||||
esp_lcd_panel_draw_bitmap(panel_handle, 0, 0, LVGL_PORT_H_RES, LVGL_PORT_V_RES, fb);
|
||||
}
|
||||
|
||||
#if LVGL_PORT_DIRECT_MODE
|
||||
#if EXAMPLE_LVGL_PORT_ROTATION_DEGREE != 0
|
||||
typedef struct {
|
||||
uint16_t inv_p;
|
||||
uint8_t inv_area_joined[LV_INV_BUF_SIZE];
|
||||
lv_area_t inv_areas[LV_INV_BUF_SIZE];
|
||||
} lv_port_dirty_area_t;
|
||||
|
||||
typedef enum {
|
||||
FLUSH_STATUS_PART,
|
||||
FLUSH_STATUS_FULL
|
||||
} lv_port_flush_status_t;
|
||||
|
||||
typedef enum {
|
||||
FLUSH_PROBE_PART_COPY,
|
||||
FLUSH_PROBE_SKIP_COPY,
|
||||
FLUSH_PROBE_FULL_COPY,
|
||||
} lv_port_flush_probe_t;
|
||||
|
||||
static lv_port_dirty_area_t dirty_area;
|
||||
|
||||
static void flush_dirty_save(lv_port_dirty_area_t *dirty_area)
|
||||
{
|
||||
lv_disp_t *disp = _lv_refr_get_disp_refreshing();
|
||||
dirty_area->inv_p = disp->inv_p;
|
||||
for (int i = 0; i < disp->inv_p; i++) {
|
||||
dirty_area->inv_area_joined[i] = disp->inv_area_joined[i];
|
||||
dirty_area->inv_areas[i] = disp->inv_areas[i];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Probe dirty area to copy
|
||||
*
|
||||
* @note This function is used to avoid tearing effect, and only work with LVGL direct-mode.
|
||||
*
|
||||
*/
|
||||
static lv_port_flush_probe_t flush_copy_probe(lv_disp_drv_t *drv)
|
||||
{
|
||||
static lv_port_flush_status_t prev_status = FLUSH_STATUS_PART;
|
||||
lv_port_flush_status_t cur_status;
|
||||
lv_port_flush_probe_t probe_result;
|
||||
lv_disp_t *disp_refr = _lv_refr_get_disp_refreshing();
|
||||
|
||||
uint32_t flush_ver = 0;
|
||||
uint32_t flush_hor = 0;
|
||||
for (int i = 0; i < disp_refr->inv_p; i++) {
|
||||
if (disp_refr->inv_area_joined[i] == 0) {
|
||||
flush_ver = (disp_refr->inv_areas[i].y2 + 1 - disp_refr->inv_areas[i].y1);
|
||||
flush_hor = (disp_refr->inv_areas[i].x2 + 1 - disp_refr->inv_areas[i].x1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* Check if the current full screen refreshes */
|
||||
cur_status = ((flush_ver == drv->ver_res) && (flush_hor == drv->hor_res)) ? (FLUSH_STATUS_FULL) : (FLUSH_STATUS_PART);
|
||||
|
||||
if (prev_status == FLUSH_STATUS_FULL) {
|
||||
if ((cur_status == FLUSH_STATUS_PART)) {
|
||||
probe_result = FLUSH_PROBE_FULL_COPY;
|
||||
} else {
|
||||
probe_result = FLUSH_PROBE_SKIP_COPY;
|
||||
}
|
||||
} else {
|
||||
probe_result = FLUSH_PROBE_PART_COPY;
|
||||
}
|
||||
prev_status = cur_status;
|
||||
|
||||
return probe_result;
|
||||
}
|
||||
|
||||
static inline void *flush_get_next_buf(void *panel_handle)
|
||||
{
|
||||
return get_next_frame_buffer(panel_handle);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Copy dirty area
|
||||
*
|
||||
* @note This function is used to avoid tearing effect, and only work with LVGL direct-mode.
|
||||
*
|
||||
*/
|
||||
static void flush_dirty_copy(void *dst, void *src, lv_port_dirty_area_t *dirty_area)
|
||||
{
|
||||
lv_coord_t x_start, x_end, y_start, y_end;
|
||||
for (int i = 0; i < dirty_area->inv_p; i++) {
|
||||
/* Refresh the unjoined areas*/
|
||||
if (dirty_area->inv_area_joined[i] == 0) {
|
||||
x_start = dirty_area->inv_areas[i].x1;
|
||||
x_end = dirty_area->inv_areas[i].x2;
|
||||
y_start = dirty_area->inv_areas[i].y1;
|
||||
y_end = dirty_area->inv_areas[i].y2;
|
||||
|
||||
rotate_copy_pixel(src, dst, x_start, y_start, x_end, y_end, LV_HOR_RES, LV_VER_RES, EXAMPLE_LVGL_PORT_ROTATION_DEGREE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void flush_callback(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *color_map)
|
||||
{
|
||||
esp_lcd_panel_handle_t panel_handle = (esp_lcd_panel_handle_t) drv->user_data;
|
||||
const int offsetx1 = area->x1;
|
||||
const int offsetx2 = area->x2;
|
||||
const int offsety1 = area->y1;
|
||||
const int offsety2 = area->y2;
|
||||
void *next_fb = NULL;
|
||||
lv_port_flush_probe_t probe_result = FLUSH_PROBE_PART_COPY;
|
||||
lv_disp_t *disp = lv_disp_get_default();
|
||||
|
||||
/* Action after last area refresh */
|
||||
if (lv_disp_flush_is_last(drv)) {
|
||||
/* Check if the `full_refresh` flag has been triggered */
|
||||
if (drv->full_refresh) {
|
||||
/* Reset flag */
|
||||
drv->full_refresh = 0;
|
||||
|
||||
// Rotate and copy data from the whole screen LVGL's buffer to the next frame buffer
|
||||
next_fb = flush_get_next_buf(panel_handle);
|
||||
rotate_copy_pixel((uint16_t *)color_map, next_fb, offsetx1, offsety1, offsetx2, offsety2, LV_HOR_RES, LV_VER_RES, EXAMPLE_LVGL_PORT_ROTATION_DEGREE);
|
||||
|
||||
/* Switch the current LCD frame buffer to `next_fb` */
|
||||
switch_lcd_frame_buffer_to(panel_handle, next_fb);
|
||||
|
||||
/* Waiting for the current frame buffer to complete transmission */
|
||||
ulTaskNotifyValueClear(NULL, ULONG_MAX);
|
||||
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
|
||||
|
||||
/* Synchronously update the dirty area for another frame buffer */
|
||||
flush_dirty_copy(flush_get_next_buf(panel_handle), color_map, &dirty_area);
|
||||
flush_get_next_buf(panel_handle);
|
||||
} else {
|
||||
/* Probe the copy method for the current dirty area */
|
||||
probe_result = flush_copy_probe(drv);
|
||||
|
||||
if (probe_result == FLUSH_PROBE_FULL_COPY) {
|
||||
/* Save current dirty area for next frame buffer */
|
||||
flush_dirty_save(&dirty_area);
|
||||
|
||||
/* Set LVGL full-refresh flag and set flush ready in advance */
|
||||
drv->full_refresh = 1;
|
||||
disp->rendering_in_progress = false;
|
||||
lv_disp_flush_ready(drv);
|
||||
|
||||
/* Force to refresh whole screen, and will invoke `flush_callback` recursively */
|
||||
lv_refr_now(_lv_refr_get_disp_refreshing());
|
||||
} else {
|
||||
/* Update current dirty area for next frame buffer */
|
||||
next_fb = flush_get_next_buf(panel_handle);
|
||||
flush_dirty_save(&dirty_area);
|
||||
flush_dirty_copy(next_fb, color_map, &dirty_area);
|
||||
|
||||
/* Switch the current LCD frame buffer to `next_fb` */
|
||||
switch_lcd_frame_buffer_to(panel_handle, next_fb);
|
||||
|
||||
/* Waiting for the current frame buffer to complete transmission */
|
||||
ulTaskNotifyValueClear(NULL, ULONG_MAX);
|
||||
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
|
||||
|
||||
if (probe_result == FLUSH_PROBE_PART_COPY) {
|
||||
/* Synchronously update the dirty area for another frame buffer */
|
||||
flush_dirty_save(&dirty_area);
|
||||
flush_dirty_copy(flush_get_next_buf(panel_handle), color_map, &dirty_area);
|
||||
flush_get_next_buf(panel_handle);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lv_disp_flush_ready(drv);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static void flush_callback(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *color_map)
|
||||
{
|
||||
esp_lcd_panel_handle_t panel_handle = (esp_lcd_panel_handle_t) drv->user_data;
|
||||
|
||||
/* Action after last area refresh */
|
||||
if (lv_disp_flush_is_last(drv)) {
|
||||
/* Switch the current LCD frame buffer to `color_map` */
|
||||
switch_lcd_frame_buffer_to(panel_handle, color_map);
|
||||
|
||||
/* Waiting for the last frame buffer to complete transmission */
|
||||
ulTaskNotifyValueClear(NULL, ULONG_MAX);
|
||||
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
|
||||
}
|
||||
|
||||
lv_disp_flush_ready(drv);
|
||||
}
|
||||
#endif /* EXAMPLE_LVGL_PORT_ROTATION_DEGREE */
|
||||
|
||||
#elif LVGL_PORT_FULL_REFRESH && LVGL_PORT_LCD_BUFFER_NUMS == 2
|
||||
|
||||
static void flush_callback(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *color_map)
|
||||
{
|
||||
esp_lcd_panel_handle_t panel_handle = (esp_lcd_panel_handle_t) drv->user_data;
|
||||
|
||||
/* Switch the current LCD frame buffer to `color_map` */
|
||||
switch_lcd_frame_buffer_to(panel_handle, color_map);
|
||||
|
||||
/* Waiting for the last frame buffer to complete transmission */
|
||||
ulTaskNotifyValueClear(NULL, ULONG_MAX);
|
||||
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
|
||||
|
||||
lv_disp_flush_ready(drv);
|
||||
}
|
||||
|
||||
#elif LVGL_PORT_FULL_REFRESH && LVGL_PORT_LCD_BUFFER_NUMS == 3
|
||||
|
||||
#if EXAMPLE_LVGL_PORT_ROTATION_DEGREE == 0
|
||||
static void *lvgl_port_rgb_last_buf = NULL;
|
||||
static void *lvgl_port_rgb_next_buf = NULL;
|
||||
static void *lvgl_port_flush_next_buf = NULL;
|
||||
#endif
|
||||
|
||||
void flush_callback(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *color_map)
|
||||
{
|
||||
esp_lcd_panel_handle_t panel_handle = (esp_lcd_panel_handle_t) drv->user_data;
|
||||
|
||||
#if EXAMPLE_LVGL_PORT_ROTATION_DEGREE != 0
|
||||
const int offsetx1 = area->x1;
|
||||
const int offsetx2 = area->x2;
|
||||
const int offsety1 = area->y1;
|
||||
const int offsety2 = area->y2;
|
||||
void *next_fb = get_next_frame_buffer(panel_handle);
|
||||
|
||||
/* Rotate and copy dirty area from the current LVGL's buffer to the next LCD frame buffer */
|
||||
rotate_copy_pixel((uint16_t *)color_map, next_fb, offsetx1, offsety1, offsetx2, offsety2, LV_HOR_RES, LV_VER_RES, EXAMPLE_LVGL_PORT_ROTATION_DEGREE);
|
||||
|
||||
/* Switch the current LCD frame buffer to `next_fb` */
|
||||
switch_lcd_frame_buffer_to(panel_handle, next_fb);
|
||||
#else
|
||||
drv->draw_buf->buf1 = color_map;
|
||||
drv->draw_buf->buf2 = lvgl_port_flush_next_buf;
|
||||
lvgl_port_flush_next_buf = color_map;
|
||||
|
||||
/* Switch the current LCD frame buffer to `color_map` */
|
||||
switch_lcd_frame_buffer_to(panel_handle, color_map);
|
||||
|
||||
lvgl_port_rgb_next_buf = color_map;
|
||||
#endif
|
||||
|
||||
lv_disp_flush_ready(drv);
|
||||
}
|
||||
#endif
|
||||
|
||||
#else
|
||||
|
||||
void flush_callback(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *color_map)
|
||||
{
|
||||
esp_lcd_panel_handle_t panel_handle = (esp_lcd_panel_handle_t) drv->user_data;
|
||||
const int offsetx1 = area->x1;
|
||||
const int offsetx2 = area->x2;
|
||||
const int offsety1 = area->y1;
|
||||
const int offsety2 = area->y2;
|
||||
|
||||
/* Just copy data from the color map to the LCD frame buffer */
|
||||
esp_lcd_panel_draw_bitmap(panel_handle, offsetx1, offsety1, offsetx2 + 1, offsety2 + 1, color_map);
|
||||
|
||||
if (lvgl_port_interface != LVGL_PORT_INTERFACE_MIPI_DSI_DMA) {
|
||||
lv_disp_flush_ready(drv);
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* LVGL_PORT_AVOID_TEAR_ENABLE */
|
||||
|
||||
static lv_disp_t *display_init(esp_lcd_panel_handle_t panel_handle)
|
||||
{
|
||||
#if LVGL_PORT_PPA_ROTATION_ENABLE
|
||||
// Initialize the PPA
|
||||
ppa_client_config_t ppa_srm_config = {
|
||||
.oper_type = PPA_OPERATION_SRM,
|
||||
};
|
||||
ESP_ERROR_CHECK(ppa_register_client(&ppa_srm_config, &ppa_srm_handle));
|
||||
ESP_ERROR_CHECK(esp_cache_get_alignment(MALLOC_CAP_SPIRAM, &data_cache_line_size));
|
||||
#endif
|
||||
|
||||
assert(panel_handle);
|
||||
|
||||
static lv_disp_draw_buf_t disp_buf = { 0 }; // Contains internal graphic buffer(s) called draw buffer(s)
|
||||
static lv_disp_drv_t disp_drv = { 0 }; // Contains LCD panel handle and callback functions
|
||||
|
||||
// alloc draw buffers used by LVGL
|
||||
void *buf1 = NULL;
|
||||
void *buf2 = NULL;
|
||||
int buffer_size = 0;
|
||||
|
||||
ESP_LOGD(TAG, "Malloc memory for LVGL buffer");
|
||||
#if LVGL_PORT_AVOID_TEAR_ENABLE
|
||||
// To avoid the tearing effect, we should use at least two frame buffers: one for LVGL rendering and another for RGB output
|
||||
buffer_size = LVGL_PORT_H_RES * LVGL_PORT_V_RES;
|
||||
#if (LVGL_PORT_LCD_BUFFER_NUMS == 3) && (EXAMPLE_LVGL_PORT_ROTATION_DEGREE == 0) && LVGL_PORT_FULL_REFRESH
|
||||
// With the usage of three buffers and full-refresh, we always have one buffer available for rendering, eliminating the need to wait for the RGB's sync signal
|
||||
ESP_ERROR_CHECK(lvgl_get_lcd_frame_buffer(panel_handle, 3, &lvgl_port_rgb_last_buf, &buf1, &buf2));
|
||||
lvgl_port_rgb_next_buf = lvgl_port_rgb_last_buf;
|
||||
lvgl_port_flush_next_buf = buf2;
|
||||
#elif (LVGL_PORT_LCD_BUFFER_NUMS == 3) && (EXAMPLE_LVGL_PORT_ROTATION_DEGREE != 0)
|
||||
// Here we are using three frame buffers, one for LVGL rendering, and the other two for RGB driver (one of them is used for rotation)
|
||||
void *fbs[3];
|
||||
ESP_ERROR_CHECK(lvgl_get_lcd_frame_buffer(panel_handle, 3, &fbs[0], &fbs[1], &fbs[2]));
|
||||
buf1 = fbs[2];
|
||||
#else
|
||||
ESP_ERROR_CHECK(lvgl_get_lcd_frame_buffer(panel_handle, 2, &buf1, &buf2));
|
||||
#endif
|
||||
#else
|
||||
// Normmaly, for RGB LCD, we just use one buffer for LVGL rendering
|
||||
buffer_size = LVGL_PORT_H_RES * LVGL_PORT_BUFFER_HEIGHT;
|
||||
buf1 = heap_caps_malloc(buffer_size * sizeof(lv_color_t), LVGL_PORT_BUFFER_MALLOC_CAPS);
|
||||
assert(buf1);
|
||||
ESP_LOGI(TAG, "LVGL buffer size: %dKB", buffer_size * sizeof(lv_color_t) / 1024);
|
||||
#endif /* LVGL_PORT_AVOID_TEAR_ENABLE */
|
||||
|
||||
// initialize LVGL draw buffers
|
||||
lv_disp_draw_buf_init(&disp_buf, buf1, buf2, buffer_size);
|
||||
|
||||
ESP_LOGD(TAG, "Register display driver to LVGL");
|
||||
lv_disp_drv_init(&disp_drv);
|
||||
#if EXAMPLE_LVGL_PORT_ROTATION_90 || EXAMPLE_LVGL_PORT_ROTATION_270
|
||||
disp_drv.hor_res = LVGL_PORT_V_RES;
|
||||
disp_drv.ver_res = LVGL_PORT_H_RES;
|
||||
#else
|
||||
disp_drv.hor_res = LVGL_PORT_H_RES;
|
||||
disp_drv.ver_res = LVGL_PORT_V_RES;
|
||||
#endif
|
||||
disp_drv.flush_cb = flush_callback;
|
||||
disp_drv.draw_buf = &disp_buf;
|
||||
disp_drv.user_data = panel_handle;
|
||||
#if LVGL_PORT_FULL_REFRESH
|
||||
disp_drv.full_refresh = 1;
|
||||
#elif LVGL_PORT_DIRECT_MODE
|
||||
disp_drv.direct_mode = 1;
|
||||
#endif
|
||||
|
||||
return lv_disp_drv_register(&disp_drv);
|
||||
}
|
||||
|
||||
static void touchpad_read(lv_indev_drv_t *indev_drv, lv_indev_data_t *data)
|
||||
{
|
||||
esp_lcd_touch_handle_t tp = (esp_lcd_touch_handle_t)indev_drv->user_data;
|
||||
assert(tp);
|
||||
|
||||
uint16_t touchpad_x;
|
||||
uint16_t touchpad_y;
|
||||
uint8_t touchpad_cnt = 0;
|
||||
/* Read data from touch controller into memory */
|
||||
esp_lcd_touch_read_data(tp);
|
||||
|
||||
/* Read data from touch controller */
|
||||
bool touchpad_pressed = esp_lcd_touch_get_coordinates(tp, &touchpad_x, &touchpad_y, NULL, &touchpad_cnt, 1);
|
||||
if (touchpad_pressed && touchpad_cnt > 0) {
|
||||
data->point.x = touchpad_x;
|
||||
data->point.y = touchpad_y;
|
||||
data->state = LV_INDEV_STATE_PRESSED;
|
||||
ESP_LOGD(TAG, "Touch position: %d,%d", touchpad_x, touchpad_y);
|
||||
} else {
|
||||
data->state = LV_INDEV_STATE_RELEASED;
|
||||
}
|
||||
}
|
||||
|
||||
static lv_indev_t *indev_init(esp_lcd_touch_handle_t tp)
|
||||
{
|
||||
assert(tp);
|
||||
|
||||
static lv_indev_drv_t indev_drv_tp;
|
||||
|
||||
/* Register a touchpad input device */
|
||||
lv_indev_drv_init(&indev_drv_tp);
|
||||
indev_drv_tp.type = LV_INDEV_TYPE_POINTER;
|
||||
indev_drv_tp.read_cb = touchpad_read;
|
||||
indev_drv_tp.user_data = tp;
|
||||
|
||||
return lv_indev_drv_register(&indev_drv_tp);
|
||||
}
|
||||
|
||||
static void tick_increment(void *arg)
|
||||
{
|
||||
/* Tell LVGL how many milliseconds have elapsed */
|
||||
lv_tick_inc(LVGL_PORT_TICK_PERIOD_MS);
|
||||
}
|
||||
|
||||
static esp_err_t tick_init(void)
|
||||
{
|
||||
// Tick interface for LVGL (using esp_timer to generate 2ms periodic event)
|
||||
const esp_timer_create_args_t lvgl_tick_timer_args = {
|
||||
.callback = &tick_increment,
|
||||
.name = "LVGL tick"
|
||||
};
|
||||
esp_timer_handle_t lvgl_tick_timer = NULL;
|
||||
ESP_ERROR_CHECK(esp_timer_create(&lvgl_tick_timer_args, &lvgl_tick_timer));
|
||||
return esp_timer_start_periodic(lvgl_tick_timer, LVGL_PORT_TICK_PERIOD_MS * 1000);
|
||||
}
|
||||
|
||||
static void lvgl_port_task(void *arg)
|
||||
{
|
||||
ESP_LOGD(TAG, "Starting LVGL task");
|
||||
|
||||
uint32_t task_delay_ms = LVGL_PORT_TASK_MAX_DELAY_MS;
|
||||
while (1) {
|
||||
if (lvgl_port_lock(-1)) {
|
||||
task_delay_ms = lv_timer_handler();
|
||||
lvgl_port_unlock();
|
||||
}
|
||||
if (task_delay_ms > LVGL_PORT_TASK_MAX_DELAY_MS) {
|
||||
task_delay_ms = LVGL_PORT_TASK_MAX_DELAY_MS;
|
||||
} else if (task_delay_ms < LVGL_PORT_TASK_MIN_DELAY_MS) {
|
||||
task_delay_ms = LVGL_PORT_TASK_MIN_DELAY_MS;
|
||||
}
|
||||
vTaskDelay(pdMS_TO_TICKS(task_delay_ms));
|
||||
}
|
||||
}
|
||||
|
||||
esp_err_t lvgl_port_init(esp_lcd_panel_handle_t lcd_handle, esp_lcd_touch_handle_t tp_handle, lvgl_port_interface_t interface)
|
||||
{
|
||||
lv_init();
|
||||
ESP_ERROR_CHECK(tick_init());
|
||||
|
||||
lvgl_port_interface = interface;
|
||||
#if LVGL_PORT_AVOID_TEAR_ENABLE
|
||||
switch (interface) {
|
||||
#if SOC_LCDCAM_RGB_LCD_SUPPORTED
|
||||
case LVGL_PORT_INTERFACE_RGB:
|
||||
lvgl_get_lcd_frame_buffer = esp_lcd_rgb_panel_get_frame_buffer;
|
||||
break;
|
||||
#endif
|
||||
|
||||
#if SOC_MIPI_DSI_SUPPORTED
|
||||
case LVGL_PORT_INTERFACE_MIPI_DSI_DMA:
|
||||
case LVGL_PORT_INTERFACE_MIPI_DSI_NO_DMA:
|
||||
lvgl_get_lcd_frame_buffer = esp_lcd_dpi_panel_get_frame_buffer;
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
ESP_LOGE(TAG, "Invalid interface type");
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
#endif
|
||||
|
||||
lv_disp_t *disp = display_init(lcd_handle);
|
||||
assert(disp);
|
||||
|
||||
if (tp_handle) {
|
||||
lv_indev_t *indev = indev_init(tp_handle);
|
||||
assert(indev);
|
||||
#if EXAMPLE_LVGL_PORT_ROTATION_90
|
||||
esp_lcd_touch_set_swap_xy(tp_handle, true);
|
||||
esp_lcd_touch_set_mirror_x(tp_handle, true);
|
||||
#elif EXAMPLE_LVGL_PORT_ROTATION_180
|
||||
esp_lcd_touch_set_mirror_x(tp_handle, true);
|
||||
esp_lcd_touch_set_mirror_y(tp_handle, true);
|
||||
#elif EXAMPLE_LVGL_PORT_ROTATION_270
|
||||
esp_lcd_touch_set_swap_xy(tp_handle, true);
|
||||
esp_lcd_touch_set_mirror_y(tp_handle, true);
|
||||
#endif
|
||||
}
|
||||
|
||||
lvgl_mux = xSemaphoreCreateRecursiveMutex();
|
||||
assert(lvgl_mux);
|
||||
|
||||
ESP_LOGI(TAG, "Create LVGL task");
|
||||
BaseType_t core_id = (LVGL_PORT_TASK_CORE < 0) ? tskNO_AFFINITY : LVGL_PORT_TASK_CORE;
|
||||
BaseType_t ret = xTaskCreatePinnedToCore(lvgl_port_task, "lvgl", LVGL_PORT_TASK_STACK_SIZE, NULL,
|
||||
LVGL_PORT_TASK_PRIORITY, &lvgl_task_handle, core_id);
|
||||
if (ret != pdPASS) {
|
||||
ESP_LOGE(TAG, "Failed to create LVGL task");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
bool lvgl_port_lock(int timeout_ms)
|
||||
{
|
||||
assert(lvgl_mux && "lvgl_port_init must be called first");
|
||||
|
||||
const TickType_t timeout_ticks = (timeout_ms < 0) ? portMAX_DELAY : pdMS_TO_TICKS(timeout_ms);
|
||||
return xSemaphoreTakeRecursive(lvgl_mux, timeout_ticks) == pdTRUE;
|
||||
}
|
||||
|
||||
void lvgl_port_unlock(void)
|
||||
{
|
||||
assert(lvgl_mux && "lvgl_port_init must be called first");
|
||||
xSemaphoreGiveRecursive(lvgl_mux);
|
||||
}
|
||||
|
||||
bool lvgl_port_notify_lcd_vsync(void)
|
||||
{
|
||||
BaseType_t need_yield = pdFALSE;
|
||||
#if LVGL_PORT_FULL_REFRESH && (LVGL_PORT_LCD_RGB_BUFFER_NUMS == 3) && (EXAMPLE_LVGL_PORT_ROTATION_DEGREE == 0)
|
||||
if (lvgl_port_rgb_next_buf != lvgl_port_rgb_last_buf) {
|
||||
lvgl_port_flush_next_buf = lvgl_port_rgb_last_buf;
|
||||
lvgl_port_rgb_last_buf = lvgl_port_rgb_next_buf;
|
||||
}
|
||||
#elif LVGL_PORT_AVOID_TEAR_ENABLE
|
||||
// Notify that the current LCD frame buffer has been transmitted
|
||||
if (lvgl_task_handle) {
|
||||
xTaskNotifyFromISR(lvgl_task_handle, ULONG_MAX, eNoAction, &need_yield);
|
||||
}
|
||||
#else
|
||||
if (lvgl_port_interface == LVGL_PORT_INTERFACE_MIPI_DSI_DMA) {
|
||||
lv_disp_drv_t *drv = lv_disp_get_default()->driver;
|
||||
lv_disp_flush_ready(drv);
|
||||
}
|
||||
#endif
|
||||
return (need_yield == pdTRUE);
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user