البرامج الثابتة DIY للطابعة فوتوبوليمير LCD 3D. الجزء 1

صورة



... أو كيف ابتكرت دراجتي الخاصة مع التفضيلات والغييشا حسب ذوقي - لقد كتبت البرنامج الثابت لطابعة فوتوبوليمير من الصفر. في الوقت الحالي ، تعمل البرامج الثابتة بالفعل بشكل كامل.



تم أخذ لوحة MKS DLP التي تم بيعها على Aliexpress كأساس ، حيث توفر الشركة المصنعة رموز مصدر الدائرة والبرنامج الثابت ، والتي رفضتها لصالح كتابة كل شيء من البداية.

تبين أن المقالة طويلة جدًا ، لذلك قررت تقسيمها إلى قسمين. في هذا الجزء ، ستكون هناك خلفية ووصف لواجهة المستخدم الرسومية محلية الصنع لشاشة تعمل باللمس. في النهاية ستكون هناك روابط لموضوع التنمر نفسه ومستودعات GitHub.





- الجزء الأول: 1. واجهة المستخدم.

- الجزء 2: 2. العمل مع نظام الملفات على محرك أقراص فلاش USB. 3. التحكم في محرك متدرج لحركة المنصة.

- الجزء 3: 4. عرض صور الطبقات على شاشة الإضاءة الخلفية. 5. كل شيء صغير ، مثل التحكم في الإضاءة والمراوح ، وتحميل وحفظ الإعدادات ، إلخ. 6. ميزات إضافية للراحة والملاءمة.



من أجل فهم أفضل ، سأقدم وصفًا موجزًا ​​جدًا لعمل طابعات فوتوبوليمير LCD ثلاثية الأبعاد لأولئك الذين ليسوا على دراية بها:



