2026-07-15 16:14:30 +08:00
|
|
|
|
#include "database.h"
|
|
|
|
|
|
|
2026-07-16 17:52:52 +08:00
|
|
|
|
// 内部 IAP 命令寄存器指令定义
|
|
|
|
|
|
#define IAP_CMD_READ 1 // IAP 字节读指令
|
|
|
|
|
|
#define IAP_CMD_WRITE 2 // IAP 字节写指令
|
|
|
|
|
|
#define IAP_CMD_ERASE 3 // IAP 扇区擦除指令
|
2026-07-15 16:14:30 +08:00
|
|
|
|
|
2026-07-16 17:52:52 +08:00
|
|
|
|
// 定义法语传感器默认名称常量数组,分别对应类型 0~4
|
2026-07-15 16:14:30 +08:00
|
|
|
|
char *code sensor_names_fr[5] = {
|
2026-07-16 17:52:52 +08:00
|
|
|
|
"PORTE", // 0: 门磁 (法语: 门)
|
|
|
|
|
|
"PIR", // 1: 红外探测器
|
|
|
|
|
|
"FUMEE", // 2: 烟雾传感器 (法语: 烟雾)
|
|
|
|
|
|
"GAZ", // 3: 燃气泄漏传感器 (法语: 燃气)
|
|
|
|
|
|
"EAU" // 4: 漏水/水浸传感器 (法语: 水)
|
2026-07-15 16:14:30 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
2026-07-16 17:52:52 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* @brief 关闭 IAP 寄存器控制并清理地址
|
|
|
|
|
|
* @details 每次 IAP 闪存操作后调用,确保安全,防止意外指针写入损坏 EEPROM 中的关键配置数据。
|
|
|
|
|
|
*/
|
2026-07-15 16:14:30 +08:00
|
|
|
|
void IAP_Disable(void)
|
|
|
|
|
|
{
|
2026-07-16 17:52:52 +08:00
|
|
|
|
IAP_CONTR = 0; // 关闭 IAP 功能,禁用触发
|
|
|
|
|
|
IAP_CMD = 0; // 清除内部命令
|
|
|
|
|
|
IAP_TRIG = 0; // 清零触发寄存器
|
|
|
|
|
|
IAP_ADDRH = 0xff; // 地址指针高八位置无效,防止意外指向
|
|
|
|
|
|
IAP_ADDRL = 0xff; // 地址指针低八位置无效
|
2026-07-15 16:14:30 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-16 17:52:52 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* @brief 读取指定 EEPROM 偏置地址处的 1 字节数据
|
|
|
|
|
|
* @param addr IAP 操作的目标物理地址
|
|
|
|
|
|
* @return u8 读取到的数据
|
|
|
|
|
|
*/
|
2026-07-15 16:14:30 +08:00
|
|
|
|
u8 IAP_ReadByte(u16 addr)
|
|
|
|
|
|
{
|
2026-07-16 17:52:52 +08:00
|
|
|
|
IAP_CONTR = 0x80; // 开启 IAP/EEPROM 功能,设置等待时间
|
|
|
|
|
|
IAP_TPS = (u8)(MAIN_Fosc / 1000000UL); // 设置闪存读写等待时间参数 (以MHz为单位,例如24MHz即24)
|
|
|
|
|
|
IAP_CMD = IAP_CMD_READ; // 发送读字节命令
|
|
|
|
|
|
IAP_ADDRL = (u8)addr; // 加载目标低八位地址
|
|
|
|
|
|
IAP_ADDRH = (u8)(addr >> 8); // 加载目标高八位地址
|
|
|
|
|
|
IAP_ADDRE = 0; // 重置拓展物理地址 (STC32G内部EEPROM通常不需要)
|
|
|
|
|
|
IAP_TRIG = 0x5a; // 触发写序列第一步密码
|
|
|
|
|
|
IAP_TRIG = 0xa5; // 触发写序列第二步密码 (必须连续送入,才能使能擦写)
|
|
|
|
|
|
_nop_(); _nop_(); _nop_(); _nop_(); // CPU 等待 4 个周期直至读取操作完成
|
|
|
|
|
|
IAP_Disable(); // 读完后立即关闭 IAP 控制
|
|
|
|
|
|
return IAP_DATA; // 返回读取出的数据寄存器字节
|
2026-07-15 16:14:30 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-16 17:52:52 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* @brief 在 EEPROM 指定地址写入 1 字节数据
|
|
|
|
|
|
* @param addr 目标物理地址
|
|
|
|
|
|
* @param dat 要写入的字节
|
|
|
|
|
|
*/
|
2026-07-15 16:14:30 +08:00
|
|
|
|
void IAP_WriteByte(u16 addr, u8 dat)
|
|
|
|
|
|
{
|
2026-07-16 17:52:52 +08:00
|
|
|
|
IAP_CONTR = 0x80; // 开启 IAP/EEPROM 功能
|
|
|
|
|
|
IAP_TPS = (u8)(MAIN_Fosc / 1000000UL); // 匹配工作频率对应的读写周期
|
|
|
|
|
|
IAP_CMD = IAP_CMD_WRITE; // 发送写字节命令
|
|
|
|
|
|
IAP_ADDRL = (u8)addr; // 送入低字节地址
|
|
|
|
|
|
IAP_ADDRH = (u8)(addr >> 8); // 送入高字节地址
|
|
|
|
|
|
IAP_ADDRE = 0; // 送入拓展地址
|
|
|
|
|
|
IAP_DATA = dat; // 装载待写入的字节数据
|
|
|
|
|
|
IAP_TRIG = 0x5a; // 送入触发密码 0x5A
|
|
|
|
|
|
IAP_TRIG = 0xa5; // 送入触发密码 0xA5 启动硬件写
|
|
|
|
|
|
_nop_(); _nop_(); _nop_(); _nop_(); // 等待写操作完成
|
|
|
|
|
|
IAP_Disable(); // 关闭 IAP 控制器
|
2026-07-15 16:14:30 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-16 17:52:52 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* @brief 擦除指定的 512 字节 EEPROM 物理扇区
|
|
|
|
|
|
* @param addr 扇区首地址
|
|
|
|
|
|
*/
|
2026-07-15 16:14:30 +08:00
|
|
|
|
void IAP_EraseSector(u16 addr)
|
|
|
|
|
|
{
|
2026-07-16 17:52:52 +08:00
|
|
|
|
IAP_CONTR = 0x80; // 开启 IAP 功能
|
|
|
|
|
|
IAP_TPS = (u8)(MAIN_Fosc / 1000000UL); // 匹配工作频率的擦除时间周期
|
|
|
|
|
|
IAP_CMD = IAP_CMD_ERASE; // 发送扇区擦除指令
|
|
|
|
|
|
IAP_ADDRL = (u8)addr; // 送入低字节地址
|
|
|
|
|
|
IAP_ADDRH = (u8)(addr >> 8); // 送入高字节地址
|
|
|
|
|
|
IAP_ADDRE = 0; // 拓展地址清零
|
|
|
|
|
|
IAP_TRIG = 0x5a; // 触发密码 0x5A
|
|
|
|
|
|
IAP_TRIG = 0xa5; // 触发密码 0xA5 启动硬件擦除
|
|
|
|
|
|
_nop_(); _nop_(); _nop_(); _nop_(); // 等待擦除完成 (扇区全部内容被初始化为 0xFF)
|
|
|
|
|
|
IAP_Disable(); // 关闭 IAP 操作以保护数据
|
2026-07-15 16:14:30 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-16 17:52:52 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* @brief 从 EEPROM 的扇区 254 中加载数据库
|
|
|
|
|
|
* @details 逐字节读取 Flash 物理数据,并直接通过指针拷贝装入 RAM 中的全局 `sensor_list` 数组。
|
|
|
|
|
|
*/
|
2026-07-15 16:14:30 +08:00
|
|
|
|
void Load_Database(void)
|
|
|
|
|
|
{
|
2026-07-16 17:52:52 +08:00
|
|
|
|
u16 addr = 0; // EEPROM 内部偏移地址从 0 开始
|
|
|
|
|
|
u8 *ptr = (u8 *)&sensor_list; // 获取传感器 RAM 数组的首地址指针
|
|
|
|
|
|
u16 size = sizeof(sensor_list); // 获取结构体数组的全部占用尺寸
|
2026-07-15 16:14:30 +08:00
|
|
|
|
u16 i;
|
|
|
|
|
|
for (i = 0; i < size; i++) {
|
2026-07-16 17:52:52 +08:00
|
|
|
|
ptr[i] = IAP_ReadByte(addr++); // 循环读取字节并还原到 RAM
|
2026-07-15 16:14:30 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-16 17:52:52 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* @brief 将 RAM 中的最新传感器列表数据持久化保存到 EEPROM
|
|
|
|
|
|
* @details 因为 Flash 写操作不能把 0 写回 1,所以必须先调用 `IAP_EraseSector` 擦除整个扇区,
|
|
|
|
|
|
* 接着遍历整个数据结构,逐字节写入最新的 RAM 内容。
|
|
|
|
|
|
*/
|
2026-07-15 16:14:30 +08:00
|
|
|
|
void Save_Database(void)
|
|
|
|
|
|
{
|
2026-07-16 17:52:52 +08:00
|
|
|
|
u16 addr = 0; // EEPROM 物理首地址
|
|
|
|
|
|
u8 *ptr = (u8 *)&sensor_list; // RAM 首指针
|
|
|
|
|
|
u16 size = sizeof(sensor_list); // 总字节量
|
2026-07-15 16:14:30 +08:00
|
|
|
|
u16 i;
|
2026-07-16 17:52:52 +08:00
|
|
|
|
IAP_EraseSector(0); // 擦除首扇区 (扇区 254,大小 512 字节足够存放)
|
2026-07-15 16:14:30 +08:00
|
|
|
|
for (i = 0; i < size; i++) {
|
2026-07-16 17:52:52 +08:00
|
|
|
|
IAP_WriteByte(addr++, ptr[i]); // 循环写入字节数据
|
2026-07-15 16:14:30 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-16 17:52:52 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* @brief 新增或更新已对码的传感器,并保存数据库
|
|
|
|
|
|
* @param addr 24位射频地址
|
|
|
|
|
|
* @param type 传感器类型索引 (0~4)
|
|
|
|
|
|
* @param zone 自定义分配的防区号 (1~99, 0)
|
|
|
|
|
|
*/
|
2026-07-15 16:14:30 +08:00
|
|
|
|
void Add_Sensor_With_Zone(u32 addr, u8 type, u8 zone)
|
|
|
|
|
|
{
|
|
|
|
|
|
u8 i;
|
2026-07-16 17:52:52 +08:00
|
|
|
|
u8 slot_to_use = 15; // 默认使用最后一槽
|
|
|
|
|
|
|
|
|
|
|
|
// 遍历现有的 16 个槽位,寻找匹配的地址或者空闲的槽位
|
2026-07-15 16:14:30 +08:00
|
|
|
|
for (i = 0; i < 16; i++) {
|
|
|
|
|
|
if (sensor_list[i].is_used == 0x01) {
|
2026-07-16 17:52:52 +08:00
|
|
|
|
// 解析该槽位中存储的 24 位传感器地址
|
2026-07-15 16:14:30 +08:00
|
|
|
|
u32 existing_addr = ((u32)sensor_list[i].addr[0] << 16) |
|
|
|
|
|
|
((u32)sensor_list[i].addr[1] << 8) |
|
|
|
|
|
|
sensor_list[i].addr[2];
|
2026-07-16 17:52:52 +08:00
|
|
|
|
// 如果地址已经存在,说明这是要修改现有槽位的防区或类型
|
2026-07-15 16:14:30 +08:00
|
|
|
|
if (existing_addr == addr) {
|
2026-07-16 17:52:52 +08:00
|
|
|
|
slot_to_use = i; // 使用该槽位进行更新
|
2026-07-15 16:14:30 +08:00
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
2026-07-16 17:52:52 +08:00
|
|
|
|
// 如果找到未启用的空闲槽位,暂定使用它
|
2026-07-15 16:14:30 +08:00
|
|
|
|
slot_to_use = i;
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-16 17:52:52 +08:00
|
|
|
|
// 配置传感器槽位数据
|
|
|
|
|
|
sensor_list[slot_to_use].is_used = 0x01; // 标记槽位为已启用状态
|
|
|
|
|
|
sensor_list[slot_to_use].addr[0] = (u8)(addr >> 16); // 存入地址高字节
|
|
|
|
|
|
sensor_list[slot_to_use].addr[1] = (u8)(addr >> 8); // 存入地址中字节
|
|
|
|
|
|
sensor_list[slot_to_use].addr[2] = (u8)addr; // 存入地址低字节
|
|
|
|
|
|
sensor_list[slot_to_use].type = type; // 记录传感器硬件类型
|
|
|
|
|
|
sensor_list[slot_to_use].zone = zone; // 记录对应的防区号
|
|
|
|
|
|
|
|
|
|
|
|
// 生成该槽位传感器的法语名称
|
2026-07-15 16:14:30 +08:00
|
|
|
|
for (i = 0; i < 15; i++) {
|
2026-07-16 17:52:52 +08:00
|
|
|
|
// 从默认名称常量数组中复制对应名字的字符
|
2026-07-15 16:14:30 +08:00
|
|
|
|
sensor_list[slot_to_use].name_gbk[i] = sensor_names_fr[type][i];
|
2026-07-16 17:52:52 +08:00
|
|
|
|
if (sensor_names_fr[type][i] == '\0') break; // 遇零终止
|
2026-07-15 16:14:30 +08:00
|
|
|
|
}
|
2026-07-16 17:52:52 +08:00
|
|
|
|
sensor_list[slot_to_use].name_gbk[15] = '\0'; // 尾部安全截断,防止字符越界
|
|
|
|
|
|
|
|
|
|
|
|
Save_Database(); // 调用保存函数,强制持久化写回 IAP Flash
|
2026-07-15 16:14:30 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-16 17:52:52 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* @brief 检测输入的 24 位射频地址是否已被当前绑定的任何槽位匹配
|
|
|
|
|
|
* @param addr 传入待匹配的 24 位射频地址
|
|
|
|
|
|
* @param out_slot_index 若匹配成功,输出槽位的索引号指针
|
|
|
|
|
|
* @return bit 1-匹配成功,0-该地址尚未被本系统学习绑定
|
|
|
|
|
|
*/
|
2026-07-15 16:14:30 +08:00
|
|
|
|
bit Check_Sensor_ID(u32 addr, u8 *out_slot_index)
|
|
|
|
|
|
{
|
|
|
|
|
|
u8 i;
|
|
|
|
|
|
for (i = 0; i < 16; i++) {
|
2026-07-16 17:52:52 +08:00
|
|
|
|
// 仅比对正在使用中的槽位
|
2026-07-15 16:14:30 +08:00
|
|
|
|
if (sensor_list[i].is_used == 0x01) {
|
|
|
|
|
|
u32 slot_addr = ((u32)sensor_list[i].addr[0] << 16) |
|
|
|
|
|
|
((u32)sensor_list[i].addr[1] << 8) |
|
|
|
|
|
|
sensor_list[i].addr[2];
|
|
|
|
|
|
if (slot_addr == addr) {
|
2026-07-16 17:52:52 +08:00
|
|
|
|
*out_slot_index = i; // 写入输出变量
|
|
|
|
|
|
return 1; // 匹配成功返回真
|
2026-07-15 16:14:30 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-07-16 17:52:52 +08:00
|
|
|
|
return 0; // 遍历结束未找到,返回假
|
2026-07-15 16:14:30 +08:00
|
|
|
|
}
|