diff --git a/App/main.c b/App/main.c index 8bec8c8..83acd0b 100644 --- a/App/main.c +++ b/App/main.c @@ -8,10 +8,17 @@ * 硬件引脚与全局变量定义 * ========================================================================= */ -// 三色共阳极 LED 控制引脚(输出0亮,输出1灭) -sbit LED_R = P0^0; -sbit LED_G = P2^7; -sbit LED_B = P2^6; +// 软件虚拟 LED 状态变量(0表示亮,1表示灭,兼容原有代码逻辑) +bit LED_R = 1; +bit LED_G = 1; +bit LED_B = 1; + +bit last_hw_r = 1; +bit last_hw_g = 1; +bit last_hw_b = 1; + +// 实际 WS2812 物理控制引脚(接 P0.0) +sbit WS2812_DI = P0^0; // 振动马达控制引脚(输出1开启振动,输出0停止) sbit MOTOR = P2^5; @@ -56,25 +63,25 @@ volatile KeyEvent key_event_buf = KEY_EVENT_NONE; // 按键事件缓冲区 * 辅助映射表及前置声明 * ========================================================================= */ -// 各防区类型的默认英文防区名称 (供报警显示使用) +// 各防区类型的默认法语防区名称 (供报警显示使用,最大15字符) char *code default_names[5] = { - "FRONT DOOR", // 0: 门磁 - "LIVING ROOM", // 1: 红外 - "KITCHEN SMOKE", // 2: 烟感 - "KITCHEN GAS", // 3: 燃气 - "BATHROOM WATER" // 4: 水浸 + "PORTE ENTREE", // 0: 门磁 + "SALON PIR", // 1: 红外 + "CUISINE FUMEE", // 2: 烟感 + "CUISINE GAZ", // 3: 燃气 + "SDB EAU" // 4: 水浸 }; // 各防区类型的默认防区类别 (0: 24小时防区-撤防亦报警, 1: 普通防区-仅布防下报警) u8 code default_zones[5] = {1, 1, 0, 0, 0}; -// 对码选择菜单显示的各条目字符串 +// 对码选择菜单显示的各条目字符串 (法语,最大15字符) char *code menu_items[5] = { - "DOOR SENSOR", // 0 - "PIR MOTION", // 1 - "SMOKE DETECT", // 2 - "GAS LEAK", // 3 - "WATER FLOOD" // 4 + "DETEC. PORTE", // 0 + "DETEC. PIR", // 1 + "DETEC. FUMEE", // 2 + "DETEC. GAZ", // 3 + "DETEC. EAU" // 4 }; // 本地函数前置声明 @@ -114,6 +121,108 @@ void UI_ShowPairFailPage(u8 is_timeout); void UI_ShowAlarmPage(char *sensor_name, u8 zone); void UI_ShowSosPage(void); +/* ========================================================================= + * WS2812B 幻彩 LED 单线驱动函数 (24MHz 1T 模式下精准时序控制) + * ========================================================================= */ + +void WS2812_Write24Bit(u8 g, u8 r, u8 b) +{ + u8 i; + + // 关全局中断,防止被其它高优先中断截断导致时序错乱 + EA = 0; + + // 绿光通道 (Green) + for (i = 0; i < 8; i++) + { + if (g & 0x80) + { + // 发送 "1" 码:高电平维持 ~0.8us,低电平维持 ~0.4us + WS2812_DI = 1; + _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); + _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); + _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); + WS2812_DI = 0; + _nop_(); + } + else + { + // 发送 "0" 码:高电平维持 ~0.35us,低电平维持 ~0.9us + WS2812_DI = 1; + _nop_(); _nop_(); _nop_(); _nop_(); + WS2812_DI = 0; + _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); + _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); + _nop_(); _nop_(); _nop_(); + } + g <<= 1; + } + + // 红光通道 (Red) + for (i = 0; i < 8; i++) + { + if (r & 0x80) + { + WS2812_DI = 1; + _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); + _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); + _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); + WS2812_DI = 0; + _nop_(); + } + else + { + WS2812_DI = 1; + _nop_(); _nop_(); _nop_(); _nop_(); + WS2812_DI = 0; + _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); + _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); + _nop_(); _nop_(); _nop_(); + } + r <<= 1; + } + + // 蓝光通道 (Blue) + for (i = 0; i < 8; i++) + { + if (b & 0x80) + { + WS2812_DI = 1; + _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); + _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); + _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); + WS2812_DI = 0; + _nop_(); + } + else + { + WS2812_DI = 1; + _nop_(); _nop_(); _nop_(); _nop_(); + WS2812_DI = 0; + _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); + _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); + _nop_(); _nop_(); _nop_(); + } + b <<= 1; + } + + // 开全局中断 + EA = 1; +} + +void WS2812_Reset(void) +{ + WS2812_DI = 0; + // 维持低电平 100us 以上以完成数据锁存 (24MHz 计数约2400) + { + u16 cnt; + for (cnt = 0; cnt < 250; cnt++) + { + _nop_(); + } + } +} + /* ========================================================================= * 延时与时间获取辅助函数 (12T/1T 自适应) * ========================================================================= */ @@ -453,26 +562,26 @@ void UI_ShowClockPage(SystemState state, u8 hour, u8 min) time_str[5] = '\0'; LCD_ShowString16x32Centered(100, time_str, (state == STATE_ARMED) ? COLOR_WHITE : ((state == STATE_DISARMED) ? COLOR_WHITE : COLOR_CYAN), COLOR_BLACK); - // 静态星期和日期 - LCD_ShowStringCentered(155, "THU 07-02", COLOR_GRAY, COLOR_BLACK); + // 静态星期和日期 (法语 JEU) + LCD_ShowStringCentered(155, "JEU 07-02", COLOR_GRAY, COLOR_BLACK); // 屏幕底端布撤防状态标识 (Y=202 避开屏幕下圆弧) if(state == STATE_ARMED) { - LCD_DrawMonoBitmap(30, 202, 16, 16, bmp_lock_16x16, COLOR_RED, COLOR_BLACK); - LCD_ShowString(50, 202, "ARMED", COLOR_RED, COLOR_BLACK); + LCD_DrawMonoBitmap(32, 202, 16, 16, bmp_lock_16x16, COLOR_RED, COLOR_BLACK); + LCD_ShowString(52, 202, "ARME", COLOR_RED, COLOR_BLACK); } else if(state == STATE_DISARMED) { - LCD_DrawMonoBitmap(26, 202, 16, 16, bmp_unlock_16x16, COLOR_GREEN, COLOR_BLACK); - LCD_ShowString(46, 202, "DISARM", COLOR_GREEN, COLOR_BLACK); + LCD_DrawMonoBitmap(22, 202, 16, 16, bmp_unlock_16x16, COLOR_GREEN, COLOR_BLACK); + LCD_ShowString(42, 202, "DESARME", COLOR_GREEN, COLOR_BLACK); } } void UI_ShowPairMenuPage(u8 selected_index) { LCD_Clear(COLOR_BLACK); - LCD_ShowStringCentered(30, "PAIR MENU", COLOR_AMBER, COLOR_BLACK); + LCD_ShowStringCentered(30, "MENU APPAI.", COLOR_AMBER, COLOR_BLACK); // 绘制上一项 if (selected_index > 0) @@ -510,8 +619,8 @@ void UI_ShowPairConfirmPage(u32 addr) LCD_DrawRectBorder(4, 4, 112, 232, COLOR_GRAY); LCD_DrawMonoBitmap(44, 60, 32, 32, bmp_confirm_lock_32x32, COLOR_AMBER, COLOR_BLACK); - LCD_ShowStringCentered(135, "CODE FOUND", COLOR_WHITE, COLOR_BLACK); - LCD_ShowStringCentered(160, "HOLD SOS TO SAVE", COLOR_AMBER, COLOR_BLACK); + LCD_ShowStringCentered(135, "CODE TROUVE", COLOR_WHITE, COLOR_BLACK); + LCD_ShowStringCentered(160, "MAINTENIR SOS", COLOR_AMBER, COLOR_BLACK); FormatHex(addr, hex_str); LCD_ShowStringCentered(185, hex_str, COLOR_GRAY, COLOR_BLACK); @@ -523,7 +632,7 @@ void UI_ShowPairSuccessPage(u32 addr) LCD_Clear(COLOR_BLACK); LCD_DrawMonoBitmap(44, 75, 32, 32, bmp_checkmark_32x32, COLOR_GREEN, COLOR_BLACK); - LCD_ShowStringCentered(135, "PAIR SUCCESS", COLOR_GREEN, COLOR_BLACK); + LCD_ShowStringCentered(135, "SUCCES APPAI.", COLOR_GREEN, COLOR_BLACK); FormatHex(addr, hex_str); LCD_ShowStringCentered(165, hex_str, COLOR_GRAY, COLOR_BLACK); @@ -536,13 +645,13 @@ void UI_ShowPairFailPage(u8 is_timeout) LCD_DrawMonoBitmap(44, 75, 32, 32, bmp_cross_32x32, COLOR_RED, COLOR_BLACK); if (is_timeout) { - LCD_ShowStringCentered(145, "PAIR TIMEOUT", COLOR_RED, COLOR_BLACK); - LCD_ShowStringCentered(170, "NO SIGNAL FOUND", COLOR_GRAY, COLOR_BLACK); + LCD_ShowStringCentered(145, "DELAI DEPASSE", COLOR_RED, COLOR_BLACK); + LCD_ShowStringCentered(170, "AUCUN SIGNAL", COLOR_GRAY, COLOR_BLACK); } else { - LCD_ShowStringCentered(145, "PAIR FAILED", COLOR_RED, COLOR_BLACK); - LCD_ShowStringCentered(170, "TYPE MISMATCH", COLOR_GRAY, COLOR_BLACK); + LCD_ShowStringCentered(145, "ECHEC APPAI.", COLOR_RED, COLOR_BLACK); + LCD_ShowStringCentered(170, "TYPE INCORRECT", COLOR_GRAY, COLOR_BLACK); } } @@ -559,7 +668,7 @@ void UI_ShowAlarmPage(char *sensor_name, u8 zone) zone_str[0] = 'Z'; zone_str[1] = 'O'; zone_str[2] = 'N'; zone_str[3] = 'E'; zone_str[4] = ' '; zone_str[5] = '0' + zone; zone_str[6] = ' '; zone_str[7] = '|'; zone_str[8] = ' '; zone_str[9] = 'A'; zone_str[10] = 'L'; - zone_str[11] = 'A'; zone_str[12] = 'R'; zone_str[13] = 'M'; zone_str[14] = '\0'; + zone_str[11] = 'A'; zone_str[12] = 'R'; zone_str[13] = 'M'; zone_str[14] = 'E'; zone_str[15] = '\0'; LCD_ShowStringCentered(155, zone_str, COLOR_WHITE, COLOR_BLACK); } @@ -572,8 +681,8 @@ void UI_ShowSosPage(void) LCD_DrawMonoBitmap(44, 55, 32, 32, bmp_siren_32x32, COLOR_RED, COLOR_BLACK); - LCD_ShowStringCentered(125, "EMIT SOS", COLOR_RED, COLOR_BLACK); - LCD_ShowStringCentered(155, "BAND TRIGGERED", COLOR_WHITE, COLOR_BLACK); + LCD_ShowStringCentered(125, "SOS EMIS", COLOR_RED, COLOR_BLACK); + LCD_ShowStringCentered(155, "BRACELET ACTIF", COLOR_WHITE, COLOR_BLACK); } void UI_ShowBindingPage(void) @@ -581,8 +690,8 @@ void UI_ShowBindingPage(void) LCD_Clear(COLOR_BLACK); LCD_DrawRectBorder(4, 4, 112, 232, COLOR_CYAN); LCD_DrawMonoBitmap(44, 60, 32, 32, bmp_confirm_lock_32x32, COLOR_CYAN, COLOR_BLACK); - LCD_ShowStringCentered(135, "BINDING IPC", COLOR_WHITE, COLOR_BLACK); - LCD_ShowStringCentered(160, "TRANSMITTING...", COLOR_CYAN, COLOR_BLACK); + LCD_ShowStringCentered(135, "LIAISON IPC", COLOR_WHITE, COLOR_BLACK); + LCD_ShowStringCentered(160, "ENVOI EN COURS", COLOR_CYAN, COLOR_BLACK); } /* ========================================================================= @@ -832,6 +941,17 @@ void Timer1_Isr(void) interrupt 3 } } } + + // 5. WS2812B 实际硬件驱动刷新 (当虚拟 LED 寄存器发生变化时) + if (LED_R != last_hw_r || LED_G != last_hw_g || LED_B != last_hw_b) + { + last_hw_r = LED_R; + last_hw_g = LED_G; + last_hw_b = LED_B; + + WS2812_Write24Bit(LED_G ? 0 : 255, LED_R ? 0 : 255, LED_B ? 0 : 255); + WS2812_Reset(); + } } /* ========================================================================= @@ -1114,8 +1234,8 @@ void main(void) { RF_SetMode(1); // 开启射频解调芯片,开始抓包 LCD_Clear(COLOR_BLACK); - LCD_ShowStringCentered(135, "SEARCHING...", COLOR_WHITE, COLOR_BLACK); - LCD_ShowStringCentered(160, "TRIGGER SENSOR", COLOR_CYAN, COLOR_BLACK); + LCD_ShowStringCentered(135, "RECHERCHE...", COLOR_WHITE, COLOR_BLACK); + LCD_ShowStringCentered(160, "DECLENCH. CAPT", COLOR_CYAN, COLOR_BLACK); UI_ShowPairWaitPage(0); last_radar_sec = 99; // 强行重置雷达波纹帧计数器 } @@ -1305,18 +1425,28 @@ void GPIO_Init(void) P0M1 &= ~((1 << 1) | (1 << 2) | (1 << 3)); P0M0 &= ~((1 << 1) | (1 << 2) | (1 << 3)); - // 配置 P0.0 (LED_R) 为推挽输出,低电平灭,初始化默认输出 1 熄灭 + // 配置 P0.0 (WS2812_DI) 为推挽输出,初始化默认输出 0 (Reset/Idle) P0M1 &= ~(1 << 0); P0M0 |= (1 << 0); - LED_R = 1; + WS2812_DI = 0; - // 配置 P2.5 (MOTOR) 震动马达、P2.6 (LED_B) 蓝灯、P2.7 (LED_G) 绿灯为推挽输出 + // 配置 P2.5 (MOTOR) 震动马达、P2.6 (LED_B)、P2.7 (LED_G) 为推挽输出 P2M1 &= ~((1 << 5) | (1 << 6) | (1 << 7)); P2M0 |= ((1 << 5) | (1 << 6) | (1 << 7)); + + // 初始化虚拟 LED 与跟踪变量为熄灭状态 (1代表灭) + LED_R = 1; LED_G = 1; LED_B = 1; + last_hw_r = 1; + last_hw_g = 1; + last_hw_b = 1; MOTOR = 0; // 默认马达静止 + // 初始化 WS2812 灯珠为熄灭状态 (0, 0, 0) + WS2812_Write24Bit(0, 0, 0); + WS2812_Reset(); + // 配置 P3.4 (SGM_CTRL) 为推挽输出,P3.1 (TxD) 为推挽输出,P3.0 (RxD) 为双向口输入 P3M1 &= ~((1 << 0) | (1 << 1) | (1 << 4)); P3M0 &= ~(1 << 0);