شرح موجز لكيفية عمل معظم طابعات فوتوبوليمير المستهلك
— LCD- ( , 5.5" 25601440 ( — 47.25 ). 405 . , FEP-. , «» . , -. , «» , . . , , . , . , .



خلفية



كيف أتيت إلى هذا ولماذا بدأت في كتابة البرامج الثابتة الخاصة بي بدلاً من مجرد تعديل كود المصدر من الشركة المصنعة لنفسي.



اتضح أن الخلفية الدرامية كانت طويلة ، لذا قمت بإزالتها تحت الجناح
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- , . , , - .



إذن ما لدينا:



  • مجموعة MKS DLP ، والتي تتضمن: اللوحة الأم ، شاشة عرض واجهة 3.5 بوصة 480x320 وشاشة خلفية 5.5 بوصة 2560x1440
  • مصادر أصلية من الشركة المصنعة
  • مخطط اللوحة الأم (بدون أسماء القيم النشطة والاسمية للمكونات السلبية)


تعتمد اللوحة الأم على متحكم STM32F407. للتحكم في عرض الإضاءة الخلفية ، تحتوي اللوحة على FPGA من الشركة المصنعة الصينية GW1N-LV4LQ144ES و SDRAM واثنين من شرائح واجهة SSD2828 MIPI. يقوم المتحكم الدقيق بتوجيه صورة الطبقة إلى FPGA ، ويقوم FPGA بتخزينها في SDRAM ومن هناك يقوم بتحديث العرض من خلال SSD2828. بالمناسبة ، لا توفر الشركة المصنعة تكوين FPGA (البرنامج الثابت) في كود المصدر: (بالإضافة إلى ذلك ، تحتوي اللوحة الأم على:



  • مدخلات الطاقة 12-24 فولت
  • USB A /
  • A4988
  • Z —
  • WiFi
  • FLASH- W25Q64
  • EEPROM- AT24C16


يتم توصيل شاشة الواجهة مع لوحة اللمس المقاومة بكابل مسطح 40 دبوس. وحدة التحكم في الشاشة - ILI9488 ، وحدة تحكم لوحة اللمس - HR2046 (تناظرية لـ TSC2046).



لتهيئة الأجهزة الطرفية ، استخدمت برنامج STM32CUBE MX. لكنني لم أستخدم النتيجة التي تم الحصول عليها منها مباشرة ، بل أدخلت القطع اللازمة في مصادري. عند العمل مع الأجهزة الطرفية ، استخدمت مكتبات HAL من ST ، وحيث احتجت إلى الحصول على أقصى سرعة ، عملت مع المسجلات مباشرة.



لذلك ، هناك مهمة - يجب أن تكون هذه المجموعة قادرة على طباعة الملفات من محرك أقراص فلاش بسهولة للمستخدم. لقد قسمت هذه المشكلة برمتها تقريبًا إلى أجزاء رئيسية ، مما أدى إلى إصدار ثلاثة منشورات.



أريد أن أحذرك على الفور من أنه لا الكود الذي أتبعه ولا أسلوبي على الإطلاق يتظاهر بأنه مثالي أو حتى جيد فقط ، أنا ألعب بقدر ما أستطيع. البرمجة بالنسبة لي هواية أكثر من كونها مهنة. لذا من فضلك لا تحكم بمعايير صارمة للغاية.



1. واجهة المستخدم



الأول كان تهيئة العرض. لا يوجد شيء مثير للاهتمام هنا ، التسلسل القياسي لوحدة التحكم ILI9488. لقد قمت بنزعها من المصادر الأصلية ، مع استبعاد رمز التهيئة لأنواع أخرى من شاشات العرض (والتي ربما بقيت هناك من حياة FDM لهذه المصادر). ثم دخلت في الخطوط.



1.1 الخطوط



هناك العديد من مكتبات الخطوط للميكروكونترولر على الشبكة ، لكن الغالبية العظمى منها تعمل مع الخطوط أحادية المسافة ، وأنا لا أحب ذلك حقًا. هذا عندما يكون لجميع الأحرف نفس العرض ، مثل الحرف "z" ، مثل الحرف "i". كتبت ذات مرة مكتبة خطوط متناسبة لأحد مشاريعي الأليفة. يستخدم مصفوفتين لكل خط - مصفوفة تحتوي على بيانات بت للأحرف نفسها ومصفوفة بعرض كل حرف. وهيكل صغير مع معلمات الخط - مؤشرات إلى المصفوفات ، ارتفاع الخط ، عدد الأحرف في الخط:



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


يبدو أن تنظيم الخط هذا يجب أن يشغل مساحة ذاكرة أكبر من مجرد صورة نقطية أحادية المسافة ، لكن هذا ليس صحيحًا تمامًا. أولاً ، ينتج عن الفضاء الأحادي بحد ذاته فائض من البيانات المخزنة. على سبيل المثال ، إذا كان الخط الذي يبلغ ارتفاعه 8 بكسل وعرضه 5 بكسل ، يكفي 1 بايت (عرض 1 بت وارتفاع 8 بت) للحرف "i" ، فسيظل يستغرق 5 بايت من البيانات (عرض 5 بت وارتفاع 8 بت) منذ العرض ثابت. ثانيًا ، كقاعدة عامة ، في مثل هذه الخطوط ، تتم المحاذاة على حدود البايت لكل سطر أو كل عمود ، اعتمادًا على كيفية تنظيم البيانات.



على سبيل المثال ، خذ نفس الخط 5x8. إذا تم تخزين بيانات البت سطرًا بسطر ، فسيكون هناك فائض بمقدار 3 بتات لكل سطر. أو 3 بايت لكل حرف:



صورة



أو خط 7 × 12 مع تخزين البيانات في أعمدة ، ثم هناك فائض من البيانات يبلغ 4 بت لكل عمود أو 3.5 بايت لكل حرف:



صورة



في مكتبتي ، تكون بيانات البت مستمرة للحرف وتكون المحاذاة على حدود البايت فقط في نهاية الحرف.



بالإضافة إلى ذلك ، هناك خدعة أخرى صغيرة تسمح لك بتقليل حجم الخط المخزن بشكل طفيف: قد لا تحتوي الشخصية على بيانات بت ، ولكنها تشير إلى حرف آخر بنفس النمط. على سبيل المثال ، الأحرف السيريلية "A" ، "B" ، "E" ، "K" ، إلخ. يمكن أن تحتوي على إشارة إلى أحرف لاتينية بنفس النمط يتم ذلك عن طريق تحديد قيمة سالبة لعرض الحرف المقابل في صفيف عرض الحرف. إذا كانت هناك قيمة سالبة هناك ، فسيتم أخذ صورة هذا الحرف من الحرف الموجود في الموضع (العرض * -1).



هذا هو الإجراء الخاص بإيجاد حرف في مصفوفة:



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]);
}


