diff --git a/Core/AdcFilter.c b/Core/AdcFilter.c
index 046089b..76aceaa 100644
--- a/Core/AdcFilter.c
+++ b/Core/AdcFilter.c
@@ -72,7 +72,7 @@ short AdcFilterCalc(AdcFilter_t* filter, int valueMax, int outRange)
/** @brief 初始化ADC范围,计算完K B之后会调整 adc Min Max 范围 */
-bool AdcScopeInit(AdcScope_t* scope, ushort adcMin, ushort adcMax, int valueMin, int valueMax)
+bool AdcConvertInit(AdcConvert_t* scope, ushort adcMin, ushort adcMax, int valueMin, int valueMax)
{
scope->ValueMax = valueMax;
scope->ValueMin = valueMin;
@@ -80,7 +80,7 @@ bool AdcScopeInit(AdcScope_t* scope, ushort adcMin, ushort adcMax, int valueMin,
scope->AdcMin = adcMin;
// 计算K和B值
scope->K = (float)(valueMax - valueMin) / (adcMax - adcMin);
- scope->B = valueMin - (scope->K * adcMin);
+ scope->B = (float)valueMin - (scope->K * adcMin);
return true;
}
@@ -90,24 +90,51 @@ bool AdcScopeInit(AdcScope_t* scope, ushort adcMin, ushort adcMax, int valueMin,
* @param filter 滤波器
* @param scope 范围
* @param fixThr 修正阈值,ADC抖动大于这个值,不进行范围矫正
- * @return double
+ * @return float
*/
-double AdcScopeCalc_Double(AdcFilter_t* filter,AdcScope_t* scp,int fixThr)
+float AdcConvertCalc_Float(AdcFilter_t* filter,AdcConvert_t* scp,int fixThr)
{
- if(scp->K < 0.0)return -1;
+ return AdcConvertCalc_GainFloat(filter,scp,fixThr,100000);
+}
+
+/**
+ * @brief 范围矫正
+ * @param filter 滤波器
+ * @param scope 范围
+ * @param fixThr 修正阈值,ADC抖动大于这个值,不进行范围矫正
+ * @return int
+ */
+int AdcConvertCalc(AdcFilter_t* filter,AdcConvert_t* scp,int fixThr)
+{
+ return AdcConvertCalc_Gain(filter,scp,fixThr,100000);
+}
+
+/**
+ * @brief 范围矫正
+ * @param filter 滤波器
+ * @param scope 范围
+ * @param fixThr 修正阈值,ADC抖动大于这个值,不进行范围矫正
+ * @param adcGain ADC数值增益,用于处理参考电压问题(原本是浮点,优化整型定点,扩大100_000)
+ * @return float
+ */
+float AdcConvertCalc_GainFloat(AdcFilter_t* filter, AdcConvert_t* scp, int fixThr, int adcGain)
+{
+ if(scp->K < 0.0f)return -1.0f;
int adc = AdcFilterCalc(filter, 4096,4096);
+ if(adcGain != 100000)adc = (int)(adc * adcGain / 100000);
+
// 超出修正范围返回 -1
- if(adc + fixThr < scp->AdcMin)return -1;
- if(adc - fixThr > scp->AdcMax)return -1;
+ if(adc + fixThr < scp->AdcMin)return -1.0f;
+ if(adc - fixThr > scp->AdcMax)return -1.0f;
// 超出范围,修正范围
if(adc < scp->AdcMin)adc = scp->AdcMin;
if(adc > scp->AdcMax)adc = scp->AdcMax;
// 计算
- int result = adc * scp->K + scp->B;
+ float result = adc * scp->K + scp->B;
// 结果限定一下,避免因为K B 计算不准,导致结果超出范围
- if(result < scp->ValueMin)return scp->ValueMin;
- if(result > scp->ValueMax)return scp->ValueMax;
+ if(result < scp->ValueMin)return (float)scp->ValueMin;
+ if(result > scp->ValueMax)return (float)scp->ValueMax;
return result;
}
@@ -117,13 +144,16 @@ double AdcScopeCalc_Double(AdcFilter_t* filter,AdcScope_t* scp,int fixThr)
* @param filter 滤波器
* @param scope 范围
* @param fixThr 修正阈值,ADC抖动大于这个值,不进行范围矫正
+ * @param adcGain ADC数值增益,用于处理参考电压问题(原本是浮点,优化整型定点,扩大100_000)
* @return int
*/
-int AdcScopeCalc(AdcFilter_t* filter,AdcScope_t* scp,int fixThr)
+int AdcConvertCalc_Gain(AdcFilter_t* filter, AdcConvert_t* scp, int fixThr, int adcGain)
{
- if(scp->K < 0.0)return -1;
+ if(scp->K < 0.0f)return -1;
int adc = AdcFilterCalc(filter, 4096,4096);
+ if(adcGain != 100000)adc = (int)(adc * adcGain / 100000);
+
// 超出修正范围返回 -1
if(adc + fixThr < scp->AdcMin)return -1;
if(adc - fixThr > scp->AdcMax)return -1;
@@ -138,3 +168,4 @@ int AdcScopeCalc(AdcFilter_t* filter,AdcScope_t* scp,int fixThr)
return result;
}
+
diff --git a/Core/AdcFilter.h b/Core/AdcFilter.h
index c2aeed8..cf5fa05 100644
--- a/Core/AdcFilter.h
+++ b/Core/AdcFilter.h
@@ -42,29 +42,84 @@ typedef struct
int ValueMax;
// Y = KX + B
- double K;
+ float K;
// Y = KX + B
- double B;
-}AdcScope_t;
+ float B;
+}AdcConvert_t;
-/** @brief 初始化ADC范围,计算K B */
-bool AdcScopeInit(AdcScope_t* scope, ushort adcMin, ushort adcMax, int valueMin, int valueMax);
+/**
+ * @brief 初始化ADC范围,计算K B
+ * @param scope
+ * @param adcMin
+ * @param adcMax 正常在 4096 以下。如果大于,需要相应修改 valueMax 范围。
+ * @param valueMin
+ * @param valueMax 控制 valueMax - valueMin < 1_000_000,避免误差
+ * @return true
+ * @return false
+ */
+bool AdcConvertInit(AdcConvert_t* scope, ushort adcMin, ushort adcMax, int valueMin, int valueMax);
+
+/**
+ * @brief 范围矫正
+ * @param filter 滤波器
+ * @param scope 范围
+ * @param fixThr 修正阈值,ADC抖动大于这个值,不进行范围矫正
+ * @return float
+ */
+float AdcConvertCalc_Float(AdcFilter_t* filter, AdcConvert_t* scp, int fixThr);
+
+/**
+ * @brief 范围矫正
+ * @param filter 滤波器
+ * @param scope 范围
+ * @param fixThr 修正阈值,ADC抖动大于这个值,不进行范围矫正
+ * @return int
+ */
+int AdcConvertCalc(AdcFilter_t* filter, AdcConvert_t* scp, int fixThr);
+
+/*
+一个信号,来自于不是基于ADC参考电源的模拟量。
+即:ADC参考电源和被测信号的参考电源不是同一个。
+测出的ADC数据是无法直接使用的,需要矫正。
+
+解决办法:
+ ADC1 采集被测信号参考电源
+ ADC2 采集被测信号
+ 两个ADC同步采样,避免时域上的干扰。
+
+矫正计算
+ ADC1数值/理想ADC1数值 = 增益
+ ADC2数值 * 增益 = 矫正后数值
+
+建议将【增益】数值做一下处理。
+低于 0.9999 到 1.0001 直接当 1.0 处理,减少函数内 float 运算。
+
+*/
/**
* @brief 范围矫正
* @param filter 滤波器
* @param scope 范围
* @param fixThr 修正阈值,ADC抖动大于这个值,不进行范围矫正
- * @return double
+ * @param adcGain ADC数值增益,用于处理参考电压问题(原本是浮点,优化整型定点,扩大100_000)
+ * @return float
*/
-double AdcScopeCalc_Double(AdcFilter_t* filter,AdcScope_t* scp,int fixThr);
+float AdcConvertCalc_GainFloat(AdcFilter_t* filter, AdcConvert_t* scp, int fixThr, int adcGain);
/**
* @brief 范围矫正
* @param filter 滤波器
* @param scope 范围
* @param fixThr 修正阈值,ADC抖动大于这个值,不进行范围矫正
+ * @param adcGain ADC数值增益,用于处理参考电压问题(原本是浮点,优化整型定点,扩大100_000)
* @return int
*/
-int AdcScopeCalc(AdcFilter_t* filter,AdcScope_t* scope,int fixThr);
+int AdcConvertCalc_Gain(AdcFilter_t* filter, AdcConvert_t* scp, int fixThr, int adcGain);
+
+/*
+测PTC/NTC电阻计算公式
+
+分压:3V3-5.1K-NTC-GND,adc/4096 = ntc/(5100+ntc) → ntc = 5100*adc/(4096-adc)
+分压:3V3-NTC-5.1K-GND,adc/4096 = 5100/(5100+ntc) → ntc = 5100*4096/adc - 5100
+*/
diff --git a/Core/Array.c b/Core/Array.c
index 4a2c0c0..03e6b38 100644
--- a/Core/Array.c
+++ b/Core/Array.c
@@ -1,5 +1,28 @@
-
#include "Array.h"
+#include "Debug.h"
+
+void ShowArray(char* name, byte* pdata, int datalen, int showlen)
+{
+#ifdef DEBUG
+ uint printlen = datalen > showlen ? showlen : datalen;
+ DebugPrintf("%s [%d] = ", name, datalen);
+
+ if (printlen > 32)
+ {
+ for (int i = 0; i < printlen; i++)
+ {
+ if (i % 32 == 0)DebugPrintf("\r\n");
+ DebugPrintf("%02X ", pdata[i]);
+ }
+ }
+ else
+ {
+ for (int i = 0; i < printlen; i++)DebugPrintf("%02X ", pdata[i]);
+ }
+
+ DebugPrintf(printlen == datalen ? "\r\n" : "...\r\n");
+#endif
+}
int ArrayIndexOf(byte* src, int srclen, byte* sub, int sublen)
{
@@ -83,22 +106,6 @@ int ArrayIndexOf_Byte_Offset(byte* src, int len, byte by, int offset)
return -1;
}
-int ArrayIndexOfBuff(Buffer_t* src, Buffer_t* sub)
-{
- if (src == NULL)return -1;
- if (sub == NULL)return -1;
-
- return ArrayIndexOf(src->Data, src->Len, sub->Data, sub->Len);
-}
-
-int ArrayIndexOfBuff2(Buffer2_t* src, Buffer2_t* sub)
-{
- if (src == NULL)return -1;
- if (sub == NULL)return -1;
-
- return ArrayIndexOf(src->Data, src->Len, sub->Data, sub->Len);
-}
-
int ArrayLastIndexOf(byte* src, int srclen, byte* sub, int sublen)
{
if (srclen < 1)return -1;
@@ -131,22 +138,6 @@ int ArrayLastIndexOf(byte* src, int srclen, byte* sub, int sublen)
return -1;
}
-int ArrayLastIndexOfBuff(Buffer_t* src, Buffer_t* sub)
-{
- if (src == NULL)return -1;
- if (sub == NULL)return -1;
-
- return ArrayLastIndexOf(src->Data, src->Len, sub->Data, sub->Len);
-}
-
-int ArrayLastIndexOfBuff2(Buffer2_t* src, Buffer2_t* sub)
-{
- if (src == NULL)return -1;
- if (sub == NULL)return -1;
-
- return ArrayLastIndexOf(src->Data, src->Len, sub->Data, sub->Len);
-}
-
void ArrayReverse(byte* data, int len)
{
if (data == NULL)return;
@@ -169,20 +160,6 @@ void ArrayReverse(byte* data, int len)
}
}
-void ArrayReverseBuff(Buffer_t* buff)
-{
- if (buff == NULL)return;
-
- ArrayReverse(buff->Data, buff->Len);
-}
-
-void ArrayReverseBuff2(Buffer2_t* buff)
-{
- if (buff == NULL)return;
-
- ArrayReverse(buff->Data, buff->Len);
-}
-
void ArraySortByte(byte* data, int len)
{
for (int i = 0; i < len - 1; i++)
diff --git a/Core/Array.h b/Core/Array.h
index e68d262..c84bf80 100644
--- a/Core/Array.h
+++ b/Core/Array.h
@@ -1,7 +1,16 @@
#pragma once
-
#include "Type.h"
-#include "Buffer.h"
+
+/**
+ * @brief 打印数组内容
+ * @param head 数组名称
+ * @param pdata 数组指针
+ * @param datalen 数组长度
+ * @param showlen 打印长度
+ */
+void ShowArray(char* name, byte* pdata, int datalen, int showlen);
+// 数组长度
+#define ArrayLength(arr) ((int)(sizeof(arr)/sizeof(arr[0])))
// 返回第一个符合的位置
// 没有找到返回 -1
@@ -11,19 +20,12 @@ int ArrayIndexOf_Byte(byte* src, int len, byte by);
int ArrayIndexOf_Offset(byte* src, int srclen, byte* sub, int sublen, int offset);
int ArrayIndexOf_Byte_Offset(byte* src, int srclen, byte by, int offset);
-int ArrayIndexOfBuff(Buffer_t* src,Buffer_t* sub);
-int ArrayIndexOfBuff2(Buffer2_t* src, Buffer2_t* sub);
-
// 从尾部匹配
// 返回从前往后的下标。
int ArrayLastIndexOf(byte* src, int srclen, byte* sub, int sublen);
-int ArrayLastIndexOfBuff(Buffer_t* src, Buffer_t* sub);
-int ArrayLastIndexOfBuff2(Buffer2_t* src, Buffer2_t* sub);
// 倒序
void ArrayReverse(byte* data, int len);
-void ArrayReverseBuff(Buffer_t* buff);
-void ArrayReverseBuff2(Buffer2_t* buff);
// 排序 从小到大 内部使用冒泡排序
void ArraySortByte(byte* data, int len);
@@ -33,7 +35,6 @@ void ArraySortInt(int* data, int len);
// 其他
// copy 直接用 c 库
-
// 对比数组,相同返回true
bool ArrayEqual(byte* b1, byte* b2, int len);
diff --git a/Core/BitHelper.c b/Core/BitHelper.c
index 7041c98..2dd9d73 100644
--- a/Core/BitHelper.c
+++ b/Core/BitHelper.c
@@ -135,3 +135,115 @@ int BitCount(byte* data, int len)
return cnt;
}
+#include "Debug.h"
+
+/**
+ * @brief 打印内容
+ * @param name 名称
+ * @param data 指针
+ * @param blen 长度
+ */
+void ShowBits(char* name, byte* data, int blen)
+{
+ if(data == NULL)return;
+ if(blen < 1)return;
+ if(name != NULL)
+ {
+ DebugPrintf("%s%s",name, (blen > 16 ? "\r\n" : " "));
+ }
+
+ // 按字节分组 "1010 1010\0" 中间空格,末尾\0
+ char chs[8 + 2];
+ memset(chs,' ',sizeof(chs) - 1);
+ chs[sizeof(chs)-1] = 0;
+
+ for (int i = 0; ; i++)
+ {
+ bool value = (data[i >> 3] & (0x01 << (i & 0x07))) != 0;
+
+ // chs 内下标
+ int cidx = i & 0x7;
+ // "1010 1010"
+ chs[cidx + (cidx > 3 ? 1 : 0)] = value ? '1':'0';
+
+ if((i & 0x7) == 0x7)
+ {
+ DebugPrintf("%s ",chs);
+ memset(chs,' ',sizeof(chs) - 1);
+ }
+ if((i & 0xf) == 0xf) { DebugPrintf("\r\n"); }
+ if(i >= blen - 1)return;
+ }
+}
+
+/**
+ * @brief 打印寄存器,每个寄存器都有名字,每个寄存器1bit
+ * @param regs 寄存器名称,NULL 或者长度0 忽略
+ * @param data 数值
+ * @param reglen 寄存器个数
+ */
+void ShowBitRegs(char** regs, byte* data, int rlen)
+{
+ if(regs == NULL)return;
+ if(data == NULL)return;
+ if(rlen < 1)return;
+
+ for (int i = 0; i < rlen; i++)
+ {
+ if(regs[i] == NULL)continue;
+ if(strlen(regs[i]) < 1)continue;
+
+ bool value = (data[i >> 3] & (0x01 << (i & 0x07))) != 0;
+ DebugPrintf("%s %s\r\n",value?"[1]":"[0]", regs[i]);
+ }
+}
+
+/**
+ * @brief 读寄存器
+ * @param reg
+ * @param data
+ * @return uint
+ */
+uint RegRead(RegItem_t* reg, byte* data)
+{
+ if(reg == NULL)return 0;
+ if(data == NULL)return 0;
+
+ uint res = 0;
+ BitCopy((byte*)&res, 0, data, reg->Offset, reg->BLen);
+ return res;
+}
+
+/**
+ * @brief 写寄存器
+ * @param reg
+ * @param data
+ */
+void RegWrite(RegItem_t* reg, byte* data, uint value)
+{
+ if(reg == NULL)return;
+ if(data == NULL)return;
+
+ BitCopy(data, reg->Offset, (byte*)&value, 0, reg->BLen);
+}
+
+/**
+ * @brief 打印寄存器值
+ * @param regs 寄存器信息
+ * @param data 内存
+ * @param rlen 寄存器长度
+ */
+void ShowRegs(RegItem_t* regs, byte* data, int rlen)
+{
+ if(regs == NULL)return;
+ if(data == NULL)return;
+
+ for (int i = 0; i < rlen; i++)
+ {
+ RegItem_t* reg = ®s[i];
+
+ uint value = 0;
+ BitCopy((byte*)&value, 0, data, reg->Offset, reg->BLen);
+ DebugPrintf("%s \t%d\r\n",reg->Name,value);
+ }
+}
diff --git a/Core/BitHelper.h b/Core/BitHelper.h
index 42d54b1..db2b165 100644
--- a/Core/BitHelper.h
+++ b/Core/BitHelper.h
@@ -41,3 +41,51 @@ bool SetBits(byte* dst, int dst_offset, byte* src, int src_offset, int bits);
bool BitCopy(byte* dst, int dst_offset, byte* src, int src_offset, int bits);
*/
+/**
+ * @brief 打印内容
+ * @param name 名称。
+ * @param data 指针
+ * @param blen 长度
+ */
+void ShowBits(char* name, byte* data, int blen);
+
+/**
+ * @brief 打印寄存器,每个寄存器都有名字,每个寄存器1bit
+ * @param regs 寄存器名称,NULL 或者长度0 忽略
+ * @param data 数值
+ * @param rlen 寄存器个数
+ */
+void ShowBitRegs(char** regs, byte* data, int rlen);
+
+/** @brief 寄存器,小于4字节,且边界对齐 */
+typedef struct
+{
+ // 名称
+ char* Name;
+ // 内存对齐用 ushort
+ ushort Offset;
+ // 长度
+ ushort BLen;
+}RegItem_t;
+
+/**
+ * @brief 读寄存器
+ * @param reg
+ * @param data
+ * @return uint
+ */
+uint RegRead(RegItem_t* reg, byte* data);
+/**
+ * @brief 写寄存器
+ * @param reg
+ * @param data
+ */
+void RegWrite(RegItem_t* reg, byte* data, uint value);
+/**
+ * @brief 打印寄存器值
+ * @param regs 寄存器信息
+ * @param data 内存
+ * @param rlen 寄存器长度
+ */
+void ShowRegs(RegItem_t* regs, byte* data, int rlen);
+
diff --git a/Core/BitmapMlmeMem.c b/Core/BitmapMlmeMem.c
index dbfa3a4..86d94e0 100644
--- a/Core/BitmapMlmeMem.c
+++ b/Core/BitmapMlmeMem.c
@@ -226,7 +226,7 @@ bool BitmapMlmeCheck(BitmapMlme_t* mlme, ushort bitIdx)
byte itemmask = 0x01 << (bitIdx & 0x7);
// 判断是否置1
- return ((*mapitem_p) & itemmask != 0x00);
+ return (((*mapitem_p) & itemmask) != 0x00);
}
int BitmapMlmeGetEmptyIdx(BitmapMlme_t* mlme, ushort bitStart, byte* buff, int bufflen)
diff --git a/Core/Buffer.c b/Core/Buffer.c
index 0a96a4a..b71f819 100644
--- a/Core/Buffer.c
+++ b/Core/Buffer.c
@@ -1,6 +1,7 @@
#include "Buffer.h"
#include "Debug.h"
+#include "Array.h"
__inline void InitBuffer(Buffer_t* data) { memset(data, 0x00, sizeof(Buffer_t)); data->Free = GlobleFree;}
__inline void InitBuffer2(Buffer2_t* data) { memset(data, 0x00, sizeof(Buffer2_t));data->Free = GlobleFree;}
@@ -31,35 +32,59 @@ __inline void TryCleanBuffer2(Buffer2_t* data)
}
}
-void ShowArray(char* name, byte* pdata, int datalen, int showlen)
+void ShowBuff(Buffer_t* buff, int len)
{
-#ifdef DEBUG
- uint printlen = datalen > showlen ? showlen : datalen;
- DebugPrintf("%s [%d] = ", name, datalen);
+ ShowArray("",buff->Data,buff->Len,len);
+}
- if (printlen > 32)
- {
- for (int i = 0; i < printlen; i++)
- {
- if (i % 32 == 0)DebugPrintf("\r\n");
- DebugPrintf("%02X ", pdata[i]);
- }
- }
- else
- {
- for (int i = 0; i < printlen; i++)DebugPrintf("%02X ", pdata[i]);
- }
+void ShowBuff2(Buffer2_t* buff, int len)
+{
+ ShowArray("", buff->Data, buff->Len, len);
+}
- DebugPrintf(printlen == datalen ? "\r\n" : "...\r\n");
-#endif
+
+int BuffIndexOf(Buffer_t* src, Buffer_t* sub)
+{
+ if (src == NULL)return -1;
+ if (sub == NULL)return -1;
+
+ return ArrayIndexOf(src->Data, src->Len, sub->Data, sub->Len);
}
-void ShowBuff(Buffer_t* buff, int len)
+int Buff2IndexOf(Buffer2_t* src, Buffer2_t* sub)
{
- ShowArray("",buff->Data,buff->Len,len);
+ if (src == NULL)return -1;
+ if (sub == NULL)return -1;
+
+ return ArrayIndexOf(src->Data, src->Len, sub->Data, sub->Len);
}
-void ShowBuff2(Buffer2_t* buff, int len)
+int BuffLastIndexOf(Buffer_t* src, Buffer_t* sub)
{
- ShowArray("", buff->Data, buff->Len, len);
+ if (src == NULL)return -1;
+ if (sub == NULL)return -1;
+
+ return ArrayLastIndexOf(src->Data, src->Len, sub->Data, sub->Len);
+}
+
+int Buff2LastIndexOf(Buffer2_t* src, Buffer2_t* sub)
+{
+ if (src == NULL)return -1;
+ if (sub == NULL)return -1;
+
+ return ArrayLastIndexOf(src->Data, src->Len, sub->Data, sub->Len);
+}
+
+void BuffReverse(Buffer_t* buff)
+{
+ if (buff == NULL)return;
+
+ ArrayReverse(buff->Data, buff->Len);
+}
+
+void Buff2Reverse(Buffer2_t* buff)
+{
+ if (buff == NULL)return;
+
+ ArrayReverse(buff->Data, buff->Len);
}
diff --git a/Core/Buffer.h b/Core/Buffer.h
index 9d1c111..96b3535 100644
--- a/Core/Buffer.h
+++ b/Core/Buffer.h
@@ -35,3 +35,12 @@ void TryCleanBuffer2(Buffer2_t* data);
void ShowBuff(Buffer_t* buff, int len);
void ShowBuff2(Buffer2_t* buff, int len);
+int BuffIndexOf(Buffer_t* src,Buffer_t* sub);
+int Buff2IndexOf(Buffer2_t* src, Buffer2_t* sub);
+
+int BuffLastIndexOf(Buffer_t* src, Buffer_t* sub);
+int Buff2LastIndexOf(Buffer2_t* src, Buffer2_t* sub);
+
+void BuffReverse(Buffer_t* buff);
+void Buff2Reverse(Buffer2_t* buff);
+
diff --git a/Core/Debug.c b/Core/Debug.c
index e5335ca..58d3665 100644
--- a/Core/Debug.c
+++ b/Core/Debug.c
@@ -1,5 +1,6 @@
#include "Debug.h"
// #include "Uart.h"
+#include "Array.h"
#ifdef DEBUG
diff --git a/Core/EConfigBase.c b/Core/EConfigBase.c
new file mode 100644
index 0000000..21cf99c
--- /dev/null
+++ b/Core/EConfigBase.c
@@ -0,0 +1,257 @@
+
+#include "EConfigBase.h"
+#include "SysInfo.h"
+#include "Array.h"
+
+// #define DebugPrintf( ... ) printf(__VA_ARGS__)
+#include "Debug.h"
+
+/** @brief 注意 8 字节对齐。 */
+typedef struct
+{
+ EConfig_t ECfg;
+ ushort Sun;
+ ushort Idx;
+}ECfgItem_t;
+
+int ECfgItemSize(void){ return sizeof(ECfgItem_t); }
+
+/**
+ * @brief 计算EEPROM最大存储份数。 用于计算 CfgCap
+ *
+ * @param eepromCap EEPROM 容量
+ * @return int CfgCap
+ */
+int ECfgMaxCap(int eepromCap)
+{
+ for (int i = 1; i < 31; i++)
+ {
+ int temp = (1 << (i + 1)) ;
+ if(temp * sizeof(ECfgItem_t) > eepromCap)return (1 << i);
+ }
+
+ return 1 << 31;
+}
+
+/**
+ * @brief 打印内容
+ * @param ecf 配置项指针
+ */
+static void ECfItemShow(ECfgItem_t* ecf){ DebugPrintf("IDX %05d CRC %04X\r\n", ecf->Idx, ecf->Sun); }
+
+/**
+ * @brief 修正数据,计算校验什么的
+ * @param ecf 配置项指针
+ * @param idx 索引
+ */
+static void ECfgItemFix(ECfgItem_t* ecf, int idx)
+{
+ // 计算sun
+ byte* p = (byte*)&ecf->ECfg;
+ ushort sun = 0;
+ for (int i = 0; i < sizeof(EConfig_t); i++) sun += p[i];
+
+ ecf->Sun = sun;
+ ecf->Idx = idx;
+}
+
+/**
+ * @brief 判断数据是否OK
+ * @param ecf 配置项指针
+ * @return true 有效,false 无效
+ */
+static bool ECfgItemIsOK(ECfgItem_t* ecf)
+{
+ // 计算sun
+ byte* p = (byte*)&ecf->ECfg;
+ ushort sun = 0;
+ for (int i = 0; i < sizeof(EConfig_t); i++) sun += p[i];
+
+ if(ecf->Sun != sun)return false;
+ return true;
+}
+
+/**
+ * @brief 初始化
+ * @param ec 配置管理控制块
+ * @param eapi EEPROM 读写 API
+ * @param cap EEPROM 里面存 配置的份数,2的幂次,不小于2
+ */
+void ECfgInit(ECfgCtrl_t* ec, EEPROM_t* eapi, int cap)
+{
+ if(ec == NULL)return;
+ if(eapi == NULL)return;
+
+ // 检查 cap 参数
+ bool ok = false;
+ for (int i = 1; i < 32; i++)
+ {
+ int cc = 1 << i;
+ if(cc == cap)
+ {
+ ok = true;
+ break;
+ }
+ }
+ if(!ok)
+ {
+ ErrPrintf("cap not allow\r\n");
+ return;
+ }
+
+ memcpy(&ec->EApi,eapi,sizeof(EEPROM_t));
+ ec->BaseAddr = 0;
+ ec->CfgCap = cap;
+}
+
+/**
+ * @brief 扫描整个存储区,找到最新且有效的配置取出来
+ * @param ec 配置管理控制块
+ * @param ecf 输出的配置指针
+ * @return true 加载成功,false 失败或没有有效配置
+ */
+bool ECfgLoad(ECfgCtrl_t* ec, EConfig_t* ecf)
+{
+ if(ec == NULL)return false;
+ if(ecf == NULL)return false;
+ if(ec->EApi.ERead == NULL)return false;
+
+ // DebugPrintf("%s:\r\n",__func__);
+
+ // EEPROM 当前数据
+ ECfgItem_t temp;
+ int len = sizeof(ECfgItem_t);
+
+ int max = -1;
+ int limitMax = -1;
+ for (int i = 0; i < ec->CfgCap; i++)
+ {
+ // 读
+ uint addr = sizeof(ECfgItem_t) * i + ec->BaseAddr;
+ ec->EApi.ERead(addr, (byte*)&temp, sizeof(temp));
+
+ bool ok = ECfgItemIsOK(&temp);
+ if((temp.Idx & (ec->CfgCap - 1)) != i) ok = false;
+
+ // DebugPrintf("Addr %08X %s",addr,ok ? "O ":"N ");
+ // ShowArray("READ", (byte*)&temp, len,len);
+
+ // 筛选有效的。
+ if(!ok)continue;
+
+ // 最大的
+ if(temp.Idx > max)max = temp.Idx;
+ // 小于16里面的最大。
+ if(temp.Idx < ec->CfgCap)
+ {
+ if(temp.Idx > limitMax)limitMax = temp.Idx;
+ }
+ }
+
+ // 没有有效的配置,直接从0开始。
+ if(max == -1)
+ {
+ ec->CfgIndex = 0;
+ memset(&ec->CurrECfg,0,sizeof(EConfig_t));
+ return false;
+ }
+
+ // 判定最后写入是哪个
+ int last = -1;
+ if((max > 65535 - ec->CfgCap) && (limitMax != -1))
+ {
+ // 刚好跨界了。大的被丢弃。使用小的里面最大的。
+ last = limitMax;
+ }
+ else
+ {
+ last = max;
+ }
+
+ {
+ // 读回来
+ uint addr = sizeof(ECfgItem_t) * (last & (ec->CfgCap - 1)) + ec->BaseAddr;
+ ec->EApi.ERead(addr, (byte*)&temp, sizeof(ECfgItem_t));
+ }
+
+ memcpy(&ec->CurrECfg, &temp.ECfg, sizeof(EConfig_t));
+ memcpy(ecf, &temp.ECfg, sizeof(EConfig_t));
+ ec->CfgIndex = temp.Idx;
+
+ DebugPrintf("%s ",__func__);
+ ECfItemShow(&temp);
+
+ return true;
+}
+
+/*
+// @brief 清理具体某个配置
+// @param ec 配置管理控制块
+// @param idx 索引
+// @return true 成功,false 失败
+static bool ECfgClean(ECfgCtrl_t* ec, int idx)
+{
+ uint16_t addr = sizeof(ECfgItem_t) * (idx & (ec->CfgCap - 1));
+ addr += offsetof2(ECfgItem_t,Sun);
+ addr += ec->BaseAddr;
+ ushort data = 0xffff;
+ return ec->EApi.EWrite(addr, (byte*)&data, sizeof(ushort));
+}
+*/
+
+/**
+ * @brief 保存
+ * @param ec 配置管理控制块
+ * @param ecf 待保存的配置指针
+ * @return true 保存成功,false 保存失败
+ */
+bool ECfgSave(ECfgCtrl_t* ec, EConfig_t* ecf)
+{
+ if(ec == NULL)return false;
+ if(ec->EApi.EWrite == NULL)return false;
+ if(ecf == NULL)return false;
+
+ // 相同,不存。
+ EConfig_t* curr = &ec->CurrECfg;
+ if(ArrayEqual((byte*)ecf, (byte*)curr,sizeof(EConfig_t)))return true;
+
+ // 下一个位置
+ ushort idx = ec->CfgIndex + 1;
+
+ // 实际写入
+ ECfgItem_t item;
+ memset(&item,0x00,sizeof(ECfgItem_t));
+ memcpy(&item.ECfg, ecf, sizeof(EConfig_t));
+ ECfgItemFix(&item, idx);
+ uint16_t addr = sizeof(ECfgItem_t) * (idx & (ec->CfgCap - 1)) + ec->BaseAddr;
+ bool res = ec->EApi.EWrite(addr, (byte*)&item, sizeof(ECfgItem_t));
+
+ // 失败直接返回
+ if(!res)return false;
+
+ // 写入完成,先回读校验,通过才提交内存状态
+ ECfgItem_t temp;
+ memset(&temp,0x00,sizeof(ECfgItem_t));
+ ec->EApi.ERead(addr, (byte*)&temp, sizeof(ECfgItem_t));
+
+ int len = sizeof(ECfgItem_t);
+ DebugPrintf("%s Addr %08X ",__func__ , addr);
+ // ShowArray("Write", (byte*)&item, len,len);
+ ECfItemShow(&temp);
+ if(!ArrayEqual((byte*)&temp, (byte*)&item,sizeof(ECfgItem_t)))
+ {
+ DebugPrintf(" Error\r\n");
+ ShowArray("Write", (byte*)&item, len,len);
+ ShowArray("Read ", (byte*)&temp, len,len);
+
+ return false;
+ }
+
+ DebugPrintf("\r\n");
+
+ // 校验通过,内存留一份
+ ec->CfgIndex = idx;
+ memcpy(&ec->CurrECfg,ecf,sizeof(EConfig_t));
+ return true;
+}
+
diff --git a/Core/EConfigBase.h b/Core/EConfigBase.h
new file mode 100644
index 0000000..eaf733c
--- /dev/null
+++ b/Core/EConfigBase.h
@@ -0,0 +1,150 @@
+#pragma once
+
+#include "Type.h"
+#include "EConfig.h"
+/*
+基于 EEPROM 的配置信息。
+特点:高频写入,不怕写入时断电。
+EPROM 保留了很多份有效配置,因为断电造成写入失败,上电会取用上一份有效内容
+循环写入,寿命达到 ItemCapNum X 100w 次。
+*/
+
+// EEPROM 里面存的份数,只能是 2,4,8,16,32
+// sizeof(ECfgItem_t) * ItemCapNum 不能大于 EEPROM 容量
+
+/** @brief EEPROM 操作函数 */
+typedef struct
+{
+ /** @brief FLASH 写 */
+ bool (*EWrite)(uint address, byte* buf, int len);
+ /** @brief 可以直接通过指针访问的 addr ,此函数为 NULL。 */
+ bool (*ERead)(uint address, byte* buf, int len);
+}EEPROM_t;
+
+/** @brief 配置管理 */
+typedef struct
+{
+ /** @brief EEPROM 内当前最新配置,用于对比是否需要写入。 */
+ EConfig_t CurrECfg;
+ /** @brief EEPROM 读写接口 */
+ EEPROM_t EApi;
+ /** @brief 储存区储存份数。只支持 2 的幂次。 */
+ ushort CfgCap;
+ /** @brief 当前使用的 IDEX 。 存放位置就是 %ItemCapNum */
+ ushort CfgIndex;
+ /** @brief EPROM 基地址 */
+ ushort BaseAddr;
+}ECfgCtrl_t;
+
+/**
+ * @brief 初始化
+ * @param ec 配置管理控制块
+ * @param eapi EEPROM 读写 API
+ * @param cap EEPROM 里面存 配置的份数,2的幂次,不小于2
+ */
+void ECfgInit(ECfgCtrl_t* ec, EEPROM_t* eapi, int cap);
+
+/**
+ * @brief 计算EEPROM最大存储份数。 用于计算 CfgCap
+ * @param eepromCap EEPROM 容量
+ * @return int CfgCap
+ */
+int ECfgMaxCap(int eepromCap);
+
+/**
+ * @brief 一份储存的FLASH容量
+ * @return int
+ */
+int ECfgItemSize(void);
+
+/**
+ * @brief 扫描整个存储区,找到最新且有效的配置取出来
+ * @param ec 配置管理控制块
+ * @param ecf 输出的配置指针
+ * @return true 加载成功,false 失败或没有有效配置
+ */
+bool ECfgLoad(ECfgCtrl_t* ec, EConfig_t* ecf);
+
+/**
+ * @brief 保存
+ * @param ec 配置管理控制块
+ * @param ecf 待保存的配置指针
+ * @return true 保存成功,false 保存失败
+ */
+bool ECfgSave(ECfgCtrl_t* ec, EConfig_t* ecf);
+
+/**
+ * @brief 测试
+ */
+void ECfgTest(void);
+
+
+/*
+
+#include "AT24CXX.h"
+
+static AT24Cxx_t AT24C02;
+static bool EpWrite(uint address, byte* buf, int len)
+{
+ AT24Cxx_t* dev = &AT24C02;
+ return AT24CXXWrite(dev,address,buf,len);
+}
+
+static bool EpRead(uint address, byte* buf, int len)
+{
+ AT24Cxx_t* dev = &AT24C02;
+ return AT24CXXRead(dev,address,buf,len);
+}
+
+
+typedef struct
+{
+ // 演示数据
+ uint Temp[3];
+}EConfig_t;
+
+/// @brief 测试
+/// @param
+void ECfgTest(void)
+{
+ static I2C_Info_t i2c;
+ i2c.SCK = PA4;
+ i2c.SDA = PA3;
+ i2c.TimeDelayUs = 1;
+
+ AT24Cxx_t* dev = &AT24C02;
+ AT24CXX_Init(dev,0,2,&i2c);
+
+ ECfgCtrl_t ECtrl;
+ EEPROM_t eapi = { .EWrite = EpWrite, .ERead = EpRead, };
+ ECfgInit(&ECtrl,&eapi,ECfgMaxCap(dev->Capacity));
+
+ EConfig_t cfg;
+ ECfgCtrl_t* ec = &ECtrl;
+ ECfgLoad(ec, &cfg);
+ for (int i = 0; i < ec->CfgCap; i++)
+ {
+ ECfgClean(ec,i);
+ }
+ ECfgLoad(ec, &cfg);
+
+ EConfig_t ecf;
+ memset(&ecf,0,sizeof(EConfig_t));
+ ecf.Temp[0] = 20;
+ ECfgSave(ec, &ecf);
+
+ for (int i = 0; i < 20; i++)
+ {
+ DebugPrintf("FOR %d\r\n",i);
+
+ ecf.Temp[0]++;
+ ECfgSave(ec, &ecf);
+
+ ECfgLoad(ec, &cfg);
+ // ECfgLoad(ec,&ecf);
+ // ECfItemShow(&ecf);
+ }
+}
+
+*/
+
diff --git a/Core/FConfigBase.c b/Core/FConfigBase.c
new file mode 100644
index 0000000..df8fda2
--- /dev/null
+++ b/Core/FConfigBase.c
@@ -0,0 +1,414 @@
+
+#include "FConfigBase.h"
+#include "Flash.h"
+#include "Crc.h"
+#include "Debug.h"
+#include "Crc.h"
+#include "Array.h"
+
+
+// Config 有效性 取决于 MagicNum SaveIdx Crc32
+typedef struct
+{
+ // 配置实际内容。
+ FConfig_t Config;
+
+ // 魔数 内部使用。 外部不要碰
+ // 默认为 : 'M' 'a' 'g' '&' 0x4D616726
+ uint MagicNum;
+ // 保存号,也是位置号。内部使用。外部不要碰 0-65535
+ ushort Index;
+ // 校验 内部使用。 外部不要碰
+ ushort Crc16;
+}FConfigItem_t;
+
+// 只存一份简化版
+#if 1
+
+static void FixFCfg(FConfigItem_t* cfg, ushort idx);
+static bool FCfgIsOK(FConfigItem_t* cfg);
+
+
+/** @brief 加载配置,只存一份简化版 */
+bool LoadFConfig(FConfig_t* cfg, NorFlash_t* fapi, uint addr)
+{
+ if(cfg == NULL)return false;
+ if(fapi == NULL)return false;
+
+ FConfigItem_t temp;
+ FConfigItem_t* fp = &temp;
+ if(fapi->FlashRead != NULL)
+ {
+ // 读取 配置项
+ memset(fp, 0x00, sizeof(FConfigItem_t));
+ if(!fapi->FlashRead(addr,(byte*)fp,sizeof(FConfigItem_t)))return false;
+ }
+ else
+ {
+ // 直接指针过去
+ fp = (FConfigItem_t*)addr;
+ }
+
+ // 配置有效性检查
+ if(!FCfgIsOK(fp))return false;
+
+ memcpy(cfg,&fp->Config,sizeof(FConfig_t));
+ return true;
+}
+
+/** @brief 保存配置,只存一份简化版 */
+bool SaveFConfig(FConfig_t* cfg, NorFlash_t* fapi, uint addr)
+{
+ if(cfg == NULL)return false;
+ if(fapi == NULL)return false;
+
+ FConfigItem_t item;
+ if(LoadFConfig(&item.Config,fapi,addr))
+ {
+ if(ArrayEqual((byte*)cfg,(byte*)&item.Config,sizeof(FConfig_t)))return true;
+ }
+
+ memcpy(&item.Config,cfg,sizeof(FConfig_t));
+ FixFCfg(&item,0xffff);
+
+ // 擦
+ if(fapi->FlashErase == NULL)return false;
+ if(!fapi->FlashErase(addr))return false;
+ // 写
+ if(fapi->FlashWrite == NULL)return false;
+ return fapi->FlashWrite(addr , (byte*)&item, sizeof(FConfigItem_t));
+}
+
+/** @brief 擦除配置,只存一份简化版 */
+bool CleanFConfig(NorFlash_t* fapi, uint addr)
+{
+ if(fapi == NULL)return false;
+ if(fapi->FlashErase == NULL)return false;
+
+ if(!fapi->FlashErase(addr))return false;
+ return true;
+}
+
+#endif
+
+
+/**
+ * @brief 修正内部数据
+ * @param cfg 配置
+ * @param idx 必要条件
+ */
+static void FixFCfg(FConfigItem_t* cfg, ushort idx)
+{
+ cfg->Index = idx;
+ cfg->MagicNum = 0x4D616726;
+ byte* p = (byte*)cfg;
+ int len = offsetof2(FConfigItem_t,Crc16);
+ cfg->Crc16 = CaclcCRC16_MODBUS(p, len);
+ // cfg->Crc16 = CaclcCRC16_MODBUS(p,sizeof(FConfigItem_t) - 2);
+}
+
+/**
+ * @brief 检查配置是否有效
+ * @param cfg
+ */
+static bool FCfgIsOK(FConfigItem_t* cfg)
+{
+ if(cfg == NULL)return false;
+ if(cfg->MagicNum != 0x4D616726)return false;
+
+ byte* p = (byte*)cfg;
+ int len = offsetof2(FConfigItem_t,Crc16);
+ ushort cl = CaclcCRC16_MODBUS(p, len);
+ // ushort cl = CaclcCRC16_MODBUS(p,sizeof(FConfigItem_t) - 2);
+ if(cl != cfg->Crc16)return false;
+
+ return true;
+}
+
+/**
+ * @brief 所在块的块地址
+ * @param ctl
+ * @param idx
+ */
+static uint FCfgSecAddr(FCfgCtrl_t* ctl,int idx)
+{
+ // 块个数
+ int sCnt = 1 << ctl->FSecBit;
+ // 所在块
+ int sec = idx & (sCnt - 1);
+ // 块地址
+ uint addr = ctl->FSecAddrs[sec];
+ return addr;
+}
+
+// #define FSECADDR(ctl,idx) (ctl->FSecAddrs[(idx & ((1 << ctl->FSecBit) - 1)))])
+// FSECADDR FCfgSecAddr
+
+/**
+ * @brief 块内偏移量
+ * @param ctl
+ * @param idx
+ */
+static uint FSecOffset(FCfgCtrl_t* ctl,int idx)
+{
+ // 块内编号
+ idx >>= ctl->FSecBit;
+ // 块内个数
+ int cCnt = 1 << ctl->FSecCfgBit;
+ // 取余
+ int sidx = idx & (cCnt - 1);
+ // 块内偏移
+ uint offset = sizeof(FConfigItem_t) * sidx;
+ return offset;
+}
+
+// #define FSECOFFSET(ctl,idx) (sizeof(FConfigItem_t) * ((idx >> ctl->FSecBit) & (1 << ctl->FSecCfgBit) - 1))))
+// #define FSECOFFSET FSecOffset
+
+/**
+ * @brief 初始化
+ * @param ctl
+ * @param api NOR FLASH 接口
+ * @param sad FLASH 块地址数组
+ * @param sadlen 块数组长度
+ * @param ccnt 块内 存放配置的份数
+ * @return 0 无错
+ */
+int FCfgInit(FCfgCtrl_t* ctl, NorFlash_t* api, uint* sad, byte sadlen, byte ccnt)
+{
+ int err = 0;
+ byte temp = 0;
+ if(ctl == NULL){ err = __LINE__; goto reserr; }
+ if(api == NULL){ err = __LINE__; goto reserr; }
+ if(sad == NULL){ err = __LINE__; goto reserr; }
+ if(sadlen < 2) { err = __LINE__; goto reserr; }
+ if(ccnt < 1) { err = __LINE__; goto reserr; }
+
+ memcpy(&ctl->FApi,api,sizeof(NorFlash_t));
+ ctl->FSecAddrs = sad;
+
+ // 计算位数
+ temp = 0;
+ for (int i = 1; ; i++)
+ {
+ int cnt = 1 << i;
+ if(sadlen >= cnt)temp = i;
+ else break;
+ }
+ ctl->FSecBit = temp;
+
+ temp = 0;
+ for (int i = 0; ; i++)
+ {
+ int cnt = 1 << i;
+ if(ccnt >= cnt)temp = i;
+ else break;
+ }
+ ctl->FSecCfgBit = temp;
+ return 0;
+
+ reserr:
+ DebugPrintf("%s [%d] ERROR\r\n",__func__,__LINE__);
+ return err;
+}
+
+/**
+ * @brief 扫描整个 FLASH 存储区,找到最新且有效的配置取出来
+ * @param ctl
+ * @param cfg
+ */
+bool FCfgLoad(FCfgCtrl_t* ctl, FConfig_t* cfg)
+{
+ if(ctl == NULL)return false;
+ if(ctl->FSecAddrs == NULL)return false;
+ if(ctl->FSecBit < 1)return false;
+ if(cfg == NULL)return false;
+
+ // 最大同时存在配置个数
+ int limit = (1 << ctl->FSecBit) * (1 << ctl->FSecCfgBit);
+ // 已存在最大IDX
+ int max = -1;
+ // 小于limit的,已存在最大IDX
+ int limitMax = -1;
+
+ FConfigItem_t temp;
+ FConfigItem_t* fp = &temp;
+ // 块个数
+ int slen = 1 << ctl->FSecBit;
+ // 块内配置个数
+ int sCfgCnt = 1 << ctl->FSecCfgBit;
+
+ for (int i = 0; i < slen; i++)
+ {
+ // 块首地址
+ uint base = ctl->FSecAddrs[i];
+ for (int j = 0; j < sCfgCnt; j++)
+ {
+ // 得到地址
+ uint addr = sizeof(FConfigItem_t) * j + base;
+
+ if(ctl->FApi.FlashRead != NULL)
+ {
+ // 读取 配置项
+ memset(fp, 0x00, sizeof(FConfigItem_t));
+ if(!ctl->FApi.FlashRead(addr,(byte*)fp,sizeof(FConfigItem_t)))continue;
+ }
+ else
+ {
+ // 直接指针过去
+ fp = (FConfigItem_t*)addr;
+ }
+
+ // 配置有效性检查
+ if(!FCfgIsOK(fp))continue;
+
+ // 选取最后存入的 idx
+ ushort idx = fp->Index;
+ if(idx > max)max = idx;
+ if(idx < limit)
+ {
+ if(idx > limitMax)limitMax = idx;
+ }
+ }
+ }
+
+ // 没有任何有效配置
+ if(max == -1)
+ {
+ ctl->CurrIndex = 0;
+ memset(&ctl->CurrCfg,0x00,sizeof(FConfigItem_t));
+ return false;
+ }
+
+ // 判定最后写入是哪个
+ int last = -1;
+ if((max > 65535 - limit) && (limitMax != -1))
+ {
+ // 刚好跨界了。大的被丢弃。使用小的里面最大的。
+ last = limitMax;
+ }
+ else
+ {
+ last = max;
+ }
+
+ // 块地址 + 块内偏移
+ uint addr = FCfgSecAddr(ctl,last) + FSecOffset(ctl,last);
+ if(ctl->FApi.FlashRead != NULL)
+ {
+ // 读取 配置项
+ memset(fp, 0x00, sizeof(FConfigItem_t));
+ ctl->FApi.FlashRead(addr,(byte*)fp,sizeof(FConfigItem_t));
+ }
+ else
+ {
+ // 直接指针过去
+ fp = (FConfigItem_t*)addr;
+ }
+
+ memcpy(&ctl->CurrCfg,&fp->Config,sizeof(FConfig_t));
+ ctl->CurrIndex = fp->Index;
+
+ memcpy(cfg,&ctl->CurrCfg,sizeof(FConfig_t));
+ return true;
+}
+
+/**
+ * @brief 写配置进去,地址为块首地址的时候擦除块。
+ * @param ctl
+ * @param cfg
+ */
+static bool FCfgWrite(FCfgCtrl_t* ctl, FConfigItem_t* cfg)
+{
+ if(ctl->FApi.FlashWrite == NULL)return false;
+
+ int idx = cfg->Index;
+ // 块地址
+ uint addr = FCfgSecAddr(ctl,idx);
+ // 块内偏移
+ uint offset = FSecOffset(ctl,idx);
+
+ if(offset == 0)
+ {
+ if(ctl->FApi.FlashErase == NULL)return false;
+ if(!ctl->FApi.FlashErase(addr))return false;
+ }
+
+ // 写
+ return ctl->FApi.FlashWrite(addr + offset, (byte*)cfg, sizeof(FConfigItem_t));
+}
+
+/**
+ * @brief 保存配置
+ * @param ctl
+ * @param cfg
+ */
+bool FCfgSave(FCfgCtrl_t* ctl, FConfig_t* cfg)
+{
+ if(ctl == NULL)return false;
+ if(ctl->FSecAddrs == NULL)return false;
+ if(ctl->FSecBit < 1)return false;
+ if(cfg == NULL)return false;
+
+ // 相同,不存。
+ FConfig_t* curr = &ctl->CurrCfg;
+ if(!ArrayEqual((byte*)cfg, (byte*)curr,sizeof(FConfig_t)))return true;
+
+ // 下一个位置保存
+ ushort idx = ctl->CurrIndex + 1;
+ FConfigItem_t item;
+ memcpy(&item.Config,cfg,sizeof(FConfig_t));
+ FixFCfg(&item,idx);
+ bool res = FCfgWrite(ctl, &item);
+ // 保存完成,更新本地信息
+ if(res)
+ {
+ memcpy(&ctl->CurrCfg,cfg,sizeof(FConfig_t));
+ ctl->CurrIndex = item.Index;
+ }
+
+ return res;
+}
+
+/**
+ * @brief 删除一个配置
+ * @param ctl
+ * @param idx
+ */
+bool FCfgClean(FCfgCtrl_t* ctl, int idx)
+{
+ if(ctl->FApi.FlashWrite == NULL)return false;
+
+ // 块地址
+ uint addr = FCfgSecAddr(ctl,idx);
+ // 块内偏移
+ uint offset = FSecOffset(ctl,idx);
+
+ // 配置内 magic 位置
+ offset += offsetof2(FConfigItem_t,MagicNum);
+
+ // FLASH 擦完是 ff 只能往 00 写
+ uint magic = 0;
+ return ctl->FApi.FlashWrite(addr+offset, (byte*)&magic, sizeof(magic));
+}
+
+/**
+ * @brief 擦除所有 FLASH 区域,一个不留
+ * @param ctl
+ */
+bool FCfgCleanAll(FCfgCtrl_t* ctl)
+{
+ if(ctl == NULL)return false;
+ if(ctl->FSecAddrs == NULL)return false;
+ if(ctl->FSecBit < 1)return false;
+ if(ctl->FApi.FlashErase == NULL)return false;
+
+ int slen = 1 << ctl->FSecBit;
+ for (int i = 0; i < slen; i++)
+ {
+ ctl->FApi.FlashErase(ctl->FSecAddrs[i]);
+ }
+ ctl->CurrIndex = 0;
+
+ return true;
+}
diff --git a/Core/FConfigBase.h b/Core/FConfigBase.h
new file mode 100644
index 0000000..22a12ee
--- /dev/null
+++ b/Core/FConfigBase.h
@@ -0,0 +1,131 @@
+#pragma once
+
+#include "Type.h"
+// 需要在自己项目内补充。 ConfigBase_t 只提供读写保存。
+#include "FConfig.h"
+
+/*
+写很多份配置的驱动。
+NOR FLASH 作为存储载体。
+多擦除块设计。
+处理写入时断电问题。
+*/
+
+// 块:擦除块
+typedef struct
+{
+ // FLASH 写
+ bool (*FlashWrite)(uint address, byte* buf, int len);
+ // 可以直接通过指针访问的 addr ,此函数为 NULL。
+ bool (*FlashRead)(uint address, byte* buf, int len);
+ // FLASH 擦块
+ bool (*FlashErase)(uint address);
+}NorFlash_t;
+
+// 只存一份简化版
+#if 1
+
+/** @brief 加载配置,只存一份简化版 */
+bool LoadFConfig(FConfig_t* cfg, NorFlash_t* fapi, uint addr);
+/** @brief 保存配置,只存一份简化版 */
+bool SaveFConfig(FConfig_t* cfg, NorFlash_t* fapi, uint addr);
+/** @brief 擦除配置,只存一份简化版 */
+bool CleanFConfig(NorFlash_t* fapi, uint addr);
+
+#endif
+
+// 完整多份存储版本,频繁写入,处理FLASH磨损问题
+#if 1
+
+// 对象
+typedef struct
+{
+ // 内当前最新配置,用于对比是否需要写入。
+ FConfig_t CurrCfg;
+ // NOR FLASH 接口
+ NorFlash_t FApi;
+ // FLASH Section 地址数组,数组长度是 FlashSecCnt
+ uint* FSecAddrs;
+ // FLASH Section 块个数。 个数 FlashSecCnt = (1 << FSecBit)
+ byte FSecBit;
+ // FLASH Section 存 Config_t 的份数 = (1 << FSecCfgBit)
+ byte FSecCfgBit;
+
+ // 当前使用的 IDEX 。也是存放位置。
+ ushort CurrIndex;
+}FCfgCtrl_t;
+
+/* 用法
+
+// FLASH 擦除块地址
+const uint secAddr[] =
+{
+ 0 + 0x8000000 + (48 * 1024),
+ 1024 + 0x8000000 + (48 * 1024),
+ 2048 + 0x8000000 + (48 * 1024),
+ 4096 + 0x8000000 + (48 * 1024)
+};
+
+// 单片机内部 NOR FLASH,读不用函数直接用指针,外部FLASH需要写函数
+NorFlash_t nfunc =
+{
+ .FlashWrite = XXX
+ .FlashRead = NULL,
+ .FlashErase = XXX,
+};
+
+// 直接定义
+FCfgCtrl_t FCtl =
+{
+ .FlashWrite = xxx,
+ .FlashErase = xxx,
+ .BaseAddrs = secAddr,
+ .FSecCnt = 2, // (1<<2) 有4块
+ .FSecCfgCnt = 3, // (1<<3) 存8份
+ .CfgIndex = 0,
+};
+
+或者 使用 FCfgInit 函数配置
+FCfgInit(&FCtl, &nfunc, secAddr, ArrayLength(secAddr), 8);
+
+*/
+
+/**
+ * @brief 初始化
+ * @param ctl
+ * @param api NOR FLASH 接口
+ * @param sad FLASH 块地址数组
+ * @param sadlen 块数组长度
+ * @param ccnt 块内 存放配置的份数
+ * @return 0 无错
+ */
+int FCfgInit(FCfgCtrl_t* ctl, NorFlash_t* api, uint* sad, byte sadlen, byte ccnt);
+
+/**
+ * @brief 扫描整个 FLASH 存储区,找到最新且有效的配置取出来
+ * @param ctl
+ * @param cfg
+ */
+bool FCfgLoad(FCfgCtrl_t* ctl, FConfig_t* cfg);
+
+/**
+ * @brief 保存配置
+ * @param ctl
+ * @param cfg
+ */
+bool FCfgSave(FCfgCtrl_t* ctl, FConfig_t* cfg);
+
+/**
+ * @brief 删除一个配置,应该用不到
+ * @param ctl
+ * @param idx
+ */
+bool FCfgClean(FCfgCtrl_t* ctl, int idx);
+
+/**
+ * @brief 擦除所有 FLASH 区域,一个不留
+ * @param ctl
+ */
+bool FCfgCleanAll(FCfgCtrl_t* ctl);
+
+#endif
diff --git a/Core/GitInfo.bat b/Core/GitInfo.bat
index 1e61317..2e28c88 100644
--- a/Core/GitInfo.bat
+++ b/Core/GitInfo.bat
@@ -5,19 +5,23 @@
@echo off
+
+:: Get the directory where this batch file resides (with trailing backslash)
+set "OUT=%~dp0"
+
where git >nul 2>&1
if %errorlevel%==0 (
- echo #define GITINFO \>GitInfo.h
- :git log -1 --pretty=format:"\"%%ci \r\n%%H \r\n%%d \r\n%%an\"" >> GitInfo.h
- git log -1 --pretty=format:"\"%%ci \r\n%%H \r\n%%d\"" >> GitInfo.h
+ echo #define GITINFO \>%OUT%GitInfo.h
+ :git log -1 --pretty=format:"\"%%ci \r\n%%H \r\n%%d \r\n%%an\"" >> %OUT%GitInfo.h
+ git log -1 --pretty=format:"\"%%ci \r\n%%H \r\n%%d\"" >> %OUT%GitInfo.h
) else (
- echo #define GITINFO \>GitInfo.h
- echo "2000-01-01 00:00:00 +0800 \r\n0000000000000000000000000000000000000000 \r\n (HEAD -> NO_GIT, origin/NO_GIT)" >>GitInfo.h
+ echo #define GITINFO \>%OUT%GitInfo.h
+ echo "2000-01-01 00:00:00 +0800 \r\n0000000000000000000000000000000000000000 \r\n (HEAD -> NO_GIT, origin/NO_GIT)" >>%OUT%GitInfo.h
)
if not %errorlevel%==0 (
- echo #define GITINFO \>GitInfo.h
- echo "2000-01-01 00:00:00 +0800 \r\n0000000000000000000000000000000000000000 \r\n (HEAD -> NO_GIT, origin/NO_GIT)" >>GitInfo.h
+ echo #define GITINFO \>%OUT%GitInfo.h
+ echo "2000-01-01 00:00:00 +0800 \r\n0000000000000000000000000000000000000000 \r\n (HEAD -> NO_GIT, origin/NO_GIT)" >>%OUT%GitInfo.h
)
-echo.>>GitInfo.h
+echo.>>%OUT%GitInfo.h
diff --git a/Core/TimeHelper.c b/Core/TimeHelper.c
index c5173cb..5978a3d 100644
--- a/Core/TimeHelper.c
+++ b/Core/TimeHelper.c
@@ -33,13 +33,33 @@ time_t GeTimeStampBase(void)
return mktime(&time);
}
+/*
+ * 把 time_t 时间戳转换为 struct tm。
+ * ARMCC(AC5) 和 ARMCLANG(AC6) 的 C 库不提供 POSIX 的 localtime_r(),
+ * 使用标准 C 的 localtime()。GNU/GCC 环境则使用线程安全的 localtime_r()。
+ */
+static void TimeToTm(time_t *tsec, struct tm *out)
+{
+#if defined(__CC_ARM) || defined(__clang__)
+ /* ARM 工具链 (AC5 / AC6)。 */
+ struct tm *ptm = localtime(tsec);
+ if( ptm != NULL )
+ {
+ *out = *ptm;
+ }
+#else
+ /* GNU / GCC 环境。 */
+ localtime_r(tsec, out);
+#endif
+}
+
void ShowTimeStamp_Sec(int sec)
{
time_t tsec = GeTimeStampBase();
tsec += sec;
struct tm time = {0};
- localtime_r(&tsec, &time);
+ TimeToTm(&tsec, &time);
print_tm(&time);
}
@@ -50,7 +70,7 @@ void ShowTimeStamp_Min(int min)
tsec += (min * 60);
struct tm time = {0};
- localtime_r(&tsec, &time);
+ TimeToTm(&tsec, &time);
print_tm(&time);
}
diff --git a/Core/Type.h b/Core/Type.h
index 04e8508..0c29ce7 100644
--- a/Core/Type.h
+++ b/Core/Type.h
@@ -42,17 +42,6 @@ extern MemOptions_t GlobleMemOpt;
// 一次设置 GlobleMalloc GlobleFree GlobleMemOpt
void SetGlobleMemOpt(MallocFunc malloc, FreeFunc free);
-/**
- * @brief 打印数组内容
- * @param head 数组名称
- * @param pdata 数组指针
- * @param datalen 数组长度
- * @param showlen 打印长度
- */
-void ShowArray(char* name, byte* pdata, int datalen, int showlen);
-// 数组长度
-#define ArrayLength(arr) ((int)(sizeof(arr)/sizeof(arr[0])))
-
// 大小端转换
#define __REV16x(x) ((ushort)((x >> 8) & 0x00ff) | ((x << 8) & 0xff00))
// 大小端切换