Files
stc32g128k/App/database.c
edisondeng a7a2cc9465 暂存
2026-07-31 13:57:06 +08:00

208 lines
9.4 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "database.h"
#include "language.h"
// 内部 IAP 命令寄存器指令定义
#define IAP_CMD_READ 1 // IAP 字节读指令
#define IAP_CMD_WRITE 2 // IAP 字节写指令
#define IAP_CMD_ERASE 3 // IAP 扇区擦除指令
// 传感器类型简称的字符串 ID分别对应类型 0~4实际文案由 Lang_Get() 按当前语言取出
const StringID sensor_type_name_ids[5] = {
STR_NAME_PORTE, // 0: 门磁
STR_NAME_PIR, // 1: 红外探测器
STR_NAME_FUMEE, // 2: 烟雾传感器
STR_NAME_GAZ, // 3: 燃气泄漏传感器
STR_NAME_EAU // 4: 漏水/水浸传感器
};
// 紧随 sensor_list 之后存放的 1 字节界面语言设置在 Flash 扇区内的偏移地址
#define LANGUAGE_FLASH_ADDR ((u16)sizeof(sensor_list))
/**
* @brief 关闭 IAP 寄存器控制并清理地址
* @details 每次 IAP 闪存操作后调用,确保安全,防止意外指针写入损坏 EEPROM 中的关键配置数据。
*/
void IAP_Disable(void)
{
IAP_CONTR = 0; // 关闭 IAP 功能,禁用触发
IAP_CMD = 0; // 清除内部命令
IAP_TRIG = 0; // 清零触发寄存器
IAP_ADDRH = 0xff; // 地址指针高八位置无效,防止意外指向
IAP_ADDRL = 0xff; // 地址指针低八位置无效
}
/**
* @brief 读取指定 EEPROM 偏置地址处的 1 字节数据
* @param addr IAP 操作的目标物理地址
* @return u8 读取到的数据
*/
u8 IAP_ReadByte(u16 addr)
{
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; // 返回读取出的数据寄存器字节
}
/**
* @brief 在 EEPROM 指定地址写入 1 字节数据
* @param addr 目标物理地址
* @param dat 要写入的字节
*/
void IAP_WriteByte(u16 addr, u8 dat)
{
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 控制器
}
/**
* @brief 擦除指定的 512 字节 EEPROM 物理扇区
* @param addr 扇区首地址
*/
void IAP_EraseSector(u16 addr)
{
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 操作以保护数据
}
/**
* @brief 从 EEPROM 的扇区 254 中加载数据库
* @details 逐字节读取 Flash 物理数据,并直接通过指针拷贝装入 RAM 中的全局 `sensor_list` 数组;
* 随后读取紧随其后的 1 字节界面语言设置,恢复上次保存的语言选择。
*/
void Load_Database(void)
{
u16 addr = 0; // EEPROM 内部偏移地址从 0 开始
u8 *ptr = (u8 *)&sensor_list; // 获取传感器 RAM 数组的首地址指针
u16 size = sizeof(sensor_list); // 获取结构体数组的全部占用尺寸
u16 i;
u8 lang_byte;
for (i = 0; i < size; i++) {
ptr[i] = IAP_ReadByte(addr++); // 循环读取字节并还原到 RAM
}
// 读取语言设置字节 (首次开机、从未保存过时 Flash 为擦除态 0xFFLang_Init 会自动回退为 LANG_FR)
lang_byte = IAP_ReadByte(LANGUAGE_FLASH_ADDR);
Lang_Init((Language)lang_byte);
}
/**
* @brief 将 RAM 中的最新传感器列表数据持久化保存到 EEPROM
* @details 因为 Flash 写操作不能把 0 写回 1所以必须先调用 `IAP_EraseSector` 擦除整个扇区,
* 接着遍历整个数据结构,逐字节写入最新的 RAM 内容,最后追加写入当前界面语言设置字节。
*/
void Save_Database(void)
{
u16 addr = 0; // EEPROM 物理首地址
u8 *ptr = (u8 *)&sensor_list; // RAM 首指针
u16 size = sizeof(sensor_list); // 总字节量
u16 i;
IAP_EraseSector(0); // 擦除首扇区 (扇区 254大小 512 字节足够存放)
for (i = 0; i < size; i++) {
IAP_WriteByte(addr++, ptr[i]); // 循环写入字节数据
}
// 紧随传感器数据之后追加写入当前界面语言设置
IAP_WriteByte(LANGUAGE_FLASH_ADDR, (u8)Lang_Current());
}
/**
* @brief 新增或更新已对码的传感器,并保存数据库
* @param addr 24位射频地址
* @param type 传感器类型索引 (0~4)
* @param zone 自定义分配的防区号 (1~99, 0)
*/
void Add_Sensor_With_Zone(u32 addr, u8 type, u8 zone)
{
u8 i;
u8 slot_to_use = 15; // 默认使用最后一槽
// 遍历现有的 16 个槽位,寻找匹配的地址或者空闲的槽位
for (i = 0; i < 16; i++) {
if (sensor_list[i].is_used == 0x01) {
// 解析该槽位中存储的 24 位传感器地址
u32 existing_addr = ((u32)sensor_list[i].addr[0] << 16) |
((u32)sensor_list[i].addr[1] << 8) |
sensor_list[i].addr[2];
// 如果地址已经存在,说明这是要修改现有槽位的防区或类型
if (existing_addr == addr) {
slot_to_use = i; // 使用该槽位进行更新
break;
}
} else {
// 如果找到未启用的空闲槽位,暂定使用它
slot_to_use = i;
break;
}
}
// 配置传感器槽位数据
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; // 记录对应的防区号
// 生成该槽位传感器的名称 (取当前界面语言下的简称)
{
const char *type_name = Lang_Get(sensor_type_name_ids[type]);
for (i = 0; i < 15; i++) {
// 从当前语言名称字符串中复制对应名字的字符
sensor_list[slot_to_use].name_gbk[i] = type_name[i];
if (type_name[i] == '\0') break; // 遇零终止
}
}
sensor_list[slot_to_use].name_gbk[15] = '\0'; // 尾部安全截断,防止字符越界
Save_Database(); // 调用保存函数,强制持久化写回 IAP Flash
}
/**
* @brief 检测输入的 24 位射频地址是否已被当前绑定的任何槽位匹配
* @param addr 传入待匹配的 24 位射频地址
* @param out_slot_index 若匹配成功,输出槽位的索引号指针
* @return bit 1-匹配成功0-该地址尚未被本系统学习绑定
*/
bit Check_Sensor_ID(u32 addr, u8 *out_slot_index)
{
u8 i;
for (i = 0; i < 16; i++) {
// 仅比对正在使用中的槽位
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) {
*out_slot_index = i; // 写入输出变量
return 1; // 匹配成功返回真
}
}
}
return 0; // 遍历结束未找到,返回假
}