يؤدي كل هذا غالبًا إلى زيادة كمية البيانات للخط. ناهيك عن أن النوع النسبي يبدو أكثر طبيعية.



سرعة عرض مثل هذا الخط مناسبة تمامًا نظرًا لإخراج الإطارات - يتم إعطاء الشاشة أولاً أمرًا لتقييد نافذة الإخراج بحجم الحرف في الموضع المطلوب ، ثم يتم سكب بيانات الحرف بأكمله في دفق واحد. ليست هناك حاجة لضبط الإحداثيات بشكل منفصل لكل بكسل.



على سبيل المثال ، في الصورة أدناه ، تم عرض النص الأزرق والخط الأبيض العلوي بواسطة مكتبتي ، والخط السفلي الأبيض - بواسطة المكتبة القياسية التي تشبه اردوينو من المصادر الأصلية:



صورة



تم تقديم النص الأزرق أسرع عدة مرات من الخط الأبيض السفلي.



في الوقت نفسه ، اضطررت إلى ابتكار أداة مساعدة لإنشاء مصفوفات خطوط جاهزة للاستخدام في برنامج من صورة. في Photoshop ، يتم إنشاء صورة بالارتفاع المطلوب بكل أحرف الخط ، ثم يتم إدخال إحداثيات X للعمود الأخير من كل حرف في الملف النصي يدويًا ، ثم يتم تعيين الأداة المساعدة على الصورة وهذا الملف النصي. يؤدي هذا إلى إنشاء ملف .c مع المصفوفات المطلوبة. مملة بعض الشيء ، بالطبع ، لكنها بسيطة.



إن إجراء عرض النص قادر على التفاف النص إلى سطر جديد في نهاية الشاشة أو بواسطة حرف تغذية سطر مصادفة ، ويمكن محاذاة إلى اليسار واليمين والوسط ، والحد من المنطقة التي لن يمر النص بعدها (سيتم قطعها). وهي قادرة على عرض الرموز مع رسم الخلفية مع لون الخلفية أو مع الحفاظ على الخلفية. يعمل الخيار الثاني بشكل أبطأ ، حيث لم يعد من الممكن ملء بيانات الأحرف في العرض في دفق واحد ، لكنه لا يزال سريعًا بما يكفي بحيث لا يكون ناتج 3-4 خطوط مرئيًا للعين.



1.2 عرض صور الواجهة



بالنسبة لواجهة المستخدم ، تحتاج إلى عرض الصور - الخلفية والأيقونات والأزرار. في البداية ، قررت عدم إزعاج الكثير وتخزين جميع الصور بتنسيق bmp. في ذاكرة فلاش 8 ميجابايت المتوفرة على اللوحة. وحتى أنني كتبت إجراءً لذلك. يتم حفظ الملف بتنسيق 16 بت (R5 G6 B5) بترتيب سطر من طرف إلى طرف أو من طرف إلى طرف ، وقد يتم تغذيته بالفعل مباشرة إلى روتين العرض. لكن حجم صورة الخلفية 480x320 يزيد عن 300 كيلوبايت. بالنظر إلى أن بعضًا من ذاكرة الفلاش هذه سيتم تخصيصها لتحديثات البرامج الثابتة ، فإن 30 صورة خلفية ستشغل كل الذاكرة. يبدو كثيرًا ، لكنه لا يزال أقل مما أرغب في الحصول عليه ، فقط في حالة. ولكن يجب أن يكون هناك أيضًا أزرار وأيقونات وما إلى ذلك. لذلك ، تقرر تحويل الصور إلى نوع من التنسيق المضغوط.



