docs: 为所有代码源文件添加详细中文注释,并在设计文档中纠正GPIO物理引脚映射表
This commit is contained in:
365
Drivers/lcd.c
365
Drivers/lcd.c
@@ -3,149 +3,179 @@
|
||||
#include "../App/config.h"
|
||||
#include "intrins.h"
|
||||
|
||||
// RM69310 屏幕接口引脚
|
||||
sbit LCD_RST = P1^0; // 硬件复位
|
||||
sbit LCD_DCX = P1^1; // 数据/命令选择
|
||||
sbit LCD_SDI = P1^4; // SPI 数据输入 (MOSI)
|
||||
sbit LCD_SCL = P1^5; // SPI 时钟 (SCLK)
|
||||
sbit LCD_CS = P1^6; // SPI 片选 (CS)
|
||||
// ====== RM69310 Truly AMOLED 屏幕 SPI 引脚映射 (8051 bit 寻址控制) ======
|
||||
sbit LCD_RST = P1^0; // 屏幕硬件复位引脚 (RESET),低电平复位
|
||||
sbit LCD_DCX = P1^1; // 串行数据 / 命令选择控制线 (1:数据, 0:指令)
|
||||
sbit LCD_SDI = P1^4; // SPI 主机输出从机输入数据线 (MOSI)
|
||||
sbit LCD_SCL = P1^5; // SPI 串行工作时钟线 (SCLK)
|
||||
sbit LCD_CS = P1^6; // SPI 物理片选选择端 (CS),低电平选通屏幕
|
||||
|
||||
// SGM3833 电源芯片控制脚
|
||||
sbit SGM_CTRL = P3^4;
|
||||
// ====== SGM3833 升压/负压三路 OLED 屏电源 PMIC 使能引脚 ======
|
||||
sbit SGM_CTRL = P3^4; // 单线脉冲通信控制线
|
||||
|
||||
// External delay functions defined in main.c
|
||||
// 引用外部延时函数 (定义在 system.c 中)
|
||||
extern void Delay_ms(u16 ms);
|
||||
extern void Delay10us(void);
|
||||
|
||||
/**
|
||||
* @brief 通过脉冲序列为 SGM3833 屏幕电源管理芯片写入开启配置
|
||||
* @param pulse_cnt 要写入的脉冲个数 (本系统传入 27 次脉冲开启正负双电轨)
|
||||
* @details SGM3833 采用单线边沿脉冲计数协议进行数字控制。
|
||||
* - tOFF 必须大于 5ms 才能完全复位芯片进入指令模式。
|
||||
* - 每次电平拉低低电平持续 10us 后拉高,形成一个脉冲计数值。
|
||||
* - 发送完毕后需拉高并维持 5ms 以上,以便将计数值存入寄存器,锁定目标电压轨开启。
|
||||
*/
|
||||
void SGM_SendPulse(u8 pulse_cnt)
|
||||
{
|
||||
u8 i;
|
||||
SGM_CTRL = 0;
|
||||
Delay_ms(5); // 确保进入关断/复位状态 (tOFF)
|
||||
Delay_ms(5); // 确保电源芯片进入关断/复位状态 (tOFF)
|
||||
SGM_CTRL = 1;
|
||||
Delay_ms(5); // 确保充分初始化 (tINIT >= 300us)
|
||||
Delay_ms(5); // 确保进入初始化状态 (tINIT >= 300us)
|
||||
for(i = 0; i < pulse_cnt; i++)
|
||||
{
|
||||
SGM_CTRL = 0;
|
||||
Delay10us();
|
||||
Delay10us(); // 保持低电平 10us
|
||||
SGM_CTRL = 1;
|
||||
Delay10us();
|
||||
Delay10us(); // 保持高电平 10us
|
||||
}
|
||||
SGM_CTRL = 1;
|
||||
Delay_ms(5); // 确保数据稳定存储 (tSTORE >= 80us)
|
||||
Delay_ms(5); // 确保数据稳定存储锁定 (tSTORE >= 80us)
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 软件模拟 8 位串行 SPI 字节数据发送
|
||||
* @param dat 待发送的 1 字节数据
|
||||
* @details 采用典型的 bit-banging 模式:时钟 SCL 在发送前置低,将数据 MSB 依次提取后赋值给
|
||||
* 数据线 SDI,随后拉高 SCL 完成一个 bit 的沿采样,循环 8 次发送完成。
|
||||
*/
|
||||
void SPI_SendData(u8 dat)
|
||||
{
|
||||
u8 i;
|
||||
for(i = 0; i < 8; i++)
|
||||
{
|
||||
LCD_SCL = 0;
|
||||
LCD_SCL = 0; // 拉低时钟,准备更新数据
|
||||
if(dat & 0x80)
|
||||
LCD_SDI = 1;
|
||||
else
|
||||
LCD_SDI = 0;
|
||||
_nop_();
|
||||
LCD_SCL = 1;
|
||||
LCD_SCL = 1; // 拉高时钟,触发屏幕在上升沿采样数据位
|
||||
_nop_();
|
||||
dat <<= 1;
|
||||
dat <<= 1; // 左移一位,准备发送下一位
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 向屏幕控制器写入单字节控制指令 (DCX=0)
|
||||
*/
|
||||
void WriteComm(u8 cmd)
|
||||
{
|
||||
LCD_CS = 0;
|
||||
LCD_DCX = 0; // 命令模式
|
||||
LCD_CS = 0; // 开启片选
|
||||
LCD_DCX = 0; // 切换为指令传输模式
|
||||
SPI_SendData(cmd);
|
||||
LCD_CS = 1;
|
||||
LCD_CS = 1; // 关闭片选
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 向屏幕控制器写入单字节显示数据 (DCX=1)
|
||||
*/
|
||||
void WriteData(u8 dat)
|
||||
{
|
||||
LCD_CS = 0;
|
||||
LCD_DCX = 1; // 数据模式
|
||||
LCD_CS = 0; // 开启片选
|
||||
LCD_DCX = 1; // 切换为数据传输模式
|
||||
SPI_SendData(dat);
|
||||
LCD_CS = 1;
|
||||
LCD_CS = 1; // 关闭片选
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 按照 RM69310 物理时序执行屏幕硬件复位
|
||||
*/
|
||||
void LCD_Reset(void)
|
||||
{
|
||||
LCD_RST = 1;
|
||||
Delay_ms(10);
|
||||
LCD_RST = 0;
|
||||
LCD_RST = 0; // 拉低复位引脚进入硬件复位状态
|
||||
Delay_ms(20);
|
||||
LCD_RST = 1;
|
||||
Delay_ms(120);
|
||||
LCD_RST = 1; // 重新拉高引脚
|
||||
Delay_ms(120); // 必须等待至少 120ms 才能执行后续的初始化
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 初始化 Truly AMOLED 屏幕控制器 RM69310
|
||||
* @details 先配置控制 GPIO 为推挽输出,接着根据 Truly 信利原厂提供的初始化指令序列,
|
||||
* 完成偏置电压、刷新周期、Gamma 参数、跳帧逻辑及色彩空间模式等一系列初始化工作。
|
||||
*/
|
||||
void LCD_Init(void)
|
||||
{
|
||||
// Configure LCD pins as push-pull output
|
||||
// 1. 配置 P1 端口的 LCD_RST/DCX/SDI/SCL/CS 引脚全部为推挽输出模式 (M1=0, M0=1)
|
||||
P1M1 &= ~((1<<0) | (1<<1) | (1<<4) | (1<<5) | (1<<6));
|
||||
P1M0 |= ((1<<0) | (1<<1) | (1<<4) | (1<<5) | (1<<6));
|
||||
|
||||
// Configure P3.4 (SGM_CTRL) as push-pull output
|
||||
// 2. 配置 P3.4 (SGM_CTRL) 为推挽输出模式
|
||||
P3M1 &= ~(1 << 4);
|
||||
P3M0 |= (1 << 4);
|
||||
|
||||
// 设置各控制引脚初始默认电平
|
||||
SGM_CTRL = 1;
|
||||
LCD_CS = 1;
|
||||
LCD_SCL = 1;
|
||||
LCD_RST = 1;
|
||||
LCD_DCX = 1;
|
||||
|
||||
LCD_Reset();
|
||||
LCD_Reset(); // 硬件复位屏幕
|
||||
|
||||
// 寄存器配置(源自 Truly 信利官方初始化配置)
|
||||
WriteComm(0xFE);WriteData(0x01);
|
||||
WriteComm(0x04);WriteData(0xa0); // SPI write ram enable
|
||||
WriteComm(0x05);WriteData(0x70); // 128RGB
|
||||
WriteComm(0x06);WriteData(0x3C); // NL = 240 line
|
||||
WriteComm(0x25);WriteData(0x06); // normal mode: gamma1
|
||||
WriteComm(0x26);WriteData(0x80); // T1A = 280
|
||||
WriteComm(0x27);WriteData(0x12); // normal mode VBP = 12
|
||||
WriteComm(0x28);WriteData(0x12); // normal mode VFP = 8
|
||||
WriteComm(0x2A);WriteData(0x06); // idle mode: gamma1
|
||||
WriteComm(0x2B);WriteData(0x80); // T1B = 280
|
||||
WriteComm(0x2D);WriteData(0x12); // idle mode VBP = 12
|
||||
WriteComm(0x2F);WriteData(0x12); // idle mode VFP = 8
|
||||
WriteComm(0x37);WriteData(0x0C); // precharge to VGSP
|
||||
WriteComm(0x6D);WriteData(0x18); // skip frame VGMP and VGSP regulator off
|
||||
WriteComm(0x29);WriteData(0x01); // normal mode skip frame off
|
||||
WriteComm(0x30);WriteData(0x43); // idle mode skip frame = 60/2 = 30Hz
|
||||
WriteComm(0x0E);WriteData(0x83); // AVDD = 5.6V normal mode
|
||||
WriteComm(0x0F);WriteData(0x83); // AVDD = 5.6V idle mode
|
||||
WriteComm(0x10);WriteData(0x71); // AVDD = 3xVCI
|
||||
WriteComm(0x11);WriteData(0xb3); // VCL = -2xVCI
|
||||
WriteComm(0x12);WriteData(0xb3); // VCL = -2xVCI
|
||||
WriteComm(0x13);WriteData(0x80); // VGH = AVDD normal mode
|
||||
WriteComm(0x14);WriteData(0x80); // VGH = AVDD idle mode
|
||||
WriteComm(0x15);WriteData(0x81); // VGL = VCL - VCI normal mode
|
||||
WriteComm(0x16);WriteData(0x81); // VGL = VCL - VCI idle mode
|
||||
WriteComm(0x18);WriteData(0x66); // VGHR = 6V normal/idle mode
|
||||
WriteComm(0x19);WriteData(0x44); // VGLR = -6V normal/idle mode
|
||||
WriteComm(0x1E);WriteData(0x02); // Switch EQ on
|
||||
WriteComm(0x5B);WriteData(0x10); // VREFN5 on
|
||||
WriteComm(0x62);WriteData(0x19); // VREFN5 = -3V normal mode
|
||||
WriteComm(0x63);WriteData(0x19); // VREFN5 = -3V idle mode
|
||||
WriteComm(0x70);WriteData(0x55); // display off SD to AVDD
|
||||
WriteComm(0x1D);WriteData(0x02); // Switch EQ on
|
||||
WriteComm(0x89);WriteData(0x18); // VGMP = 5.5V
|
||||
WriteComm(0x8A);WriteData(0xb9); // VGSP = 2V
|
||||
WriteComm(0x8B);WriteData(0x01); // VGMP VGSP high byte
|
||||
WriteComm(0x8C);WriteData(0x10); // VGMP = 5.4V
|
||||
WriteComm(0x8D);WriteData(0x90); // VGSP = 2V
|
||||
WriteComm(0x8E);WriteData(0x01); // VGMP VGSP high byte
|
||||
WriteComm(0x6E);WriteData(0x0A); // MIPI interface off
|
||||
WriteComm(0x6A);WriteData(0x05); // MUX
|
||||
WriteComm(0x3A);WriteData(0x08); // T1_sd
|
||||
WriteComm(0x3B);WriteData(0x00); // Tp_sd
|
||||
WriteComm(0x3D);WriteData(0x16); // Th_sd
|
||||
WriteComm(0x3F);WriteData(0x27); // Tsw_sd
|
||||
WriteComm(0x40);WriteData(0x0F); // Thsw_sd
|
||||
WriteComm(0x41);WriteData(0x0D); // Thsd_sd
|
||||
// ====== 写入 RM69310 原厂寄存器初始化指令序列 ======
|
||||
WriteComm(0xFE);WriteData(0x01); // 切换至扩展 Page 1 寄存器页
|
||||
WriteComm(0x04);WriteData(0xa0); // 开启 SPI 写入显存 RAM 允许
|
||||
WriteComm(0x05);WriteData(0x70); // 配置屏幕尺寸规格为 128RGB
|
||||
WriteComm(0x06);WriteData(0x3C); // 设定垂直有效扫描线 NL = 240
|
||||
WriteComm(0x25);WriteData(0x06); // 正常模式伽马设定Gamma1
|
||||
WriteComm(0x26);WriteData(0x80); // T1A 时序常数 = 280
|
||||
WriteComm(0x27);WriteData(0x12); // 正常模式消隐后肩 VBP = 12
|
||||
WriteComm(0x28);WriteData(0x12); // 正常模式消隐前肩 VFP = 8
|
||||
WriteComm(0x2A);WriteData(0x06); // 空闲模式伽马设定 Gamma1
|
||||
WriteComm(0x2B);WriteData(0x80); // T1B 时序常数 = 280
|
||||
WriteComm(0x2D);WriteData(0x12); // 空闲消隐后肩 = 12
|
||||
WriteComm(0x2F);WriteData(0x12); // 空闲消隐前肩 = 8
|
||||
WriteComm(0x37);WriteData(0x0C); // 预充电控制电压 VGSP
|
||||
WriteComm(0x6D);WriteData(0x18); // 关闭跳帧 VGMP 和 VGSP 稳压调节
|
||||
WriteComm(0x29);WriteData(0x01); // 关闭正常模式的跳帧
|
||||
WriteComm(0x30);WriteData(0x43); // 设定空闲模式跳帧使刷新频率降为 30Hz
|
||||
WriteComm(0x0E);WriteData(0x83); // 正常模式 AVDD 电压 = 5.6V
|
||||
WriteComm(0x0F);WriteData(0x83); // 空闲模式 AVDD = 5.6V
|
||||
WriteComm(0x10);WriteData(0x71); // AVDD 三倍 VCI 升压控制
|
||||
WriteComm(0x11);WriteData(0xb3); // VCL = -2倍 VCI 产生负压
|
||||
WriteComm(0x12);WriteData(0xb3);
|
||||
WriteComm(0x13);WriteData(0x80); // 正常模式下 VGH 等于 AVDD
|
||||
WriteComm(0x14);WriteData(0x80); // 空闲模式下 VGH 等于 AVDD
|
||||
WriteComm(0x15);WriteData(0x81); // 正常模式下低侧门电压 VGL = VCL - VCI
|
||||
WriteComm(0x16);WriteData(0x81);
|
||||
WriteComm(0x18);WriteData(0x66); // 偏置电平 VGHR = 6V
|
||||
WriteComm(0x19);WriteData(0x44); // 偏置电平 VGLR = -6V
|
||||
WriteComm(0x1E);WriteData(0x02); // 开启电荷平衡 EQ 调制
|
||||
WriteComm(0x5B);WriteData(0x10); // 启用参考负压 VREFN5
|
||||
WriteComm(0x62);WriteData(0x19); // 正常模式 VREFN5 电平 = -3V
|
||||
WriteComm(0x63);WriteData(0x19); // 空闲模式 VREFN5 = -3V
|
||||
WriteComm(0x70);WriteData(0x55); // 关闭显示时源极驱动器至 AVDD
|
||||
WriteComm(0x1D);WriteData(0x02); // 启用 EQ 平衡
|
||||
WriteComm(0x89);WriteData(0x18); // 控制偏置 VGMP = 5.5V
|
||||
WriteComm(0x8A);WriteData(0xb9); // 控制偏置 VGSP = 2V
|
||||
WriteComm(0x8B);WriteData(0x01); // 偏置高字节
|
||||
WriteComm(0x8C);WriteData(0x10); // VGMP 设为 5.4V
|
||||
WriteComm(0x8D);WriteData(0x90); // VGSP 设为 2V
|
||||
WriteComm(0x8E);WriteData(0x01);
|
||||
WriteComm(0x6E);WriteData(0x0A); // 关闭 MIPI 高速数据接口以选择 SPI 驱动方式
|
||||
WriteComm(0x6A);WriteData(0x05); // 配置 MUX 选择器
|
||||
WriteComm(0x3A);WriteData(0x08); // 行延迟常数 T1_sd
|
||||
WriteComm(0x3B);WriteData(0x00); // 帧延迟常数 Tp_sd
|
||||
WriteComm(0x3D);WriteData(0x16); // 保持延迟常数 Th_sd
|
||||
WriteComm(0x3F);WriteData(0x27); // 切换时序 Tsw_sd
|
||||
WriteComm(0x40);WriteData(0x0F); // 切换保持 Thsw_sd
|
||||
WriteComm(0x41);WriteData(0x0D); // 切换释放 Thsd_sd
|
||||
|
||||
WriteComm(0x42);WriteData(0x14); // Mux 142536
|
||||
// 如下配置模拟通道多路选通时序
|
||||
WriteComm(0x42);WriteData(0x14);
|
||||
WriteComm(0x43);WriteData(0x41);
|
||||
WriteComm(0x44);WriteData(0x25);
|
||||
WriteComm(0x45);WriteData(0x52);
|
||||
@@ -157,7 +187,7 @@ void LCD_Init(void)
|
||||
WriteComm(0x4B);WriteData(0x52);
|
||||
WriteComm(0x4C);WriteData(0x36);
|
||||
WriteComm(0x4D);WriteData(0x63);
|
||||
WriteComm(0x4E);WriteData(0x14); // Data R1R2G1G2B1B2
|
||||
WriteComm(0x4E);WriteData(0x14);
|
||||
WriteComm(0x4F);WriteData(0x41);
|
||||
WriteComm(0x50);WriteData(0x25);
|
||||
WriteComm(0x51);WriteData(0x52);
|
||||
@@ -170,16 +200,16 @@ void LCD_Init(void)
|
||||
WriteComm(0x58);WriteData(0x36);
|
||||
WriteComm(0x59);WriteData(0x63);
|
||||
|
||||
WriteComm(0x66);WriteData(0x90); // idle mode internal power
|
||||
WriteComm(0x67);WriteData(0x40); // internal power delay 1 frame off
|
||||
WriteComm(0x72);WriteData(0x1A); // internal OVDD = 4.6V
|
||||
WriteComm(0x73);WriteData(0x07); // internal OVSS = -2V
|
||||
WriteComm(0x74);WriteData(0x0C); // OVDD power from AVDD
|
||||
WriteComm(0x6A);WriteData(0x3D); // swire 61=0x3D pulse
|
||||
WriteComm(0x6B);WriteData(0x29); // swire 41=0x29 pulse
|
||||
WriteComm(0x66);WriteData(0x90); // 空闲模式使用内部集成供电
|
||||
WriteComm(0x67);WriteData(0x40); // 内部延迟 1 帧切断电源
|
||||
WriteComm(0x72);WriteData(0x1A); // 内部核心 OVDD 电压 = 4.6V
|
||||
WriteComm(0x73);WriteData(0x07); // 内部核心 OVSS 电压 = -2V
|
||||
WriteComm(0x74);WriteData(0x0C); // OVDD 的最终输入来源设为 AVDD
|
||||
WriteComm(0x6A);WriteData(0x3D); // 向单线接口发送脉冲,配置升压
|
||||
WriteComm(0x6B);WriteData(0x29);
|
||||
|
||||
WriteComm(0xFE);WriteData(0x04);
|
||||
WriteComm(0x5E);WriteData(0x01);
|
||||
WriteComm(0xFE);WriteData(0x04); // 切换至 Page 4
|
||||
WriteComm(0x5E);WriteData(0x01);
|
||||
WriteComm(0x5F);WriteData(0xB8);
|
||||
WriteComm(0x60);WriteData(0xBB);
|
||||
WriteComm(0x61);WriteData(0xBB);
|
||||
@@ -190,7 +220,7 @@ void LCD_Init(void)
|
||||
WriteComm(0x79);WriteData(0xBB);
|
||||
WriteComm(0x7A);WriteData(0xBB);
|
||||
|
||||
WriteComm(0x00);WriteData(0xDC); // SN_CK1
|
||||
WriteComm(0x00);WriteData(0xDC); // 扫描时钟 CK1 寄存器配置
|
||||
WriteComm(0x01);WriteData(0x00);
|
||||
WriteComm(0x02);WriteData(0x02);
|
||||
WriteComm(0x03);WriteData(0x00);
|
||||
@@ -200,7 +230,7 @@ void LCD_Init(void)
|
||||
WriteComm(0x07);WriteData(0x0A);
|
||||
WriteComm(0x08);WriteData(0x00);
|
||||
|
||||
WriteComm(0x09);WriteData(0xDC); // SN_CK2
|
||||
WriteComm(0x09);WriteData(0xDC); // 扫描时钟 CK2 寄存器配置
|
||||
WriteComm(0x0a);WriteData(0x00);
|
||||
WriteComm(0x0b);WriteData(0x02);
|
||||
WriteComm(0x0C);WriteData(0x00);
|
||||
@@ -210,7 +240,7 @@ void LCD_Init(void)
|
||||
WriteComm(0x10);WriteData(0x0A);
|
||||
WriteComm(0x11);WriteData(0x00);
|
||||
|
||||
WriteComm(0x12);WriteData(0xCC); // EM_CK1
|
||||
WriteComm(0x12);WriteData(0xCC); // 发光逻辑 EM_CK1 配置
|
||||
WriteComm(0x13);WriteData(0x00);
|
||||
WriteComm(0x14);WriteData(0x02);
|
||||
WriteComm(0x15);WriteData(0x00);
|
||||
@@ -220,7 +250,7 @@ void LCD_Init(void)
|
||||
WriteComm(0x19);WriteData(0x26);
|
||||
WriteComm(0x1A);WriteData(0x00);
|
||||
|
||||
WriteComm(0x1B);WriteData(0xCC); // EM_CK2
|
||||
WriteComm(0x1B);WriteData(0xCC); // 发光逻辑 EM_CK2 配置
|
||||
WriteComm(0x1C);WriteData(0x00);
|
||||
WriteComm(0x1D);WriteData(0x02);
|
||||
WriteComm(0x1E);WriteData(0x00);
|
||||
@@ -230,7 +260,7 @@ void LCD_Init(void)
|
||||
WriteComm(0x22);WriteData(0x26);
|
||||
WriteComm(0x23);WriteData(0x00);
|
||||
|
||||
WriteComm(0x4C);WriteData(0x89); // SN_STV
|
||||
WriteComm(0x4C);WriteData(0x89); // 扫描起始信号 SN_STV 配置
|
||||
WriteComm(0x4D);WriteData(0x00);
|
||||
WriteComm(0x4E);WriteData(0x01);
|
||||
WriteComm(0x4F);WriteData(0x00);
|
||||
@@ -238,43 +268,48 @@ void LCD_Init(void)
|
||||
WriteComm(0x51);WriteData(0xBB);
|
||||
WriteComm(0x52);WriteData(0xBB);
|
||||
|
||||
WriteComm(0x53);WriteData(0xCA); // EM_STV
|
||||
WriteComm(0x53);WriteData(0xCA); // 发光起始信号 EM_STV 配置
|
||||
WriteComm(0x54);WriteData(0x00);
|
||||
WriteComm(0x55);WriteData(0x03);
|
||||
WriteComm(0x56);WriteData(0x00);
|
||||
WriteComm(0x58);WriteData(0x00);
|
||||
WriteComm(0x59);WriteData(0x00);
|
||||
WriteComm(0x65);WriteData(0x45); // 04
|
||||
WriteComm(0x66);WriteData(0x0C); // 03
|
||||
WriteComm(0x67);WriteData(0x00); // 10
|
||||
WriteComm(0x65);WriteData(0x45);
|
||||
WriteComm(0x66);WriteData(0x0C);
|
||||
WriteComm(0x67);WriteData(0x00);
|
||||
|
||||
WriteComm(0xFE);WriteData(0x01); // ID
|
||||
WriteComm(0xFE);WriteData(0x01); // 切换至 Page 1 验证芯片 ID
|
||||
WriteComm(0xE5);WriteData(0x00);
|
||||
WriteComm(0xE6);WriteData(0x10);
|
||||
WriteComm(0xE7);WriteData(0x31);
|
||||
|
||||
WriteComm(0xFE);WriteData(0x00); // 切换到 Page 0
|
||||
WriteComm(0xFE);WriteData(0x00); // 切换回 Page 0 进行正常显示操作
|
||||
WriteComm(0xC4);WriteData(0x80);
|
||||
WriteComm(0x2A);WriteData(0x00);WriteData(0x04);WriteData(0x00);WriteData(0x7B); // 设置列
|
||||
WriteComm(0x2A);WriteData(0x00);WriteData(0x04);WriteData(0x00);WriteData(0x7B); // 设定列像素物理边界 (4~123,宽120像素)
|
||||
|
||||
// 显式设置 16 位像素格式 (COLMOD = 0x75)
|
||||
// ====== 显式设置屏幕像素色彩格式为 16 位像素格式 (COLMOD = 0x75,即RGB565格式) ======
|
||||
WriteComm(0x3A);WriteData(0x75);
|
||||
|
||||
WriteComm(0x11);WriteData(0x00); // 退出睡眠
|
||||
Delay_ms(150);
|
||||
WriteComm(0x11);WriteData(0x00); // 唤醒屏芯片:发送 Sleep Out 退出睡眠指令
|
||||
Delay_ms(150); // 退出睡眠状态后,必须等待 150ms 以便驱动电荷泵稳定
|
||||
|
||||
WriteComm(0x29);WriteData(0x00); // 开启显示
|
||||
WriteComm(0x29);WriteData(0x00); // 开启屏幕显示:发送 Display ON 指令,像素层开始发光
|
||||
Delay_ms(20);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置屏幕绘制显存窗口区域
|
||||
*/
|
||||
void LCD_SetWindow(u16 x_start, u16 x_end, u16 y_start, u16 y_end)
|
||||
{
|
||||
// 写入 Column Address Set 列地址设置寄存器命令 (0x2A)
|
||||
WriteComm(0x2A);
|
||||
WriteData((u8)(x_start >> 8));
|
||||
WriteData((u8)(x_start & 0xFF));
|
||||
WriteData((u8)(x_end >> 8));
|
||||
WriteData((u8)(x_end & 0xFF));
|
||||
|
||||
// 写入 Row Address Set 行地址设置寄存器命令 (0x2B)
|
||||
WriteComm(0x2B);
|
||||
WriteData((u8)(y_start >> 8));
|
||||
WriteData((u8)(y_start & 0xFF));
|
||||
@@ -282,29 +317,41 @@ void LCD_SetWindow(u16 x_start, u16 x_end, u16 y_start, u16 y_end)
|
||||
WriteData((u8)(y_end & 0xFF));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 用指定色彩刷写清空全屏幕
|
||||
* @param color 16位 RGB565 颜色填充值
|
||||
*/
|
||||
void LCD_Clear(u16 color)
|
||||
{
|
||||
u16 i, j;
|
||||
// 【物理引脚偏移说明】:
|
||||
// 该 AMOLED 屏在物理层面上水平方向的列像素地址为 4 ~ 123(总宽 120 像素)。
|
||||
// 因此调用 LCD_SetWindow 时,X 轴的物理起始和终止坐标必须偏置 +4 才能与物理屏幕像素完全对齐。
|
||||
LCD_SetWindow(4, 123, 0, 239);
|
||||
WriteComm(0x2C);
|
||||
WriteComm(0x2C); // 发送 RAM Write (0x2C) 显存写入命令
|
||||
for(i = 0; i < 120; i++)
|
||||
{
|
||||
for(j = 0; j < 240; j++)
|
||||
{
|
||||
WriteData((u8)(color >> 8));
|
||||
WriteData((u8)(color & 0xFF));
|
||||
WriteData((u8)(color >> 8)); // 发送 RGB565 高 8 位字节
|
||||
WriteData((u8)(color & 0xFF)); // 发送 RGB565 低 8 位字节
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 在屏幕指定视口绘制实心矩形
|
||||
*/
|
||||
void LCD_FillRect(u16 x, u16 y, u16 w, u16 h, u16 color)
|
||||
{
|
||||
u16 i, count;
|
||||
u8 ch = (u8)(color >> 8);
|
||||
u8 cl = (u8)(color & 0xFF);
|
||||
|
||||
// X 坐标应用物理偏置偏移 +4 像素
|
||||
LCD_SetWindow(x + 4, x + w - 1 + 4, y, y + h - 1);
|
||||
WriteComm(0x2C);
|
||||
count = w * h;
|
||||
WriteComm(0x2C); // 写入显存
|
||||
count = w * h; // 实心矩形包含的总像素数
|
||||
for(i = 0; i < count; i++)
|
||||
{
|
||||
WriteData(ch);
|
||||
@@ -312,63 +359,90 @@ void LCD_FillRect(u16 x, u16 y, u16 w, u16 h, u16 color)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 绘制空心矩形边界框
|
||||
* @details 通过组合调用四次 LCD_FillRect 实心线来绘制矩形的上、下、左、右四条边。
|
||||
*/
|
||||
void LCD_DrawRectBorder(u16 x, u16 y, u16 w, u16 h, u16 color)
|
||||
{
|
||||
LCD_FillRect(x, y, w, 1, color);
|
||||
LCD_FillRect(x, y + h - 1, w, 1, color);
|
||||
LCD_FillRect(x, y, 1, h, color);
|
||||
LCD_FillRect(x + w - 1, y, 1, h, color);
|
||||
LCD_FillRect(x, y, w, 1, color); // 上边框线 (厚度 1 像素)
|
||||
LCD_FillRect(x, y + h - 1, w, 1, color); // 下边框线
|
||||
LCD_FillRect(x, y, 1, h, color); // 左边框线
|
||||
LCD_FillRect(x + w - 1, y, 1, h, color); // 右边框线
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 渲染显示一个单色字模位图资源
|
||||
* @param x 渲染起始坐标 X
|
||||
* @param y 渲染起始坐标 Y
|
||||
* @param w 位图宽度 (单位: 像素点数)
|
||||
* @param h 位图高度 (单位: 像素点数)
|
||||
* @param bmp FLASH 中的单色像素数组指针 (1表示画前景色,0表示画背景色)
|
||||
* @param color 16位前景色
|
||||
* @param bg_color 16位背景色
|
||||
* @details 会逐行、逐列解析单色图的字节数组,每一行占 (w+7)/8 个字节,
|
||||
* 利用位操作提取指定 bit 的状态并发送色彩数据写入屏幕。
|
||||
*/
|
||||
void LCD_DrawMonoBitmap(u16 x, u16 y, u16 w, u16 h, unsigned char code *bmp, u16 color, u16 bg_color)
|
||||
{
|
||||
u16 r, c;
|
||||
u16 byte_width = (w + 7) / 8;
|
||||
u16 byte_width = (w + 7) / 8; // 计算在单色编码中,一行图像占用的实际字节数
|
||||
u8 ch = (u8)(color >> 8);
|
||||
u8 cl = (u8)(color & 0xFF);
|
||||
u8 bgh = (u8)(bg_color >> 8);
|
||||
u8 bgl = (u8)(bg_color & 0xFF);
|
||||
|
||||
LCD_SetWindow(x + 4, x + w - 1 + 4, y, y + h - 1);
|
||||
LCD_SetWindow(x + 4, x + w - 1 + 4, y, y + h - 1); // 限制绘制显存窗口并应用偏移
|
||||
WriteComm(0x2C);
|
||||
|
||||
// 嵌套循环,从第 0 行第 0 列开始遍历每个像素位置
|
||||
for(r = 0; r < h; r++)
|
||||
{
|
||||
for(c = 0; c < w; c++)
|
||||
{
|
||||
u16 byte_idx = r * byte_width + (c / 8);
|
||||
u8 bit_shift = 7 - (c % 8);
|
||||
u16 byte_idx = r * byte_width + (c / 8); // 定位当前像素所在单色图数组中的字节索引
|
||||
u8 bit_shift = 7 - (c % 8); // 定位该像素在该字节中对应的 bit 位置 (高位在前)
|
||||
|
||||
if(bmp[byte_idx] & (1 << bit_shift))
|
||||
{
|
||||
WriteData(ch);
|
||||
WriteData(ch); // 该位为 1,发送前景色
|
||||
WriteData(cl);
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteData(bgh);
|
||||
WriteData(bgh); // 该位为 0,发送背景色
|
||||
WriteData(bgl);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 在指定坐标渲染输出一个 8x16 尺寸的普通英文 ASCII 字符
|
||||
* @param ch ASCII 字符本身
|
||||
* @details 遍历 8x16 点阵字库,将索引对应的 16 个字符编码字节依次提取,按位发送。
|
||||
*/
|
||||
void LCD_ShowChar8x16(u16 x, u16 y, char ch, u16 color, u16 bg_color)
|
||||
{
|
||||
u8 r, c;
|
||||
u8 char_idx = ch - 0x20;
|
||||
u8 char_idx = ch - 0x20; // 偏移计算:ASCII 可打印字符从 0x20 (空格) 开始
|
||||
u8 f_h = (u8)(color >> 8);
|
||||
u8 f_l = (u8)(color & 0xFF);
|
||||
u8 b_h = (u8)(bg_color >> 8);
|
||||
u8 b_l = (u8)(bg_color & 0xFF);
|
||||
|
||||
if(ch < 0x20 || ch > 0x7E) return;
|
||||
if(ch < 0x20 || ch > 0x7E) return; // 过滤不可见控制字符
|
||||
|
||||
LCD_SetWindow(x + 4, x + 8 - 1 + 4, y, y + 16 - 1);
|
||||
LCD_SetWindow(x + 4, x + 8 - 1 + 4, y, y + 16 - 1); // 应用 +4 像素偏移
|
||||
WriteComm(0x2C);
|
||||
|
||||
// 循环 16 行,绘制 8x16 字型
|
||||
for(r = 0; r < 16; r++)
|
||||
{
|
||||
u8 line = FONT_8x16[char_idx][r];
|
||||
u8 line = FONT_8x16[char_idx][r]; // 提取当前行对应的 8 位单色像素信息
|
||||
for(c = 0; c < 8; c++)
|
||||
{
|
||||
// 从高到低依次提取单行中的像素点
|
||||
if(line & (0x80 >> c))
|
||||
{
|
||||
WriteData(f_h);
|
||||
@@ -383,27 +457,43 @@ void LCD_ShowChar8x16(u16 x, u16 y, char ch, u16 color, u16 bg_color)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 在指定坐标打印一行普通英文 ASCII 字符串 (左对齐)
|
||||
*/
|
||||
void LCD_ShowString(u16 x, u16 y, char *str, u16 color, u16 bg_color)
|
||||
{
|
||||
while(*str)
|
||||
{
|
||||
LCD_ShowChar8x16(x, y, *str, color, bg_color);
|
||||
x += 8;
|
||||
str++;
|
||||
LCD_ShowChar8x16(x, y, *str, color, bg_color); // 渲染当前字符
|
||||
x += 8; // X 轴起始位置偏移 8 像素,准备绘制下一个字符
|
||||
str++; // 移向下一个字符
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 在指定 Y 轴高度上,自动计算偏移以实现字符串水平居中显示
|
||||
*/
|
||||
void LCD_ShowStringCentered(u16 y, char *str, u16 color, u16 bg_color)
|
||||
{
|
||||
u16 len = 0;
|
||||
char *p = str;
|
||||
while(*p++) len++;
|
||||
while(*p++) len++; // 计算字符串长度
|
||||
|
||||
if(len * 8 >= 120)
|
||||
// 若超出或等同于屏幕有效宽度 (120像素),强制从最左侧 0 开始左对齐绘制
|
||||
LCD_ShowString(0, y, str, color, bg_color);
|
||||
else
|
||||
// 否则通过公式 (120 - 字符宽度) / 2 计算出水平居中的首字起始偏移 X 坐标并渲染
|
||||
LCD_ShowString((120 - len * 8) / 2, y, str, color, bg_color);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 渲染大号特粗 16x32 尺寸的英文/数字字符
|
||||
* @details 【字库双重放大技术】:
|
||||
* 由于 8051 FLASH 容量有限,手环未附带特大型号的 16x32 大字库。
|
||||
* 此函数通过算法对 8x16 基础字库点阵数据在水平方向(列)和垂直方向(行)各重复绘制 2 遍
|
||||
* (即单个点扩充绘制为 2x2 的像素团),从而实现对原字型双重放大渲染为 16x32 规格,节省空间。
|
||||
*/
|
||||
void LCD_ShowChar16x32(u16 x, u16 y, char ch, u16 color, u16 bg_color)
|
||||
{
|
||||
u8 r, c, i, j;
|
||||
@@ -415,16 +505,22 @@ void LCD_ShowChar16x32(u16 x, u16 y, char ch, u16 color, u16 bg_color)
|
||||
|
||||
if(ch < 0x20 || ch > 0x7E) return;
|
||||
|
||||
LCD_SetWindow(x + 4, x + 16 - 1 + 4, y, y + 32 - 1);
|
||||
LCD_SetWindow(x + 4, x + 16 - 1 + 4, y, y + 32 - 1); // 锁定 16x32 的绘图视窗
|
||||
WriteComm(0x2C);
|
||||
|
||||
// 遍历字型的 16 个点阵行数据
|
||||
for(r = 0; r < 16; r++)
|
||||
{
|
||||
u8 line = FONT_8x16[char_idx][r];
|
||||
|
||||
// 纵向插点翻倍:将当前行重复绘制两次 (i = 0 和 i = 1)
|
||||
for(i = 0; i < 2; i++)
|
||||
{
|
||||
// 遍历单行内的 8 个 bit 像素点
|
||||
for(c = 0; c < 8; c++)
|
||||
{
|
||||
u8 val_bit = (line & (0x80 >> c));
|
||||
// 横向插点翻倍:单个像素点重复发送写入 2 次 (j = 0 和 j = 1)
|
||||
for(j = 0; j < 2; j++)
|
||||
{
|
||||
if(val_bit)
|
||||
@@ -443,23 +539,32 @@ void LCD_ShowChar16x32(u16 x, u16 y, char ch, u16 color, u16 bg_color)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 在指定坐标绘制特大 16x32 规格的数字/英文字符串
|
||||
*/
|
||||
void LCD_ShowString16x32(u16 x, u16 y, char *str, u16 color, u16 bg_color)
|
||||
{
|
||||
while(*str)
|
||||
{
|
||||
LCD_ShowChar16x32(x, y, *str, color, bg_color);
|
||||
x += 16;
|
||||
x += 16; // 每次起始坐标偏移 16 像素
|
||||
str++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 在指定 Y 轴高度上,水平居中渲染显示特大 16x32 规格字符串
|
||||
*/
|
||||
void LCD_ShowString16x32Centered(u16 y, char *str, u16 color, u16 bg_color)
|
||||
{
|
||||
u16 len = 0;
|
||||
char *p = str;
|
||||
while(*p++) len++;
|
||||
while(*p++) len++; // 计算字数
|
||||
|
||||
if(len * 16 >= 120)
|
||||
// 超出屏幕宽度,左对齐
|
||||
LCD_ShowString16x32(0, y, str, color, bg_color);
|
||||
else
|
||||
// 水平居中计算
|
||||
LCD_ShowString16x32((120 - len * 16) / 2, y, str, color, bg_color);
|
||||
}
|
||||
|
||||
158
Drivers/lcd.h
158
Drivers/lcd.h
@@ -4,49 +4,157 @@
|
||||
#include "../App/config.h"
|
||||
#include "stc32g.h"
|
||||
|
||||
// Color Constants (RGB565)
|
||||
#define COLOR_BLACK 0x0000
|
||||
#define COLOR_WHITE 0xFFFF
|
||||
#define COLOR_CYAN 0x073F
|
||||
#define COLOR_RED 0xF800
|
||||
#define COLOR_GREEN 0x07E0
|
||||
#define COLOR_BLUE 0x001F
|
||||
#define COLOR_GRAY 0x5AEB
|
||||
#define COLOR_AMBER 0xFDC0
|
||||
/* ====== Truly AMOLED RM69310 屏幕驱动 (RGB565格式) 常数定义 ====== */
|
||||
|
||||
// Public screen functions
|
||||
// 16位 RGB565 颜色常量定义:
|
||||
// 格式为:Red(5位) Green(6位) Blue(5位) 拼接而成的 16 位整数值
|
||||
#define COLOR_BLACK 0x0000 // 黑色
|
||||
#define COLOR_WHITE 0xFFFF // 白色
|
||||
#define COLOR_CYAN 0x073F // 青色/天蓝色
|
||||
#define COLOR_RED 0xF800 // 红色
|
||||
#define COLOR_GREEN 0x07E0 // 绿色
|
||||
#define COLOR_BLUE 0x001F // 蓝色
|
||||
#define COLOR_GRAY 0x5AEB // 灰色
|
||||
#define COLOR_AMBER 0xFDC0 // 琥珀色/橙黄色
|
||||
|
||||
/* ====== 屏幕电源控制与基本驱动接口 ====== */
|
||||
|
||||
/**
|
||||
* @brief 通过脉冲序列为 SGM3833 屏幕负压供电芯片发送开启配置脉冲
|
||||
* @param pulse_cnt 发送的脉冲个数 (例如 27 次脉冲开启并稳压屏幕输出)
|
||||
* @details 采用单线脉冲边沿计数调制技术,为 RM69310 控制芯片提供其正常显示所需的高质量偏置正负电压。
|
||||
*/
|
||||
void SGM_SendPulse(u8 pulse_cnt);
|
||||
|
||||
/**
|
||||
* @brief 执行 Truly AMOLED 屏幕的硬件复位
|
||||
* @details 将 LCD_RST 引脚拉低保持一定时间后拉高,强制复位屏幕控制器,使其恢复到初始逻辑状态。
|
||||
*/
|
||||
void LCD_Reset(void);
|
||||
|
||||
/**
|
||||
* @brief 初始化 Truly AMOLED 屏幕控制器 RM69310
|
||||
* @details 执行屏幕硬件复位,通过软件 SPI 总线依次向屏幕控制器写入寄存器偏置、显示周期、
|
||||
* 通道选择、关闭休眠 (Sleep Out: 0x11)、开启显示 (Display On: 0x29) 等一系列
|
||||
* 出厂初始化命令序列,使屏幕准备好接收显示像素帧。
|
||||
*/
|
||||
void LCD_Init(void);
|
||||
|
||||
/**
|
||||
* @brief 向屏幕写入 1 字节控制命令
|
||||
* @param cmd 8 位命令字节
|
||||
*/
|
||||
void WriteComm(u8 cmd);
|
||||
|
||||
/**
|
||||
* @brief 设定屏幕的数据更新绘制窗口区域 (显存写入范围)
|
||||
* @param x_start X 轴起始坐标 (0 ~ 119)
|
||||
* @param x_end X 轴终止坐标 (0 ~ 119)
|
||||
* @param y_start Y 轴起始坐标 (0 ~ 239)
|
||||
* @param y_end Y 轴终止坐标 (0 ~ 239)
|
||||
* @details 限定后续的数据流写入范围,实现局部页面刷新或全部页面刷新。
|
||||
*/
|
||||
void LCD_SetWindow(u16 x_start, u16 x_end, u16 y_start, u16 y_end);
|
||||
|
||||
/**
|
||||
* @brief 以指定纯色清除整个屏幕显示内容
|
||||
* @param color 16位 RGB565 颜色填充值
|
||||
*/
|
||||
void LCD_Clear(u16 color);
|
||||
|
||||
// Drawing Primitives
|
||||
|
||||
/* ====== 像素级图形几何算法与字符渲染接口 ====== */
|
||||
|
||||
/**
|
||||
* @brief 在屏幕指定位置绘制一个单色填充的实心矩形
|
||||
* @param x 矩形左上角 X 轴坐标
|
||||
* @param y 矩形左上角 Y 轴坐标
|
||||
* @param w 矩形宽度
|
||||
* @param h 矩形高度
|
||||
* @param color 16位 RGB565 填充色彩值
|
||||
*/
|
||||
void LCD_FillRect(u16 x, u16 y, u16 w, u16 h, u16 color);
|
||||
|
||||
/**
|
||||
* @brief 绘制一个无填充的空心矩形边界框
|
||||
* @param x 矩形左上角 X 轴坐标
|
||||
* @param y 矩形左上角 Y 轴坐标
|
||||
* @param w 边框总宽度
|
||||
* @param h 边框总高度
|
||||
* @param color 16位 RGB565 边框线条色彩
|
||||
*/
|
||||
void LCD_DrawRectBorder(u16 x, u16 y, u16 w, u16 h, u16 color);
|
||||
|
||||
/**
|
||||
* @brief 绘制渲染一个自定义宽高的单色单色位图
|
||||
* @param x 绘制的左上角 X 轴坐标
|
||||
* @param y 绘制的左上角 Y 轴坐标
|
||||
* @param w 单色图宽度 (单位: 像素点数)
|
||||
* @param h 单色图高度
|
||||
* @param bmp 单色位图所在的 FLASH code 区指针 (1位对应1像素:1画前景色,0画背景色)
|
||||
* @param color 位图前景色 (RGB565)
|
||||
* @param bg_color 位图背景色
|
||||
*/
|
||||
void LCD_DrawMonoBitmap(u16 x, u16 y, u16 w, u16 h, unsigned char code *bmp, u16 color, u16 bg_color);
|
||||
|
||||
/**
|
||||
* @brief 渲染显示一个 8x16 尺寸的普通英文 ASCII 字符
|
||||
*/
|
||||
void LCD_ShowChar8x16(u16 x, u16 y, char ch, u16 color, u16 bg_color);
|
||||
|
||||
/**
|
||||
* @brief 渲染显示一行普通的英文 ASCII 字符串 (左对齐)
|
||||
* @param x 绘制行起始 X 坐标
|
||||
* @param y 绘制行 Y 坐标
|
||||
* @param str 字符串头指针
|
||||
* @param color 字符颜色 (RGB565)
|
||||
* @param bg_color 字符背景背景填充色
|
||||
*/
|
||||
void LCD_ShowString(u16 x, u16 y, char *str, u16 color, u16 bg_color);
|
||||
|
||||
/**
|
||||
* @brief 在指定 Y 轴高度上,水平居中对齐显示一行字符串
|
||||
* @param y 渲染的 Y 轴高度 (0 ~ 239)
|
||||
* @param str 字符串内容
|
||||
* @param color 颜色值
|
||||
* @param bg_color 背景色
|
||||
*/
|
||||
void LCD_ShowStringCentered(u16 y, char *str, u16 color, u16 bg_color);
|
||||
|
||||
/**
|
||||
* @brief 渲染显示一个 16x32 大号特粗尺寸的英文/数字字符
|
||||
*/
|
||||
void LCD_ShowChar16x32(u16 x, u16 y, char ch, u16 color, u16 bg_color);
|
||||
|
||||
/**
|
||||
* @brief 显示大号特粗 16x32 规格的数字/英文字符串 (如时钟大字显示)
|
||||
*/
|
||||
void LCD_ShowString16x32(u16 x, u16 y, char *str, u16 color, u16 bg_color);
|
||||
|
||||
/**
|
||||
* @brief 在指定 Y 轴高度上,居中显示 16x32 特大规格字体的字符串
|
||||
* @param y 渲染的 Y 轴高度 (0 ~ 239)
|
||||
* @param str 大字字符串内容
|
||||
* @param color 大字前景色
|
||||
* @param bg_color 大字背景色
|
||||
*/
|
||||
void LCD_ShowString16x32Centered(u16 y, char *str, u16 color, u16 bg_color);
|
||||
|
||||
|
||||
/* ====== 图标与点阵字模外部数据声明 ====== */
|
||||
// 这些数据实体均定义在底层字库 lcd_font.h 文件中
|
||||
|
||||
// Font and Bitmap externs (defined in lcd_font.h via lcd.c)
|
||||
extern unsigned char code FONT_8x16[95][16];
|
||||
extern unsigned char code bmp_battery_20x10[];
|
||||
extern unsigned char code bmp_lock_16x16[];
|
||||
extern unsigned char code bmp_unlock_16x16[];
|
||||
extern unsigned char code bmp_radar_32x32_f1[];
|
||||
extern unsigned char code bmp_radar_32x32_f2[];
|
||||
extern unsigned char code bmp_radar_32x32_f3[];
|
||||
extern unsigned char code bmp_confirm_lock_32x32[];
|
||||
extern unsigned char code bmp_checkmark_32x32[];
|
||||
extern unsigned char code bmp_cross_32x32[];
|
||||
extern unsigned char code bmp_door_32x32[];
|
||||
extern unsigned char code bmp_siren_32x32[];
|
||||
extern unsigned char code FONT_8x16[95][16]; // 8x16 英文 ASCII 点阵字库
|
||||
extern unsigned char code bmp_battery_20x10[]; // 20x10 电池电量指示位图
|
||||
extern unsigned char code bmp_lock_16x16[]; // 16x16 闭合锁头位图
|
||||
extern unsigned char code bmp_unlock_16x16[]; // 16x16 开启锁头位图
|
||||
extern unsigned char code bmp_radar_32x32_f1[]; // 32x32 雷达扩散帧 1
|
||||
extern unsigned char code bmp_radar_32x32_f2[]; // 32x32 雷达扩散帧 2
|
||||
extern unsigned char code bmp_radar_32x32_f3[]; // 32x32 雷达扩散帧 3
|
||||
extern unsigned char code bmp_confirm_lock_32x32[]; // 32x32 传感器类型锁头大图
|
||||
extern unsigned char code bmp_checkmark_32x32[]; // 32x32 配对成功大对勾
|
||||
extern unsigned char code bmp_cross_32x32[]; // 32x32 配对失败大红叉
|
||||
extern unsigned char code bmp_door_32x32[]; // 32x32 门磁大警告图标
|
||||
extern unsigned char code bmp_siren_32x32[]; // 32x32 烟感喇叭警示图标
|
||||
|
||||
#endif
|
||||
#endif // __LCD_H__
|
||||
|
||||
@@ -1,6 +1,19 @@
|
||||
#ifndef __FONT_H__
|
||||
#define __FONT_H__
|
||||
|
||||
/**************************************************************************
|
||||
* @file lcd_font.h
|
||||
* @brief 屏幕显示字库与单色位图数据定义文件
|
||||
* @details 本文件存储了手环系统在 AMOLED 屏幕上渲染所使用的各类静态图形资源,
|
||||
* 包含以下内容:
|
||||
* 1. 电池电量单色图 (bmp_battery_20x10)
|
||||
* 2. 布防/撤防指示用 16x16 锁图标 (bmp_lock, bmp_unlock)
|
||||
* 3. 对码动画所需的 32x32 雷达动态扫描帧 (bmp_radar_32x32_f1 ~ f3)
|
||||
* 4. 设备类型确认与保存界面用的 32x32 锁、成功对勾、失败红叉位图
|
||||
* 5. 报警入侵指示用的 32x32 门磁大图 (bmp_door_32x32) 与警笛喇叭大图
|
||||
* 6. 标准 8x16 英文 ASCII 字符点阵字库 (FONT_8x16),用于文本渲染
|
||||
* @note 所有数据均使用 `code` 关键字修饰,强制存储在单片机 Flash 空间中以节省 RAM。
|
||||
**************************************************************************/
|
||||
|
||||
// 1. Horizontal Battery Icon (20x10 pixels)
|
||||
// Width: 20 pixels (3 bytes per row), Height: 10 rows
|
||||
|
||||
328
Drivers/rf.c
328
Drivers/rf.c
@@ -1,18 +1,17 @@
|
||||
#include "rf.h"
|
||||
|
||||
#include "../App/config.h"
|
||||
#include "intrins.h"
|
||||
|
||||
// ATR5179 射频开关控制脚
|
||||
sbit RF_TX = P2^0; // V1 控制端 (1: 选通天线到 TX 路径)
|
||||
sbit RF_RX = P3^7; // V2 控制端 (1: 选通天线到 RX 路径)
|
||||
// ATR5179 射频前端天线开关控制引脚定义
|
||||
sbit RF_TX = P2^0; // V1 控制端 (1: 选通天线到发射 TX 路径)
|
||||
sbit RF_RX = P3^7; // V2 控制端 (1: 选通天线到接收 RX 路径)
|
||||
|
||||
// 射频数据与控制引脚
|
||||
sbit RF_TX_DAT = P3^6; // LT4455 数据输入 (DIN)
|
||||
sbit RF_RX_DATA = P2^1; // LR690L 数据输出 (DATA)
|
||||
sbit SHUT = P2^2; // LR690L 休眠脚 (0:工作, 1:休眠)
|
||||
// 射频芯片数据与电源控制引脚定义
|
||||
sbit RF_TX_DAT = P3^6; // LT4455 发射调制数据输入引脚 (DIN)
|
||||
sbit RF_RX_DATA = P2^1; // LR690L 接收解调数据输出引脚 (DATA),连接单片机中断管脚
|
||||
sbit SHUT = P2^2; // LR690L 芯片休眠控制引脚 (1:休眠,0:工作)
|
||||
|
||||
// External functions defined in main.c
|
||||
// 声明外部延时及串口打印函数 (在 system.c 中定义)
|
||||
extern void Delay_us(u16 us);
|
||||
extern void Delay_ms(u16 ms);
|
||||
extern u16 GetTimer0_Safe(void);
|
||||
@@ -23,61 +22,80 @@ extern void Uart_SendHex16(u16 val);
|
||||
extern void Uart_SendHex20(u32 val);
|
||||
extern void Uart_SendByte(u8 dat);
|
||||
|
||||
/**
|
||||
* @brief 初始化无线射频芯片相关的 GPIO 引脚方向与初始状态
|
||||
*/
|
||||
void RF_Init(void)
|
||||
{
|
||||
// P2.0 (RF_TX) 和 P2.2 (SHUT) 配置为推挽输出,P2.1 (RF_RX_DATA) 配置为双向口/输入
|
||||
// 1. 配置 P2.0 (RF_TX) 和 P2.2 (SHUT) 为推挽输出模式 (M1=0, M0=1)
|
||||
// 配置 P2.1 (RF_RX_DATA) 为准双向口/输入模式 (M1=0, M0=0)
|
||||
P2M1 &= ~((1 << 0) | (1 << 1) | (1 << 2));
|
||||
P2M0 &= ~(1 << 1);
|
||||
P2M0 |= ((1 << 0) | (1 << 2));
|
||||
|
||||
// P3.6 (RF_TX_DAT), P3.7 (RF_RX) 配置为推挽输出
|
||||
// 2. 配置 P3.6 (RF_TX_DAT) 和 P3.7 (RF_RX) 为推挽输出模式 (M1=0, M0=1)
|
||||
P3M1 &= ~((1 << 6) | (1 << 7));
|
||||
P3M0 |= ((1 << 6) | (1 << 7));
|
||||
|
||||
RF_TX = 0;
|
||||
SHUT = 1; // 默认关闭接收芯片
|
||||
RF_RX = 0;
|
||||
RF_TX_DAT = 0;
|
||||
// 3. 设置系统默认的初始电平
|
||||
RF_TX = 0; // 断开发射天线通路
|
||||
SHUT = 1; // 默认拉高 SHUT,使 LR690L 接收芯片处于低功耗休眠关闭状态
|
||||
RF_RX = 0; // 断开接收天线通路
|
||||
RF_TX_DAT = 0; // 调制发送数据线置低
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 配置无线射频收发系统的运行模式与天线切换
|
||||
* @param mode 运行模式 (0-休眠隔离, 1-接收工作, 2-发射工作)
|
||||
*/
|
||||
void RF_SetMode(u8 mode)
|
||||
{
|
||||
if (mode == 0) // Sleep/Idle
|
||||
if (mode == 0) // ====== 模式 0: 休眠隔离模式 (Sleep/Idle) ======
|
||||
{
|
||||
SHUT = 1; // LR690L Sleep
|
||||
RF_RX = 0; // 断开接收天线
|
||||
RF_TX = 0; // 断开发射天线
|
||||
SHUT = 1; // 强制拉高 SHUT 引脚,使 LR690L 彻底进入低功耗待机
|
||||
RF_RX = 0; // 关闭接收天线开关,防止外界电磁杂波串入接收电路
|
||||
RF_TX = 0; // 关闭发射天线开关
|
||||
}
|
||||
else if (mode == 1) // Rx Mode
|
||||
else if (mode == 1) // ====== 模式 1: 射频接收模式 (Rx Mode) ======
|
||||
{
|
||||
SHUT = 0; // LR690L 工作
|
||||
RF_RX = 1; // 天线切至接收端
|
||||
RF_TX = 0;
|
||||
SHUT = 0; // 拉低 SHUT,启动并使能 LR690L 接收解调芯片工作
|
||||
RF_RX = 1; // 选通天线切换开关到 RX 路径,连通物理接收天线
|
||||
RF_TX = 0; // 断开 TX 发射路径,防止回环烧毁前端
|
||||
}
|
||||
else if (mode == 2) // Tx Mode
|
||||
else if (mode == 2) // ====== 模式 2: 射频发射模式 (Tx Mode) ======
|
||||
{
|
||||
SHUT = 1; // LR690L 睡眠
|
||||
RF_RX = 0;
|
||||
RF_TX = 1; // 天线切至发射端
|
||||
SHUT = 1; // 关闭 LR690L 接收芯片,避免发射大功率波形串入接收机
|
||||
RF_RX = 0; // 断开接收路径天线
|
||||
RF_TX = 1; // 选通天线切换开关至 TX 路径,连通物理天线进行发射
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 按 EV1527 载波规范单次调制发送一帧 24 位无线射频数据
|
||||
* @param addr 20 位的发射地址编码
|
||||
* @param dat 4 位的命令状态码
|
||||
* @details EV1527 是常见的无线编码格式:
|
||||
* - 一个基本脉冲周期设为 T ≈ 320us。
|
||||
* - 同步头脉冲: 1T 高电平 (320us) + 31T 低电平 (9920us)。
|
||||
* - 数据位 '1' (逻辑1) 编码: 3T 高电平 (960us) + 1T 低电平 (320us)。
|
||||
* - 数据位 '0' (逻辑0) 编码: 1T 高电平 (320us) + 3T 低电平 (960us)。
|
||||
*/
|
||||
void EV1527_TxFrame(u32 addr, u8 dat)
|
||||
{
|
||||
u8 i;
|
||||
|
||||
// 1. 同步脉冲: 1T 高电平 + 31T 低电平
|
||||
// ====== 1. 发送同步引导脉冲 ======
|
||||
RF_TX_DAT = 1;
|
||||
Delay_us(320);
|
||||
Delay_us(320); // 1T 高电平
|
||||
RF_TX_DAT = 0;
|
||||
Delay_us(9920);
|
||||
Delay_us(9920); // 31T 低电平
|
||||
|
||||
// 2. 20位地址码 (MSB first)
|
||||
// ====== 2. 依次调制发送 20 位物理地址码 (高位 MSB 优先发送) ======
|
||||
for (i = 0; i < 20; i++)
|
||||
{
|
||||
if ((addr >> (19 - i)) & 1)
|
||||
{
|
||||
// 逻辑 1: 3T 高电平 + 1T 低电平
|
||||
// 数据 bit 1: 3T高 + 1T低
|
||||
RF_TX_DAT = 1;
|
||||
Delay_us(960);
|
||||
RF_TX_DAT = 0;
|
||||
@@ -85,7 +103,7 @@ void EV1527_TxFrame(u32 addr, u8 dat)
|
||||
}
|
||||
else
|
||||
{
|
||||
// 逻辑 0: 1T 高电平 + 3T 低电平
|
||||
// 数据 bit 0: 1T高 + 3T低
|
||||
RF_TX_DAT = 1;
|
||||
Delay_us(320);
|
||||
RF_TX_DAT = 0;
|
||||
@@ -93,12 +111,12 @@ void EV1527_TxFrame(u32 addr, u8 dat)
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 4位数据码 (MSB first)
|
||||
// ====== 3. 依次调制发送 4 位指令状态数据码 (高位 MSB 优先) ======
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
if ((dat >> (3 - i)) & 1)
|
||||
{
|
||||
// 逻辑 1: 3T 高电平 + 1T 低电平
|
||||
// 数据 bit 1: 3T高 + 1T低
|
||||
RF_TX_DAT = 1;
|
||||
Delay_us(960);
|
||||
RF_TX_DAT = 0;
|
||||
@@ -106,7 +124,7 @@ void EV1527_TxFrame(u32 addr, u8 dat)
|
||||
}
|
||||
else
|
||||
{
|
||||
// 逻辑 0: 1T 高电平 + 3T 低电平
|
||||
// 数据 bit 0: 1T高 + 3T低
|
||||
RF_TX_DAT = 1;
|
||||
Delay_us(320);
|
||||
RF_TX_DAT = 0;
|
||||
@@ -115,33 +133,62 @@ void EV1527_TxFrame(u32 addr, u8 dat)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 循环发射多帧无线 EV1527 信号以保证数据通信的完整性
|
||||
* @param addr 20位源地址
|
||||
* @param dat 4位命令码
|
||||
*/
|
||||
void EV1527_Transmit(u32 addr, u8 dat)
|
||||
{
|
||||
u8 r;
|
||||
for (r = 0; r < 25; r++) // 增加到 25 帧(约 340ms)
|
||||
// 循环发射 25 次相同的射频帧 (累计耗时约 340 毫秒),抵御外界瞬间同频杂波干扰
|
||||
for (r = 0; r < 25; r++)
|
||||
{
|
||||
EV1527_TxFrame(addr, dat);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 使用定时器 0 (Timer0) 高精度的测量引脚指定电平状态的持续宽度时间
|
||||
* @param state 待测电平状态 (1-测量高电平持续宽度, 0-测量低电平持续宽度)
|
||||
* @param timeout_us 最大等待超时微秒数上限,超时自动退出防锁死
|
||||
* @return u16 最终测量的实际电平持续微秒数。若发生超时则直接返回 0。
|
||||
*/
|
||||
u16 GetPulseDuration(u8 state, u16 timeout_us)
|
||||
{
|
||||
// 根据系统晶振频率计算在 12T 模式下目标超时的 Timer0 Tick 数值上限
|
||||
u16 timeout_ticks = (u16)((u32)timeout_us * (MAIN_Fosc / 1000000UL) / 12UL);
|
||||
TL0 = 0;
|
||||
TL0 = 0; // 定时器 0 计数值清零
|
||||
TH0 = 0;
|
||||
TR0 = 1; // 启动定时器0
|
||||
TR0 = 1; // 启动定时器 0 硬件计数
|
||||
|
||||
// 轮询引脚:只要电平仍旧等于目标待测状态,就继续保持循环
|
||||
while (RF_RX_DATA == state)
|
||||
{
|
||||
// 软件防卡死判定:如果当前计数值超过了设定的最大允许超时 Tick 值
|
||||
if (GetTimer0_Safe() > timeout_ticks)
|
||||
{
|
||||
TR0 = 0;
|
||||
return 0; // 超时
|
||||
TR0 = 0; // 关断定时器
|
||||
return 0; // 判定超时,返回 0
|
||||
}
|
||||
}
|
||||
TR0 = 0;
|
||||
return (u16)((u32)GetTimer0_Safe() * 12UL / (MAIN_Fosc / 1000000UL)); // 返回微秒数
|
||||
TR0 = 0; // 关断定时器
|
||||
|
||||
// 将最终读取的 Timer0 硬件 Tick 数值,重新换算成微秒数返回 (1 Tick = 12 / 24MHz = 0.5us)
|
||||
return (u16)((u32)GetTimer0_Safe() * 12UL / (MAIN_Fosc / 1000000UL));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 软件自适应解码接收一帧无线 EV1527 信号
|
||||
* @param out_addr 解码成功后,用于回传物理地址高 20 位的指针
|
||||
* @param out_data 解码成功后,用于回传指令数据低 4 位的指针
|
||||
* @return bit 1-解码成功且校验通过,0-捕获失败或脉冲畸变
|
||||
* @note 核心算法:
|
||||
* 1. 等待并寻找合法的同步引导头脉冲 (1T高电平 + 31T低电平,其中低/高比率约为 31,本程序允许比率在 15~48 内变化以抗物理畸变)。
|
||||
* 2. 依据捕获的同步低电平宽度估算基准周期 T = low_time / 31。
|
||||
* 3. 随后依次高精度捕获 24 个数据位的高电平和低电平时间。
|
||||
* 4. 自适应决策:对于每个 bit,如果其高电平时间 raw_h > 低电平时间 raw_l,则判定该位为 1;否则判定该位为 0。
|
||||
*/
|
||||
bit EV1527_Decode(u32 *out_addr, u8 *out_data)
|
||||
{
|
||||
u16 high_time, low_time;
|
||||
@@ -149,68 +196,97 @@ bit EV1527_Decode(u32 *out_addr, u8 *out_data)
|
||||
u32 addr = 0;
|
||||
u8 dat = 0;
|
||||
u16 T;
|
||||
u16 idata raw_h[24];
|
||||
u16 idata raw_l[24];
|
||||
u16 idata raw_h[24]; // 高电平时间寄存数组 (置于 idata 区)
|
||||
u16 idata raw_l[24]; // 低电平时间寄存数组
|
||||
|
||||
// 1. 同步头捕获: 必须是低电平状态之后变高电平开始
|
||||
if (RF_RX_DATA == 0)
|
||||
// ====== 1. 同步头引导脉冲捕获 (定位上升沿) ======
|
||||
{
|
||||
u16 wait_cnt = 0;
|
||||
while (RF_RX_DATA == 0)
|
||||
// 如果当前射频解调输出是高电平,等待其变低
|
||||
while (RF_RX_DATA == 1)
|
||||
{
|
||||
Delay_us(10);
|
||||
Delay_us(5);
|
||||
wait_cnt++;
|
||||
if (wait_cnt > 3000) // 30ms 超时
|
||||
if (wait_cnt > 2000) // 10 毫秒超时保护
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// 测量同步头高电平时间 (放宽至 2500us)
|
||||
high_time = GetPulseDuration(1, 2500);
|
||||
if (high_time < 50 || high_time > 2500)
|
||||
return 0;
|
||||
|
||||
// 测量同步头低电平时间 (放宽至 60000us)
|
||||
low_time = GetPulseDuration(0, 60000);
|
||||
if (low_time < 1500 || low_time > 60000)
|
||||
return 0;
|
||||
|
||||
// 计算高低电平时间之比。对于标准 EV1527,低电平是高电平的 31 倍。
|
||||
// 放宽比例范围为 15 到 48,以应对各种不同的遥控器电阻。
|
||||
{
|
||||
u32 ratio = (u32)low_time / high_time;
|
||||
if (ratio < 15 || ratio > 48)
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 估算基准脉冲周期 T = low_time / 31
|
||||
T = low_time / 31;
|
||||
if (T == 0) T = 1;
|
||||
|
||||
// 2. 连续抓取 24 个数据脉冲
|
||||
for (i = 0; i < 24; i++)
|
||||
{
|
||||
u16 wait_cnt = 0;
|
||||
// 等待下一个数据脉冲的上升沿 (如果当前是低电平,等待它变高)
|
||||
|
||||
// 当引脚维持在低电平后,阻塞等待它跳变为高电平(捕获到同步引导信号的上升沿)
|
||||
wait_cnt = 0;
|
||||
while (RF_RX_DATA == 0)
|
||||
{
|
||||
Delay_us(5);
|
||||
wait_cnt++;
|
||||
if (wait_cnt > 1000) // 5ms 超时
|
||||
if (wait_cnt > 4000) // 20 毫秒超时保护
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// 捕获引导头高电平时间 (放宽测量阈值上限至 2500 微秒)
|
||||
high_time = GetPulseDuration(1, 2500);
|
||||
if (high_time < 50 || high_time > 2500) {
|
||||
if (high_time > 10) { // 剔除高频毛刺噪声,防止调试串口满载
|
||||
Uart_SendString("Sync H Err: ");
|
||||
Uart_SendHex16(high_time);
|
||||
Uart_SendString("us\r\n");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 捕获引导头后续的 31T 低电平时间 (设定上限 60 毫秒)
|
||||
low_time = GetPulseDuration(0, 60000);
|
||||
if (low_time < 1500 || low_time > 60000) {
|
||||
Uart_SendString("Sync L Err H=");
|
||||
Uart_SendHex16(high_time);
|
||||
Uart_SendString(" L=");
|
||||
Uart_SendHex16(low_time);
|
||||
Uart_SendString("us\r\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 计算低电平与高电平的比值,以检验是否满足 EV1527 的 31:1 特征
|
||||
{
|
||||
u32 ratio = (u32)low_time / high_time;
|
||||
if (ratio < 15 || ratio > 48) { // 允许 50% 左右的空气无线传输畸变误差
|
||||
Uart_SendString("Sync Ratio Err H=");
|
||||
Uart_SendHex16(high_time);
|
||||
Uart_SendString(" L=");
|
||||
Uart_SendHex16(low_time);
|
||||
Uart_SendString(" R=");
|
||||
Uart_SendHex16((u16)ratio);
|
||||
Uart_SendString("\r\n");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// 自适应估算出基准脉冲时值 T
|
||||
T = low_time / 31;
|
||||
if (T == 0) T = 1; // 零安全防护
|
||||
|
||||
// ====== 2. 依次采集接下来的 24 个数据 bit 脉冲 ======
|
||||
for (i = 0; i < 24; i++)
|
||||
{
|
||||
u16 wait_cnt = 0;
|
||||
// 等待下一个数据位跳变产生的上升沿 (低电平变高)
|
||||
while (RF_RX_DATA == 0)
|
||||
{
|
||||
Delay_us(5);
|
||||
wait_cnt++;
|
||||
if (wait_cnt > 1000) // 5ms 超时判定,防无线信号中断卡死
|
||||
{
|
||||
return 0; // 解码失败
|
||||
return 0; // 捕获失败
|
||||
}
|
||||
}
|
||||
|
||||
// 测量高电平持续时间和低电平持续时间
|
||||
// 精密测算本位的高电平与低电平宽度
|
||||
raw_h[i] = GetPulseDuration(1, 5000);
|
||||
if (raw_h[i] == 0) return 0;
|
||||
if (raw_h[i] == 0) return 0; // 超时错误直接退出
|
||||
|
||||
raw_l[i] = GetPulseDuration(0, 5000);
|
||||
if (raw_l[i] == 0) return 0;
|
||||
}
|
||||
|
||||
// 3. 将抓到的全部 24 位脉冲数据无条件输出到串口 (缩短输出信息以防止阻塞接收)
|
||||
// ====== 3. 串口输出捕获的时序参数 ======
|
||||
Uart_SendString("Captured: Sync H=");
|
||||
Uart_SendHex16(high_time);
|
||||
Uart_SendString("us, L=");
|
||||
@@ -219,13 +295,16 @@ bit EV1527_Decode(u32 *out_addr, u8 *out_data)
|
||||
Uart_SendHex16(T);
|
||||
Uart_SendString("us\r\n");
|
||||
|
||||
// 4. 组合成 24 位地址与数据,使用自适应阈值:如果高电平时间比低电平时间长,判定为 1,反之为 0
|
||||
// ====== 4. 自适应决策:组合成地址及数据位 ======
|
||||
for (i = 0; i < 24; i++)
|
||||
{
|
||||
// 比较本位高低电平持续宽度:
|
||||
// 如果高电平大于低电平,说明宽脉冲在高,判定该位为 1 (3T高+1T低)。
|
||||
// 否则高电平短,判定为 0 (1T高+3T低)。
|
||||
if (raw_h[i] > raw_l[i])
|
||||
{
|
||||
if (i < 20) addr = (addr << 1) | 1;
|
||||
else dat = (dat << 1) | 1;
|
||||
if (i < 20) addr = (addr << 1) | 1; // 高 20 位写入物理地址
|
||||
else dat = (dat << 1) | 1; // 低 4 位写入状态码
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -237,28 +316,33 @@ bit EV1527_Decode(u32 *out_addr, u8 *out_data)
|
||||
*out_addr = addr;
|
||||
*out_data = dat;
|
||||
|
||||
// 串口打印解码结果方便现场调试
|
||||
// 串口打印解码最终结果
|
||||
Uart_SendString("Decoded ADDR: 0x");
|
||||
Uart_SendHex20(addr);
|
||||
Uart_SendString(", DATA: 0x");
|
||||
Uart_SendHex4(dat);
|
||||
Uart_SendString("\r\n");
|
||||
|
||||
return 1;
|
||||
return 1; // 成功解析
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 软硬件环路自检回环测试
|
||||
* @details 将发射引脚置位并同时采样接收引脚,用于测试收发芯片控制连通性。
|
||||
*/
|
||||
void Loopback_Test(void)
|
||||
{
|
||||
u16 match_count = 0;
|
||||
u16 i;
|
||||
|
||||
SHUT = 0; // 使能接收芯片
|
||||
RF_RX = 1; // 选通天线到接收端
|
||||
RF_TX = 0; // 发送侧断开
|
||||
SHUT = 0; // 强行开启接收芯片供电
|
||||
RF_RX = 1; // 选通天线到 RX
|
||||
RF_TX = 0; // 发送引脚隔离
|
||||
Delay_ms(100);
|
||||
|
||||
Uart_SendString("Starting RF Loopback Self-Test...\r\n");
|
||||
|
||||
// 高频翻转模拟发送引脚,同步读取接收解调出的管脚信号,统计匹配率
|
||||
for (i = 0; i < 100; i++)
|
||||
{
|
||||
RF_TX_DAT = 1;
|
||||
@@ -283,6 +367,62 @@ void Loopback_Test(void)
|
||||
Uart_SendString("Result: FAIL! No RF signal detected. Check hardware.\r\n");
|
||||
}
|
||||
|
||||
SHUT = 1;
|
||||
RF_RX = 0;
|
||||
SHUT = 1; // 关闭接收芯片
|
||||
RF_RX = 0; // 断开接收通道
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 统计接收引脚在 5 秒内的电平翻转次数,用以评估物理噪声污染度或管脚短路
|
||||
*/
|
||||
void RF_Test_Pin(void)
|
||||
{
|
||||
u8 i;
|
||||
u16 j;
|
||||
u16 transitions = 0;
|
||||
bit last_state = RF_RX_DATA;
|
||||
|
||||
Uart_SendString("=== RF RX Pin Data Test Start (5 seconds) ===\r\n");
|
||||
Uart_SendString("SHUT pin state: ");
|
||||
Uart_SendByte(SHUT ? '1' : '0');
|
||||
Uart_SendString("\r\n");
|
||||
|
||||
// 强制设为接收模式
|
||||
RF_SetMode(1);
|
||||
Delay_ms(100);
|
||||
|
||||
Uart_SendString("SHUT pin state (after RF_SetMode(1)): ");
|
||||
Uart_SendByte(SHUT ? '1' : '0');
|
||||
Uart_SendString("\r\n");
|
||||
Uart_SendString("Please trigger your 433MHz transmitter now...\r\n");
|
||||
|
||||
// 连续进行 5 秒的高频电平抖动采样
|
||||
for (i = 0; i < 5; i++)
|
||||
{
|
||||
transitions = 0;
|
||||
// 1秒循环
|
||||
for (j = 0; j < 1000; j++)
|
||||
{
|
||||
u8 k;
|
||||
// 每次内循环进行 5 次采样,每次间隔 200us
|
||||
for (k = 0; k < 5; k++)
|
||||
{
|
||||
bit current_state = RF_RX_DATA;
|
||||
if (current_state != last_state)
|
||||
{
|
||||
transitions++; // 统计电平边沿翻转次数
|
||||
last_state = current_state;
|
||||
}
|
||||
Delay_us(200);
|
||||
}
|
||||
}
|
||||
|
||||
// 输出本秒的电平跳转总数,若非发射期该数值很大,表明空中有丰富噪声噪底跳变,属接收机正常表现
|
||||
Uart_SendString("Sampling... Transitions in last 1s: ");
|
||||
Uart_SendHex16(transitions);
|
||||
Uart_SendString(", current pin value: ");
|
||||
Uart_SendByte(RF_RX_DATA ? '1' : '0');
|
||||
Uart_SendString("\r\n");
|
||||
}
|
||||
|
||||
Uart_SendString("=== RF Pin Test End ===\r\n");
|
||||
}
|
||||
|
||||
59
Drivers/rf.h
59
Drivers/rf.h
@@ -4,13 +4,66 @@
|
||||
#include "../App/config.h"
|
||||
#include "stc32g.h"
|
||||
|
||||
// Public RF functions
|
||||
/* ====== LR690L / EV1527 无线射频编解码驱动接口 ====== */
|
||||
|
||||
/**
|
||||
* @brief 初始化无线射频芯片相关的 GPIO 引脚
|
||||
* @details 配置 RF_TX_DAT (P3.6) 为推挽输出,配置 RF_TX (P2.0)、RF_RX (P3.7)、SHUT (P2.2) 等引脚,
|
||||
* 使能 ATR5179 射频前端开关,为无线射频收发建立硬件基础。
|
||||
*/
|
||||
void RF_Init(void);
|
||||
void RF_SetMode(u8 mode); // mode: 0=Sleep, 1=Rx, 2=Tx
|
||||
|
||||
/**
|
||||
* @brief 配置无线射频收发系统的运行模式
|
||||
* @param mode 0-休眠模式 (Sleep),1-接收模式 (Rx),2-发射模式 (Tx)
|
||||
* @details 负责联合调度 SHUT 控制引脚、天线切换使能引脚以及 ATR5179 射频开关:
|
||||
* - 模式 0 (Sleep): 拉高 SHUT 以关断 LR690L 芯片电源,实现零电流待机。
|
||||
* - 模式 1 (Rx): 拉低 SHUT 启动 LR690L 芯片,接通天线并配置前端为接收放大状态。
|
||||
* - 模式 2 (Tx): 切换天线前端开关为发射直通状态,准备调制发送。
|
||||
*/
|
||||
void RF_SetMode(u8 mode);
|
||||
|
||||
/**
|
||||
* @brief 按照 EV1527 协议单次调制发送一帧 24 位射频数据
|
||||
* @param addr 24 位发射源地址
|
||||
* @param dat 4 位命令状态数据码
|
||||
* @details 会生成由 1 个同步码 (Sync Code) 和 24 个数据编码(由 addr + dat 组成)组成的完整帧脉冲。
|
||||
*/
|
||||
void EV1527_TxFrame(u32 addr, u8 dat);
|
||||
|
||||
/**
|
||||
* @brief 连续多次发送 EV1527 射频帧,防止单帧丢失
|
||||
* @param addr 24 位源地址
|
||||
* @param dat 4 位命令状态码
|
||||
* @details 连续发送多次以保证无线通信在嘈杂噪声环境下的接收可靠性。
|
||||
*/
|
||||
void EV1527_Transmit(u32 addr, u8 dat);
|
||||
|
||||
/**
|
||||
* @brief 获取特定引脚电平状态的持续微秒时间
|
||||
* @param state 待检测的电平目标状态 (0-低电平, 1-高电平)
|
||||
* @param timeout_us 最大等待超时上限 (单位: 微秒)
|
||||
* @return u16 最终测得的脉冲宽度时长。如果超出 timeout_us 仍未改变电平,则直接返回超时值。
|
||||
*/
|
||||
u16 GetPulseDuration(u8 state, u16 timeout_us);
|
||||
|
||||
/**
|
||||
* @brief 阻塞式检测并解码一个合法的 EV1527 射频信号数据帧
|
||||
* @param out_addr 输出参数指针:若解码成功,将写入解析得到的 20 位发送地址 (即 24 位帧除去 4 位数据位后的剩余高 20 位)
|
||||
* @param out_data 输出参数指针:若解码成功,将写入解析出的 4 位命令状态数据码 (例如报警/SOS等)
|
||||
* @return bit 1-成功解析出一帧有效数据;0-当前未捕获到合法脉冲或校验失败
|
||||
* @note 此函数通过对 P2.1 (RF_RX_DATA) 输入脉冲宽度进行软件精密测量,定位同步头,依次采集 24 个数据位,并进行 3 次重复性去噪校验。
|
||||
*/
|
||||
bit EV1527_Decode(u32 *out_addr, u8 *out_data);
|
||||
|
||||
/**
|
||||
* @brief 串口与射频功能的回环测试函数 (调试使用)
|
||||
*/
|
||||
void Loopback_Test(void);
|
||||
|
||||
#endif
|
||||
/**
|
||||
* @brief 射频控制引脚的高低电平输出极性测试 (硬件排查使用)
|
||||
*/
|
||||
void RF_Test_Pin(void);
|
||||
|
||||
#endif // __RF_H__
|
||||
|
||||
@@ -1,15 +1,23 @@
|
||||
#ifndef __STC32G_H__
|
||||
#define __STC32G_H__
|
||||
|
||||
/**************************************************************************
|
||||
* @file stc32g.h
|
||||
* @brief STC32G 系列单片机官方特殊功能寄存器 (SFR) 定义头文件
|
||||
* @details 本文件包含了 STC32G12K128 单片机内核与所有外设寄存器地址的宏定义。
|
||||
* 为保持与官方标准库及编译器的最大兼容性,本文件不对寄存器宏进行逐行改动,
|
||||
* 仅在此处添加中文引导说明。
|
||||
**************************************************************************/
|
||||
|
||||
/////////////////////////////////////////////////
|
||||
|
||||
//<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ͷ<EFBFBD>ļ<EFBFBD><EFBFBD><EFBFBD>,<2C><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ٰ<EFBFBD><D9B0><EFBFBD>"REG51.H"
|
||||
//包含本头文件后,不用另外再包含"REG51.H"
|
||||
|
||||
#include "stdio.h"
|
||||
#include "intrins.h"
|
||||
#include "string.h"
|
||||
|
||||
//<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>LCALL 0x0000<EFBFBD>쳣
|
||||
//修正编译器的LCALL 0x0000异常
|
||||
extern void _m(void);
|
||||
#define main(x) main(x){_m();sprintf(0, "0");strlen("0");}; void _m(x)
|
||||
|
||||
@@ -529,10 +537,10 @@ sfr RSTCFG = 0xff;
|
||||
sbit ENLVR = RSTCFG^6;
|
||||
sbit P54RST = RSTCFG^4;
|
||||
|
||||
//<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ܼĴ<EFBFBD><EFBFBD><EFBFBD>λ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>չRAM<EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
//<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Щ<EFBFBD>Ĵ<EFBFBD><EFBFBD><EFBFBD>,<2C><><EFBFBD>Ƚ<EFBFBD>EAXFR<46><52><EFBFBD><EFBFBD>Ϊ1,<2C>ſ<EFBFBD><C5BF><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>д
|
||||
//如下特殊功能寄存器位于扩展RAM区域
|
||||
//访问这些寄存器,需先将EAXFR设置为1,才可正常读写
|
||||
// EAXFR = 1;
|
||||
//<EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
//或者
|
||||
// P_SW2 |= 0x80;
|
||||
|
||||
/////////////////////////////////////////////////
|
||||
@@ -949,14 +957,14 @@ typedef struct TAG_PWM_STRUCT
|
||||
#define HSSPI_CFG2 (*(unsigned char volatile far *)0x7efbf9)
|
||||
#define HSSPI_STA (*(unsigned char volatile far *)0x7efbfa)
|
||||
|
||||
//ʹ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ĺ<EFBFBD>,<2C><><EFBFBD>Ƚ<EFBFBD>EAXFR<46><52><EFBFBD><EFBFBD>Ϊ1
|
||||
//ʹ<EFBFBD>÷<EFBFBD><EFBFBD><EFBFBD>:
|
||||
//使用下面的宏,需先将EAXFR设置为1
|
||||
//使用方法:
|
||||
// char val;
|
||||
//
|
||||
// EAXFR = 1; //ʹ<EFBFBD>ܷ<EFBFBD><EFBFBD><EFBFBD>XFR
|
||||
// READ_HSPWMA(PWMA_CR1, val); //<EFBFBD>첽<EFBFBD><EFBFBD>PWMA<EFBFBD><EFBFBD><EFBFBD>Ĵ<EFBFBD><EFBFBD><EFBFBD>
|
||||
// EAXFR = 1; //使能访问XFR
|
||||
// READ_HSPWMA(PWMA_CR1, val); //异步读PWMA组寄存器
|
||||
// val |= 0x01;
|
||||
// WRITE_HSPWMA(PWMA_CR1, val); //<EFBFBD>첽дPWMA<EFBFBD><EFBFBD><EFBFBD>Ĵ<EFBFBD><EFBFBD><EFBFBD>
|
||||
// WRITE_HSPWMA(PWMA_CR1, val); //异步写PWMA组寄存器
|
||||
|
||||
#define READ_HSPWMA(reg, dat) \
|
||||
{ \
|
||||
@@ -1168,13 +1176,13 @@ typedef struct TAG_PWM_STRUCT
|
||||
//#define CANAR (*(unsigned char volatile far *)0x7efebb)
|
||||
//#define CANDR (*(unsigned char volatile far *)0x7efebc)
|
||||
|
||||
//ʹ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ĺ<EFBFBD>,<2C><><EFBFBD>Ƚ<EFBFBD>EAXFR<46><52><EFBFBD><EFBFBD>Ϊ1
|
||||
//ʹ<EFBFBD>÷<EFBFBD><EFBFBD><EFBFBD>:
|
||||
//使用下面的宏,需先将EAXFR设置为1
|
||||
//使用方法:
|
||||
// char dat;
|
||||
//
|
||||
// EAXFR = 1; //ʹ<EFBFBD>ܷ<EFBFBD><EFBFBD><EFBFBD>XFR
|
||||
// dat = READ_CAN(RX_BUF0); //<EFBFBD><EFBFBD>CAN<EFBFBD>Ĵ<EFBFBD><EFBFBD><EFBFBD>
|
||||
// WRITE_CAN(TX_BUF0, 0x55); //дCAN<EFBFBD>Ĵ<EFBFBD><EFBFBD><EFBFBD>
|
||||
// EAXFR = 1; //使能访问XFR
|
||||
// dat = READ_CAN(RX_BUF0); //读CAN寄存器
|
||||
// WRITE_CAN(TX_BUF0, 0x55); //写CAN寄存器
|
||||
|
||||
#define READ_CAN(reg) (CANAR = (reg), CANDR)
|
||||
#define WRITE_CAN(reg, dat) (CANAR = (reg), CANDR = (dat))
|
||||
@@ -1218,11 +1226,11 @@ typedef struct TAG_PWM_STRUCT
|
||||
//sfr LINAR = 0xfa;
|
||||
//sfr LINDR = 0xfb;
|
||||
|
||||
//ʹ<EFBFBD>÷<EFBFBD><EFBFBD><EFBFBD>:
|
||||
//使用方法:
|
||||
// char dat;
|
||||
//
|
||||
// dat = READ_LIN(LBUF); //<EFBFBD><EFBFBD>CAN<EFBFBD>Ĵ<EFBFBD><EFBFBD><EFBFBD>
|
||||
// WRITE_LIN(LBUF, 0x55); //дCAN<EFBFBD>Ĵ<EFBFBD><EFBFBD><EFBFBD>
|
||||
// dat = READ_LIN(LBUF); //读CAN寄存器
|
||||
// WRITE_LIN(LBUF, 0x55); //写CAN寄存器
|
||||
|
||||
#define READ_LIN(reg) (LINAR = (reg), LINDR)
|
||||
#define WRITE_LIN(reg, dat) (LINAR = (reg), LINDR = (dat))
|
||||
@@ -1249,11 +1257,11 @@ typedef struct TAG_PWM_STRUCT
|
||||
//sfr USBCON = 0xf4;
|
||||
//sfr USBADR = 0xfc;
|
||||
|
||||
//ʹ<EFBFBD>÷<EFBFBD><EFBFBD><EFBFBD>:
|
||||
//使用方法:
|
||||
// char dat;
|
||||
//
|
||||
// READ_USB(CSR0, dat); //<EFBFBD><EFBFBD>USB<EFBFBD>Ĵ<EFBFBD><EFBFBD><EFBFBD>
|
||||
// WRITE_USB(FADDR, 0x00); //дUSB<EFBFBD>Ĵ<EFBFBD><EFBFBD><EFBFBD>
|
||||
// READ_USB(CSR0, dat); //读USB寄存器
|
||||
// WRITE_USB(FADDR, 0x00); //写USB寄存器
|
||||
|
||||
#define READ_USB(reg, dat) \
|
||||
{ \
|
||||
@@ -1435,8 +1443,8 @@ typedef struct TAG_PWM_STRUCT
|
||||
#define DMA_I2SR_VECTOR 64 //0203H
|
||||
|
||||
/////////////////////////////////////////////////
|
||||
#define EAXSFR() EAXFR = 1 /* MOVX A,@DPTR/MOVX @DPTR,Aָ<EFBFBD><EFBFBD><EFBFBD>IJ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ϊ<EFBFBD><EFBFBD>չSFR(XSFR) */
|
||||
#define EAXRAM() EAXFR = 0 /* MOVX A,@DPTR/MOVX @DPTR,Aָ<EFBFBD><EFBFBD><EFBFBD>IJ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ϊ<EFBFBD><EFBFBD>չRAM(XRAM) */
|
||||
#define EAXSFR() EAXFR = 1 /* MOVX A,@DPTR/MOVX @DPTR,A指令的操作对象为扩展SFR(XSFR) */
|
||||
#define EAXRAM() EAXFR = 0 /* MOVX A,@DPTR/MOVX @DPTR,A指令的操作对象为扩展RAM(XRAM) */
|
||||
|
||||
|
||||
/////////////////////////////////////////////////
|
||||
|
||||
Reference in New Issue
Block a user