DIY-Firmware für Photopolymer-LCD-3D-Drucker. Teil 1

Bild



... oder wie ich meine eigenen Fahrräder mit Vorlieben und Geisha nach meinem Geschmack erfunden habe - ich habe die Firmware für einen Fotopolymerdrucker von Grund auf neu geschrieben. Im Moment ist die Firmware bereits voll funktionsfähig.



Als Grundlage diente die auf Aliexpress verkaufte MKS DLP-Karte, für die der Hersteller eine Schaltung und Firmware-Quellcodes bereitstellt, die ich abgelehnt habe, um alles von Grund auf neu zu schreiben.

Der Artikel ist sehr lang, deshalb habe ich beschlossen, ihn in zwei Teile zu teilen. In diesem Teil finden Sie einen Hintergrund und eine Beschreibung einer hausgemachten Benutzeroberfläche für einen Touchscreen. Am Ende wird es Links zum Thema Mobbing selbst und zu den GitHub-Repositories geben.





- Teil 1: 1. Benutzeroberfläche.

- Teil 2: 2. Arbeiten mit dem Dateisystem auf einem USB-Stick. 3. Schrittmotorsteuerung für Plattformbewegung.

- Teil 3: 4. Anzeigen von Bildern von Ebenen auf dem Hintergrundbeleuchtungsdisplay. 5. Alles, wie die Steuerung von Beleuchtung und Lüftern, das Laden und Speichern von Einstellungen usw. 6. Zusätzliche Funktionen für Komfort und Bequemlichkeit.



Zum besseren Verständnis werde ich eine sehr kurze Beschreibung der Arbeit von Photopolymer-LCD-3D-Druckern für diejenigen geben, die mit ihnen nicht vertraut sind:



Eine kurze Erklärung der Funktionsweise der meisten Consumer-Fotopolymerdrucker
— LCD- ( , 5.5" 25601440 ( — 47.25 ). 405 . , FEP-. , «» . , -. , «» , . . , , . , . , .



Hintergrund



Wie bin ich dazu gekommen und warum habe ich angefangen, meine eigene Firmware zu schreiben, anstatt nur den Quellcode des Herstellers für mich selbst zu optimieren.



Die Hintergrundgeschichte stellte sich als lang heraus, also entfernte ich sie unter dem Spoiler
5 3D-. , , . FDM-, — Anet A8. - , , . - — - , , . , . - — Anycubic Photon S. , .



, , «» — , . , .., FDM-. , , — 11565 , :) «» , , , . . «» — . , , 20-30 . , — , . .



. , , .. , , , , , . . , (), . , , . - . , 3D- — MKS DLP. : , (5.5", 25601440) (3.5", 480320). — ! , , .



, , . , - , , . . , . … -, CMSIS HAL ST ( STM32F407). -, Marlin 3D. — Marlin 3D — FDM 3D-. 6 , , , G- - . 3 . . — G- . , . , FDM- .



, GUI- , . , , - .



Also was wir haben:



  • MKS DLP-Kit, das Folgendes umfasst: Motherboard, 3,5 "480 x 320-Schnittstellendisplay und 5,5" 2560 x 1440-Hintergrundbeleuchtungsdisplay
  • native Quellen vom Hersteller
  • Motherboard-Diagramm (ohne Namen der aktiven und Nennwerte der passiven Komponenten)