لا توجد العديد من الخيارات مع الضغط - كل الخوارزميات التي تضغط الصور بشكل جيد أو أقل تتطلب إما ذاكرة وصول عشوائي مناسبة (وفقًا لمعايير وحدة التحكم الدقيقة) أو قدرًا مناسبًا من الوقت لفك الضغط. من ناحية أخرى ، يجب عرض الصور ، بدون اختناق بسرعة ، ومن المستحسن ألا تشبه الصورة عند العرض شريط تقدم الزحف. لذلك ، استقرت على ضغط RLE - 1 بايت يشفر عدد التكرارات ، والاثنان اللاحقان له - اللون. لهذا ، تمت كتابة أداة مساعدة تحول ملفات .bmp إلى صور مضغوطة بهذه الطريقة. يتكون الرأس من 4 بايت فقط - 2 بايت لعرض الصورة وارتفاعها. في المتوسط ​​، يتم ضغط صور الخلفية بهذه الطريقة بمعدل 5-7 مرات ، ويعتمد ذلك بشدة على حجم المناطق أحادية اللون (وهو أمر متوقع). على سبيل المثال ، تقلصت صورة كهذه من 307 كيلوبايت إلى 74 كيلوبايت:



صورة



لكن هذا - ما يصل إلى 23 كيلوبايت من نفس 307:





بالمناسبة ، مصمم لي أكثر إمتصاصًا من المبرمج ...



لقد كنت راضيًا عن هذه النتيجة. يتم فك تشفير الصور وعرضها بسرعة كبيرة - حوالي 40 مللي ثانية لكل صورة خلفية كاملة. لذلك استقرت على هذا الخيار.



وبالمناسبة ، فإن التبديل إلى وضع DMA لإخراج البيانات إلى الشاشة لا يعطي أي تسريع للإخراج تقريبًا. يتم توصيل الشاشة عبر ناقل بيانات خارجي 16 بت كذاكرة خارجية ، لكن توقيتها محزن إلى حد ما ، مما يلغي تقريبًا مزايا إخراج DMA على إخراج البكسل اليدوي.



1.3 إطار عمل واجهة المستخدم الرسومية



يتم عرض النصوص ، يتم رسم الصور ، حان الوقت الآن للتفكير في كيفية تنظيم أساس واجهة المستخدم.



مع لوحة اللمس ، كل شيء بسيط - يقوم المتحكم الدقيق باستمرار باستقصاء وحدة التحكم في لوحة اللمس عن طريق المقاطعات ويقيم آخر 4 نتائج تم الحصول عليها ، ويترجمها إلى إحداثيات عرض. وهكذا ، فإن حالة المستشعر معروفة في أي وقت - سواء تم الضغط عليه أم لا ، وإذا تم الضغط عليه ، في أي مكان. طبقة أخرى بين لوحة اللمس والجزء الرئيسي من البرنامج هي إجراء معالجة نقرات الأزرار ، والتي كانت تتجول من مشروع إلى آخر مع تعديلات صغيرة لظروف محددة لبعض الوقت.



هنا ملخص موجز لكيفية عملها.
«». (100-150 ). , «». , . , , «», . , «», «». «», «». - «» «», . ( «»), - . , , .



تعمل لوحة اللمس كزر الواجهة الوحيد ، فقط إلى جانب حقيقة الضغط عليها ، يتم أيضًا تحليل إحداثيات النقرة.



