Files
stc32g128k/App/database.c

208 lines
9.4 KiB
C
Raw Normal View History

#include "database.h"
2026-07-31 13:57:06 +08:00
#include "language.h"
// 内部 IAP 命令寄存器指令定义
#define IAP_CMD_READ 1 // IAP 字节读指令
#define IAP_CMD_WRITE 2 // IAP 字节写指令
#define IAP_CMD_ERASE 3 // IAP 扇区擦除指令
2026-07-31 13:57:06 +08:00
// 传感器类型简称的字符串 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: 漏水/水浸传感器
};
2026-07-31 13:57:06 +08:00
// 紧随 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
2026-07-31 13:57:06 +08:00
* @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;
2026-07-31 13:57:06 +08:00
u8 lang_byte;
for (i = 0; i < size; i++) {
ptr[i] = IAP_ReadByte(addr++); // 循环读取字节并还原到 RAM
}
2026-07-31 13:57:06 +08:00
// 读取语言设置字节 (首次开机、从未保存过时 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`
2026-07-31 13:57:06 +08:00
* 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]); // 循环写入字节数据
}
2026-07-31 13:57:06 +08:00
// 紧随传感器数据之后追加写入当前界面语言设置
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; // 记录对应的防区号
2026-07-31 13:57:06 +08:00
// 生成该槽位传感器的名称 (取当前界面语言下的简称)
{
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; // 遍历结束未找到,返回假
}