Procházet zdrojové kódy

Merge branch 'master' of https://gitee.com/openLuat/LuatOS

liszt123 před 1 měsícem
rodič
revize
c5f26f395f
100 změnil soubory, kde provedl 9570 přidání a 1909 odebrání
  1. 2 0
      .gitignore
  2. 20 1
      components/fatfs/luat_lib_fatfs.c
  3. 3 2
      components/lcd/luat_lib_lcd_jpg.c
  4. 143 10
      components/multimedia/luat_lib_multimedia_codec.c
  5. 6 6
      components/multimedia/vtool/src/luat_vtool_mp4box.c
  6. 9 15
      components/ui/sdl2/luat_lcd_sdl2.c
  7. 3 5
      components/ui/sdl2/luat_sdl2.c
  8. 511 491
      luat/vfs/luat_inline_libs.c
  9. 516 496
      luat/vfs/luat_inline_libs_64bit_size32.c
  10. 0 230
      module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirLCD_1000/AirLCD_1000.lua
  11. 1 0
      module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirLCD_1000/exeasyui/hw_drv/hw_customer_font_drv.lua
  12. 62 0
      module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirLCD_1000/exeasyui/hw_drv/hw_default_font_drv.lua
  13. 65 0
      module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirLCD_1000/exeasyui/hw_drv/hw_gtfont_drv.lua
  14. 65 0
      module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirLCD_1000/exeasyui/hw_drv/hw_hzfont_drv.lua
  15. binární
      module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirLCD_1000/exeasyui/images/1.jpg
  16. binární
      module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirLCD_1000/exeasyui/images/2.jpg
  17. binární
      module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirLCD_1000/exeasyui/images/3.jpg
  18. binární
      module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirLCD_1000/exeasyui/images/4.jpg
  19. binární
      module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirLCD_1000/exeasyui/images/5.jpg
  20. binární
      module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirLCD_1000/exeasyui/images/logo.jpg
  21. 104 0
      module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirLCD_1000/exeasyui/main.lua
  22. 222 0
      module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirLCD_1000/exeasyui/readme.md
  23. 70 0
      module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirLCD_1000/exeasyui/tp_key_drv/key_drv.lua
  24. 214 0
      module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirLCD_1000/exeasyui/ui/component_page.lua
  25. 156 0
      module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirLCD_1000/exeasyui/ui/default_font_page.lua
  26. 164 0
      module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirLCD_1000/exeasyui/ui/gtfont_page.lua
  27. 129 0
      module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirLCD_1000/exeasyui/ui/home_page.lua
  28. 293 0
      module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirLCD_1000/exeasyui/ui/hzfont_page.lua
  29. 333 0
      module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirLCD_1000/exeasyui/ui/key_handler.lua
  30. 178 0
      module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirLCD_1000/exeasyui/ui/ui_main.lua
  31. binární
      module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirLCD_1000/lcd/font_drv/customer_font_24.bin
  32. 16 0
      module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirLCD_1000/lcd/font_drv/customer_font_drv.lua
  33. 49 0
      module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirLCD_1000/lcd/font_drv/gtfont_drv.lua
  34. 1 0
      module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirLCD_1000/lcd/font_drv/hzfont_drv.lua
  35. binární
      module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirLCD_1000/lcd/images/logo.jpg
  36. 52 0
      module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirLCD_1000/lcd/lcd_drv/exlcd_drv.lua
  37. 80 0
      module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirLCD_1000/lcd/lcd_drv/lcd_drv.lua
  38. 113 0
      module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirLCD_1000/lcd/main.lua
  39. 247 0
      module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirLCD_1000/lcd/readme.md
  40. 70 0
      module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirLCD_1000/lcd/tp_key_drv/key_drv.lua
  41. 117 0
      module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirLCD_1000/lcd/ui/customer_font_page.lua
  42. 391 0
      module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirLCD_1000/lcd/ui/gtfont_page.lua
  43. 179 0
      module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirLCD_1000/lcd/ui/home_page.lua
  44. 177 0
      module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirLCD_1000/lcd/ui/lcd_page.lua
  45. 186 0
      module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirLCD_1000/lcd/ui/ui_main.lua
  46. binární
      module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirLCD_1000/logo.jpg
  47. 0 207
      module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirLCD_1000/readme.md
  48. 0 187
      module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirLCD_1000/ui_main.lua
  49. 1 1
      module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirLCD_1010/exeasyui/hw_drv/hw_default_font_drv.lua
  50. 1 1
      module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirLCD_1010/exeasyui/hw_drv/hw_gtfont_drv.lua
  51. 1 1
      module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirLCD_1010/exeasyui/hw_drv/hw_hzfont_drv.lua
  52. 1 1
      module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirLCD_1010/exeasyui/main.lua
  53. 11 9
      module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirLCD_1010/exeasyui/readme.md
  54. 2 2
      module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirLCD_1010/lcd/font_drv/gtfont_drv.lua
  55. 1 1
      module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirLCD_1010/lcd/lcd_drv/lcd_drv.lua
  56. 1 1
      module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirLCD_1010/lcd/main.lua
  57. 43 12
      module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirLCD_1010/lcd/readme.md
  58. 189 0
      module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirRC522_1000/AirRC522_1000.lua
  59. 73 0
      module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirRC522_1000/main.lua
  60. 646 0
      module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirRC522_1000/rc522.lua
  61. 156 0
      module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirRC522_1000/readme.md
  62. binární
      module/Air780EPM/accessory_board/AirLCD_1000/lcd/font_drv/customer_font_12.bin
  63. binární
      module/Air780EPM/accessory_board/AirLCD_1000/lcd/font_drv/customer_font_22.bin
  64. 16 0
      module/Air780EPM/accessory_board/AirLCD_1000/lcd/font_drv/customer_font_drv.lua
  65. binární
      module/Air780EPM/accessory_board/AirLCD_1000/lcd/images/logo.jpg
  66. 52 0
      module/Air780EPM/accessory_board/AirLCD_1000/lcd/lcd_drv/exlcd_drv.lua
  67. 80 0
      module/Air780EPM/accessory_board/AirLCD_1000/lcd/lcd_drv/lcd_drv.lua
  68. 106 0
      module/Air780EPM/accessory_board/AirLCD_1000/lcd/main.lua
  69. 192 0
      module/Air780EPM/accessory_board/AirLCD_1000/lcd/readme.md
  70. 70 0
      module/Air780EPM/accessory_board/AirLCD_1000/lcd/tp_key_drv/key_drv.lua
  71. 120 0
      module/Air780EPM/accessory_board/AirLCD_1000/lcd/ui/customer_font_page.lua
  72. 158 0
      module/Air780EPM/accessory_board/AirLCD_1000/lcd/ui/home_page.lua
  73. 146 0
      module/Air780EPM/accessory_board/AirLCD_1000/lcd/ui/lcd_page.lua
  74. 146 0
      module/Air780EPM/accessory_board/AirLCD_1000/lcd/ui/ui_main.lua
  75. binární
      module/Air780EPM/accessory_board/AirLCD_1010/lcd/font_drv/customer_font_12.bin
  76. binární
      module/Air780EPM/accessory_board/AirLCD_1010/lcd/font_drv/customer_font_22.bin
  77. 16 0
      module/Air780EPM/accessory_board/AirLCD_1010/lcd/font_drv/customer_font_drv.lua
  78. binární
      module/Air780EPM/accessory_board/AirLCD_1010/lcd/images/logo.jpg
  79. 52 0
      module/Air780EPM/accessory_board/AirLCD_1010/lcd/lcd_drv/exlcd_drv.lua
  80. 80 0
      module/Air780EPM/accessory_board/AirLCD_1010/lcd/lcd_drv/lcd_drv.lua
  81. 110 0
      module/Air780EPM/accessory_board/AirLCD_1010/lcd/main.lua
  82. 199 0
      module/Air780EPM/accessory_board/AirLCD_1010/lcd/readme.md
  83. 60 0
      module/Air780EPM/accessory_board/AirLCD_1010/lcd/tp_key_drv/extp_drv.lua
  84. 69 0
      module/Air780EPM/accessory_board/AirLCD_1010/lcd/tp_key_drv/tp_drv.lua
  85. 137 0
      module/Air780EPM/accessory_board/AirLCD_1010/lcd/ui/customer_font_page.lua
  86. 120 0
      module/Air780EPM/accessory_board/AirLCD_1010/lcd/ui/home_page.lua
  87. 174 0
      module/Air780EPM/accessory_board/AirLCD_1010/lcd/ui/lcd_page.lua
  88. 142 0
      module/Air780EPM/accessory_board/AirLCD_1010/lcd/ui/ui_main.lua
  89. 188 0
      module/Air780EPM/demo/accessory_board/AirRC522_1000/AirRC522_1000.lua
  90. 73 0
      module/Air780EPM/demo/accessory_board/AirRC522_1000/main.lua
  91. 646 0
      module/Air780EPM/demo/accessory_board/AirRC522_1000/rc522.lua
  92. 152 0
      module/Air780EPM/demo/accessory_board/AirRC522_1000/readme.md
  93. 0 230
      module/Air8000/demo/accessory_board/AirLCD_1000/AirLCD_1000.lua
  94. 1 0
      module/Air8000/demo/accessory_board/AirLCD_1000/exeasyui/hw_drv/hw_customer_font_drv.lua
  95. 62 0
      module/Air8000/demo/accessory_board/AirLCD_1000/exeasyui/hw_drv/hw_default_font_drv.lua
  96. 65 0
      module/Air8000/demo/accessory_board/AirLCD_1000/exeasyui/hw_drv/hw_gtfont_drv.lua
  97. 65 0
      module/Air8000/demo/accessory_board/AirLCD_1000/exeasyui/hw_drv/hw_hzfont_drv.lua
  98. binární
      module/Air8000/demo/accessory_board/AirLCD_1000/exeasyui/images/1.jpg
  99. binární
      module/Air8000/demo/accessory_board/AirLCD_1000/exeasyui/images/2.jpg
  100. binární
      module/Air8000/demo/accessory_board/AirLCD_1000/exeasyui/images/3.jpg

+ 2 - 0
.gitignore

@@ -101,3 +101,5 @@ cmd.lnk
 
 luat/demo/
 tools/snippet.json
+
+self_docs/

+ 20 - 1
components/fatfs/luat_lib_fatfs.c

@@ -131,6 +131,7 @@ static int fatfs_mount(lua_State *L)
 			LLOGD("init sdcard at spi=%d cs=%d", spit->spi_id, spit->spi_cs);
 			diskio_open_spitf(0, (void*)spit);
 		}
+	#ifdef LUAT_USE_SDIO
 	}else if(fatfs_mode == DISK_SDIO){
 		luat_fatfs_sdio_t *fatfs_sdio = luat_heap_malloc(sizeof(luat_fatfs_sdio_t));
 		if (fatfs_sdio == NULL) {
@@ -145,13 +146,16 @@ static int fatfs_mount(lua_State *L)
 
 		LLOGD("init FatFS at sdio");
 		diskio_open_sdio(0, (void*)fatfs_sdio);
+	#endif
+	#if defined(LUA_USE_LINUX) || defined(LUA_USE_WINDOWS) || defined(LUA_USE_MACOSX)
 	}else if(fatfs_mode == DISK_RAM){
 		LLOGD("init ramdisk at FatFS");
 		diskio_open_ramdisk(0, luaL_optinteger(L, 3, 64*1024));
+	#endif
 	}else if(fatfs_mode == DISK_USB){
 
 	}else{
-		LLOGD("fatfs_mode error");
+		LLOGD("fatfs_mode error %d", fatfs_mode);
 		lua_pushboolean(L, 0);
 		lua_pushstring(L, "fatfs_mode error");
 		return 2;
@@ -176,6 +180,21 @@ static int fatfs_mount(lua_State *L)
 			if (re == FR_OK) {
 				re = f_mount(fs, mount_point, 1);
 				LLOGD("remount again %d", re);
+				if (re == FR_OK) {
+					LLOGI("sd/tf mount success after auto format");
+				}
+				else {
+					LLOGE("sd/tf mount failed again %d after auto format", re);
+					lua_pushboolean(L, 0);
+					lua_pushstring(L, "mount error");
+					return 2; 
+				}
+			}
+			else {
+				LLOGE("sd/tf format failed %d", re);
+				lua_pushboolean(L, 0);
+				lua_pushstring(L, "format error");
+				return 2;
 			}
 		}
 	}

+ 3 - 2
components/lcd/luat_lib_lcd_jpg.c

@@ -138,9 +138,10 @@ static int decode_out_func (JDEC* jd, void* bitmap, JRECT* rect){
     uint16_t* tmp = (uint16_t*)bitmap;
 
     // rgb高低位swap
-	uint16_t idx = 0;
+    uint16_t idx = 0;
 	for (size_t y = rect->top; y <= rect->bottom; y++){
-		uint16_t offset = y*buff_info->width + rect->left;
+        // 防止大图时 y*width 溢出 16bit,改用 size_t 计算偏移
+        size_t offset = (size_t)y * buff_info->width + rect->left;
 		for (size_t x = rect->left; x <= rect->right; x++){
 			if (lcd_dft_conf->endianness_swap)
 				buff_info->buff[offset] = ((tmp[idx] >> 8) & 0xFF)+ ((tmp[idx] << 8) & 0xFF00);

+ 143 - 10
components/multimedia/luat_lib_multimedia_codec.c

@@ -18,6 +18,10 @@
 #ifdef LUAT_SUPPORT_AMR
 #include "interf_enc.h"
 #include "interf_dec.h"
+#include "dec_if.h"
+static const uint8_t  amr_nb_byte_len[16] = {12, 13, 15, 17, 19, 20, 26, 31, 5, 0, 0, 0, 0, 0, 0, 0};
+static const uint8_t  amr_wb_byte_len[16] = {17, 23, 32, 36, 40, 46, 50, 58, 60, 5,0, 0, 0, 0, 0, 0};
+typedef void (*amr_decode_fun_t)(void* state, const unsigned char* in, short* out, int bfi);
 #endif
 
 #ifdef LUAT_USE_AUDIO_G711
@@ -39,6 +43,12 @@
 @int 编码等级,部分bsp有内部编解码器,可能需要提前输入编解码等级,不知道的就填7
 @return userdata 成功返回一个数据结构,否则返回nil
 @usage
+-- 目前支持:
+-- codec.MP3 解码
+-- codec.AMR 编码+解码
+-- codec.AMR_WB 编码(部分BSP支持,例如Air780EHM,Air8000)+解码
+-- codec.WAV WAV本身就是PCM数据,无需编解码
+-- codec.ULAW codec.ALAW 编码+解码
 -- 创建解码器
 local decoder = codec.create(codec.MP3)--创建一个mp3的decoder
 -- 创建编码器
@@ -86,6 +96,22 @@ static int l_codec_create(lua_State *L) {
             		return 1;
             	}
             	break;
+#ifdef LUAT_SUPPORT_AMR
+        	case LUAT_MULTIMEDIA_DATA_TYPE_AMR_NB:
+        		coder->amr_coder = Decoder_Interface_init();
+            	if (!coder->amr_coder) {
+            		lua_pushnil(L);
+            		return 1;
+            	}
+         		break;
+         	case LUAT_MULTIMEDIA_DATA_TYPE_AMR_WB:
+        		coder->amr_coder = D_IF_init();
+            	if (!coder->amr_coder) {
+            		lua_pushnil(L);
+            		return 1;
+            	}
+             	break;
+#endif
 #ifdef LUAT_USE_AUDIO_G711
          	case LUAT_MULTIMEDIA_DATA_TYPE_ULAW:
          	case LUAT_MULTIMEDIA_DATA_TYPE_ALAW:
@@ -257,6 +283,46 @@ static int l_codec_get_audio_info(lua_State *L) {
 				LLOGD("head error");
 			}
 			break;
+#ifdef LUAT_SUPPORT_AMR
+		case LUAT_MULTIMEDIA_DATA_TYPE_AMR_NB:
+			luat_fs_fread(temp, 6, 1, fd);
+			if (!memcmp(temp, "#!AMR\n", 6))
+			{
+				coder->buff.addr = luat_heap_malloc(320);
+				if (coder->buff.addr)
+				{
+					num_channels = 1;
+					sample_rate = 8000;
+					audio_format = LUAT_MULTIMEDIA_DATA_TYPE_PCM;
+					result = 1;
+				}
+
+			}
+			else
+			{
+				result = 0;
+			}
+			break;
+		case LUAT_MULTIMEDIA_DATA_TYPE_AMR_WB:
+			luat_fs_fread(temp, 9, 1, fd);
+			if (!memcmp(temp, "#!AMR-WB\n", 9))
+			{
+				coder->buff.addr = luat_heap_malloc(640);
+				if (coder->buff.addr)
+				{
+					num_channels = 1;
+					sample_rate = 16000;
+					audio_format = LUAT_MULTIMEDIA_DATA_TYPE_PCM;
+					result = 1;
+				}
+
+			}
+			else
+			{
+				result = 0;
+			}
+			break;
+#endif
 #ifdef LUAT_USE_AUDIO_G711
 		case LUAT_MULTIMEDIA_DATA_TYPE_ULAW:
 		case LUAT_MULTIMEDIA_DATA_TYPE_ALAW:
@@ -300,13 +366,20 @@ decoder从文件中解析出原始音频数据,比如从MP3文件里解析出P
 @usage
 -- 大内存设备
 local buff = zbuff.create(16*1024)
-local result = codec.data(coder, buff)
+local result = codec.data(coder, buff, 8192)
 -- 小内存设备
 local buff = zbuff.create(8*1024)
 local result = codec.data(coder, buff, 4096)
  */
 static int l_codec_get_audio_data(lua_State *L) {
 	luat_multimedia_codec_t *coder = (luat_multimedia_codec_t *)luaL_checkudata(L, 1, LUAT_M_CODE_TYPE);
+#ifdef LUAT_SUPPORT_AMR
+	amr_decode_fun_t decode_if;
+	uint32_t frame_len;
+	uint8_t *size_table;
+	uint8_t size;
+	uint8_t temp[64];
+#endif
 	uint32_t pos = 0;
 	int read_len;
 	int result = 0;
@@ -400,6 +473,47 @@ GET_MP3_DATA:
 			}
 
 			break;
+#ifdef LUAT_SUPPORT_AMR
+        	case LUAT_MULTIMEDIA_DATA_TYPE_AMR_NB:
+         	case LUAT_MULTIMEDIA_DATA_TYPE_AMR_WB:
+         		if (coder->type == LUAT_MULTIMEDIA_DATA_TYPE_AMR_NB)
+         		{
+         			frame_len = 320;
+         			size_table = amr_nb_byte_len;
+         			decode_if = Decoder_Interface_Decode;
+         		}
+         		else
+         		{
+         			frame_len = 640;
+         			size_table = amr_wb_byte_len;
+         			decode_if = D_IF_decode;
+         		}
+
+         		while ((out_buff->used < mini_output) && is_not_end && ((out_buff->len - out_buff->used) >= frame_len))
+				{
+         			read_len = luat_fs_fread(temp, 1, 1, coder->fd);
+    				if (read_len <= 0)
+    				{
+    					is_not_end = 0;
+    					break;
+    				}
+    				size = size_table[(temp[0] >> 3) & 0x0f];
+    				if (size > 0)
+					{
+						read_len = luat_fs_fread(temp + 1, 1, size, coder->fd);
+						if (read_len <= 0)
+						{
+	    					is_not_end = 0;
+	    					break;
+						}
+					}
+    				decode_if(coder->amr_coder, temp, coder->buff.addr, 0);
+    				memcpy(out_buff->addr + out_buff->used, coder->buff.addr, frame_len);
+    				out_buff->used += frame_len;
+				}
+         		result = 1;
+             	break;
+#endif
 #ifdef LUAT_USE_AUDIO_G711
 		case LUAT_MULTIMEDIA_DATA_TYPE_ULAW:
 		case LUAT_MULTIMEDIA_DATA_TYPE_ALAW:
@@ -446,7 +560,7 @@ GET_MP3_DATA:
 
 
 /**
-编码音频数据,由于flash和ram空间一般比较有限,除了部分bsp有内部amr编码功能,目前只支持amr-nb编码
+编码音频数据,由于flash和ram空间一般比较有限,除了部分bsp有内部amr编码功能以外只支持amr-nb编码
 @api codec.encode(coder, in_buffer, out_buffer, mode)
 @userdata codec.create创建的编解码用的coder
 @zbuff 输入的数据,zbuff形式,从0到used
@@ -644,23 +758,42 @@ static int l_codec_gc(lua_State *L)
 		}
 		break;
 #ifdef LUAT_SUPPORT_AMR
-#ifdef LUAT_USE_INTER_AMR
 	case LUAT_MULTIMEDIA_DATA_TYPE_AMR_NB:
-	case LUAT_MULTIMEDIA_DATA_TYPE_AMR_WB:
-		if (!coder->is_decoder && coder->amr_coder) {
-			luat_audio_inter_amr_coder_deinit(coder->amr_coder);
+		if (coder->amr_coder)
+		{
+			if (!coder->is_decoder)
+			{
+#ifdef LUAT_USE_INTER_AMR
+				luat_audio_inter_amr_coder_deinit(coder->amr_coder);
+#else
+				Encoder_Interface_exit(coder->amr_coder);
+#endif
+			}
+			else
+			{
+				Decoder_Interface_exit(coder->amr_coder);
+			}
 			coder->amr_coder = NULL;
 		}
 		break;
+	case LUAT_MULTIMEDIA_DATA_TYPE_AMR_WB:
+		if (coder->amr_coder)
+		{
+			if (!coder->is_decoder)
+			{
+#ifdef LUAT_USE_INTER_AMR
+				luat_audio_inter_amr_coder_deinit(coder->amr_coder);
 #else
-	case LUAT_MULTIMEDIA_DATA_TYPE_AMR_NB:
-		if (!coder->is_decoder && coder->amr_coder) {
-			Encoder_Interface_exit(coder->amr_coder);
+#endif
+			}
+			else
+			{
+				D_IF_exit(coder->amr_coder);
+			}
 			coder->amr_coder = NULL;
 		}
 		break;
 #endif
-#endif
 #ifdef LUAT_USE_AUDIO_G711
  	case LUAT_MULTIMEDIA_DATA_TYPE_ULAW:
  	case LUAT_MULTIMEDIA_DATA_TYPE_ALAW:

+ 6 - 6
components/multimedia/vtool/src/luat_vtool_mp4box.c

@@ -812,7 +812,7 @@ int luat_vtool_mp4box_close(mp4_ctx_t* ctx) {
         LLOGE("ctx is NULL");
         return -1;
     }
-    LLOGI("开始关闭mp4文件 %s", ctx->path);
+    // LLOGI("开始关闭mp4文件 %s", ctx->path);
     // 刷新缓冲,确保文件大小正确
     buffered_flush(ctx);
     // 然后, 把文件关掉, 重新打开
@@ -823,7 +823,7 @@ int luat_vtool_mp4box_close(mp4_ctx_t* ctx) {
     luat_fs_fseek(ctx->fd, 0, SEEK_END);
     // 把mdat的box大小更新一下
     ret = luat_fs_ftell(ctx->fd);
-    LLOGI("文件当前长度 %d", ret);
+    LLOGI("mp4 file size before mdat %d", ret);
     long int pos = ctx->mdat_offset;
     size_t mdat_len = ret - ctx->mdat_offset;
     // LLOGI("mdat 长度更新为 %d 目标偏移量 %d sizeof(int) %d sizeof(long int) %d", mdat_len, ctx->mdat_offset, sizeof(int), sizeof(long int));
@@ -832,7 +832,7 @@ int luat_vtool_mp4box_close(mp4_ctx_t* ctx) {
         LLOGE("seek mdat offset failed %d", ret);
     }
     ret = luat_fs_ftell(ctx->fd);
-    LLOGD("当前fd偏移量位置 %d 期望 %d", ret, ctx->mdat_offset);
+    // LLOGD("当前fd偏移量位置 %d 期望 %d", ret, ctx->mdat_offset);
     if (ret != (int)ctx->mdat_offset) {
         LLOGE("seek mdat offset failed %d", ret);
         ret = -1;
@@ -915,7 +915,7 @@ clean:
             luat_fs_fseek(ctx->fd, 0, SEEK_END);
             luat_fs_fflush(ctx->fd);
             ret = luat_fs_ftell(ctx->fd);
-            LLOGI("写入完成, 文件最终长度 %d", ret);
+            LLOGI("mp4 file final size %d", ret);
             ret = 0;
             luat_fs_fclose(ctx->fd);
         }
@@ -928,7 +928,7 @@ clean:
         LLOGE("文件句柄为空!!!");
         ret = -10;
     }
-    LLOGD("释放mp4资源, 释放内存");
+    // LLOGD("释放mp4资源, 释放内存");
     // 释放全部资源
     if (ctx->sps) {
         luat_heap_free(ctx->sps);
@@ -970,6 +970,6 @@ clean:
     }
     clean_box(&ctx->box_moov);
     luat_heap_free(ctx);
-    LLOGI("mp4文件关闭完成, box写入结束, 文件已关闭");
+    LLOGI("mp4 file closed, box write finished, file closed");
     return ret;
 }

+ 9 - 15
components/ui/sdl2/luat_lcd_sdl2.c

@@ -9,7 +9,7 @@
 #define LUAT_LOG_TAG "ui_sdl2"
 #include "luat_log.h"
 
-static uint32_t* fb;
+static luat_color_t* fb;
 
 static inline uint32_t luat_color_565to8888(luat_color_t color);
 
@@ -19,7 +19,7 @@ static int sdl2_init(luat_lcd_conf_t* conf) {
         .height = conf->h
     };
     luat_sdl2_init(&sdl2_conf);
-    size_t fb_size = sizeof(uint32_t) * conf->w * conf->h;
+    size_t fb_size = sizeof(luat_color_t) * conf->w * conf->h;
     fb = luat_heap_opt_malloc(LUAT_HEAP_PSRAM, fb_size);
     if (fb == NULL) {
         fb = luat_heap_opt_malloc(LUAT_HEAP_SRAM, fb_size);
@@ -34,7 +34,8 @@ static int sdl2_init(luat_lcd_conf_t* conf) {
             return -1;
         }
     }
-    LLOGD("sdl2_init conf->buff %p, conf->opts %p fb %p", conf->buff, conf->opts, fb);
+    LLOGD("sdl2_init w %d h %d fb_size %u conf->buff %p conf->opts %p fb %p",
+          conf->w, conf->h, (unsigned int)fb_size, conf->buff, conf->opts, fb);
     luat_lcd_clear(conf, LCD_WHITE);
     // printf("ARGB8888 0xFFFF %08X\n", luat_color_565to8888(0xFFFF));
     // printf("ARGB8888 0X001F %08X\n", luat_color_565to8888(0X001F));
@@ -94,14 +95,7 @@ int luat_lcd_flush(luat_lcd_conf_t* conf) {
     size_t height = y_max - y_min + 1;
 
     luat_color_t* src = conf->buff + y_min * conf->w;
-    uint32_t* tmp = fb;
-    for (size_t row = 0; row < height; row++) {
-        for (size_t col = 0; col < width; col++) {
-            tmp[row * width + col] = luat_color_565to8888(src[col]);
-        }
-        src += conf->w;
-    }
-    luat_sdl2_draw(0, y_min, conf->w - 1, y_max, fb);
+    luat_sdl2_draw(0, y_min, conf->w - 1, y_max, src);
     luat_sdl2_flush();
 
     conf->flush_y_max = 0;
@@ -155,18 +149,18 @@ int luat_lcd_draw(luat_lcd_conf_t* conf, int16_t x1, int16_t y1, int16_t x2, int
     size_t rw = x2 - x1 + 1;
     size_t rh = y2 - y1 + 1;
 
-    uint32_t *tmp = fb;
+    luat_color_t *tmp = fb;
     for (size_t i = 0; i < rh; i++)
     {
         for (size_t j = 0; j < rw; j++)
         {
-            // 输入为 RGB565,SDL 纹理为 ARGB8888,这里做明确转换
-            *tmp = luat_color_565to8888(*color_p);
+            // 直接拷贝 RGB565 数据
+            *tmp = *color_p;
             tmp ++;
             color_p ++;
         }
     }
-    
+
     luat_sdl2_draw(x1, y1, x2, y2, fb);
     return 0;
 }

+ 3 - 5
components/ui/sdl2/luat_sdl2.c

@@ -9,8 +9,6 @@
 static SDL_Window *window = NULL;
 static SDL_Renderer *renderer = NULL;
 static SDL_Texture *framebuffer = NULL;
-
-static uint32_t* fb;
 static luat_sdl2_conf_t sdl_conf;
 
 static void luat_sdl2_pump_events(void) {
@@ -41,11 +39,10 @@ int luat_sdl2_init(luat_sdl2_conf_t *conf) {
 
     renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
     framebuffer = SDL_CreateTexture(renderer,
-                                    SDL_PIXELFORMAT_ARGB8888,
+                                    SDL_PIXELFORMAT_RGB565,
                                     SDL_TEXTUREACCESS_STREAMING,
                                     conf->width,
                                     conf->height);
-    // fb = luat_heap_malloc(sizeof(uint32_t) * conf->width * conf->height);
     luat_sdl2_pump_events();
     return 0;
 }
@@ -70,7 +67,8 @@ void luat_sdl2_draw(int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint32_t* da
     r.w = x2 - x1 + 1;
     r.h = y2 - y1 + 1;
 
-    SDL_UpdateTexture(framebuffer, &r, data, r.w * 4);
+    // RGB565: 2 bytes per pixel
+    SDL_UpdateTexture(framebuffer, &r, data, r.w * 2);
 }
 
 void luat_sdl2_flush(void) {

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 511 - 491
luat/vfs/luat_inline_libs.c


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 516 - 496
luat/vfs/luat_inline_libs_64bit_size32.c


+ 0 - 230
module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirLCD_1000/AirLCD_1000.lua

@@ -1,230 +0,0 @@
---[[
-@module  AirLCD_1000
-@summary AirLCD_1000显示驱动模块
-@version 1.0
-@date    2025.09.4
-@author  江访
-@usage
-
-核心业务逻辑为:
-1、初始化AirLCD_1000显示屏
-2、管理屏幕背光亮度及开关状态
-3、管理屏幕休眠和唤醒状态
-4、提供屏幕状态管理功能
-
-本文件的对外接口有5个:
-1、AirLCD_1000.lcd_init()           --LCD初始化函数,
-2、AirLCD_1000.set_backlight(level) -- 设置背光亮度接口,level 亮度级别(0-100)
-3、AirLCD_1000.lcd_on()             -- 开启LCD背光
-4、AirLCD_1000.lcd_off()            -- 关闭LCD背光
-5、AirLCD_1000.set_sleep(sleep)     -- 设置休眠状态接口,sleep:true进入休眠, false唤醒
-]]
-
--- AirLCD_1000.lua - LCD显示驱动模块
--- 此文件负责初始化LCD显示屏,管理背光及休眠状态
-local AirLCD_1000 = {}
-
--- 屏幕状态管理表
-local screen_state = {
-    lcd_pin_pwr = 1,        -- 屏幕背光引脚GPIO号
-    lcd_pwm_id = 0,         -- 屏幕背光引脚PWM端口号
-    is_sleeping = false,    -- 是否休眠中标识
-    last_brightness = 50,   -- 默认亮度50%
-    backlight_on = true     -- 背光默认开启
-}
-
--- LCD初始化函数
--- @param LCD_MODEL   显示屏型号型号
--- @param lcd_vcc     屏幕供电引脚GPIO号
--- @param lcd_pin_rst 复位引脚GPIO号
--- @param lcd_pin_pwr 背光引脚GPIO号
--- @param lcd_pwm_id  背光引脚PWM端口号
-
-function AirLCD_1000.lcd_init(LCD_MODEL, lcd_vcc, lcd_pin_rst, lcd_pin_pwr,lcd_pwm_id)
-    -- 根据接线设置AirLCD_1000初始化参数
-    local LCD_MODEL, lcd_vcc, lcd_pin_rst, lcd_pin_pwr,lcd_pwm_id = "AirLCD_1000" or LCD_MODEL, lcd_vcc or 29, lcd_pin_rst or 36, lcd_pin_pwr or 1, lcd_pwm_id or 0
-
-    -- 设置屏幕尺寸
-    local width, height = 320, 480
-
-    -- LCD参数配置表
-    local lcd_param = {
-        port = lcd.HWID_0,       -- 使用SPI设备模式
-        pin_dc = 0xff,           -- DC引脚,无需设置
-        pin_rst = lcd_pin_rst,   -- 复位引脚,对应GPIO号
-        pin_pwr = lcd_pin_pwr,   -- 背光引脚(可选)
-        direction = 0,           -- 屏幕默认0°方向
-                                 -- 0:0°, 1:90°, 2:180°, 3:270°
-        w = width,               -- 屏幕宽度(像素)
-        h = height,              -- 屏幕高度(像素)
-        xoffset = 0,             -- X方向偏移量
-        yoffset = 0,             -- Y方向偏移量
-        sleepcmd = 0x10,         -- 睡眠命令
-        wakecmd = 0x11           -- 唤醒命令
-    }
-
-    -- 初始化SPI设备
-    spi.deviceSetup(
-        lcd.HWID_0,  -- LCD端口号
-        nil,         -- CS片选脚,可选
-        0,           -- CPHA=0
-        0,           -- CPOL=0
-        8,           -- 8位数据宽度
-        20000000,    -- 20MHz波特率
-        spi.MSB,     -- 高位先传
-        1,           -- 主机模式
-        1            -- 全双工模式
-    )
-
-    -- 设置VCC/RST/背光引脚为输出模式,并启用上拉电阻
-    gpio.setup(lcd_vcc, 1, gpio.PULLUP)
-    gpio.setup(lcd_pin_rst, 0, gpio.PULLUP)
-    gpio.setup(lcd_pin_pwr, 1, gpio.PULLUP)
-    
-    -- 开启屏幕VCC供电
-    gpio.set(lcd_vcc, 1)
-
-    -- 拉低屏幕复位引脚
-    gpio.set(lcd_pin_rst, 0)  -- 拉低复位引脚
-    sys.wait(20)                    -- 等待20ms(ST7796复位拉低大于10ms生效)
-    gpio.set(lcd_pin_rst, 1)  -- 拉高复位引脚
-    sys.wait(150)                   -- 等待150ms(ST7796复位拉高大于120ms重置)
-    gpio.set(lcd_pin_pwr, 1)
-
-    -- 初始化ST7796显示芯片
-    lcd.init("st7796", lcd_param)
-
-    -- 通用显示设置
-    lcd.setupBuff(nil,false)     -- 设置帧缓冲区
-    lcd.autoFlush(false)         -- 禁止自动刷新
-
-    screen_state.lcd_pin_pwr = lcd_pin_pwr
-    screen_state.lcd_pwm_id = lcd_pwm_id
-
-    log.info("AirLCD_1000", "LCD初始化成功,尺寸:", width, "x", height)
-    -- return true
-end
-
--- 设置背光亮度接口
--- 使用背光PWM模式控制亮度,休眠和关闭背光使用背光引脚GPIO模式
--- @param level 亮度级别(0-100)
--- @return 设置成功状态
-function AirLCD_1000.set_backlight(level)
-
-    -- 检查屏幕状态和PWM配置
-    if screen_state.is_sleeping then
-        log.warn("AirLCD_1000", "屏幕处于休眠状态,无法调节背光")
-        return false
-    end
-    if not screen_state.lcd_pin_pwr then
-        log.error("AirLCD_1000", "PWM配置不存在,无法调节背光")
-        return false
-    end
-
-    -- 确保GPIO已关闭 
-    gpio.close(screen_state.lcd_pin_pwr)
-
-    -- 设置并开启PWM
-    pwm.stop(screen_state.lcd_pwm_id)
-    pwm.close(screen_state.lcd_pwm_id)
-    pwm.setup(screen_state.lcd_pwm_id, 1000, 100)
-    pwm.open(screen_state.lcd_pwm_id, 1000, level)
-
-    -- 修改默认背光亮度为当前设置
-    screen_state.last_brightness = level
-    screen_state.backlight_on = (level > 0)
-    log.info("AirLCD_1000", "背光设置为", level, "%")
-    return true
-end
-
--- 开启LCD显示屏背光
--- @return 操作成功状态
-function AirLCD_1000.lcd_on()
-    if screen_state.is_sleeping then
-        log.warn("AirLCD_1000", "屏幕处于休眠状态,无法开启背光")
-        return false
-    end
-
-    pwm.stop(screen_state.lcd_pwm_id)
-    pwm.close(screen_state.lcd_pwm_id)
-
-    -- 设置GPIO控制电源(如果存在)
-    if screen_state.lcd_pin_pwr then
-        gpio.setup(screen_state.lcd_pin_pwr, 1, gpio.PULLUP)
-    end
-
-    lcd.on()
-    log.info("AirLCD_1000", "LCD背光已开启")
-end
-
--- 关闭LCD显示屏背光
--- @return 操作成功状态
-function AirLCD_1000.lcd_off()
-    if screen_state.is_sleeping then
-        log.warn("AirLCD_1000", "屏幕处于休眠状态,无法关闭背光")
-        return false
-    end
-    pwm.stop(screen_state.lcd_pwm_id)
-    pwm.close(screen_state.lcd_pwm_id)
-        -- 设置GPIO控制电源(如果存在)
-    if screen_state.lcd_pin_pwr then
-        gpio.setup(screen_state.lcd_pin_pwr, 1, gpio.PULLUP)
-    end
-    lcd.off()
-    log.info("AirLCD_1000", "LCD背光已关闭")
-end
-
--- 设置休眠状态接口
--- @param sleep true进入休眠, false唤醒
--- @return 操作成功状态
-function AirLCD_1000.set_sleep(sleep)
-    if sleep then
-        -- 进入休眠模式
-        if not screen_state.is_sleeping then
-            -- 关闭PWM(如果存在)
-            if screen_state.lcd_pwm_id then
-                pwm.stop(screen_state.lcd_pwm_id)
-                pwm.close(screen_state.lcd_pwm_id)
-            end
-
-            -- 设置GPIO控制电源(如果存在)
-            if screen_state.lcd_pin_pwr then
-                gpio.setup(screen_state.lcd_pin_pwr, 1, gpio.PULLUP)
-                gpio.set(screen_state.lcd_pin_pwr, 0)
-            end
-
-            -- 执行LCD睡眠
-            lcd.sleep()
-            screen_state.is_sleeping = true
-            log.info("AirLCD_1000", "LCD进入休眠状态")
-        end
-    else
-        -- 退出休眠模式
-        if screen_state.is_sleeping then
-            -- 唤醒LCD
-            lcd.wakeup()
-
-            -- 关闭GPIO控制(如果存在)
-            if screen_state.lcd_pin_pwr then
-                gpio.close(screen_state.lcd_pin_pwr)
-            end
-
-            -- 恢复之前的背光设置(如果PWM存在)
-            if screen_state.lcd_pin_pwr then
-                if screen_state.backlight_on then
-                    AirLCD_1000.set_backlight(screen_state.last_brightness)
-                else
-                    -- 如果背光原本是关闭状态,保持关闭
-                    pwm.stop(screen_state.lcd_pwm_id)
-                    pwm.close(screen_state.lcd_pwm_id)
-                end
-            end
-
-            screen_state.is_sleeping = false
-            log.info("AirLCD_1000", "LCD唤醒")
-        end
-    end
-    return true
-end
-
-return AirLCD_1000

+ 1 - 0
module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirLCD_1000/exeasyui/hw_drv/hw_customer_font_drv.lua

@@ -0,0 +1 @@
+-- hw_customer_font_drv是用户外部自定义外部点阵字体、lcd显示驱动配置和tp触摸驱动配置的驱动配置文件,目前exeasyui自定义字体功能正在开发中。

+ 62 - 0
module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirLCD_1000/exeasyui/hw_drv/hw_default_font_drv.lua

@@ -0,0 +1,62 @@
+--[[
+@module  hw_default_font_drv
+@summary 默认字体、lcd和tp驱动模块,使用内置12号点阵字体
+@version 1.0
+@date    2025.12.3
+@author  江访
+@usage
+本文件为默认字体、lcd和tp驱动模块,核心业务逻辑为:
+1、使用lcd内核固件中自带的12号中文字体;
+2、根据配置的字体、lcd和tp参数,初始化exEasyUI默认使用的字体、硬件显示和触摸;
+3、提供无需外部硬件的字体显示能力;
+
+本文件的对外接口有0个:
+1、require加载后自动执行初始化;
+
+@api ui.hw_init(config)
+@summary 初始化exEasyUI硬件系统
+@table config 硬件配置参数表,包含LCD配置和触摸配置
+@field lcd_config table LCD显示配置参数
+@field tp_config table 触摸屏配置参数
+@return nil
+
+@usage
+
+]]
+
+-- 使用默认12号中文字体初始化exEasyUI硬件
+ui.hw_init({
+    -- lcd_config参数填写可以参考合宙exlcd显示扩展库exlcd.init(param)接口说明:https://docs.openluat.com/osapi/ext/exlcd/#31-exlcdinitparam
+    lcd_config = {
+        lcd_model = "AirLCD_1000", -- LCD型号
+        -- pin_vcc = 24,           -- 供电引脚,使用GPIO控制屏幕供电可配置
+        pin_rst = 36,              -- 复位引脚
+        pin_pwr = 1,               -- 背光控制引脚GPIO ID号
+        pin_pwm = 0,               -- 背光控制引脚PWM D号
+        port = lcd.HWID_0,         -- 驱动端口
+        -- pin_dc = nil,          -- lcd数据/命令选择引脚GPIO ID号,使用lcd 专用 SPI 接口 lcd.HWID_0不需要填此参数,使用通用SPI接口需要赋值
+        direction = 0,             -- lcd屏幕方向 0:0° 1:90° 2:180° 3:270°,屏幕方向和分辨率保存一致
+        w = 320,                   -- lcd 水平分辨率
+        h = 480,                   -- lcd 竖直分辨率
+        xoffset = 0,               -- x偏移(不同屏幕ic 不同屏幕方向会有差异)
+        yoffset = 0,               -- y偏移(不同屏幕ic 不同屏幕方向会有差异)
+        sleepcmd = 0X10,           -- 睡眠命令,默认0X10
+        wakecmd = 0X11,            -- 唤醒命令,默认0X11
+        -- bus_speed = 50*1000*1000,                            -- SPI总线速度,不填默认50M,若速率要求更高需要进行设置
+        -- interface_mode = lcd.WIRE_4_BIT_8_INTERFACE_I,       -- lcd模式,默认lcd.WIRE_4_BIT_8_INTERFACE_I
+        -- direction0 = {0x36,0x00},                            -- 0°方向的命令,(不同屏幕ic会有差异)
+        -- direction90 = {0x36,0x60},                           -- 90°方向的命令,(不同屏幕ic会有差异)
+        -- direction180 ={0x36,0xc0} ,                          -- 180°方向的命令,(不同屏幕ic会有差异)
+        -- direction270 = {0x36,0xA0},                          -- 270°方向的命令,(不同屏幕ic会有差异)
+        -- hbp = nil,                                           -- 水平后廊
+        -- hspw = nil,                                          -- 水平同步脉冲宽度
+        -- hfp = 0,                                             -- 水平前廊
+        -- vbp = 0,                                             -- 垂直后廊
+        -- vspw = 0,                                            -- 垂直同步脉冲宽度
+        -- vfp = 0,                                             -- 垂直前廊
+        -- initcmd = nil,                                       -- 自定义屏幕初始化命令表
+        -- flush_rate = nil,                                    -- 刷新率
+        -- spi_dev = nil,                                       -- spi设备,当port = "device"时有效,当port ≠ "device"时可不填或者填nil
+        -- init_in_service = false,                             -- 允许初始化在lcd service里运行,在后台初始化LCD,默认是false,Air8000/G/W/T/A、Air780EHM/EGH/EHV 支持填true,可加快初始化速度,默认SPI总线速度80M
+    }
+})

+ 65 - 0
module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirLCD_1000/exeasyui/hw_drv/hw_gtfont_drv.lua

@@ -0,0 +1,65 @@
+--[[
+@module  hw_gtfont_drv
+@summary GTFont矢量字体驱动模块,使用AirFONTS_1000配件板
+@version 1.0
+@date    2025.12.3
+@author  江访
+@usage
+本文件为GTFont矢量字体、lcd和tp驱动模块,核心业务逻辑为:
+1、使用gtfont核心库驱动AirFONTS_1000矢量字库配件板;
+2、根据配置的字体、lcd和tp参数,初始化exEasyUI默认使用的字体、硬件显示和触摸;
+3、提供高质量矢量字体显示能力;
+
+本文件的对外接口有0个:
+1、require加载后自动执行初始化;
+
+@api ui.hw_init(config)
+@summary 初始化exEasyUI硬件系统
+@table config 硬件配置参数表,包含字体配置、LCD配置和触摸配置
+@field font_config table 字体配置参数
+@field lcd_config table LCD显示配置参数
+@field tp_config table 触摸屏配置参数
+@return nil
+
+@usage
+
+]]
+
+-- 使用gtfont矢量字库初始化exEasyUI硬件
+ui.hw_init({
+    font_config = { type = "gtfont", spi = { id = 0, cs = 8 }, size = 32 },
+
+    -- lcd_config参数填写可以参考合宙exlcd显示扩展库exlcd.init(param)接口说明:https://docs.openluat.com/osapi/ext/exlcd/#31-exlcdinitparam
+    lcd_config = {
+        lcd_model = "AirLCD_1000", -- LCD型号
+        -- pin_vcc = 24,                  -- 供电引脚,使用GPIO控制屏幕供电可配置
+        pin_rst = 36,              -- 复位引脚
+        pin_pwr = 1,               -- 背光控制引脚GPIO ID号
+        pin_pwm = 0,               -- 背光控制引脚PWM D号
+        port = lcd.HWID_0,         -- 驱动端口
+        -- pin_dc = 0xFF,          -- lcd数据/命令选择引脚GPIO ID号,使用lcd 专用 SPI 接口 lcd.HWID_0不需要填此参数,使用通用SPI接口需要赋值
+        direction = 0,             -- lcd屏幕方向 0:0° 1:90° 2:180° 3:270°,屏幕方向和分辨率保存一致
+        w = 320,                   -- lcd 水平分辨率
+        h = 480,                   -- lcd 竖直分辨率
+        xoffset = 0,               -- x偏移(不同屏幕ic 不同屏幕方向会有差异)
+        yoffset = 0,               -- y偏移(不同屏幕ic 不同屏幕方向会有差异)
+        sleepcmd = 0X10,           -- 睡眠命令,默认0X10
+        wakecmd = 0X11,            -- 唤醒命令,默认0X11
+        -- bus_speed = 50*1000*1000,                            -- SPI总线速度,不填默认50M,若速率要求更高需要进行设置
+        -- interface_mode = lcd.WIRE_4_BIT_8_INTERFACE_I,       -- lcd模式,默认lcd.WIRE_4_BIT_8_INTERFACE_I
+        -- direction0 = {0x36,0x00},                            -- 0°方向的命令,(不同屏幕ic会有差异)
+        -- direction90 = {0x36,0x60},                           -- 90°方向的命令,(不同屏幕ic会有差异)
+        -- direction180 ={0x36,0xc0} ,                          -- 180°方向的命令,(不同屏幕ic会有差异)
+        -- direction270 = {0x36,0xA0},                          -- 270°方向的命令,(不同屏幕ic会有差异)
+        -- hbp = nil,                                           -- 水平后廊
+        -- hspw = nil,                                          -- 水平同步脉冲宽度
+        -- hfp = 0,                                             -- 水平前廊
+        -- vbp = 0,                                             -- 垂直后廊
+        -- vspw = 0,                                            -- 垂直同步脉冲宽度
+        -- vfp = 0,                                             -- 垂直前廊
+        -- initcmd = nil,                                       -- 自定义屏幕初始化命令表
+        -- flush_rate = nil,                                    -- 刷新率
+        -- spi_dev = nil,                                       -- spi设备,当port = "device"时有效,当port ≠ "device"时可不填或者填nil
+        -- init_in_service = false,                             -- 允许初始化在lcd service里运行,在后台初始化LCD,默认是false,Air8000/G/W/T/A、Air780EHM/EGH/EHV 支持填true,可加快初始化速度,默认SPI总线速度80M
+    }
+})

+ 65 - 0
module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirLCD_1000/exeasyui/hw_drv/hw_hzfont_drv.lua

@@ -0,0 +1,65 @@
+--[[
+@module  hw_hzfont_drv
+@summary HZFont矢量字体驱动模块,使用合宙内置矢量字库
+@version 1.0
+@date    2025.12.3
+@author  江访
+@usage
+本文件为HZFont矢量字体、lcd和tp驱动模块,核心业务逻辑为:
+1、使用Air780EHM/EHV/EGH V2020版本以上14号固件或114号固件内置的hzfont合宙矢量字库;
+2、根据配置的字体、lcd和tp参数,初始化exEasyUI默认使用的字体、硬件显示和触摸;
+3、提供动态字体大小调整和高质量字体显示能力;
+
+本文件的对外接口有0个:
+1、require加载后自动执行初始化;
+
+@api ui.hw_init(config)
+@summary 初始化exEasyUI硬件系统
+@table config 硬件配置参数表,包含字体配置、LCD配置和触摸配置
+@field font_config table 字体配置参数
+@field lcd_config table LCD显示配置参数
+@field tp_config table 触摸屏配置参数
+@return nil
+
+@usage
+
+]]
+
+-- 使用Air780EHM/EHV/EGH V2020版本以上14号固件内置的hzfont合宙矢量字库初始化exEasyUI硬件
+ui.hw_init({
+    font_config = { type = "hzfont", size = 24, antialias = -1 }, -- 默认-1,表示自动抗锯齿
+
+    -- lcd_config参数填写可以参考合宙exlcd显示扩展库exlcd.init(param)接口说明:https://docs.openluat.com/osapi/ext/exlcd/#31-exlcdinitparam
+    lcd_config = {
+        lcd_model = "AirLCD_1000", -- LCD型号
+        -- pin_vcc = 24,           -- 供电引脚,使用GPIO控制屏幕供电可配置
+        pin_rst = 36,              -- 复位引脚
+        pin_pwr = 1,               -- 背光控制引脚GPIO ID号
+        pin_pwm = 0,               -- 背光控制引脚PWM D号
+        port = lcd.HWID_0,         -- 驱动端口
+        -- pin_dc = 0xFF,          -- lcd数据/命令选择引脚GPIO ID号,使用lcd 专用 SPI 接口 lcd.HWID_0不需要填此参数,使用通用SPI接口需要赋值
+        direction = 0,             -- lcd屏幕方向 0:0° 1:90° 2:180° 3:270°,屏幕方向和分辨率保存一致
+        w = 320,                   -- lcd 水平分辨率
+        h = 480,                   -- lcd 竖直分辨率
+        xoffset = 0,               -- x偏移(不同屏幕ic 不同屏幕方向会有差异)
+        yoffset = 0,               -- y偏移(不同屏幕ic 不同屏幕方向会有差异)
+        sleepcmd = 0X10,           -- 睡眠命令,默认0X10
+        wakecmd = 0X11,            -- 唤醒命令,默认0X11
+        -- bus_speed = 50*1000*1000,                            -- SPI总线速度,不填默认50M,若速率要求更高需要进行设置
+        -- interface_mode = lcd.WIRE_4_BIT_8_INTERFACE_I,       -- lcd模式,默认lcd.WIRE_4_BIT_8_INTERFACE_I
+        -- direction0 = {0x36,0x00},                            -- 0°方向的命令,(不同屏幕ic会有差异)
+        -- direction90 = {0x36,0x60},                           -- 90°方向的命令,(不同屏幕ic会有差异)
+        -- direction180 ={0x36,0xc0} ,                          -- 180°方向的命令,(不同屏幕ic会有差异)
+        -- direction270 = {0x36,0xA0},                          -- 270°方向的命令,(不同屏幕ic会有差异)
+        -- hbp = nil,                                           -- 水平后廊
+        -- hspw = nil,                                          -- 水平同步脉冲宽度
+        -- hfp = 0,                                             -- 水平前廊
+        -- vbp = 0,                                             -- 垂直后廊
+        -- vspw = 0,                                            -- 垂直同步脉冲宽度
+        -- vfp = 0,                                             -- 垂直前廊
+        -- initcmd = nil,                                       -- 自定义屏幕初始化命令表
+        -- flush_rate = nil,                                    -- 刷新率
+        -- spi_dev = nil,                                       -- spi设备,当port = "device"时有效,当port ≠ "device"时可不填或者填nil
+        -- init_in_service = false,                             -- 允许初始化在lcd service里运行,在后台初始化LCD,默认是false,Air8000/G/W/T/A、Air780EHM/EGH/EHV 支持填true,可加快初始化速度,默认SPI总线速度80M
+    }
+})

binární
module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirLCD_1000/exeasyui/images/1.jpg


binární
module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirLCD_1000/exeasyui/images/2.jpg


binární
module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirLCD_1000/exeasyui/images/3.jpg


binární
module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirLCD_1000/exeasyui/images/4.jpg


binární
module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirLCD_1000/exeasyui/images/5.jpg


binární
module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirLCD_1000/exeasyui/images/logo.jpg


+ 104 - 0
module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirLCD_1000/exeasyui/main.lua

@@ -0,0 +1,104 @@
+--[[
+@module  main
+@summary LuatOS用户应用脚本文件入口,总体调度应用逻辑
+@version 1.0
+@date    2025.12.3
+@author  江访
+@usage
+本demo演示的核心功能为:
+1、加载exeasyui扩展库,初始化用户界面系统;
+2、根据选择的字体类型驱动,进行显示硬件以及字体的初始化,
+   支持默认字体、外部矢量字库、内部软件矢量字库和外部自定义点阵字库四选一;
+3、初始化按键驱动系统,实现按键控制界面导航;
+4、启动用户界面主循环,实现多页面切换和按键事件处理;
+5、配置系统看门狗,确保系统稳定运行;
+
+更多说明参考本目录下的readme.md文件
+]]
+
+--[[
+必须定义PROJECT和VERSION变量,Luatools工具会用到这两个变量,远程升级功能也会用到这两个变量
+PROJECT:项目名,ascii string类型
+        可以随便定义,只要不使用,就行
+VERSION:项目版本号,ascii string类型
+        如果使用合宙iot.openluat.com进行远程升级,必须按照"XXX.YYY.ZZZ"三段格式定义:
+            X、Y、Z各表示1位数字,三个X表示的数字可以相同,也可以不同,同理三个Y和三个Z表示的数字也是可以相同,可以不同
+            因为历史原因,YYY这三位数字必须存在,但是没有任何用处,可以一直写为000
+        如果不使用合宙iot.openluat.com进行远程升级,根据自己项目的需求,自定义格式即可
+]]
+
+-- main.lua - 程序入口文件
+
+-- 定义项目名称和版本号
+PROJECT = "ui_demo" -- 项目名称
+VERSION = "001.000.000"   -- 版本号
+
+-- 在日志中打印项目名和项目版本号
+log.info("ui_demo", PROJECT, VERSION)
+
+-- 设置日志输出风格为样式2(建议调试时开启)
+-- log.style(2)
+
+-- 如果内核固件支持wdt看门狗功能,此处对看门狗进行初始化和定时喂狗处理
+-- 如果脚本程序死循环卡死,就会无法及时喂狗,最终会自动重启
+if wdt then
+    --配置喂狗超时时间为9秒钟
+    wdt.init(9000)
+    --启动一个循环定时器,每隔3秒钟喂一次狗
+    sys.timerLoopStart(wdt.feed, 3000)
+end
+
+
+-- 如果内核固件支持errDump功能,此处进行配置,【强烈建议打开此处的注释】
+-- 因为此功能模块可以记录并且上传脚本在运行过程中出现的语法错误或者其他自定义的错误信息,可以初步分析一些设备运行异常的问题
+-- 以下代码是最基本的用法,更复杂的用法可以详细阅读API说明文档
+-- 启动errDump日志存储并且上传功能,600秒上传一次
+-- if errDump then
+--     errDump.config(true, 600)
+-- end
+
+
+-- 使用LuatOS开发的任何一个项目,都强烈建议使用远程升级FOTA功能
+-- 可以使用合宙的iot.openluat.com平台进行远程升级
+-- 也可以使用客户自己搭建的平台进行远程升级
+-- 远程升级的详细用法,可以参考fota的demo进行使用
+
+
+-- 启动一个循环定时器
+-- 每隔3秒钟打印一次总内存,实时的已使用内存,历史最高的已使用内存情况
+-- 方便分析内存使用是否有异常
+-- sys.timerLoopStart(function()
+--     log.info("mem.lua", rtos.meminfo())
+--     log.info("mem.sys", rtos.meminfo("sys"))
+-- end, 3000)
+
+
+-- 必须加载才能启用exeasyui的功能
+ui = require("exeasyui")
+
+
+-- 加载lcd、tp和字库驱动管理功能模块,有以下四种:
+-- 1、使用lcd内核固件中自带的12号中文字体的hw_default_font_drv,并按lcd显示驱动配置进行初始化
+-- 2、使用hzfont核心库驱动内核固件中支持的软件矢量字库的hw_hzfont_drv.lua,并按lcd显示驱动配置初始化
+-- 3、使用gtfont核心库驱动AirFONTS_1000矢量字库配件板的hw_gtfont_drv.lua,并按lcd显示驱动配置初始化
+-- 4、使用自定义字体的hw_customer_font_drv(目前开发中)
+-- 最新情况可查看模组选型手册中对应型号的固件列表内,支持的核心库是否包含lcd、tp、12号中文、gtfont、hzfont,链接https://docs.openluat.com/air780epm/common/product/
+-- 目前exeasyui V1.7.0版本支持使用已经实现的四种功能中的一种进行初始化,同时支持多种字体初始化功能正在开发中
+require("hw_default_font_drv")
+-- require("hw_hzfont_drv")
+-- require("hw_gtfont_drv")
+-- require("hw_customer_font_drv")开发中
+
+-- 加载按键驱动模块
+require("key_drv")
+
+-- 加载exeassyui扩展库实现的用户界面功能模块
+-- 实现多页面切换、触摸事件分发和界面渲染功能
+-- 包含主页、组件演示页、默认字体演示页、HZfont演示页、GTFont演示页和自定义字体演示页
+require("ui_main")
+
+
+-- 用户代码已结束
+-- 结尾总是这一句
+sys.run()
+-- sys.run()之后不要加任何语句!!!!!因为添加的任何语句都不会被执行

+ 222 - 0
module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirLCD_1000/exeasyui/readme.md

@@ -0,0 +1,222 @@
+# exEasyUI 演示
+
+## 一、功能模块介绍
+
+### 1.1 核心主程序模块
+
+1. **main.lua** - 主程序入口,负责系统初始化和任务调度
+2. **ui_main.lua** - exeasyui 主程序,负责页面管理和按键事件分发和执行exeasyui的任务调度
+
+### 1.2 显示页面模块
+
+1. **home_page.lua** - 主页模块,提供应用入口和导航功能
+2. **component_page.lua** - UI 组件演示模块
+3. **default_font_page.lua** - 默认字体演示模块
+4. **gtfont_page.lua** - GTFont 矢量字体演示模块
+5. **hzfont_page.lua** - HZFont 矢量字体演示模块
+
+### 1.3 硬件驱动模块
+
+1. **hw_default_font_drv.lua** - LCD显示驱动配置和默认字体驱动模块,使用内置 12 号点阵字体
+2. **hw_gtfont_drv.lua** - LCD显示驱动配置和GTFont 矢量字库驱动模块
+3. **hw_hzfont_drv.lua** - LCD显示驱动配置和HZFont 矢量字体驱动模块
+4. **hw_customer_font_drv.lua** - LCD显示驱动配置和自定义外部字体驱动模块(开发中)
+5. **key_drv.lua** - 按键硬件驱动模块,负责GPIO初始化和按键事件发布
+
+当前演示的exeasyui V1.7.0版本还不支持同时启用多种字体,仅支持选择一种字体初始化,同时启用多种字体功能正在开发中
+
+使用 HZfont 需要使用 V2020 版本以上的 14 号或者114号固件,且 14 号或114号固件仅支持 HZfont,不支持内置12号中文字体和GTfont核心库
+
+### 1.4 按键处理模块
+
+1. **key_handler.lua** - 按键逻辑处理模块,负责光标管理和界面导航
+- BOOT键 在可点击区域间循环切换
+- 开机键 模拟点击当前中心区域
+
+## 二、演示效果
+
+<table>
+<tr>
+<td>主页<br/></td><td>组件演示页<br/></td><td>默认字体页<br/></td><td>HZFont页<br/></td><td>GTFont页<br/></td></tr>
+<tr>
+<td><img src="https://docs.openluat.com/cdn/image/exeasyui_home_gage.png" width="80" /><br/></td><td><img src="https://docs.openluat.com/cdn/image/exeasyui_AirLCD_1000组件页面.jpg" width="80" /><br/></td><td><img src="https://docs.openluat.com/cdn/image/exeasyui_default_font_page.png" width="80" /><br/></td><td><img src="https://docs.openluat.com/cdn/image/exeasyui_hzfont_page.png" width="80" /><br/></td><td><img src="https://docs.openluat.com/cdn/image/exeasyui_gtfont_page.png" width="80" /><br/></td></tr>
+</table>
+
+
+## 三、演示硬件环境
+
+### 3.1 硬件清单
+
+- Air780EHM/Air780EHV/Air780EGH 核心板 × 1
+- AirLCD_1000 配件板 × 1
+- GTFont 矢量字库,使用的是 AirFONTS_1000 配件板 × 1
+- 母对母杜邦线 × 14,杜邦线太长的话,会出现 spi 通信不稳定的现象;
+- TYPE-C 数据线 × 1
+- Air780EHM/Air780EHV/Air780EGH 核心板和 AirLCD_1000配件板以及AirFONTS_1000 配件板的硬件接线方式为
+
+  - Air780EHM/Air780EHV/Air780EGH 核心板通过 TYPE-C USB 口供电(核心板正面开关拨到 ON 一端),此种供电方式下,VDD_EXT 引脚为 3.3V,可以直接给 AirLCD_1000配件板和AirFONTS_1000 配件板供电;
+  - 为了演示方便,所以 Air780EHM/Air780EHV/Air780EGH 核心板上电后直接通过 VBAT 引脚给 AirLCD_1000配件板,VDD-EXT引脚给AirFONTS_1000 配件板供电;
+  - 客户在设计实际项目时,一般来说,需要通过一个 GPIO 来控制 LDO 给配件板供电,这样可以灵活地控制配件板的供电,可以使项目的整体功耗降到最低;
+
+### 3.2 接线配置
+
+#### 3.2.1 LCD 显示屏接线
+
+<table>
+<tr>
+<td>Air780EHM/Air780EHV/Air780EGH 核心板<br/></td><td>AirLCD_1000配件板<br/></td></tr>
+<tr>
+<td>53/LCD_CLK<br/></td><td>SCLK/CLK<br/></td></tr>
+<tr>
+<td>52/LCD_CS<br/></td><td>CS<br/></td></tr>
+<tr>
+<td>49/LCD_RST<br/></td><td>RES/RST<br/></td></tr>
+<tr>
+<td>50/LCD_SDA<br/></td><td>SDA/MOS<br/></td></tr>
+<tr>
+<td>51/LCD_RS<br/></td><td>DC/RS<br/></td></tr>
+<tr>
+<td>22/GPIO1<br/></td><td>BLK<br/></td></tr>
+<tr>
+<td>VBAT<br/></td><td>VCC<br/></td></tr>
+<tr>
+<td>GND<br/></td><td>GND<br/></td></tr>
+</table>
+
+#### 3.2.2 GTFont 字库接线
+
+<table>
+<tr>
+<td>Air780EHM/Air780EHV/Air780EGH 核心板<br/></td><td>AirFONTS_1000配件板<br/></td></tr>
+<tr>
+<td>83/SPI0_CS<br/></td><td>CS<br/></td></tr>
+<tr>
+<td>84/SPI0_MISO<br/></td><td>MISO<br/></td></tr>
+<tr>
+<td>85/SPI0_MOSI<br/></td><td>MOSI<br/></td></tr>
+<tr>
+<td>86/SPI0_CLK<br/></td><td>CLK<br/></td></tr>
+<tr>
+<td>24/VDD_EXT<br/></td><td>VCC<br/></td></tr>
+<tr>
+<td>GND<br/></td><td>GND<br/></td></tr>
+</table>
+
+#### 3.2.3 接线图
+![](https://docs.openLuat.com/cdn/image/Air780EHV核心板_AirLCD_1000_AirFONTS_1000接线图.jpg)
+
+## 四、演示软件环境
+
+### 4.1 开发工具
+
+- [Luatools下载调试工具](https://docs.openluat.com/air780egh/luatos/common/download/) - 固件烧录和代码调试
+
+### 4.2 内核固件
+
+- [点击下载Air780EHM系列最新版本内核固件](https://docs.openluat.com/air780epm/luatos/firmware/version/),demo所使用的是LuatOS-SoC_V2018_Air780EHM 1号固件
+  
+- [点击下载Air780EHV系列最新版本内核固件](https://docs.openluat.com/air780ehv/luatos/firmware/version/),demo所使用的是LuatOS-SoC_V2018_Air780EHV 1号固件
+  
+- [点击下载Air780EGH系列最新版本内核固件](https://docs.openluat.com/air780egh/luatos/firmware/version/),demo所使用的是LuatOS-SoC_V2018_Air780EGH 1号固件
+
+
+使用 HZfont 需要使用 V2020 版本以上的 14 号固件或114号固件,且 14 号固件或114号固件仅支持 HZfont
+使用其他字体,demo 所使用的是 LuatOS-SoC_V2018 1 号固件
+
+## 五、演示核心步骤
+
+### 5.1 硬件准备
+
+1. 按照硬件接线表连接所有设备
+2. 如使用 GTFont,需要连接 AirFONTS_1000 配件板
+3. 通过 TYPE-C USB 口供电
+4. 检查所有接线无误
+
+### 5.2 软件配置
+
+在 `main.lua` 中配置系统参数:
+
+```lua
+-- 必须加载才能启用exeasyui的功能
+ui = require("exeasyui")
+
+
+-- 加载lcd、tp和字库驱动管理功能模块,有以下四种:
+-- 1、使用lcd内核固件中自带的12号中文字体的hw_default_font_drv,并按lcd显示驱动配置初始化
+-- 2、使用hzfont核心库驱动内核固件中支持的软件矢量字库的hw_hzfont_drv.lua,并按lcd显示驱动配置初始化
+-- 3、使用gtfont核心库驱动AirFONTS_1000矢量字库配件板的hw_gtfont_drv.lua,并按lcd显示驱动配置初始化
+-- 4、使用自定义字体的hw_customer_font_drv(目前开发中)
+-- 最新情况可查看模组选型手册中对应型号的固件列表内,支持的核心库是否包含lcd、tp、12号中文、gtfont、hzfont,链接https://docs.openluat.com/air780epm/common/product/
+-- 目前exeasyui V1.7.0版本支持使用已经实现的四种功能中的一种进行初始化,同时支持多种字体初始化功能正在开发中
+require("hw_default_font_drv")
+-- require("hw_hzfont_drv")
+-- require("hw_gtfont_drv")
+-- require("hw_customer_font_drv")开发中
+
+-- 加载按键驱动模块
+require("key_drv")
+
+-- 加载exeassyui扩展库实现的用户界面功能模块
+-- 实现多页面切换、触摸事件分发和界面渲染功能
+-- 包含主页、组件演示页、默认字体演示页、HZfont演示页、GTFont演示页和自定义字体演示页
+require("ui_main")
+```
+
+### 5.3 驱动参数配置
+
+在对应的驱动文件中根据实际硬件调整硬件参数:
+
+1. **hw_default_font_drv.lua** - LCD显示驱动配置和默认字体驱动模块,使用内置 12 号点阵字体
+2. **hw_gtfont_drv.lua** - LCD显示驱动配置和GTFont 矢量字库驱动模块
+3. **hw_hzfont_drv.lua** - LCD显示驱动配置和HZFont 矢量字体驱动模块
+4. **hw_customer_font_drv.lua** - LCD显示驱动配置和自定义外部字体驱动模块(开发中)
+5. **key_drv.lua** - 按键硬件驱动模块,负责GPIO初始化和按键事件发布
+
+### 5.4 软件烧录
+
+1. 使用 Luatools 烧录对应型号的最新内核固件
+2. 下载并烧录本项目所有脚本文件
+3. 将图片文件随脚本文件一起烧录到脚本分区
+4. 设备自动重启后开始运行
+5. [点击查看Luatools 下载和详细使用](https://docs.openluat.com/air780epm/common/Luatools/)
+
+### 5.5 功能测试
+
+#### 5.5.1 主页面操作
+
+1. 设备启动后显示主页面,光标自动定位在第一个按钮
+2. 按BOOT键在不同按钮间切换焦点
+3. 按PWR键进入对应演示页面
+4. 使用返回按钮返回主页面
+
+#### 5.5.2 组件演示页面
+
+1. 测试进度条组件的动态更新
+2. 体验复选框的状态变化
+3. 查看图片轮播效果(如有图片文件)
+4. 使用按键在不同组件间切换焦点
+
+#### 5.5.3 字体演示页面
+
+1. **默认字体页**:查看固定 12 号字体的颜色和中英文显示
+2. **HZFont 页**:测试动态字体大小调整功能
+3. **GTFont 页**:体验多尺寸矢量字体显示效果
+4. 在各页面使用按键操作界面按钮
+
+### 5.6 预期效果
+
+- **系统启动**:正常初始化,显示主页面
+- **字体显示**:各字体页面正常显示,动态调整功能正常
+- **按键操作**:在各页面使用按键操作界面按钮
+
+### 5.7 故障排除
+
+1. **显示异常**:检查 LCD 接线,确认对应驱动文件中的硬件参数正确
+2. **触摸无响应**:检查 I2C 接线,确认触摸芯片型号配置正确
+3. **字体显示异常**:确认选择的字体驱动与硬件匹配
+4. **图片无法显示**:确认图片文件已正确烧录到指定路径
+5. **系统卡顿**:调整 `ui_main.lua` 中的刷新率参数
+
+## 六、扩展开发
+
+本演示 demo 所有接口都在 [exeasyUI UI 扩展库](https://docs.openluat.com/osapi/ext/exeasyui/)内有详细说明,如需实现更丰富的自定义功能可按接口说明实现。

+ 70 - 0
module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirLCD_1000/exeasyui/tp_key_drv/key_drv.lua

@@ -0,0 +1,70 @@
+--[[
+@module  key_drv
+@summary 按键驱动模块
+@version 1.0
+@date    2025.12.3
+@author  江访
+@usage
+本文件为按键驱动功能模块,核心业务逻辑为:
+1、初始化BOOT键和PWR键的GPIO;
+2、配置按键事件的中断处理函数;
+3、实现按键防抖功能,防止误触发;
+4、对外发布按键消息;
+
+本文件的对外接口有1个:
+1、key_drv.init():初始化按键驱动;
+]]
+
+local key_drv = {}
+
+-- 按键定义
+local key_boot = 0           -- GPIO0按键(BOOT键)
+local key_pwr = gpio.PWR_KEY -- 电源按键
+
+
+-- 按键事件处理函数
+local function handle_boot_key(val)
+
+    if val == 1 then
+        sys.publish("KEY_EVENT", "boot_down")
+    else
+        sys.publish("KEY_EVENT", "boot_up")
+    end
+end
+
+local function handle_pwr_key(val)
+
+    if val == 1 then
+        sys.publish("KEY_EVENT", "pwr_up")
+    else
+        sys.publish("KEY_EVENT", "pwr_down")
+    end
+end
+
+--[[
+初始化按键GPIO;
+配置BOOT键和PWR键的GPIO中断;
+
+@api key_drv.init()
+@summary 配置BOOT键和PWR键的GPIO中断
+@return bool 初始化只会返回true
+
+@usage
+local result = key_drv.init()
+if result then
+    log.info("按键驱动初始化成功")
+end
+]]
+function key_drv.init()
+    gpio.setup(key_boot, handle_boot_key, gpio.PULLDOWN, gpio.BOTH)
+    gpio.debounce(key_boot, 50, 0)     -- 防抖,防止频繁触发
+
+    gpio.setup(key_pwr, handle_pwr_key, gpio.PULLUP, gpio.BOTH)
+    gpio.debounce(key_pwr, 50, 0)     -- 防抖,防止频繁触发
+
+    log.info("key_drv", "按键初始化完成")
+    return true
+end
+
+key_drv.init()
+return key_drv

+ 214 - 0
module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirLCD_1000/exeasyui/ui/component_page.lua

@@ -0,0 +1,214 @@
+--[[
+@module  component_page
+@summary exEasyUI组件演示页面模块
+@version 1.0
+@date    2025.12.3
+@author  江访
+@usage
+本文件为组件演示页面功能模块,核心业务逻辑为:
+1、创建带上下滚动功能的演示窗口;
+2、展示进度条、按钮、复选框、图片轮播等UI组件;
+3、演示组件的交互功能和事件处理;
+4、提供返回主页的导航功能;
+
+本文件的对外接口有1个:
+1、component_page.create(ui):创建组件演示页面;
+]]
+
+local component_page = {}
+
+--[[
+创建组件演示页面;
+
+@api component_page.create(ui)
+@summary 创建组件演示页面界面
+@table ui UI库对象
+@return table 组件演示窗口对象
+
+@usage
+-- 在子页面工厂中调用创建组件演示页面
+local component_page = require("component_page").create(ui)
+]]
+
+function component_page.create(ui)
+    local win = ui.window({
+        background_color = ui.COLOR_WHITE,
+        x = 0,
+        y = 0,
+        w = 320,
+        h = 480
+    })
+
+
+    -- 标题
+    local title = ui.label({
+        x = 120,
+        y = 25,
+        text = "组件演示",
+        color = ui.COLOR_BLACK,
+        size = 16
+    })
+
+    -- 返回按钮
+    local btn_back = ui.button({
+        x = 20,
+        y = 20,
+        w = 60,
+        h = 30,
+        text = "返回",
+        on_click = function()
+            win:back()
+        end
+    })
+
+    -- ==================== 1. 进度条组件演示 ====================
+    local progress_label = ui.label({
+        x = 20,
+        y = 70,
+        text = "1. 进度条组件:",
+        color = ui.COLOR_BLACK,
+        size = 14
+    })
+
+    local progress_value = 0
+    local progress_bar = ui.progress_bar({
+        x = 20,
+        y = 100,
+        w = 180,
+        h = 26,
+        progress = progress_value
+    })
+
+    local btn_progress = ui.button({
+        x = 210,
+        y = 100,
+        w = 70,
+        h = 26,
+        text = "+10%",
+        on_click = function()
+            progress_value = progress_value + 10
+            if progress_value > 100 then
+                progress_value = 0
+            end
+            progress_bar:set_progress(progress_value)
+            progress_bar:set_text("进度: " .. progress_value .. "%")
+        end
+    })
+
+    -- ==================== 2. 复选框组件演示 ====================
+    local checkbox_label = ui.label({
+        x = 20,
+        y = 140,
+        text = "2. 复选框组件:",
+        color = ui.COLOR_BLACK,
+        size = 14
+    })
+
+    local checkbox1 = ui.check_box({
+        x = 20,
+        y = 170,
+        text = "选项A",
+        checked = false,
+        on_change = function(checked)
+            log.info("component_page", "选项A:", checked)
+        end
+    })
+
+    local checkbox2 = ui.check_box({
+        x = 120,
+        y = 170,
+        text = "选项B",
+        checked = true,
+        on_change = function(checked)
+            log.info("component_page", "选项B:", checked)
+        end
+    })
+
+    -- -- ==================== 3. 图片轮播组件演示 ====================
+    local picture_label = ui.label({
+        x = 20,
+        y = 220,
+        text = "3. 图片轮播组件:",
+        color = ui.COLOR_BLACK,
+        size = 14
+    })
+
+    local picture = ui.picture({
+        x = 20,
+        y = 250,
+        w = 128,
+        h = 128,
+        sources = { "/luadb/1.jpg", "/luadb/2.jpg", "/luadb/3.jpg" },
+        autoplay = true,
+        interval = 2000
+    })
+
+    -- ==================== 4. 按钮组件演示 ====================
+    local button_label = ui.label({
+        x = 20,
+        y = 400,
+        text = "4. 按钮组件:",
+        color = ui.COLOR_BLACK,
+        size = 14
+    })
+
+    -- 普通按钮
+    local normal_btn = ui.button({
+        x = 20,
+        y = 430,
+        w = 80,
+        h = 30,
+        text = "普通按钮",
+        on_click = function()
+            log.info("component_page", "普通按钮被点击")
+        end
+    })
+
+    -- 带颜色的按钮
+    local colored_btn = ui.button({
+        x = 110,
+        y = 430,
+        w = 80,
+        h = 30,
+        text = "蓝色按钮",
+        bg_color = ui.COLOR_BLUE,
+        text_color = ui.COLOR_WHITE,
+        on_click = function()
+            log.info("component_page", "蓝色按钮被点击")
+        end
+    })
+
+    -- 图片按钮
+    local image_btn = ui.button({
+        x = 200,
+        y = 413,
+        w = 64,
+        h = 64,
+        src = "/luadb/4.jpg",
+        src_toggled = "/luadb/5.jpg",
+        toggle = true,
+        on_click = function()
+            log.info("component_page", "图片按钮被点击")
+        end
+    })
+
+    -- 添加所有组件到窗口
+    win:add(title)
+    win:add(btn_back)
+    win:add(progress_label)
+    win:add(progress_bar)
+    win:add(btn_progress)
+    win:add(checkbox_label)
+    win:add(checkbox1)
+    win:add(checkbox2)
+    win:add(picture_label)
+    win:add(picture)
+    win:add(button_label)
+    win:add(normal_btn)
+    win:add(colored_btn)
+    win:add(image_btn)
+
+    return win
+end
+
+return component_page

+ 156 - 0
module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirLCD_1000/exeasyui/ui/default_font_page.lua

@@ -0,0 +1,156 @@
+--[[
+@module  default_font_page
+@summary 默认字体演示页面模块
+@version 1.0
+@date    2025.12.3
+@author  江访
+@usage
+本文件为默认字体演示页面功能模块,核心业务逻辑为:
+1、创建演示窗口,展示内置12号点阵字体的固定大小特性;
+2、演示数字、符号、中英文混排的显示效果;
+3、展示默认字体的特性和限制说明;
+4、提供返回主页的导航功能;
+
+本文件的对外接口有1个:
+1、default_font_page.create(ui):创建默认字体演示页面;
+]]
+
+local default_font_page = {}
+
+--[[
+创建默认字体演示页面;
+
+@api default_font_page.create(ui)
+@summary 创建默认字体演示页面界面
+@table ui UI库对象
+@return table 默认字体演示窗口对象
+
+@usage
+-- 在子页面工厂中调用创建默认字体演示页面
+local default_font_page = require("default_font_page").create(ui)
+]]
+
+function default_font_page.create(ui)
+    local win = ui.window({
+        background_color = ui.COLOR_WHITE,
+        x = 0,
+        y = 0,
+        w = 320,
+        h = 480
+    })
+
+    -- 标题
+    local title = ui.label({
+        x = 120,
+        y = 25,
+        text = "默认字体演示",
+        color = ui.COLOR_BLACK,
+        size = 12
+    })
+
+    -- 返回按钮
+    local btn_back = ui.button({
+        x = 20,
+        y = 20,
+        w = 60,
+        h = 30,
+        text = "返回",
+        on_click = function()
+            win:back()
+        end
+    })
+
+    -- 字体演示标题
+    local demo_title = ui.label({
+        x = 20,
+        y = 70,
+        text = "字体演示 (固定12号):",
+        color = ui.COLOR_BLACK,
+        size = 12
+    })
+
+    -- 数字演示
+    local number_demo = ui.label({
+        x = 20,
+        y = 100,
+        text = "1、数字: 0123456789",
+        color = ui.COLOR_BLUE,
+        size = 12
+    })
+
+    -- 符号演示
+    local symbol_demo = ui.label({
+        x = 20,
+        y = 130,
+        text = "2、符号: !@#$%^&*()_+-=[]",
+        color = ui.COLOR_ORANGE,
+        size = 12
+    })
+
+    -- 中英文演示
+    local text_demo = ui.label({
+        x = 20,
+        y = 160,
+        text = "3、中英文: Hello 世界 ABC",
+        color = ui.COLOR_RED,
+        size = 12
+    })
+
+    -- 默认字体特性说明
+    local feature_title = ui.label({
+        x = 20,
+        y = 200,
+        text = "默认字体特性:",
+        color = ui.COLOR_BLACK,
+        size = 12
+    })
+
+    local feature1 = ui.label({
+        x = 20,
+        y = 230,
+        text = "- 内置12号点阵字体",
+        color = ui.COLOR_GRAY,
+        size = 12
+    })
+
+    local feature2 = ui.label({
+        x = 20,
+        y = 260,
+        text = "- 无需外部硬件支持",
+        color = ui.COLOR_GRAY,
+        size = 12
+    })
+
+    local feature3 = ui.label({
+        x = 20,
+        y = 290,
+        text = "- 启动快速,资源占用小",
+        color = ui.COLOR_GRAY,
+        size = 12
+    })
+
+    local feature4 = ui.label({
+        x = 20,
+        y = 320,
+        text = "- 字体大小固定为12号",
+        color = ui.COLOR_GRAY,
+        size = 12
+    })
+
+    -- 添加所有组件到窗口
+    win:add(title)
+    win:add(btn_back)
+    win:add(demo_title)
+    win:add(number_demo)
+    win:add(symbol_demo)
+    win:add(text_demo)
+    win:add(feature_title)
+    win:add(feature1)
+    win:add(feature2)
+    win:add(feature3)
+    win:add(feature4)
+
+    return win
+end
+
+return default_font_page

+ 164 - 0
module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirLCD_1000/exeasyui/ui/gtfont_page.lua

@@ -0,0 +1,164 @@
+--[[
+@module  gtfont_page
+@summary GTFont矢量字体演示页面模块
+@version 1.0
+@date    2025.12.3
+@author  江访
+@usage
+本文件为GTFont矢量字体演示页面功能模块,核心业务逻辑为:
+1、创建带上下滚动功能的演示窗口,展示GTFont矢量字体特性;
+2、演示动态字体大小调整功能,展示的是12-32号字体切换,
+   GTFont支持10-192号字体,demo以展示GTFont特性为主;
+3、展示不同大小和颜色的数字、符号、中英文显示效果;
+4、提供GTFont特性说明和返回主页的导航功能;
+
+本文件的对外接口有1个:
+1、gtfont_page.create(ui):创建GTFont演示页面;
+]]
+local gtfont_page = {}
+local demo_state = {
+    current_size = 16
+}
+
+function gtfont_page.create(ui)
+    local win = ui.window({
+        background_color = ui.COLOR_WHITE,
+        x = 0,
+        y = 0,
+        w = 320,
+        h = 480
+    })
+
+    -- 标题
+    local title = ui.label({
+        x = 85,
+        y = 25,
+        text = "GTFont矢量字体演示",
+        color = ui.COLOR_BLACK,
+        size = 16
+    })
+
+    -- 返回按钮
+    local btn_back = ui.button({
+        x = 20,
+        y = 20,
+        w = 60,
+        h = 30,
+        text = "返回",
+        on_click = function()
+            win:back()
+        end
+    })
+
+    -- 动态字体大小演示区域
+    local dynamic_title = ui.label({
+        x = 20,
+        y = 70,
+        text = "动态字体大小调整:",
+        color = ui.COLOR_BLACK,
+        size = 16
+    })
+
+    local size_display = ui.label({
+        x = 20,
+        y = 100,
+        text = "当前字体: 16号 蓝色",
+        color = ui.COLOR_BLUE,
+        size = 16
+    })
+
+    local btn_change = ui.button({
+        x = 20,
+        y = 140,
+        w = 120,
+        h = 30,
+        text = "切换字体大小",
+        on_click = function()
+            demo_state.current_size = demo_state.current_size + 4
+            if demo_state.current_size > 32 then
+                demo_state.current_size = 12
+            end
+            size_display:set_text("当前字体: " .. demo_state.current_size .. "号 蓝色")
+            size_display:set_size(demo_state.current_size)
+        end
+    })
+
+    -- 字体演示标题
+    local demo_title = ui.label({
+        x = 20,
+        y = 180,
+        text = "字体演示:",
+        color = ui.COLOR_BLACK,
+        size = 16
+    })
+
+    -- 数字演示
+    local number_demo = ui.label({
+        x = 20,
+        y = 210,
+        text = "数字: 0123456789",
+        color = ui.COLOR_BLUE,
+        size = 14
+    })
+
+    -- 符号演示
+    local symbol_demo = ui.label({
+        x = 20,
+        y = 250,
+        text = "符号: !@#$%^&*()_+-=[]",
+        color = ui.COLOR_ORANGE,
+        size = 20
+    })
+
+    -- 中英文演示
+    local text_demo = ui.label({
+        x = 20,
+        y = 300,
+        text = "中英文: LuatOS",
+        color = ui.COLOR_RED,
+        size = 28
+    })
+
+    -- 特性说明
+    local feature_title = ui.label({
+        x = 20,
+        y = 350,
+        text = "GTFont特性:",
+        color = ui.COLOR_BLACK,
+        size = 14
+    })
+
+    local feature1 = ui.label({
+        x = 20,
+        y = 380,
+        text = "- 使用AirFONTS_1000配件板",
+        color = ui.COLOR_GRAY,
+        size = 12
+    })
+
+    local feature2 = ui.label({
+        x = 20,
+        y = 410,
+        text = "- 支持10到192号黑体字体",
+        color = ui.COLOR_GRAY,
+        size = 12
+    })
+
+    -- 添加所有组件到窗口
+    win:add(title)
+    win:add(btn_back)
+    win:add(dynamic_title)
+    win:add(size_display)
+    win:add(btn_change)
+    win:add(demo_title)
+    win:add(number_demo)
+    win:add(symbol_demo)
+    win:add(text_demo)
+    win:add(feature_title)
+    win:add(feature1)
+    win:add(feature2)
+
+    return win
+end
+
+return gtfont_page

+ 129 - 0
module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirLCD_1000/exeasyui/ui/home_page.lua

@@ -0,0 +1,129 @@
+--[[
+@module  home_page
+@summary 主页模块,提供应用主界面和页面导航功能
+@version 1.0
+@date    2025.12.3
+@author  江访
+@usage
+本文件为主页功能模块,核心业务逻辑为:
+1、创建应用主窗口并配置背景颜色;
+2、配置子页面工厂函数,管理各演示页面的创建;
+3、创建标题和功能按钮,提供页面导航功能;
+4、处理按钮点击事件,实现页面切换;
+
+本文件的对外接口有1个:
+1、home_page.create():创建主页界面;
+]]
+
+local home_page = {}
+
+--[[
+创建主页界面;
+
+@api home_page.create()
+@summary 创建并配置应用主页面
+@return nil
+
+@usage
+-- 在UI主程序中调用创建主页
+home_page.create()
+]]
+
+function home_page.create()
+    -- 创建主页
+    local home = ui.window({ background_color = ui.COLOR_WHITE })
+    home.visible = true
+
+    -- 配置子页面工厂
+    home:configure_subpages({
+        component = function() return require("component_page").create(ui) end,
+        default_font = function() return require("default_font_page").create(ui) end,
+        gtfont = function() return require("gtfont_page").create(ui) end,
+        hzfont = function() return require("hzfont_page").create(ui) end
+    })
+
+    -- 标题
+    local title = ui.label({
+        x = 80,
+        y = 30,
+        text = "exEasyUI v1.7.0演示系统",
+        color = ui.COLOR_BLACK,
+        size = 16
+    })
+
+    local subtitle = ui.label({
+        x = 80,
+        y = 60,
+        text = "boot键:选择 pwr键:确认",
+        color = ui.COLOR_GRAY,
+        size = 12
+    })
+
+    -- 组件演示按钮
+    local btn_component = ui.button({
+        x = 20,
+        y = 100,
+        w = 280,
+        h = 50,
+        text = "组件演示",
+        bg_color = ui.COLOR_BLUE,
+        text_color = ui.COLOR_WHITE,
+        on_click = function()
+            home:show_subpage("component")
+        end
+    })
+
+    -- 默认字体演示按钮
+    local btn_default_font = ui.button({
+        x = 20,
+        y = 170,
+        w = 280,
+        h = 50,
+        text = "默认字体演示",
+        bg_color = ui.COLOR_RED,
+        text_color = ui.COLOR_WHITE,
+        on_click = function()
+            home:show_subpage("default_font")
+        end
+    })
+
+    -- GTFont演示按钮
+    local btn_gtfont = ui.button({
+        x = 20,
+        y = 240,
+        w = 280,
+        h = 50,
+        text = "GTFont演示",
+        bg_color = ui.COLOR_GREEN,
+        text_color = ui.COLOR_WHITE,
+        on_click = function()
+            home:show_subpage("gtfont")
+        end
+    })
+
+    -- HZFont演示按钮
+    local btn_hzfont = ui.button({
+        x = 20,
+        y = 310,
+        w = 280,
+        h = 50,
+        text = "HZFont演示",
+        bg_color = ui.COLOR_ORANGE,
+        text_color = ui.COLOR_WHITE,
+        on_click = function()
+            home:show_subpage("hzfont")
+        end
+    })
+
+    -- 添加所有组件到窗口
+    home:add(title)
+    home:add(subtitle)
+    home:add(btn_component)
+    home:add(btn_default_font)
+    home:add(btn_gtfont)
+    home:add(btn_hzfont)
+
+    ui.add(home)
+end
+
+return home_page

+ 293 - 0
module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirLCD_1000/exeasyui/ui/hzfont_page.lua

@@ -0,0 +1,293 @@
+--[[
+@module  hzfont_page
+@summary HZFont矢量字体演示页面模块
+@version 1.0
+@date    2025.12.3
+@author  江访
+@usage
+本文件为HZFont矢量字体演示页面模块,核心业务逻辑为:
+1、创建HZFont矢量字体演示页面,展示矢量字体的动态调整能力;
+2、演示多种字体大小和颜色的文本显示效果;
+3、提供字体大小动态切换功能,展示矢量字体的缩放优势;
+4、展示HZFont矢量字体的特性和应用场景;
+
+本文件的对外接口有1个:
+1、hzfont_page.create(ui):创建并返回HZFont演示窗口;
+]]
+
+local hzfont_page = {}
+
+--[[
+页面演示状态记录表
+
+@table demo_state
+@field current_size number 当前演示字体大小,初始值为16号
+]]
+local demo_state = {
+    current_size = 16  -- 当前字体大小,从16号开始
+}
+
+--[[
+创建HZFont矢量字体演示页面窗口;
+
+@api hzfont_page.create(ui)
+
+@summary 创建HZFont矢量字体演示页面
+
+@param ui table exEasyUI库对象,用于创建UI组件
+
+@return table 返回创建的窗口对象
+
+@usage
+-- 在页面切换逻辑中调用
+local hzfont_win = hzfont_page.create(ui)
+return hzfont_win
+]]
+function hzfont_page.create(ui)
+    --[[
+    创建主窗口
+    @param background_color number 窗口背景颜色,白色
+    @param x number 窗口左上角X坐标,0
+    @param y number 窗口左上角Y坐标,0
+    @param w number 窗口宽度,320像素
+    @param h number 窗口高度,480像素
+    ]]
+    local win = ui.window({
+        background_color = ui.COLOR_WHITE,
+        x = 0,
+        y = 0,
+        w = 320,
+        h = 480
+    })
+
+    --[[
+    页面标题标签
+    @param x number 标签左上角X坐标,85像素
+    @param y number 标签左上角Y坐标,25像素
+    @param text string 标签文本内容
+    @param color number 文本颜色,黑色
+    @param size number 字体大小,16号
+    ]]
+    local title = ui.label({
+        x = 85,
+        y = 25,
+        text = "HZFont矢量字体演示",
+        color = ui.COLOR_BLACK,
+        size = 16
+    })
+
+    --[[
+    返回按钮
+    @param x number 按钮左上角X坐标,20像素
+    @param y number 按钮左上角Y坐标,20像素
+    @param w number 按钮宽度,60像素
+    @param h number 按钮高度,30像素
+    @param text string 按钮文本
+    @param on_click function 按钮点击回调函数,调用窗口的back方法返回上一页
+    ]]
+    local btn_back = ui.button({
+        x = 20,
+        y = 20,
+        w = 60,
+        h = 30,
+        text = "返回",
+        on_click = function()
+            win:back()
+        end
+    })
+
+    --[[
+    动态字体大小调整标题
+    @param x number 标签左上角X坐标,20像素
+    @param y number 标签左上角Y坐标,70像素
+    @param text string 标签文本内容
+    @param color number 文本颜色,黑色
+    @param size number 字体大小,16号
+    ]]
+    local dynamic_title = ui.label({
+        x = 20,
+        y = 70,
+        text = "动态字体大小调整:",
+        color = ui.COLOR_BLACK,
+        size = 16
+    })
+
+    --[[
+    字体大小信息显示标签
+    @param x number 标签左上角X坐标,20像素
+    @param y number 标签左上角Y坐标,100像素
+    @param text string 标签文本内容,显示当前字体大小
+    @param color number 文本颜色,蓝色
+    @param size number 字体大小,16号
+    ]]
+    local size_info = ui.label({
+        x = 20,
+        y = 100,
+        text = "当前大小: 16号 (可调整)",
+        color = ui.COLOR_BLUE,
+        size = 16
+    })
+
+    --[[
+    字体大小切换按钮
+    @param x number 按钮左上角X坐标,20像素
+    @param y number 按钮左上角Y坐标,140像素
+    @param w number 按钮宽度,120像素
+    @param h number 按钮高度,30像素
+    @param text string 按钮文本
+    @param on_click function 按钮点击回调函数,切换字体大小
+    ]]
+    local btn_change_size = ui.button({
+        x = 20,
+        y = 140,
+        w = 120,
+        h = 30,
+        text = "切换字体大小",
+        on_click = function()
+            -- 每次点击增加4号字体大小
+            demo_state.current_size = demo_state.current_size + 4
+            
+            -- 当字体大小超过32号时,重置为12号
+            if demo_state.current_size > 32 then
+                demo_state.current_size = 12
+            end
+            
+            -- 更新标签文本和字体大小
+            size_info:set_text("当前字体大小: " .. demo_state.current_size .. "号")
+            size_info:set_size(demo_state.current_size)
+        end
+    })
+
+    --[[
+    字体演示区域标题
+    @param x number 标签左上角X坐标,20像素
+    @param y number 标签左上角Y坐标,180像素
+    @param text string 标签文本内容
+    @param color number 文本颜色,黑色
+    @param size number 字体大小,16号
+    ]]
+    local demo_title = ui.label({
+        x = 20,
+        y = 180,
+        text = "字体演示:",
+        color = ui.COLOR_BLACK,
+        size = 16
+    })
+
+    --[[
+    数字演示标签
+    @param x number 标签左上角X坐标,20像素
+    @param y number 标签左上角Y坐标,210像素
+    @param text string 标签文本内容,显示数字
+    @param color number 文本颜色,蓝色
+    @param size number 字体大小,14号
+    ]]
+    local number_demo = ui.label({
+        x = 20,
+        y = 210,
+        text = "数字: 0123456789",
+        color = ui.COLOR_BLUE,
+        size = 14
+    })
+
+    --[[
+    符号演示标签
+    @param x number 标签左上角X坐标,20像素
+    @param y number 标签左上角Y坐标,250像素
+    @param text string 标签文本内容,显示特殊符号
+    @param color number 文本颜色,橙色
+    @param size number 字体大小,20号
+    ]]
+    local symbol_demo = ui.label({
+        x = 20,
+        y = 250,
+        text = "符号: !@#$%^&*()_+-=[]",
+        color = ui.COLOR_ORANGE,
+        size = 20
+    })
+
+    --[[
+    中英文演示标签
+    @param x number 标签左上角X坐标,20像素
+    @param y number 标签左上角Y坐标,300像素
+    @param text string 标签文本内容,显示中英文混合文本
+    @param color number 文本颜色,红色
+    @param size number 字体大小,28号
+    ]]
+    local text_demo = ui.label({
+        x = 20,
+        y = 300,
+        text = "中英文: LuatOS",
+        color = ui.COLOR_RED,
+        size = 28
+    })
+
+    --[[
+    HZFont特性说明标题
+    @param x number 标签左上角X坐标,20像素
+    @param y number 标签左上角Y坐标,350像素
+    @param text string 标签文本内容
+    @param color number 文本颜色,黑色
+    @param size number 字体大小,14号
+    ]]
+    local feature_title = ui.label({
+        x = 20,
+        y = 350,
+        text = "HZFont特性:",
+        color = ui.COLOR_BLACK,
+        size = 14
+    })
+
+    --[[
+    HZFont特性说明第一条
+    @param x number 标签左上角X坐标,20像素
+    @param y number 标签左上角Y坐标,380像素
+    @param text string 标签文本内容,说明HZFont的内置特性
+    @param color number 文本颜色,灰色
+    @param size number 字体大小,12号
+    ]]
+    local feature1 = ui.label({
+        x = 20,
+        y = 380,
+        text = "- 内置矢量字体,无需外部硬件",
+        color = ui.COLOR_GRAY,
+        size = 12
+    })
+
+    --[[
+    HZFont特性说明第二条
+    @param x number 标签左上角X坐标,20像素
+    @param y number 标签左上角Y坐标,410像素
+    @param text string 标签文本内容,说明HZFont的动态调整能力
+    @param color number 文本颜色,灰色
+    @param size number 字体大小,12号
+    ]]
+    local feature2 = ui.label({
+        x = 20,
+        y = 410,
+        text = "- 支持10-100号字体动态调整",
+        color = ui.COLOR_GRAY,
+        size = 12
+    })
+
+    --[[
+    将创建的UI组件添加到窗口
+    按照从背景到前景的顺序添加,确保显示层次正确
+    ]]
+    win:add(title)
+    win:add(btn_back)
+    win:add(dynamic_title)
+    win:add(size_info)
+    win:add(btn_change_size)
+    win:add(demo_title)
+    win:add(number_demo)
+    win:add(symbol_demo)
+    win:add(text_demo)
+    win:add(feature_title)
+    win:add(feature1)
+    win:add(feature2)
+
+    return win
+end
+
+return hzfont_page

+ 333 - 0
module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirLCD_1000/exeasyui/ui/key_handler.lua

@@ -0,0 +1,333 @@
+--[[
+@module  key_handler
+@summary 按键处理模块
+@version 1.0
+@date    2025.12.3
+@author  江访
+@usage
+本文件为按键处理功能模块,核心业务逻辑为:
+1、管理页面可点击区域列表,支持动态区域注册;
+2、处理按键事件,实现光标在可点击区域间的切换;
+3、提供光标绘制和清除功能,视觉上标记当前焦点;
+4、支持模拟点击操作,触发界面元素响应;
+5、实现页面切换时的区域列表更新;
+
+本文件的对外接口有8个:
+1、key_handler.register_page():注册页面区域回调函数;
+2、key_handler.switch_to_page():切换到指定页面;
+3、key_handler.clear_areas():清空当前区域列表;
+4、key_handler.add_area():添加可点击区域;
+5、key_handler.next_area():切换到下一个区域;
+6、key_handler.simulate_click():模拟点击当前区域;
+7、key_handler.set_visible():显示/隐藏光标;
+8、key_handler.get_current_area():获取当前区域信息;
+]]
+
+local key_handler = {}
+
+-- 模块内部状态
+local clickable_areas = {}
+local current_area_index = 0
+local page_callbacks = {}
+local config = {
+    cursor_thickness = 2,
+    cursor_color = 0xFF0000,
+    show_cursor = true,
+    last_switch_time = 0
+}
+local current_drawn_rect = nil
+
+
+--[[
+注册页面区域回调函数;
+
+@api key_handler.register_page(page_name, callback)
+
+@summary 注册页面的可点击区域回调函数
+
+@string
+page_name
+页面名称标识符,用于后续页面切换时引用;
+
+@function
+callback
+区域注册回调函数,函数内应调用key_handler.clear_areas()和key_handler.add_area()来定义页面的可点击区域;
+
+@return nil
+
+@usage
+key_handler.register_page("home", function()
+    key_handler.clear_areas()
+    key_handler.add_area(20, 100, 280, 50)  -- 主页按钮1
+    key_handler.add_area(20, 170, 280, 50)  -- 主页按钮2
+end)
+]]
+function key_handler.register_page(page_name, callback)
+    page_callbacks[page_name] = callback
+end
+
+--[[
+切换到指定页面并更新区域列表;
+
+@api key_handler.switch_to_page(page_name)
+
+@summary 切换到指定页面
+
+@string
+page_name
+目标页面名称,必须已通过key_handler.register_page()注册;
+
+@return boolean
+切换成功返回true,失败返回false;
+
+@usage
+local result = key_handler.switch_to_page("home")
+if result then
+    log.info("成功切换到主页")
+end
+]]
+function key_handler.switch_to_page(page_name)
+    key_handler.clear_cursor()
+
+    if page_callbacks[page_name] then
+        page_callbacks[page_name]()
+    else
+        clickable_areas = {}
+    end
+
+    if #clickable_areas > 0 then
+        current_area_index = 1
+        config.last_switch_time = mcu.ticks()
+        key_handler.draw_cursor()
+        return true
+    else
+        current_area_index = 0
+        return false
+    end
+end
+
+-- 清空区域
+function key_handler.clear_areas()
+    key_handler.clear_cursor()
+    clickable_areas = {}
+    current_area_index = 0
+end
+
+-- 添加区域
+function key_handler.add_area(x, y, w, h)
+    table.insert(clickable_areas, { x = x, y = y, w = w, h = h })
+
+    if #clickable_areas == 1 and current_area_index == 0 then
+        current_area_index = 1
+        config.last_switch_time = mcu.ticks()
+    end
+end
+
+-- 获取当前区域矩形
+local function get_current_area_rect()
+    if current_area_index < 1 or current_area_index > #clickable_areas then
+        return nil
+    end
+
+    local area = clickable_areas[current_area_index]
+    return {
+        x1 = area.x - 3,
+        y1 = area.y - 3,
+        x2 = area.x + area.w + 3,
+        y2 = area.y + area.h + 3
+    }
+end
+
+-- 获取当前区域中心
+local function get_current_area_center()
+    if current_area_index < 1 or current_area_index > #clickable_areas then
+        return 160, 240
+    end
+
+    local area = clickable_areas[current_area_index]
+    local center_x = area.x + math.floor(area.w / 2)
+    local center_y = area.y + math.floor(area.h / 2)
+
+    return center_x, center_y
+end
+
+-- 清除光标
+function key_handler.clear_cursor()
+    if current_drawn_rect then
+        lcd.setColor(0xFFFFFF, 0xFFFFFF)
+        local r = current_drawn_rect
+        for i = 1, config.cursor_thickness do
+            local offset = i - 1
+            lcd.drawRectangle(
+                r.x1 + offset,
+                r.y1 + offset,
+                r.x2 - offset,
+                r.y2 - offset
+            )
+        end
+        current_drawn_rect = nil
+    end
+end
+
+-- 绘制光标
+function key_handler.draw_cursor()
+    if not config.show_cursor then
+        return
+    end
+    if current_area_index < 1 or current_area_index > #clickable_areas then
+        return
+    end
+
+    key_handler.clear_cursor()
+
+    local rect = get_current_area_rect()
+    if not rect then return end
+
+    current_drawn_rect = rect
+    lcd.setColor(0xFFFFFF, config.cursor_color)
+
+    local thickness = config.cursor_thickness
+    for i = 1, thickness do
+        local offset = i - 1
+        lcd.drawRectangle(
+            rect.x1 + offset,
+            rect.y1 + offset,
+            rect.x2 - offset,
+            rect.y2 - offset
+        )
+    end
+
+    config.last_switch_time = mcu.ticks()
+end
+
+--[[
+切换到下一个可点击区域;
+
+@api key_handler.next_area()
+
+@summary 在可点击区域间循环切换焦点
+
+@return boolean
+切换成功返回true,失败返回false;
+
+@usage
+local result = key_handler.next_area()
+if result then
+    log.info("已切换到下一个区域")
+end
+]]
+function key_handler.next_area()
+    if #clickable_areas == 0 then
+        return false
+    end
+
+    local old_index = current_area_index
+    current_area_index = current_area_index + 1
+    if current_area_index > #clickable_areas then
+        current_area_index = 1
+    end
+
+    key_handler.draw_cursor()
+    return true
+end
+
+--[[
+模拟点击当前选中区域;
+
+@api key_handler.simulate_click()
+
+@summary 模拟点击当前焦点区域
+
+@return number, number
+返回点击的坐标X, Y;如果当前没有选中区域,返回0, 0;
+
+@usage
+local x, y = key_handler.simulate_click()
+log.info("点击坐标:", x, ",", y)
+]]
+function key_handler.simulate_click()
+    if #clickable_areas == 0 or current_area_index == 0 then
+        return 0, 0
+    end
+
+    local center_x, center_y = get_current_area_center()
+    -- exeasyui必须先按下再单击
+    sys.publish("BASE_TOUCH_EVENT", "TOUCH_DOWN", center_x, center_y)
+    sys.publish("BASE_TOUCH_EVENT", "SINGLE_TAP", center_x, center_y)
+
+    return center_x, center_y
+end
+
+--[[
+显示或隐藏光标;
+
+@api key_handler.set_visible(visible)
+
+@summary 设置光标可见性
+
+@boolean
+visible
+true表示显示光标,false表示隐藏光标;
+
+@return nil
+
+@usage
+key_handler.set_visible(true)   -- 显示光标
+key_handler.set_visible(false)  -- 隐藏光标
+]]
+function key_handler.set_visible(visible)
+    config.show_cursor = visible
+
+    if not visible then
+        key_handler.clear_cursor()
+    else
+        key_handler.draw_cursor()
+    end
+end
+
+--[[
+获取当前选中区域信息;
+
+@api key_handler.get_current_area()
+
+@summary 获取当前焦点区域的详细信息
+
+@return table or nil
+区域信息表,包含以下字段:
+- index: number 当前区域索引
+- total: number 总区域数
+- x: number 区域X坐标
+- y: number 区域Y坐标
+- w: number 区域宽度
+- h: number 区域高度
+- center_x: number 中心点X坐标
+- center_y: number 中心点Y坐标
+如果没有选中区域,返回nil;
+
+@usage
+local area = key_handler.get_current_area()
+if area then
+    log.info("当前区域:", area.index, "/", area.total)
+end
+]]
+function key_handler.get_current_area()
+    if current_area_index < 1 or current_area_index > #clickable_areas then
+        return nil
+    end
+
+    local area = clickable_areas[current_area_index]
+    local center_x, center_y = get_current_area_center()
+
+    return {
+        index = current_area_index,
+        total = #clickable_areas,
+        x = area.x,
+        y = area.y,
+        w = area.w,
+        h = area.h,
+        center_x = center_x,
+        center_y = center_y
+    }
+end
+
+return key_handler

+ 178 - 0
module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirLCD_1000/exeasyui/ui/ui_main.lua

@@ -0,0 +1,178 @@
+--[[
+@module  ui_main
+@summary exEasyUI主程序模块
+@version 1.0
+@date    2025.12.3
+@author  江访
+@usage
+本文件为exEasyUI主程序模块,核心业务逻辑为:
+1、初始化UI主题和窗口系统;
+2、注册所有页面的可点击区域回调函数;
+3、包装窗口方法以捕获页面切换事件;
+4、订阅按键事件并分发到按键处理器;
+5、启动UI渲染主循环,维持界面刷新;
+
+本文件的对外接口有0个:
+1、require加载后自动启动UI主任务;
+]]
+
+local home_page = require("home_page")
+local key_handler = require("key_handler")
+
+-- 页面名称常量
+local PAGE_NAMES = {
+    HOME = "home",
+    COMPONENT = "component",
+    DEFAULT_FONT = "default_font",
+    GTFONT = "gtfont",
+    HZFONT = "hzfont"
+}
+
+-- 当前页面
+local current_page = nil
+
+-- 处理按键事件
+local function handle_key_event(event_type)
+    if event_type == "boot_down" then
+        key_handler.next_area()
+    elseif event_type == "pwr_down" then
+        key_handler.simulate_click()
+    end
+end
+
+-- 主页区域回调函数
+local function home_page_callback()
+    key_handler.clear_areas()
+    key_handler.add_area(20, 100, 280, 50) -- 组件按钮
+    key_handler.add_area(20, 170, 280, 50) -- 默认字体按钮
+    key_handler.add_area(20, 240, 280, 50) -- GTFont按钮
+    key_handler.add_area(20, 310, 280, 50) -- HZFont按钮
+    log.info("ui_main", "已注册主页区域,共4个区域")
+end
+
+-- 组件页面区域回调函数
+local function component_page_callback()
+    key_handler.clear_areas()
+    key_handler.add_area(20, 20, 60, 30)   -- 返回按钮
+    key_handler.add_area(210, 100, 70, 26) -- +10%按钮
+    key_handler.add_area(20, 170, 60, 30)  -- 复选框A
+    key_handler.add_area(120, 170, 60, 30) -- 复选框B
+    key_handler.add_area(20, 430, 80, 30)  -- 普通按钮
+    key_handler.add_area(110, 430, 80, 30) -- 蓝色按钮
+    key_handler.add_area(200, 430, 64, 30) -- 图片按钮
+    log.info("ui_main", "已注册组件页面区域,共7个区域")
+end
+
+-- 默认字体页面区域回调函数
+local function default_font_page_callback()
+    key_handler.clear_areas()
+    key_handler.add_area(20, 20, 60, 30) -- 返回
+    log.info("ui_main", "已注册默认字体页面区域,共1个区域")
+end
+
+-- GTFont页面区域回调函数
+local function gtfont_page_callback()
+    key_handler.clear_areas()
+    key_handler.add_area(20, 20, 60, 30)   -- 返回
+    key_handler.add_area(20, 140, 120, 30) -- 切换字体按钮
+    log.info("ui_main", "已注册GTFont页面区域,共2个区域")
+end
+
+-- HZFont页面区域回调函数
+local function hzfont_page_callback()
+    key_handler.clear_areas()
+    key_handler.add_area(20, 20, 60, 30)   -- 返回
+    key_handler.add_area(20, 140, 120, 30) -- 切换字体按钮
+    log.info("ui_main", "已注册HZFont页面区域,共2个区域")
+end
+
+-- 注册所有页面的回调函数
+local function register_all_pages()
+    key_handler.register_page(PAGE_NAMES.HOME, home_page_callback)
+    key_handler.register_page(PAGE_NAMES.COMPONENT, component_page_callback)
+    key_handler.register_page(PAGE_NAMES.DEFAULT_FONT, default_font_page_callback)
+    key_handler.register_page(PAGE_NAMES.GTFONT, gtfont_page_callback)
+    key_handler.register_page(PAGE_NAMES.HZFONT, hzfont_page_callback)
+end
+
+-- 页面切换处理
+local function handle_page_change(page_name)
+    if not page_name then
+        page_name = PAGE_NAMES.HOME
+    end
+
+    if page_name ~= current_page then
+        current_page = page_name
+        key_handler.switch_to_page(page_name)
+    end
+end
+
+-- 页面切换任务函数
+local function show_subpage_task(page_name)
+    handle_page_change(page_name)
+end
+
+local function back_task()
+    handle_page_change(PAGE_NAMES.HOME)
+end
+
+-- 包装的show_subpage方法
+local function wrapped_show_subpage(self, page_name)
+    -- 调用原始方法
+    local original_show_subpage = getmetatable(self).original_show_subpage
+    original_show_subpage(self, page_name)
+
+    if page_name then
+        sys.taskInit(show_subpage_task, page_name)
+    end
+end
+
+-- 包装的back方法
+local function wrapped_back(self)
+    -- 调用原始方法
+    local original_back = getmetatable(self).original_back
+    original_back(self)
+
+    sys.taskInit(back_task)
+end
+
+-- 包装窗口方法
+local function wrap_window_methods()
+    local test_win = ui.window({})
+    local window_meta = getmetatable(test_win)
+
+    -- 保存原始方法
+    window_meta.original_show_subpage = window_meta.show_subpage
+    window_meta.original_back = window_meta.back
+
+    -- 替换为包装的方法
+    window_meta.show_subpage = wrapped_show_subpage
+    window_meta.back = wrapped_back
+end
+
+-- 启动UI主任务
+local function ui_main()
+    -- 初始化UI主题
+    ui.sw_init({ theme = "light" })
+
+    -- 包装窗口方法
+    wrap_window_methods()
+    register_all_pages()
+
+    -- 创建主页
+    home_page.create()
+
+    -- 确保光标显示
+    key_handler.switch_to_page(PAGE_NAMES.HOME)
+
+    -- 订阅按键事件
+    sys.subscribe("KEY_EVENT", handle_key_event)
+
+    -- 主渲染循环
+    while true do
+        ui.refresh()
+        sys.wait(30)
+    end
+end
+
+sys.taskInit(ui_main)

binární
module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirLCD_1000/lcd/font_drv/customer_font_24.bin


+ 16 - 0
module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirLCD_1000/lcd/font_drv/customer_font_drv.lua

@@ -0,0 +1,16 @@
+--[[ 
+说明:
+customer_font_drv是用户外部自定义外部点阵字体驱动功能模块,因为不需要进行初始化,所以本文件没有任何实质代码,只在此处描述在项目中如何使用。
+自定义字体制作说明可以参考lcd核心库自定义字体制作说明:https://docs.openluat.com/osapi/core/lcd/#_6
+1、通过链接 https://gitee.com/Dozingfiretruck/u8g2_font_tool 下载字体文件制作工具
+2、制作字体
+   - 将.ttf和.otf格式字体加载到制作工具内
+   - 选择自定义,输入需要生成的字体内容,调用自定义文字显示接口只能显示所输入的内容,其他内容需要切换支持的字体
+   - 选择其他,则会按苏哦选编码格式生成对应的字体
+   - 设置字体大小,生成合宙lcd核心库可加载的.bin格式的字体文件
+3、使用lcd.setFontFile(font)接口设置
+   - 其中font为字体文件路径
+   - 若将生成的字体文件命名为customer_font_24.bin 并通过luatools工具同代码一起烧录到脚本分区,则设置接口为lcd.setFontFile("/luadb/customer_font_24.bin")
+   - 若将生成的字体文件命名为customer_font_24.bin 并通过luatools工具同固件和代码一起烧录到文件系统,则设置接口为lcd.setFontFile("/customer_font_24.bin")
+4、使用lcd.drawStr(x,y,str,fg_color)接口显示具体内容
+]]

+ 49 - 0
module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirLCD_1000/lcd/font_drv/gtfont_drv.lua

@@ -0,0 +1,49 @@
+--[[
+@module  gtfont_drv
+@summary GTFont矢量字库驱动模块
+@version 1.0
+@date    2025.12.3
+@author  江访
+@usage
+本模块为GTFont矢量字库驱动功能模块,主要功能包括:
+1、初始化AirFONTS_1000矢量字库小板的SPI接口;
+2、配置SPI通信参数和设备对象;
+3、初始化矢量字库核心功能;
+
+
+说明:
+1、gtfont核心库演示demo,是使用gtfont核心库来驱动合宙AirFONTS_1000矢量字库小板
+2、在主程序mian.lua中require "gtfont_drv"即可执行加载本demo内的演示代码
+3、通过使用gtfont_drv.init()对合宙AirFONTS_1000矢量字库小板进行初始化
+4、通过使用 lcd.drawGtfontUtf8Gray(str,size,gray,x,y)接口在lcd屏幕上灰度显示 UTF8 字符串,支持10-192号字体
+-- 在main.lua中require本模块即可自动初始化
+require "gtfont_drv"
+]]
+
+--[[
+初始化AirFONTS_1000矢量字库小板;
+配置SPI接口和矢量字库;
+
+@api init()
+@summary 初始化AirFONTS_1000矢量字库小板
+@return bool 成功返回true,失败返回false
+@usage
+
+]]
+local function init()
+    --创建一个SPI设备对象
+    gtfont_spi = spi.deviceSetup(0 , 8, 0, 0, 8, 20*1000*1000, spi.MSB, 1, 0)
+    log.error("AirFONTS_1000.init", "spi.deviceSetup", type(gtfont_spi))
+    --检查SPI设备对象是否创建成功
+    if type(gtfont_spi) ~= "userdata" then
+        log.error("AirFONTS_1000.init", "spi.deviceSetup error", type(gtfont_spi))
+    end
+
+    --初始化矢量字库
+    if not gtfont.init(gtfont_spi) then
+        log.error("gtfont_drv.init", "gtfont_drv.init error")
+    end
+
+end
+
+init()

+ 1 - 0
module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirLCD_1000/lcd/font_drv/hzfont_drv.lua

@@ -0,0 +1 @@
+-- hzfont 是合宙开发的一个内置矢量字体库,目前正在开发中

binární
module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirLCD_1000/lcd/images/logo.jpg


+ 52 - 0
module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirLCD_1000/lcd/lcd_drv/exlcd_drv.lua

@@ -0,0 +1,52 @@
+--[[
+@module  exlcd_drv
+@summary 扩展LCD显示驱动模块,基于exlcd扩展库
+@version 1.0
+@date    2025.12.3
+@author  江访
+@usage
+本模块为扩展LCD显示驱动功能模块,主要功能包括:
+1、初始化AirLCD_1000扩展LCD显示;
+2、配置显示缓冲区和自动刷新设置;
+
+对外接口:
+1、exlcd_drv.init():初始化扩展LCD显示驱动
+如果需要实现背光亮度调节,参考exlcd扩展库中的exlcd.set_bl(level)进行设置
+]]
+
+local exlcd = require "exlcd"
+
+local exlcd_drv = {}
+
+--[[
+初始化扩展LCD显示驱动;
+
+@api exlcd_drv.init()
+@summary 配置并初始化AirLCD_1000扩展LCD显示
+@return boolean 初始化成功返回true,失败返回false
+
+@usage
+-- 初始化扩展LCD显示
+local result = exlcd_drv.init({})
+if result then
+    log.info("扩展LCD初始化成功")
+else
+    log.error("扩展LCD初始化失败")
+end
+]]
+
+function exlcd_drv.init()
+    local result = exlcd.init({lcd_model = "AirLCD_1000"})
+
+    log.info("exlcd.init", result)
+
+    if result then
+        -- 显示设置
+        lcd.setupBuff(nil, true)
+        lcd.autoFlush(false)
+    end
+
+    return result
+end
+
+return exlcd_drv

+ 80 - 0
module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirLCD_1000/lcd/lcd_drv/lcd_drv.lua

@@ -0,0 +1,80 @@
+--[[
+@module  lcd_drv
+@summary LCD显示驱动模块,基于lcd核心库
+@version 1.0
+@date    2025.12.3
+@author  江访
+@usage
+本模块为LCD显示驱动功能模块,主要功能包括:
+1、初始化AirLCD_1000 显示IC ST7796;
+2、配置LCD显示参数和显示缓冲区;
+3、支持多种屏幕方向和分辨率设置;
+
+对外接口:
+1、lcd_drv.init():初始化LCD显示驱动
+]]
+
+
+local lcd_drv = {}
+
+--[[
+初始化LCD显示驱动;
+
+@api lcd_drv.init()
+@summary 配置并初始化ST7796 LCD控制器
+@return boolean 初始化成功返回true,失败返回false
+
+@usage
+-- 初始化LCD显示
+local result = lcd_drv.init()
+if result then
+    log.info("LCD初始化成功")
+else
+    log.error("LCD初始化失败")
+end
+]]
+
+function lcd_drv.init()
+    local result = lcd.init("st7796",
+        {
+            pin_rst = 36,                          -- 复位引脚
+            pin_pwr = 1,                           -- 背光控制引脚GPIO的ID号
+            port = lcd.HWID_0,                     -- 驱动端口
+            -- pin_dc = 0xFF,          -- lcd数据/命令选择引脚GPIO ID号,使用lcd 专用 SPI 接口 lcd.HWID_0不需要填此参数,使用通用SPI接口需要赋值
+            direction = 0,                         -- lcd屏幕方向 0:0° 1:90° 2:180° 3:270°,屏幕方向和分辨率保存一致
+            w = 320,                               -- lcd 水平分辨率
+            h = 480,                               -- lcd 竖直分辨率
+            xoffset = 0,                           -- x偏移(不同屏幕ic 不同屏幕方向会有差异)
+            yoffset = 0,                           -- y偏移(不同屏幕ic 不同屏幕方向会有差异)
+            sleepcmd = 0X10,                       -- 睡眠命令,默认0X10
+            wakecmd = 0X11,                        -- 唤醒命令,默认0X11
+            -- bus_speed = 50*1000*1000,                            -- SPI总线速度,不填默认50M,若速率要求更高需要进行设置
+            -- interface_mode = lcd.WIRE_4_BIT_8_INTERFACE_I,       -- lcd模式,默认lcd.WIRE_4_BIT_8_INTERFACE_I
+            -- direction0 = {0x36,0x00},                            -- 0°方向的命令,(不同屏幕ic会有差异)
+            -- direction90 = {0x36,0x60},                           -- 90°方向的命令,(不同屏幕ic会有差异)
+            -- direction180 ={0x36,0xc0} ,                          -- 180°方向的命令,(不同屏幕ic会有差异)
+            -- direction270 = {0x36,0xA0},                          -- 270°方向的命令,(不同屏幕ic会有差异)
+            -- hbp = nil,                                           -- 水平后廊
+            -- hspw = nil,                                          -- 水平同步脉冲宽度
+            -- hfp = 0,                                             -- 水平前廊
+            -- vbp = 0,                                             -- 垂直后廊
+            -- vspw = 0,                                            -- 垂直同步脉冲宽度
+            -- vfp = 0,                                             -- 垂直前廊
+            -- initcmd = nil,                                       -- 自定义屏幕初始化命令表
+            -- flush_rate = nil,                                    -- 刷新率
+            -- spi_dev = nil,                                       -- spi设备,当port = "device"时有效,当port ≠ "device"时可不填或者填nil
+            -- init_in_service = false,                             -- 允许初始化在lcd service里运行,在后台初始化LCD,默认是false,Air8000/G/W/T/A、Air780EHM/EGH/EHV 支持填true,可加快初始化速度,默认SPI总线速度80M
+        })
+
+    log.info("lcd.init", result)
+
+    if result then
+        -- 显示设置
+        lcd.setupBuff(nil, true)
+        lcd.autoFlush(false)
+    end
+
+    return result
+end
+
+return lcd_drv

+ 113 - 0
module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirLCD_1000/lcd/main.lua

@@ -0,0 +1,113 @@
+--[[
+@module  main
+@summary LuatOS用户应用脚本文件入口,总体调度应用逻辑
+@version 1.0
+@date    2025.12.3
+@author  江访
+@usage
+本demo演示的核心功能为:
+本demo演示的核心功能为:
+1、LCD显示屏驱动初始化,支持多种LCD驱动模式;
+2、按键驱动初始化,支持BOOT键和PWR键操作;
+3、字库驱动管理,支持外部矢量字库、内部软件矢量字库和外部自定义点阵字库;
+4、用户界面主循环,实现多页面切换和按键事件处理;
+5、系统看门狗配置,确保系统稳定运行;
+
+更多说明参考本目录下的readme.md文件
+]]
+
+--[[
+必须定义PROJECT和VERSION变量,Luatools工具会用到这两个变量,远程升级功能也会用到这两个变量
+PROJECT:项目名,ascii string类型
+        可以随便定义,只要不使用,就行
+VERSION:项目版本号,ascii string类型
+        如果使用合宙iot.openluat.com进行远程升级,必须按照"XXX.YYY.ZZZ"三段格式定义:
+            X、Y、Z各表示1位数字,三个X表示的数字可以相同,也可以不同,同理三个Y和三个Z表示的数字也是可以相同,可以不同
+            因为历史原因,YYY这三位数字必须存在,但是没有任何用处,可以一直写为000
+        如果不使用合宙iot.openluat.com进行远程升级,根据自己项目的需求,自定义格式即可
+]]
+
+-- main.lua - 程序入口文件
+
+-- 定义项目名称和版本号
+PROJECT = "ui_demo" -- 项目名称
+VERSION = "001.000.000"    -- 版本号
+
+-- 在日志中打印项目名和项目版本号
+log.info("ui_demo", PROJECT, VERSION)
+
+-- 设置日志输出风格为样式2(建议调试时开启)
+-- log.style(2)
+
+-- 如果内核固件支持wdt看门狗功能,此处对看门狗进行初始化和定时喂狗处理
+-- 如果脚本程序死循环卡死,就会无法及时喂狗,最终会自动重启
+if wdt then
+    --配置喂狗超时时间为9秒钟
+    wdt.init(9000)
+    --启动一个循环定时器,每隔3秒钟喂一次狗
+    sys.timerLoopStart(wdt.feed, 3000)
+end
+
+
+-- 如果内核固件支持errDump功能,此处进行配置,【强烈建议打开此处的注释】
+-- 因为此功能模块可以记录并且上传脚本在运行过程中出现的语法错误或者其他自定义的错误信息,可以初步分析一些设备运行异常的问题
+-- 以下代码是最基本的用法,更复杂的用法可以详细阅读API说明文档
+-- 启动errDump日志存储并且上传功能,600秒上传一次
+-- if errDump then
+--     errDump.config(true, 600)
+-- end
+
+
+-- 使用LuatOS开发的任何一个项目,都强烈建议使用远程升级FOTA功能
+-- 可以使用合宙的iot.openluat.com平台进行远程升级
+-- 也可以使用客户自己搭建的平台进行远程升级
+-- 远程升级的详细用法,可以参考fota的demo进行使用
+
+
+-- 启动一个循环定时器
+-- 每隔3秒钟打印一次总内存,实时的已使用内存,历史最高的已使用内存情况
+-- 方便分析内存使用是否有异常
+-- sys.timerLoopStart(function()
+--     log.info("mem.lua", rtos.meminfo())
+--     log.info("mem.sys", rtos.meminfo("sys"))
+-- end, 3000)
+
+
+-- 加载显示屏驱动管理功能模块,有以下两种:
+-- 1、使用lcd核心库驱动的lcd_drv.lua
+-- 2、使用exlcd扩展库驱动的exlcd_drv.lua
+-- 根据自己的需求,启用两者中的任何一种都可以
+-- 也可以不启用任何一种,不使用显示屏功能
+lcd_drv = require "lcd_drv"
+-- lcd_drv = require "exlcd_drv"
+
+
+-- 加载按键驱动管理功能模块
+key_drv = require "key_drv"
+
+
+-- 加载字库驱动管理功能模块,有以下三种:
+-- 1、使用gtfont核心库驱动AirFONTS_1000矢量字库配件板的gtfont_drv.lua
+-- 2、使用hzfont核心库驱动内核固件中支持的软件矢量字库的hzfont_drv.lua(正在开发中,后续补充)
+-- 3、使用自定义字体
+-- 根据自己的需求,启用三者中的任何几种都可以
+-- 也可以不启用任何一种,只使用内核固件中自带的点阵字库
+require "gtfont_drv"
+-- require "hzfont_drv"
+-- 使用外部自定义字体不需要require "customer_font_drv",可以参照customer_font_drv.lua内的使用说明进行创建和加载字体文件
+
+
+
+-- 加载输入法驱动管理功能模块(正在开发中,后续补充)
+
+
+-- 加载lcd核心库实现的用户界面功能模块
+-- 实现多页面切换、按键事件分发和界面渲染功能
+-- 包含主页、lcd核心库功能演示页、GTFont演示页和自定义字体演示页
+require "ui_main"
+
+
+-- 用户代码已结束
+-- 结尾总是这一句
+sys.run()
+-- sys.run()之后不要加任何语句!!!!!因为添加的任何语句都不会被执行

+ 247 - 0
module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirLCD_1000/lcd/readme.md

@@ -0,0 +1,247 @@
+# LCD、按键与字体演示系统
+
+## 一、功能模块介绍
+
+### 1.1 核心主程序模块
+1. **main.lua** - 主程序入口,负责系统初始化和任务调度
+2. **ui_main.lua** - 用户界面主控模块,管理页面切换和事件分发
+
+### 1.2 显示页面模块
+3. **home_page.lua** - 主页模块,提供应用入口和导航功能
+4. **lcd_page.lua** - LCD图形绘制演示模块
+5. **gtfont_page.lua** - GTFont矢量字体演示模块
+6. **customer_font_page.lua** - 自定义字体演示模块
+
+### 1.3 驱动模块
+7. **lcd_drv.lua** - LCD显示驱动模块,基于lcd核心库,lcd_drv和exlcd_drv二选一使用
+8. **exlcd_drv.lua** - LCD显示驱动模块,基于exlcd扩展库,lcd_drv和exlcd_drv二选一使用
+9. **key_drv.lua** - 按键驱动模块,管理BOOT键和PWR键
+10. **gtfont_drv.lua** - GTFont矢量字库驱动模块
+11. **customer_font_drv.lua** - 自定义外部字体驱动功能模块
+12. **hzfont_drv.lua** - 合宙软件矢量字库(开发中)
+    - gtfont_drv、customer_font_drv、hzfont_drv
+    - 可以都不启用
+    - 可以仅启用一种
+    - 可以启用任意两种
+    - 可以全部启用
+
+## 二、按键消息介绍
+
+1. **"KEY_EVENT"** - 按键事件消息,包含按键类型和状态
+   - boot键事件:`boot_down`(按下)、`boot_up`(释放)
+   - pwr键事件:`pwr_down`(按下)、`pwr_up`(释放)
+   - 按键功能定义:
+     - 主页:boot键(按下)选择/切换选项,pwr键(按下)确认
+     - GTFont页面:boot键(按下)切换模式,pwr键(按下)返回
+     - LCD页面:pwr键(按下)返回,boot键无功能
+     - 自定义字体页面:pwr键(按下)返回,boot键无功能
+
+## 三、显示效果
+
+<table>
+<tr>
+<td>主页<br/></td><td>lcd核心库页面<br/></td><td>gtfont页面<br/></td><td>自定义字体页面<br/></td></tr>
+<tr>
+<td rowspan="2"><img src="https://docs.openluat.com/cdn/image/Air780EHV_AirLCD_1000_AirFONTS_1000演示主页.png" width="80" /><br/></td><td rowspan="2"><img src="https://docs.openluat.com/cdn/image/Air780EHMLCD演示2.png" width="80" /><br/></td><td><img src="https://docs.openluat.com/cdn/image/Air780EHMLCD演3.png" width="80" /><br/></td><td rowspan="2"><img src="https://docs.openluat.com/cdn/image/Air780EHMLCD演示5.jpg" width="80" /><br/></td></tr>
+<tr>
+<td><img src="https://docs.openluat.com/cdn/image/Air780EHMLCD演示4.jpg" width="80" /><br/></td></tr>
+</table>
+
+
+### 4.1 LCD图形绘制演示
+1. **基本图形绘制** - 展示点、线、矩形、圆形等基本图形绘制功能
+2. **图片显示** - 支持外部图片文件显示
+3. **二维码生成** - 动态生成并显示二维码
+4. **xbm格式位图示例** - 显示16*16 xbm点阵
+5. **中文、英文字体示例** - 显示12号中文字体和英文字体
+
+### 4.2 GTFont矢量字体演示
+1. **矢量字体显示** - 使用AirFONTS_1000矢量字库小板显示平滑字体
+2. **字体大小切换** - 支持10-192号字体大小动态变化
+3. **灰度模式** - 支持灰度显示模式,字体边缘更平滑
+4. **多颜色显示** - 支持多种颜色字体显示
+
+### 4.3 自定义字体演示
+1. **外部字体加载** - 支持加载外部自定义字体文件
+2. **多颜色文字** - 支持不同颜色的文字显示
+
+### 4.4 按键交互功能
+1. **页面导航** - 支持多页面之间的切换
+2. **按钮响应** - 按键的点击响应功能
+3. **模式切换** - 支持gtfont切换灰度/常规显示
+
+## 五、演示硬件环境
+
+### 5.1 硬件清单
+
+- Air780EHM/Air780EHV/Air780EGH 核心板 × 1
+- AirLCD_1000 配件板 × 1
+- GTFont 矢量字库,使用的是 AirFONTS_1000 配件板 × 1
+- 母对母杜邦线 × 14,杜邦线太长的话,会出现 spi 通信不稳定的现象;
+- TYPE-C 数据线 × 1
+- Air780EHM/Air780EHV/Air780EGH 核心板和 AirLCD_1000配件板以及AirFONTS_1000 配件板的硬件接线方式为
+
+  - Air780EHM/Air780EHV/Air780EGH 核心板通过 TYPE-C USB 口供电(核心板正面开关拨到 ON 一端),此种供电方式下,VDD_EXT 引脚为 3.3V,可以直接给 AirLCD_1000配件板和AirFONTS_1000 配件板供电;
+  - 为了演示方便,所以 Air780EHM/Air780EHV/Air780EGH 核心板上电后直接通过 VBAT 引脚给 AirLCD_1000配件板,VDD-EXT引脚给AirFONTS_1000 配件板供电;
+  - 客户在设计实际项目时,一般来说,需要通过一个 GPIO 来控制 LDO 给配件板供电,这样可以灵活地控制配件板的供电,可以使项目的整体功耗降到最低;
+
+### 5.2 接线配置
+
+#### 5.2.1 LCD 显示屏接线
+
+<table>
+<tr>
+<td>Air780EHM/Air780EHV/Air780EGH 核心板<br/></td><td>AirLCD_1000配件板<br/></td></tr>
+<tr>
+<td>53/LCD_CLK<br/></td><td>SCLK/CLK<br/></td></tr>
+<tr>
+<td>52/LCD_CS<br/></td><td>CS<br/></td></tr>
+<tr>
+<td>49/LCD_RST<br/></td><td>RES/RST<br/></td></tr>
+<tr>
+<td>50/LCD_SDA<br/></td><td>SDA/MOS<br/></td></tr>
+<tr>
+<td>51/LCD_RS<br/></td><td>DC/RS<br/></td></tr>
+<tr>
+<td>22/GPIO1<br/></td><td>BLK<br/></td></tr>
+<tr>
+<td>VBAT<br/></td><td>VCC<br/></td></tr>
+<tr>
+<td>GND<br/></td><td>GND<br/></td></tr>
+</table>
+
+#### 5.2.2 GTFont 字库接线
+
+<table>
+<tr>
+<td>Air780EHM/Air780EHV/Air780EGH 核心板<br/></td><td>AirFONTS_1000配件板<br/></td></tr>
+<tr>
+<td>83/SPI0_CS<br/></td><td>CS<br/></td></tr>
+<tr>
+<td>84/SPI0_MISO<br/></td><td>MISO<br/></td></tr>
+<tr>
+<td>85/SPI0_MOSI<br/></td><td>MOSI<br/></td></tr>
+<tr>
+<td>86/SPI0_CLK<br/></td><td>CLK<br/></td></tr>
+<tr>
+<td>24/VDD_EXT<br/></td><td>VCC<br/></td></tr>
+<tr>
+<td>GND<br/></td><td>GND<br/></td></tr>
+</table>
+
+#### 5.2.3 接线图
+![](https://docs.openLuat.com/cdn/image/Air780EHV核心板_AirLCD_1000_AirFONTS_1000接线图.jpg)
+
+## 六、演示软件环境
+
+### 6.1 开发工具
+
+- [Luatools下载调试工具](https://docs.openluat.com/air780egh/luatos/common/download/) - 固件烧录和代码调试
+
+### 6.2 内核固件
+
+- [点击下载Air780EHM系列最新版本内核固件](https://docs.openluat.com/air780epm/luatos/firmware/version/),demo所使用的是LuatOS-SoC_V2018_Air780EHM 1号固件
+  
+- [点击下载Air780EHV系列最新版本内核固件](https://docs.openluat.com/air780ehv/luatos/firmware/version/),demo所使用的是LuatOS-SoC_V2018_Air780EHV 1号固件
+  
+- [点击下载Air780EGH系列最新版本内核固件](https://docs.openluat.com/air780egh/luatos/firmware/version/),demo所使用的是LuatOS-SoC_V2018_Air780EGH 1号固件
+
+### 6.3 字体文件
+- 自定义字体文件:`customer_font_24.bin`(和lua脚本文件一起烧录,会自动放置在`/luadb/`目录下)
+- 演示图片文件:`logo.jpg`(和lua脚本文件一起烧录,会自动放置在`/luadb/`目录下)
+- [点击查看自定义字体生成和使用说明](https://docs.openluat.com/osapi/core/lcd/?h=lcd#_6)
+
+
+## 七、演示核心步骤
+
+### 7.1 硬件准备
+1. 按照硬件接线表连接所有设备
+2. 确保电源连接正确,通过TYPE-C USB口供电
+3. 检查所有接线无误,避免短路
+
+### 7.2 软件配置
+在`main.lua`中选择加载对应的驱动模块:
+
+```lua
+-- 加载显示屏驱动管理功能模块,有以下两种:
+-- 1、使用lcd核心库驱动的lcd_drv.lua
+-- 2、使用exlcd扩展库驱动的exlcd_drv.lua
+-- 根据自己的需求,启用两者中的任何一种都可以
+-- 也可以不启用任何一种,不使用显示屏功能
+-- lcd_drv = require "lcd_drv"
+lcd_drv = require "exlcd_drv"
+
+
+-- 加载按键驱动管理功能模块
+key_drv = require "key_drv"
+
+
+-- 加载字库驱动管理功能模块,有以下三种:
+-- 1、使用gtfont核心库驱动AirFONTS_1000矢量字库配件板的gtfont_drv.lua
+-- 2、使用hzfont核心库驱动内核固件中支持的软件矢量字库的hzfont_drv.lua(正在开发中,后续补充)
+-- 3、使用自定义字体
+-- 根据自己的需求,启用三者中的任何几种都可以
+-- 也可以不启用任何一种,只使用内核固件中自带的点阵字库
+require "gtfont_drv"
+-- require "hzfont_drv"
+-- 使用外部自定义字体不需要require "customer_font_drv",可以参照customer_font_drv.lua内的使用说明进行创建和加载字体文件
+
+
+
+-- 加载输入法驱动管理功能模块(正在开发中,后续补充)
+
+
+-- 加载lcd核心库实现的用户界面功能模块
+-- 实现多页面切换、按键事件分发和界面渲染功能
+-- 包含主页、lcd核心库功能演示页、GTFont演示页和自定义字体演示页
+require "ui_main"
+```
+
+### 7.3 软件烧录
+1. 使用Luatools烧录最新内核固件
+2. 下载并烧录本项目所有脚本文件
+3. 将字体文件和图片文件随脚本文件一起烧录到脚本分区
+4. 烧录成功后设备自动重启后开始运行
+
+### 7.4 功能测试
+
+#### 7.4.1 主页面操作
+1. 设备启动后显示主页面,包含三个功能按钮
+2. 查看系统功能概览信息
+3. 使用boot键切换选择,pwr键进入对应演示页面
+
+#### 7.4.2 LCD演示页面
+1. 查看基本图形绘制示例(点、线、矩形、圆形)
+2. 查看图片显示区域(显示logo图片)
+3. 查看二维码区域(合宙文档二维码)
+4. 查看位图和字体示例
+5. 按pwr键返回主页
+
+#### 7.4.3 GTFont演示页面
+1. 计时阶段,gtfont说明显示
+2. 字体大小变化阶段,查看10-192号字体动态变化
+3. 按boot键切换"灰度/常规"模式
+4. 查看多颜色字体显示效果
+5. 按pwr键返回主页
+
+#### 7.4.4 自定义字体页面
+1. 查看外部字体文件显示效果
+2. 查看多颜色文字显示(红色、绿色、蓝色)
+3. 查看字体使用说明和接口信息
+4. 按pwr键返回主页
+
+### 7.5 预期效果
+
+- **主页面**:正常显示,使用boot键切换选项,pwr键确认
+- **LCD演示页面**:图形绘制清晰,图片和二维码显示正常,颜色示例完整,pwr键返回
+- **GTFont演示页面**:字体显示平滑,字号切换流畅,boot键切换模式,pwr键返回
+- **自定义字体页面**:外部字体加载正确,多颜色文字显示正常,pwr键返回
+- **按键交互**:所有按键操作响应及时准确,页面切换流畅
+
+### 7.6 故障排除
+
+1. **显示异常**:检查LCD接线是否正确,确认电源供电稳定
+2. **按键无响应**:检查参数是否配置正确,确认按键驱动初始化成功
+3. **字体显示异常**:检查SPI接线(如使用GTFont),确认字体文件路径正确
+4. **图片无法显示**:确认图片文件已正确烧录到指定路径
+5. **系统卡顿**:检查内存使用情况,适当调整刷新频率

+ 70 - 0
module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirLCD_1000/lcd/tp_key_drv/key_drv.lua

@@ -0,0 +1,70 @@
+--[[
+@module  key_drv
+@summary 按键驱动模块
+@version 1.0
+@date    2025.12.3
+@author  江访
+@usage
+本文件为按键驱动功能模块,核心业务逻辑为:
+1、初始化BOOT键和PWR键的GPIO;
+2、配置按键事件的中断处理函数;
+3、实现按键防抖功能,防止误触发;
+4、对外发布按键消息;
+
+本文件的对外接口有1个:
+1、key_drv.init():初始化按键驱动;
+]]
+
+local key_drv = {}
+
+-- 按键定义
+local key_boot = 0           -- GPIO0按键(BOOT键)
+local key_pwr = gpio.PWR_KEY -- 电源按键
+
+
+-- 按键事件处理函数
+local function handle_boot_key(val)
+    -- print("key_boot", val)
+    if val == 1 then
+        sys.publish("KEY_EVENT", "boot_down")
+    else
+        sys.publish("KEY_EVENT", "boot_up")
+    end
+end
+
+local function handle_pwr_key(val)
+    -- print("key_pwr", val)
+    if val == 1 then
+        sys.publish("KEY_EVENT", "pwr_up")
+    else
+        sys.publish("KEY_EVENT", "pwr_down")
+    end
+end
+
+--[[
+初始化按键GPIO;
+配置BOOT键和PWR键的GPIO中断;
+
+@api key_drv.init()
+@summary 配置BOOT键和PWR键的GPIO中断
+@return bool 初始化只会返回true
+
+@usage
+local result = key_drv.init()
+if result then
+    log.info("按键驱动初始化成功")
+end
+]]
+function key_drv.init()
+    gpio.setup(key_boot, handle_boot_key, gpio.PULLDOWN, gpio.BOTH)
+    gpio.debounce(key_boot, 50, 0)     -- 防抖,防止频繁触发
+
+    gpio.setup(key_pwr, handle_pwr_key, gpio.PULLUP, gpio.BOTH)
+    gpio.debounce(key_pwr, 50, 0)     -- 防抖,防止频繁触发
+
+    log.info("key_drv", "按键初始化完成")
+    return true
+end
+
+key_drv.init()
+return key_drv

+ 117 - 0
module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirLCD_1000/lcd/ui/customer_font_page.lua

@@ -0,0 +1,117 @@
+--[[
+@module  customer_font_page
+@summary 自定义字体演示模块
+@version 1.0
+@date    2025.12.3
+@author  江访
+@usage
+本模块为自定义字体演示功能模块,主要功能包括:
+1、展示外部自定义字体文件的加载和显示功能;
+2、提供多颜色文字显示效果;
+3、显示字体使用说明和接口信息;
+
+按键功能:
+- PWR键:返回主页
+- BOOT键:无功能
+
+对外接口:
+1、customer_font_page.draw():绘制自定义字体演示页面
+2、customer_font_page.handle_key():处理自定义字体页面按键事件
+3、customer_font_page.on_leave():页面离开时恢复系统字体
+]]
+
+local customer_font_page = {}
+
+--[[
+绘制自定义字体演示页面;
+绘制自定义字体演示页面的所有UI元素和字体内容;
+
+@api customer_font_page.draw()
+@summary 绘制自定义字体演示页面的所有UI元素和字体内容
+@return nil
+
+@usage
+-- 在UI主循环中调用
+customer_font_page.draw()
+]]
+function customer_font_page.draw()
+    lcd.clear()
+    lcd.setFont(lcd.font_opposansm12_chinese)
+
+    -- 设置默认颜色
+    lcd.setColor(0xFFFF, 0x0000)
+
+    -- 显示标题(使用系统字体)
+    lcd.drawStr(106, 20, "自定义点阵字体演示", 0x0000)
+    
+    -- 显示操作提示
+    lcd.drawStr(20, 40, "按PWR键返回主页", 0x0000)
+
+    -- 设置自定义字体文件
+    lcd.setFontFile("/luadb/customer_font_24.bin")
+
+    lcd.drawStr(112, 160, "上海合宙", 0xF800) -- 红色
+    lcd.drawStr(120, 200, "LuatOS", 0x07E0) -- 绿色
+    lcd.drawStr(100, 240, "演示demo", 0x001F) -- 蓝色
+
+    -- 恢复系统字体显示说明
+    lcd.setFont(lcd.font_opposansm12_chinese)
+    lcd.setColor(0xFFFF, 0x0000)
+
+    -- 显示说明信息
+    lcd.drawStr(20, 320, "- 字体路径: /luadb/customer_font_24.bin", 0x0000)
+    -- 显示使用说明
+    lcd.drawStr(20, 340, "使用接口:", 0x0000)
+    lcd.drawStr(20, 360, "lcd.setFontFile(字体路径)", 0x0000)
+    lcd.drawStr(20, 380, "lcd.drawStr(x, y,文本)", 0x0000)
+    
+    -- 显示当前操作状态
+    lcd.drawStr(20, 420, "当前页面: 自定义字体演示", 0x0000)
+end
+
+--[[
+处理按键事件;
+根据按键类型执行相应的操作;
+
+@api customer_font_page.handle_key(key_type, switch_page)
+@summary 处理自定义字体页面按键事件
+@string key_type 按键类型
+@valid_values "pwr_up"
+@function switch_page 页面切换回调函数
+@return bool 事件处理成功返回true,否则返回false
+
+@usage
+-- 在UI主循环中调用
+local handled = customer_font_page.handle_key("pwr_up", switch_page)
+]]
+function customer_font_page.handle_key(key_type, switch_page)
+    log.info("customer_font_page.handle_key", "key_type:", key_type)
+    
+    if key_type == "pwr_up" then
+        -- PWR键:返回首页
+        switch_page("home")
+        return true
+    end
+    -- BOOT键无功能
+    return false
+end
+
+--[[
+页面离开时恢复系统字体;
+恢复系统默认字体设置;
+
+@api customer_font_page.on_leave()
+@summary 恢复系统默认字体设置
+@return nil
+
+@usage
+-- 在页面切换时调用
+customer_font_page.on_leave()
+]]
+function customer_font_page.on_leave()
+    -- 恢复使用12号中文字体
+    lcd.setFont(lcd.font_opposansm12_chinese)
+    log.info("customer_font_page", "已恢复系统字体")
+end
+
+return customer_font_page

+ 391 - 0
module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirLCD_1000/lcd/ui/gtfont_page.lua

@@ -0,0 +1,391 @@
+--[[
+@module  gtfont_page
+@summary GTFont矢量字体演示模块
+@version 1.0
+@date    2025.12.3
+@author  江访
+@usage
+本模块为GTFont矢量字体演示功能模块,主要功能包括:
+1、展示AirFONTS_1000矢量字库小板的字体显示功能;
+2、支持10-192号字体大小动态变化演示;
+3、支持灰度模式和常规模式切换;
+4、提供倒计时和字体大小变化两种演示阶段;
+
+按键功能:
+- BOOT键:切换灰度/常规模式
+- PWR键:返回主页
+
+对外接口:
+1、gtfont_page.draw():绘制GTFont演示页面
+2、gtfont_page.handle_key():处理GTFont页面按键事件
+3、gtfont_page.on_enter():页面进入时状态重置
+4、gtfont_page.on_leave():页面离开时恢复系统刷新率
+]]
+
+local gtfont_page = {}
+
+-- 按钮区域定义
+local back_button = { x1 = 10, y1 = 10, x2 = 80, y2 = 50 }
+local switch_button = { x1 = 220, y1 = 10, x2 = 310, y2 = 50 }
+
+-- 字体显示状态
+local font_demo_state = {
+    use_gray = true,                   -- 默认为灰度模式
+    demo_phase = 1,                    -- 演示阶段
+    last_update = 0,
+    update_interval = 1000,            -- 倒计时更新间隔
+    font_update_interval = 20,         -- 字体更新间隔
+    countdown = 18,                     -- 倒计时
+    current_size = 16,                 -- 当前字体大小
+    color_phase = 1,                   -- 颜色阶段
+}
+
+--[[
+绘制开关按钮;
+绘制灰度/常规模式切换按钮;
+
+@api draw_switch_button()
+@summary 绘制灰度/常规模式切换按钮
+@return nil
+
+@usage
+-- 在draw函数中调用
+draw_switch_button()
+]]
+local function draw_switch_button()
+    -- 按钮背景
+    lcd.fill(switch_button.x1, switch_button.y1, switch_button.x2, switch_button.y2, 0xC618)
+
+    -- 开关指示器
+    if font_demo_state.use_gray then
+        -- 灰度模式,左边填充
+        lcd.fill(switch_button.x1, switch_button.y1,
+            switch_button.x1 + (switch_button.x2 - switch_button.x1) / 2,
+            switch_button.y2, 0x07E0)
+    else
+        -- 常规模式,右边填充
+        lcd.fill(switch_button.x1 + (switch_button.x2 - switch_button.x1) / 2,
+            switch_button.y1, switch_button.x2, switch_button.y2, 0x07E0)
+    end
+
+    -- 按钮文字
+    lcd.setColor(0xFFFF, 0x0000)
+    lcd.setFont(lcd.font_opposansm12_chinese)
+    lcd.drawStr(switch_button.x1 + 10, switch_button.y1 + 25, "灰度", 0x0000)
+    lcd.drawStr(switch_button.x1 + (switch_button.x2 - switch_button.x1) / 2 + 10,
+        switch_button.y1 + 25, "常规", 0x0000)
+end
+
+--[[
+绘制返回按钮;
+绘制返回主页按钮;
+
+@api draw_back_button()
+@summary 绘制返回主页按钮
+@return nil
+
+@usage
+-- 在draw函数中调用
+draw_back_button()
+]]
+local function draw_back_button()
+    lcd.fill(back_button.x1, back_button.y1, back_button.x2, back_button.y2, 0xC618)
+    lcd.setColor(0xFFFF, 0x0000)
+    lcd.setFont(lcd.font_opposansm12_chinese)
+    lcd.drawStr(back_button.x1 + 25, back_button.y1 + 25, "返回", 0x0000)
+end
+
+--[[
+绘制倒计时阶段;
+绘制第一阶段倒计时演示内容;
+
+@api draw_countdown_phase()
+@summary 绘制倒计时阶段演示内容
+@return bool 是否完成倒计时
+
+@usage
+-- 在draw_font_demo函数中调用
+local finished = draw_countdown_phase()
+]]
+local function draw_countdown_phase()
+    local _, current_time = mcu.ticks2(1)
+    
+    if current_time - font_demo_state.last_update > font_demo_state.update_interval then
+        font_demo_state.countdown = font_demo_state.countdown - 1
+        font_demo_state.last_update = current_time
+        
+        if font_demo_state.countdown <= 0 then
+            font_demo_state.demo_phase = 2
+            font_demo_state.current_size = 16
+            font_demo_state.color_phase = 1
+            font_demo_state.last_update = current_time
+            return true
+        end
+    end
+    
+    -- 设置背景色为白色,文字的前景色为黑色
+    lcd.setColor(0xFFFF, 0x0000)
+    if font_demo_state.use_gray then
+        lcd.drawGtfontUtf8Gray("AirFONTS_1000配件板", 24, 2, 50, 70)
+    else
+        lcd.drawGtfontUtf8("AirFONTS_1000配件板", 24, 50, 70)
+    end
+
+    -- 设置背景色为白色,文字的前景色为红色
+    lcd.setColor(0xFFFF, 0xF800)
+    if font_demo_state.use_gray then
+        lcd.drawGtfontUtf8Gray("支持10到192号的黑体字体", 24, 2, 40, 111)
+    else
+        lcd.drawGtfontUtf8("支持10到192号的黑体字体", 24, 40, 111)
+    end
+
+    -- 设置背景色为白色,文字的前景色为绿色
+    lcd.setColor(0xFFFF, 0x07E0)
+    if font_demo_state.use_gray then
+        lcd.drawGtfontUtf8Gray("支持GBK中文和ASCII码字符集", 20, 2, 35, 152)
+    else
+        lcd.drawGtfontUtf8("支持GBK中文和ASCII码字符集", 20, 35, 152)
+    end
+
+    -- 设置背景色为白色,文字的前景色为蓝色
+    lcd.setColor(0xFFFF, 0x001F)
+    if font_demo_state.use_gray then
+        lcd.drawGtfontUtf8Gray("支持灰度显示,字体边缘更平滑", 20, 2, 35, 193)
+    else
+        lcd.drawGtfontUtf8("支持灰度显示,字体边缘更平滑", 20, 35, 193)
+    end
+
+    -- 倒计时
+    lcd.setColor(0xFFFF, 0x0000)
+    if font_demo_state.use_gray then
+        lcd.drawGtfontUtf8Gray("倒计时 : " .. font_demo_state.countdown, 24, 2, 100, 233)
+    else
+        lcd.drawGtfontUtf8("倒计时 : " .. font_demo_state.countdown, 24, 100, 233)
+    end
+    
+    return false
+end
+
+--[[
+绘制字体大小变化阶段;
+绘制第二阶段字体大小变化演示内容;
+
+@api draw_font_size_phase()
+@summary 绘制字体大小变化阶段演示内容
+@return bool 是否完成所有阶段
+
+@usage
+-- 在draw_font_demo函数中调用
+local finished = draw_font_size_phase()
+]]
+local function draw_font_size_phase()
+    local _, current_time = mcu.ticks2(1)
+    
+    if current_time - font_demo_state.last_update > font_demo_state.font_update_interval then
+        font_demo_state.current_size = font_demo_state.current_size + 1
+        font_demo_state.last_update = current_time
+    end
+    
+    -- 根据颜色阶段设置颜色
+    if font_demo_state.color_phase == 1 then
+        lcd.setColor(0xFFFF, 0x0000) -- 黑色
+    elseif font_demo_state.color_phase == 2 then
+        lcd.setColor(0xFFFF, 0xF800) -- 红色
+    elseif font_demo_state.color_phase == 3 then
+        lcd.setColor(0xFFFF, 0x07E0) -- 绿色
+    else
+        lcd.setColor(0xFFFF, 0x001F) -- 蓝色
+    end
+    
+    -- 根据颜色阶段和字体大小显示不同内容
+    if font_demo_state.color_phase == 1 then
+        if font_demo_state.current_size <= 64 then
+            if font_demo_state.use_gray then
+                lcd.drawGtfontUtf8Gray(font_demo_state.current_size .. "号:合宙AirFONTS_1000", 
+                                      font_demo_state.current_size, 4, 10, 100)
+            else
+                lcd.drawGtfontUtf8(font_demo_state.current_size .. "号:合宙AirFONTS_1000", 
+                                  font_demo_state.current_size, 10, 100)
+            end
+        else
+            font_demo_state.color_phase = 2
+            font_demo_state.current_size = 65
+        end
+    elseif font_demo_state.color_phase == 2 then
+        if font_demo_state.current_size <= 96 then
+            if font_demo_state.use_gray then
+                lcd.drawGtfontUtf8Gray(font_demo_state.current_size .. "号", 
+                                      font_demo_state.current_size, 4, 10, 100)
+                lcd.drawGtfontUtf8Gray("AirFONTS_1000", 
+                                      font_demo_state.current_size, 4, 10, 100 + font_demo_state.current_size + 5)
+            else
+                lcd.drawGtfontUtf8(font_demo_state.current_size .. "号", 
+                                  font_demo_state.current_size, 10, 100)
+                lcd.drawGtfontUtf8("AirFONTS_1000", 
+                                  font_demo_state.current_size, 10, 100 + font_demo_state.current_size + 5)
+            end
+        else
+            font_demo_state.color_phase = 3
+            font_demo_state.current_size = 97
+        end
+    elseif font_demo_state.color_phase == 3 then
+        if font_demo_state.current_size <= 128 then
+            if font_demo_state.use_gray then
+                lcd.drawGtfontUtf8Gray(font_demo_state.current_size .. "号", 
+                                      font_demo_state.current_size, 4, 10, 50)
+                lcd.drawGtfontUtf8Gray("合宙", 
+                                      font_demo_state.current_size, 4, 10, 50 + font_demo_state.current_size + 5)
+            else
+                lcd.drawGtfontUtf8(font_demo_state.current_size .. "号", 
+                                  font_demo_state.current_size, 10, 50)
+                lcd.drawGtfontUtf8("合宙", 
+                                  font_demo_state.current_size, 10, 50 + font_demo_state.current_size + 5)
+            end
+        else
+            font_demo_state.color_phase = 4
+            font_demo_state.current_size = 129
+        end
+    else
+        if font_demo_state.current_size <= 192 then
+            -- 矢量字体大小目前不能到180号,常规字体可到192号
+            -- if font_demo_state.use_gray then
+            --     lcd.drawGtfontUtf8Gray(font_demo_state.current_size .. "号", 
+            --                           font_demo_state.current_size, 4, 10, 50)
+            --     lcd.drawGtfontUtf8Gray("合宙", 
+            --                           font_demo_state.current_size, 4, 10, 50 + font_demo_state.current_size + 5)
+            -- else
+                lcd.drawGtfontUtf8(font_demo_state.current_size .. "号", 
+                                  font_demo_state.current_size, 10, 50)
+                lcd.drawGtfontUtf8("合宙", 
+                                  font_demo_state.current_size, 10, 50 + font_demo_state.current_size + 5)
+            -- end
+        else
+            -- 所有阶段完成,重置
+            font_demo_state.demo_phase = 1
+            font_demo_state.countdown = 5
+            font_demo_state.color_phase = 1
+            font_demo_state.current_size = 16
+            return true
+        end
+    end
+    
+    return false
+end
+
+--[[
+绘制字体演示内容;
+根据演示阶段调用不同的绘制函数;
+
+@api draw_font_demo()
+@summary 绘制字体演示内容
+@return nil
+
+@usage
+-- 在draw函数中调用
+draw_font_demo()
+]]
+local function draw_font_demo()
+    -- 根据演示阶段调用不同的绘制函数
+    if font_demo_state.demo_phase == 1 then
+        draw_countdown_phase()
+    else
+        draw_font_size_phase()
+    end
+end
+
+--[[
+绘制GTFont演示页面;
+绘制GTFont演示页面的所有UI元素和字体演示内容;
+
+@api gtfont_page.draw()
+@summary 绘制GTFont演示页面的所有UI元素和字体演示内容
+@return nil
+
+@usage
+-- 在UI主循环中调用
+gtfont_page.draw()
+]]
+function gtfont_page.draw()
+    lcd.clear()
+    -- 绘制按钮区域
+    draw_back_button()
+    draw_switch_button()
+
+    -- 绘制字体演示内容
+    draw_font_demo()
+
+    lcd.flush()
+end
+
+--[[
+处理按键事件;
+根据按键类型执行相应的操作;
+
+@api gtfont_page.handle_key(key_type, switch_page)
+@summary 处理GTFont页面按键事件
+@string key_type 按键类型
+@valid_values "boot_up", "pwr_up"
+@function switch_page 页面切换回调函数
+@return bool 事件处理成功返回true,否则返回false
+
+@usage
+-- 在UI主循环中调用
+local handled = gtfont_page.handle_key("boot_up", switch_page)
+]]
+function gtfont_page.handle_key(key_type, switch_page)
+    log.info("gtfont_page.handle_key", "key_type:", key_type)
+    
+    if key_type == "boot_up" then
+        -- BOOT键:切换灰度模式
+        font_demo_state.use_gray = not font_demo_state.use_gray
+        log.info("gtfont_page", "切换灰度模式:", font_demo_state.use_gray)
+        return true
+    elseif key_type == "pwr_up" then
+        -- PWR键:返回首页
+        switch_page("home")
+        return true
+    end
+    return false
+end
+
+--[[
+页面进入时重置状态;
+重置字体演示状态到初始值;
+
+@api gtfont_page.on_enter()
+@summary 重置字体演示状态到初始值
+@return nil
+
+@usage
+-- 在页面切换时调用
+gtfont_page.on_enter()
+]]
+function gtfont_page.on_enter()
+    font_demo_state.use_gray = true -- 默认灰度模式
+    font_demo_state.demo_phase = 1
+    font_demo_state.countdown = 18
+    font_demo_state.current_size = 16
+    font_demo_state.color_phase = 1
+    local _, ms_l = mcu.ticks2(1)
+    font_demo_state.last_update = ms_l
+    frame_time = 20 -- 进入时改成20ms刷新一次
+end
+
+--[[
+页面离开时恢复系统刷新率;
+恢复刷新率到默认值;
+
+@api gtfont_page.on_leave()
+@summary 恢复刷新率
+@return nil
+
+@usage
+-- 在页面切换时调用
+gtfont_page.on_leave()
+]]
+function gtfont_page.on_leave()
+    -- 恢复恢复20S刷新一次
+    frame_time = 20*1000
+end
+
+return gtfont_page

+ 179 - 0
module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirLCD_1000/lcd/ui/home_page.lua

@@ -0,0 +1,179 @@
+--[[
+@module  home_page
+@summary 主页模块,提供应用入口和导航功能
+@version 1.0
+@date    2025.12.3
+@author  江访
+@usage
+本模块为主页模块,主要功能包括:
+1、提供应用入口和导航功能;
+2、显示系统标题和操作提示;
+3、管理三个功能按钮的选中状态;
+4、处理主页面的按键事件;
+
+对外接口:
+1、home_page.draw():绘制主页面所有UI元素,包括选中指示
+2、home_page.handle_key():处理主页面按键事件
+3、home_page.on_enter():页面进入时重置选中状态
+4、home_page.on_leave():页面离开时执行清理操作
+]]
+
+local home_page = {}
+
+
+-- 按钮区域定义
+local buttons = {
+    {name = "lcd", text = "lcd核心库演示", x1 = 10, y1 = 350, x2 = 100, y2 = 420, color = 0x001F},
+    {name = "gtfont", text = "矢量字体芯片", x1 = 115, y1 = 350, x2 = 205, y2 = 420, color = 0xF800},
+    {name = "customer_font", text = "自定义字体", x1 = 220, y1 = 350, x2 = 310, y2 = 420, color = 0x07E0}
+}
+
+-- 当前选中项索引
+local selected_index = 1
+
+local title = "合宙lcd演示系统"
+local content1 = "本页面使用的是系统内置的12号中文点阵字体"
+local hint = "boot键:选择 pwr键:确认"
+
+--[[
+绘制光标指示
+@local
+@return nil
+]]
+local function draw_cursor()
+    local btn = buttons[selected_index]
+    
+    -- 在选中按钮周围绘制矩形光标
+    lcd.drawRectangle(btn.x1 - 2, btn.y1 - 2, btn.x2 + 2, btn.y2 + 2, 0x3186)  -- 蓝色外框
+    lcd.drawRectangle(btn.x1 - 1, btn.y1 - 1, btn.x2 + 1, btn.y2 + 1, 0x0000)  -- 黑色内框
+end
+
+--[[
+绘制主页界面;
+绘制主页面所有UI元素,包括选中指示;
+
+@api home_page.draw()
+@summary 绘制主页面所有UI元素,包括选中指示
+@return nil
+
+@usage
+-- 在UI主循环中调用
+home_page.draw()
+]]
+function home_page.draw()
+    lcd.clear()
+    lcd.setColor(0xFFFF, 0x0000)
+    lcd.setFont(lcd.font_opposansm12_chinese)
+
+    -- 显示标题
+    lcd.drawStr(106, 30, title, 0x0000)
+
+    -- 显示说明文字
+    lcd.drawStr(46, 48, content1, 0x0000)
+    lcd.drawStr(86, 66, hint, 0x0000)
+
+    -- 绘制所有按钮
+    for i, btn in ipairs(buttons) do
+        local color = btn.color
+        if i == selected_index then
+            -- 选中状态:颜色稍微变亮
+            color = color + 0x0842
+        end
+        
+        lcd.fill(btn.x1, btn.y1, btn.x2, btn.y2, color)
+        
+        -- 根据按钮调整文字位置
+        if btn.name == "lcd" then
+            lcd.drawStr(btn.x1 + 5, btn.y1 + 30, "lcd核心库演示", 0xFFFF)
+        elseif btn.name == "gtfont" then
+            lcd.drawStr(btn.x1 + 28, btn.y1 + 20, "外部", 0xFFFF)
+            lcd.drawStr(btn.x1 + 4, btn.y1 + 40, "矢量字体芯片", 0xFFFF)
+        elseif btn.name == "customer_font" then
+            lcd.drawStr(btn.x1 + 15, btn.y1 + 30, "自定义字体", 0xFFFF)
+        end
+    end
+
+    -- 绘制光标指示
+    draw_cursor()
+end
+
+--[[
+处理主页按键事件;
+根据按键类型执行相应的操作;
+
+@api home_page.handle_key(key_type, switch_page)
+@summary 处理主页按键事件
+@string key_type 按键类型
+@valid_values "confirm", "next", "prev", "back"
+@function switch_page 页面切换回调函数
+@return bool 事件处理成功返回true,否则返回false
+
+@usage
+-- 在UI主循环中调用
+local handled = home_page.handle_key("next", switch_page)
+]]
+function home_page.handle_key(key_type, switch_page)
+    log.info("home_page.handle_key", "key_type:", key_type, "selected_index:", selected_index)
+    
+    if key_type == "confirm" then
+        -- 确认键:切换到选中的页面
+        local btn = buttons[selected_index]
+        switch_page(btn.name)
+        return true
+    elseif key_type == "right" or key_type == "next" then
+        -- 向右/下一个
+        selected_index = selected_index % #buttons + 1
+        return true
+    elseif key_type == "left" or key_type == "prev" then
+        -- 向左/上一个
+        selected_index = (selected_index - 2) % #buttons + 1
+        return true
+    elseif key_type == "back" then
+        -- 返回键:可以执行其他操作或提示
+        return false
+    end
+    return false
+end
+
+--[[
+页面进入时重置选中状态;
+重置选中状态为第一个按钮;
+
+@api home_page.on_enter()
+@summary 重置选中状态
+@return nil
+
+@usage
+-- 在页面切换时调用
+home_page.on_enter()
+]]
+function home_page.on_enter()
+    selected_index = 1  -- 默认选中第一个
+end
+
+--[[
+获取当前选中项信息;
+用于调试或状态查询;
+
+@api home_page.get_selected_info()
+@summary 获取当前选中项信息
+@return table 包含选中项信息的表
+@field index number 当前选中项的索引
+@field name string 当前选中项的名称
+@field text string 当前选中项的显示文本
+
+@usage
+-- 获取当前选中项信息
+local info = home_page.get_selected_info()
+log.info("当前选中", info.name, info.text)
+]]
+function home_page.get_selected_info()
+    local btn = buttons[selected_index]
+    return {
+        index = selected_index,
+        name = btn.name,
+        text = btn.text
+    }
+end
+
+return home_page

+ 177 - 0
module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirLCD_1000/lcd/ui/lcd_page.lua

@@ -0,0 +1,177 @@
+--[[
+@module  lcd_page
+@summary LCD图形绘制演示模块
+@version 1.0
+@date    2025.12.3
+@author  江访
+@usage
+本模块为LCD图形绘制演示功能模块,主要功能包括:
+1、展示LCD核心库的基本图形绘制功能;
+2、演示点、线、矩形、圆形等基本图形绘制;
+3、显示图片和二维码生成功能;
+4、提供颜色示例展示;
+
+按键功能:
+- PWR键:返回主页
+- BOOT键:无功能
+
+对外接口:
+1、lcd_page.draw():绘制LCD演示页面
+2、lcd_page.handle_key():处理LCD页面按键事件
+]]
+
+local lcd_page = {}
+
+-- 按钮区域定义
+local back_button = { x1 = 10, y1 = 10, x2 = 80, y2 = 50 }
+
+--[[
+绘制LCD演示页面;
+绘制LCD演示页面的所有图形和UI元素;
+
+@api lcd_page.draw()
+@summary 绘制LCD演示页面的所有图形和UI元素
+@return nil
+
+@usage
+-- 在UI主循环中调用
+lcd_page.draw()
+]]
+function lcd_page.draw()
+    lcd.clear()
+    lcd.setFont(lcd.font_opposansm12_chinese)
+
+    -- 绘制返回按钮
+    lcd.fill(back_button.x1, back_button.y1, back_button.x2, back_button.y2, 0xC618)
+    lcd.setColor(0x07E0, 0x0000)
+    lcd.drawStr(35, 35, "返回", 0x0000)
+    -- 设置默认颜色
+    lcd.setColor(0xFFFF, 0x0000)
+
+    -- 显示标题
+    lcd.drawStr(120, 33, "LCD核心库演示", 0x0000)
+    lcd.drawLine(20, 55, 300, 55, 0x8410)
+
+    -- === 第一区域:基本图形 ===
+    lcd.drawStr(20, 75, "基本图形绘制:", 0x0000)
+
+    -- 绘制点
+    lcd.drawStr(30, 98, "点:", 0x0000)
+    lcd.drawPoint(55, 98, 0xFCC0)
+    lcd.drawPoint(65, 98, 0x07E0)
+    lcd.drawPoint(75, 98, 0x001F)
+
+    -- 绘制线
+    lcd.drawStr(30, 125, "线:", 0x0000)
+    lcd.drawLine(55, 113, 115, 113, 0xFCC0)
+    lcd.drawLine(55, 118, 115, 123, 0x07E0)
+    lcd.drawLine(55, 123, 115, 118, 0x001F)
+
+    -- 绘制矩形(预留右侧空间)
+    lcd.drawStr(30, 160, "矩形:", 0x0000)
+    lcd.drawRectangle(65, 138, 105, 163, 0x7296)
+    lcd.fill(120, 138, 160, 163, 0x07E0)
+
+    -- 绘制圆(预留右侧空间)
+    lcd.drawStr(30, 200, "圆形:", 0x0000)
+    lcd.drawCircle(90, 193, 15, 0x001F)
+    lcd.drawCircle(130, 193, 15, 0xFCC0)
+
+    lcd.drawLine(170, 70, 170, 300, 0x8410) -- 垂直分隔线
+
+    -- === 第二区域:图片和二维码 ===
+    lcd.drawStr(180, 75, "图片/二维码:", 0x0000)
+
+    -- 图片显示区域 (80x80)
+    lcd.drawStr(180, 100, "LOGO:", 0x0000)
+    lcd.drawRectangle(180, 110, 270, 200, 0x0000) 
+    lcd.showImage(185, 115, "/luadb/logo.jpg")  
+
+    -- 二维码区域 (80x80)
+    lcd.drawStr(180, 215, "二维码:", 0x0000)
+    lcd.drawRectangle(180, 225, 270, 308, 0x0000)
+    lcd.drawQrcode(185, 226, "https://docs.openluat.com/air8000/", 80)
+
+    lcd.drawLine(20, 325, 300, 325, 0x8410)
+
+    -- === 第三区域:位图 ===
+    lcd.drawLine(20, 235, 160, 235, 0x8410)
+
+    -- 位图区域
+    lcd.setFont(lcd.font_opposansm12_chinese)
+    lcd.drawStr(20, 255, "位图示例:", 0x0000)
+
+    -- 绘制位图
+    local x_start = 30
+    local y_start = 265
+    lcd.drawXbm(x_start, y_start, 16, 16, string.char(
+        0x00, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x3F, 0x80, 0x00,
+        0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0xFE, 0x7F, 0x00, 0x00))
+
+    lcd.drawXbm(x_start + 20, y_start, 16, 16, string.char(
+        0x00, 0x00, 0x80, 0x00, 0xC4, 0x7F, 0x28, 0x00, 0x10, 0x00, 0xD0, 0x3F, 0x42, 0x20, 0x44, 0x22,
+        0x40, 0x24, 0xF0, 0xFF, 0x24, 0x20, 0x24, 0x22, 0x24, 0x20, 0xE2, 0x7F, 0x02, 0x20, 0x02, 0x1E))
+
+    lcd.drawXbm(x_start, y_start + 20, 16, 16, string.char(
+        0x00, 0x00, 0x00, 0x01, 0x80, 0x01, 0x40, 0x02, 0x20, 0x04, 0x18, 0x18, 0xF4, 0x6F, 0x02, 0x00,
+        0x00, 0x00, 0xF8, 0x1F, 0x08, 0x10, 0x08, 0x10, 0x08, 0x10, 0x08, 0x10, 0xF8, 0x1F, 0x08, 0x10))
+
+    lcd.drawXbm(x_start + 20, y_start + 20, 16, 16, string.char(
+        0x00, 0x00, 0x80, 0x00, 0x00, 0x01, 0xFE, 0x7F, 0x02, 0x40, 0x02, 0x40, 0x00, 0x01, 0xFC, 0x3F,
+        0x04, 0x21, 0x04, 0x21, 0xFC, 0x3F, 0x04, 0x21, 0x04, 0x21, 0x04, 0x21, 0xFC, 0x3F, 0x04, 0x20))
+
+    -- === 第四区域:字体 ===
+    lcd.drawStr(20, 345, "中英文字体示例:", 0x0000)
+
+    -- 中文字体
+    lcd.setFont(lcd.font_opposansm12_chinese)
+    lcd.drawStr(20, 368, "中文字体ABC123", 0x0000)
+
+    -- 英文字体
+    local font_y = 388
+    lcd.setFont(lcd.font_opposansm12)
+    lcd.drawStr(20, font_y, "12px: ABCabc123", 0x0000)
+    font_y = font_y + 18
+
+    lcd.setFont(lcd.font_opposansm16)
+    lcd.drawStr(20, font_y, "16px: ABCabc123", 0x0000)
+    font_y = font_y + 22
+
+    lcd.setFont(lcd.font_opposansm20)
+    lcd.drawStr(20, font_y, "20px: ABCabc123", 0x0000)
+    font_y = font_y + 26
+
+    lcd.setFont(lcd.font_opposansm24)
+    lcd.drawStr(20, font_y, "24px: ABCabc123", 0x0000)
+
+    lcd.drawLine(20, 470, 300, 470, 0x8410)
+end
+
+--[[
+处理按键事件;
+根据按键类型执行相应的操作;
+
+@api lcd_page.handle_key(key_type, switch_page)
+@summary 处理LCD页面按键事件
+@string key_type 按键类型
+@valid_values "pwr_up"
+@function switch_page 页面切换回调函数
+@return bool 事件处理成功返回true,否则返回false
+
+@usage
+-- 在UI主循环中调用
+local handled = lcd_page.handle_key("pwr_up", switch_page)
+]]
+function lcd_page.handle_key(key_type, switch_page)
+    log.info("lcd_page.handle_key", "key_type:", key_type)
+    
+    if key_type == "pwr_up" then
+        -- PWR键:返回首页
+        switch_page("home")
+        return true
+    end
+    -- BOOT键无功能
+    return false
+end
+
+return lcd_page

+ 186 - 0
module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirLCD_1000/lcd/ui/ui_main.lua

@@ -0,0 +1,186 @@
+--[[
+@module  ui_main
+@summary 用户界面主控模块,负责页面管理和事件分发
+@version 1.0
+@date    2025.12.3
+@author  江访
+本模块为用户界面主控模块,主要功能包括:
+1、管理页面切换和事件分发;
+2、处理按键事件并调用对应页面的处理函数;
+3、协调各页面之间的状态转移;
+4、控制界面刷新频率;
+
+对外接口:
+1、ui_main():用户界面主任务,初始化显示和按键驱动,启动UI主循环
+]]
+
+-- 加载子页面
+local home_page = require "home_page"
+local lcd_page = require "lcd_page"
+local gtfont_page = require "gtfont_page"
+local customer_font_page = require "customer_font_page"
+
+-- 当前页面状态
+local current_page = "home"
+local last_page = ""
+frame_time = 20 * 1000
+
+--[[
+切换页面;
+从当前页面切换到目标页面;
+
+@api switch_page(new_page)
+@summary 执行页面切换操作
+@string new_page 目标页面名称
+@valid_values "home", "lcd", "gtfont", "customer_font"
+@return nil
+
+@usage
+-- 切换到主页
+switch_page("home")
+
+-- 切换到LCD演示页面
+switch_page("lcd")
+]]
+local function switch_page(new_page)
+    log.info("switch_page", "从", current_page, "切换到", new_page)
+    
+    -- 调用旧页面的退出函数(如果存在)
+    if current_page == "home" and home_page.on_leave then
+        home_page.on_leave()
+    elseif current_page == "lcd" and lcd_page.on_leave then
+        lcd_page.on_leave()
+    elseif current_page == "gtfont" and gtfont_page.on_leave then
+        gtfont_page.on_leave()
+    elseif current_page == "customer_font" and customer_font_page.on_leave then
+        customer_font_page.on_leave()
+    end
+
+    last_page = current_page
+    current_page = new_page
+
+    -- 调用新页面的进入函数(如果存在)
+    if new_page == "home" and home_page.on_enter then
+        home_page.on_enter()
+    elseif new_page == "lcd" and lcd_page.on_enter then
+        lcd_page.on_enter()
+    elseif new_page == "gtfont" and gtfont_page.on_enter then
+        gtfont_page.on_enter()
+    elseif new_page == "customer_font" and customer_font_page.on_enter then
+        customer_font_page.on_enter()
+    end
+
+    log.info("ui_main", "已切换到页面:", current_page)
+end
+
+--[[
+处理按键事件;
+根据按键类型和当前页面调用对应的处理函数;
+
+@api handle_key_event(key_event)
+@summary 处理按键事件并分发到对应页面
+@string key_event 按键事件类型
+@valid_values "boot_up", "pwr_up"
+@return bool 事件处理成功返回true,否则返回false
+
+@usage
+-- 在ui_main任务中调用
+local handled = handle_key_event("boot_up")
+]]
+local function handle_key_event(key_event)
+    log.info("按键事件", "event:", key_event, "当前页面:", current_page)
+    
+    -- 只在按键释放时处理(防止重复触发)
+    if key_event == "boot_up" then
+        -- BOOT键:在主页作为方向键,在gtfont页面切换模式
+        if current_page == "home" then
+            -- 主页:向右移动光标
+            return home_page.handle_key("next", switch_page)
+        elseif current_page == "gtfont" then
+            -- GTFont页面:切换灰度模式
+            if gtfont_page.handle_key then
+                return gtfont_page.handle_key("boot_up", switch_page)
+            end
+        end
+        -- 其他页面BOOT键无功能
+        return false
+    elseif key_event == "pwr_up" then
+        -- PWR键:确认/返回功能
+        if current_page == "home" then
+            return home_page.handle_key("confirm", switch_page)
+        elseif current_page == "lcd" then
+            -- LCD页面:返回首页
+            if lcd_page.handle_key then
+                return lcd_page.handle_key("pwr_up", switch_page)
+            end
+        elseif current_page == "gtfont" then
+            -- GTFont页面:返回首页
+            if gtfont_page.handle_key then
+                return gtfont_page.handle_key("pwr_up", switch_page)
+            end
+        elseif current_page == "customer_font" then
+            -- 自定义字体页面:返回首页
+            if customer_font_page.handle_key then
+                return customer_font_page.handle_key("pwr_up", switch_page)
+            end
+        end
+    end
+    
+    return false
+end
+
+--[[
+用户界面主任务;
+初始化显示和按键驱动,启动UI主循环;
+
+@api ui_main()
+@summary 初始化显示和按键驱动,启动UI主循环
+@return nil
+
+@usage
+-- 在主程序中通过sys.taskInit调用
+sys.taskInit(ui_main)
+]]
+local function ui_main()
+    if not lcd_drv.init() then
+        log.error("ui_main", "显示初始化失败")
+        return
+    end
+
+    -- 初始化按键驱动
+    if not key_drv.init() then
+        log.error("ui_main", "按键驱动初始化失败")
+        return
+    end
+
+    -- 默认使用系统自带的12号中文字体
+    lcd.setFont(lcd.font_opposansm12_chinese)
+
+    -- 调用主页的进入函数
+    if home_page.on_enter then
+        home_page.on_enter()
+    end
+
+    while true do
+        -- 根据当前页面绘制内容
+        if current_page == "home" then
+            home_page.draw()
+        elseif current_page == "lcd" then
+            lcd_page.draw()
+        elseif current_page == "gtfont" then
+            gtfont_page.draw()
+        elseif current_page == "customer_font" then
+            customer_font_page.draw()
+        end
+
+        lcd.flush()
+
+        -- 等待按键事件
+        local result, key_event = sys.waitUntil("KEY_EVENT", frame_time)
+        if result then
+            handle_key_event(key_event)
+        end
+    end
+end
+
+sys.taskInit(ui_main)

binární
module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirLCD_1000/logo.jpg


+ 0 - 207
module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirLCD_1000/readme.md

@@ -1,207 +0,0 @@
-# AirLCD_1000_Air780EHM 演示 demo
-
-
-## 一、功能模块介绍
-
-1. main.lua:主程序入口;
-
-2. ui_main.lua:UI应用模块,负责UI启动初始和UI主循环
-
-3. AirLCD_1000.lua:显示驱动模块,负责执行AirLCD_1000初始化,背光亮度调节和屏幕休眠和唤醒
-
-## 二、演示功能概述
-
-1. 屏幕接线引脚配置在AirLCD_1000.lua内进行设置
-
-2. 设置好后,启动主循环屏幕初始化程序,点亮屏幕
-
-3. 进入UI主task,循环显示显示图片、字符、色块等内容,并通过接口设置背光亮度和对屏幕进行休眠和唤醒。
-
-## 三、演示硬件环境
-
-### 3.1 硬件准备
-
-1. AirLCD_1000 显示屏*1
-
-2. Air780EHM或EGH或EHV 核心板*1
-
-3. 母对母杜邦线*8
-
-4. TYPE-C 数据线*1
-
-5. 按照硬件接线图正确连接 LCD 显示屏
-
-6. 将核心板正面开关打到 ON 位置
-
-7. 本demo使用的背光引脚,既是GPIO_1也是PWM_0引脚
-
-8. 调节屏幕亮度需要确保背光控制引脚(如 demo 中 GPIO1)支持 PWM 功能
-
-9. 背光引脚通过切换到PWM模式,通过调节占空比实现背光亮度调节
-
-10. 背光引脚通过切换到GPIO模式,再调用休眠/唤醒接口使屏幕进入休眠/唤醒
-
-### 3.2接线说明
-
-<table>
-<tr>
-<td>引脚说明<br/></td><td>核心开发板<br/></td><td>屏幕<br/></td><td>引脚说明<br/></td></tr>
-<tr>
-<td>屏幕供电<br/></td><td>VDD-EXT<br/><br/></td><td>VCC<br/><br/></td><td>显示屏电源供电脚<br/><br/></td></tr>
-<tr>
-<td>电源地<br/></td><td>GND<br/></td><td>GND<br/></td><td>电源地<br/></td></tr>
-<tr>
-<td>LCD SPI 串口的时钟信号<br/></td><td>LCD_CLK<br/></td><td>CLK<br/></td><td>SPI 串口的时钟信号<br/></td></tr>
-<tr>
-<td>LCD SPI 串口的数据脚<br/></td><td>LCD_SDA<br/></td><td>MOS<br/></td><td>SPI 串口的数据输入脚<br/></td></tr>
-<tr>
-<td>LCD 复位脚<br/></td><td>LCD_RST<br/></td><td>RES<br/></td><td>显示屏复位脚<br/></td></tr>
-<tr>
-<td>LCD 数据/寄存器控制脚<br/></td><td>LCD_RS<br/><br/></td><td>DC<br/></td><td>4 线 SPI 串口的显示数据/寄存器指令<br/></td></tr>
-<tr>
-<td>LCD SPI片选,同一个SPI接口上有多个设备才使用<br/></td><td>LCD_CS<br/></td><td>CS<br/></td><td>LCD驱动芯片片选脚<br/></td></tr>
-<tr>
-<td>LCD 背光控制引脚<br/></td><td>GPIO_1/PWM0<br/></td><td>BKL<br/></td><td>背光使能引脚<br/></td></tr>
-</table>
-
-![](https://docs.openLuat.com/cdn/image/Air780EHM_AirLCD_1000.jpg)
-
-注意:模组GPIO供电能力弱,所以使用VDD-EXT供电。
-
-
-## 四、演示软件环境
-
-### 4.1 底层固件准备
-
-固件版本,推荐使用 V2015 及以后最新固件版本:
-
-  [Air780EHM固件下载链接](https://docs.openluat.com/air780epm/luatos/firmware/version/)
-
-  [Air780EHV固件下载链接](https://docs.openluat.com/air780ehv/luatos/firmware/version/)
-
-  [Air780EGH固件下载链接](https://docs.openluat.com/air780egh/luatos/firmware/version/)
-
-
-
-### 4.2Luatools下载调试工具
-[demo下载调试工具:Luatools](https://docs.openluat.com/air780egh/luatos/common/download/?h=luatools#33-luatools)
-
-### 4.2演示emo下载
-下载本 demo中除readme外的所有的代码文件以及图片文件
-
-## 五、演示核心步骤
-
-### 5.1 烧录程序
-
-1. 下载 luatools 烧录工具:[luatools下载链接及使用说明](https://docs.openluat.com/air780epm/common/Luatools/)
-
-2. 按照操作说明,将本 demo 内除 readme.md 外的全部文件通过 luatools 下载到模块内**(按住 BOOT 键开机,选择下载底层和脚本)**
-
-3. 如需显示图片,如 demo 中显示的 logo,确保 `logo.jpg` 图片文件存在,下载时会放在模组 `/luadb/` 目录下
-   ![](https://docs.openluat.com/air8000/luatos/app/AirLCD_1000%E6%BC%94%E7%A4%BAdemo/imges/UdWZbZ0tXoYPK5xG2Kbcx2y7nSc.png)
-
-
-### 5.2、演示 demo 效果
-
-1. 通过 demo 了解屏幕连接、配置、初始化过程,为显示做好准备
-
-2. 通过 demo 将图片、12 号中文、英文、点、线、圆形、矩形、颜色填充、二维码显示在屏幕上
-
-3. 通过 demo 了解如何使用接口对屏幕进行背光亮度调节、屏幕休眠、屏幕唤醒
-
-4. 实现效果图
-
-![](https://docs.openLuat.com/cdn/image/AirLCD_1000.jpg)
-
-![](https://docs.openluat.com/air8000/luatos/app/AirLCD_1000%E6%BC%94%E7%A4%BAdemo/imges/BaH5b9WnKomL4SxNJxvc5rZKnIg.png)
-
-
-
-
-## 六、程序对外接口
-
-### 6.1 AirLCD_1000 模块接口
-
-#### `AirLCD_1000.lcd_init()`
-
-- 功能:初始化 AirLCD_1000LCD 显示屏
-- 参数:
-
-  - LCD_MODEL: LCD 型号字符串(如"AirLCD_1000")
-  - lcd_pin_dc: 数据/命令引脚号
-  - lcd_pin_rst: 复位引脚号
-  - lcd_pin_pwr: 背光控制引脚号
-- 返回值:初始化成功状态(true/false), 屏幕宽度, 屏幕高度
-
-#### `AirLCD_1000.set_backlight(level)`
-
-- 功能:设置背光亮度
-- 参数:level - 亮度级别(0-100)
-- 返回值:设置成功状态(true/false)
-
-#### `AirLCD_1000.set_sleep(sleep)`
-
-- 功能:设置屏幕休眠或唤醒
-- 参数:sleep - true 进入休眠, false 唤醒
-- 返回值:设置成功状态(true/false)
-- **注意事项:目前在屏幕休眠模式下,仅 AIR780EPM 支持模组设置 pm.power(pm.WORK_MODE, 1),其他型号暂时不支持,唤醒模组会导致模组死机**
-
-### 6.2 screen_data_table 配置参数
-
-- `lcd_models`: 支持的 LCD 型号及其参数
-- `default`: 默认配置(滑动阈值、点击时间阈值等)预留后续扩展功能使用
-
-### 6.3 更多显示接口
-
-- 可以参考合宙 docs 上 LCD 库进行扩展使用:[https://docs.openluat.com/osapi/core/lcd/](https://docs.openluat.com/osapi/core/lcd/)
-
-## 七、扩展说明
-
-1. 目前支持 ST7796、ST7789、CO5300 显示芯片,其他 st7735 、st7735v、st7735s、gc9a01、gc9106l、gc9306x、ili9486 也同样支持,
-
-2. 模组字体支持有限,需要使用其他字体可以搭配合宙 AirFONTS_1000 矢量字库使用
-
-3. Air780EPM/EHM/EGH/EHV、Air8000 系列、Air8101,按正确的接口接线并修改 screen_data_table.lua 都可以使用此 demo
-
-4. 其他屏幕可以参考 custom 的方式自定义屏幕初始化配置
-
-```lua
--- 配置接口参数
-local lcd_param = {
-        port = lcd.HWID_0,        -- 使用的spi id 号
-        pin_dc = 0xff,            -- 命令选择引脚
-        pin_rst = 36,             -- 复位引脚  
-        direction = 0,            -- 屏幕方向
-        w = width,                -- 屏幕宽度
-        h = height,               -- 屏幕高度
-        xoffset = 0,              -- X轴偏移像素
-        yoffset = 0,              -- Y轴偏移像素
-        sleepcmd = 0x10,          -- LCD睡眠命令
-        wakecmd = 0x11,           -- LCD唤醒命令
-    }
-    
-  -- 初始化SPI设备
-        spi.deviceSetup(
-            lcd.HWID_0,  -- LCD端口号
-            nil,         -- CS片选脚,可选
-            0,           -- CPHA=0
-            0,           -- CPOL=0
-            8,           -- 8位数据宽度
-            20000000,    -- 20MHz波特率
-            spi.MSB,     -- 高位先传
-            1,           -- 主机模式
-            1            -- 全双工模式
-        )
-        
-    -- QSPI如有特殊配置,需要在lcd.init前配置好
-    --lcd.qspi(0x02, 0x32, 0x12)
-        
-    -- 初始化屏幕 
-    lcd.init("custom", lcd_param)
-    
-    -- 如有其他参数需要配置,可使用lcd.cmd命令
-    --lcd.cmd(0x53, 0x20)
-        
-    -- 自定义初始化后必须运行该程序
-    lcd.user_done()
-```

+ 0 - 187
module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirLCD_1000/ui_main.lua

@@ -1,187 +0,0 @@
---[[
-@module  ui_main
-@summary UI子模块的主程序
-@version 1.0
-@date    2025.09.04
-@author  江访
-@usage
-
-本demo演示的核心功能为:
-1、依据显示屏配置参数初始化显示屏,点亮AirLCD_1000屏幕
-2、循环显示显示图片、字符、色块等内容
-3、通过接口设置背光亮度和对屏幕进行休眠和唤醒。
-4、本demo使用的背光引脚,既是GPIO_1也是PWM_0引脚
-5、背光引脚通过切换到PWM模式,通过调节占空比实现背光亮度调节
-6、背光引脚通过切换到GPIO模式,再调用休眠/唤醒接口使屏幕进入休眠/唤醒
-]]
-
-
--- 加载AirLCD_1000驱动模块
-local AirLCD_1000 = require "AirLCD_1000"           -- 显示初始化执行程序
-
--- 配置AirLCD_1000接线引脚
-local LCD_MODEL = "AirLCD_1000" -- 显示屏型号
-local lcd_vcc = 29              -- 屏幕供电引脚GPIO号,VDD-EXT供电可填255
-local lcd_pin_rst = 36          -- 复位引脚GPIO号
-local lcd_pin_pwr = 1           -- 背光引脚GPIO号
-local lcd_pwm_id = 0            -- 背光引脚PWM端口号
-
--- UI主task,所有UI相关的代码都会通过该task进行调度
-local function ui_main()
-
-    AirLCD_1000.lcd_init(LCD_MODEL, lcd_vcc, lcd_pin_rst, lcd_pin_pwr,lcd_pwm_id)
-
-    -- 设置字体为模组自带的opposansm12中文字体
-    lcd.setFont(lcd.font_opposansm12_chinese)
-
-    -- 清除屏幕显示
-    -- lcd.clear()
-
-    -- 主循环
-    while true do
-        -- 获取并打印屏幕尺寸信息,实际是初始化传入的信息
-        -- log.info("屏幕尺寸", lcd.getSize())
-        
-        ------------------------------------------以下为图片/位图/英文显示设置------------------------------------------
-        --设置前景色和背景色(RGB565格式)
-        --需要放在循环内部,因为是一次性设置
-        lcd.setColor(0xFFFF, 0x0000)  -- 白底黑字,背景色:白色(0xFFFF), 前景色:黑色(0x0000),
-
-        -- 在屏幕左上角(0,0)显示一张图片
-        -- 图片路径为/luadb/logo.jpg
-        lcd.showImage(0, 0, "/luadb/logo.jpg")
-
-        -- 在位置(0,82)绘制一个16x16的位图,内容依次为“上”,“海”,“合”,“宙”
-        -- 位图数据使用字符串格式表示
-        lcd.drawXbm(0, 82, 16, 16, string.char(
-        0x00,0x00,0x80,0x00,0x80,0x00,0x80,0x00,0x80,0x00,0x80,0x00,0x80,0x3F,0x80,0x00,
-        0x80,0x00,0x80,0x00,0x80,0x00,0x80,0x00,0x80,0x00,0x80,0x00,0xFE,0x7F,0x00,0x00))
-
-        lcd.drawXbm(18, 82, 16, 16, string.char(
-        0x00,0x00,0x80,0x00,0xC4,0x7F,0x28,0x00,0x10,0x00,0xD0,0x3F,0x42,0x20,0x44,0x22,
-        0x40,0x24,0xF0,0xFF,0x24,0x20,0x24,0x22,0x24,0x20,0xE2,0x7F,0x02,0x20,0x02,0x1E))
-
-        lcd.drawXbm(36, 82, 16, 16, string.char(
-        0x00,0x00,0x00,0x01,0x80,0x01,0x40,0x02,0x20,0x04,0x18,0x18,0xF4,0x6F,0x02,0x00,
-        0x00,0x00,0xF8,0x1F,0x08,0x10,0x08,0x10,0x08,0x10,0x08,0x10,0xF8,0x1F,0x08,0x10))
-
-        lcd.drawXbm(54, 82, 16, 16, string.char(
-        0x00,0x00,0x80,0x00,0x00,0x01,0xFE,0x7F,0x02,0x40,0x02,0x40,0x00,0x01,0xFC,0x3F,
-        0x04,0x21,0x04,0x21,0xFC,0x3F,0x04,0x21,0x04,0x21,0x04,0x21,0xFC,0x3F,0x04,0x20))
-
-        -- 在位置(120,40)绘制一个蓝色点
-        lcd.drawPoint(120, 40, 0x001F)
-
-        -- 以(120,40)为圆心,40为半径绘制一个蓝色圆
-        lcd.drawCircle(120, 40, 40, 0x001F)
-
-        -- 从(170,40)到(280,40)绘制一条蓝色水平线
-        lcd.drawLine(170, 40, 280, 40, 0x001F)
-
-        -- 从(170,50)到(280,80)绘制一个蓝色矩形框
-        lcd.drawRectangle(170, 50, 280, 80, 0x001F)
-
-
-        -- 在位置(200,170)绘制一个100x100的二维码,内容为指定URL
-        lcd.drawQrcode(200, 170, "https://docs.openluat.com/air8000/", 100)
-
-        lcd.setFont(lcd.font_opposansm12)
-        lcd.drawStr(20,172,"hello hezhou") --显示字符
-        lcd.setFont(lcd.font_opposansm16)
-        lcd.drawStr(20,189,"hello hezhou") --显示字符
-        lcd.setFont(lcd.font_opposansm18)
-        lcd.drawStr(20,210,"hello hezhou") --显示字符
-        lcd.setFont(lcd.font_opposansm20)
-        lcd.drawStr(20,233,"hello hezhou") --显示字符
-        lcd.setFont(lcd.font_opposansm22)
-        lcd.drawStr(20,258,"hello hezhou") --显示字符
-        lcd.setFont(lcd.font_opposansm24)
-        lcd.drawStr(20,285,"hello hezhou") --显示字符
-        lcd.setFont(lcd.font_opposansm32)
-        lcd.drawStr(20,316,"hello hezhou") --显示字符
-
-        lcd.fill(10, 380, 150, 460, 0xF800)  -- 绘制红色矩形区域(0xF800是红色)
-        lcd.fill(170, 380, 310, 460, 0x07E0)  -- 绘制绿色矩形区域(0x07E0是绿色)
-
-        -- 主动刷新数据到屏幕
-        lcd.flush()
-        sys.wait(5000)
-
-        --------------------------------------------以下为显示中文设置--------------------------------------------
-        -- Air780EPM不支持中文显示
-        -- Air780EHM/EGH/EHV/Air8000支持12号中文字体
-        -- 中文以左下角为坐标显示与位图左上角方式不同
-
-        -- 设置字体为opposansm12中文字体,从英文显示切换到中文前一定要设置
-        lcd.setFont(lcd.font_opposansm12_chinese)
-        lcd.setColor(0xFFFF, 0x0000)  -- 白底黑字,背景色:白色(0xFFFF), 前景色:黑色(0x0000),
-
-        -- 显示重拍按钮(左侧)
-        lcd.drawStr(70, 420, "重拍", 0xFFFF)  -- 在按钮上绘制白色文字"重拍"
-
-        -- 显示返回按钮(右侧)
-        lcd.drawStr(230, 420, "返回", 0xFFFF)  -- 在按钮上绘制白色文字"返回"
-
-        -- 在位置(160,155)绘制文本
-        -- 不设置字体颜色,默认会使用lcd.setColor所设置的字体颜色
-        lcd.drawStr(160, 155, "扫码进入Air8000资料站",0x0000)
-
-        -- 主动刷新数据到屏幕
-        lcd.flush()
-        sys.wait(5000)
-        --------------------------------------------以下为背光亮度设置--------------------------------------------
-        -- 背光引脚使用PWM模式控制,pwm_id正确配置后可以实现背光控制
-        -- 参数 level: 亮度级别(0-100)
-        AirLCD_1000.set_backlight(5)  -- 设置背光为5%
-        sys.wait(5000)
-        AirLCD_1000.set_backlight(10)  -- 设置背光为10%
-        sys.wait(5000)
-        AirLCD_1000.set_backlight(20)  -- 设置背光为20%
-        sys.wait(5000)
-        AirLCD_1000.set_backlight(30)  -- 设置背光为30%
-        sys.wait(5000)
-        AirLCD_1000.set_backlight(40)  -- 设置背光为40%
-        sys.wait(5000)
-        AirLCD_1000.set_backlight(50)  -- 设置背光为50%
-        sys.wait(5000)
-        AirLCD_1000.set_backlight(60)  -- 设置背光为60%
-        sys.wait(5000)
-        AirLCD_1000.set_backlight(70)  -- 设置背光为70%
-        sys.wait(5000)
-        AirLCD_1000.set_backlight(80)  -- 设置背光为80%
-        sys.wait(5000)
-        AirLCD_1000.set_backlight(90)  -- 设置背光为90%
-        sys.wait(5000)
-        AirLCD_1000.set_backlight(100)  -- 设置背光为100%
-
-        --------------------------------------------以下为屏幕休眠设置--------------------------------------------
-        -- 背光引脚使用GPIO模式控制,pin_pwr正确配置可以实现背光控制
-
-        -- 进入休眠,功耗13ma
-        AirLCD_1000.set_sleep(true)    -- 进入休眠状态,此时屏幕供电需要稳定,若不稳定,唤醒后需要重新初始化屏幕
-        pm.power(pm.WORK_MODE, 1)
-        sys.wait(10000)
-        pm.power(pm.WORK_MODE, 0)
-        AirLCD_1000.set_sleep(false)   -- 唤醒屏幕,自动恢复之前的背光设置
-
-        -- 主动刷新数据到屏幕
-        lcd.flush()
-        sys.wait(5000)
-        ----------------------------------------以下为屏幕关闭/打开背光设置----------------------------------------
-        -- --关闭LCD背光,功耗20ma
-        -- AirLCD_1000.lcd_off()
-        -- pm.power(pm.WORK_MODE, 1)
-        -- sys.wait(15000)
-        -- -- 唤醒
-        -- pm.power(pm.WORK_MODE, 0)
-        -- sys.wait(5000)
-        -- AirLCD_1000.lcd_on()
-
-        -- 主动刷新数据到屏幕
-        -- lcd.flush()
-        -- sys.wait(50)
-    end
-end
-
--- 创建UI主循环ui_main的task
-sys.taskInit(ui_main)

+ 1 - 1
module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirLCD_1010/exeasyui/hw_drv/hw_default_font_drv.lua

@@ -34,7 +34,7 @@ ui.hw_init({
         pin_pwr = 1,               -- 背光控制引脚GPIO ID号
         pin_pwm = 0,               -- 背光控制引脚PWM D号
         port = lcd.HWID_0,         -- 驱动端口
-        -- pin_dc = 0xFF,          -- lcd数据/命令选择引脚GPIO ID号,默认:nil
+        -- pin_dc = 0xFF,          -- lcd数据/命令选择引脚GPIO ID号,使用lcd 专用 SPI 接口 lcd.HWID_0不需要填此参数,使用通用SPI接口需要赋值
         direction = 0,             -- lcd屏幕方向 0:0° 1:90° 2:180° 3:270°,屏幕方向和分辨率保存一致
         w = 320,                   -- lcd 水平分辨率
         h = 480,                   -- lcd 竖直分辨率

+ 1 - 1
module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirLCD_1010/exeasyui/hw_drv/hw_gtfont_drv.lua

@@ -37,7 +37,7 @@ ui.hw_init({
         pin_pwr = 1,               -- 背光控制引脚GPIO ID号
         pin_pwm = 0,               -- 背光控制引脚PWM D号
         port = lcd.HWID_0,         -- 驱动端口
-        -- pin_dc = 0xFF,          -- lcd数据/命令选择引脚GPIO ID号,默认:nil
+        -- pin_dc = 0xFF,          -- lcd数据/命令选择引脚GPIO ID号,使用lcd 专用 SPI 接口 lcd.HWID_0不需要填此参数,使用通用SPI接口需要赋值
         direction = 0,             -- lcd屏幕方向 0:0° 1:90° 2:180° 3:270°,屏幕方向和分辨率保存一致
         w = 320,                   -- lcd 水平分辨率
         h = 480,                   -- lcd 竖直分辨率

+ 1 - 1
module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirLCD_1010/exeasyui/hw_drv/hw_hzfont_drv.lua

@@ -37,7 +37,7 @@ ui.hw_init({
         pin_pwr = 1,               -- 背光控制引脚GPIO ID号
         pin_pwm = 0,               -- 背光控制引脚PWM D号
         port = lcd.HWID_0,         -- 驱动端口
-        -- pin_dc = 0xFF,          -- lcd数据/命令选择引脚GPIO ID号,默认:nil
+        -- pin_dc = 0xFF,          -- lcd数据/命令选择引脚GPIO ID号,使用lcd 专用 SPI 接口 lcd.HWID_0不需要填此参数,使用通用SPI接口需要赋值
         direction = 0,             -- lcd屏幕方向 0:0° 1:90° 2:180° 3:270°,屏幕方向和分辨率保存一致
         w = 320,                   -- lcd 水平分辨率
         h = 480,                   -- lcd 竖直分辨率

+ 1 - 1
module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirLCD_1010/exeasyui/main.lua

@@ -30,7 +30,7 @@ VERSION:项目版本号,ascii string类型
 
 -- 定义项目名称和版本号
 PROJECT = "ui_demo" -- 项目名称
-VERSION = "1.0.0"   -- 版本号
+VERSION = "001.000.000"   -- 版本号
 
 -- 在日志中打印项目名和项目版本号
 log.info("ui_demo", PROJECT, VERSION)

+ 11 - 9
module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirLCD_1010/exeasyui/readme.md

@@ -42,13 +42,13 @@
 
 - Air780EHM/Air780EHV/Air780EGH 核心板 × 1
 - AirLCD_1010 触摸配件板 × 1
-- GTFont 矢量字库,使用的是 AirFONT_1000 配件板 × 1
+- GTFont 矢量字库,使用的是 AirFONTS_1000 配件板 × 1
 - 母对母杜邦线 × 17,杜邦线太长的话,会出现 spi 通信不稳定的现象;
 - TYPE-C 数据线 × 1
-- Air780EHM/Air780EHV/Air780EGH 核心板和 AirLCD_1010 配件板以及 AirFONT_1000 配件板的硬件接线方式为
+- Air780EHM/Air780EHV/Air780EGH 核心板和 AirLCD_1010 配件板以及 AirFONTS_1000 配件板的硬件接线方式为
 
-  - Air780EHM/Air780EHV/Air780EGH 核心板通过 TYPE-C USB 口供电(核心板背面的功耗测试开关拨到 OFF 一端),此种供电方式下,VDD_EXT 引脚为 3.3V,可以直接给 AirLCD_1010 配件板和 AirFONT_1000 配件板供电;
-  - 为了演示方便,所以 Air780EHM/Air780EHV/Air780EGH 核心板上电后直接通过 VDD_EXT 引脚给 AirLCD_1010 配件板供电,通过3V3引脚给 AirFONT_1000 配件板供电;
+  - Air780EHM/Air780EHV/Air780EGH 核心板通过 TYPE-C USB 口供电(核心板正面开关拨到 ON 一端),此种供电方式下,VDD_EXT 引脚为 3.3V,可以直接给 AirLCD_1010 配件板和 AirFONTS_1000 配件板供电;
+  - 为了演示方便,所以 Air780EHM/Air780EHV/Air780EGH 核心板上电后直接通过 VDD_EXT 引脚给 AirLCD_1010 配件板供电,通过3V3引脚给 AirFONTS_1000 配件板供电;
   - 客户在设计实际项目时,一般来说,需要通过一个 GPIO 来控制 LDO 给配件板供电,这样可以灵活地控制配件板的供电,可以使项目的整体功耗降到最低;
 
 ### 3.2 接线配置
@@ -84,7 +84,7 @@
 
 <table>
 <tr>
-<td>Air780EHM/Air780EHV/Air780EGH 核心板<br/></td><td>AirFONT_1000配件板<br/></td></tr>
+<td>Air780EHM/Air780EHV/Air780EGH 核心板<br/></td><td>AirFONTS_1000配件板<br/></td></tr>
 <tr>
 <td>83/SPI0_CS<br/></td><td>CS<br/></td></tr>
 <tr>
@@ -104,13 +104,15 @@
 
 ### 4.1 开发工具
 
-- [Luatools 下载调试工具](https://gitee.com/link?target=https%3A%2F%2Fdocs.openluat.com%2Fair780egh%2Fluatos%2Fcommon%2Fdownload%2F) - 固件烧录和代码调试
+- [Luatools下载调试工具](https://docs.openluat.com/air780egh/luatos/common/download/) - 固件烧录和代码调试
 
 ### 4.2 内核固件
 
-- [点击下载 Air780EHM 系列最新版本内核固件](https://gitee.com/link?target=https%3A%2F%2Fdocs.openluat.com%2Fair780epm%2Fluatos%2Ffirmware%2Fversion%2F)
-- [点击下载 Air780EHV 系列最新版本内核固件](https://gitee.com/link?target=https%3A%2F%2Fdocs.openluat.com%2Fair780ehv%2Fluatos%2Ffirmware%2Fversion%2F)
-- [点击下载 Air780EGH 系列最新版本内核固件](https://gitee.com/link?target=https%3A%2F%2Fdocs.openluat.com%2Fair780egh%2Fluatos%2Ffirmware%2Fversion%2F)
+- [点击下载Air780EHM系列最新版本内核固件](https://docs.openluat.com/air780epm/luatos/firmware/version/),demo所使用的是LuatOS-SoC_V2018_Air780EHM 1号固件
+  
+- [点击下载Air780EHV系列最新版本内核固件](https://docs.openluat.com/air780ehv/luatos/firmware/version/),demo所使用的是LuatOS-SoC_V2018_Air780EHV 1号固件
+  
+- [点击下载Air780EGH系列最新版本内核固件](https://docs.openluat.com/air780egh/luatos/firmware/version/),demo所使用的是LuatOS-SoC_V2018_Air780EGH 1号固件
 
 使用 HZfont 需要使用 V2020 版本以上的 14 号固件或114号固件,且 14 号固件或114号固件仅支持 HZfont
 使用其他字体,demo 所使用的是 LuatOS-SoC_V2018 1 号固件

+ 2 - 2
module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirLCD_1010/lcd/font_drv/gtfont_drv.lua

@@ -12,9 +12,9 @@
 
 
 说明:
-1、gtfont核心库演示demo,是使用gtfont核心库来驱动合宙AirFONT_1000矢量字库小板
+1、gtfont核心库演示demo,是使用gtfont核心库来驱动合宙AirFONTS_1000矢量字库小板
 2、在主程序mian.lua中require "gtfont_drv"即可执行加载本demo内的演示代码
-3、通过使用gtfont_drv.init()对合宙AirFONT_1000矢量字库小板进行初始化
+3、通过使用gtfont_drv.init()对合宙AirFONTS_1000矢量字库小板进行初始化
 4、通过使用 lcd.drawGtfontUtf8Gray(str,size,gray,x,y)接口在lcd屏幕上灰度显示 UTF8 字符串,支持10-192号字体
 ]]
 

+ 1 - 1
module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirLCD_1010/lcd/lcd_drv/lcd_drv.lua

@@ -40,7 +40,7 @@ function lcd_drv.init()
             pin_rst = 36,                          -- 复位引脚
             pin_pwr = 1,                           -- 背光控制引脚GPIO的ID号
             port = lcd.HWID_0,                     -- 驱动端口
-            pin_dc = 0xFF,                         -- lcd数据/命令选择引脚GPIO号,默认:nil
+            -- pin_dc = 0xFF,                      -- lcd数据/命令选择引脚GPIO ID号,使用lcd 专用 SPI 接口 lcd.HWID_0不需要填此参数,使用通用SPI接口需要赋值
             direction = 0,                         -- lcd屏幕方向 0:0° 1:90° 2:180° 3:270°,屏幕方向和分辨率保存一致
             w = 320,                               -- lcd 水平分辨率
             h = 480,                               -- lcd 竖直分辨率

+ 1 - 1
module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirLCD_1010/lcd/main.lua

@@ -30,7 +30,7 @@ VERSION:项目版本号,ascii string类型
 
 -- 定义项目名称和版本号
 PROJECT = "ui_demo" -- 项目名称
-VERSION = "1.0.0"    -- 版本号
+VERSION = "001.000.000"    -- 版本号
 
 -- 在日志中打印项目名和项目版本号
 log.info("ui_demo", PROJECT, VERSION)

+ 43 - 12
module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirLCD_1010/lcd/readme.md

@@ -62,8 +62,7 @@
 
 ### 4.3 自定义字体演示
 1. **外部字体加载** - 支持加载外部自定义字体文件
-2. **GB2312编码** - 支持GB2312编码的中文字体
-3. **多颜色文字** - 支持不同颜色的文字显示
+2. **多颜色文字** - 支持不同颜色的文字显示
 
 ### 4.4 触摸交互功能
 1. **页面导航** - 支持多页面之间的切换
@@ -76,30 +75,62 @@
 
 - Air780EHM/Air780EHV/Air780EGH 核心板 × 1
 - AirLCD_1010 触摸配件板 × 1
-- GTFont 矢量字库,使用的是 AirFONT_1000 配件板 × 1
+- GTFont 矢量字库,使用的是 AirFONTS_1000 配件板 × 1
 - 母对母杜邦线 × 17,杜邦线太长的话,会出现 spi 通信不稳定的现象;
 - TYPE-C 数据线 × 1
-- Air780EHM/Air780EHV/Air780EGH 核心板和 AirLCD_1010配件板以及AirFONT_1000 配件板的硬件接线方式为
+- Air780EHM/Air780EHV/Air780EGH 核心板和 AirLCD_1010 配件板以及 AirFONTS_1000 配件板的硬件接线方式为
 
-  - Air780EHM/Air780EHV/Air780EGH 核心板通过 TYPE-C USB 口供电(核心板背面的功耗测试开关拨到 OFF 一端),此种供电方式下,VDD_EXT 引脚为 3.3V,可以直接给 AirLCD_1010配件板和AirFONT_1000 配件板供电;
-  - 为了演示方便,所以 Air780EHM/Air780EHV/Air780EGH 核心板上电后直接通过 vbat 引脚给 AirLCD_1010配件板和AirFONT_1000 配件板提供了 3.3V 的供电;
+  - Air780EHM/Air780EHV/Air780EGH 核心板通过 TYPE-C USB 口供电(核心板正面开关拨到 ON 一端),此种供电方式下,VDD_EXT 引脚为 3.3V,可以直接给 AirLCD_1010 配件板和 AirFONTS_1000 配件板供电;
+  - 为了演示方便,所以 Air780EHM/Air780EHV/Air780EGH 核心板上电后直接通过 VDD_EXT 引脚给 AirLCD_1010 配件板供电,通过3V3引脚给 AirFONTS_1000 配件板供电;
   - 客户在设计实际项目时,一般来说,需要通过一个 GPIO 来控制 LDO 给配件板供电,这样可以灵活地控制配件板的供电,可以使项目的整体功耗降到最低;
 
 ### 5.2 接线配置
 
-#### 5.2.1 LCD 显示屏接线
+#### 5.2.1 显示屏接线
 
-<table> 
-<tr> <td>Air780EHM/Air780EHV/Air780EGH 核心板</td><td>AirLCD_1010配件板</td></tr> <tr> <td>53/LCD_CLK</td><td>SCLK/CLK</td></tr> <tr> <td>52/LCD_CS</td><td>CS</td></tr> <tr> <td>49/LCD_RST</td><td>RES/RST</td></tr> <tr> <td>50/LCD_SDA</td><td>SDA/MOS</td></tr> <tr> <td>51/LCD_RS</td><td>DC/RS</td></tr> <tr> <td>22/GPIO1</td><td>BLK</td></tr> <tr> <td>24/VDD_EXT</td><td>VCC</td></tr> <tr> <td>67/I2C1_SCL</td><td>SCL</td></tr> <tr> <td>66/I2C1_SDA</td><td>SDA</td></tr> <tr> <td>19/GPIO22</td><td>INT</td></tr> 
+<table>
+<tr>
+<td>Air780EHM/Air780EHV/Air780EGH 核心板<br/></td><td>AirLCD_1010配件板<br/></td></tr>
+<tr>
+<td>53/LCD_CLK<br/></td><td>SCLK/CLK<br/></td></tr>
+<tr>
+<td>52/LCD_CS<br/></td><td>CS<br/></td></tr>
+<tr>
+<td>49/LCD_RST<br/></td><td>RES/RST<br/></td></tr>
+<tr>
+<td>50/LCD_SDA<br/></td><td>SDA/MOS<br/></td></tr>
+<tr>
+<td>51/LCD_RS<br/></td><td>DC/RS<br/></td></tr>
+<tr>
+<td>22/GPIO1<br/></td><td>BLK<br/></td></tr>
+<tr>
+<td>24/VDD_EXT<br/></td><td>VCC<br/></td></tr>
+<tr>
+<td>67/I2C1_SCL<br/></td><td>SCL<br/></td></tr>
+<tr>
+<td>66/I2C1_SDA<br/></td><td>SDA<br/></td></tr>
+<tr>
+<td>19/GPIO22<br/></td><td>INT<br/></td></tr>
 </table>
 
 #### 5.2.2 GTFont 字库接线
 
-<table> 
-<tr> <td>Air780EHM/Air780EHV/Air780EGH 核心板</td><td>AirFONT_1000配件板</td></tr> <tr> <td>83/SPI0_CS</td><td>CS</td></tr> <tr> <td>84/SPI0_MISO</td><td>MISO</td></tr> <tr> <td>85/SPI0_MOSI</td><td>MOSI</td></tr> <tr> <td>86/SPI0_CLK</td><td>CLK</td></tr> <tr> <td>24/VDD_EXT</td><td>VCC</td></tr> 
+<table>
+<tr>
+<td>Air780EHM/Air780EHV/Air780EGH 核心板<br/></td><td>AirFONTS_1000配件板<br/></td></tr>
+<tr>
+<td>83/SPI0_CS<br/></td><td>CS<br/></td></tr>
+<tr>
+<td>84/SPI0_MISO<br/></td><td>MISO<br/></td></tr>
+<tr>
+<td>85/SPI0_MOSI<br/></td><td>MOSI<br/></td></tr>
+<tr>
+<td>86/SPI0_CLK<br/></td><td>CLK<br/></td></tr>
+<tr>
+<td>3V3<br/></td><td>VCC<br/></td></tr>
 </table>
 
-#### 5.2.3 接线图
+### 5.3 实际接线图
 ![](https://docs.openLuat.com/cdn/image/Air780EHV_AirLCD_10010_AirFONTS_1000接线图.jpg)
 
 ## 六、演示软件环境

+ 189 - 0
module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirRC522_1000/AirRC522_1000.lua

@@ -0,0 +1,189 @@
+--[[
+@module  AirRC522_1000
+@summary AirRC522_1000测试功能模块
+@version 1.0
+@date    2025.07.23
+@author  马亚丹
+@usage
+本demo演示的功能为:使用Air780EHM/EHV/EGH核心板通过SPI实现对RC522的操作,演示读写数据等操作。
+以 Air780EHM/EHV/EGH核心板为例, 接线如下:
+Air780EHM/EHV/EGH         AirRC522_1000
+GND(任意)                     GND
+3V3                           3.3V
+83/SPI0CS                     SDA
+86/SPI0CLK                    SCK
+85/SPI0MOSI                   MOSI
+84/SPI0MISO                   MISO
+19/GPIO22(可选任意空闲IO)      RST
+
+核心逻辑:
+1. 初始化并启用spi,如果初始化失败,退出程序
+2. 初始化RC522模块,如果初始化失败,退出程序
+3. 循环检测卡片。
+4. 向卡片指定块号写入数据,并读取数据验证一致性
+5. 读取卡片所有数据
+]]
+
+
+
+rc522 = require "rc522"
+
+
+-- 硬件配置参数(Air780EHM/EHV/EGH适配)
+local RC522_CONFIG = {
+    spi_id = 0,              -- SPI通道
+    cs_pin = 8,             -- 片选引脚
+    rst_pin = 22,            -- 复位引脚
+    spi_baud = 2 * 1000 * 1000, -- SPI波特率
+}
+
+-- 全局变量(模块内可见)
+local spi_dev = nil -- SPI设备对象
+
+-- 1. 初始化SPI接口
+local function init_spi()
+    log.info("RC522", "初始化SPI接口")
+    -- 配置SPI参数:模式0,8位数据,高位在前
+    spi_dev = spi.setup(
+        RC522_CONFIG.spi_id,
+        RC522_CONFIG.cs_pin,
+        0,       -- 极性0
+        0,       -- 相位0
+        8,       -- 数据位
+        RC522_CONFIG.spi_baud,
+        spi.MSB, -- 高位优先
+        spi.master,--主模式    
+        spi.half   --半双工
+    )
+
+    if not spi_dev then
+        log.error("RC522", "SPI初始化失败")
+        return false
+    end
+    log.info("RC522", "SPI初始化成功")
+    return true
+end
+
+-- 2. 初始化RC522模块
+local function init_rc522()
+    log.info("RC522", "初始化RC522传感器")
+    -- 初始化RC522硬件(SPI ID、CS引脚、RST引脚)
+    local init_ok = rc522.init(
+        RC522_CONFIG.spi_id,
+        RC522_CONFIG.cs_pin,
+        RC522_CONFIG.rst_pin
+    )
+
+    if not init_ok then
+        log.error("RC522", "传感器初始化失败")
+        return false
+    end
+    log.info("RC522", "传感器初始化成功")
+    return true
+end
+
+-- 3. 写数据
+local function write_ic_card(block, data)
+    
+    -- 步骤1:验证数据长度
+    if #data > 16 then
+        log.error("RC522", "数据过长(最大16字节)")
+        return false
+    end
+    
+    -- 步骤2:写入数据块
+    local write_ok = rc522.write_datablock(block, data)
+    if not write_ok then
+        log.error("RC522", "数据写入失败,块号:", block)
+        return false
+    end
+   
+    log.info("RC522", "数据写入成功,块号:", block,"写入数据是:",string.char(table.unpack(data)):toHex())
+    return true
+end
+
+-- 4. 检测并读取IC卡数据
+local function read_ic_card()
+    -- 检测是否有卡片靠近
+    local status, array_id = rc522.request(rc522.reqall)
+    if not status then
+        log.info("RC522", "未检测到卡片")
+        return false
+    end
+    log.info("RC522", "检测到卡片,类型:", array_id:toHex())
+
+    -- 防冲突检测,获取卡片唯一ID
+    local status, card_uid = rc522.anticoll(array_id)
+    if not status then
+        log.error("RC522", "防冲突检测失败")
+        return false
+    end
+    log.info("RC522", "卡片UID:", card_uid:toHex())
+
+    -- -- 选择卡片,激活卡片进行后续操作
+    local select_ok = rc522.select(card_uid)
+    if not select_ok then
+        log.error("RC522", "卡片选择失败")
+        return false
+    end
+
+    -- 写数据测试
+    local TEST_BLOCK=9
+    -- 待写入的数据
+    local TEST_DATA={0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00} 
+    if write_ic_card(TEST_BLOCK, TEST_DATA) then
+        -- 读数据验证
+        local read_ok, read_data = rc522.read_datablock(TEST_BLOCK)
+        
+        if read_ok and read_data == string.char(table.unpack(TEST_DATA))then            
+            log.info("RC522", "写入验证成功,数据是:", read_data:toHex())
+        else            
+            log.warn("RC522", "写入验证失败,实际读取的数据是:", read_data:toHex())
+        end
+    end
+
+    -- 读取卡片数据块(0-63块)
+    log.info("RC522", "开始读取卡片数据...")
+    for block = 0, 63 do
+        local read_ok, data = rc522.read_datablock(block)
+        if read_ok and data then
+            log.info(string.format("块[%d]", block), data:toHex())
+        else
+            log.warn(string.format("块[%d]读取失败", block))
+        end
+        --每读取完一个块等待20ms(时间可按需修改)
+        sys.wait(20)
+    end
+
+    -- 停止卡片操作
+    rc522.halt()
+    return true
+end
+-- 5. 关闭SPI设备,成功返回0
+local function spi_close_func()    
+    log.info("关闭spi", spi.close(RC522_CONFIG.spi_id))
+end
+
+-- 6. 主测试任务
+local function rc522_main_test()
+    -- 初始化硬件
+    if not init_spi() then
+        spi_close_func()
+        return
+    end
+    if not init_rc522() then
+        spi_close_func()
+        return
+    end
+
+    -- 循环检测卡片
+    log.info("RC522", "开始检测卡片")
+    while true do
+        read_ic_card()
+         -- 循环检测间隔:2s
+        sys.wait(2000)
+    end
+end
+
+-- 启动主任务
+sys.taskInit(rc522_main_test)

+ 73 - 0
module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirRC522_1000/main.lua

@@ -0,0 +1,73 @@
+--[[
+@module  main
+@summary LuatOS用户应用脚本文件入口,总体调度应用逻辑 
+@version 001.000.000
+@date    2025.9.05
+@author  马亚丹
+@usage
+本demo是使用Air780EHM/EHV/EGH核心板挂载合宙AirRC522_1000配件板,来演示rc522的功能使用方法,代码脚本如下:
+1.rc522.lua :rc522扩展库文件,在AirRC522_1000.lua功能模块中require使用  
+2.AirRC522_1000.lua:功能演示核心文件,在main.lua中加载运行,详细逻辑请看AirRC522_1000.lua 文件
+]]
+
+
+--[[
+必须定义PROJECT和VERSION变量,Luatools工具会用到这两个变量,远程升级功能也会用到这两个变量
+PROJECT:项目名,ascii string类型
+        可以随便定义,只要不使用,就行
+VERSION:项目版本号,ascii string类型
+        如果使用合宙iot.openluat.com进行远程升级,必须按照"XXX.YYY.ZZZ"三段格式定义:
+            X、Y、Z各表示1位数字,三个X表示的数字可以相同,也可以不同,同理三个Y和三个Z表示的数字也是可以相同,可以不同
+            因为历史原因,YYY这三位数字必须存在,但是没有任何用处,可以一直写为000
+        如果不使用合宙iot.openluat.com进行远程升级,根据自己项目的需求,自定义格式即可
+]]
+
+
+PROJECT = "Air780EHM/EHV/EGH_rc522_DEMO"
+VERSION = "001.000.000"
+
+
+-- 在日志中打印项目名和项目版本号
+log.info("main", PROJECT, VERSION)
+
+
+-- 如果内核固件支持wdt看门狗功能,此处对看门狗进行初始化和定时喂狗处理
+-- 如果脚本程序死循环卡死,就会无法及时喂狗,最终会自动重启
+if wdt then
+    --配置喂狗超时时间为9秒钟
+    wdt.init(9000)
+    --启动一个循环定时器,每隔3秒钟喂一次狗
+    sys.timerLoopStart(wdt.feed, 3000)
+end
+
+
+-- 如果内核固件支持errDump功能,此处进行配置,【强烈建议打开此处的注释】
+-- 因为此功能模块可以记录并且上传脚本在运行过程中出现的语法错误或者其他自定义的错误信息,可以初步分析一些设备运行异常的问题
+-- 以下代码是最基本的用法,更复杂的用法可以详细阅读API说明文档
+-- 启动errDump日志存储并且上传功能,600秒上传一次
+-- if errDump then
+--     errDump.config(true, 600)
+-- end
+
+
+-- 使用LuatOS开发的任何一个项目,都强烈建议使用远程升级FOTA功能
+-- 可以使用合宙的iot.openluat.com平台进行远程升级
+-- 也可以使用客户自己搭建的平台进行远程升级
+-- 远程升级的详细用法,可以参考fota的demo进行使用
+
+
+-- 启动一个循环定时器
+-- 每隔3秒钟打印一次总内存,实时的已使用内存,历史最高的已使用内存情况
+-- 方便分析内存使用是否有异常
+-- sys.timerLoopStart(function()
+--     log.info("mem.lua", rtos.meminfo())
+--     log.info("mem.sys", rtos.meminfo("sys"))
+-- end, 3000)
+
+-- 加载AirRC522_1000功能模块
+require "AirRC522_1000"
+
+-- 用户代码已结束---------------------------------------------
+-- 结尾总是这一句
+sys.run()
+-- sys.run()之后不要加任何语句!!!!!因为添加的任何语句都不会被执行

+ 646 - 0
module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirRC522_1000/rc522.lua

@@ -0,0 +1,646 @@
+--[[
+@module rc522
+@summary rc522 非接触式读写卡驱动
+@version 1.0
+@date    2022.06.14
+@author  Dozingfiretruck
+@usage
+--注意:因使用了sys.wait()所有api需要在task中使用
+-- 用法实例
+local rc522 = require "rc522"
+local function rc522_test()
+    spi_rc522 = spi.setup(0,nil,0,0,8,10*1000*1000,spi.MSB, spi.master,spi.half)
+    rc522.init(0,12,16)
+    wdata = {0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}
+    while 1 do
+        rc522.write_datablock(8,wdata)
+        for i=0,63 do
+            local a,b = rc522.read_datablock(i)
+            if a then
+                log.info("read",i,b:toHex())
+            end
+        end
+        sys.wait(500)
+    end
+end)
+sys.taskInit(rc522_test)
+]]
+
+
+local rc522= {}
+
+local rc522_spi, rc522_rst, rc522_irq, rc522_cs
+
+---器件所用地址
+local rc522_idle             = 0x00 --取消当前命令
+local rc522_authent          = 0x0E --验证密钥
+local rc522_receive          = 0x08 --接收数据
+local rc522_transmit         = 0x04 --发送数据
+local rc522_transceive       = 0x0C --发送并接收数据
+local rc522_resetphase       = 0x0F --复位
+local rc522_calccrc          = 0x03 --CRC计算
+
+--Mifare_One卡片命令字
+rc522.reqidl                 = 0x26 --寻天线区内未进入休眠状态
+rc522.reqall                 = 0x52 --寻天线区内全部卡
+local rc522_anticoll1        = 0x93 --防冲撞
+local rc522_anticoll2        = 0x95 --防冲撞
+local rc522_authent1a        = 0x60 --验证A密钥
+local rc522_authent1b        = 0x61 --验证B密钥
+local rc522_read             = 0x30 --读块
+local rc522_write            = 0xA0 --写块
+local rc522_decrement        = 0xC0 --扣款
+local rc522_increment        = 0xC1 --充值
+local rc522_restore          = 0xC2 --调块数据到缓冲区
+local rc522_transfer         = 0xB0 --保存缓冲区中数据
+local rc522_halt             = 0x50 --休眠
+
+--MF522寄存器定义
+-- PAGE 0
+local rc522_rfu00            = 0x00 --保留为将来之用
+local rc522_com_mand         = 0x01 --启动和停止命令的执行
+local rc522_com_ie           = 0x02 --中断请求传递的使能和禁能控制位
+local rc522_divl_en          = 0x03 --中断请求传递的使能和禁能控制位
+local rc522_com_irq          = 0x04 --包含中断请求标志
+local rc522_div_irq          = 0x05 --包含中断请求标志
+local rc522_error            = 0x06 --错误标志,指示执行的上个命令的错误状态
+local rc522_status1          = 0x07 --包含通信的状态标志
+local rc522_status2          = 0x08 --包含接收器和发送器的状态标志
+local rc522_fifo_data        = 0x09 --64 字节 FIFO 缓冲区的输入和输出
+local rc522_fifo_level       = 0x0A --指示 FIFO 中存储的字节数
+local rc522_water_level      = 0x0B --定义 FIFO 下溢和上溢报警的 FIFO 深度
+local rc522_control          = 0x0C --不同的控制寄存器
+local rc522_bit_framing      = 0x0D --面向位的帧的调节
+local rc522_coll             = 0x0E --RF 接口上检测到的第一个位冲突的位的位置
+local rc522_rfu0f            = 0x0F --保留为将来之用
+-- PAGE 1
+local RFU10                  = 0x10 --保留为将来之用
+local rc522_mode             = 0x11 --定义发送和接收的常用模式
+local rc522_tx_mode          = 0x12 --定义发送过程的数据传输速率
+local rc522_rx_mode          = 0x13 --定义接收过程中的数据传输速率
+local rc522_tx_control       = 0x14 --控制天线驱动器管脚 TX1 和 TX2 的逻辑特性
+local rc522_tx_ayto          = 0x15 --控制天线驱动器的设置
+local rc522_tx_sel           = 0x16 --选择天线驱动器的内部源
+local rc522_rx_sel           = 0x17 --选择内部的接收器设置
+local rc522_rx_threshold     = 0x18 --选择位译码器的阈值
+local rc522_demod            = 0x19 --定义解调器的设置
+local rc522_rfu1a            = 0x1A --保留为将来之用
+local rc522_rfu1b            = 0x1B --保留为将来之用
+local rc522_mifare           = 0x1C --控制 ISO 14443/MIFARE 模式中 106kbit/s 的通信
+local rc522_rfu1d            = 0x1D --保留为将来之用
+local rc522_rfu1e            = 0x1E --保留为将来之用
+local rc522_serial_speed     = 0x1F --选择串行 UART 接口的速率
+-- PAGE 2
+local rc522_rfu20            = 0x20 --保留为将来之用
+local rc522_crcresult_m      = 0x21 --显示 CRC 计算的实际 MSB 值
+local rc522_crcresult_l      = 0x22 --显示 CRC 计算的实际 LSB 值
+local rc522_rfu23            = 0x23 --保留为将来之用
+local rc522_mod_width        = 0x24 --控制 ModWidth 的设置
+local rc522_rfu25            = 0x25 --保留为将来之用
+local rc522_rfcfg            = 0x26 --配置接收器增益
+local rc522_gsn              = 0x27 --选择天线驱动器管脚 TX1 和 TX2 的调制电导
+local rc522_cwgscfg          = 0x28 --选择天线驱动器管脚 TX1 和 TX2 的调制电导
+local rc522_modgscfg         = 0x29 --选择天线驱动器管脚 TX1 和 TX2 的调制电导
+local rc522_tmode            = 0x2A --定义内部定时器的设置
+local rc522_tprescaler       = 0x2B --定义内部定时器的设置
+local rc522_tpreload_h       = 0x2C --描述 16 位长的定时器重装值
+local rc522_tpreload_l       = 0x2D --描述 16 位长的定时器重装值
+local rc522_tcounter_value_h = 0x2E --描述 16 位长的定时器重装值
+local rc522_tcounter_value_l = 0x2F --描述 16 位长的定时器重装值
+-- PAGE 3
+local rc522_rfu30            = 0x30 --保留为将来之用
+local rc522_testsel1         = 0x31 --常用测试信号的配置
+local rc522_testsel2         = 0x32 --常用测试信号的配置和 PRBS 控制
+local rc522_testpin_en       = 0x33 --D1-D7 输出驱动器的使能管脚(注:仅用于串行接口)
+local rc522_testpin_value    = 0x34 --定义 D1-D7 用作 I/O 总线时的值
+local rc522_testbus          = 0x35 --显示内部测试总线的状态
+local rc522_autotest         = 0x36 --控制数字自测试
+local rc522_version          = 0x37 --显示版本
+local rc522_analogtest       = 0x38 --控制管脚 AUX1 和 AUX2
+local rc522_testadc1         = 0x39 --定义 TestDAC1 的测试值
+local rc522_testadc2         = 0x3A --定义 TestDAC2 的测试值
+local rc522_testadc          = 0x3B --显示 ADC I 和 Q 通道的实际值
+local rc522_rfu3c            = 0x3C --保留用于产品测试
+local rc522_rfu3d            = 0x3D --保留用于产品测试
+local rc522_rfu3e            = 0x3E --保留用于产品测试
+local rc522_rfu3f            = 0x3F --保留用于产品测试
+
+local Key_A                  = string.char(0xff, 0xff, 0xff, 0xff, 0xff, 0xff)
+local Key_B                  = string.char(0xff, 0xff, 0xff, 0xff, 0xff, 0xff)
+
+--[[
+写rc522寄存器
+@api rc522.set_bit_mask(address, value)
+@number address 地址
+@number value    值
+@usage
+write_rawrc(rc522_bit_framing,0x07)
+]]
+local function write_rawrc(address, value)
+    rc522_cs(0)
+    local data = string.char((address << 1) & 0x7E) .. string.char(value)
+    spi.send(rc522_spi, data)
+    -- rc522_spi:send(data)
+    rc522_cs(1)
+end
+
+--[[
+读rc522寄存器
+@api rc522.read_rawrc(address)
+@number address 地址
+@return number 寄存器值
+@usage
+local n = read_rawrc(rc522_com_irq)
+]]
+local function read_rawrc(address)
+    rc522_cs(0)
+    local data = string.char(((address << 1) & 0x7E)|0x80)
+    spi.send(rc522_spi, data)
+    local val = spi.recv(rc522_spi, 1)
+    -- rc522_spi:send(data)
+    -- local val = rc522_spi:recv(1)
+    rc522_cs(1)
+    return string.byte(val)
+end
+
+--[[
+对rc522寄存器置位
+@api rc522.set_bit_mask(address, mask)
+@number address 地址
+@number mask    置位值
+@usage
+rc522.set_bit_mask (rc522_fifo_level, 0x80)	
+]]
+function rc522.set_bit_mask(address, mask)
+    local current = read_rawrc(address)
+    write_rawrc(address, bit.bor(current, mask))
+end
+
+--[[
+对rc522寄存器清位
+@api rc522.clear_bit_mask(address, mask)
+@number address 地址
+@number mask    清位值
+@usage
+rc522.clear_bit_mask(rc522_com_irq, 0x80 )
+]]
+function rc522.clear_bit_mask(address, mask)
+    local current = read_rawrc(address)
+    write_rawrc(address, bit.band(current, bit.bnot(mask)))
+end
+
+--[[
+命令通讯
+@api rc522.command(command,data)
+@number command
+@number data
+@return status data len 结果,返回数据,收到的位长度
+@usage
+rc522.version()
+]]
+function rc522.command(command, data)
+    local out_data = {}
+    local len      = 0
+    local status   = false
+    local Irq_en   = 0x00
+    local waitfor  = 0x00
+    local last_bits, n, l
+    if command == rc522_authent then
+        Irq_en  = 0x12
+        waitfor = 0x10
+    elseif command == rc522_transceive then
+        Irq_en  = 0x77
+        waitfor = 0x30
+    end
+    write_rawrc(0x02, bit.bor(Irq_en, 0x80))
+    rc522.clear_bit_mask(rc522_com_irq, 0x80)
+    write_rawrc(rc522_com_mand, rc522_idle)
+    rc522.set_bit_mask(rc522_fifo_level, 0x80)
+    for i = 1, #data do
+        write_rawrc(rc522_fifo_data, data[i])
+    end
+    write_rawrc(rc522_com_mand, command)
+    if (command == rc522_transceive) then
+        rc522.set_bit_mask(rc522_bit_framing, 0x80)
+    end
+    l = 12
+    while true do
+        sys.wait(1)
+        n = read_rawrc(rc522_com_irq)
+        l = l - 1
+        if not ((l ~= 0) and (bit.band(n, 0x01) == 0) and (bit.band(n, waitfor) == 0)) then
+            break
+        end
+    end
+    rc522.clear_bit_mask(rc522_bit_framing, 0x80)
+    if (l ~= 0) then
+        if bit.band(read_rawrc(rc522_error), 0x1B) == 0x00 then
+            status = true
+            if bit.band(n, Irq_en, 0x01) ~= 0 then
+                status = false
+            end
+            if (command == rc522_transceive) then
+                n = read_rawrc(rc522_fifo_level)
+                last_bits = bit.band(read_rawrc(rc522_control), 0x07)
+                if last_bits ~= 0 then
+                    len = (n - 1) * 8 + last_bits
+                else
+                    len = n * 8
+                end
+                if n == 0 then
+                    n = 1
+                end
+                for i = 1, n do
+                    out_data[i] = read_rawrc(rc522_fifo_data)
+                end
+            end
+        end
+    else
+        status = false
+    end
+    rc522.set_bit_mask(rc522_control, 0x80)
+    write_rawrc(rc522_com_mand, rc522_idle)
+    return status, out_data, len
+end
+
+--[[
+防冲撞
+@api rc522.anticoll(id)
+@string id 卡片序列号,4字节
+@return status uid 结果,uid
+@usage
+local status,uid = rc522.anticoll(array_id)
+]]
+function rc522.anticoll(id)
+    local check = 0
+    local uid
+    rc522.clear_bit_mask(rc522_status2, 0x08)
+    write_rawrc(rc522_bit_framing, 0x00)
+    rc522.clear_bit_mask(rc522_coll, 0x80)
+    local status, back_data = rc522.command(rc522_transceive, { 0x93, 0x20 })
+    if back_data and #back_data >= 5 then
+        for i = 1, 4 do
+            check = bit.bxor(check, back_data[i])
+        end
+        if check ~= back_data[5] then
+            status = false
+        end
+        uid = string.char(table.unpack(back_data, 1, 4))
+    end
+    rc522.clear_bit_mask(rc522_coll, 0x80)
+    return status, uid
+end
+
+--[[
+crc计算
+@api calculate_crc(data)
+@table data 数据
+@return table crc值
+@usage
+local crc = calculate_crc(buff)
+]]
+local function calculate_crc(data)
+    local ret_data = {}
+    rc522.clear_bit_mask(rc522_div_irq, 0x04)
+    write_rawrc(rc522_com_mand, rc522_idle)
+    rc522.set_bit_mask(rc522_fifo_level, 0x80)
+    for i = 1, #data do
+        write_rawrc(rc522_fifo_data, data[i])
+    end
+    write_rawrc(rc522_com_mand, rc522_calccrc)
+    local i = 0xFF
+    while true do
+        local n = read_rawrc(0x05)
+        i = i - 1
+        if not ((i ~= 0) and not (n & 0x04)) then
+            break
+        end
+    end
+    ret_data[1] = read_rawrc(0x22)
+    ret_data[2] = read_rawrc(0x21)
+    return ret_data
+end
+
+--[[
+验证卡片密码
+@api authstate(mode, addr,key,uid )
+@number mode 模式
+@number addr 地址
+@string key 密码
+@string uid uid
+@return bool 结果
+@usage
+status = authstate(rc522_authent1b, addr,Key_B,uid )
+]]
+local function authstate(mode, addr, key, uid)
+    local buff = {}
+    buff[1] = mode
+    buff[2] = addr
+    for i = 1, 6 do
+        buff[i + 2] = key:byte(i)
+    end
+    for i = 1, 4 do
+        buff[i + 8] = uid:byte(i)
+    end
+    local status, back_data = rc522.command(rc522_authent, buff)
+    if status == true and (read_rawrc(rc522_status2) & 0x08) ~= 0 then
+        return true
+    end
+    return false
+end
+
+--[[
+写数据到M1卡一块
+@api rc522.write(addr, data)
+@number addr 块地址(0-63)M1卡总共有16个扇区(每个扇区有:3个数据块+1个控制块),共64个块
+@table data 数据
+@return bool 结果
+@usage
+rc522.write(addr, data)
+]]
+function rc522.write(addr, data)
+    local buff = {}
+    buff[1] = rc522_write
+    buff[2] = addr
+    local crc = calculate_crc(buff)
+    buff[3] = crc[1]
+    buff[4] = crc[2]
+    local status, back_data, back_bits = rc522.command(rc522_transceive, buff)
+    if status == true and back_bits == 4 and (back_data[1] & 0x0F) == 0x0A then
+        local buf_w = {}
+        for i = 0, 16 do
+            table.insert(buf_w, data[i])
+        end
+        local crc = calculate_crc(buf_w)
+        table.insert(buf_w, crc[1])
+        table.insert(buf_w, crc[2])
+        status, back_data, back_bits = rc522.command(rc522_transceive, buf_w)
+        if status == true and back_bits == 4 and (back_data[1] & 0x0F) == 0x0A then
+            return status
+        end
+    end
+    return status
+end
+
+--[[
+写数据到M1卡一块
+@api rc522.read(addr)
+@number addr 块地址(0-63)M1卡总共有16个扇区(每个扇区有:3个数据块+1个控制块),共64个块
+@return bool,string 结果,数据
+@usage
+rc522.read(addr, data)
+]]
+local function read(addr)
+    local buff = {}
+    buff[1] = rc522_read
+    buff[2] = addr
+    local crc = calculate_crc(buff)
+    buff[3] = crc[1]
+    buff[4] = crc[2]
+    local status, back_data, back_bits = rc522.command(rc522_transceive, buff)
+    if status == true and back_bits == 0x90 then
+        local data = string.char(table.unpack(back_data, 1, 16))
+        return status, data
+    end
+    return false
+end
+
+--[[
+rc522 硬件版本
+@api rc522.version()
+@return number 硬件版本
+@usage
+rc522.version()
+]]
+function rc522.version()
+    return read_rawrc(rc522_version)
+end
+
+--[[
+rc522 命令卡片进入休眠状态
+@api rc522.halt()
+@return bool 结果
+@usage
+rc522.halt()
+]]
+function rc522.halt()
+    local buff = {}
+    buff[1] = rc522_halt
+    buff[2] = 0
+    local crc = calculate_crc(buff)
+    buff[3] = crc[1]
+    buff[4] = crc[2]
+    local status = rc522.command(rc522_transceive, buff)
+    return status
+end
+
+--[[
+rc522 复位
+@api rc522.reset()
+@usage
+rc522.reset()
+]]
+function rc522.reset()
+    rc522_rst(1)
+    rc522_rst(0)
+    rc522_rst(1)
+    write_rawrc(rc522_com_mand, 0x0f)
+    write_rawrc(rc522_mode, 0x3D)
+    write_rawrc(rc522_tpreload_l, 30)
+    write_rawrc(rc522_tpreload_h, 0)
+    write_rawrc(rc522_tmode, 0x8D)
+    write_rawrc(rc522_tprescaler, 0x3E)
+    write_rawrc(rc522_tx_ayto, 0x40)
+end
+
+--[[
+开启天线
+@api rc522.antenna_on()
+@usage
+rc522.antenna_on()
+]]
+function rc522.antenna_on()
+    local uc = read_rawrc(rc522_tx_control)
+    if ((uc & 0x03) == 0) then
+        rc522.set_bit_mask(rc522_tx_control, 0x03)
+    end
+end
+
+--[[
+关闭天线
+@api rc522.antenna_on()
+@usage
+rc522.antenna_on()
+]]
+function rc522.antenna_off()
+    rc522.clear_bit_mask(rc522_tx_control, 0x03)
+end
+
+--[[
+设置rc522工作方式为ISO14443_A
+@api rc522_config_isotype()
+@usage
+rc522_config_isotype()
+]]
+local function rc522_config_isotype()
+    rc522.clear_bit_mask(rc522_status2, 0x08)
+    write_rawrc(rc522_mode, 0x3D)
+    write_rawrc(rc522_rx_sel, 0x86)
+    write_rawrc(rc522_rfcfg, 0x7F)
+    write_rawrc(rc522_tpreload_l, 30)
+    write_rawrc(rc522_tpreload_h, 0)
+    write_rawrc(rc522_tmode, 0x8D)
+    write_rawrc(rc522_tprescaler, 0x3E)
+    rc522.antenna_on()
+end
+
+--[[
+rc522 寻卡
+@api rc522.request(req_code)
+@number req_code rc522.reqidl 寻天线区内未进入休眠状态 rc522.reqall 寻天线区内全部卡
+@return bool tagtype 结果,卡类型
+@usage
+status,array_id = rc522.request(rc522.reqall)
+]]
+function rc522.request(req_code)
+    rc522.clear_bit_mask(rc522_status2, 0x08)
+    write_rawrc(rc522_bit_framing, 0x07)
+    rc522.set_bit_mask(rc522_tx_control, 0x03)
+    local tagtype
+    local status, back_data, back_bits = rc522.command(rc522_transceive, { req_code })
+    if status == true and back_bits == 0x10 then
+        tagtype = string.char(table.unpack(back_data, 1, 2))
+        return status, tagtype
+    end
+    return false
+end
+
+--[[
+选定卡片
+@api rc522.select(id)
+@number id 卡片序列号,4字节
+@return bool 结果
+@usage
+status = rc522.select(id)
+]]
+function rc522.select(id)
+    if not id then
+        return false
+    end
+    local buff = {}
+    buff[1] = rc522_anticoll1
+    buff[2] = 0x70
+    buff[7] = 0
+    for i = 1, 4 do
+        buff[i + 2] = id:byte(i)
+        buff[7] = bit.bxor(buff[7], id:byte(i))
+    end
+    local crc = calculate_crc(buff)
+    buff[8] = crc[1]
+    buff[9] = crc[2]
+    rc522.clear_bit_mask(rc522_status2, 0x08)
+    local status, back_data, back_bits = rc522.command(rc522_transceive, buff)
+    if status == true and back_bits == 0x18 then
+        return true
+    end
+    return false
+end
+
+--[[
+按照rc522操作流程写入16字节数据到块
+@api rc522.write_datablock(addr,data)
+@number addr 任意块地址.M1卡总共有16个扇区(每个扇区有:3个数据块+1个控制块),共64个块
+@table data 指向要写入的数据,必须为16个字符
+@return bool 结果
+@usage
+rc522.write_datablock(addr,data)
+]]
+function rc522.write_datablock(addr, data)
+    if #data ~= 16 then
+        return false
+    end
+    local status, array_id = rc522.request(rc522.reqall)
+    if status ~= true then
+        status, array_id = rc522.request(rc522.reqall)
+    end
+    if status == true then
+        local status, uid = rc522.anticoll(array_id)
+        if not uid then
+            return false
+        end
+        if status == true and uid then
+            rc522.select(uid)
+            status = authstate(rc522_authent1b, addr, Key_B, uid)
+            if status == true then
+                status = rc522.write(addr, data)
+                rc522.halt()
+                return status
+            end
+        end
+    end
+    return false
+end
+
+--[[
+按照rc522操作流程读取块
+@api rc522.read_datablock(addr)
+@number addr 任意块地址.M1卡总共有16个扇区(每个扇区有:3个数据块+1个控制块),共64个块
+@return bool string 结果 数据
+@usage
+    for i=0,63 do
+        local a,b = rc522.read_datablock(i)
+        if a then
+            print("read",i,b:toHex())
+        end
+    end
+]]
+function rc522.read_datablock(addr)
+    local status, array_id = rc522.request(rc522.reqall)
+    if status ~= true then
+        status, array_id = rc522.request(rc522.reqall)
+    end
+    if status == true then
+        local status, uid = rc522.anticoll(array_id)
+        if status == true and uid then
+            rc522.select(uid)
+            status = authstate(rc522_authent1b, addr, Key_B, uid)
+            if status == true then
+                local status, data = read(addr)
+                if status == true then
+                    return true, data
+                end
+                rc522.halt()
+            end
+        end
+    end
+    return false
+end
+
+--[[
+rc522 初始化
+@api rc522.init(spi_id, cs, rst)
+@number spi_id spi端口号
+@number cs      cs引脚
+@number rst     rst引脚
+@return bool 初始化结果
+@usage
+spi_rc522 = spi.setup(0,nil,0,0,8,10*1000*1000,spi.MSB,1,1)
+rc522.init(0,pin.PB04,pin.PB01)
+]]
+function rc522.init(spi_id, cs, rst)
+    rc522_spi = spi_id
+    rc522_cs = gpio.setup(cs, 0, gpio.PULLUP)
+    rc522_cs(1)
+    rc522_rst = gpio.setup(rst, 0, gpio.PULLUP)
+    rc522_rst(1)
+    rc522.reset()
+    rc522_config_isotype()
+    log.debug("rc522.version", rc522.version())
+    return true
+end
+
+function rc522.test()
+
+end
+
+return rc522

+ 156 - 0
module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirRC522_1000/readme.md

@@ -0,0 +1,156 @@
+## 功能模块介绍:
+
+1. main.lua:主程序入口
+
+2. rc522.lua :rc522扩展库文件,在AirRC522_1000.lua功能模块中require使用
+
+3. AirRC522_1000.lua:功能演示核心文件,在main.lua中加载运行,详细逻辑请看AirRC522_1000.lua 文件
+
+## 演示功能概述:
+
+核心逻辑:
+
+1. 初始化并启用spi,如果初始化失败,退出程序
+
+2. 初始化RC522模块,如果初始化失败,退出程序
+
+3. 循环检测卡片。
+
+4. 向卡片指定块号写入数据,并读取数据验证一致性
+
+5. 读取卡片所有数据
+
+## 演示硬件环境:
+
+![](https://docs.openluat.com/accessory/AirSPINORFLASH_1000/image/780EHV.jpg)
+
+![](https://docs.openluat.com/accessory/AirSPINORFLASH_1000/image/rc522.jpg)
+
+1. 合宙 Air780EHM/EHV/EGH 核心板一块
+
+2. 合宙 AirRC522_1000 一套
+
+3. TYPE-C USB 数据线一根 ,Air780EHV/EHM/EGH 核心板和数据线的硬件接线方式为:
+* Air780EHV/EHM/EGH核心板通过 TYPE-C USB 口供电;(USB的拨码开关off/on,拨到on)
+
+* TYPE-C USB 数据线直接插到核心板的 TYPE-C USB 座子,另外一端连接电脑 USB 口;
+4. 杜邦线 7根
+   Air780EHV/EHM/EGH 核心板与 AirRC522_1000 按以下方式接线:
+
+| Air780EHV/EHM/EGH核心板                         | AirRC522_1000 |
+| -------------------------------------------- | ------------- |
+| GND(任意)                                      | GND           |
+| 3V3                                          | 3.3V          |
+| 83/SPI0CS                                    | SDA           |
+| 86/SPI0CLK                                   | SCK           |
+| 85/SPI0MOSI                                  | MOSI          |
+| 84/SPI0MISO                                  | MISO          |
+| GPIO22(可选任意空闲IO) | RST           |
+
+## 演示软件环境:
+
+1. Luatools 下载调试工具
+
+2. 固件版本:LuatOS-SoC_V2018_Air780EHM_1,固件地址,如有最新固件请用最新 [https://docs.openluat.com/air780ehm/luatos/firmware/version/](https://docs.openluat.com/air780ehm/luatos/firmware/version/)
+
+3. 固件版本:LuatOS-SoC_V2018_Air780EHV_1,固件地址,如有最新固件请用最新 [https://docs.openluat.com/Air780EHV/luatos/firmware/version/](https://docs.openluat.com/air780ehv/luatos/firmware/version/)
+
+4. 固件版本:LuatOS-SoC_V2018_Air780EGH_1,固件地址,如有最新固件请用最新 [https://docs.openluat.com/air780egh/luatos/firmware/version/](https://docs.openluat.com/air780egh/luatos/firmware/version/)
+
+5. pc 系统 win11(win10 及以上)
+
+## 演示核心步骤:
+
+1. 搭建好硬件环境
+
+2. Luatools 烧录内核固件和demo 脚本代码.
+   AirRC522_1000.lua脚本中,硬件配置参数中rst_pin是使用Air780EHV/EHM/EGH核心板的GPIO22,如果接其他IO,注意修改硬件配置参数中rst_pin对应的管脚号。
+
+3. 烧录成功后,代码会自动运行,查看打印日志,如果正常运行,会打印相关信息,spi 初始化,rc522初始化,写入数据读取数据等。
+
+4. 如下 log 显示:
+
+```
+[2025-12-01 18:17:19.642][000000000.252] I/user.main Air780EHM/EHV/EGH_rc522_DEMO 001.000.000
+[2025-12-01 18:17:19.648][000000000.274] I/user.RC522 初始化SPI接口
+[2025-12-01 18:17:19.654][000000000.274] SPI_HWInit 552:spi0 speed 2000000,1994805,154
+[2025-12-01 18:17:19.661][000000000.275] I/user.RC522 SPI初始化成功
+[2025-12-01 18:17:19.668][000000000.275] I/user.RC522 初始化RC522传感器
+[2025-12-01 18:17:19.681][000000000.277] D/user.rc522.version 178
+[2025-12-01 18:17:19.694][000000000.278] I/user.RC522 传感器初始化成功
+[2025-12-01 18:17:19.705][000000000.278] I/user.RC522 开始检测卡片
+[2025-12-01 18:17:19.715][000000000.283] I/user.RC522 检测到卡片,类型: 0400 4
+[2025-12-01 18:17:19.723][000000000.290] I/user.RC522 卡片UID: E3E2AD14 8
+[2025-12-01 18:17:19.736][000000000.379] I/user.RC522 数据写入成功,块号: 9 写入数据是: 01000000000000000000000000000000 32
+[2025-12-01 18:17:19.744][000000000.417] I/user.RC522 写入验证成功,数据是: 01000000000000000000000000000000 32
+[2025-12-01 18:17:19.754][000000000.418] I/user.RC522 开始读取卡片数据...
+[2025-12-01 18:17:19.764][000000000.473] I/user.块[0] E3E2AD14B80804006263646566676869 32
+[2025-12-01 18:17:19.773][000000000.553] I/user.块[1] 00000000000000000000000000000000 32
+[2025-12-01 18:17:19.864][000000000.642] I/user.块[2] 00000000000000000000000000000000 32
+[2025-12-01 18:17:19.881][000000000.715] I/user.块[3] 000000000000FF078069FFFFFFFFFFFF 32
+[2025-12-01 18:17:20.063][000000000.813] I/user.块[4] 00000000000000000000000000000000 32
+[2025-12-01 18:17:20.071][000000000.891] I/user.块[5] 00000000000000000000000000000000 32
+[2025-12-01 18:17:20.083][000000001.010] I/user.块[6] 00000000000000000000000000000000 32
+[2025-12-01 18:17:20.098][000000001.076] I/user.块[7] 000000000000FF078069FFFFFFFFFFFF 32
+[2025-12-01 18:17:20.109][000000001.141] I/user.块[8] 00000000000000000000000000000000 32
+[2025-12-01 18:17:20.118][000000001.206] I/user.块[9] 01000000000000000000000000000000 32
+[2025-12-01 18:17:20.181][000000001.273] I/user.块[10] 00000000000000000000000000000000 32
+[2025-12-01 18:17:20.244][000000001.339] I/user.块[11] 000000000000FF078069FFFFFFFFFFFF 32
+[2025-12-01 18:17:20.308][000000001.404] I/user.块[12] 00000000000000000000000000000000 32
+[2025-12-01 18:17:20.370][000000001.469] I/user.块[13] 00000000000000000000000000000000 32
+[2025-12-01 18:17:20.448][000000001.534] I/user.块[14] 00000000000000000000000000000000 32
+[2025-12-01 18:17:20.510][000000001.599] I/user.块[15] 000000000000FF078069FFFFFFFFFFFF 32
+[2025-12-01 18:17:20.573][000000001.665] I/user.块[16] 00000000000000000000000000000000 32
+[2025-12-01 18:17:20.735][000000001.719] D/mobile cid1, state0
+[2025-12-01 18:17:20.745][000000001.720] D/mobile bearer act 0, result 0
+[2025-12-01 18:17:20.759][000000001.721] D/mobile NETIF_LINK_ON -> IP_READY
+[2025-12-01 18:17:20.773][000000001.753] I/user.块[17] 00000000000000000000000000000000 32
+[2025-12-01 18:17:20.785][000000001.758] D/mobile TIME_SYNC 0
+[2025-12-01 18:17:20.795][000000001.819] I/user.块[18] 00000000000000000000000000000000 32
+[2025-12-01 18:17:20.806][000000001.884] I/user.块[19] 000000000000FF078069FFFFFFFFFFFF 32
+[2025-12-01 18:17:20.850][000000001.949] I/user.块[20] 00000000000000000000000000000000 32
+[2025-12-01 18:17:20.928][000000002.014] I/user.块[21] 00000000000000000000000000000000 32
+[2025-12-01 18:17:20.990][000000002.080] I/user.块[22] 00000000000000000000000000000000 32
+[2025-12-01 18:17:21.054][000000002.145] I/user.块[23] 000000000000FF078069FFFFFFFFFFFF 32
+[2025-12-01 18:17:21.116][000000002.210] I/user.块[24] 00000000000000000000000000000000 32
+[2025-12-01 18:17:21.180][000000002.276] I/user.块[25] 00000000000000000000000000000000 32
+[2025-12-01 18:17:21.256][000000002.345] I/user.块[26] 00000000000000000000000000000000 32
+[2025-12-01 18:17:21.318][000000002.410] I/user.块[27] 000000000000FF078069FFFFFFFFFFFF 32
+[2025-12-01 18:17:21.382][000000002.475] I/user.块[28] 00000000000000000000000000000000 32
+[2025-12-01 18:17:21.443][000000002.540] I/user.块[29] 00000000000000000000000000000000 32
+[2025-12-01 18:17:21.507][000000002.605] I/user.块[30] 00000000000000000000000000000000 32
+[2025-12-01 18:17:21.585][000000002.670] I/user.块[31] 000000000000FF078069FFFFFFFFFFFF 32
+[2025-12-01 18:17:21.648][000000002.736] I/user.块[32] 00000000000000000000000000000000 32
+[2025-12-01 18:17:21.711][000000002.801] I/user.块[33] 00000000000000000000000000000000 32
+[2025-12-01 18:17:21.774][000000002.866] I/user.块[34] 00000000000000000000000000000000 32
+[2025-12-01 18:17:21.835][000000002.932] I/user.块[35] 000000000000FF078069FFFFFFFFFFFF 32
+[2025-12-01 18:17:21.912][000000002.998] I/user.块[36] 00000000000000000000000000000000 32
+[2025-12-01 18:17:21.976][000000003.064] I/user.块[37] 00000000000000000000000000000000 32
+[2025-12-01 18:17:22.038][000000003.129] I/user.块[38] 00000000000000000000000000000000 32
+[2025-12-01 18:17:22.102][000000003.194] I/user.块[39] 000000000000FF078069FFFFFFFFFFFF 32
+[2025-12-01 18:17:22.164][000000003.259] I/user.块[40] 00000000000000000000000000000000 32
+[2025-12-01 18:17:22.240][000000003.324] I/user.块[41] 00000000000000000000000000000000 32
+[2025-12-01 18:17:22.303][000000003.389] I/user.块[42] 00000000000000000000000000000000 32
+[2025-12-01 18:17:22.365][000000003.454] I/user.块[43] 000000000000FF078069FFFFFFFFFFFF 32
+[2025-12-01 18:17:22.429][000000003.519] I/user.块[44] 00000000000000000000000000000000 32
+[2025-12-01 18:17:22.491][000000003.584] I/user.块[45] 00000000000000000000000000000000 32
+[2025-12-01 18:17:22.554][000000003.649] I/user.块[46] 00000000000000000000000000000000 32
+[2025-12-01 18:17:22.615][000000003.714] I/user.块[47] 000000000000FF078069FFFFFFFFFFFF 32
+[2025-12-01 18:17:22.696][000000003.779] I/user.块[48] 00000000000000000000000000000000 32
+[2025-12-01 18:17:22.757][000000003.844] I/user.块[49] 00000000000000000000000000000000 32
+[2025-12-01 18:17:22.817][000000003.909] I/user.块[50] 00000000000000000000000000000000 32
+[2025-12-01 18:17:22.879][000000003.974] I/user.块[51] 000000000000FF078069FFFFFFFFFFFF 32
+[2025-12-01 18:17:22.942][000000004.040] I/user.块[52] 00000000000000000000000000000000 32
+[2025-12-01 18:17:23.018][000000004.105] I/user.块[53] 00000000000000000000000000000000 32
+[2025-12-01 18:17:23.080][000000004.170] I/user.块[54] 00000000000000000000000000000000 32
+[2025-12-01 18:17:23.142][000000004.235] I/user.块[55] 000000000000FF078069FFFFFFFFFFFF 32
+[2025-12-01 18:17:23.205][000000004.300] I/user.块[56] 00000000000000000000000000000000 32
+[2025-12-01 18:17:23.267][000000004.366] I/user.块[57] 00000000000000000000000000000000 32
+[2025-12-01 18:17:23.346][000000004.431] I/user.块[58] 00000000000000000000000000000000 32
+[2025-12-01 18:17:23.407][000000004.497] I/user.块[59] 000000000000FF078069FFFFFFFFFFFF 32
+[2025-12-01 18:17:23.467][000000004.562] I/user.块[60] 00000000000000000000000000000000 32
+[2025-12-01 18:17:23.530][000000004.627] I/user.块[61] 00000000000000000000000000000000 32
+[2025-12-01 18:17:23.607][000000004.692] I/user.块[62] 00000000000000000000000000000000 32
+[2025-12-01 18:17:23.669][000000004.757] I/user.块[63] 000000000000FF078069FFFFFFFFFFFF 32
+
+```

binární
module/Air780EPM/accessory_board/AirLCD_1000/lcd/font_drv/customer_font_12.bin


binární
module/Air780EPM/accessory_board/AirLCD_1000/lcd/font_drv/customer_font_22.bin


+ 16 - 0
module/Air780EPM/accessory_board/AirLCD_1000/lcd/font_drv/customer_font_drv.lua

@@ -0,0 +1,16 @@
+--[[ 
+说明:
+customer_font_drv是用户外部自定义外部点阵字体驱动功能模块,因为不需要进行初始化,所以本文件没有任何实质代码,只在此处描述在项目中如何使用。
+自定义字体制作说明可以参考lcd核心库自定义字体制作说明:https://docs.openluat.com/osapi/core/lcd/#_6
+1、通过链接 https://gitee.com/Dozingfiretruck/u8g2_font_tool 下载字体文件制作工具
+2、制作字体
+   - 将.ttf和.otf格式字体加载到制作工具内
+   - 选择自定义,输入需要生成的字体内容,调用自定义文字显示接口只能显示所输入的内容,其他内容需要切换支持的字体
+   - 选择其他,则会按苏哦选编码格式生成对应的字体
+   - 设置字体大小,生成合宙lcd核心库可加载的.bin格式的字体文件
+3、使用lcd.setFontFile(font)接口设置
+   - 其中font为字体文件路径
+   - 若将生成的字体文件命名为customer_font_24.bin 并通过luatools工具同代码一起烧录到脚本分区,则设置接口为lcd.setFontFile("/luadb/customer_font_24.bin")
+   - 若将生成的字体文件命名为customer_font_24.bin 并通过luatools工具同固件和代码一起烧录到文件系统,则设置接口为lcd.setFontFile("/customer_font_24.bin")
+4、使用lcd.drawStr(x,y,str,fg_color)接口显示具体内容
+]]

binární
module/Air780EPM/accessory_board/AirLCD_1000/lcd/images/logo.jpg


+ 52 - 0
module/Air780EPM/accessory_board/AirLCD_1000/lcd/lcd_drv/exlcd_drv.lua

@@ -0,0 +1,52 @@
+--[[
+@module  exlcd_drv
+@summary 扩展LCD显示驱动模块,基于exlcd扩展库
+@version 1.0
+@date    2025.12.3
+@author  江访
+@usage
+本模块为扩展LCD显示驱动功能模块,主要功能包括:
+1、初始化AirLCD_1000扩展LCD显示;
+2、配置显示缓冲区和自动刷新设置;
+
+对外接口:
+1、exlcd_drv.init():初始化扩展LCD显示驱动
+如果需要实现背光亮度调节,参考exlcd扩展库中的exlcd.set_bl(level)进行设置
+]]
+
+local exlcd = require "exlcd"
+
+local exlcd_drv = {}
+
+--[[
+初始化扩展LCD显示驱动;
+
+@api exlcd_drv.init()
+@summary 配置并初始化AirLCD_1000扩展LCD显示
+@return boolean 初始化成功返回true,失败返回false
+
+@usage
+-- 初始化扩展LCD显示
+local result = exlcd_drv.init({})
+if result then
+    log.info("扩展LCD初始化成功")
+else
+    log.error("扩展LCD初始化失败")
+end
+]]
+
+function exlcd_drv.init()
+    local result = exlcd.init({lcd_model = "AirLCD_1000"})
+
+    log.info("exlcd.init", result)
+
+    if result then
+        -- 显示设置
+        lcd.setupBuff(nil, true)
+        lcd.autoFlush(false)
+    end
+
+    return result
+end
+
+return exlcd_drv

+ 80 - 0
module/Air780EPM/accessory_board/AirLCD_1000/lcd/lcd_drv/lcd_drv.lua

@@ -0,0 +1,80 @@
+--[[
+@module  lcd_drv
+@summary LCD显示驱动模块,基于lcd核心库
+@version 1.0
+@date    2025.12.3
+@author  江访
+@usage
+本模块为LCD显示驱动功能模块,主要功能包括:
+1、初始化AirLCD_1000 显示IC ST7796;
+2、配置LCD显示参数和显示缓冲区;
+3、支持多种屏幕方向和分辨率设置;
+
+对外接口:
+1、lcd_drv.init():初始化LCD显示驱动
+]]
+
+
+local lcd_drv = {}
+
+--[[
+初始化LCD显示驱动;
+
+@api lcd_drv.init()
+@summary 配置并初始化ST7796 LCD控制器
+@return boolean 初始化成功返回true,失败返回false
+
+@usage
+-- 初始化LCD显示
+local result = lcd_drv.init()
+if result then
+    log.info("LCD初始化成功")
+else
+    log.error("LCD初始化失败")
+end
+]]
+
+function lcd_drv.init()
+    local result = lcd.init("st7796",
+        {
+            pin_rst = 36,                          -- 复位引脚
+            pin_pwr = 1,                           -- 背光控制引脚GPIO的ID号
+            port = lcd.HWID_0,                     -- 驱动端口
+            -- pin_dc = 0xFF,          -- lcd数据/命令选择引脚GPIO ID号,使用lcd 专用 SPI 接口 lcd.HWID_0不需要填此参数,使用通用SPI接口需要赋值
+            direction = 0,                         -- lcd屏幕方向 0:0° 1:90° 2:180° 3:270°,屏幕方向和分辨率保存一致
+            w = 320,                               -- lcd 水平分辨率
+            h = 480,                               -- lcd 竖直分辨率
+            xoffset = 0,                           -- x偏移(不同屏幕ic 不同屏幕方向会有差异)
+            yoffset = 0,                           -- y偏移(不同屏幕ic 不同屏幕方向会有差异)
+            sleepcmd = 0X10,                       -- 睡眠命令,默认0X10
+            wakecmd = 0X11,                        -- 唤醒命令,默认0X11
+            -- bus_speed = 50*1000*1000,                            -- SPI总线速度,不填默认50M,若速率要求更高需要进行设置
+            -- interface_mode = lcd.WIRE_4_BIT_8_INTERFACE_I,       -- lcd模式,默认lcd.WIRE_4_BIT_8_INTERFACE_I
+            -- direction0 = {0x36,0x00},                            -- 0°方向的命令,(不同屏幕ic会有差异)
+            -- direction90 = {0x36,0x60},                           -- 90°方向的命令,(不同屏幕ic会有差异)
+            -- direction180 ={0x36,0xc0} ,                          -- 180°方向的命令,(不同屏幕ic会有差异)
+            -- direction270 = {0x36,0xA0},                          -- 270°方向的命令,(不同屏幕ic会有差异)
+            -- hbp = nil,                                           -- 水平后廊
+            -- hspw = nil,                                          -- 水平同步脉冲宽度
+            -- hfp = 0,                                             -- 水平前廊
+            -- vbp = 0,                                             -- 垂直后廊
+            -- vspw = 0,                                            -- 垂直同步脉冲宽度
+            -- vfp = 0,                                             -- 垂直前廊
+            -- initcmd = nil,                                       -- 自定义屏幕初始化命令表
+            -- flush_rate = nil,                                    -- 刷新率
+            -- spi_dev = nil,                                       -- spi设备,当port = "device"时有效,当port ≠ "device"时可不填或者填nil
+            -- init_in_service = false,                             -- 允许初始化在lcd service里运行,在后台初始化LCD,默认是false,Air8000/G/W/T/A、Air780EHM/EGH/EHV 支持填true,可加快初始化速度,默认SPI总线速度80M
+        })
+
+    log.info("lcd.init", result)
+
+    if result then
+        -- 显示设置
+        lcd.setupBuff(nil, true)
+        lcd.autoFlush(false)
+    end
+
+    return result
+end
+
+return lcd_drv

+ 106 - 0
module/Air780EPM/accessory_board/AirLCD_1000/lcd/main.lua

@@ -0,0 +1,106 @@
+--[[
+@module  main
+@summary LuatOS用户应用脚本文件入口,总体调度应用逻辑
+@version 1.0
+@date    2025.12.3
+@author  江访
+@usage
+本demo演示的核心功能为:
+1、LCD显示屏驱动初始化,支持lcd核心库和exlcd扩展库两种驱动方式;
+2、按键驱动初始化,支持BOOT键和PWR键的GPIO中断和防抖处理;
+3、自定义字体支持,使用外部点阵字体文件显示中文和英文字符;
+4、用户界面主循环,实现多页面切换功能,包含主页、LCD演示页和自定义字体演示页;
+5、系统监控功能,支持看门狗定时喂狗、错误日志记录和内存使用监控;
+
+注意:当前设备仅支持自定义点阵字体显示中文,不支持内置12号中文字体和GTFont矢量字体
+
+更多说明参考本目录下的readme.md文件
+]]
+
+--[[
+必须定义PROJECT和VERSION变量,Luatools工具会用到这两个变量,远程升级功能也会用到这两个变量
+PROJECT:项目名,ascii string类型
+        可以随便定义,只要不使用,就行
+VERSION:项目版本号,ascii string类型
+        如果使用合宙iot.openluat.com进行远程升级,必须按照"XXX.YYY.ZZZ"三段格式定义:
+            X、Y、Z各表示1位数字,三个X表示的数字可以相同,也可以不同,同理三个Y和三个Z表示的数字也是可以相同,可以不同
+            因为历史原因,YYY这三位数字必须存在,但是没有任何用处,可以一直写为000
+        如果不使用合宙iot.openluat.com进行远程升级,根据自己项目的需求,自定义格式即可
+]]
+
+-- main.lua - 程序入口文件
+
+-- 定义项目名称和版本号
+PROJECT = "ui_demo" -- 项目名称
+VERSION = "001.000.000"    -- 版本号
+
+-- 在日志中打印项目名和项目版本号
+log.info("ui_demo", PROJECT, VERSION)
+
+-- 设置日志输出风格为样式2(建议调试时开启)
+-- log.style(2)
+
+-- 如果内核固件支持wdt看门狗功能,此处对看门狗进行初始化和定时喂狗处理
+-- 如果脚本程序死循环卡死,就会无法及时喂狗,最终会自动重启
+if wdt then
+    --配置喂狗超时时间为9秒钟
+    wdt.init(9000)
+    --启动一个循环定时器,每隔3秒钟喂一次狗
+    sys.timerLoopStart(wdt.feed, 3000)
+end
+
+
+-- 如果内核固件支持errDump功能,此处进行配置,【强烈建议打开此处的注释】
+-- 因为此功能模块可以记录并且上传脚本在运行过程中出现的语法错误或者其他自定义的错误信息,可以初步分析一些设备运行异常的问题
+-- 以下代码是最基本的用法,更复杂的用法可以详细阅读API说明文档
+-- 启动errDump日志存储并且上传功能,600秒上传一次
+-- if errDump then
+--     errDump.config(true, 600)
+-- end
+
+
+-- 使用LuatOS开发的任何一个项目,都强烈建议使用远程升级FOTA功能
+-- 可以使用合宙的iot.openluat.com平台进行远程升级
+-- 也可以使用客户自己搭建的平台进行远程升级
+-- 远程升级的详细用法,可以参考fota的demo进行使用
+
+
+-- 启动一个循环定时器
+-- 每隔3秒钟打印一次总内存,实时的已使用内存,历史最高的已使用内存情况
+-- 方便分析内存使用是否有异常
+-- sys.timerLoopStart(function()
+--     log.info("mem.lua", rtos.meminfo())
+--     log.info("mem.sys", rtos.meminfo("sys"))
+-- end, 3000)
+
+
+-- 加载显示屏驱动管理功能模块,有以下两种:
+-- 1、使用lcd核心库驱动的lcd_drv.lua
+-- 2、使用exlcd扩展库驱动的exlcd_drv.lua
+-- 根据自己的需求,启用两者中的任何一种都可以
+-- 也可以不启用任何一种,不使用显示屏功能
+lcd_drv = require "lcd_drv"
+-- lcd_drv = require "exlcd_drv"
+
+
+-- 加载按键驱动管理功能模块
+key_drv = require "key_drv"
+
+
+-- 加载字库驱动管理功能模块
+-- 使用外部自定义字体不需要require "customer_font_drv",可以参照customer_font_drv.lua内的使用说明进行创建和加载字体文件
+
+
+-- 加载输入法驱动管理功能模块(正在开发中,后续补充)
+
+
+-- 加载lcd核心库实现的用户界面功能模块
+-- 实现多页面切换、按键事件分发和界面渲染功能
+-- 包含主页、lcd核心库功能演示页、GTFont演示页和自定义字体演示页
+require "ui_main"
+
+
+-- 用户代码已结束
+-- 结尾总是这一句
+sys.run()
+-- sys.run()之后不要加任何语句!!!!!因为添加的任何语句都不会被执行

+ 192 - 0
module/Air780EPM/accessory_board/AirLCD_1000/lcd/readme.md

@@ -0,0 +1,192 @@
+# LCD、按键与字体演示系统
+
+## 一、功能模块介绍
+
+### 1.1 核心主程序模块
+1. **main.lua** - 主程序入口,负责系统初始化和任务调度
+2. **ui_main.lua** - 用户界面主控模块,管理页面切换和事件分发
+
+### 1.2 显示页面模块
+3. **home_page.lua** - 主页模块,提供应用入口和导航功能
+4. **lcd_page.lua** - LCD图形绘制演示模块
+5. **customer_font_page.lua** - 自定义字体演示模块
+
+### 1.3 驱动模块
+7. **lcd_drv.lua** - LCD显示驱动模块,基于lcd核心库,lcd_drv和exlcd_drv二选一使用
+8. **exlcd_drv.lua** - LCD显示驱动模块,基于exlcd扩展库,lcd_drv和exlcd_drv二选一使用
+9. **key_drv.lua** - 按键驱动模块,管理BOOT键和PWR键
+10. **customer_font_drv.lua** - 自定义外部字体驱动功能模块
+
+**注意**:当前设备仅支持自定义点阵字体显示中文,不支持内置12号中文字体和GTFont矢量字体。
+
+## 二、按键消息介绍
+
+1. **"KEY_EVENT"** - 按键事件消息,包含按键类型和状态
+   - boot键事件:`boot_down`(按下)、`boot_up`(释放)
+   - pwr键事件:`pwr_down`(按下)、`pwr_up`(释放)
+   - 按键功能定义:
+     - 主页:boot键(按下)选择/切换选项,pwr键(按下)确认
+     - LCD页面:pwr键(按下)返回,boot键无功能
+     - 自定义字体页面:pwr键(按下)返回,boot键无功能
+
+## 三、显示效果
+
+<table>
+<tr><td>主页<br/></td><td>lcd核心库页面<br/></td><td>自定义字体页面<br/></td></tr><tr>
+<td rowspan="2"><img src="https://docs.openluat.com/cdn/image/Air780EPM_AirLCD_1000_主页.png" width="80" /><br/></td>
+<td rowspan="2"><img src="https://docs.openluat.com/cdn/image/Air780EPM_AirLCD_1000_lcd演示页.png" width="80" /><br/></td>
+<td><img src="https://docs.openluat.com/cdn/image/Air780EPM_AirLCD_1000_自定义字体演示页.png" width="80" /><br/></td></tr>
+</table>
+
+
+### 4.1 LCD图形绘制演示
+1. **基本图形绘制** - 展示点、线、矩形、圆形等基本图形绘制功能
+2. **图片显示** - 支持外部图片文件显示
+3. **二维码生成** - 动态生成并显示二维码
+4. **xbm格式位图示例** - 显示16*16 xbm点阵
+5. **自定义字体示例** - 使用自定义字体显示文字
+
+### 4.2 自定义字体演示
+1. **外部字体加载** - 支持加载外部自定义字体文件
+2. **多颜色文字** - 支持不同颜色的文字显示
+
+### 4.3 按键交互功能
+1. **页面导航** - 支持多页面之间的切换
+2. **按钮响应** - 按键的点击响应功能
+
+
+## 五、演示硬件环境
+
+### 5.1 硬件清单
+
+- Air780EPM核心板 × 1
+- AirLCD_1000 配件板 × 1
+- 母对母杜邦线 × 14
+- TYPE-C 数据线 × 1
+- Air780EPM核心板和 AirLCD_1000配件板以及AirFONTS_1000 配件板的硬件接线方式为
+
+  - Air780EPM核心板通过 TYPE-C USB 口供电(核心板正面开关拨到 ON 一端),此种供电方式下,VDD_EXT 引脚为 3.3V,可以直接给 AirLCD_1000配件板供电;
+  - 为了演示方便,所以 Air780EPM核心板上电后直接通过 VBAT 引脚给 AirLCD_1000配件板供电;
+  - 客户在设计实际项目时,一般来说,需要通过一个 GPIO 来控制 LDO 给配件板供电,这样可以灵活地控制配件板的供电,可以使项目的整体功耗降到最低;
+
+### 5.2 接线配置
+
+#### 5.2.1 LCD 显示屏接线
+
+<table>
+<tr>
+<td>Air780EPM核心板<br/></td><td>AirLCD_1000配件板<br/></td></tr>
+<tr>
+<td>53/LCD_CLK<br/></td><td>SCLK/CLK<br/></td></tr>
+<tr>
+<td>52/LCD_CS<br/></td><td>CS<br/></td></tr>
+<tr>
+<td>49/LCD_RST<br/></td><td>RES/RST<br/></td></tr>
+<tr>
+<td>50/LCD_SDA<br/></td><td>SDA/MOS<br/></td></tr>
+<tr>
+<td>51/LCD_RS<br/></td><td>DC/RS<br/></td></tr>
+<tr>
+<td>22/GPIO1<br/></td><td>BLK<br/></td></tr>
+<tr>
+<td>VBAT<br/></td><td>VCC<br/></td></tr>
+<tr>
+<td>GND<br/></td><td>GND<br/></td></tr>
+</table>
+
+
+#### 5.2.3 接线图
+![](https://docs.openLuat.com/cdn/image/Air780EPM_AirLCD_1000_lcd演示接线图.png)
+
+## 六、演示软件环境
+
+### 6.1 开发工具
+
+- [Luatools下载调试工具](https://docs.openluat.com/air780epm/luatos/common/download/) - 固件烧录和代码调试
+
+### 6.2 内核固件
+
+- [点击下载Air780EPM系列最新版本内核固件](https://docs.openluat.com/air780epm/luatos/firmware/version/),demo所使用的是LuatOS-SoC_V2018_Air780EPM 1号固件
+
+### 6.3 字体文件
+- 自定义字体文件:`customer_font_12.bin` 和 `customer_font_22.bin`(和lua脚本文件一起烧录,会自动放置在`/luadb/`目录下)
+- 演示图片文件:`logo.jpg`(和lua脚本文件一起烧录,会自动放置在`/luadb/`目录下)
+- [点击查看自定义字体生成和使用说明](https://docs.openluat.com/osapi/core/lcd/?h=lcd#_6)
+
+
+## 七、演示核心步骤
+
+### 7.1 硬件准备
+1. 按照硬件接线表连接所有设备
+2. 确保电源连接正确,通过TYPE-C USB口供电
+3. 检查所有接线无误,避免短路
+
+### 7.2 软件配置
+在`main.lua`中选择加载对应的驱动模块:
+
+```lua
+-- 加载显示屏驱动管理功能模块,有以下两种:
+-- 1、使用lcd核心库驱动的lcd_drv.lua
+-- 2、使用exlcd扩展库驱动的exlcd_drv.lua
+-- 根据自己的需求,启用两者中的任何一种都可以
+-- 也可以不启用任何一种,不使用显示屏功能
+lcd_drv = require "lcd_drv"
+-- lcd_drv = require "exlcd_drv"
+
+
+-- 加载按键驱动管理功能模块
+key_drv = require "key_drv"
+
+
+-- 加载字库驱动管理功能模块
+-- 使用外部自定义字体不需要require "customer_font_drv",可以参照customer_font_drv.lua内的使用说明进行创建和加载字体文件
+
+
+-- 加载输入法驱动管理功能模块(正在开发中,后续补充)
+
+
+-- 加载lcd核心库实现的用户界面功能模块
+-- 实现多页面切换、按键事件分发和界面渲染功能
+-- 包含主页、lcd核心库功能演示页、GTFont演示页和自定义字体演示页
+require "ui_main"
+```
+
+### 7.3 软件烧录
+1. 使用Luatools烧录最新内核固件
+2. 下载并烧录本项目所有脚本文件
+3. 将字体文件和图片文件随脚本文件一起烧录到脚本分区
+4. 烧录成功后设备自动重启后开始运行
+
+### 7.4 功能测试
+
+#### 7.4.1 主页面操作
+1. 设备启动后显示主页面,包含两个个功能按钮,作为按键页面切换演示
+2. 使用boot键切换选择,pwr键进入对应演示页面
+
+#### 7.4.2 LCD演示页面
+1. 查看基本图形绘制示例(点、线、矩形、圆形)
+2. 查看图片显示区域(显示logo图片)
+3. 查看二维码区域(合宙文档二维码)
+4. 查看位图和字体示例
+5. 按pwr键返回主页
+
+#### 7.4.3 自定义字体页面
+1. 查看外部字体文件显示效果
+2. 查看多颜色文字显示(红色、绿色、蓝色)
+3. 查看字体使用说明和接口信息
+4. 按pwr键返回主页
+
+### 7.5 预期效果
+
+- **主页面**:正常显示,使用boot键切换选项,pwr键确认
+- **LCD演示页面**:图形绘制清晰,图片和二维码显示正常,颜色示例完整,pwr键返回
+- **自定义字体页面**:外部字体加载正确,多颜色文字显示正常,pwr键返回
+- **按键交互**:所有按键操作响应及时准确,页面切换流畅
+
+### 7.6 故障排除
+
+1. **显示异常**:检查LCD接线是否正确,确认电源供电稳定
+2. **按键无响应**:检查参数是否配置正确,确认按键驱动初始化成功
+3. **字体显示异常**:检查所需显示内容是否包含在字体内,确认字体文件路径正确
+4. **图片无法显示**:确认图片文件已正确烧录到指定路径
+5. **系统卡顿**:检查内存使用情况,适当调整刷新频率

+ 70 - 0
module/Air780EPM/accessory_board/AirLCD_1000/lcd/tp_key_drv/key_drv.lua

@@ -0,0 +1,70 @@
+--[[
+@module  key_drv
+@summary 按键驱动模块,管理BOOT键和PWR键的GPIO中断处理
+@version 1.0
+@date    2025.12.8
+@author  江访
+@usage
+本文件为按键驱动功能模块,核心业务逻辑为:
+1、初始化BOOT键(GPIO0)和PWR键(gpio.PWR_KEY)的GPIO引脚;
+2、配置按键事件的GPIO中断处理函数,支持上升沿和下降沿触发;
+3、实现50ms防抖功能,防止抖动误触发;
+4、对外发布按键消息"KEY_EVENT",包含按键类型和状态信息;
+
+本文件的对外接口有1个:
+1、key_drv.init():初始化按键驱动,配置GPIO中断和防抖参数;
+]]
+
+local key_drv = {}
+
+-- 按键定义
+local key_boot = 0           -- GPIO0按键(BOOT键)
+local key_pwr = gpio.PWR_KEY -- 电源按键
+
+
+-- 按键事件处理函数
+local function handle_boot_key(val)
+    -- print("key_boot", val)
+    if val == 1 then
+        sys.publish("KEY_EVENT", "boot_down")
+    else
+        sys.publish("KEY_EVENT", "boot_up")
+    end
+end
+
+local function handle_pwr_key(val)
+    -- print("key_pwr", val)
+    if val == 1 then
+        sys.publish("KEY_EVENT", "pwr_up")
+    else
+        sys.publish("KEY_EVENT", "pwr_down")
+    end
+end
+
+--[[
+初始化按键GPIO;
+配置BOOT键和PWR键的GPIO中断;
+
+@api key_drv.init()
+@summary 配置BOOT键和PWR键的GPIO中断
+@return bool 初始化只会返回true
+
+@usage
+local result = key_drv.init()
+if result then
+    log.info("按键驱动初始化成功")
+end
+]]
+function key_drv.init()
+    gpio.setup(key_boot, handle_boot_key, gpio.PULLDOWN, gpio.BOTH)
+    gpio.debounce(key_boot, 50, 0)     -- 防抖,防止频繁触发
+
+    gpio.setup(key_pwr, handle_pwr_key, gpio.PULLUP, gpio.BOTH)
+    gpio.debounce(key_pwr, 50, 0)     -- 防抖,防止频繁触发
+
+    log.info("key_drv", "按键初始化完成")
+    return true
+end
+
+key_drv.init()
+return key_drv

+ 120 - 0
module/Air780EPM/accessory_board/AirLCD_1000/lcd/ui/customer_font_page.lua

@@ -0,0 +1,120 @@
+--[[
+@module  customer_font_page
+@summary 自定义字体演示模块,展示外部点阵字体的加载和显示功能
+@version 1.0
+@date    2025.12.8
+@author  江访
+@usage
+本模块为自定义字体演示功能模块,核心业务逻辑为:
+1、展示外部自定义字体文件的加载和显示功能,支持12号和22号字体;
+2、提供多颜色文字显示效果,支持红、绿、蓝三色文字;
+3、显示字体使用说明和设备支持信息;
+4、展示字体使用接口示例和文件路径信息;
+
+注意:当前设备仅支持自定义点阵字体显示中文
+
+本文件的对外接口有3个:
+1、customer_font_page.draw():绘制自定义字体演示页面;
+2、customer_font_page.handle_key(key_type, switch_page):处理按键事件;
+3、customer_font_page.on_leave():页面离开时恢复默认字体;
+]]
+local customer_font_page = {}
+
+--[[
+绘制自定义字体演示页面;
+@api customer_font_page.draw()
+@summary 绘制自定义字体演示页面的所有UI元素和字体内容
+@return nil
+]]
+function customer_font_page.draw()
+    lcd.clear()
+
+    -- 显示标题,使用22号自定义字体
+    -- 设置背景颜色
+    lcd.setColor(0xFFFF, 0x0000)
+    lcd.setFontFile("/luadb/customer_font_22.bin")
+
+    -- 显示标题区域
+    lcd.fill(0, 0, 320, 40, 0x001F) -- 蓝色标题栏    lcd.setColor(0xFFFF, 0x0000)
+
+    -- 显示标题
+    lcd.drawStr(70, 30, "自定义点阵字体演示", 0xFFFF)
+
+
+    -- 显示演示内容,使用12号自定义字体
+    lcd.setFontFile("/luadb/customer_font_12.bin")
+
+    -- 字体支持说明区域(带背景色)
+    lcd.fill(15, 60, 305, 155, 0xF7DE)          -- 浅灰色背景
+    lcd.drawRectangle(15, 60, 305, 155, 0x8410) -- 边框
+
+    -- 显示字体支持说明
+    lcd.drawStr(30, 80, "当前设备字体支持:", 0x0000)
+    lcd.drawStr(40, 105, "- 仅支持自定义字体", 0x0000)
+    lcd.drawStr(40, 120, "- 不支持内置12号中文", 0x0000)
+    lcd.drawStr(40, 135, "- 不支持GTFont矢量字体", 0x0000)
+
+    lcd.drawLine(20, 165, 300, 165, 0x8410)
+
+    -- 设置22号自定义字体文件显示示例文字
+    lcd.setFontFile("/luadb/customer_font_22.bin")
+
+    -- 示例文字区域(居中显示)
+    lcd.drawStr(100, 200, "上海合宙", 0xF800) -- 红色
+    lcd.drawStr(105, 230, "LuatOS", 0x07E0) -- 绿色
+    lcd.drawStr(95, 260, "演示demo", 0x001F) -- 蓝色
+
+    -- 恢复12号字体显示说明
+    lcd.setFontFile("/luadb/customer_font_12.bin")
+    lcd.setColor(0xFFFF, 0x0000)
+
+    -- 字体文件信息区域
+    lcd.drawLine(20, 280, 300, 280, 0x8410)
+    lcd.drawStr(20, 300, "字体文件信息:", 0x0000)
+    lcd.drawStr(30, 325, "- 12号字体: /luadb/customer_font_12.bin", 0x0000)
+    lcd.drawStr(30, 350, "- 22号字体: /luadb/customer_font_22.bin", 0x0000)
+
+    -- 显示使用接口
+    lcd.drawStr(20, 380, "使用接口:", 0x0000)
+    lcd.drawStr(35, 405, "- lcd.setFontFile(字体路径)", 0x0000)
+    lcd.drawStr(35, 425, "- lcd.drawStr(x, y, 文本, 颜色)", 0x0000)
+
+    -- 底部状态栏
+    lcd.fill(0, 440, 320, 480, 0x001F) -- 蓝色背景
+    lcd.drawStr(20, 465, "按PWR键返回主页", 0xFFFF)
+end
+
+--[[
+处理按键事件;
+@api customer_font_page.handle_key(key_type, switch_page)
+@summary 处理自定义字体页面按键事件
+@string key_type 按键类型
+@valid_values "pwr_up"
+@function switch_page 页面切换回调函数
+@return bool 事件处理成功返回true,否则返回false
+]]
+function customer_font_page.handle_key(key_type, switch_page)
+    log.info("customer_font_page.handle_key", "key_type:", key_type)
+
+    if key_type == "pwr_up" then
+        -- PWR键:返回首页
+        switch_page("home")
+        return true
+    end
+    -- BOOT键无功能
+    return false
+end
+
+--[[
+页面离开时恢复默认字体;
+@api customer_font_page.on_leave()
+@summary 恢复12号自定义字体
+@return nil
+]]
+function customer_font_page.on_leave()
+    -- 恢复使用12号自定义字体
+    lcd.setFontFile("/luadb/customer_font_12.bin")
+    log.info("customer_font_page", "已恢复12号自定义字体")
+end
+
+return customer_font_page

+ 158 - 0
module/Air780EPM/accessory_board/AirLCD_1000/lcd/ui/home_page.lua

@@ -0,0 +1,158 @@
+--[[
+@module  home_page
+@summary 主页模块,提供应用入口和导航功能
+@version 1.0
+@date    2025.12.8
+@author  江访
+@usage
+本模块为主页模块,核心业务逻辑为:
+1、提供应用入口和导航功能,支持LCD演示和自定义字体演示两个功能选项;
+2、显示系统标题和操作提示信息;
+3、管理功能按钮的选中状态,支持光标指示器;
+4、处理主页面的按键事件,支持BOOT键选择和PWR键确认;
+
+注意:使用自定义字体显示中文
+
+本文件的对外接口有4个:
+1、home_page.draw():绘制主页面UI;
+2、home_page.handle_key(key_type, switch_page):处理按键事件;
+3、home_page.on_enter():页面进入时重置选中状态;
+4、home_page.on_leave():页面离开时执行清理操作;
+]]
+
+local home_page = {}
+
+-- 按钮区域定义(调整为2个按钮)
+local buttons = {
+    { name = "lcd", text = "LCD演示", x1 = 40, y1 = 150, x2 = 140, y2 = 240, color = 0x001F }, -- 蓝色
+    { name = "customer_font", text = "自定义字体", x1 = 180, y1 = 150, x2 = 280, y2 = 240, color = 0xF800 } -- 红色
+}
+
+-- 当前选中项索引
+local selected_index = 1
+
+--[[
+绘制光标指示
+@local
+@return nil
+]]
+local function draw_cursor()
+    local btn = buttons[selected_index]
+
+    -- 在选中按钮周围绘制矩形光标
+    lcd.drawRectangle(btn.x1 - 4, btn.y1 - 4, btn.x2 + 4, btn.y2 + 4, 0xFFFF) -- 白色外框
+    lcd.drawRectangle(btn.x1 - 3, btn.y1 - 3, btn.x2 + 3, btn.y2 + 3, 0x0000) -- 黑色内框
+end
+
+--[[
+绘制主页界面;
+@api home_page.draw()
+@summary 绘制主页面所有UI元素,包括选中指示
+@return nil
+]]
+function home_page.draw()
+    lcd.clear()
+    -- 显示标题,使用22号自定义字体
+    lcd.setColor(0xFFFF, 0x0000)
+    lcd.setFontFile("/luadb/customer_font_22.bin")
+
+    -- 顶部标题栏
+    lcd.fill(0, 0, 320, 60, 0x001F) -- 蓝色背景
+    lcd.drawStr(80, 39, "合宙LCD演示系统", 0xFFFF) -- 白色文字
+
+    -- 显示演示内容,使用12号自定义字体
+    lcd.setFontFile("/luadb/customer_font_12.bin")
+
+    -- 绘制所有按钮
+    for i, btn in ipairs(buttons) do
+        local color = btn.color
+
+        -- 绘制按钮背景
+        lcd.fill(btn.x1, btn.y1, btn.x2, btn.y2, color)
+
+        -- 绘制按钮文字(居中)
+        local text_x = btn.x1 + (btn.x2 - btn.x1) / 2 - 25
+        local text_y = btn.y1 + (btn.y2 - btn.y1) / 2
+
+        lcd.drawStr(text_x, text_y, btn.text, 0xFFFF)
+
+        -- 绘制按钮边框
+        lcd.drawRectangle(btn.x1, btn.y1, btn.x2, btn.y2, 0x0000)
+    end
+
+    -- 绘制光标指示
+    draw_cursor()
+
+    -- 底部状态栏
+    lcd.fill(0, 380, 320, 480, 0x001F) -- 蓝色背景
+    lcd.drawStr(20, 405, "当前页面: 主页", 0xFFFF)
+    lcd.drawStr(30, 430, "- BOOT键: 选择选项", 0xFFFF)
+    lcd.drawStr(30, 450, "- PWR键: 确认进入", 0xFFFF)
+    lcd.drawStr(30, 470, "- 中文仅支持自定义点阵字体", 0xFFFF)
+end
+
+--[[
+处理主页按键事件;
+@api home_page.handle_key(key_type, switch_page)
+@summary 处理主页按键事件
+@string key_type 按键类型
+@valid_values "confirm", "next", "prev"
+@function switch_page 页面切换回调函数
+@return bool 事件处理成功返回true,否则返回false
+]]
+function home_page.handle_key(key_type, switch_page)
+    log.info("home_page.handle_key", "key_type:", key_type, "selected_index:", selected_index)
+
+    if key_type == "confirm" then
+        -- 确认键:切换到选中的页面
+        local btn = buttons[selected_index]
+        switch_page(btn.name)
+        return true
+    elseif key_type == "right" or key_type == "next" then
+        -- 向右/下一个
+        selected_index = selected_index % #buttons + 1
+        return true
+    elseif key_type == "left" or key_type == "prev" then
+        -- 向左/上一个
+        selected_index = (selected_index - 2) % #buttons + 1
+        return true
+    end
+    return false
+end
+
+--[[
+页面进入时重置选中状态;
+@api home_page.on_enter()
+@summary 重置选中状态
+@return nil
+]]
+function home_page.on_enter()
+    selected_index = 1 -- 默认选中第一个
+end
+
+--[[
+页面离开时执行清理操作;
+@api home_page.on_leave()
+@summary 页面离开时清理
+@return nil
+]]
+function home_page.on_leave()
+    -- 可以在这里执行清理操作
+end
+
+--[[
+获取当前选中项信息;
+@api home_page.get_selected_info()
+@summary 获取当前选中项信息
+@return table 包含选中项信息的表
+]]
+function home_page.get_selected_info()
+    local btn = buttons[selected_index]
+    return {
+        index = selected_index,
+        name = btn.name,
+        text = btn.text
+    }
+end
+
+return home_page

+ 146 - 0
module/Air780EPM/accessory_board/AirLCD_1000/lcd/ui/lcd_page.lua

@@ -0,0 +1,146 @@
+--[[
+@module  lcd_page
+@summary LCD图形绘制演示模块,展示lcd核心库的图形绘制功能
+@version 1.0
+@date    2025.12.8
+@author  江访
+@usage
+本模块为LCD图形绘制演示功能模块,核心业务逻辑为:
+1、展示LCD核心库的基本图形绘制功能,包括点、线、矩形、圆形;
+2、演示图片显示功能,支持外部JPG图片文件;
+3、显示二维码生成功能,动态生成并显示合宙文档二维码;
+4、展示xbm格式位图示例,支持16x16点阵显示;
+
+注意:使用自定义字体显示文字
+
+本文件的对外接口有2个:
+1、lcd_page.draw():绘制LCD演示页面所有UI元素;
+2、lcd_page.handle_key(key_type, switch_page):处理LCD页面按键事件;
+]]
+
+local lcd_page = {}
+
+--[[
+绘制LCD演示页面;
+@api lcd_page.draw()
+@summary 绘制LCD演示页面的所有图形和UI元素
+@return nil
+]]
+function lcd_page.draw()
+    lcd.clear()
+
+    -- 设置背景颜色
+    lcd.setColor(0xFFFF, 0x0000)
+
+    -- 显示标题区域
+    lcd.fill(0, 0, 320, 40, 0x001F) -- 蓝色标题栏    lcd.setColor(0xFFFF, 0x0000)
+
+    -- 显示标题,使用22号自定义字体
+    lcd.setFontFile("/luadb/customer_font_22.bin")
+    lcd.drawStr(95, 31, "核心库演示", 0xFFFF)
+
+    -- 显示演示内容,使用12号自定义字体
+    lcd.setFontFile("/luadb/customer_font_12.bin")
+
+    lcd.drawStr(20, 75, "基本图形绘制", 0x0000)
+    lcd.drawLine(20, 55, 300, 55, 0x8410)
+
+    lcd.drawStr(30, 98, "点", 0x0000)
+    lcd.drawPoint(55, 98, 0xFCC0)
+    lcd.drawPoint(65, 98, 0x07E0)
+    lcd.drawPoint(75, 98, 0x001F)
+
+    lcd.drawStr(30, 125, "线", 0x0000)
+    lcd.drawLine(55, 113, 115, 113, 0xFCC0)
+    lcd.drawLine(55, 118, 115, 123, 0x07E0)
+    lcd.drawLine(55, 123, 115, 118, 0x001F)
+
+    -- 绘制矩形(预留右侧空间)
+    lcd.drawStr(30, 160, "矩形", 0x0000)
+    lcd.drawRectangle(65, 138, 105, 163, 0x7296)
+    lcd.fill(120, 138, 160, 163, 0x07E0)
+
+    -- 绘制圆(预留右侧空间)
+    lcd.drawStr(30, 200, "圆形", 0x0000)
+    lcd.drawCircle(90, 193, 15, 0x001F)
+    lcd.drawCircle(130, 193, 15, 0xFCC0)
+
+    lcd.drawLine(170, 70, 170, 300, 0x8410) -- 垂直分隔线
+
+    -- === 第二区域:图片和二维码 ===
+    lcd.drawStr(180, 75, "图片二维码", 0x0000)
+
+    -- 图片显示区域 (80x80)
+    lcd.drawStr(180, 100, "LOGO", 0x0000)
+    lcd.drawRectangle(200, 110, 290, 200, 0x0000)
+    lcd.showImage(205, 115, "/luadb/logo.jpg")
+
+    -- 二维码区域 (80x80)
+    lcd.drawStr(180, 215, "二维码", 0x0000)
+    lcd.drawRectangle(200, 225, 290, 308, 0x0000)
+    lcd.drawQrcode(205, 226, "https://docs.openluat.com/air780epm/", 80)
+
+    lcd.drawLine(20, 325, 300, 325, 0x8410)
+
+    -- === 第三区域:位图 ===
+    lcd.drawLine(20, 235, 160, 235, 0x8410)
+
+    -- 位图区域
+    lcd.drawStr(20, 255, "位图示例", 0x0000)
+
+    -- 绘制位图
+    local x_start = 30
+    local y_start = 265
+    lcd.drawXbm(x_start, y_start, 16, 16, string.char(
+        0x00, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x3F, 0x80, 0x00,
+        0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0xFE, 0x7F, 0x00, 0x00))
+
+    lcd.drawXbm(x_start + 20, y_start, 16, 16, string.char(
+        0x00, 0x00, 0x80, 0x00, 0xC4, 0x7F, 0x28, 0x00, 0x10, 0x00, 0xD0, 0x3F, 0x42, 0x20, 0x44, 0x22,
+        0x40, 0x24, 0xF0, 0xFF, 0x24, 0x20, 0x24, 0x22, 0x24, 0x20, 0xE2, 0x7F, 0x02, 0x20, 0x02, 0x1E))
+
+    lcd.drawXbm(x_start, y_start + 20, 16, 16, string.char(
+        0x00, 0x00, 0x00, 0x01, 0x80, 0x01, 0x40, 0x02, 0x20, 0x04, 0x18, 0x18, 0xF4, 0x6F, 0x02, 0x00,
+        0x00, 0x00, 0xF8, 0x1F, 0x08, 0x10, 0x08, 0x10, 0x08, 0x10, 0x08, 0x10, 0xF8, 0x1F, 0x08, 0x10))
+
+    lcd.drawXbm(x_start + 20, y_start + 20, 16, 16, string.char(
+        0x00, 0x00, 0x80, 0x00, 0x00, 0x01, 0xFE, 0x7F, 0x02, 0x40, 0x02, 0x40, 0x00, 0x01, 0xFC, 0x3F,
+        0x04, 0x21, 0x04, 0x21, 0xFC, 0x3F, 0x04, 0x21, 0x04, 0x21, 0x04, 0x21, 0xFC, 0x3F, 0x04, 0x20))
+
+    -- === 第四区域:字体 ===
+    lcd.drawStr(20, 345, "中英文字体示例", 0x0000)
+
+
+    lcd.drawStr(30, 368, "- 中文字体ABC123", 0x0000)
+    lcd.drawStr(30, 394, "- 英文符号,-_./()[]{}<>!@#$%^&*_+=|?", 0x0000)
+
+    -- 显示当前字体信息
+    lcd.drawStr(20, 420, "字体路径:/luadb/customer_font_12.bin", 0x0000)
+
+    -- 底部状态栏
+    lcd.fill(0, 440, 320, 480, 0x001F) -- 蓝色背景
+    lcd.drawStr(20, 465, "按PWR键返回主页", 0xFFFF)
+end
+
+--[[
+处理按键事件;
+@api lcd_page.handle_key(key_type, switch_page)
+@summary 处理LCD页面按键事件
+@string key_type 按键类型
+@valid_values "pwr_up"
+@function switch_page 页面切换回调函数
+@return bool 事件处理成功返回true,否则返回false
+]]
+function lcd_page.handle_key(key_type, switch_page)
+    log.info("lcd_page.handle_key", "key_type:", key_type)
+
+    if key_type == "pwr_up" then
+        -- PWR键:返回首页
+        switch_page("home")
+        return true
+    end
+    -- BOOT键无功能
+    return false
+end
+
+return lcd_page

+ 146 - 0
module/Air780EPM/accessory_board/AirLCD_1000/lcd/ui/ui_main.lua

@@ -0,0 +1,146 @@
+--[[
+@module  ui_main
+@summary 用户界面主控模块,负责页面管理和事件分发
+@version 1.0
+@date    2025.12.8
+@author  江访
+@usage
+本模块为用户界面主控模块,核心业务逻辑为:
+1、管理页面切换和事件分发,支持主页、LCD演示页和自定义字体演示页;
+2、处理按键事件,分发到对应页面的处理函数;
+3、控制界面刷新频率,支持不同页面的不同刷新率需求;
+4、加载和切换自定义字体文件;
+
+注意:当前仅支持自定义点阵字体
+
+本文件的对外接口:通过sys.taskInit(ui_main)启动UI主循环
+]]
+
+-- 加载子页面
+local home_page = require "home_page"
+local lcd_page = require "lcd_page"
+local customer_font_page = require "customer_font_page"
+
+-- 当前页面状态
+local current_page = "home"
+local last_page = ""
+frame_time = 20 * 1000 -- 超时刷新时间,进入刷新率要求高的页面可以更此变量,退出再更改回来
+
+--[[
+切换页面;
+@api switch_page(new_page)
+@summary 执行页面切换操作
+@string new_page 目标页面名称
+@valid_values "home", "lcd", "customer_font"
+@return nil
+]]
+local function switch_page(new_page)
+    log.info("switch_page", "从", current_page, "切换到", new_page)
+    
+    -- 调用旧页面的退出函数
+    if current_page == "home" and home_page.on_leave then
+        home_page.on_leave()
+    elseif current_page == "lcd" and lcd_page.on_leave then
+        lcd_page.on_leave()
+    elseif current_page == "customer_font" and customer_font_page.on_leave then
+        customer_font_page.on_leave()
+    end
+
+    last_page = current_page
+    current_page = new_page
+
+    -- 调用新页面的进入函数
+    if new_page == "home" and home_page.on_enter then
+        home_page.on_enter()
+    elseif new_page == "lcd" and lcd_page.on_enter then
+        lcd_page.on_enter()
+    elseif new_page == "customer_font" and customer_font_page.on_enter then
+        customer_font_page.on_enter()
+    end
+
+    log.info("ui_main", "已切换到页面:", current_page)
+end
+
+--[[
+处理按键事件;
+@api handle_key_event(key_event)
+@summary 处理按键事件并分发到对应页面
+@string key_event 按键事件类型
+@valid_values "boot_up", "pwr_up"
+@return bool 事件处理成功返回true,否则返回false
+]]
+local function handle_key_event(key_event)
+    log.info("按键事件", "event:", key_event, "当前页面:", current_page)
+    
+    -- 只在按键释放时处理
+    if key_event == "boot_up" then
+        -- BOOT键:在主页作为方向键
+        if current_page == "home" then
+            return home_page.handle_key("next", switch_page)
+        end
+        -- 其他页面BOOT键无功能
+        return false
+    elseif key_event == "pwr_up" then
+        -- PWR键:确认/返回功能
+        if current_page == "home" then
+            return home_page.handle_key("confirm", switch_page)
+        elseif current_page == "lcd" then
+            if lcd_page.handle_key then
+                return lcd_page.handle_key("pwr_up", switch_page)
+            end
+        elseif current_page == "customer_font" then
+            if customer_font_page.handle_key then
+                return customer_font_page.handle_key("pwr_up", switch_page)
+            end
+        end
+    end
+    
+    return false
+end
+
+--[[
+用户界面主任务;
+@api ui_main()
+@summary 初始化显示和按键驱动,启动UI主循环
+@return nil
+]]
+local function ui_main()
+    if not lcd_drv.init() then
+        log.error("ui_main", "显示初始化失败")
+        return
+    end
+
+    if not key_drv.init() then
+        log.error("ui_main", "按键驱动初始化失败")
+        return
+    end
+
+    -- 使用自定义字体(12号)
+    lcd.setFontFile("/luadb/customer_font_12.bin")
+
+    -- 调用主页的进入函数
+    if home_page.on_enter then
+        home_page.on_enter()
+    end
+
+    while true do
+        -- 根据当前页面绘制内容
+        if current_page == "home" then
+            home_page.draw()
+        elseif current_page == "lcd" then
+            lcd_page.draw()
+        elseif current_page == "customer_font" then
+            customer_font_page.draw()
+        end
+
+        lcd.flush()
+
+        -- 等待按键事件
+        local result, key_event = sys.waitUntil("KEY_EVENT", frame_time)
+        if result then
+            handle_key_event(key_event)
+        end
+    end
+end
+
+sys.taskInit(ui_main)

binární
module/Air780EPM/accessory_board/AirLCD_1010/lcd/font_drv/customer_font_12.bin


binární
module/Air780EPM/accessory_board/AirLCD_1010/lcd/font_drv/customer_font_22.bin


+ 16 - 0
module/Air780EPM/accessory_board/AirLCD_1010/lcd/font_drv/customer_font_drv.lua

@@ -0,0 +1,16 @@
+--[[ 
+说明:
+customer_font_drv是用户外部自定义外部点阵字体驱动功能模块,因为不需要进行初始化,所以本文件没有任何实质代码,只在此处描述在项目中如何使用。
+自定义字体制作说明可以参考lcd核心库自定义字体制作说明:https://docs.openluat.com/osapi/core/lcd/#_6
+1、通过链接 https://gitee.com/Dozingfiretruck/u8g2_font_tool 下载字体文件制作工具
+2、制作字体
+   - 将.ttf和.otf格式字体加载到制作工具内
+   - 选择自定义,输入需要生成的字体内容,调用自定义文字显示接口只能显示所输入的内容,其他内容需要切换支持的字体
+   - 选择其他,则会按苏哦选编码格式生成对应的字体
+   - 设置字体大小,生成合宙lcd核心库可加载的.bin格式的字体文件
+3、使用lcd.setFontFile(font)接口设置
+   - 其中font为字体文件路径
+   - 若将生成的字体文件命名为customer_font_24.bin 并通过luatools工具同代码一起烧录到脚本分区,则设置接口为lcd.setFontFile("/luadb/customer_font_24.bin")
+   - 若将生成的字体文件命名为customer_font_24.bin 并通过luatools工具同固件和代码一起烧录到文件系统,则设置接口为lcd.setFontFile("/customer_font_24.bin")
+4、使用lcd.drawStr(x,y,str,fg_color)接口显示具体内容
+]]

binární
module/Air780EPM/accessory_board/AirLCD_1010/lcd/images/logo.jpg


+ 52 - 0
module/Air780EPM/accessory_board/AirLCD_1010/lcd/lcd_drv/exlcd_drv.lua

@@ -0,0 +1,52 @@
+--[[
+@module  exlcd_drv
+@summary 扩展LCD显示驱动模块,基于exlcd扩展库
+@version 1.0
+@date    2025.12.3
+@author  江访
+@usage
+本模块为扩展LCD显示驱动功能模块,主要功能包括:
+1、初始化AirLCD_1010扩展LCD显示;
+2、配置显示缓冲区和自动刷新设置;
+
+对外接口:
+1、exlcd_drv.init():初始化扩展LCD显示驱动
+如果需要实现背光亮度调节,参考exlcd扩展库中的exlcd.set_bl(level)进行设置
+]]
+
+local exlcd = require "exlcd"
+
+local exlcd_drv = {}
+
+--[[
+初始化扩展LCD显示驱动;
+
+@api exlcd_drv.init()
+@summary 配置并初始化AirLCD_1010扩展LCD显示
+@return boolean 初始化成功返回true,失败返回false
+
+@usage
+-- 初始化扩展LCD显示
+local result = exlcd_drv.init({})
+if result then
+    log.info("扩展LCD初始化成功")
+else
+    log.error("扩展LCD初始化失败")
+end
+]]
+
+function exlcd_drv.init()
+    local result = exlcd.init({lcd_model = "AirLCD_1010"})
+
+    log.info("exlcd.init", result)
+
+    if result then
+        -- 显示设置
+        lcd.setupBuff(nil, true)
+        lcd.autoFlush(false)
+    end
+
+    return result
+end
+
+return exlcd_drv

+ 80 - 0
module/Air780EPM/accessory_board/AirLCD_1010/lcd/lcd_drv/lcd_drv.lua

@@ -0,0 +1,80 @@
+--[[
+@module  lcd_drv
+@summary LCD显示驱动模块,基于lcd核心库
+@version 1.0
+@date    2025.12.3
+@author  江访
+@usage
+本模块为LCD显示驱动功能模块,主要功能包括:
+1、初始化AirLCD_1010 显示IC ST7796;
+2、配置LCD显示参数和显示缓冲区;
+3、支持多种屏幕方向和分辨率设置;
+
+对外接口:
+1、lcd_drv.init():初始化LCD显示驱动
+]]
+
+
+local lcd_drv = {}
+
+--[[
+初始化LCD显示驱动;
+
+@api lcd_drv.init()
+@summary 配置并初始化ST7796 LCD控制器
+@return boolean 初始化成功返回true,失败返回false
+
+@usage
+-- 初始化LCD显示
+local result = lcd_drv.init()
+if result then
+    log.info("LCD初始化成功")
+else
+    log.error("LCD初始化失败")
+end
+]]
+
+function lcd_drv.init()
+    local result = lcd.init("st7796",
+        {
+            pin_rst = 36,                          -- 复位引脚
+            pin_pwr = 1,                           -- 背光控制引脚GPIO的ID号
+            port = lcd.HWID_0,                     -- 驱动端口
+            -- pin_dc = 0xFF,          -- lcd数据/命令选择引脚GPIO ID号,使用lcd 专用 SPI 接口 lcd.HWID_0不需要填此参数,使用通用SPI接口需要赋值
+            direction = 0,                         -- lcd屏幕方向 0:0° 1:90° 2:180° 3:270°,屏幕方向和分辨率保存一致
+            w = 320,                               -- lcd 水平分辨率
+            h = 480,                               -- lcd 竖直分辨率
+            xoffset = 0,                           -- x偏移(不同屏幕ic 不同屏幕方向会有差异)
+            yoffset = 0,                           -- y偏移(不同屏幕ic 不同屏幕方向会有差异)
+            sleepcmd = 0X10,                       -- 睡眠命令,默认0X10
+            wakecmd = 0X11,                        -- 唤醒命令,默认0X11
+            -- bus_speed = 50*1000*1000,                            -- SPI总线速度,不填默认50M,若速率要求更高需要进行设置
+            -- interface_mode = lcd.WIRE_4_BIT_8_INTERFACE_I,       -- lcd模式,默认lcd.WIRE_4_BIT_8_INTERFACE_I
+            -- direction0 = {0x36,0x00},                            -- 0°方向的命令,(不同屏幕ic会有差异)
+            -- direction90 = {0x36,0x60},                           -- 90°方向的命令,(不同屏幕ic会有差异)
+            -- direction180 ={0x36,0xc0} ,                          -- 180°方向的命令,(不同屏幕ic会有差异)
+            -- direction270 = {0x36,0xA0},                          -- 270°方向的命令,(不同屏幕ic会有差异)
+            -- hbp = nil,                                           -- 水平后廊
+            -- hspw = nil,                                          -- 水平同步脉冲宽度
+            -- hfp = 0,                                             -- 水平前廊
+            -- vbp = 0,                                             -- 垂直后廊
+            -- vspw = 0,                                            -- 垂直同步脉冲宽度
+            -- vfp = 0,                                             -- 垂直前廊
+            -- initcmd = nil,                                       -- 自定义屏幕初始化命令表
+            -- flush_rate = nil,                                    -- 刷新率
+            -- spi_dev = nil,                                       -- spi设备,当port = "device"时有效,当port ≠ "device"时可不填或者填nil
+            -- init_in_service = false,                             -- 允许初始化在lcd service里运行,在后台初始化LCD,默认是false,Air8000/G/W/T/A、Air780EHM/EGH/EHV 支持填true,可加快初始化速度,默认SPI总线速度80M
+        })
+
+    log.info("lcd.init", result)
+
+    if result then
+        -- 显示设置
+        lcd.setupBuff(nil, true)
+        lcd.autoFlush(false)
+    end
+
+    return result
+end
+
+return lcd_drv

+ 110 - 0
module/Air780EPM/accessory_board/AirLCD_1010/lcd/main.lua

@@ -0,0 +1,110 @@
+--[[
+@module  main
+@summary LuatOS用户应用脚本文件入口,总体调度应用逻辑
+@version 1.0
+@date    2025.11.20
+@author  江访
+@usage
+本demo演示的核心功能为:
+1、LCD显示屏驱动初始化,支持多种LCD驱动模式;
+2、触摸面板驱动初始化,支持多种触摸驱动模式;
+3、字库驱动管理,中文仅支持自定义点阵字库;
+4、用户界面主循环,实现多页面切换和触摸事件处理;
+5、系统看门狗配置,确保系统稳定运行;
+
+更多说明参考本目录下的readme.md文件
+]]
+
+--[[
+必须定义PROJECT和VERSION变量,Luatools工具会用到这两个变量,远程升级功能也会用到这两个变量
+PROJECT:项目名,ascii string类型
+        可以随便定义,只要不使用,就行
+VERSION:项目版本号,ascii string类型
+        如果使用合宙iot.openluat.com进行远程升级,必须按照"XXX.YYY.ZZZ"三段格式定义:
+            X、Y、Z各表示1位数字,三个X表示的数字可以相同,也可以不同,同理三个Y和三个Z表示的数字也是可以相同,可以不同
+            因为历史原因,YYY这三位数字必须存在,但是没有任何用处,可以一直写为000
+        如果不使用合宙iot.openluat.com进行远程升级,根据自己项目的需求,自定义格式即可
+]]
+
+-- main.lua - 程序入口文件
+
+-- 定义项目名称和版本号
+PROJECT = "ui_demo" -- 项目名称
+VERSION = "001.000.000"    -- 版本号
+
+-- 在日志中打印项目名和项目版本号
+log.info("ui_demo", PROJECT, VERSION)
+
+-- 设置日志输出风格为样式2(建议调试时开启)
+-- log.style(2)
+
+-- 如果内核固件支持wdt看门狗功能,此处对看门狗进行初始化和定时喂狗处理
+-- 如果脚本程序死循环卡死,就会无法及时喂狗,最终会自动重启
+if wdt then
+    --配置喂狗超时时间为9秒钟
+    wdt.init(9000)
+    --启动一个循环定时器,每隔3秒钟喂一次狗
+    sys.timerLoopStart(wdt.feed, 3000)
+end
+
+
+-- 如果内核固件支持errDump功能,此处进行配置,【强烈建议打开此处的注释】
+-- 因为此功能模块可以记录并且上传脚本在运行过程中出现的语法错误或者其他自定义的错误信息,可以初步分析一些设备运行异常的问题
+-- 以下代码是最基本的用法,更复杂的用法可以详细阅读API说明文档
+-- 启动errDump日志存储并且上传功能,600秒上传一次
+-- if errDump then
+--     errDump.config(true, 600)
+-- end
+
+
+-- 使用LuatOS开发的任何一个项目,都强烈建议使用远程升级FOTA功能
+-- 可以使用合宙的iot.openluat.com平台进行远程升级
+-- 也可以使用客户自己搭建的平台进行远程升级
+-- 远程升级的详细用法,可以参考fota的demo进行使用
+
+
+-- 启动一个循环定时器
+-- 每隔3秒钟打印一次总内存,实时的已使用内存,历史最高的已使用内存情况
+-- 方便分析内存使用是否有异常
+-- sys.timerLoopStart(function()
+--     log.info("mem.lua", rtos.meminfo())
+--     log.info("mem.sys", rtos.meminfo("sys"))
+-- end, 3000)
+
+
+-- 加载显示屏驱动管理功能模块,有以下两种:
+-- 1、使用lcd核心库驱动的lcd_drv.lua
+-- 2、使用exlcd扩展库驱动的exlcd_drv.lua
+-- 根据自己的需求,启用两者中的任何一种都可以
+-- 也可以不启用任何一种,不使用显示屏功能
+lcd_drv = require "lcd_drv"
+-- lcd_drv = require "exlcd_drv"
+
+
+-- 加载触摸面板驱动管理功能模块,有以下两种:
+-- 1、使用tp核心库驱动的tp_drv.lua
+-- 2、使用extp扩展库驱动的extp_drv.lua
+-- 根据自己的需求,启用两者中的任何一种都可以
+-- 也可以不启用任何一种,不使用触摸面板功能
+tp_drv = require "tp_drv"
+-- tp_drv = require "extp_drv"
+
+
+-- 加载字库驱动管理功能模块
+-- 使用外部自定义字体不需要require "customer_font_drv",可以参照customer_font_drv.lua内的使用说明进行创建和加载字体文件
+
+
+
+-- 加载输入法驱动管理功能模块(正在开发中,后续补充)
+
+
+-- 加载lcd核心库实现的用户界面功能模块
+-- 实现多页面切换、触摸事件分发和界面渲染功能
+-- 包含主页、lcd核心库功能演示页、GTFont演示页和自定义字体演示页
+require "ui_main"
+
+
+-- 用户代码已结束
+-- 结尾总是这一句
+sys.run()
+-- sys.run()之后不要加任何语句!!!!!因为添加的任何语句都不会被执行

+ 199 - 0
module/Air780EPM/accessory_board/AirLCD_1010/lcd/readme.md

@@ -0,0 +1,199 @@
+# LCD、触摸与字体演示系统
+
+## 一、功能模块介绍
+
+### 1.1 核心主程序模块
+1. **main.lua** - 主程序入口,负责系统初始化和任务调度
+2. **ui_main.lua** - 用户界面主控模块,管理页面切换和事件分发
+
+### 1.2 显示页面模块
+1. **home_page.lua** - 主页模块,提供应用入口和导航功能
+2. **lcd_page.lua** - LCD图形绘制演示模块
+3. **customer_font_page.lua** - 自定义字体演示模块
+
+### 1.3 驱动模块
+1. **lcd_drv.lua** - LCD显示驱动模块,基于lcd核心库,lcd_drv和exlcd_drv二选一使用
+2. **exlcd_drv.lua** - LCD显示驱动模块,基于exlcd扩展库,lcd_drv和exlcd_drv二选一使用
+3. **tp_drv.lua** - 触摸驱动模块,基于tp核心库,tp_drv和extp_drv二选一使用
+4. **extp_drv.lua** - 触摸驱动模块,基于extp扩展库,tp_drv和extp_drv二选一使用
+
+
+## 二、触摸消息介绍
+
+1. **"BASE_TOUCH_EVENT"** - 基础触摸事件消息,包含触摸坐标和事件类型
+   - tp触摸库事件类型:`tp.EVENT_DOWN`(按下)、`tp.EVENT_MOVE`(移动)、`tp.EVENT_UP`(抬起)
+   - extp触摸库事件类型:`SINGLE_TAP`(单击)、`LONG_PRESS`(长按)可根据UI需求打开滑动事件
+   - 坐标范围:X: 0-319,Y: 0-479
+
+## 三、显示效果
+
+<table>
+<tr><td>主页<br/></td><td>lcd核心库页面<br/></td><td>自定义字体页面<br/></td></tr><tr>
+<td rowspan="2"><img src="https://docs.openluat.com/cdn/image/Air780EPM_AirLCD_1010_主页.png" width="80" /><br/></td>
+<td rowspan="2"><img src="https://docs.openluat.com/cdn/image/Air780EPM_AirLCD_1010_lcd演示页.png" width="80" /><br/></td>
+<td><img src="https://docs.openluat.com/cdn/image/Air780EPM_AirLCD_1010_自定义字体演示页.png" width="80" /><br/></td></tr>
+</table>
+
+
+## 四、演示功能概述
+
+### 4.1 LCD图形绘制演示
+1. **基本图形绘制** - 展示点、线、矩形、圆形等基本图形绘制功能
+2. **图片显示** - 支持外部图片文件显示
+3. **二维码生成** - 动态生成并显示二维码
+4. **xbm格式位图示例** - 显示16*16 xbm点阵
+5. **字体示例** - 显示12号和22号自定义字体
+
+### 4.2 自定义字体演示
+1. **外部字体加载** - 支持加载外部自定义字体文件
+2. **多颜色文字** - 支持不同颜色的文字显示
+3. **字体大小切换** - 支持12号和22号字体切换
+
+### 4.3 触摸交互功能
+1. **页面导航** - 支持多页面之间的触摸切换
+2. **按钮响应** - 触摸按钮的点击响应功能
+
+## 五、演示硬件环境
+
+### 5.1 硬件清单
+
+- Air780EPM 核心板 × 1
+- AirLCD_1010 触摸配件板 × 1
+- 母对母杜邦线 × 11,杜邦线太长的话,会出现 spi 通信不稳定的现象;
+- TYPE-C 数据线 × 1
+- Air780EPM 核心板和 AirLCD_1010 配件板的硬件接线方式为
+
+  - Air780EPM 核心板通过 TYPE-C USB 口供电(核心板正面开关拨到 ON 一端),此种供电方式下,VBAT 引脚为 3.3V,可以直接给 AirLCD_1010 配件板;
+  - 为了演示方便,所以 Air780EPM 核心板上电后直接通过 VBAT 引脚给 AirLCD_1010 配件板供电;
+  - 客户在设计实际项目时,一般来说,需要通过一个 GPIO 来控制 LDO 给配件板供电,这样可以灵活地控制配件板的供电,可以使项目的整体功耗降到最低;
+
+### 5.2 接线配置
+
+#### 5.2.1 显示屏接线
+
+<table>
+<tr>
+<td>Air780EPM 核心板<br/></td><td>AirLCD_1010配件板<br/></td></tr>
+<tr>
+<td>53/LCD_CLK<br/></td><td>SCLK/CLK<br/></td></tr>
+<tr>
+<td>52/LCD_CS<br/></td><td>CS<br/></td></tr>
+<tr>
+<td>49/LCD_RST<br/></td><td>RES/RST<br/></td></tr>
+<tr>
+<td>50/LCD_SDA<br/></td><td>SDA/MOS<br/></td></tr>
+<tr>
+<td>51/LCD_RS<br/></td><td>DC/RS<br/></td></tr>
+<tr>
+<td>22/GPIO1<br/></td><td>BLK<br/></td></tr>
+<tr>
+<td>VBAT<br/></td><td>VCC<br/></td></tr>
+<tr>
+<td>67/I2C1_SCL<br/></td><td>SCL<br/></td></tr>
+<tr>
+<td>66/I2C1_SDA<br/></td><td>SDA<br/></td></tr>
+<tr>
+<td>19/GPIO22<br/></td><td>INT<br/></td></tr>
+</table>
+
+
+### 5.3 实际接线图
+![](https://docs.openLuat.com/cdn/image/Air780EPM_AirLCD_1010_lcd演示接线图.png)
+
+## 六、演示软件环境
+
+### 6.1 开发工具
+
+- [Luatools下载调试工具](https://docs.openluat.com/air780epm/luatos/common/download/) - 固件烧录和代码调试
+
+### 6.2 内核固件
+
+- [点击下载Air780EPM系列最新版本内核固件](https://docs.openluat.com/air780epm/luatos/firmware/version/),demo所使用的是LuatOS-SoC_V2018_Air780EPM 1号固件
+
+### 6.3 字体文件
+- 自定义字体文件:`customer_font_12.bin` 和 `customer_font_22.bin`(和lua脚本文件一起烧录,会自动放置在`/luadb/`目录下)
+- 演示图片文件:`logo.jpg`(和lua脚本文件一起烧录,会自动放置在`/luadb/`目录下)
+- [点击查看自定义字体生成和使用说明](https://docs.openluat.com/osapi/core/lcd/?h=lcd#_6)
+
+## 七、演示核心步骤
+
+### 7.1 硬件准备
+1. 按照硬件接线表连接所有设备
+2. 确保电源连接正确,通过TYPE-C USB口供电
+3. 检查所有接线无误,避免短路
+
+### 7.2 软件配置
+在`main.lua`中选择加载对应的驱动模块:
+
+```lua
+-- 加载显示屏驱动管理功能模块,有以下两种:
+-- 1、使用lcd核心库驱动的lcd_drv.lua
+-- 2、使用exlcd扩展库驱动的exlcd_drv.lua
+-- 根据自己的需求,启用两者中的任何一种都可以
+-- 也可以不启用任何一种,不使用显示屏功能
+lcd_drv = require "lcd_drv"
+-- lcd_drv = require "exlcd_drv"
+
+
+-- 加载触摸面板驱动管理功能模块,有以下两种:
+-- 1、使用tp核心库驱动的tp_drv.lua
+-- 2、使用extp扩展库驱动的extp_drv.lua
+-- 根据自己的需求,启用两者中的任何一种都可以
+-- 也可以不启用任何一种,不使用触摸面板功能
+tp_drv = require "tp_drv"
+-- tp_drv = require "extp_drv"
+
+
+-- 加载字库驱动管理功能模块
+-- 使用外部自定义字体不需要require "customer_font_drv",可以参照customer_font_drv.lua内的使用说明进行创建和加载字体文件
+
+
+-- 加载输入法驱动管理功能模块(正在开发中,后续补充)
+
+
+-- 加载lcd核心库实现的用户界面功能模块
+-- 实现多页面切换、触摸事件分发和界面渲染功能
+-- 包含主页、lcd核心库功能演示页、GTFont演示页和自定义字体演示页
+require "ui_main"
+```
+
+### 7.3 软件烧录
+1. 使用Luatools烧录最新内核固件
+2. 下载并烧录本项目所有脚本文件
+3. 将字体文件和图片文件随脚本文件一起烧录到脚本分区或者单独烧录到文件系统
+4. 设备自动重启后开始运行
+
+### 7.4 功能测试
+
+#### 7.4.1 主页面操作
+1. 设备启动后显示主页面,包含两个功能按钮
+2. 查看系统功能概览信息
+3. 点击各功能按钮进入对应演示页面
+
+#### 7.4.2 LCD演示页面
+1. 查看基本图形绘制示例(点、线、矩形、圆形)
+2. 查看图片显示区域(显示logo图片)
+3. 查看二维码区域(合宙文档二维码)
+4. 查看位图和字体示例
+5. 点击"返回"按钮回到主页
+
+#### 7.4.3 自定义字体页面
+1. 查看外部字体文件显示效果
+2. 查看多颜色文字显示(红色、绿色、蓝色)
+3. 查看字体使用说明和接口信息
+4. 点击"返回"按钮回到主页
+
+### 7.5 预期效果
+
+- **主页面**:正常显示,三个功能按钮响应灵敏
+- **LCD演示页面**:图形绘制清晰,图片和二维码显示正常,颜色示例完整
+- **自定义字体页面**:外部字体加载正确,多颜色文字显示正常
+- **触摸交互**:所有触摸操作响应及时准确,页面切换流畅
+
+### 7.6 故障排除
+
+1. **显示异常**:检查LCD接线是否正确,确认电源供电稳定
+2. **触摸无响应**:检查I2C接线,确认触摸芯片初始化成功
+3. **字体显示异常**:检查所需显示内容是否包含在字体内,确认字体文件路径正确
+4. **图片无法显示**:确认图片文件已正确烧录到指定路径
+5. **系统卡顿**:检查内存使用情况,适当调整刷新频率

+ 60 - 0
module/Air780EPM/accessory_board/AirLCD_1010/lcd/tp_key_drv/extp_drv.lua

@@ -0,0 +1,60 @@
+--[[
+@module  extp_drv
+@summary 扩展触摸面板驱动模块,基于extp扩展库
+@version 1.0
+@date    2025.11.20
+@author  江访
+@usage
+本模块为扩展触摸面板驱动功能模块,主要功能包括:
+1、初始化AirLCD_1010扩展触摸面板;
+2、配置I2C通信接口和触摸参数;
+3、发布触摸事件消息供UI系统处理;
+
+说明:
+成功执行extp_drv.init()之后,在触摸面板上有触摸动作时,在extp扩展库内,默认会发布以下消息:
+1、sys.publish("BASE_TOUCH_EVENT", "SINGLE_TAP", x, y) --单击消息,x和y表示坐标
+2、sys.publish("BASE_TOUCH_EVENT", "LONG_PRESS", x, y) --长按消息,x和y表示坐标
+如果需要发布上下左右滑动、移动、原始数据几种消息,参考extp扩展库中的extp.set_publish_enabled(msg_type, enabled)进行设置
+
+对外接口:
+1、extp_drv.init():初始化扩展触摸面板驱动
+]]
+
+local extp = require "extp"
+
+local extp_drv = {}
+
+--[[
+初始化扩展触摸面板驱动;
+
+@api extp_drv.init()
+@summary 配置并初始化AirLCD_1010扩展触摸面板
+@return boolean 初始化成功返回true,失败返回false
+
+@usage
+-- 初始化扩展触摸面板
+local result = extp_drv.init()
+if result then
+    log.info("扩展触摸面板初始化成功")
+else
+    log.error("扩展触摸面板初始化失败")
+end
+]]
+
+function extp_drv.init()
+    -- 初始化I2C
+    local result = i2c.setup(1, i2c.SLOW)
+
+    if result==0 then
+        log.error("extp_drv.init i2c.setup error")
+        return false
+    end
+
+    result = extp.init({tp_model = "AirLCD_1010", i2c_id = 1, pin_rst = 0xFF, pin_int = 22, w = 320, h = 480})
+
+    log.info("extp.init", result)
+
+    return result
+end
+
+return extp_drv

+ 69 - 0
module/Air780EPM/accessory_board/AirLCD_1010/lcd/tp_key_drv/tp_drv.lua

@@ -0,0 +1,69 @@
+--[[
+@module  tp_drv
+@summary 触摸面板驱动模块,基于tp核心库
+@version 1.0
+@date    2025.11.20
+@author  江访
+@usage
+本模块为触摸面板驱动功能模块,主要功能包括:
+1、初始化GT911触摸控制器;
+2、配置I2C通信接口和触摸回调函数;
+3、发布触摸事件消息供UI系统处理;
+
+对外接口:
+1、tp_drv.init():初始化触摸面板驱动
+]]
+
+local tp_drv = {}
+
+--[[
+触摸事件回调函数;
+
+@local
+@function tp_callback(tp_device, tp_data)
+@userdata tp_device 触摸设备对象
+@table tp_data 触摸数据数组
+@return nil
+]]
+
+local function tp_callback(tp_device, tp_data)
+    if tp_data[1].event == tp.EVENT_DOWN then
+        -- log.info("tp_drv tp_callback", tp_data[1].event, tp_data[1].x, tp_data[1].y)
+        sys.publish("BASE_TOUCH_EVENT", tp_data[1].event, tp_data[1].x, tp_data[1].y)
+    end
+end
+
+--[[
+初始化触摸面板驱动;
+
+@api tp_drv.init()
+@summary 配置并初始化GT911触摸控制器
+@return boolean 初始化成功返回true,失败返回false
+
+@usage
+-- 初始化触摸面板
+local result = tp_drv.init()
+if result then
+    log.info("触摸面板初始化成功")
+else
+    log.error("触摸面板初始化失败")
+end
+]]
+
+function tp_drv.init()
+    -- 初始化I2C
+    local result = i2c.setup(1, i2c.SLOW)
+
+    if result==0 then
+        log.error("tp_drv.init i2c.setup error")
+        return false
+    end
+
+    result = tp.init("gt911", {port = 1, pin_rst = 0xFF, pin_int = 22, w = 320, h = 480}, tp_callback)
+
+    log.info("tp.init", result)
+
+    return result
+end
+
+return tp_drv

+ 137 - 0
module/Air780EPM/accessory_board/AirLCD_1010/lcd/ui/customer_font_page.lua

@@ -0,0 +1,137 @@
+--[[
+@module  customer_font_page
+@summary 自定义字体演示模块,展示外部点阵字体的加载和显示功能
+@version 1.0
+@date    2025.12.8
+@author  江访
+@usage
+本模块为自定义字体演示功能模块,核心业务逻辑为:
+1、展示外部自定义字体文件的加载和显示功能,支持12号和22号字体;
+2、提供多颜色文字显示效果,支持红、绿、蓝三色文字;
+3、显示字体使用说明和设备支持信息;
+4、展示字体使用接口示例和文件路径信息;
+
+注意:当前设备仅支持自定义点阵字体显示中文
+
+本文件的对外接口有3个:
+1、customer_font_page.draw():绘制自定义字体演示页面;
+2、customer_font_page.handle_touch():处理触摸事件;
+3、customer_font_page.on_leave():页面离开时恢复默认字体;
+]]
+local customer_font_page = {}
+
+-- 按钮区域定义
+local back_button = { x1 = 10, y1 = 445, x2 = 50, y2 = 475 }
+
+--[[
+绘制自定义字体演示页面;
+@api customer_font_page.draw()
+@summary 绘制自定义字体演示页面的所有UI元素和字体内容
+@return nil
+]]
+function customer_font_page.draw()
+    lcd.clear()
+
+    -- 设置默认颜色
+    lcd.setColor(0xFFFF, 0x0000)
+
+    -- 显示标题,使用22号自定义字体
+    lcd.setFontFile("/luadb/customer_font_22.bin")
+
+    -- 显示标题区域
+    lcd.fill(0, 0, 320, 40, 0x001F) -- 蓝色标题栏
+    lcd.drawStr(70, 30, "自定义点阵字体演示", 0xFFFF)
+
+    -- 显示演示内容,使用12号自定义字体
+    lcd.setFontFile("/luadb/customer_font_12.bin")
+
+    -- 字体支持说明区域(带背景色)
+    lcd.fill(15, 60, 305, 155, 0xF7DE)          -- 浅灰色背景
+    lcd.drawRectangle(15, 60, 305, 155, 0x8410) -- 边框
+
+    -- 显示字体支持说明
+    lcd.drawStr(30, 80, "当前设备字体支持:", 0x0000)
+    lcd.drawStr(40, 105, "- 仅支持自定义字体", 0x0000)
+    lcd.drawStr(40, 120, "- 不支持内置12号中文", 0x0000)
+    lcd.drawStr(40, 135, "- 仅支持自定义点阵字体", 0x0000)
+
+    lcd.drawLine(20, 165, 300, 165, 0x8410)
+
+    -- 设置22号自定义字体文件显示示例文字
+    lcd.setFontFile("/luadb/customer_font_22.bin")
+
+    -- 示例文字区域(居中显示)
+    lcd.drawStr(100, 200, "上海合宙", 0xF800) -- 红色
+    lcd.drawStr(105, 230, "LuatOS", 0x07E0) -- 绿色
+    lcd.drawStr(95, 260, "演示demo", 0x001F) -- 蓝色
+
+    -- 恢复12号字体显示说明
+    lcd.setFontFile("/luadb/customer_font_12.bin")
+    lcd.setColor(0xFFFF, 0x0000)
+
+    -- 字体文件信息区域
+    lcd.drawLine(20, 280, 300, 280, 0x8410)
+    lcd.drawStr(20, 300, "字体文件信息:", 0x0000)
+    lcd.drawStr(30, 325, "- 12号字体: /luadb/customer_font_12.bin", 0x0000)
+    lcd.drawStr(30, 350, "- 22号字体: /luadb/customer_font_22.bin", 0x0000)
+
+    -- 显示使用接口
+    lcd.drawStr(20, 380, "使用接口:", 0x0000)
+    lcd.drawStr(35, 405, "- lcd.setFontFile(字体路径)", 0x0000)
+    lcd.drawStr(35, 425, "- lcd.drawStr(x, y, 文本, 颜色)", 0x0000)
+
+    -- 底部状态栏
+    -- 绘制返回按钮
+    lcd.setFontFile("/luadb/customer_font_12.bin")
+    lcd.fill(0, 440, 320, 480, 0x001F)     -- 蓝色背景
+    lcd.fill(back_button.x1, back_button.y1, back_button.x2, back_button.y2, 0xC618)
+    lcd.drawStr(20, 465, "返回", 0x0000)
+    lcd.drawStr(180, 465, "触摸返回按钮回到主页", 0xFFFF)
+end
+
+--[[
+处理触摸事件;
+@api customer_font_page.handle_touch(x, y, switch_page)
+@summary 处理自定义字体页面触摸事件
+@number x 触摸点X坐标
+@number y 触摸点Y坐标
+@function switch_page 页面切换回调函数
+@return bool 事件处理成功返回true,否则返回false
+]]
+function customer_font_page.handle_touch(x, y, switch_page)
+    log.info("customer_font_page.handle_touch", "x:", x, "y:", y)
+
+    -- 检查返回按钮
+    if x >= back_button.x1 and x <= back_button.x2 and
+        y >= back_button.y1 and y <= back_button.y2 then
+        switch_page("home")
+        return true
+    end
+
+    return false
+end
+
+--[[
+页面进入时初始化;
+@api customer_font_page.on_enter()
+@summary 页面进入时设置字体
+@return nil
+]]
+function customer_font_page.on_enter()
+    -- 使用12号自定义字体
+    lcd.setFontFile("/luadb/customer_font_12.bin")
+end
+
+--[[
+页面离开时恢复默认字体;
+@api customer_font_page.on_leave()
+@summary 恢复12号自定义字体
+@return nil
+]]
+function customer_font_page.on_leave()
+    -- 恢复使用12号自定义字体
+    lcd.setFontFile("/luadb/customer_font_12.bin")
+    log.info("customer_font_page", "已恢复12号自定义字体")
+end
+
+return customer_font_page

+ 120 - 0
module/Air780EPM/accessory_board/AirLCD_1010/lcd/ui/home_page.lua

@@ -0,0 +1,120 @@
+--[[
+@module  home_page
+@summary 主页模块,提供应用入口和导航功能
+@version 1.0
+@date    2025.12.8
+@author  江访
+@usage
+本模块为主页模块,核心业务逻辑为:
+1、提供应用入口和导航功能,支持LCD演示和自定义字体演示两个功能选项;
+2、显示系统标题和操作提示信息;
+3、处理主页面的触摸事件;
+
+注意:使用自定义字体显示中文
+
+本文件的对外接口有3个:
+1、home_page.draw():绘制主页面UI;
+2、home_page.handle_touch():处理触摸事件;
+3、home_page.on_enter():页面进入时重置状态;
+]]
+
+local home_page = {}
+
+-- 按钮区域定义(调整为2个按钮)
+local buttons = {
+    { name = "lcd", text = "LCD演示", x1 = 40, y1 = 150, x2 = 140, y2 = 240, color = 0x001F }, -- 蓝色
+    { name = "customer_font", text = "自定义字体", x1 = 180, y1 = 150, x2 = 280, y2 = 240, color = 0xF800 } -- 红色
+}
+
+--[[
+绘制主页界面;
+@api home_page.draw()
+@summary 绘制主页面所有UI元素
+@return nil
+]]
+function home_page.draw()
+    lcd.clear()
+
+    -- 显示标题,使用22号自定义字体
+    lcd.setColor(0xFFFF, 0x0000)
+    lcd.setFontFile("/luadb/customer_font_22.bin")
+
+    -- 顶部标题栏
+    lcd.fill(0, 0, 320, 60, 0x001F) -- 蓝色背景
+    lcd.drawStr(80, 39, "合宙LCD演示系统", 0xFFFF) -- 白色文字
+
+
+    -- 显示演示内容,使用12号自定义字体
+    lcd.setFontFile("/luadb/customer_font_12.bin")
+
+    -- 绘制所有按钮
+    for i, btn in ipairs(buttons) do
+        local color = btn.color
+
+        -- 绘制按钮背景
+        lcd.fill(btn.x1, btn.y1, btn.x2, btn.y2, color)
+
+        -- 绘制按钮文字(居中)
+        local text_x = btn.x1 + (btn.x2 - btn.x1) / 2 - 25
+        local text_y = btn.y1 + (btn.y2 - btn.y1) / 2
+
+        lcd.drawStr(text_x, text_y, btn.text, 0xFFFF)
+
+        -- 绘制按钮边框
+        lcd.drawRectangle(btn.x1, btn.y1, btn.x2, btn.y2, 0x0000)
+    end
+
+    -- 底部状态栏
+    lcd.fill(0, 380, 320, 480, 0x001F) -- 蓝色背景
+    lcd.drawStr(20, 405, "当前页面: 主页", 0xFFFF)
+    lcd.drawStr(30, 430, "- 触摸按钮进入功能演示", 0xFFFF)
+    lcd.drawStr(30, 450, "- LCD演示: 显示图形绘制功能", 0xFFFF)
+    lcd.drawStr(30, 470, "- 自定义字体: 显示外部字体", 0xFFFF)
+end
+
+--[[
+处理触摸事件;
+@api home_page.handle_touch(x, y, switch_page)
+@summary 处理主页触摸事件
+@number x 触摸点X坐标
+@number y 触摸点Y坐标
+@function switch_page 页面切换回调函数
+@return bool 事件处理成功返回true,否则返回false
+]]
+function home_page.handle_touch(x, y, switch_page)
+    log.info("home_page.handle_touch", "x:", x, "y:", y)
+
+    -- 检查触摸是否在按钮区域内
+    for i, btn in ipairs(buttons) do
+        if x >= btn.x1 and x <= btn.x2 and
+            y >= btn.y1 and y <= btn.y2 then
+            -- 切换到选中的页面
+            switch_page(btn.name)
+            return true
+        end
+    end
+
+    return false
+end
+
+--[[
+页面进入时重置状态;
+@api home_page.on_enter()
+@summary 重置页面状态
+@return nil
+]]
+function home_page.on_enter()
+    -- 可以在这里执行初始化操作
+end
+
+--[[
+页面离开时执行清理操作;
+@api home_page.on_leave()
+@summary 页面离开时清理
+@return nil
+]]
+function home_page.on_leave()
+    -- 可以在这里执行清理操作
+end
+
+return home_page

+ 174 - 0
module/Air780EPM/accessory_board/AirLCD_1010/lcd/ui/lcd_page.lua

@@ -0,0 +1,174 @@
+--[[
+@module  lcd_page
+@summary LCD图形绘制演示模块
+@version 1.0
+@date    2025.11.20
+@author  江访
+@usage
+本模块为LCD图形绘制演示功能模块,主要功能包括:
+1、展示LCD核心库的基本图形绘制功能;
+2、演示点、线、矩形、圆形等基本图形绘制;
+3、显示图片和二维码生成功能;
+4、提供颜色示例展示;
+
+对外接口:
+1、lcd_page.draw():绘制LCD演示页面
+2、lcd_page.handle_touch():处理LCD页面触摸事件
+]]
+local lcd_page = {}
+
+-- 按钮区域定义
+local back_button = { x1 = 10, y1 = 445, x2 = 50, y2 = 475 }
+
+--[[
+绘制LCD演示页面;
+
+@api lcd_page.draw()
+@summary 绘制LCD演示页面的所有图形和UI元素
+@return nil
+]]
+function lcd_page.draw()
+    lcd.clear()
+    -- 显示标题,使用22号自定义字体
+    lcd.setFontFile("/luadb/customer_font_22.bin")
+
+    -- 设置默认颜色
+    lcd.setColor(0xFFFF, 0x0000)
+
+    -- 显示标题区域
+    lcd.fill(0, 0, 320, 40, 0x001F) -- 蓝色标题栏
+    lcd.drawStr(80, 30, "LCD核心库演示", 0xFFFF)
+    lcd.drawLine(20, 55, 300, 55, 0x8410)
+
+
+    -- 显示演示内容,使用12号自定义字体
+    -- === 第一区域:基本图形 ===
+    lcd.setFontFile("/luadb/customer_font_12.bin")
+    lcd.drawStr(20, 75, "基本图形绘制:", 0x0000)
+
+    -- 绘制点
+    lcd.drawStr(30, 98, "点:", 0x0000)
+    lcd.drawPoint(55, 98, 0xFCC0)
+    lcd.drawPoint(65, 98, 0x07E0)
+    lcd.drawPoint(75, 98, 0x001F)
+
+    -- 绘制线
+    lcd.drawStr(30, 125, "线:", 0x0000)
+    lcd.drawLine(55, 113, 115, 113, 0xFCC0)
+    lcd.drawLine(55, 118, 115, 123, 0x07E0)
+    lcd.drawLine(55, 123, 115, 118, 0x001F)
+
+    -- 绘制矩形(预留右侧空间)
+    lcd.drawStr(30, 160, "矩形:", 0x0000)
+    lcd.drawRectangle(65, 138, 105, 163, 0x7296)
+    lcd.fill(120, 138, 160, 163, 0x07E0)
+
+    -- 绘制圆(预留右侧空间)
+    lcd.drawStr(30, 200, "圆形:", 0x0000)
+    lcd.drawCircle(90, 193, 15, 0x001F)
+    lcd.drawCircle(130, 193, 15, 0xFCC0)
+
+    lcd.drawLine(170, 70, 170, 300, 0x8410) -- 垂直分隔线
+
+    -- === 第二区域:图片和二维码 ===
+    lcd.drawStr(180, 75, "图片/二维码:", 0x0000)
+
+    -- 图片显示区域 (80x80)
+    lcd.drawStr(180, 100, "LOGO", 0x0000)
+    lcd.drawRectangle(200, 110, 290, 200, 0x0000)
+    lcd.showImage(205, 115, "/luadb/logo.jpg")
+
+    -- 二维码区域 (80x80)
+    lcd.drawStr(180, 215, "二维码", 0x0000)
+    lcd.drawRectangle(200, 225, 290, 308, 0x0000)
+    lcd.drawQrcode(205, 226, "https://docs.openluat.com/air780epm/", 80)
+
+    lcd.drawLine(20, 325, 300, 325, 0x8410)
+
+    -- === 第三区域:位图 ===
+    lcd.drawLine(20, 235, 160, 235, 0x8410)
+
+    -- 位图区域
+    lcd.drawStr(20, 255, "位图示例:", 0x0000)
+
+    -- 绘制位图
+    local x_start = 30
+    local y_start = 265
+    lcd.drawXbm(x_start, y_start, 16, 16, string.char(
+        0x00, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x3F, 0x80, 0x00,
+        0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0xFE, 0x7F, 0x00, 0x00))
+
+    lcd.drawXbm(x_start + 20, y_start, 16, 16, string.char(
+        0x00, 0x00, 0x80, 0x00, 0xC4, 0x7F, 0x28, 0x00, 0x10, 0x00, 0xD0, 0x3F, 0x42, 0x20, 0x44, 0x22,
+        0x40, 0x24, 0xF0, 0xFF, 0x24, 0x20, 0x24, 0x22, 0x24, 0x20, 0xE2, 0x7F, 0x02, 0x20, 0x02, 0x1E))
+
+    lcd.drawXbm(x_start, y_start + 20, 16, 16, string.char(
+        0x00, 0x00, 0x00, 0x01, 0x80, 0x01, 0x40, 0x02, 0x20, 0x04, 0x18, 0x18, 0xF4, 0x6F, 0x02, 0x00,
+        0x00, 0x00, 0xF8, 0x1F, 0x08, 0x10, 0x08, 0x10, 0x08, 0x10, 0x08, 0x10, 0xF8, 0x1F, 0x08, 0x10))
+
+    lcd.drawXbm(x_start + 20, y_start + 20, 16, 16, string.char(
+        0x00, 0x00, 0x80, 0x00, 0x00, 0x01, 0xFE, 0x7F, 0x02, 0x40, 0x02, 0x40, 0x00, 0x01, 0xFC, 0x3F,
+        0x04, 0x21, 0x04, 0x21, 0xFC, 0x3F, 0x04, 0x21, 0x04, 0x21, 0x04, 0x21, 0xFC, 0x3F, 0x04, 0x20))
+
+    -- === 第四区域:字体 ===
+    lcd.drawStr(20, 345, "字体示例:", 0x0000)
+
+    -- 12号字体
+    lcd.setFontFile("/luadb/customer_font_12.bin")
+    lcd.drawStr(20, 368, "12px字体示例", 0x0000)
+
+    -- 22号字体
+    lcd.setFontFile("/luadb/customer_font_22.bin")
+    lcd.drawStr(20, 398, "22px字体示例", 0x0000)
+
+    -- 底部状态栏
+    -- 绘制返回按钮
+    lcd.setFontFile("/luadb/customer_font_12.bin")
+    lcd.fill(0, 440, 320, 480, 0x001F) -- 蓝色背景
+    lcd.fill(back_button.x1, back_button.y1, back_button.x2, back_button.y2, 0xC618)
+    lcd.drawStr(20, 465, "返回", 0x0000)
+    lcd.drawStr(180, 465, "触摸返回按钮回到主页", 0xFFFF)
+end
+
+--[[
+处理LCD页面触摸事件;
+
+@api lcd_page.handle_touch(x, y, switch_page)
+@number x 触摸点X坐标,范围0-319
+@number y 触摸点Y坐标,范围0-479
+@function switch_page 页面切换回调函数
+@return boolean 事件处理成功返回true,否则返回false
+]]
+function lcd_page.handle_touch(x, y, switch_page)
+    -- 检查返回按钮
+    if x >= back_button.x1 and x <= back_button.x2 and
+        y >= back_button.y1 and y <= back_button.y2 then
+        switch_page("home")
+        return true
+    end
+    return false
+end
+
+--[[
+页面进入时初始化;
+@api lcd_page.on_enter()
+@summary 页面进入时设置字体
+@return nil
+]]
+function lcd_page.on_enter()
+    -- 使用12号自定义字体
+    lcd.setFontFile("/luadb/customer_font_12.bin")
+end
+
+--[[
+页面离开时执行清理操作;
+@api lcd_page.on_leave()
+@summary 页面离开时恢复默认字体
+@return nil
+]]
+function lcd_page.on_leave()
+    -- 恢复使用12号自定义字体
+    lcd.setFontFile("/luadb/customer_font_12.bin")
+end
+
+return lcd_page

+ 142 - 0
module/Air780EPM/accessory_board/AirLCD_1010/lcd/ui/ui_main.lua

@@ -0,0 +1,142 @@
+--[[
+@module  ui_main
+@summary 用户界面主控模块,负责页面管理和事件分发
+@version 1.0
+@date    2025.11.20
+@author  江访
+@usage
+本模块为核心UI控制模块,主要功能包括:
+1、初始化LCD显示和触摸面板驱动;
+2、管理多个页面之间的切换逻辑;
+3、分发触摸事件到各个页面处理;
+4、维护页面状态和生命周期管理;
+
+对外接口:
+1、ui_main():主UI任务入口函数
+2、switch_page():页面切换函数
+3、handle_touch_event():触摸事件处理函数
+]]
+
+
+-- 加载子页面
+local home_page = require "home_page"
+local lcd_page = require "lcd_page"
+local customer_font_page = require "customer_font_page"
+
+-- 当前页面状态
+local current_page = "home"
+local last_page = ""
+frame_time = 20*1000
+
+--[[
+切换页面;
+
+@api switch_page(new_page)
+@string new_page 目标页面名称
+@valid_values "home", "lcd", "customer_font"
+@return nil
+
+@usage
+-- 切换到LCD演示页面
+switch_page("lcd")
+]]
+local function switch_page(new_page)
+    -- 调用旧页面的退出函数(如果存在)
+    if current_page == "home" and home_page.on_leave then
+        home_page.on_leave()
+    elseif current_page == "lcd" and lcd_page.on_leave then
+        lcd_page.on_leave()
+    elseif current_page == "customer_font" and customer_font_page.on_leave then
+        customer_font_page.on_leave()
+    end
+
+    last_page = current_page
+    current_page = new_page
+
+    -- 调用新页面的进入函数(如果存在)
+    if new_page == "home" and home_page.on_enter then
+        home_page.on_enter()
+    elseif new_page == "lcd" and lcd_page.on_enter then
+        lcd_page.on_enter()
+    elseif new_page == "customer_font" and customer_font_page.on_enter then
+        customer_font_page.on_enter()
+    end
+
+    log.info("ui_main", "切换到页面:", current_page)
+end
+
+--[[
+处理触摸事件;
+
+@api handle_touch_event(event, x, y)
+@number event 触摸事件类型
+@valid_values tp.EVENT_DOWN, tp.EVENT_MOVE, tp.EVENT_UP
+@number x 触摸点X坐标,范围0-319
+@number y 触摸点Y坐标,范围0-479
+@return boolean 事件处理成功返回true,否则返回false
+]]
+-- 处理触摸事件
+local function handle_touch_event(event, x, y)
+    log.info("触摸事件", "event:", event, "x:", x, "y:", y)
+
+    if event then -- 只在抬起时处理点击
+        if current_page == "home" then
+            return home_page.handle_touch(x, y, switch_page)
+        elseif current_page == "lcd" then
+            return lcd_page.handle_touch(x, y, switch_page)
+        elseif current_page == "customer_font" then
+            return customer_font_page.handle_touch(x, y, switch_page)
+        end
+    end
+    return false
+end
+
+--[[
+用户界面主任务;
+
+@api ui_main()
+@summary 初始化显示和触摸驱动,启动UI主循环
+@return nil
+]]
+local function ui_main()
+    if not lcd_drv.init() then
+        log.error("ui_main", "显示初始化失败")
+        return
+    end
+
+    if not tp_drv.init() then
+        log.error("ui_main", "触摸初始化失败")
+        return
+    end
+
+    -- 默认使用12号自定义字体
+    lcd.setFontFile("/luadb/customer_font_12.bin")
+
+    -- 调用主页的进入函数
+    if home_page.on_enter then
+        home_page.on_enter()
+    end
+
+    while true do
+        -- 根据当前页面绘制内容
+        if current_page == "home" then
+            home_page.draw()
+        elseif current_page == "lcd" then
+            lcd_page.draw()
+        elseif current_page == "customer_font" then
+            customer_font_page.draw()
+        end
+
+        lcd.flush()
+
+
+        -- 等待触摸事件
+        local result, event, x, y = sys.waitUntil("BASE_TOUCH_EVENT",frame_time)
+        if result then
+            handle_touch_event(event, x, y)
+        end
+    end
+end
+
+
+sys.taskInit(ui_main)

+ 188 - 0
module/Air780EPM/demo/accessory_board/AirRC522_1000/AirRC522_1000.lua

@@ -0,0 +1,188 @@
+--[[
+@module  AirRC522_1000
+@summary AirRC522_1000测试功能模块
+@version 1.0
+@date    2025.07.23
+@author  马亚丹
+@usage
+本demo演示的功能为:使用Air780EPM核心板通过SPI实现对RC522的操作,演示读写数据等操作。
+以 Air780EPM核心板为例, 接线如下:
+Air780EPM核心板            AirRC522_1000
+GND(任意)                    GND
+3V3                          3.3V
+83/SPI0CS                    SDA
+86/SPI0CLK                   SCK
+85/SPI0MOSI                  MOSI
+84/SPI0MISO                  MISO
+19/GPIO22(可选任意空闲IO)     RST
+
+核心逻辑:
+1. 初始化并启用spi,如果初始化失败,退出程序
+2. 初始化RC522模块,如果初始化失败,退出程序
+3. 循环检测卡片。
+4. 向卡片指定块号写入数据,并读取数据验证一致性
+5. 读取卡片所有数据
+]]
+
+
+
+rc522 = require "rc522"
+
+
+-- 硬件配置参数(Air780EPM适配)
+local RC522_CONFIG = {
+    spi_id = 0,                 -- SPI通道
+    cs_pin = 8,                 -- 片选引脚
+    rst_pin = 22,               -- 复位引脚
+    spi_baud = 2 * 1000 * 1000, -- SPI波特率
+}
+
+-- 全局变量(模块内可见)
+local spi_dev = nil -- SPI设备对象
+
+-- 1. 初始化SPI接口
+local function init_spi()
+    log.info("RC522", "初始化SPI接口")
+    -- 配置SPI参数:模式0,8位数据,高位在前
+    spi_dev = spi.setup(
+        RC522_CONFIG.spi_id,
+        RC522_CONFIG.cs_pin,
+        0,          -- 极性0
+        0,          -- 相位0
+        8,          -- 数据位
+        RC522_CONFIG.spi_baud,
+        spi.MSB,    -- 高位优先
+        spi.master, --主模式
+        spi.half    --半双工
+    )
+
+    if not spi_dev then
+        log.error("RC522", "SPI初始化失败")
+        return false
+    end
+    log.info("RC522", "SPI初始化成功")
+    return true
+end
+
+-- 2. 初始化RC522模块
+local function init_rc522()
+    log.info("RC522", "初始化RC522传感器")
+    -- 初始化RC522硬件(SPI ID、CS引脚、RST引脚)
+    local init_ok = rc522.init(
+        RC522_CONFIG.spi_id,
+        RC522_CONFIG.cs_pin,
+        RC522_CONFIG.rst_pin
+    )
+
+    if not init_ok then
+        log.error("RC522", "传感器初始化失败")
+        return false
+    end
+    log.info("RC522", "传感器初始化成功")
+    return true
+end
+
+-- 3. 写数据
+local function write_ic_card(block, data)
+    -- 步骤1:验证数据长度
+    if #data > 16 then
+        log.error("RC522", "数据过长(最大16字节)")
+        return false
+    end
+
+    -- 步骤2:写入数据块
+    local write_ok = rc522.write_datablock(block, data)
+    if not write_ok then
+        log.error("RC522", "数据写入失败,块号:", block)
+        return false
+    end
+
+    log.info("RC522", "数据写入成功,块号:", block, "写入数据是:", string.char(table.unpack(data)):toHex())
+    return true
+end
+
+-- 4. 检测并读取IC卡数据
+local function read_ic_card()
+    -- 检测是否有卡片靠近
+    local status, array_id = rc522.request(rc522.reqall)
+    if not status then
+        log.info("RC522", "未检测到卡片")
+        return false
+    end
+    log.info("RC522", "检测到卡片,类型:", array_id:toHex())
+
+    -- 防冲突检测,获取卡片唯一ID
+    local status, card_uid = rc522.anticoll(array_id)
+    if not status then
+        log.error("RC522", "防冲突检测失败")
+        return false
+    end
+    log.info("RC522", "卡片UID:", card_uid:toHex())
+
+    -- -- 选择卡片,激活卡片进行后续操作
+    local select_ok = rc522.select(card_uid)
+    if not select_ok then
+        log.error("RC522", "卡片选择失败")
+        return false
+    end
+
+    -- 写数据测试
+    local TEST_BLOCK = 9
+    -- 待写入的数据
+    local TEST_DATA = { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
+    if write_ic_card(TEST_BLOCK, TEST_DATA) then
+        -- 读数据验证
+        local read_ok, read_data = rc522.read_datablock(TEST_BLOCK)
+
+        if read_ok and read_data == string.char(table.unpack(TEST_DATA)) then
+            log.info("RC522", "写入验证成功,数据是:", read_data:toHex())
+        else
+            log.warn("RC522", "写入验证失败,实际读取的数据是:", read_data:toHex())
+        end
+    end
+
+    -- 读取卡片数据块(0-63块)
+    log.info("RC522", "开始读取卡片数据...")
+    for block = 0, 63 do
+        local read_ok, data = rc522.read_datablock(block)
+        if read_ok and data then
+            log.info(string.format("块[%d]", block), data:toHex())
+        else
+            log.warn(string.format("块[%d]读取失败", block))
+        end
+        --每读取完一个块等待20ms(时间可按需修改)
+        sys.wait(20)
+    end
+
+    -- 停止卡片操作
+    rc522.halt()
+    return true
+end
+-- 5. 关闭SPI设备,成功返回0
+local function spi_close_func()
+    log.info("关闭spi", spi.close(RC522_CONFIG.spi_id))
+end
+
+-- 6. 主测试任务
+local function rc522_main_test()
+    -- 初始化硬件
+    if not init_spi() then
+        spi_close_func()
+        return
+    end
+    if not init_rc522() then
+        spi_close_func()
+        return
+    end
+
+    -- 循环检测卡片
+    log.info("RC522", "开始检测卡片")
+    while true do
+        read_ic_card()
+        -- 循环检测间隔:2s
+        sys.wait(2000)
+    end
+end
+
+-- 启动主任务
+sys.taskInit(rc522_main_test)

+ 73 - 0
module/Air780EPM/demo/accessory_board/AirRC522_1000/main.lua

@@ -0,0 +1,73 @@
+--[[
+@module  main
+@summary LuatOS用户应用脚本文件入口,总体调度应用逻辑 
+@version 001.000.000
+@date    2025.9.05
+@author  马亚丹
+@usage
+本demo是使用Air780EPM核心板挂载合宙AirRC522_1000配件板,来演示rc522的功能使用方法,代码脚本如下:
+1.rc522.lua :rc522扩展库文件,在AirRC522_1000.lua功能模块中require使用  
+2.AirRC522_1000.lua:功能演示核心文件,在main.lua中加载运行,详细逻辑请看AirRC522_1000.lua 文件
+]]
+
+
+--[[
+必须定义PROJECT和VERSION变量,Luatools工具会用到这两个变量,远程升级功能也会用到这两个变量
+PROJECT:项目名,ascii string类型
+        可以随便定义,只要不使用,就行
+VERSION:项目版本号,ascii string类型
+        如果使用合宙iot.openluat.com进行远程升级,必须按照"XXX.YYY.ZZZ"三段格式定义:
+            X、Y、Z各表示1位数字,三个X表示的数字可以相同,也可以不同,同理三个Y和三个Z表示的数字也是可以相同,可以不同
+            因为历史原因,YYY这三位数字必须存在,但是没有任何用处,可以一直写为000
+        如果不使用合宙iot.openluat.com进行远程升级,根据自己项目的需求,自定义格式即可
+]]
+
+
+PROJECT = "Air780EPM_rc522_DEMO"
+VERSION = "001.000.000"
+
+
+-- 在日志中打印项目名和项目版本号
+log.info("main", PROJECT, VERSION)
+
+
+-- 如果内核固件支持wdt看门狗功能,此处对看门狗进行初始化和定时喂狗处理
+-- 如果脚本程序死循环卡死,就会无法及时喂狗,最终会自动重启
+if wdt then
+    --配置喂狗超时时间为9秒钟
+    wdt.init(9000)
+    --启动一个循环定时器,每隔3秒钟喂一次狗
+    sys.timerLoopStart(wdt.feed, 3000)
+end
+
+
+-- 如果内核固件支持errDump功能,此处进行配置,【强烈建议打开此处的注释】
+-- 因为此功能模块可以记录并且上传脚本在运行过程中出现的语法错误或者其他自定义的错误信息,可以初步分析一些设备运行异常的问题
+-- 以下代码是最基本的用法,更复杂的用法可以详细阅读API说明文档
+-- 启动errDump日志存储并且上传功能,600秒上传一次
+-- if errDump then
+--     errDump.config(true, 600)
+-- end
+
+
+-- 使用LuatOS开发的任何一个项目,都强烈建议使用远程升级FOTA功能
+-- 可以使用合宙的iot.openluat.com平台进行远程升级
+-- 也可以使用客户自己搭建的平台进行远程升级
+-- 远程升级的详细用法,可以参考fota的demo进行使用
+
+
+-- 启动一个循环定时器
+-- 每隔3秒钟打印一次总内存,实时的已使用内存,历史最高的已使用内存情况
+-- 方便分析内存使用是否有异常
+-- sys.timerLoopStart(function()
+--     log.info("mem.lua", rtos.meminfo())
+--     log.info("mem.sys", rtos.meminfo("sys"))
+-- end, 3000)
+
+-- 加载AirRC522_1000功能模块
+require "AirRC522_1000"
+
+-- 用户代码已结束---------------------------------------------
+-- 结尾总是这一句
+sys.run()
+-- sys.run()之后不要加任何语句!!!!!因为添加的任何语句都不会被执行

+ 646 - 0
module/Air780EPM/demo/accessory_board/AirRC522_1000/rc522.lua

@@ -0,0 +1,646 @@
+--[[
+@module rc522
+@summary rc522 非接触式读写卡驱动
+@version 1.0
+@date    2022.06.14
+@author  Dozingfiretruck
+@usage
+--注意:因使用了sys.wait()所有api需要在task中使用
+-- 用法实例
+local rc522 = require "rc522"
+local function rc522_test()
+    spi_rc522 = spi.setup(0,nil,0,0,8,10*1000*1000,spi.MSB, spi.master,spi.half)
+    rc522.init(0,12,16)
+    wdata = {0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}
+    while 1 do
+        rc522.write_datablock(8,wdata)
+        for i=0,63 do
+            local a,b = rc522.read_datablock(i)
+            if a then
+                log.info("read",i,b:toHex())
+            end
+        end
+        sys.wait(500)
+    end
+end)
+sys.taskInit(rc522_test)
+]]
+
+
+local rc522                  = {}
+
+local rc522_spi, rc522_rst, rc522_irq, rc522_cs
+
+---器件所用地址
+local rc522_idle             = 0x00 --取消当前命令
+local rc522_authent          = 0x0E --验证密钥
+local rc522_receive          = 0x08 --接收数据
+local rc522_transmit         = 0x04 --发送数据
+local rc522_transceive       = 0x0C --发送并接收数据
+local rc522_resetphase       = 0x0F --复位
+local rc522_calccrc          = 0x03 --CRC计算
+
+--Mifare_One卡片命令字
+rc522.reqidl                 = 0x26 --寻天线区内未进入休眠状态
+rc522.reqall                 = 0x52 --寻天线区内全部卡
+local rc522_anticoll1        = 0x93 --防冲撞
+local rc522_anticoll2        = 0x95 --防冲撞
+local rc522_authent1a        = 0x60 --验证A密钥
+local rc522_authent1b        = 0x61 --验证B密钥
+local rc522_read             = 0x30 --读块
+local rc522_write            = 0xA0 --写块
+local rc522_decrement        = 0xC0 --扣款
+local rc522_increment        = 0xC1 --充值
+local rc522_restore          = 0xC2 --调块数据到缓冲区
+local rc522_transfer         = 0xB0 --保存缓冲区中数据
+local rc522_halt             = 0x50 --休眠
+
+--MF522寄存器定义
+-- PAGE 0
+local rc522_rfu00            = 0x00 --保留为将来之用
+local rc522_com_mand         = 0x01 --启动和停止命令的执行
+local rc522_com_ie           = 0x02 --中断请求传递的使能和禁能控制位
+local rc522_divl_en          = 0x03 --中断请求传递的使能和禁能控制位
+local rc522_com_irq          = 0x04 --包含中断请求标志
+local rc522_div_irq          = 0x05 --包含中断请求标志
+local rc522_error            = 0x06 --错误标志,指示执行的上个命令的错误状态
+local rc522_status1          = 0x07 --包含通信的状态标志
+local rc522_status2          = 0x08 --包含接收器和发送器的状态标志
+local rc522_fifo_data        = 0x09 --64 字节 FIFO 缓冲区的输入和输出
+local rc522_fifo_level       = 0x0A --指示 FIFO 中存储的字节数
+local rc522_water_level      = 0x0B --定义 FIFO 下溢和上溢报警的 FIFO 深度
+local rc522_control          = 0x0C --不同的控制寄存器
+local rc522_bit_framing      = 0x0D --面向位的帧的调节
+local rc522_coll             = 0x0E --RF 接口上检测到的第一个位冲突的位的位置
+local rc522_rfu0f            = 0x0F --保留为将来之用
+-- PAGE 1
+local RFU10                  = 0x10 --保留为将来之用
+local rc522_mode             = 0x11 --定义发送和接收的常用模式
+local rc522_tx_mode          = 0x12 --定义发送过程的数据传输速率
+local rc522_rx_mode          = 0x13 --定义接收过程中的数据传输速率
+local rc522_tx_control       = 0x14 --控制天线驱动器管脚 TX1 和 TX2 的逻辑特性
+local rc522_tx_ayto          = 0x15 --控制天线驱动器的设置
+local rc522_tx_sel           = 0x16 --选择天线驱动器的内部源
+local rc522_rx_sel           = 0x17 --选择内部的接收器设置
+local rc522_rx_threshold     = 0x18 --选择位译码器的阈值
+local rc522_demod            = 0x19 --定义解调器的设置
+local rc522_rfu1a            = 0x1A --保留为将来之用
+local rc522_rfu1b            = 0x1B --保留为将来之用
+local rc522_mifare           = 0x1C --控制 ISO 14443/MIFARE 模式中 106kbit/s 的通信
+local rc522_rfu1d            = 0x1D --保留为将来之用
+local rc522_rfu1e            = 0x1E --保留为将来之用
+local rc522_serial_speed     = 0x1F --选择串行 UART 接口的速率
+-- PAGE 2
+local rc522_rfu20            = 0x20 --保留为将来之用
+local rc522_crcresult_m      = 0x21 --显示 CRC 计算的实际 MSB 值
+local rc522_crcresult_l      = 0x22 --显示 CRC 计算的实际 LSB 值
+local rc522_rfu23            = 0x23 --保留为将来之用
+local rc522_mod_width        = 0x24 --控制 ModWidth 的设置
+local rc522_rfu25            = 0x25 --保留为将来之用
+local rc522_rfcfg            = 0x26 --配置接收器增益
+local rc522_gsn              = 0x27 --选择天线驱动器管脚 TX1 和 TX2 的调制电导
+local rc522_cwgscfg          = 0x28 --选择天线驱动器管脚 TX1 和 TX2 的调制电导
+local rc522_modgscfg         = 0x29 --选择天线驱动器管脚 TX1 和 TX2 的调制电导
+local rc522_tmode            = 0x2A --定义内部定时器的设置
+local rc522_tprescaler       = 0x2B --定义内部定时器的设置
+local rc522_tpreload_h       = 0x2C --描述 16 位长的定时器重装值
+local rc522_tpreload_l       = 0x2D --描述 16 位长的定时器重装值
+local rc522_tcounter_value_h = 0x2E --描述 16 位长的定时器重装值
+local rc522_tcounter_value_l = 0x2F --描述 16 位长的定时器重装值
+-- PAGE 3
+local rc522_rfu30            = 0x30 --保留为将来之用
+local rc522_testsel1         = 0x31 --常用测试信号的配置
+local rc522_testsel2         = 0x32 --常用测试信号的配置和 PRBS 控制
+local rc522_testpin_en       = 0x33 --D1-D7 输出驱动器的使能管脚(注:仅用于串行接口)
+local rc522_testpin_value    = 0x34 --定义 D1-D7 用作 I/O 总线时的值
+local rc522_testbus          = 0x35 --显示内部测试总线的状态
+local rc522_autotest         = 0x36 --控制数字自测试
+local rc522_version          = 0x37 --显示版本
+local rc522_analogtest       = 0x38 --控制管脚 AUX1 和 AUX2
+local rc522_testadc1         = 0x39 --定义 TestDAC1 的测试值
+local rc522_testadc2         = 0x3A --定义 TestDAC2 的测试值
+local rc522_testadc          = 0x3B --显示 ADC I 和 Q 通道的实际值
+local rc522_rfu3c            = 0x3C --保留用于产品测试
+local rc522_rfu3d            = 0x3D --保留用于产品测试
+local rc522_rfu3e            = 0x3E --保留用于产品测试
+local rc522_rfu3f            = 0x3F --保留用于产品测试
+
+local Key_A                  = string.char(0xff, 0xff, 0xff, 0xff, 0xff, 0xff)
+local Key_B                  = string.char(0xff, 0xff, 0xff, 0xff, 0xff, 0xff)
+
+--[[
+写rc522寄存器
+@api rc522.set_bit_mask(address, value)
+@number address 地址
+@number value    值
+@usage
+write_rawrc(rc522_bit_framing,0x07)
+]]
+local function write_rawrc(address, value)
+    rc522_cs(0)
+    local data = string.char((address << 1) & 0x7E) .. string.char(value)
+    spi.send(rc522_spi, data)
+    -- rc522_spi:send(data)
+    rc522_cs(1)
+end
+
+--[[
+读rc522寄存器
+@api rc522.read_rawrc(address)
+@number address 地址
+@return number 寄存器值
+@usage
+local n = read_rawrc(rc522_com_irq)
+]]
+local function read_rawrc(address)
+    rc522_cs(0)
+    local data = string.char(((address << 1) & 0x7E)|0x80)
+    spi.send(rc522_spi, data)
+    local val = spi.recv(rc522_spi, 1)
+    -- rc522_spi:send(data)
+    -- local val = rc522_spi:recv(1)
+    rc522_cs(1)
+    return string.byte(val)
+end
+
+--[[
+对rc522寄存器置位
+@api rc522.set_bit_mask(address, mask)
+@number address 地址
+@number mask    置位值
+@usage
+rc522.set_bit_mask (rc522_fifo_level, 0x80)	
+]]
+function rc522.set_bit_mask(address, mask)
+    local current = read_rawrc(address)
+    write_rawrc(address, bit.bor(current, mask))
+end
+
+--[[
+对rc522寄存器清位
+@api rc522.clear_bit_mask(address, mask)
+@number address 地址
+@number mask    清位值
+@usage
+rc522.clear_bit_mask(rc522_com_irq, 0x80 )
+]]
+function rc522.clear_bit_mask(address, mask)
+    local current = read_rawrc(address)
+    write_rawrc(address, bit.band(current, bit.bnot(mask)))
+end
+
+--[[
+命令通讯
+@api rc522.command(command,data)
+@number command
+@number data
+@return status data len 结果,返回数据,收到的位长度
+@usage
+rc522.version()
+]]
+function rc522.command(command, data)
+    local out_data = {}
+    local len      = 0
+    local status   = false
+    local Irq_en   = 0x00
+    local waitfor  = 0x00
+    local last_bits, n, l
+    if command == rc522_authent then
+        Irq_en  = 0x12
+        waitfor = 0x10
+    elseif command == rc522_transceive then
+        Irq_en  = 0x77
+        waitfor = 0x30
+    end
+    write_rawrc(0x02, bit.bor(Irq_en, 0x80))
+    rc522.clear_bit_mask(rc522_com_irq, 0x80)
+    write_rawrc(rc522_com_mand, rc522_idle)
+    rc522.set_bit_mask(rc522_fifo_level, 0x80)
+    for i = 1, #data do
+        write_rawrc(rc522_fifo_data, data[i])
+    end
+    write_rawrc(rc522_com_mand, command)
+    if (command == rc522_transceive) then
+        rc522.set_bit_mask(rc522_bit_framing, 0x80)
+    end
+    l = 12
+    while true do
+        sys.wait(1)
+        n = read_rawrc(rc522_com_irq)
+        l = l - 1
+        if not ((l ~= 0) and (bit.band(n, 0x01) == 0) and (bit.band(n, waitfor) == 0)) then
+            break
+        end
+    end
+    rc522.clear_bit_mask(rc522_bit_framing, 0x80)
+    if (l ~= 0) then
+        if bit.band(read_rawrc(rc522_error), 0x1B) == 0x00 then
+            status = true
+            if bit.band(n, Irq_en, 0x01) ~= 0 then
+                status = false
+            end
+            if (command == rc522_transceive) then
+                n = read_rawrc(rc522_fifo_level)
+                last_bits = bit.band(read_rawrc(rc522_control), 0x07)
+                if last_bits ~= 0 then
+                    len = (n - 1) * 8 + last_bits
+                else
+                    len = n * 8
+                end
+                if n == 0 then
+                    n = 1
+                end
+                for i = 1, n do
+                    out_data[i] = read_rawrc(rc522_fifo_data)
+                end
+            end
+        end
+    else
+        status = false
+    end
+    rc522.set_bit_mask(rc522_control, 0x80)
+    write_rawrc(rc522_com_mand, rc522_idle)
+    return status, out_data, len
+end
+
+--[[
+防冲撞
+@api rc522.anticoll(id)
+@string id 卡片序列号,4字节
+@return status uid 结果,uid
+@usage
+local status,uid = rc522.anticoll(array_id)
+]]
+function rc522.anticoll(id)
+    local check = 0
+    local uid
+    rc522.clear_bit_mask(rc522_status2, 0x08)
+    write_rawrc(rc522_bit_framing, 0x00)
+    rc522.clear_bit_mask(rc522_coll, 0x80)
+    local status, back_data = rc522.command(rc522_transceive, { 0x93, 0x20 })
+    if back_data and #back_data >= 5 then
+        for i = 1, 4 do
+            check = bit.bxor(check, back_data[i])
+        end
+        if check ~= back_data[5] then
+            status = false
+        end
+        uid = string.char(table.unpack(back_data, 1, 4))
+    end
+    rc522.clear_bit_mask(rc522_coll, 0x80)
+    return status, uid
+end
+
+--[[
+crc计算
+@api calculate_crc(data)
+@table data 数据
+@return table crc值
+@usage
+local crc = calculate_crc(buff)
+]]
+local function calculate_crc(data)
+    local ret_data = {}
+    rc522.clear_bit_mask(rc522_div_irq, 0x04)
+    write_rawrc(rc522_com_mand, rc522_idle)
+    rc522.set_bit_mask(rc522_fifo_level, 0x80)
+    for i = 1, #data do
+        write_rawrc(rc522_fifo_data, data[i])
+    end
+    write_rawrc(rc522_com_mand, rc522_calccrc)
+    local i = 0xFF
+    while true do
+        local n = read_rawrc(0x05)
+        i = i - 1
+        if not ((i ~= 0) and not (n & 0x04)) then
+            break
+        end
+    end
+    ret_data[1] = read_rawrc(0x22)
+    ret_data[2] = read_rawrc(0x21)
+    return ret_data
+end
+
+--[[
+验证卡片密码
+@api authstate(mode, addr,key,uid )
+@number mode 模式
+@number addr 地址
+@string key 密码
+@string uid uid
+@return bool 结果
+@usage
+status = authstate(rc522_authent1b, addr,Key_B,uid )
+]]
+local function authstate(mode, addr, key, uid)
+    local buff = {}
+    buff[1] = mode
+    buff[2] = addr
+    for i = 1, 6 do
+        buff[i + 2] = key:byte(i)
+    end
+    for i = 1, 4 do
+        buff[i + 8] = uid:byte(i)
+    end
+    local status, back_data = rc522.command(rc522_authent, buff)
+    if status == true and (read_rawrc(rc522_status2) & 0x08) ~= 0 then
+        return true
+    end
+    return false
+end
+
+--[[
+写数据到M1卡一块
+@api rc522.write(addr, data)
+@number addr 块地址(0-63)M1卡总共有16个扇区(每个扇区有:3个数据块+1个控制块),共64个块
+@table data 数据
+@return bool 结果
+@usage
+rc522.write(addr, data)
+]]
+function rc522.write(addr, data)
+    local buff = {}
+    buff[1] = rc522_write
+    buff[2] = addr
+    local crc = calculate_crc(buff)
+    buff[3] = crc[1]
+    buff[4] = crc[2]
+    local status, back_data, back_bits = rc522.command(rc522_transceive, buff)
+    if status == true and back_bits == 4 and (back_data[1] & 0x0F) == 0x0A then
+        local buf_w = {}
+        for i = 0, 16 do
+            table.insert(buf_w, data[i])
+        end
+        local crc = calculate_crc(buf_w)
+        table.insert(buf_w, crc[1])
+        table.insert(buf_w, crc[2])
+        status, back_data, back_bits = rc522.command(rc522_transceive, buf_w)
+        if status == true and back_bits == 4 and (back_data[1] & 0x0F) == 0x0A then
+            return status
+        end
+    end
+    return status
+end
+
+--[[
+写数据到M1卡一块
+@api rc522.read(addr)
+@number addr 块地址(0-63)M1卡总共有16个扇区(每个扇区有:3个数据块+1个控制块),共64个块
+@return bool,string 结果,数据
+@usage
+rc522.read(addr, data)
+]]
+local function read(addr)
+    local buff = {}
+    buff[1] = rc522_read
+    buff[2] = addr
+    local crc = calculate_crc(buff)
+    buff[3] = crc[1]
+    buff[4] = crc[2]
+    local status, back_data, back_bits = rc522.command(rc522_transceive, buff)
+    if status == true and back_bits == 0x90 then
+        local data = string.char(table.unpack(back_data, 1, 16))
+        return status, data
+    end
+    return false
+end
+
+--[[
+rc522 硬件版本
+@api rc522.version()
+@return number 硬件版本
+@usage
+rc522.version()
+]]
+function rc522.version()
+    return read_rawrc(rc522_version)
+end
+
+--[[
+rc522 命令卡片进入休眠状态
+@api rc522.halt()
+@return bool 结果
+@usage
+rc522.halt()
+]]
+function rc522.halt()
+    local buff = {}
+    buff[1] = rc522_halt
+    buff[2] = 0
+    local crc = calculate_crc(buff)
+    buff[3] = crc[1]
+    buff[4] = crc[2]
+    local status = rc522.command(rc522_transceive, buff)
+    return status
+end
+
+--[[
+rc522 复位
+@api rc522.reset()
+@usage
+rc522.reset()
+]]
+function rc522.reset()
+    rc522_rst(1)
+    rc522_rst(0)
+    rc522_rst(1)
+    write_rawrc(rc522_com_mand, 0x0f)
+    write_rawrc(rc522_mode, 0x3D)
+    write_rawrc(rc522_tpreload_l, 30)
+    write_rawrc(rc522_tpreload_h, 0)
+    write_rawrc(rc522_tmode, 0x8D)
+    write_rawrc(rc522_tprescaler, 0x3E)
+    write_rawrc(rc522_tx_ayto, 0x40)
+end
+
+--[[
+开启天线
+@api rc522.antenna_on()
+@usage
+rc522.antenna_on()
+]]
+function rc522.antenna_on()
+    local uc = read_rawrc(rc522_tx_control)
+    if ((uc & 0x03) == 0) then
+        rc522.set_bit_mask(rc522_tx_control, 0x03)
+    end
+end
+
+--[[
+关闭天线
+@api rc522.antenna_on()
+@usage
+rc522.antenna_on()
+]]
+function rc522.antenna_off()
+    rc522.clear_bit_mask(rc522_tx_control, 0x03)
+end
+
+--[[
+设置rc522工作方式为ISO14443_A
+@api rc522_config_isotype()
+@usage
+rc522_config_isotype()
+]]
+local function rc522_config_isotype()
+    rc522.clear_bit_mask(rc522_status2, 0x08)
+    write_rawrc(rc522_mode, 0x3D)
+    write_rawrc(rc522_rx_sel, 0x86)
+    write_rawrc(rc522_rfcfg, 0x7F)
+    write_rawrc(rc522_tpreload_l, 30)
+    write_rawrc(rc522_tpreload_h, 0)
+    write_rawrc(rc522_tmode, 0x8D)
+    write_rawrc(rc522_tprescaler, 0x3E)
+    rc522.antenna_on()
+end
+
+--[[
+rc522 寻卡
+@api rc522.request(req_code)
+@number req_code rc522.reqidl 寻天线区内未进入休眠状态 rc522.reqall 寻天线区内全部卡
+@return bool tagtype 结果,卡类型
+@usage
+status,array_id = rc522.request(rc522.reqall)
+]]
+function rc522.request(req_code)
+    rc522.clear_bit_mask(rc522_status2, 0x08)
+    write_rawrc(rc522_bit_framing, 0x07)
+    rc522.set_bit_mask(rc522_tx_control, 0x03)
+    local tagtype
+    local status, back_data, back_bits = rc522.command(rc522_transceive, { req_code })
+    if status == true and back_bits == 0x10 then
+        tagtype = string.char(table.unpack(back_data, 1, 2))
+        return status, tagtype
+    end
+    return false
+end
+
+--[[
+选定卡片
+@api rc522.select(id)
+@number id 卡片序列号,4字节
+@return bool 结果
+@usage
+status = rc522.select(id)
+]]
+function rc522.select(id)
+    if not id then
+        return false
+    end
+    local buff = {}
+    buff[1] = rc522_anticoll1
+    buff[2] = 0x70
+    buff[7] = 0
+    for i = 1, 4 do
+        buff[i + 2] = id:byte(i)
+        buff[7] = bit.bxor(buff[7], id:byte(i))
+    end
+    local crc = calculate_crc(buff)
+    buff[8] = crc[1]
+    buff[9] = crc[2]
+    rc522.clear_bit_mask(rc522_status2, 0x08)
+    local status, back_data, back_bits = rc522.command(rc522_transceive, buff)
+    if status == true and back_bits == 0x18 then
+        return true
+    end
+    return false
+end
+
+--[[
+按照rc522操作流程写入16字节数据到块
+@api rc522.write_datablock(addr,data)
+@number addr 任意块地址.M1卡总共有16个扇区(每个扇区有:3个数据块+1个控制块),共64个块
+@table data 指向要写入的数据,必须为16个字符
+@return bool 结果
+@usage
+rc522.write_datablock(addr,data)
+]]
+function rc522.write_datablock(addr, data)
+    if #data ~= 16 then
+        return false
+    end
+    local status, array_id = rc522.request(rc522.reqall)
+    if status ~= true then
+        status, array_id = rc522.request(rc522.reqall)
+    end
+    if status == true then
+        local status, uid = rc522.anticoll(array_id)
+        if not uid then
+            return false
+        end
+        if status == true and uid then
+            rc522.select(uid)
+            status = authstate(rc522_authent1b, addr, Key_B, uid)
+            if status == true then
+                status = rc522.write(addr, data)
+                rc522.halt()
+                return status
+            end
+        end
+    end
+    return false
+end
+
+--[[
+按照rc522操作流程读取块
+@api rc522.read_datablock(addr)
+@number addr 任意块地址.M1卡总共有16个扇区(每个扇区有:3个数据块+1个控制块),共64个块
+@return bool string 结果 数据
+@usage
+    for i=0,63 do
+        local a,b = rc522.read_datablock(i)
+        if a then
+            print("read",i,b:toHex())
+        end
+    end
+]]
+function rc522.read_datablock(addr)
+    local status, array_id = rc522.request(rc522.reqall)
+    if status ~= true then
+        status, array_id = rc522.request(rc522.reqall)
+    end
+    if status == true then
+        local status, uid = rc522.anticoll(array_id)
+        if status == true and uid then
+            rc522.select(uid)
+            status = authstate(rc522_authent1b, addr, Key_B, uid)
+            if status == true then
+                local status, data = read(addr)
+                if status == true then
+                    return true, data
+                end
+                rc522.halt()
+            end
+        end
+    end
+    return false
+end
+
+--[[
+rc522 初始化
+@api rc522.init(spi_id, cs, rst)
+@number spi_id spi端口号
+@number cs      cs引脚
+@number rst     rst引脚
+@return bool 初始化结果
+@usage
+spi_rc522 = spi.setup(0,nil,0,0,8,10*1000*1000,spi.MSB,1,1)
+rc522.init(0,pin.PB04,pin.PB01)
+]]
+function rc522.init(spi_id, cs, rst)
+    rc522_spi = spi_id
+    rc522_cs = gpio.setup(cs, 0, gpio.PULLUP)
+    rc522_cs(1)
+    rc522_rst = gpio.setup(rst, 0, gpio.PULLUP)
+    rc522_rst(1)
+    rc522.reset()
+    rc522_config_isotype()
+    log.debug("rc522.version", rc522.version())
+    return true
+end
+
+function rc522.test()
+
+end
+
+return rc522

+ 152 - 0
module/Air780EPM/demo/accessory_board/AirRC522_1000/readme.md

@@ -0,0 +1,152 @@
+## 功能模块介绍:
+
+1. main.lua:主程序入口
+
+2. rc522.lua :rc522扩展库文件,在AirRC522_1000.lua功能模块中require使用
+
+3. AirRC522_1000.lua:功能演示核心文件,在main.lua中加载运行,详细逻辑请看AirRC522_1000.lua 文件
+
+## 演示功能概述:
+
+核心逻辑:
+
+1. 初始化并启用spi,如果初始化失败,退出程序
+
+2. 初始化RC522模块,如果初始化失败,退出程序
+
+3. 循环检测卡片。
+
+4. 向卡片指定块号写入数据,并读取数据验证一致性
+
+5. 读取卡片所有数据
+
+## 演示硬件环境:
+
+![](https://docs.openluat.com/accessory/AirSPINORFLASH_1000/image/780EPM.jpg)
+
+![](https://docs.openluat.com/accessory/AirSPINORFLASH_1000/image/rc522.jpg)
+
+1. 合宙 Air780EPM 核心板一块
+
+2. 合宙 AirRC522_1000 一套
+
+3. TYPE-C USB 数据线一根 ,Air780EPM 核心板和数据线的硬件接线方式为:
+* Air780EPM 核心板通过 TYPE-C USB 口供电;(USB的拨码开关off/on,拨到on)
+
+* TYPE-C USB 数据线直接插到核心板的 TYPE-C USB 座子,另外一端连接电脑 USB 口;
+4. 杜邦线 7根
+   Air780EPM 核心板与 AirRC522_1000 按以下方式接线:
+
+| Air780EPM核心板                                    | AirRC522_1000 |
+| ----------------------------------------------- | ------------- |
+| GND(任意)                                         | GND           |
+| 3V3                                             | 3.3V          |
+| 83/SPI0CS                                       | SDA           |
+| 86/SPI0CLK                                      | SCK           |
+| 85/SPI0MOSI                                     | MOSI          |
+| 84/SPI0MISO                                     | MISO          |
+| 19/GPIO22(可选任意空闲IO) | RST           |
+
+## 演示软件环境:
+
+1. Luatools 下载调试工具
+
+2. 固件版本:LuatOS-SoC_V2018_Air780EPM_1,固件地址,如有最新固件请用最新[https://docs.openluat.com/air780epm/luatos/firmware/version/](https://docs.openluat.com/air780epm/luatos/firmware/version/)
+
+3. pc 系统 win11(win10 及以上)
+
+## 演示核心步骤:
+
+1. 搭建好硬件环境
+
+2. Luatools 烧录内核固件和demo 脚本代码.
+   AirRC522_1000.lua脚本中,硬件配置参数中rst_pin是使用Air780EPM核心板的GPIO22,如果接其他IO,注意修改硬件配置参数中rst_pin对应的管脚号。
+
+3. 烧录成功后,代码会自动运行,查看打印日志,如果正常运行,会打印相关信息,spi 初始化,rc522初始化,写入数据读取数据等。
+
+4. 如下 log 显示:
+
+```
+[2025-12-01 17:35:36.521][000000000.205] I/user.main Air780EPM_rc522_DEMO 001.000.000
+[2025-12-01 17:35:36.529][000000000.226] I/user.RC522 初始化SPI接口
+[2025-12-01 17:35:36.538][000000000.227] SPI_HWInit 552:spi0 speed 2000000,1994805,154
+[2025-12-01 17:35:36.546][000000000.227] I/user.RC522 SPI初始化成功
+[2025-12-01 17:35:36.553][000000000.227] I/user.RC522 初始化RC522传感器
+[2025-12-01 17:35:36.562][000000000.230] D/user.rc522.version 178
+[2025-12-01 17:35:36.568][000000000.230] I/user.RC522 传感器初始化成功
+[2025-12-01 17:35:36.574][000000000.230] I/user.RC522 开始检测卡片
+[2025-12-01 17:35:36.579][000000000.236] I/user.RC522 检测到卡片,类型: 0400 4
+[2025-12-01 17:35:36.584][000000000.243] I/user.RC522 卡片UID: E3E2AD14 8
+[2025-12-01 17:35:36.589][000000000.335] I/user.RC522 数据写入成功,块号: 9 写入数据是: 01000000000000000000000000000000 32
+[2025-12-01 17:35:36.595][000000000.378] I/user.RC522 写入验证成功,数据是: 01000000000000000000000000000000 32
+[2025-12-01 17:35:36.601][000000000.378] I/user.RC522 开始读取卡片数据...
+[2025-12-01 17:35:36.608][000000000.429] I/user.块[0] E3E2AD14B80804006263646566676869 32
+[2025-12-01 17:35:36.613][000000000.506] I/user.块[1] 00000000000000000000000000000000 32
+[2025-12-01 17:35:36.618][000000000.576] I/user.块[2] 00000000000000000000000000000000 32
+[2025-12-01 17:35:36.623][000000000.648] I/user.块[3] 000000000000FF078069FFFFFFFFFFFF 32
+[2025-12-01 17:35:36.630][000000000.720] I/user.块[4] 00000000000000000000000000000000 32
+[2025-12-01 17:35:36.677][000000000.793] I/user.块[5] 00000000000000000000000000000000 32
+[2025-12-01 17:35:36.682][000000000.861] I/user.块[6] 00000000000000000000000000000000 32
+[2025-12-01 17:35:36.690][000000000.927] I/user.块[7] 000000000000FF078069FFFFFFFFFFFF 32
+[2025-12-01 17:35:36.698][000000000.992] I/user.块[8] 00000000000000000000000000000000 32
+[2025-12-01 17:35:36.702][000000001.059] I/user.块[9] 01000000000000000000000000000000 32
+[2025-12-01 17:35:36.734][000000001.124] I/user.块[10] 00000000000000000000000000000000 32
+[2025-12-01 17:35:36.796][000000001.191] I/user.块[11] 000000000000FF078069FFFFFFFFFFFF 32
+[2025-12-01 17:35:36.874][000000001.256] I/user.块[12] 00000000000000000000000000000000 32
+[2025-12-01 17:35:36.935][000000001.322] I/user.块[13] 00000000000000000000000000000000 32
+[2025-12-01 17:35:37.154][000000001.403] I/user.块[14] 00000000000000000000000000000000 32
+[2025-12-01 17:35:37.174][000000001.473] I/user.块[15] 000000000000FF078069FFFFFFFFFFFF 32
+[2025-12-01 17:35:37.192][000000001.585] I/user.块[16] 00000000000000000000000000000000 32
+[2025-12-01 17:35:37.264][000000001.650] I/user.块[17] 00000000000000000000000000000000 32
+[2025-12-01 17:35:37.325][000000001.718] I/user.块[18] 00000000000000000000000000000000 32
+[2025-12-01 17:35:37.387][000000001.784] I/user.块[19] 000000000000FF078069FFFFFFFFFFFF 32
+[2025-12-01 17:35:37.463][000000001.850] I/user.块[20] 00000000000000000000000000000000 32
+[2025-12-01 17:35:37.541][000000001.926] I/user.块[21] 00000000000000000000000000000000 32
+[2025-12-01 17:35:37.603][000000001.993] I/user.块[22] 00000000000000000000000000000000 32
+[2025-12-01 17:35:37.668][000000002.060] I/user.块[23] 000000000000FF078069FFFFFFFFFFFF 32
+[2025-12-01 17:35:37.786][000000002.138] I/user.块[24] 00000000000000000000000000000000 32
+[2025-12-01 17:35:37.793][000000002.139] D/mobile cid1, state0
+[2025-12-01 17:35:37.800][000000002.139] D/mobile bearer act 0, result 0
+[2025-12-01 17:35:37.811][000000002.140] D/mobile NETIF_LINK_ON -> IP_READY
+[2025-12-01 17:35:37.815][000000002.205] I/user.块[25] 00000000000000000000000000000000 32
+[2025-12-01 17:35:37.826][000000002.211] D/mobile TIME_SYNC 0
+[2025-12-01 17:35:37.884][000000002.276] I/user.块[26] 00000000000000000000000000000000 32
+[2025-12-01 17:35:37.961][000000002.343] I/user.块[27] 000000000000FF078069FFFFFFFFFFFF 32
+[2025-12-01 17:35:38.022][000000002.409] I/user.块[28] 00000000000000000000000000000000 32
+[2025-12-01 17:35:38.085][000000002.476] I/user.块[29] 00000000000000000000000000000000 32
+[2025-12-01 17:35:38.146][000000002.542] I/user.块[30] 00000000000000000000000000000000 32
+[2025-12-01 17:35:38.224][000000002.608] I/user.块[31] 000000000000FF078069FFFFFFFFFFFF 32
+[2025-12-01 17:35:38.287][000000002.672] I/user.块[32] 00000000000000000000000000000000 32
+[2025-12-01 17:35:38.350][000000002.738] I/user.块[33] 00000000000000000000000000000000 32
+[2025-12-01 17:35:38.413][000000002.804] I/user.块[34] 00000000000000000000000000000000 32
+[2025-12-01 17:35:38.490][000000002.872] I/user.块[35] 000000000000FF078069FFFFFFFFFFFF 32
+[2025-12-01 17:35:38.552][000000002.940] I/user.块[36] 00000000000000000000000000000000 32
+[2025-12-01 17:35:38.614][000000003.005] I/user.块[37] 00000000000000000000000000000000 32
+[2025-12-01 17:35:38.676][000000003.070] I/user.块[38] 00000000000000000000000000000000 32
+[2025-12-01 17:35:38.753][000000003.136] I/user.块[39] 000000000000FF078069FFFFFFFFFFFF 32
+[2025-12-01 17:35:38.813][000000003.203] I/user.块[40] 00000000000000000000000000000000 32
+[2025-12-01 17:35:38.877][000000003.269] I/user.块[41] 00000000000000000000000000000000 32
+[2025-12-01 17:35:38.938][000000003.334] I/user.块[42] 00000000000000000000000000000000 32
+[2025-12-01 17:35:39.015][000000003.400] I/user.块[43] 000000000000FF078069FFFFFFFFFFFF 32
+[2025-12-01 17:35:39.079][000000003.464] I/user.块[44] 00000000000000000000000000000000 32
+[2025-12-01 17:35:39.142][000000003.529] I/user.块[45] 00000000000000000000000000000000 32
+[2025-12-01 17:35:39.204][000000003.596] I/user.块[46] 00000000000000000000000000000000 32
+[2025-12-01 17:35:39.268][000000003.662] I/user.块[47] 000000000000FF078069FFFFFFFFFFFF 32
+[2025-12-01 17:35:39.345][000000003.727] I/user.块[48] 00000000000000000000000000000000 32
+[2025-12-01 17:35:39.409][000000003.792] I/user.块[49] 00000000000000000000000000000000 32
+[2025-12-01 17:35:39.473][000000003.857] I/user.块[50] 00000000000000000000000000000000 32
+[2025-12-01 17:35:39.535][000000003.923] I/user.块[51] 000000000000FF078069FFFFFFFFFFFF 32
+[2025-12-01 17:35:39.597][000000003.988] I/user.块[52] 00000000000000000000000000000000 32
+[2025-12-01 17:35:39.660][000000004.054] I/user.块[53] 00000000000000000000000000000000 32
+[2025-12-01 17:35:39.723][000000004.118] I/user.块[54] 00000000000000000000000000000000 32
+[2025-12-01 17:35:39.786][000000004.183] I/user.块[55] 000000000000FF078069FFFFFFFFFFFF 32
+[2025-12-01 17:35:39.864][000000004.250] I/user.块[56] 00000000000000000000000000000000 32
+[2025-12-01 17:35:39.927][000000004.317] I/user.块[57] 00000000000000000000000000000000 32
+[2025-12-01 17:35:39.988][000000004.382] I/user.块[58] 00000000000000000000000000000000 32
+[2025-12-01 17:35:40.052][000000004.448] I/user.块[59] 000000000000FF078069FFFFFFFFFFFF 32
+[2025-12-01 17:35:40.129][000000004.514] I/user.块[60] 00000000000000000000000000000000 32
+[2025-12-01 17:35:40.191][000000004.580] I/user.块[61] 00000000000000000000000000000000 32
+[2025-12-01 17:35:40.254][000000004.646] I/user.块[62] 00000000000000000000000000000000 32
+[2025-12-01 17:35:40.318][000000004.714] I/user.块[63] 000000000000FF078069FFFFFFFFFFFF 32
+
+```

+ 0 - 230
module/Air8000/demo/accessory_board/AirLCD_1000/AirLCD_1000.lua

@@ -1,230 +0,0 @@
---[[
-@module  AirLCD_1000
-@summary AirLCD_1000显示驱动模块
-@version 1.0
-@date    2025.09.4
-@author  江访
-@usage
-
-核心业务逻辑为:
-1、初始化AirLCD_1000显示屏
-2、管理屏幕背光亮度及开关状态
-3、管理屏幕休眠和唤醒状态
-4、提供屏幕状态管理功能
-
-本文件的对外接口有5个:
-1、AirLCD_1000.lcd_init()           --LCD初始化函数,
-2、AirLCD_1000.set_backlight(level) -- 设置背光亮度接口,level 亮度级别(0-100)
-3、AirLCD_1000.lcd_on()             -- 开启LCD背光
-4、AirLCD_1000.lcd_off()            -- 关闭LCD背光
-5、AirLCD_1000.set_sleep(sleep)     -- 设置休眠状态接口,sleep:true进入休眠, false唤醒
-]]
-
--- AirLCD_1000.lua - LCD显示驱动模块
--- 此文件负责初始化LCD显示屏,管理背光及休眠状态
-local AirLCD_1000 = {}
-
--- 屏幕状态管理表
-local screen_state = {
-    lcd_pin_pwr = 1,        -- 屏幕背光引脚GPIO号
-    lcd_pwm_id = 0,         -- 屏幕背光引脚PWM端口号
-    is_sleeping = false,    -- 是否休眠中标识
-    last_brightness = 50,   -- 默认亮度50%
-    backlight_on = true     -- 背光默认开启
-}
-
--- LCD初始化函数
--- @param LCD_MODEL   显示屏型号型号
--- @param lcd_vcc     屏幕供电引脚GPIO号
--- @param lcd_pin_rst 复位引脚GPIO号
--- @param lcd_pin_pwr 背光引脚GPIO号
--- @param lcd_pwm_id  背光引脚PWM端口号
-
-function AirLCD_1000.lcd_init(LCD_MODEL, lcd_vcc, lcd_pin_rst, lcd_pin_pwr,lcd_pwm_id)
-    -- 根据接线设置AirLCD_1000初始化参数
-    local LCD_MODEL, lcd_vcc, lcd_pin_rst, lcd_pin_pwr,lcd_pwm_id = "AirLCD_1000" or LCD_MODEL, lcd_vcc or 141, lcd_pin_rst or 36, lcd_pin_pwr or 1, lcd_pwm_id or 0
-
-    -- 设置屏幕尺寸
-    local width, height = 320, 480
-
-    -- LCD参数配置表
-    local lcd_param = {
-        port = lcd.HWID_0,       -- 使用SPI设备模式
-        pin_dc = 0xff,           -- DC引脚,无需设置
-        pin_rst = lcd_pin_rst,   -- 复位引脚,对应GPIO号
-        pin_pwr = lcd_pin_pwr,   -- 背光引脚(可选)
-        direction = 0,           -- 屏幕默认0°方向
-                                 -- 0:0°, 1:90°, 2:180°, 3:270°
-        w = width,               -- 屏幕宽度(像素)
-        h = height,              -- 屏幕高度(像素)
-        xoffset = 0,             -- X方向偏移量
-        yoffset = 0,             -- Y方向偏移量
-        sleepcmd = 0x10,         -- 睡眠命令
-        wakecmd = 0x11           -- 唤醒命令
-    }
-
-    -- 初始化SPI设备
-    spi.deviceSetup(
-        lcd.HWID_0,  -- LCD端口号
-        nil,         -- CS片选脚,可选
-        0,           -- CPHA=0
-        0,           -- CPOL=0
-        8,           -- 8位数据宽度
-        20000000,    -- 20MHz波特率
-        spi.MSB,     -- 高位先传
-        1,           -- 主机模式
-        1            -- 全双工模式
-    )
-
-    -- 设置VCC/RST/背光引脚为输出模式,并启用上拉电阻
-    gpio.setup(lcd_vcc, 1, gpio.PULLUP)
-    gpio.setup(lcd_pin_rst, 0, gpio.PULLUP)
-    gpio.setup(lcd_pin_pwr, 1, gpio.PULLUP)
-    
-    -- 开启屏幕VCC供电
-    gpio.set(lcd_vcc, 1)
-
-    -- 拉低屏幕复位引脚
-    gpio.set(lcd_pin_rst, 0)  -- 拉低复位引脚
-    sys.wait(20)                    -- 等待20ms(ST7796复位拉低大于10ms生效)
-    gpio.set(lcd_pin_rst, 1)  -- 拉高复位引脚
-    sys.wait(150)                   -- 等待150ms(ST7796复位拉高大于120ms重置)
-    gpio.set(lcd_pin_pwr, 1)
-
-    -- 初始化ST7796显示芯片
-    lcd.init("st7796", lcd_param)
-
-    -- 通用显示设置
-    lcd.setupBuff(nil,false)     -- 设置帧缓冲区
-    lcd.autoFlush(false)         -- 禁止自动刷新
-
-    screen_state.lcd_pin_pwr = lcd_pin_pwr
-    screen_state.lcd_pwm_id = lcd_pwm_id
-
-    log.info("AirLCD_1000", "LCD初始化成功,尺寸:", width, "x", height)
-    -- return true
-end
-
--- 设置背光亮度接口
--- 使用背光PWM模式控制亮度,休眠和关闭背光使用背光引脚GPIO模式
--- @param level 亮度级别(0-100)
--- @return 设置成功状态
-function AirLCD_1000.set_backlight(level)
-
-    -- 检查屏幕状态和PWM配置
-    if screen_state.is_sleeping then
-        log.warn("AirLCD_1000", "屏幕处于休眠状态,无法调节背光")
-        return false
-    end
-    if not screen_state.lcd_pin_pwr then
-        log.error("AirLCD_1000", "PWM配置不存在,无法调节背光")
-        return false
-    end
-
-    -- 确保GPIO已关闭 
-    gpio.close(screen_state.lcd_pin_pwr)
-
-    -- 设置并开启PWM
-    pwm.stop(screen_state.lcd_pwm_id)
-    pwm.close(screen_state.lcd_pwm_id)
-    pwm.setup(screen_state.lcd_pwm_id, 1000, 100)
-    pwm.open(screen_state.lcd_pwm_id, 1000, level)
-
-    -- 修改默认背光亮度为当前设置
-    screen_state.last_brightness = level
-    screen_state.backlight_on = (level > 0)
-    log.info("AirLCD_1000", "背光设置为", level, "%")
-    return true
-end
-
--- 开启LCD显示屏背光
--- @return 操作成功状态
-function AirLCD_1000.lcd_on()
-    if screen_state.is_sleeping then
-        log.warn("AirLCD_1000", "屏幕处于休眠状态,无法开启背光")
-        return false
-    end
-
-    pwm.stop(screen_state.lcd_pwm_id)
-    pwm.close(screen_state.lcd_pwm_id)
-
-    -- 设置GPIO控制电源(如果存在)
-    if screen_state.lcd_pin_pwr then
-        gpio.setup(screen_state.lcd_pin_pwr, 1, gpio.PULLUP)
-    end
-
-    lcd.on()
-    log.info("AirLCD_1000", "LCD背光已开启")
-end
-
--- 关闭LCD显示屏背光
--- @return 操作成功状态
-function AirLCD_1000.lcd_off()
-    if screen_state.is_sleeping then
-        log.warn("AirLCD_1000", "屏幕处于休眠状态,无法关闭背光")
-        return false
-    end
-    pwm.stop(screen_state.lcd_pwm_id)
-    pwm.close(screen_state.lcd_pwm_id)
-        -- 设置GPIO控制电源(如果存在)
-    if screen_state.lcd_pin_pwr then
-        gpio.setup(screen_state.lcd_pin_pwr, 1, gpio.PULLUP)
-    end
-    lcd.off()
-    log.info("AirLCD_1000", "LCD背光已关闭")
-end
-
--- 设置休眠状态接口
--- @param sleep true进入休眠, false唤醒
--- @return 操作成功状态
-function AirLCD_1000.set_sleep(sleep)
-    if sleep then
-        -- 进入休眠模式
-        if not screen_state.is_sleeping then
-            -- 关闭PWM(如果存在)
-            if screen_state.lcd_pwm_id then
-                pwm.stop(screen_state.lcd_pwm_id)
-                pwm.close(screen_state.lcd_pwm_id)
-            end
-
-            -- 设置GPIO控制电源(如果存在)
-            if screen_state.lcd_pin_pwr then
-                gpio.setup(screen_state.lcd_pin_pwr, 1, gpio.PULLUP)
-                gpio.set(screen_state.lcd_pin_pwr, 0)
-            end
-
-            -- 执行LCD睡眠
-            lcd.sleep()
-            screen_state.is_sleeping = true
-            log.info("AirLCD_1000", "LCD进入休眠状态")
-        end
-    else
-        -- 退出休眠模式
-        if screen_state.is_sleeping then
-            -- 唤醒LCD
-            lcd.wakeup()
-
-            -- 关闭GPIO控制(如果存在)
-            if screen_state.lcd_pin_pwr then
-                gpio.close(screen_state.lcd_pin_pwr)
-            end
-
-            -- 恢复之前的背光设置(如果PWM存在)
-            if screen_state.lcd_pin_pwr then
-                if screen_state.backlight_on then
-                    AirLCD_1000.set_backlight(screen_state.last_brightness)
-                else
-                    -- 如果背光原本是关闭状态,保持关闭
-                    pwm.stop(screen_state.lcd_pwm_id)
-                    pwm.close(screen_state.lcd_pwm_id)
-                end
-            end
-
-            screen_state.is_sleeping = false
-            log.info("AirLCD_1000", "LCD唤醒")
-        end
-    end
-    return true
-end
-
-return AirLCD_1000

+ 1 - 0
module/Air8000/demo/accessory_board/AirLCD_1000/exeasyui/hw_drv/hw_customer_font_drv.lua

@@ -0,0 +1 @@
+-- hw_customer_font_drv是用户外部自定义外部点阵字体、lcd显示驱动配置和tp触摸驱动配置的驱动配置文件,目前exeasyui自定义字体功能正在开发中。

+ 62 - 0
module/Air8000/demo/accessory_board/AirLCD_1000/exeasyui/hw_drv/hw_default_font_drv.lua

@@ -0,0 +1,62 @@
+--[[
+@module  hw_default_font_drv
+@summary 默认字体、lcd和tp驱动模块,使用内置12号点阵字体
+@version 1.0
+@date    2025.12.3
+@author  江访
+@usage
+本文件为默认字体、lcd和tp驱动模块,核心业务逻辑为:
+1、使用lcd内核固件中自带的12号中文字体;
+2、根据配置的字体、lcd和tp参数,初始化exEasyUI默认使用的字体、硬件显示和触摸;
+3、提供无需外部硬件的字体显示能力;
+
+本文件的对外接口有0个:
+1、require加载后自动执行初始化;
+
+@api ui.hw_init(config)
+@summary 初始化exEasyUI硬件系统
+@table config 硬件配置参数表,包含LCD配置和触摸配置
+@field lcd_config table LCD显示配置参数
+@field tp_config table 触摸屏配置参数
+@return nil
+
+@usage
+
+]]
+
+-- 使用默认12号中文字体初始化exEasyUI硬件
+ui.hw_init({
+    -- lcd_config参数填写可以参考合宙exlcd显示扩展库exlcd.init(param)接口说明:https://docs.openluat.com/osapi/ext/exlcd/#31-exlcdinitparam
+    lcd_config = {
+        lcd_model = "AirLCD_1000", -- LCD型号
+        -- pin_vcc = 24,           -- 供电引脚,使用GPIO控制屏幕供电可配置
+        pin_rst = 36,              -- 复位引脚
+        pin_pwr = 1,               -- 背光控制引脚GPIO ID号
+        pin_pwm = 0,               -- 背光控制引脚PWM D号
+        port = lcd.HWID_0,         -- 驱动端口
+        -- pin_dc = nil,          -- lcd数据/命令选择引脚GPIO ID号,使用lcd 专用 SPI 接口 lcd.HWID_0不需要填此参数,使用通用SPI接口需要赋值
+        direction = 0,             -- lcd屏幕方向 0:0° 1:90° 2:180° 3:270°,屏幕方向和分辨率保存一致
+        w = 320,                   -- lcd 水平分辨率
+        h = 480,                   -- lcd 竖直分辨率
+        xoffset = 0,               -- x偏移(不同屏幕ic 不同屏幕方向会有差异)
+        yoffset = 0,               -- y偏移(不同屏幕ic 不同屏幕方向会有差异)
+        sleepcmd = 0X10,           -- 睡眠命令,默认0X10
+        wakecmd = 0X11,            -- 唤醒命令,默认0X11
+        -- bus_speed = 50*1000*1000,                            -- SPI总线速度,不填默认50M,若速率要求更高需要进行设置
+        -- interface_mode = lcd.WIRE_4_BIT_8_INTERFACE_I,       -- lcd模式,默认lcd.WIRE_4_BIT_8_INTERFACE_I
+        -- direction0 = {0x36,0x00},                            -- 0°方向的命令,(不同屏幕ic会有差异)
+        -- direction90 = {0x36,0x60},                           -- 90°方向的命令,(不同屏幕ic会有差异)
+        -- direction180 ={0x36,0xc0} ,                          -- 180°方向的命令,(不同屏幕ic会有差异)
+        -- direction270 = {0x36,0xA0},                          -- 270°方向的命令,(不同屏幕ic会有差异)
+        -- hbp = nil,                                           -- 水平后廊
+        -- hspw = nil,                                          -- 水平同步脉冲宽度
+        -- hfp = 0,                                             -- 水平前廊
+        -- vbp = 0,                                             -- 垂直后廊
+        -- vspw = 0,                                            -- 垂直同步脉冲宽度
+        -- vfp = 0,                                             -- 垂直前廊
+        -- initcmd = nil,                                       -- 自定义屏幕初始化命令表
+        -- flush_rate = nil,                                    -- 刷新率
+        -- spi_dev = nil,                                       -- spi设备,当port = "device"时有效,当port ≠ "device"时可不填或者填nil
+        -- init_in_service = false,                             -- 允许初始化在lcd service里运行,在后台初始化LCD,默认是false,Air8000/G/W/T/A、Air780EHM/EGH/EHV 支持填true,可加快初始化速度,默认SPI总线速度80M
+    }
+})

+ 65 - 0
module/Air8000/demo/accessory_board/AirLCD_1000/exeasyui/hw_drv/hw_gtfont_drv.lua

@@ -0,0 +1,65 @@
+--[[
+@module  hw_gtfont_drv
+@summary GTFont矢量字体驱动模块,使用AirFONTS_1000配件板
+@version 1.0
+@date    2025.12.3
+@author  江访
+@usage
+本文件为GTFont矢量字体、lcd和tp驱动模块,核心业务逻辑为:
+1、使用gtfont核心库驱动AirFONTS_1000矢量字库配件板;
+2、根据配置的字体、lcd和tp参数,初始化exEasyUI默认使用的字体、硬件显示和触摸;
+3、提供高质量矢量字体显示能力;
+
+本文件的对外接口有0个:
+1、require加载后自动执行初始化;
+
+@api ui.hw_init(config)
+@summary 初始化exEasyUI硬件系统
+@table config 硬件配置参数表,包含字体配置、LCD配置和触摸配置
+@field font_config table 字体配置参数
+@field lcd_config table LCD显示配置参数
+@field tp_config table 触摸屏配置参数
+@return nil
+
+@usage
+
+]]
+
+-- 使用gtfont矢量字库初始化exEasyUI硬件
+ui.hw_init({
+    font_config = { type = "gtfont", spi = { id = 1, cs = 12 }, size = 32 },
+
+    -- lcd_config参数填写可以参考合宙exlcd显示扩展库exlcd.init(param)接口说明:https://docs.openluat.com/osapi/ext/exlcd/#31-exlcdinitparam
+    lcd_config = {
+        lcd_model = "AirLCD_1000", -- LCD型号
+        -- pin_vcc = 24,                  -- 供电引脚,使用GPIO控制屏幕供电可配置
+        pin_rst = 36,              -- 复位引脚
+        pin_pwr = 1,               -- 背光控制引脚GPIO ID号
+        pin_pwm = 0,               -- 背光控制引脚PWM D号
+        port = lcd.HWID_0,         -- 驱动端口
+        -- pin_dc = 0xFF,          -- lcd数据/命令选择引脚GPIO ID号,使用lcd 专用 SPI 接口 lcd.HWID_0不需要填此参数,使用通用SPI接口需要赋值
+        direction = 0,             -- lcd屏幕方向 0:0° 1:90° 2:180° 3:270°,屏幕方向和分辨率保存一致
+        w = 320,                   -- lcd 水平分辨率
+        h = 480,                   -- lcd 竖直分辨率
+        xoffset = 0,               -- x偏移(不同屏幕ic 不同屏幕方向会有差异)
+        yoffset = 0,               -- y偏移(不同屏幕ic 不同屏幕方向会有差异)
+        sleepcmd = 0X10,           -- 睡眠命令,默认0X10
+        wakecmd = 0X11,            -- 唤醒命令,默认0X11
+        -- bus_speed = 50*1000*1000,                            -- SPI总线速度,不填默认50M,若速率要求更高需要进行设置
+        -- interface_mode = lcd.WIRE_4_BIT_8_INTERFACE_I,       -- lcd模式,默认lcd.WIRE_4_BIT_8_INTERFACE_I
+        -- direction0 = {0x36,0x00},                            -- 0°方向的命令,(不同屏幕ic会有差异)
+        -- direction90 = {0x36,0x60},                           -- 90°方向的命令,(不同屏幕ic会有差异)
+        -- direction180 ={0x36,0xc0} ,                          -- 180°方向的命令,(不同屏幕ic会有差异)
+        -- direction270 = {0x36,0xA0},                          -- 270°方向的命令,(不同屏幕ic会有差异)
+        -- hbp = nil,                                           -- 水平后廊
+        -- hspw = nil,                                          -- 水平同步脉冲宽度
+        -- hfp = 0,                                             -- 水平前廊
+        -- vbp = 0,                                             -- 垂直后廊
+        -- vspw = 0,                                            -- 垂直同步脉冲宽度
+        -- vfp = 0,                                             -- 垂直前廊
+        -- initcmd = nil,                                       -- 自定义屏幕初始化命令表
+        -- flush_rate = nil,                                    -- 刷新率
+        -- spi_dev = nil,                                       -- spi设备,当port = "device"时有效,当port ≠ "device"时可不填或者填nil
+        -- init_in_service = false,                             -- 允许初始化在lcd service里运行,在后台初始化LCD,默认是false,Air8000/G/W/T/A、Air780EHM/EGH/EHV 支持填true,可加快初始化速度,默认SPI总线速度80M
+    }
+})

+ 65 - 0
module/Air8000/demo/accessory_board/AirLCD_1000/exeasyui/hw_drv/hw_hzfont_drv.lua

@@ -0,0 +1,65 @@
+--[[
+@module  hw_hzfont_drv
+@summary HZFont矢量字体驱动模块,使用合宙内置矢量字库
+@version 1.0
+@date    2025.12.3
+@author  江访
+@usage
+本文件为HZFont矢量字体、lcd和tp驱动模块,核心业务逻辑为:
+1、使用Air780EHM/EHV/EGH V2020版本以上14号固件或114号固件内置的hzfont合宙矢量字库;
+2、根据配置的字体、lcd和tp参数,初始化exEasyUI默认使用的字体、硬件显示和触摸;
+3、提供动态字体大小调整和高质量字体显示能力;
+
+本文件的对外接口有0个:
+1、require加载后自动执行初始化;
+
+@api ui.hw_init(config)
+@summary 初始化exEasyUI硬件系统
+@table config 硬件配置参数表,包含字体配置、LCD配置和触摸配置
+@field font_config table 字体配置参数
+@field lcd_config table LCD显示配置参数
+@field tp_config table 触摸屏配置参数
+@return nil
+
+@usage
+
+]]
+
+-- 使用Air780EHM/EHV/EGH V2020版本以上14号固件内置的hzfont合宙矢量字库初始化exEasyUI硬件
+ui.hw_init({
+    font_config = { type = "hzfont", size = 24, antialias = -1 }, -- 默认-1,表示自动抗锯齿
+
+    -- lcd_config参数填写可以参考合宙exlcd显示扩展库exlcd.init(param)接口说明:https://docs.openluat.com/osapi/ext/exlcd/#31-exlcdinitparam
+    lcd_config = {
+        lcd_model = "AirLCD_1000", -- LCD型号
+        -- pin_vcc = 24,           -- 供电引脚,使用GPIO控制屏幕供电可配置
+        pin_rst = 36,              -- 复位引脚
+        pin_pwr = 1,               -- 背光控制引脚GPIO ID号
+        pin_pwm = 0,               -- 背光控制引脚PWM D号
+        port = lcd.HWID_0,         -- 驱动端口
+        -- pin_dc = 0xFF,          -- lcd数据/命令选择引脚GPIO ID号,使用lcd 专用 SPI 接口 lcd.HWID_0不需要填此参数,使用通用SPI接口需要赋值
+        direction = 0,             -- lcd屏幕方向 0:0° 1:90° 2:180° 3:270°,屏幕方向和分辨率保存一致
+        w = 320,                   -- lcd 水平分辨率
+        h = 480,                   -- lcd 竖直分辨率
+        xoffset = 0,               -- x偏移(不同屏幕ic 不同屏幕方向会有差异)
+        yoffset = 0,               -- y偏移(不同屏幕ic 不同屏幕方向会有差异)
+        sleepcmd = 0X10,           -- 睡眠命令,默认0X10
+        wakecmd = 0X11,            -- 唤醒命令,默认0X11
+        -- bus_speed = 50*1000*1000,                            -- SPI总线速度,不填默认50M,若速率要求更高需要进行设置
+        -- interface_mode = lcd.WIRE_4_BIT_8_INTERFACE_I,       -- lcd模式,默认lcd.WIRE_4_BIT_8_INTERFACE_I
+        -- direction0 = {0x36,0x00},                            -- 0°方向的命令,(不同屏幕ic会有差异)
+        -- direction90 = {0x36,0x60},                           -- 90°方向的命令,(不同屏幕ic会有差异)
+        -- direction180 ={0x36,0xc0} ,                          -- 180°方向的命令,(不同屏幕ic会有差异)
+        -- direction270 = {0x36,0xA0},                          -- 270°方向的命令,(不同屏幕ic会有差异)
+        -- hbp = nil,                                           -- 水平后廊
+        -- hspw = nil,                                          -- 水平同步脉冲宽度
+        -- hfp = 0,                                             -- 水平前廊
+        -- vbp = 0,                                             -- 垂直后廊
+        -- vspw = 0,                                            -- 垂直同步脉冲宽度
+        -- vfp = 0,                                             -- 垂直前廊
+        -- initcmd = nil,                                       -- 自定义屏幕初始化命令表
+        -- flush_rate = nil,                                    -- 刷新率
+        -- spi_dev = nil,                                       -- spi设备,当port = "device"时有效,当port ≠ "device"时可不填或者填nil
+        -- init_in_service = false,                             -- 允许初始化在lcd service里运行,在后台初始化LCD,默认是false,Air8000/G/W/T/A、Air780EHM/EGH/EHV 支持填true,可加快初始化速度,默认SPI总线速度80M
+    }
+})

binární
module/Air8000/demo/accessory_board/AirLCD_1000/exeasyui/images/1.jpg


binární
module/Air8000/demo/accessory_board/AirLCD_1000/exeasyui/images/2.jpg


binární
module/Air8000/demo/accessory_board/AirLCD_1000/exeasyui/images/3.jpg


Některé soubory nejsou zobrazeny, neboť je v těchto rozdílových datech změněno mnoho souborů