الآن يجب القيام بكل شيء حتى يمكن عرض مجموعة متنوعة من عناصر الواجهة على الشاشة ، والتي قد تتفاعل أو لا تتفاعل مع النقرات ، والتحديث حسب الأحداث ، ولها أحجام وصور مختلفة ، وما إلى ذلك.



في النهاية ، جئت إلى هذا المخطط: تتكون الواجهة من نوعين رئيسيين من العناصر - الشاشات والأزرار.



الشاشة هي نوع من حاوية ملء الشاشة للأزرار. الشاشة لها الخصائص التالية:



  • الصورة الخلفية
  • لون الخلفية
  • طريقة رسم الخلفية - ملء لون الخلفية أو عرض صورة
  • نص العنوان
  • لون نص العنوان
  • خط نص الرأس
  • مؤشر إلى الشاشة الرئيسية (والتي يجب الرجوع إليها عند إغلاق هذا)
  • مجموعة من المؤشرات على الأزرار
  • مؤشر إلى إجراء حدث (يسمى دوريًا في حلقة البرنامج الرئيسية)
  • مؤشر إلى روتين عرض الشاشة


هيكل الشاشة
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;




لا يمكن أن تكون الأزرار في الواقع أزرارًا فحسب ، بل يمكن أن تكون أيضًا نصًا أو رمزًا أو نوعًا من عناصر التغيير مثل عداد أو ساعة. لقد تبين أنه من الملائم دمج كل شيء في نوع واحد ، وتعيين سلوك كل زر محدد من خلال خصائصه.



خصائص الزر:



  • الإحداثيات على الشاشة
  • لون الخلفية
  • صورة الخلفية للدولة الحرة
  • صورة الخلفية للحالة المضغوطة
  • صورة الخلفية لحالة المعوقين
  • صورة الخلفية للحالة النشطة (للعنصر النشط لمجموعة أزرار الاختيار ، على سبيل المثال)
  • طريقة التقديم - الصورة أو لون الخلفية
  • ما إذا كان سيتم إعادة رسم الزر عند الضغط عليه وتحريره
  • زر كتابة
  • ( )
  • (, )
  • ( )
  • ,
  • ,


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;




بمساعدة هذه المجموعة من الخصائص ، أصبح من الممكن إنشاء أي شيء تقريبًا في الواجهة بناءً على هذا العنصر. إذا كانت الشاشة أو الزر يحتوي على مؤشر لأي من الإجراءات فارغة ، فسيتم استدعاء الإجراء القياسي المقابل. بدلاً من مؤشر الإجراء للضغط على زر ، على سبيل المثال ، قد يكون هناك معرّف خاص يشير إلى أنك بحاجة إلى الانتقال إلى الطفل أو الشاشة السابقة ، ثم الإجراء القياسي سيفعل ذلك. بشكل عام ، تغطي الإجراءات القياسية جميع حالات استخدام الأزرار العادية تقريبًا ويتعين عليك إنشاء الإجراءات الخاصة بك لزر فقط في الحالات غير القياسية - على سبيل المثال ، عندما يعمل الزر مثل الساعة ، أو كعنصر في قائمة الملفات.



ولكن ما كانت تفتقر إليه إمكانيات هذا المخطط هو النوافذ المشروطة التي تحتوي على رسائل أو أسئلة (مثل MessageBox في واجهة برمجة تطبيقات Windows) ، لذلك قمت بعمل نوع منفصل من الشاشات لهم. لا توجد صور خلفية والحجم الذي يحدده العنوان أو الرسالة نفسها. يمكن إنشاء هذه الرسائل بأربعة إصدارات - بأزرار "نعم / لا" أو أزرار "موافق / إلغاء" أو بزر "موافق" واحد أو بدون أزرار على الإطلاق (مثل "انتظر ، يتم تحميل البيانات ...").







هيكل نافذة الرسالة
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;