Das Motherboard basiert auf dem Mikrocontroller STM32F407. Zur Steuerung der Hintergrundbeleuchtung enthält die Karte ein FPGA des chinesischen Herstellers GW1N-LV4LQ144ES, SDRAM und zwei SSD2828-MIPI-Schnittstellenchips. Der Mikrocontroller steuert das Bild der Schicht in das FPGA, das FPGA speichert es im SDRAM und aktualisiert von dort die Anzeige über die SSD2828. Übrigens stellt der Hersteller die FPGA-Konfiguration (Firmware) nicht im Quellcode zur Verfügung: (Zusätzlich hat das Motherboard:



  • Leistungsaufnahme 12-24 Volt
  • USB A /
  • A4988
  • Z —
  • WiFi
  • FLASH- W25Q64
  • EEPROM- AT24C16


Das Schnittstellendisplay mit einem resistiven Touchpanel ist mit einem flachen 40-poligen Flachbandkabel verbunden. Display-Controller - ILI9488, Touchpanel-Controller - HR2046 (ähnlich TSC2046).



Zum Initialisieren der Peripheriegeräte habe ich das Programm STM32CUBE MX verwendet. Aber ich habe das daraus erhaltene Ergebnis nicht direkt verwendet, sondern die notwendigen Stücke in meine Quellen eingefügt. Bei der Arbeit mit Peripheriegeräten habe ich die HAL-Bibliotheken von ST verwendet. Wo ich die maximale Geschwindigkeit erreichen wollte, habe ich direkt mit Registern gearbeitet.



Es gibt also eine Aufgabe: Dieses Kit sollte für den Benutzer problemlos Dateien von einem Flash-Laufwerk drucken können. Ich habe dieses ganze Problem grob in Hauptteile unterteilt, was zu drei Veröffentlichungen führte.



Ich möchte Sie sofort warnen, dass weder mein Code noch mein Ansatz vorgeben, ideal oder sogar nur gut zu sein. Ich spiele so gut ich kann. Programmieren ist für mich eher ein Hobby als ein Beruf. Bitte urteilen Sie nicht nach zu strengen Maßstäben.



1. Benutzeroberfläche



Zunächst erfolgte die Initialisierung der Anzeige. Nichts interessantes hier, die Standardsequenz für den ILI9488-Controller. Ich habe es aus nativen Quellen herausgerissen und den Initialisierungscode für andere Anzeigetypen herausgeschnitten (die wahrscheinlich aus dem FDM-Leben dieser Quellen dort geblieben sind). Dann habe ich Schriften aufgenommen.



1.1 Schriftarten



Es gibt viele Schriftbibliotheken für Mikrocontroller im Internet, aber die überwiegende Mehrheit von ihnen arbeitet mit monospaced Schriftarten, und das gefällt mir nicht wirklich. In diesem Fall haben alle Zeichen die gleiche Breite wie der Buchstabe "z" und der Buchstabe "i". Ich habe einmal eine proportionale Schriftbibliothek für eines meiner Lieblingsprojekte geschrieben. Es werden zwei Arrays für jede Schriftart verwendet - ein Array mit den Bitdaten der Zeichen selbst und ein Array mit der Breite jedes Zeichens. Und eine kleine Struktur mit Schriftparametern - Zeiger auf Arrays, Schrifthöhe, Anzahl der Zeichen in der Schrift:



typedef struct
{
	uint16_t	*width;
	uint8_t		*data;
	uint8_t		height;
	uint16_t	symcount;
} LCDUI_FONT;


Es scheint, dass eine solche Schriftorganisation mehr Speicherplatz beanspruchen sollte als nur eine Monospaced-Bitmap, aber dies ist nicht ganz richtig. Erstens führt der Monospace selbst zu einem Überschuss an gespeicherten Daten. Wenn beispielsweise in einer Schriftart mit einer Höhe von 8 Pixel und einer Breite von 5 Pixel 1 Byte (1 Bit breit und 8 Bit hoch) für den Buchstaben "i" ausreicht, werden weiterhin 5 Byte Daten (5 Bit breit und 8 Bit hoch) benötigt. schon seit Die Breite ist fest. Zweitens erfolgt in solchen Schriftarten in der Regel die Ausrichtung an den Bytegrenzen jeder Zeile oder Spalte, je nachdem, wie die Daten organisiert sind.



Nehmen Sie zum Beispiel dieselbe 5x8-Schriftart. Wenn die Bitdaten zeilenweise gespeichert werden, gibt es einen Überschuss von 3 Bits für jede Zeile. Oder 3 Bytes pro Zeichen:



Bild



Oder eine 7x12-Schriftart mit Datenspeicherung in Spalten, dann gibt es einen Überschuss von 4 Bit pro Spalte oder 3,5 Bytes pro Zeichen:



Bild



In meiner Bibliothek sind Bitdaten für ein Zeichen fortlaufend und die Ausrichtung an der Bytegrenze befindet sich nur am Ende des Zeichens.



Außerdem gibt es noch einen kleinen Trick, mit dem Sie die gespeicherte Schriftgröße geringfügig reduzieren können: Ein Zeichen enthält möglicherweise keine Bitdaten, verweist jedoch auf ein anderes Zeichen mit demselben Stil. Zum Beispiel die kyrillischen Buchstaben "A", "B", "E", "K" usw. kann auf lateinische Buchstaben mit demselben Stil verweisen. Dies erfolgt durch Angabe eines negativen Werts für die Breite des entsprechenden Zeichens im Array der Zeichenbreiten. Wenn dort ein negativer Wert vorhanden ist, wird das Bild dieses Zeichens von dem Zeichen in Position (Breite * -1) aufgenommen.



Hier ist die Vorgehensweise zum Suchen eines Zeichens in einem Array:



uint8_t*	_lcdui_GetCharData(char c)
{
	if (c < 32)
		return 0;
	if (c > 126)
		c -= 65;
	c -= 32;
	if (c >= lcdui_current_font->symcount)
		return 0;
	uint16_t c1 = lcdui_current_font->width[c];
	if (c1 & 0x8000)
		c = (c1 & 0x7FFF);
	uint16_t ch = lcdui_current_font->height;
	int32_t i = 0, ptr = 0, bits = 0, line_bits = ch;
	for (i = 0; i < c; i++)
	{
		if (lcdui_current_font->width[i] & 0x8000)
			continue;
		bits = lcdui_current_font->width[i] * line_bits;
		ptr += bits >> 3;
		if (bits & 0x07)
			ptr++;
	}

	return &(lcdui_current_font->data[ptr]);
}


All dies führt häufig sogar zu einem Anstieg der Datenmenge für die Schriftart. Ganz zu schweigen davon, dass der proportionale Typ natürlicher aussieht.



Die Rendergeschwindigkeit einer solchen Schriftart ist aufgrund der Fensterausgabe recht anständig. Die Anzeige erhält zuerst den Befehl, das Ausgabefenster auf die Größe des Zeichens an der gewünschten Position zu beschränken, und dann werden die Daten des gesamten Zeichens in einem Stream hineingegossen. Es ist nicht erforderlich, die Koordinaten für jedes Pixel separat festzulegen.



Auf dem Foto unten wurden beispielsweise der blaue Text und die obere weiße Linie von meiner Bibliothek und die weiße untere Linie von der Standard-Arduino-ähnlichen Bibliothek aus den nativen Quellen



Bild



gerendert : Der blaue Text wurde mehrmals schneller gerendert als die untere weiße Linie.



Gleichzeitig musste ich aus einem Bild ein Dienstprogramm zum Erstellen von Schriftarten-Arrays erfinden, die für die Verwendung in einem Programm bereit sind. In Photoshop wird ein Bild mit der gewünschten Höhe mit allen Zeichen der Schriftart erstellt. Anschließend werden die X-Koordinaten der letzten Spalte jedes Zeichens von Hand in die Textdatei eingegeben. Anschließend wird das Dienstprogramm für das Bild und diese Textdatei festgelegt. Dadurch wird eine C-Datei mit den erforderlichen Arrays erstellt. Ein bisschen langweilig natürlich, aber einfach.



Das Verfahren zum Anzeigen von Text kann Text in eine neue Zeile am Ende des Bildschirms einfügen oder durch ein angetroffenes Zeilenvorschubzeichen nach links, rechts und in der Mitte ausgerichtet werden und den Bereich begrenzen, über den der Text nicht hinausgeht (abgeschnitten wird). Und es ist in der Lage, Symbole mit Hintergrundmalerei mit Hintergrundfarbe oder mit Hintergrunderhaltung anzuzeigen. Die zweite Option arbeitet langsamer, da es nicht mehr möglich ist, die Zeichendaten in einem Stream in die Anzeige zu füllen, aber sie ist immer noch schnell genug, dass die Ausgabe von 3-4 Zeilen für das Auge nicht sichtbar ist.



1.2 Anzeigen von Schnittstellenbildern



Für die Benutzeroberfläche müssen Sie Bilder anzeigen - Hintergrund, Symbole, Schaltflächen. Zuerst habe ich beschlossen, mich nicht zu sehr darum zu kümmern und alle Bilder im BMP-Format im 8-MB-Flash-Speicher auf der Platine zu speichern. Und ich habe sogar ein Verfahren dafür geschrieben. Die Datei wird im 16-Bit-Format (R5 G6 B5) mit End-to-End- oder End-to-End-Zeilenreihenfolge gespeichert und kann bereits direkt der Rendering-Routine zugeführt werden. Die Größe eines Hintergrundbilds von 480 x 320 beträgt jedoch mehr als 300 KB. In Anbetracht der Tatsache, dass ein Teil dieses Flash-Speichers für Firmware-Updates vorgesehen ist, belegen 30 Hintergrundbilder den gesamten Speicher. Es scheint viel zu sein, aber immer noch weniger, als ich gerne hätte, nur für den Fall. Es sollten aber auch Schaltflächen, Symbole usw. vorhanden sein. Daher wurde beschlossen, die Bilder in ein komprimiertes Format zu konvertieren.



Es gibt nicht viele Optionen für die Komprimierung - alle Algorithmen, die Bilder mehr oder weniger gut komprimieren, erfordern entweder einen angemessenen RAM (nach den Standards eines Mikrocontrollers) oder eine angemessene Zeit zum Dekomprimieren. Auf der anderen Seite sollten Bilder angezeigt werden, die sich im laufenden Betrieb lösen, und es ist wünschenswert, dass das Bild beim Anzeigen nicht einem kriechenden Fortschrittsbalken ähnelt. Daher habe ich mich für die RLE-Komprimierung entschieden - 1 Byte codiert die Anzahl der Wiederholungen und die beiden folgenden - die Farbe. Zu diesem Zweck wurde auch ein Dienstprogramm geschrieben, das BMP-Dateien in auf diese Weise komprimierte Bilder konvertiert. Der Header besteht nur aus 4 Bytes - 2 Bytes für die Breite und Höhe des Bildes. Im Durchschnitt werden Hintergrundbilder auf diese Weise um das 5- bis 7-fache komprimiert, was stark von der Größe der monochromen Bereiche abhängt (was zu erwarten ist). Ein Bild wie dieses ist beispielsweise von ursprünglich 307 KB auf 74 KB geschrumpft:



Bild



Aber dieses hier - bis zu 23 KB vom selben 307:





Übrigens, der Designer von mir ist noch mehr Mist als der Programmierer ...



Ich war mit diesem Ergebnis zufrieden. Das Dekodieren und Anzeigen von Bildern ist sehr schnell - etwa 40 Millisekunden pro vollständigem Hintergrundbild. Also habe ich mich für diese Option entschieden.



Übrigens hat das Umschalten in den DMA-Modus für die Ausgabe von Daten auf dem Display fast keine Beschleunigung der Ausgabe ergeben. Das Display ist über einen externen 16-Bit-Datenbus als externer Speicher angeschlossen, aber seine Timings sind ziemlich traurig, was die Vorteile der DMA-Ausgabe gegenüber der manuellen Pixelausgabe fast zunichte macht.



1.3 GUI-Framework



Texte werden angezeigt, Bilder werden gezeichnet, jetzt ist es Zeit darüber nachzudenken, wie die Basis der Benutzeroberfläche organisiert wird.



Mit dem Touchpanel ist alles einfach - der Mikrocontroller fragt den Touchpanel-Controller ständig durch Interrupts ab und mittelt die letzten 4 erhaltenen Ergebnisse, um sie in Anzeigekoordinaten zu übersetzen. Somit ist der Zustand des Sensors jederzeit bekannt - ob er gedrückt wird oder nicht und wenn er gedrückt wird, an welcher Stelle. Eine weitere Ebene zwischen dem Touchpanel und dem Hauptteil des Programms ist das Verfahren zum Verarbeiten von Tastenklicks, das seit geraumer Zeit mit kleinen Anpassungen an bestimmte Bedingungen von Projekt zu Projekt wandert.



Hier ist eine kurze Zusammenfassung, wie es funktioniert.
«». (100-150 ). , «». , . , , «», . , «», «». «», «». - «» «», . ( «»), - . , , .



Das Touchpanel dient als einzige Schaltfläche der Benutzeroberfläche, außer dass neben dem Drücken auch die Koordinaten analysiert werden, bei denen der Klick aufgetreten ist.



Jetzt muss alles getan werden, damit eine Vielzahl von Oberflächenelementen auf dem Bildschirm angezeigt werden können, die möglicherweise auf Klicks reagieren, durch Ereignisse aktualisiert werden, unterschiedliche Größen und Bilder haben usw.



Letztendlich habe ich mir dieses Schema ausgedacht: Die Benutzeroberfläche besteht aus zwei Haupttypen von Elementen - Bildschirmen und Schaltflächen.



Ein Bildschirm ist eine Art Vollbild-Container für Schaltflächen. Der Bildschirm hat folgende Eigenschaften:



  • Hintergrundbild
  • Hintergrundfarbe
  • Art und Weise des Zeichnens des Hintergrunds - Füllen mit einer Hintergrundfarbe oder Anzeigen eines Bildes
  • Überschrift
  • Titeltextfarbe
  • Header-Textschrift
  • ein Zeiger auf den übergeordneten Bildschirm (zu dem beim Schließen zurückgekehrt werden soll)
  • eine Reihe von Zeigern auf Schaltflächen
  • Zeiger auf eine Ereignisprozedur (wird in der Hauptprogrammschleife regelmäßig aufgerufen)
  • Zeiger auf die Bildschirmzeichnungsroutine


Bildschirmstruktur
typedef struct
{
	void				*addparameter;

	char				*bgimagename;
	
	void				*prevscreen;
	
	LNG_STRING_ID		name;
	TG_RECT				nameposition;
	TG_TEXTOPTIONS		nameoptions;
	
	uint8_t				btns_count;
	TG_BUTTON			*buttons;
	
	LCDUI_FONT_TYPE		font;
	LCDUI_FONT_TYPE		namefont;
	uint16_t			textcolor;
	uint16_t			nametextcolor;
	uint16_t			backcolor;

	struct {
		paintfunc		_callpaint;	// repaint screen
		processfunc		_process;	// screen process handling (check for changes, touch pressed, etc)
	} funcs;
} TG_SCREEN;




Schaltflächen können nicht nur Schaltflächen sein, sondern auch Text, ein Symbol, eine Art sich änderndes Element wie ein Zähler oder eine Uhr. Es hat sich als praktisch erwiesen, alles in einem Typ zu kombinieren und das Verhalten jeder einzelnen Schaltfläche anhand ihrer Eigenschaften festzulegen.



Schaltflächeneigenschaften:



  • Koordinaten auf dem Bildschirm
  • Hintergrundfarbe
  • Hintergrundbild für freien Zustand
  • Hintergrundbild für den gedrückten Zustand
  • Hintergrundbild für deaktivierten Zustand
  • Hintergrundbild für den aktiven Status (z. B. für das aktive Element einer Optionsfeldgruppe)
  • Rendering-Methode - Bild- oder Hintergrundfarbe
  • Gibt an, ob die Taste beim Drücken und Loslassen neu gezeichnet werden soll
  • Schaltflächentext
  • ( )
  • (, )
  • ( )
  • ,
  • ,


typedef struct
{
	void				*addparameter;
	
	uint8_t				button_id;
	

	int8_t				group_id;		// for swithed options buttons, >0 - single selection from group (select), <0 - multiple selection (switch)
	
	TG_RECT				position;
	
	void				*parentscreen;
	void				*childscreen;

	char				*bgimagename_en;
	char				*bgimagename_press;
	char				*bgimagename_dis;
	char				*bgimagename_act;	// for swithed options buttons

	LNG_STRING_ID		text;
	TG_RECT				textposition;
	LCDUI_FONT_TYPE		font;
	uint16_t			textcolor_en;
	uint16_t			textcolor_press;
	uint16_t			textcolor_dis;
	uint16_t			textcolor_act;	// for swithed options buttons
	uint16_t			backcolor_en;
	uint16_t			backcolor_press;
	uint16_t			backcolor_dis;
	uint16_t			backcolor_act;	// for swithed options buttons
	
	struct {
		uint8_t				active:1;		// for swithed options buttons
		uint8_t				needrepaint:1;
		uint8_t				pressed:1;
		uint8_t				disabled:1;
		uint8_t				repaintonpress:1;		// repaint or not when pressed - for indicate pressed state
		BGPAINT_TYPE		bgpaint:2;
	} options;
	
	TG_TEXTOPTIONS	textoptions;

	struct {
		paintfunc		_call_paint;	// repaint button
		pressfunc		_call_press;	// touch events handling
		pressfunc		_call_longpress;	// touch events handling
		processfunc		_call_process;	// periodical processing (for example text value refresh)
	} funcs;
} TG_BUTTON;




Mit Hilfe dieser Eigenschaften wurde es möglich, fast alles in der Schnittstelle basierend auf einem solchen Element zu erstellen. Wenn ein Bildschirm oder eine Schaltfläche einen Zeiger auf eine der Prozeduren null hat, wird die entsprechende Standardprozedur aufgerufen. Anstelle eines Prozedurzeigers zum Drücken einer Taste kann beispielsweise eine spezielle Kennung vorhanden sein, die angibt, dass Sie zum untergeordneten oder vorherigen Bildschirm wechseln müssen. Dies wird dann von der Standardprozedur ausgeführt. Im Allgemeinen decken Standardprozeduren fast alle Fälle ab, in denen normale Schaltflächen verwendet werden, und Sie müssen Ihre eigenen Prozeduren für eine Schaltfläche nur in nicht standardmäßigen Fällen erstellen - beispielsweise wenn eine Schaltfläche wie eine Uhr funktioniert oder als Element einer Dateiliste.



Was diesem Schema jedoch fehlte, war für modale Fenster mit Nachrichten oder Fragen (wie MessageBox in der Windows-API), daher habe ich einen separaten Bildschirmtyp für sie erstellt. Keine Hintergrundbilder und eine Größe, die durch den Titel oder die Nachricht selbst bestimmt wird. Diese Meldungen können in vier Versionen erstellt werden - mit den Schaltflächen "Ja / Nein", mit den Schaltflächen "OK / Abbrechen", mit einer Schaltfläche "OK" oder ohne Schaltflächen (z. B. "Warten, Daten werden geladen ...").







Struktur des Nachrichtenfensters
typedef struct
{
	MSGBOXTYPE			type;
	
	void				*prevscreen;
	
	char				caption[128];
	char				text[512];
	TG_RECT				boxpos;
	
	uint8_t				btns_count;
	TG_BUTTON			buttons[TG_BTN_CNT_MSGBOX];
	
	uint16_t			caption_height;
	
	LCDUI_FONT_TYPE		font_caption;
	LCDUI_FONT_TYPE		font_text;
	uint16_t			text_color;
	uint16_t			box_backcolor;
	uint16_t			capt_textcolor;
	uint16_t			capt_backcolor;
} TG_MSGBOX;




Auf der Grundlage dieser drei Typen wurde die gesamte Schnittstelle erstellt, was sich als recht flexibel herausstellte. Jetzt wird die Initialisierung aller Elemente starr in der Firmware durchgeführt. Es besteht jedoch die Idee, Benutzern die Möglichkeit zu geben, eine eigene Schnittstelle zu erstellen, indem die Eigenschaften aller Elemente in der Konfigurationsdatei beschrieben und eine Reihe der erforderlichen Bilder hinzugefügt werden. Theoretisch wird es möglich sein, den Inhalt verschiedener Bildschirme zu ändern - welche Schaltflächen auf dem Hauptbildschirm angezeigt werden sollen, welche Schaltflächen auf dem Servicebildschirm usw.



1.4 Mehrsprachig







Mehrsprachigkeit war anfangs in den Aufgaben. Aber zuerst bin ich den dummen Weg gegangen - als ich alle Elemente initialisiert habe, habe ich ihnen Texte aus der Sprachtabelle zugewiesen, die die aktuelle war. Das Wechseln der Sprache bedeutete, alle Textelemente neu zu initialisieren. Als mehr als zwei Bildschirme in der Benutzeroberfläche und mehr als 20 Schaltflächen und Beschriftungen vorhanden waren, wurde mir klar, dass ich so nicht mehr leben konnte. Dann machte er alle Verweise auf die Texte durch das Verfahren. Die Prozedur erhält eine Textkennung als Parameter und gibt einen Zeiger auf den Text in der aktuellen Sprache zurück:



	char *mshortname = LANG_GetString(LSTR_SHORT_JANUARY);


Beim Ändern der Sprache wechselt der Zeiger einfach von einem Array von Texten in der alten Sprache zu einem Array mit Texten in der neuen Sprache:



void		LANG_SetLanguage(uint8_t lang)
{
	lngCurrent = lngLanguages[lang].strings;
	
	return;
}


Alle Quelltexte sind in UTF-8-Codierung. Ich musste auch an diesen Kodierungen basteln. Texte - in UTF-8, kyrillische Dateien - in Unicode-16, einige Zeichenfolgen - in regulärem ANSI. Ich wollte nicht eine ganze Reihe von Bibliotheken zur Unterstützung von Multibyte-Codierungen in die Firmware ziehen, daher wurden verschiedene Funktionen für die Konvertierung von Codierung zu Codierung und für Operationen mit Texten in unterschiedlichen Codierungen geschrieben, z. B. das Hinzufügen einer UTF-8-Zeichenfolge am Ende einer Unicode16-Zeichenfolge.

Das Hinzufügen einer neuen Sprache hat sich nun darauf beschränkt, eine Texttabelle darin zu erstellen und den Wert der Konstanten LNG_LANGS_COUNT zu ändern. Es bleibt zwar eine Frage mit Schriftarten, ob die neue Sprache andere Symbole als Kyrillisch und Latein verwendet ... Jetzt unterstütze ich Russisch und Google-übersetztes Englisch im Quellcode.



1.5 Speichern von Bildern und anderen Ressourcen



Zum Speichern großer Ressourcen verfügt das Board über einen 8-Megabyte-SPI-Flash W25Q64. Anfangs wollte ich wie immer einen Offset für jede Ressource im Flash festlegen und dort nur als Binärdaten speichern. Aber dann wurde mir klar, dass mir Probleme mit dieser Methode garantiert sind, sobald die Anzahl der gespeicherten Ressourcen ein paar Dutzend überschreitet und ich zum Beispiel ein Bild ändern möchte, das in der sechsten Reihenfolge gespeichert wird. Wenn die Größe zunimmt, müssen Sie die Adressen aller folgenden Ressourcen verschieben und neu schreiben. Oder lassen Sie nach jeder Ressource einen freien Speicherplatz unbekannter Größe - wer weiß, wie sich eine der Ressourcen ändern kann. Ja, in einem Sarg habe ich diese Aufregung gesehen :) Also habe ich auf diesem Flash ein Dateisystem ausgespuckt und organisiert.Zu diesem Zeitpunkt hatte ich bereits ein USB-Dateisystem, das auf der FatFS-Bibliothek basierte, sodass es ausreichte, nur separate Lese- / Schreibfunktionen auf niedriger Ebene für Sektoren zu schreiben. Nur eines hat mich ein wenig verärgert - die Größe des gelöschten Sektors in dieser Mikroschaltung beträgt bereits 4 KB. Dies führt zum einen dazu, dass Dateien in Teilen von 4 KB Speicherplatz beanspruchen (die Datei hat 200 Bytes geschrieben - es wurden 4 KB Flash benötigt), und zum anderen verbraucht der Puffer in der Struktur jedes Dateizeigers dieselben 4 KB RAM, die in Der Mikrocontroller ist nicht so viel - 192 KB. Man könnte natürlich pervers sein und Funktionen auf niedriger Ebene schreiben, so dass sie in kleineren Abschnitten schreiben und lesen können, wobei über die Sektorgröße berichtet wird, beispielsweise 512 Bytes. Dies würde den Flash jedoch verlangsamen und die Sektorgröße bei 4 KB belassen.Auf jede Ressource kann also einfach über ihren Dateinamen zugegriffen werden, was sich als sehr praktisch herausstellte. Im Moment hat zum Beispiel die Anzahl der gespeicherten Ressourcen bereits 90 überschritten. Und ich habe ihre Aktualisierung so einfach wie möglich gemacht - die aktualisierten (oder neuen) Ressourcen werden auf ein USB-Flash-Laufwerk in einem bestimmten Verzeichnis geschrieben, das Flash-Laufwerk wird in die Karte eingelegt, die Karte wird im Servicemodus neu gestartet (während) einschalten oder neu starten, halten Sie die obere rechte Ecke des Displays gedrückt und kopieren Sie automatisch alle in diesem Verzeichnis gefundenen Dateien vom USB-Stick auf den SPI-Flash.Die Karte wird im Servicemodus neu gestartet (halten Sie beim Einschalten oder Neustart die obere rechte Ecke des Displays gedrückt) und kopieren automatisch alle in diesem Verzeichnis gefundenen Dateien vom USB-Flash-Laufwerk auf den SPI-Flash.Die Karte wird im Servicemodus neu gestartet (halten Sie beim Einschalten oder Neustart die obere rechte Ecke des Displays gedrückt) und kopieren automatisch alle in diesem Verzeichnis gefundenen Dateien vom USB-Flash-Laufwerk auf den SPI-Flash.







Fortsetzung folgt...



Der vielleicht umfangreichste Teil kam auf der Schnittstelle heraus. Wenn sich herausstellt, dass dieser Artikel für die Community interessant ist, werde ich im zweiten Teil versuchen, alles andere unterzubringen.



Nun, ich freue mich über Fragen und Kommentare.



- Teil 1: 1. Benutzeroberfläche.

- Teil 2: 2. Arbeiten mit dem Dateisystem auf einem USB-Stick. 3. Schrittmotorsteuerung für Plattformbewegung.

- Teil 3: 4. Anzeigen von Bildern von Ebenen auf dem Hintergrundbeleuchtungsdisplay. 5. Alles, wie die Steuerung von Beleuchtung und Lüftern, das Laden und Speichern von Einstellungen usw. 6. Zusätzliche Funktionen für Komfort und Bequemlichkeit.



Links



MKS DLP-Kit auf Aliexpress

Original-Firmware-Quellen des Herstellers auf GitHub

Schemes vom Hersteller von zwei Versionen des Boards auf GitHub

Meine Quellen auf GitHub



All Articles