على أساس هذه الأنواع الثلاثة تم بناء الواجهة بالكامل ، واتضح أنها مرنة للغاية. الآن تم ترميز تهيئة جميع العناصر في البرنامج الثابت ، ولكن هناك فكرة لمنح المستخدمين الفرصة لإنشاء واجهة خاصة بهم عن طريق وصف خصائص جميع العناصر في ملف التكوين وإضافة عدد من الصور الضرورية. من الناحية النظرية ، سيكون من الممكن تغيير محتويات الشاشات المختلفة - الأزرار التي يجب وضعها على الشاشة الرئيسية ، والأزرار لشاشة الخدمة ، وما إلى ذلك.



1.4 متعدد اللغات







كانت التعددية اللغوية في المهام في البداية. لكن في البداية ، سرت في المسار الغبي - عند تهيئة جميع العناصر ، قمت بتعيين نصوص لهم من جدول اللغة الذي كان هو الحالي. يعني تبديل اللغة إعادة تهيئة جميع عناصر النص ، وعندما كان هناك أكثر من شاشتين في الواجهة ، وأكثر من 20 زرًا وتسمية ، أدركت أنه لا يمكنني العيش على هذا النحو بعد الآن. ثم قدم جميع الإشارات إلى النصوص من خلال الإجراء. يُعطى الإجراء معرف نص كمعامل ، ويعيد مؤشرًا إلى النص باللغة الحالية:



	char *mshortname = LANG_GetString(LSTR_SHORT_JANUARY);


عند تغيير اللغة ، يتغير المؤشر ببساطة من مجموعة من النصوص في اللغة القديمة إلى مصفوفة بها نصوص باللغة الجديدة:



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


جميع النصوص المصدرية بترميز UTF-8. اضطررت أيضًا إلى العبث بهذه الترميزات. النصوص - في UTF-8 ، وملفات السيريلية - في Unicode-16 ، وبعض السلاسل - في ANSI العادي. لم أرغب في سحب مجموعة كاملة من المكتبات لدعم ترميز متعدد البايت في البرنامج الثابت ، لذلك تمت كتابة العديد من الوظائف للتحويل من الترميز إلى الترميز وللعمليات مع النصوص بترميزات مختلفة ، على سبيل المثال ، إضافة سلسلة UTF-8 إلى نهاية سلسلة Unicode16.

إن إضافة لغة جديدة قد اختصر الآن لإنشاء جدول نصوص فيه وتغيير قيمة LNG_LANGS_COUNT الثابتة. صحيح ، لا يزال هناك سؤال حول الخطوط ، إذا كانت اللغة الجديدة تستخدم رموزًا غير السيريلية واللاتينية ... الآن أنا أؤيد اللغة الروسية المترجمة من Google والإنجليزية في شفرة المصدر.



1.5 تخزين الصور والموارد الأخرى



لتخزين الموارد الكبيرة ، تحتوي اللوحة على فلاش SPI W25Q64 سعة 8 ميجابايت. في البداية ، أردت أن أفعل كما هو الحال دائمًا - تعيين إزاحة لكل مورد داخل الفلاش وحفظها هناك كبيانات ثنائية فقط. ولكن بعد ذلك أدركت أن المشكلات المتعلقة بهذه الطريقة مضمونة لي بمجرد أن يتجاوز عدد الموارد المحفوظة بضع عشرات وأريد تغيير ، على سبيل المثال ، بعض الصور المحفوظة في المرتبة السادسة بالترتيب. إذا زاد حجمه ، فسيتعين عليك تغيير عناوين جميع الموارد التالية وإعادة كتابتها. أو اترك مساحة فارغة بحجم غير معروف بعد كل مورد - من يعرف كيف يمكن أن يتغير أي من الموارد. نعم ، في نعش رأيت هذه الضجة :) لذلك بصقت ونظمت نظام ملفات على هذا الفلاش.بحلول ذلك الوقت ، كان لدي بالفعل نظام ملفات USB يعتمد على مكتبة FatFS ، لذلك كان كافياً بالنسبة لي ببساطة كتابة وظائف قراءة / كتابة منفصلة للقطاع منخفض المستوى. شيء واحد فقط أزعجني قليلاً - حجم القطاع الممسوح في هذه الدائرة الدقيقة يصل بالفعل إلى 4 كيلوبايت. هذا ، أولاً ، يؤدي إلى حقيقة أن الملفات سوف تشغل مساحة في أجزاء من 4 كيلوبايت (كتب الملف 200 بايت - استغرق 4 كيلوبايت من الفلاش) ، وثانيًا ، المخزن المؤقت في بنية كل مؤشر ملف سوف يأكل نفس 4 كيلوبايت من ذاكرة الوصول العشوائي ، والتي في الميكروكونترولر ليس بهذا القدر - 192 كيلوبايت. يمكن للمرء ، بالطبع ، أن يكون منحرفًا ويكتب وظائف منخفضة المستوى حتى يتمكنوا من الكتابة والقراءة في أجزاء أصغر ، والإبلاغ عن حجم القطاع ، على سبيل المثال ، 512 بايت. لكن هذا من شأنه أن يبطئ الفلاش ، لذلك ترك حجم القطاع عند 4 كيلوبايت.لذلك يمكن الوصول إلى أي مورد ببساطة من خلال اسم الملف الخاص به ، والذي اتضح أنه ملائم للغاية. في الوقت الحالي ، على سبيل المثال ، تجاوز عدد الموارد المخزنة بالفعل 90. وقد جعلت تحديثها بسيطًا قدر الإمكان - تتم كتابة الموارد المحدثة (أو الجديدة) على محرك أقراص USB محمول في دليل معين ، ويتم إدخال محرك الأقراص المحمول في اللوحة ، ويتم إعادة تشغيل اللوحة في وضع الخدمة (أثناء قم بتشغيل أو إعادة التشغيل ، اضغط مع الاستمرار على الزاوية اليمنى العليا من الشاشة) وقم تلقائيًا بنسخ جميع الملفات الموجودة في هذا الدليل من محرك أقراص فلاش USB إلى فلاش SPI.يتم إعادة تشغيل اللوحة في وضع الخدمة (أثناء التشغيل أو إعادة التشغيل ، اضغط مع الاستمرار على الزاوية اليمنى العليا من الشاشة) ونسخ جميع الملفات الموجودة في هذا الدليل تلقائيًا من محرك أقراص USB المحمول إلى فلاش SPI.يتم إعادة تشغيل اللوحة في وضع الخدمة (أثناء التشغيل أو إعادة التشغيل ، اضغط مع الاستمرار على الزاوية اليمنى العليا من الشاشة) ونسخ جميع الملفات الموجودة في هذا الدليل تلقائيًا من محرك أقراص USB المحمول إلى فلاش SPI.







يتبع...



ربما ظهر الجزء الأكثر ضخامة على الواجهة. إذا تبين أن هذه المقالة تهم المجتمع ، فسأحاول في الجزء الثاني استيعاب كل شيء آخر.



حسنًا ، سأكون سعيدًا بالأسئلة والتعليقات.



- الجزء الأول: 1. واجهة المستخدم.

- الجزء 2: 2. العمل مع نظام الملفات على محرك أقراص فلاش USB. 3. تحكم محرك متدرج لحركة المنصة.

- الجزء 3: 4. عرض صور الطبقات على شاشة الإضاءة الخلفية. 5. كل شيء صغير ، مثل التحكم في الإضاءة والمراوح ، وتحميل وحفظ الإعدادات ، إلخ. 6. ميزات إضافية للراحة والملاءمة.



الروابط



مجموعة MKS DLP على

مصادر البرامج الثابتة الأصلية من Aliexpress من الشركة المصنعة في

مخططات GitHub من الشركة المصنعة لنسختين من اللوحة على GitHub

مصادري على GitHub



All Articles