终版
This commit is contained in:
@@ -32,6 +32,7 @@ static AttendanceRecord attendance_records[MAX_ATTENDANCE_RECORDS];
|
||||
static uint16_t record_count = 0;
|
||||
static uint32_t total_attendance_count = 0; // 总考勤人次
|
||||
|
||||
/* 函数作用:根据当前小时和分钟查找所处的考勤时间段。 */
|
||||
static int8_t Attendance_FindTimeSlot(uint8_t hour, uint8_t minute)
|
||||
{
|
||||
uint16_t current_minutes = hour * 60 + minute;
|
||||
@@ -57,6 +58,7 @@ static int8_t Attendance_FindTimeSlot(uint8_t hour, uint8_t minute)
|
||||
/**
|
||||
* @brief 初始化考勤模块
|
||||
*/
|
||||
/* 函数作用:初始化考勤记录、时间段配置和今日已签到缓存。 */
|
||||
void Attendance_Init(void)
|
||||
{
|
||||
record_count = 0;
|
||||
@@ -81,6 +83,7 @@ void Attendance_Init(void)
|
||||
/**
|
||||
* @brief 清空今日签到缓存
|
||||
*/
|
||||
/* 函数作用:清空当前时段的服务器已签到学号缓存。 */
|
||||
void Attendance_ClearCache(void)
|
||||
{
|
||||
cached_id_count = 0;
|
||||
@@ -93,6 +96,7 @@ void Attendance_ClearCache(void)
|
||||
/**
|
||||
* @brief 添加学号到缓存
|
||||
*/
|
||||
/* 函数作用:把指定学号加入当前考勤时段的本地查重缓存。 */
|
||||
void Attendance_AddToCache(const char* student_id)
|
||||
{
|
||||
int8_t current_slot = Attendance_GetCurrentTimeSlot();
|
||||
@@ -132,6 +136,7 @@ void Attendance_AddToCache(const char* student_id)
|
||||
* @brief 在本地缓存中查重
|
||||
* @retval 1=在缓存中找到(重复), 0=未找到
|
||||
*/
|
||||
/* 函数作用:检查指定学号是否已存在于当前时段缓存中。 */
|
||||
uint8_t Attendance_IsDuplicateInCache(const char* student_id)
|
||||
{
|
||||
int8_t current_slot = Attendance_GetCurrentTimeSlot();
|
||||
@@ -158,6 +163,7 @@ uint8_t Attendance_IsDuplicateInCache(const char* student_id)
|
||||
* @brief 从服务器加载今日已签到记录到缓存
|
||||
* @retval 加载的记录数量
|
||||
*/
|
||||
/* 函数作用:下载当前时段已签到学号列表并写入本地查重缓存。 */
|
||||
uint8_t Attendance_LoadTodayRecordsFromServer(void)
|
||||
{
|
||||
char slot_start[8];
|
||||
@@ -221,6 +227,7 @@ uint8_t Attendance_LoadTodayRecordsFromServer(void)
|
||||
* @param slots 新的时间段配置数组
|
||||
* @param count 时间段数量
|
||||
*/
|
||||
/* 函数作用:用服务端下发的新时间段数组替换本地时间段配置。 */
|
||||
void Attendance_UpdateTimeSlots(TimeSlot* slots, uint8_t count)
|
||||
{
|
||||
if (count > MAX_TIME_SLOT_COUNT) {
|
||||
@@ -249,6 +256,7 @@ void Attendance_UpdateTimeSlots(TimeSlot* slots, uint8_t count)
|
||||
* @param json_str JSON字符串(格式:{"time_slots":[{"enabled":true,"start_time":"08:00",...}]})
|
||||
* @retval 成功解析的时间段数量
|
||||
*/
|
||||
/* 函数作用:从服务端同步 JSON 中解析时间段配置并应用到本地。 */
|
||||
uint8_t Attendance_ParseTimeSlotsJSON(const char* json_str)
|
||||
{
|
||||
TimeSlot temp_slots[MAX_TIME_SLOT_COUNT];
|
||||
@@ -386,6 +394,7 @@ uint8_t Attendance_ParseTimeSlotsJSON(const char* json_str)
|
||||
* @param hour 小时 (0-23)
|
||||
* @param minute 分钟 (0-59)
|
||||
*/
|
||||
/* 函数作用:更新考勤模块当前时间,并在跨时段时清空查重缓存。 */
|
||||
void Attendance_SetCurrentTime(uint8_t hour, uint8_t minute)
|
||||
{
|
||||
int8_t old_slot = Attendance_FindTimeSlot(current_hour, current_minute);
|
||||
@@ -405,6 +414,7 @@ void Attendance_SetCurrentTime(uint8_t hour, uint8_t minute)
|
||||
* @brief 获取当前所处的考勤时段
|
||||
* @retval 时段编号 (0-N),如果不在任何时段内则返回 -1
|
||||
*/
|
||||
/* 函数作用:返回当前时间对应的考勤时段索引。 */
|
||||
int8_t Attendance_GetCurrentTimeSlot(void)
|
||||
{
|
||||
return Attendance_FindTimeSlot(current_hour, current_minute);
|
||||
@@ -415,6 +425,7 @@ int8_t Attendance_GetCurrentTimeSlot(void)
|
||||
* @param start_time 输出:开始时间(格式"HH:MM")
|
||||
* @param end_time 输出:结束时间(格式"HH:MM")
|
||||
*/
|
||||
/* 函数作用:输出当前考勤时段的开始和结束时间字符串。 */
|
||||
void Attendance_GetCurrentSlotTime(char* start_time, char* end_time)
|
||||
{
|
||||
int8_t current_slot = Attendance_GetCurrentTimeSlot();
|
||||
@@ -438,6 +449,7 @@ void Attendance_GetCurrentSlotTime(char* start_time, char* end_time)
|
||||
* @param student_id 学生学号
|
||||
* @retval 1=服务器显示已签到(重复), 0=未签到或查询失败
|
||||
*/
|
||||
/* 函数作用:向服务器查询指定学生在当前时段是否已经签到。 */
|
||||
uint8_t Attendance_IsDuplicateOnServer(const char* student_id)
|
||||
{
|
||||
char slot_start[8];
|
||||
@@ -458,6 +470,7 @@ uint8_t Attendance_IsDuplicateOnServer(const char* student_id)
|
||||
* @param student_id 学生学号
|
||||
* @retval 1=重复, 0=不重复
|
||||
*/
|
||||
/* 函数作用:检查指定学生是否已在当前时段产生本地重复签到。 */
|
||||
uint8_t Attendance_IsDuplicate(const char* student_id)
|
||||
{
|
||||
int8_t current_slot = Attendance_GetCurrentTimeSlot();
|
||||
@@ -485,6 +498,7 @@ uint8_t Attendance_IsDuplicate(const char* student_id)
|
||||
* @param result 人脸识别结果
|
||||
* @retval 考勤状态
|
||||
*/
|
||||
/* 函数作用:校验识别结果、查重并生成一条待同步考勤记录。 */
|
||||
AttendanceStatus Attendance_ProcessFaceResult(FaceRecognitionResult* result)
|
||||
{
|
||||
// 1. 检查识别是否成功
|
||||
@@ -598,6 +612,7 @@ AttendanceStatus Attendance_ProcessFaceResult(FaceRecognitionResult* result)
|
||||
/**
|
||||
* @brief 清空所有考勤记录
|
||||
*/
|
||||
/* 函数作用:清空当前缓存的考勤记录,但保留累计统计值。 */
|
||||
void Attendance_ClearRecords(void)
|
||||
{
|
||||
record_count = 0;
|
||||
@@ -608,6 +623,7 @@ void Attendance_ClearRecords(void)
|
||||
/**
|
||||
* @brief 获取累计考勤总数
|
||||
*/
|
||||
/* 函数作用:返回系统启动以来累计处理的成功考勤次数。 */
|
||||
uint16_t Attendance_GetTotalCount(void)
|
||||
{
|
||||
return total_attendance_count;
|
||||
@@ -616,6 +632,7 @@ uint16_t Attendance_GetTotalCount(void)
|
||||
/**
|
||||
* @brief 获取当前缓存中的记录数
|
||||
*/
|
||||
/* 函数作用:返回当前本地缓存中的考勤记录数量。 */
|
||||
uint16_t Attendance_GetActiveCount(void)
|
||||
{
|
||||
return record_count;
|
||||
@@ -625,6 +642,7 @@ uint16_t Attendance_GetActiveCount(void)
|
||||
* @brief 获取待同步记录的数量(PENDING或FAILED状态)
|
||||
* @retval 待同步记录数
|
||||
*/
|
||||
/* 函数作用:统计当前等待上传或需要重试上传的记录数量。 */
|
||||
uint16_t Attendance_GetPendingCount(void)
|
||||
{
|
||||
uint16_t count = 0;
|
||||
@@ -644,6 +662,7 @@ uint16_t Attendance_GetPendingCount(void)
|
||||
* @param index 输出参数,返回记录的索引
|
||||
* @retval 记录指针,如果没有待同步记录则返回NULL
|
||||
*/
|
||||
/* 函数作用:按优先级取出下一条可上传或可重试的考勤记录。 */
|
||||
AttendanceRecord* Attendance_GetNextPendingRecord(uint16_t* index)
|
||||
{
|
||||
uint32_t current_time = Get_Time_ms();
|
||||
@@ -690,6 +709,7 @@ AttendanceRecord* Attendance_GetNextPendingRecord(uint16_t* index)
|
||||
* @param index 记录索引
|
||||
* @param status 新的同步状态
|
||||
*/
|
||||
/* 函数作用:更新指定考勤记录的服务器同步状态。 */
|
||||
void Attendance_MarkSyncStatus(uint16_t index, SyncStatus status)
|
||||
{
|
||||
if (index < record_count)
|
||||
@@ -711,6 +731,7 @@ void Attendance_MarkSyncStatus(uint16_t index, SyncStatus status)
|
||||
* @brief 增加记录的重试计数
|
||||
* @param index 记录索引
|
||||
*/
|
||||
/* 函数作用:增加指定记录的上传重试次数。 */
|
||||
void Attendance_IncrementRetry(uint16_t index)
|
||||
{
|
||||
if (index < record_count)
|
||||
@@ -724,6 +745,7 @@ void Attendance_IncrementRetry(uint16_t index)
|
||||
/**
|
||||
* @brief 将所有FAILED记录重置为PENDING(用于网络恢复后)
|
||||
*/
|
||||
/* 函数作用:网络恢复后把失败记录重新置为待同步状态。 */
|
||||
void Attendance_ResetFailedRecords(void)
|
||||
{
|
||||
uint16_t reset_count = 0;
|
||||
@@ -748,6 +770,7 @@ void Attendance_ResetFailedRecords(void)
|
||||
* @param index 记录索引
|
||||
* @retval 记录指针,如果索引无效则返回NULL
|
||||
*/
|
||||
/* 函数作用:根据数组索引返回对应考勤记录指针。 */
|
||||
AttendanceRecord* Attendance_GetRecordByIndex(uint16_t index)
|
||||
{
|
||||
if (index < record_count)
|
||||
|
||||
+40
-18
@@ -51,29 +51,51 @@ typedef struct {
|
||||
} TimeSlot;
|
||||
|
||||
// 函数声明
|
||||
// 函数作用:初始化考勤模块的记录缓存、时间段配置和运行状态。
|
||||
void Attendance_Init(void);
|
||||
void Attendance_SetCurrentTime(uint8_t hour, uint8_t minute); // 设置当前时间
|
||||
void Attendance_UpdateTimeSlots(TimeSlot* slots, uint8_t count); // 更新时间段配置(从服务器下载后调用)
|
||||
uint8_t Attendance_ParseTimeSlotsJSON(const char* json_str); // 从JSON字符串解析时间段配置
|
||||
// 函数作用:设置考勤模块当前使用的小时和分钟,用于判断当前是否处于考勤时段。
|
||||
void Attendance_SetCurrentTime(uint8_t hour, uint8_t minute);
|
||||
// 函数作用:使用服务器下发的时间段数组更新本地考勤时间段配置。
|
||||
void Attendance_UpdateTimeSlots(TimeSlot* slots, uint8_t count);
|
||||
// 函数作用:从服务器返回的 JSON 字符串中解析时间段配置并更新本地列表。
|
||||
uint8_t Attendance_ParseTimeSlotsJSON(const char* json_str);
|
||||
// 函数作用:处理 K210 人脸识别结果,判断是否可打卡并生成考勤记录。
|
||||
AttendanceStatus Attendance_ProcessFaceResult(FaceRecognitionResult* result);
|
||||
// 函数作用:检查指定学号在当前考勤时段内是否已经打卡。
|
||||
uint8_t Attendance_IsDuplicate(const char* student_id);
|
||||
uint8_t Attendance_IsDuplicateOnServer(const char* student_id); // 服务器端查重
|
||||
void Attendance_GetCurrentSlotTime(char* start_time, char* end_time); // 获取当前时段时间
|
||||
uint8_t Attendance_LoadTodayRecordsFromServer(void); // 新增:从服务器加载今日签到记录
|
||||
uint8_t Attendance_IsDuplicateInCache(const char* student_id); // 新增:在本地缓存中查重
|
||||
void Attendance_AddToCache(const char* student_id); // 新增:添加学号到缓存
|
||||
void Attendance_ClearCache(void); // 新增:清空缓存
|
||||
// 函数作用:通过服务器接口检查指定学号在当前时段是否已经打卡。
|
||||
uint8_t Attendance_IsDuplicateOnServer(const char* student_id);
|
||||
// 函数作用:获取当前考勤时段的开始和结束时间字符串。
|
||||
void Attendance_GetCurrentSlotTime(char* start_time, char* end_time);
|
||||
// 函数作用:从服务器加载今日已签到记录,用于设备重启后恢复去重缓存。
|
||||
uint8_t Attendance_LoadTodayRecordsFromServer(void);
|
||||
// 函数作用:在本地今日签到缓存中检查指定学号是否已存在。
|
||||
uint8_t Attendance_IsDuplicateInCache(const char* student_id);
|
||||
// 函数作用:把指定学号加入本地今日签到缓存。
|
||||
void Attendance_AddToCache(const char* student_id);
|
||||
// 函数作用:清空本地今日签到缓存。
|
||||
void Attendance_ClearCache(void);
|
||||
// 函数作用:清空本地保存的考勤记录和同步状态。
|
||||
void Attendance_ClearRecords(void);
|
||||
uint16_t Attendance_GetTotalCount(void); // 获取累计考勤总数
|
||||
uint16_t Attendance_GetActiveCount(void); // 获取当前缓存记录数
|
||||
int8_t Attendance_GetCurrentTimeSlot(void); // 获取当前时段编号(-1表示不在考勤时段)
|
||||
// 函数作用:返回累计考勤记录总数。
|
||||
uint16_t Attendance_GetTotalCount(void);
|
||||
// 函数作用:返回当前本地缓存中的有效记录数量。
|
||||
uint16_t Attendance_GetActiveCount(void);
|
||||
// 函数作用:返回当前所在考勤时段编号,-1 表示不在任意考勤时段。
|
||||
int8_t Attendance_GetCurrentTimeSlot(void);
|
||||
|
||||
// 数据同步相关函数
|
||||
uint16_t Attendance_GetPendingCount(void); // 获取待同步记录数量(PENDING状态)
|
||||
AttendanceRecord* Attendance_GetNextPendingRecord(uint16_t* index); // 获取下一个待同步记录
|
||||
void Attendance_MarkSyncStatus(uint16_t index, SyncStatus status); // 更新记录的同步状态
|
||||
void Attendance_IncrementRetry(uint16_t index); // 增加重试计数
|
||||
AttendanceRecord* Attendance_GetRecordByIndex(uint16_t index); // 按索引获取记录
|
||||
void Attendance_ResetFailedRecords(void); // 将所有FAILED记录重置为PENDING
|
||||
// 函数作用:统计当前处于待同步状态的考勤记录数量。
|
||||
uint16_t Attendance_GetPendingCount(void);
|
||||
// 函数作用:查找下一条待同步记录,并通过 index 返回其在缓存中的位置。
|
||||
AttendanceRecord* Attendance_GetNextPendingRecord(uint16_t* index);
|
||||
// 函数作用:按缓存索引更新指定考勤记录的同步状态。
|
||||
void Attendance_MarkSyncStatus(uint16_t index, SyncStatus status);
|
||||
// 函数作用:按缓存索引增加指定考勤记录的同步重试次数。
|
||||
void Attendance_IncrementRetry(uint16_t index);
|
||||
// 函数作用:按缓存索引返回指定考勤记录指针。
|
||||
AttendanceRecord* Attendance_GetRecordByIndex(uint16_t index);
|
||||
// 函数作用:将所有同步失败记录重置为待同步,便于后续重新上传。
|
||||
void Attendance_ResetFailedRecords(void);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "Buzzer_LED.h"
|
||||
|
||||
/* 函数作用:初始化蜂鸣器、成功指示灯和板载状态 LED 对应 GPIO。 */
|
||||
void Buzzer_LED_Init(void)
|
||||
{
|
||||
GPIO_InitTypeDef GPIO_InitStructure;
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#define LED_STATUS_OFF() GPIO_SetBits(GPIOC, GPIO_Pin_13) // 就绪后:熄灭
|
||||
|
||||
// 声明初始化函数
|
||||
// 函数作用:初始化成功指示灯、状态灯和蜂鸣器相关 GPIO 输出状态。
|
||||
void Buzzer_LED_Init(void);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -23,6 +23,7 @@ static const uint8_t AES_IV[16] = {
|
||||
|
||||
static struct AES_ctx aes_ctx;
|
||||
|
||||
/* 函数作用:初始化 AES 加密上下文。 */
|
||||
void Crypto_Init(void)
|
||||
{
|
||||
// 初始化AES上下文(使用CBC模式)
|
||||
@@ -33,6 +34,7 @@ void Crypto_Init(void)
|
||||
* @brief PKCS#7填充
|
||||
* 将数据填充到16字节的倍数
|
||||
*/
|
||||
/* 函数作用:为 JSON 明文追加 PKCS#7 填充。 */
|
||||
static uint32_t pkcs7_pad(uint8_t* data, uint32_t data_len, uint32_t buffer_size)
|
||||
{
|
||||
// 计算需要填充的字节数
|
||||
@@ -52,6 +54,7 @@ static uint32_t pkcs7_pad(uint8_t* data, uint32_t data_len, uint32_t buffer_size
|
||||
return data_len + padding_len;
|
||||
}
|
||||
|
||||
/* 函数作用:移除解密结果中的 PKCS#7 填充。 */
|
||||
static uint32_t pkcs7_unpad(uint8_t* data, uint32_t data_len)
|
||||
{
|
||||
uint8_t padding_len;
|
||||
@@ -75,6 +78,7 @@ static uint32_t pkcs7_unpad(uint8_t* data, uint32_t data_len)
|
||||
return data_len - padding_len;
|
||||
}
|
||||
|
||||
/* 函数作用:将 JSON 字符串 AES-CBC 加密并 Base64 编码输出。 */
|
||||
uint32_t Crypto_EncryptJSON(const char* json_data, char* encrypted_output)
|
||||
{
|
||||
uint32_t json_len = strlen(json_data);
|
||||
@@ -121,6 +125,7 @@ uint32_t Crypto_EncryptJSON(const char* json_data, char* encrypted_output)
|
||||
return encoded_len;
|
||||
}
|
||||
|
||||
/* 函数作用:将 Base64 密文解码并 AES-CBC 解密为 JSON 字符串。 */
|
||||
uint32_t Crypto_DecryptJSON(const char* encrypted_base64, char* plain_output, uint32_t output_size)
|
||||
{
|
||||
uint32_t encrypted_len;
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
*
|
||||
* 注意:encrypted_output缓冲区至少需要 (strlen(json_data) / 16 + 1) * 16 * 4/3 + 10 字节
|
||||
*/
|
||||
// 函数作用:对 JSON 字符串执行 AES-CBC 加密和 Base64 编码,生成可上传服务端的密文。
|
||||
uint32_t Crypto_EncryptJSON(const char* json_data, char* encrypted_output);
|
||||
|
||||
/**
|
||||
@@ -21,6 +22,7 @@ uint32_t Crypto_EncryptJSON(const char* json_data, char* encrypted_output);
|
||||
* @param output_size 输出缓冲区大小
|
||||
* @return 明文字节数,失败返回0
|
||||
*/
|
||||
// 函数作用:对 Base64 编码的 AES-CBC 密文进行解码和解密,输出 JSON 明文。
|
||||
uint32_t Crypto_DecryptJSON(const char* encrypted_base64, char* plain_output, uint32_t output_size);
|
||||
|
||||
/**
|
||||
@@ -34,6 +36,7 @@ uint32_t Crypto_DecryptJSON(const char* encrypted_base64, char* plain_output, ui
|
||||
* @brief 初始化加密模块
|
||||
* 设置密钥和IV(初始化向量)
|
||||
*/
|
||||
// 函数作用:初始化加密模块使用的固定密钥、IV 和底层 AES 上下文。
|
||||
void Crypto_Init(void);
|
||||
|
||||
#endif // _CRYPTO_H_
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
#define DEC_TO_BCD(dec) ((((dec) / 10) << 4) | ((dec) % 10))
|
||||
|
||||
// 微秒延时
|
||||
/* 函数作用:为 DS1302 时序提供简短的微秒级软件延时。 */
|
||||
static void delay_us(uint32_t us)
|
||||
{
|
||||
volatile uint32_t i;
|
||||
@@ -44,6 +45,7 @@ static void delay_us(uint32_t us)
|
||||
/**
|
||||
* @brief 配置IO口为输出
|
||||
*/
|
||||
/* 函数作用:把 DS1302 数据引脚配置为推挽输出模式。 */
|
||||
static void DS1302_DATAOUT_init(void)
|
||||
{
|
||||
GPIO_InitTypeDef GPIO_InitStructure;
|
||||
@@ -56,6 +58,7 @@ static void DS1302_DATAOUT_init(void)
|
||||
/**
|
||||
* @brief 配置IO口为输入
|
||||
*/
|
||||
/* 函数作用:把 DS1302 数据引脚配置为浮空输入模式。 */
|
||||
static void DS1302_DATAINPUT_init(void)
|
||||
{
|
||||
GPIO_InitTypeDef GPIO_InitStructure;
|
||||
@@ -67,6 +70,7 @@ static void DS1302_DATAINPUT_init(void)
|
||||
/**
|
||||
* @brief DS1302初始化
|
||||
*/
|
||||
/* 函数作用:初始化 DS1302 的时钟、数据和使能引脚。 */
|
||||
void DS1302_Init(void)
|
||||
{
|
||||
GPIO_InitTypeDef GPIO_InitStructure;
|
||||
@@ -92,6 +96,7 @@ void DS1302_Init(void)
|
||||
* @brief 向DS1302发送一字节数据(根据demo代码)
|
||||
* @param data 要发送的数据
|
||||
*/
|
||||
/* 函数作用:按 DS1302 时序向芯片写入一个字节。 */
|
||||
static void DS1302_write_onebyte(uint8_t data)
|
||||
{
|
||||
uint8_t count = 0;
|
||||
@@ -119,6 +124,7 @@ static void DS1302_write_onebyte(uint8_t data)
|
||||
* @param address 寄存器地址
|
||||
* @param data 数据
|
||||
*/
|
||||
/* 函数作用:向 DS1302 指定寄存器写入一个字节数据。 */
|
||||
void DS1302_WriteByte(uint8_t address, uint8_t data)
|
||||
{
|
||||
CE_L;
|
||||
@@ -141,6 +147,7 @@ void DS1302_WriteByte(uint8_t address, uint8_t data)
|
||||
* @param address 寄存器地址
|
||||
* @retval 读取到的数据
|
||||
*/
|
||||
/* 函数作用:从 DS1302 指定寄存器读取一个字节数据。 */
|
||||
uint8_t DS1302_ReadByte(uint8_t address)
|
||||
{
|
||||
uint8_t count = 0;
|
||||
@@ -184,6 +191,7 @@ uint8_t DS1302_ReadByte(uint8_t address)
|
||||
* @brief 设置DS1302时间
|
||||
* @param time 时间结构体指针
|
||||
*/
|
||||
/* 函数作用:把时间结构体转换为 BCD 后写入 DS1302。 */
|
||||
void DS1302_SetTime(DS1302_Time* time)
|
||||
{
|
||||
// 关闭写保护
|
||||
@@ -206,6 +214,7 @@ void DS1302_SetTime(DS1302_Time* time)
|
||||
* @brief 读取DS1302时间
|
||||
* @param time 时间结构体指针
|
||||
*/
|
||||
/* 函数作用:读取 DS1302 时间寄存器并转换为十进制时间结构体。 */
|
||||
void DS1302_GetTime(DS1302_Time* time)
|
||||
{
|
||||
uint8_t read_time[7];
|
||||
|
||||
@@ -15,10 +15,15 @@ typedef struct {
|
||||
} DS1302_Time;
|
||||
|
||||
// 函数声明
|
||||
// 函数作用:初始化 DS1302 时钟芯片使用的 GPIO 引脚和通信状态。
|
||||
void DS1302_Init(void);
|
||||
// 函数作用:把指定时间写入 DS1302 时钟芯片。
|
||||
void DS1302_SetTime(DS1302_Time* time);
|
||||
// 函数作用:从 DS1302 时钟芯片读取当前日期和时间。
|
||||
void DS1302_GetTime(DS1302_Time* time);
|
||||
// 函数作用:从 DS1302 指定寄存器地址读取一个字节数据。
|
||||
uint8_t DS1302_ReadByte(uint8_t addr);
|
||||
// 函数作用:向 DS1302 指定寄存器地址写入一个字节数据。
|
||||
void DS1302_WriteByte(uint8_t addr, uint8_t dat);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -11,6 +11,7 @@ extern volatile uint8_t g_k210_rx_flag;
|
||||
// 全局变量:存储解析后的人脸识别结果
|
||||
FaceRecognitionResult K210_FaceResult;
|
||||
|
||||
/* 函数作用:初始化与 K210 通信的 USART3 串口和接收中断。 */
|
||||
void K210_USART_Init(void)
|
||||
{
|
||||
// 根据连接图.md: K210使用USART3 (PB10/PB11)
|
||||
@@ -60,6 +61,7 @@ void K210_USART_Init(void)
|
||||
* K210数据格式示例: "ID:2021001,NAME:Zhang_San,CONF:0.95"
|
||||
* 或者: "UNKNOWN" (未识别到人脸)
|
||||
*/
|
||||
/* 函数作用:解析 K210 串口发送的人脸识别字符串并填充结果结构体。 */
|
||||
uint8_t K210_ParseFaceData(const char* buffer, FaceRecognitionResult* result)
|
||||
{
|
||||
DEBUG_PRINT("[K210] Raw data: %s\r\n", buffer);
|
||||
@@ -109,6 +111,7 @@ uint8_t K210_ParseFaceData(const char* buffer, FaceRecognitionResult* result)
|
||||
return 0; // 解析失败
|
||||
}
|
||||
|
||||
/* 函数作用:检查 K210 接收完成标志,并在有新数据时解析识别结果。 */
|
||||
uint8_t K210_GetRxFlag(void)
|
||||
{
|
||||
if (g_k210_rx_flag)
|
||||
@@ -129,6 +132,7 @@ uint8_t K210_GetRxFlag(void)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 函数作用:清除 K210 串口接收完成标志和计数器。 */
|
||||
void K210_ClearRxFlag(void)
|
||||
{
|
||||
g_k210_rx_flag = 0;
|
||||
|
||||
@@ -10,9 +10,13 @@
|
||||
extern FaceRecognitionResult K210_FaceResult;
|
||||
|
||||
// 声明公共函数
|
||||
// 函数作用:初始化 STM32 与 K210 通信用的 USART3 接口和接收中断。
|
||||
void K210_USART_Init(void);
|
||||
// 函数作用:返回 K210 串口接收完成标志,用于主循环判断是否有新识别结果。
|
||||
uint8_t K210_GetRxFlag(void);
|
||||
// 函数作用:清除 K210 串口接收完成标志,准备接收下一帧数据。
|
||||
void K210_ClearRxFlag(void);
|
||||
// 函数作用:解析 K210 发来的识别结果字符串,填充人脸识别结果结构体。
|
||||
uint8_t K210_ParseFaceData(const char* buffer, FaceRecognitionResult* result);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -6,6 +6,7 @@ static uint8_t g_fill_light_on = 0;
|
||||
/**
|
||||
* @brief 初始化光敏电阻ADC和补光灯GPIO
|
||||
*/
|
||||
/* 函数作用:初始化光敏电阻 ADC 输入和补光灯 GPIO 输出。 */
|
||||
void LightSensor_Init(void)
|
||||
{
|
||||
GPIO_InitTypeDef GPIO_InitStructure;
|
||||
@@ -60,6 +61,7 @@ void LightSensor_Init(void)
|
||||
* @brief 获取光敏电阻ADC值
|
||||
* @return ADC值 (0-4095),值越大表示光线越暗(电阻越大)
|
||||
*/
|
||||
/* 函数作用:采集一次光敏电阻 ADC 数值。 */
|
||||
uint16_t LightSensor_GetValue(void)
|
||||
{
|
||||
// 启动ADC转换
|
||||
@@ -76,6 +78,7 @@ uint16_t LightSensor_GetValue(void)
|
||||
* @brief 更新补光灯状态(在主循环中定期调用)
|
||||
* 使用迟滞控制避免灯光频繁开关
|
||||
*/
|
||||
/* 函数作用:根据环境光强自动打开或关闭补光灯。 */
|
||||
void LightSensor_Update(void)
|
||||
{
|
||||
uint16_t light_value = LightSensor_GetValue();
|
||||
@@ -100,6 +103,7 @@ void LightSensor_Update(void)
|
||||
* @brief 获取补光灯当前状态
|
||||
* @return 1=亮,0=灭
|
||||
*/
|
||||
/* 函数作用:返回补光灯当前开关状态。 */
|
||||
uint8_t LightSensor_IsLightOn(void)
|
||||
{
|
||||
return g_fill_light_on;
|
||||
|
||||
@@ -22,9 +22,13 @@
|
||||
// 使用迟滞区间避免频繁开关
|
||||
|
||||
// 函数声明
|
||||
void LightSensor_Init(void); // 初始化光敏电阻ADC和补光灯GPIO
|
||||
uint16_t LightSensor_GetValue(void); // 获取光敏电阻ADC值
|
||||
void LightSensor_Update(void); // 更新补光灯状态(主循环中调用)
|
||||
uint8_t LightSensor_IsLightOn(void); // 获取补光灯当前状态
|
||||
// 函数作用:初始化光敏电阻 ADC 输入和补光灯 GPIO 输出。
|
||||
void LightSensor_Init(void);
|
||||
// 函数作用:读取光敏电阻 ADC 当前采样值。
|
||||
uint16_t LightSensor_GetValue(void);
|
||||
// 函数作用:根据当前光照强度和迟滞阈值更新补光灯开关状态。
|
||||
void LightSensor_Update(void);
|
||||
// 函数作用:返回补光灯当前是否处于点亮状态。
|
||||
uint8_t LightSensor_IsLightOn(void);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
/* ... 请确保您文件里这部分内容是完整的 ... */
|
||||
|
||||
/*引脚初始化*/
|
||||
/* 函数作用:初始化 OLED 软件 I2C 使用的 SCL 和 SDA GPIO。 */
|
||||
void OLED_I2C_Init(void)
|
||||
{
|
||||
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
|
||||
@@ -28,6 +29,7 @@ void OLED_I2C_Init(void)
|
||||
OLED_W_SDA(1);
|
||||
}
|
||||
|
||||
/* 函数作用:产生 OLED 软件 I2C 起始信号。 */
|
||||
void OLED_I2C_Start(void)
|
||||
{
|
||||
OLED_W_SDA(1);
|
||||
@@ -36,6 +38,7 @@ void OLED_I2C_Start(void)
|
||||
OLED_W_SCL(0);
|
||||
}
|
||||
|
||||
/* 函数作用:产生 OLED 软件 I2C 停止信号。 */
|
||||
void OLED_I2C_Stop(void)
|
||||
{
|
||||
OLED_W_SDA(0);
|
||||
@@ -43,6 +46,7 @@ void OLED_I2C_Stop(void)
|
||||
OLED_W_SDA(1);
|
||||
}
|
||||
|
||||
/* 函数作用:通过软件 I2C 向 OLED 发送一个字节。 */
|
||||
void OLED_I2C_SendByte(uint8_t Byte)
|
||||
{
|
||||
uint8_t i;
|
||||
@@ -56,6 +60,7 @@ void OLED_I2C_SendByte(uint8_t Byte)
|
||||
OLED_W_SCL(0);
|
||||
}
|
||||
|
||||
/* 函数作用:向 OLED 写入一条控制命令。 */
|
||||
void OLED_WriteCommand(uint8_t Command)
|
||||
{
|
||||
OLED_I2C_Start();
|
||||
@@ -65,6 +70,7 @@ void OLED_WriteCommand(uint8_t Command)
|
||||
OLED_I2C_Stop();
|
||||
}
|
||||
|
||||
/* 函数作用:向 OLED 写入一个显示数据字节。 */
|
||||
void OLED_WriteData(uint8_t Data)
|
||||
{
|
||||
OLED_I2C_Start();
|
||||
@@ -74,6 +80,7 @@ void OLED_WriteData(uint8_t Data)
|
||||
OLED_I2C_Stop();
|
||||
}
|
||||
|
||||
/* 函数作用:设置 OLED 显存写入光标位置。 */
|
||||
void OLED_SetCursor(uint8_t Y, uint8_t X)
|
||||
{
|
||||
OLED_WriteCommand(0xB0 | Y);
|
||||
@@ -81,6 +88,7 @@ void OLED_SetCursor(uint8_t Y, uint8_t X)
|
||||
OLED_WriteCommand(0x00 | (X & 0x0F));
|
||||
}
|
||||
|
||||
/* 函数作用:清空 OLED 屏幕显示内容。 */
|
||||
void OLED_Clear(void)
|
||||
{
|
||||
uint8_t i, j;
|
||||
@@ -94,6 +102,7 @@ void OLED_Clear(void)
|
||||
}
|
||||
}
|
||||
|
||||
/* 函数作用:在指定行列显示一个 ASCII 字符。 */
|
||||
void OLED_ShowChar(uint8_t Line, uint8_t Column, char Char)
|
||||
{
|
||||
uint8_t i;
|
||||
@@ -109,6 +118,7 @@ void OLED_ShowChar(uint8_t Line, uint8_t Column, char Char)
|
||||
}
|
||||
}
|
||||
|
||||
/* 函数作用:从指定行列开始显示字符串。 */
|
||||
void OLED_ShowString(uint8_t Line, uint8_t Column, char *String)
|
||||
{
|
||||
uint8_t i;
|
||||
@@ -118,6 +128,7 @@ void OLED_ShowString(uint8_t Line, uint8_t Column, char *String)
|
||||
}
|
||||
}
|
||||
|
||||
/* 函数作用:计算整数 X 的 Y 次方,用于数字拆位显示。 */
|
||||
uint32_t OLED_Pow(uint32_t X, uint32_t Y)
|
||||
{
|
||||
uint32_t Result = 1;
|
||||
@@ -128,6 +139,7 @@ uint32_t OLED_Pow(uint32_t X, uint32_t Y)
|
||||
return Result;
|
||||
}
|
||||
|
||||
/* 函数作用:在 OLED 上显示指定长度的无符号十进制数字。 */
|
||||
void OLED_ShowNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length)
|
||||
{
|
||||
uint8_t i;
|
||||
@@ -137,6 +149,7 @@ void OLED_ShowNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length)
|
||||
}
|
||||
}
|
||||
|
||||
/* 函数作用:在 OLED 上显示带正负号的十进制数字。 */
|
||||
void OLED_ShowSignedNum(uint8_t Line, uint8_t Column, int32_t Number, uint8_t Length)
|
||||
{
|
||||
uint8_t i;
|
||||
@@ -157,6 +170,7 @@ void OLED_ShowSignedNum(uint8_t Line, uint8_t Column, int32_t Number, uint8_t Le
|
||||
}
|
||||
}
|
||||
|
||||
/* 函数作用:在 OLED 上显示指定长度的十六进制数字。 */
|
||||
void OLED_ShowHexNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length)
|
||||
{
|
||||
uint8_t i, SingleNumber;
|
||||
@@ -174,6 +188,7 @@ void OLED_ShowHexNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Leng
|
||||
}
|
||||
}
|
||||
|
||||
/* 函数作用:在 OLED 上显示指定长度的二进制数字。 */
|
||||
void OLED_ShowBinNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length)
|
||||
{
|
||||
uint8_t i;
|
||||
@@ -191,6 +206,7 @@ void OLED_ShowBinNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Leng
|
||||
* @param num 要显示的汉字在OLED_F16x16_CN字模数组中的索引
|
||||
* @retval 无
|
||||
*/
|
||||
/* 函数作用:按照字模索引在 OLED 上显示一个 16x16 汉字。 */
|
||||
void OLED_ShowChinese(uint8_t Line, uint8_t Column, uint8_t num)
|
||||
{
|
||||
uint8_t i;
|
||||
@@ -219,6 +235,7 @@ void OLED_ShowChinese(uint8_t Line, uint8_t Column, uint8_t num)
|
||||
|
||||
|
||||
/* ... OLED_Init() 函数保持不变 ... */
|
||||
/* 函数作用:初始化 OLED 控制器参数并清屏。 */
|
||||
void OLED_Init(void)
|
||||
{
|
||||
uint32_t i, j;
|
||||
|
||||
@@ -6,16 +6,25 @@
|
||||
|
||||
#include "stm32f10x.h"
|
||||
|
||||
// 函数作用:初始化 OLED 屏幕通信接口并完成显示控制器基础配置。
|
||||
void OLED_Init(void);
|
||||
// 函数作用:清空 OLED 显示缓冲区或屏幕内容。
|
||||
void OLED_Clear(void);
|
||||
// 函数作用:在指定行列显示单个 ASCII 字符。
|
||||
void OLED_ShowChar(uint8_t Line, uint8_t Column, char Char);
|
||||
// 函数作用:从指定行列开始显示字符串。
|
||||
void OLED_ShowString(uint8_t Line, uint8_t Column, char *String);
|
||||
// 函数作用:在指定位置显示无符号十进制数字。
|
||||
void OLED_ShowNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length);
|
||||
// 函数作用:在指定位置显示带符号十进制数字。
|
||||
void OLED_ShowSignedNum(uint8_t Line, uint8_t Column, int32_t Number, uint8_t Length);
|
||||
// 函数作用:在指定位置显示十六进制数字。
|
||||
void OLED_ShowHexNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length);
|
||||
// 函数作用:在指定位置显示二进制数字。
|
||||
void OLED_ShowBinNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length);
|
||||
|
||||
// 添加显示汉字的函数声明
|
||||
// 函数作用:在指定行列显示字库中编号对应的汉字。
|
||||
void OLED_ShowChinese(uint8_t Line, uint8_t Column, uint8_t num);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -7,6 +7,7 @@ char RxBuffer[RX_BUFFER_SIZE];
|
||||
uint16_t RxCounter = 0;
|
||||
|
||||
// 初始化ESP8266所用的串口 (根据连接图.md: USART1 PA9/PA10)
|
||||
/* 函数作用:初始化与 ESP8266 通信的 USART1 串口和接收中断。 */
|
||||
void Serial_Init(void)
|
||||
{
|
||||
GPIO_InitTypeDef GPIO_InitStructure;
|
||||
@@ -46,6 +47,7 @@ void Serial_Init(void)
|
||||
}
|
||||
|
||||
// 发送字符串
|
||||
/* 函数作用:通过 USART1 向 ESP8266 发送字符串。 */
|
||||
void Serial_SendString(const char* String)
|
||||
{
|
||||
for (uint32_t i = 0; String[i] != '\0'; i++)
|
||||
@@ -56,12 +58,14 @@ void Serial_SendString(const char* String)
|
||||
}
|
||||
|
||||
// 获取接收缓冲区
|
||||
/* 函数作用:返回 USART1 接收缓冲区指针。 */
|
||||
char* Serial_GetRxBuffer(void)
|
||||
{
|
||||
return RxBuffer;
|
||||
}
|
||||
|
||||
// 清空接收缓冲区
|
||||
/* 函数作用:清空 USART1 接收缓冲区和接收计数。 */
|
||||
void Serial_ClearRxBuffer(void)
|
||||
{
|
||||
memset(RxBuffer, 0, RX_BUFFER_SIZE);
|
||||
@@ -69,6 +73,7 @@ void Serial_ClearRxBuffer(void)
|
||||
}
|
||||
|
||||
// USART1中断服务函数 (ESP8266)
|
||||
/* 函数作用:接收 ESP8266 串口返回数据并写入环形缓冲区。 */
|
||||
void USART1_IRQHandler(void)
|
||||
{
|
||||
if (USART_GetITStatus(USART1, USART_IT_RXNE) == SET)
|
||||
|
||||
+11
-5
@@ -4,14 +4,20 @@
|
||||
#include <stdio.h>
|
||||
#include "stm32f10x.h"
|
||||
|
||||
void Serial_Init(void); // 初始化串口
|
||||
void Serial_SendByte(uint8_t Byte); // 发送一个字节
|
||||
void Serial_SendString(const char* String); // 发送字符串
|
||||
char* Serial_GetRxBuffer(void); // 获取接收缓冲区指针
|
||||
void Serial_ClearRxBuffer(void); // 清空接收缓冲区
|
||||
// 函数作用:初始化调试/通信串口及其接收缓冲区。
|
||||
void Serial_Init(void);
|
||||
// 函数作用:通过串口发送一个字节。
|
||||
void Serial_SendByte(uint8_t Byte);
|
||||
// 函数作用:通过串口连续发送以空字符结尾的字符串。
|
||||
void Serial_SendString(const char* String);
|
||||
// 函数作用:返回串口接收缓冲区指针,供主循环解析接收到的数据。
|
||||
char* Serial_GetRxBuffer(void);
|
||||
// 函数作用:清空串口接收缓冲区并复位接收状态。
|
||||
void Serial_ClearRxBuffer(void);
|
||||
|
||||
// 注意:Get_Time_ms() 函数与串口无关,应该在 `Delay.h` 中声明。
|
||||
// 我们暂时将它与串口放一起,但更规范的做法是分离。
|
||||
// 函数作用:返回系统启动以来经过的毫秒数。
|
||||
uint32_t Get_Time_ms(void);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -54,6 +54,7 @@ static const uint8_t Rcon[11] = {
|
||||
#define getSBoxInvert(num) (rsbox[(num)])
|
||||
|
||||
// 密钥扩展
|
||||
/* 函数作用:根据 AES-128 主密钥生成每轮加密使用的轮密钥。 */
|
||||
static void KeyExpansion(uint8_t* RoundKey, const uint8_t* Key)
|
||||
{
|
||||
unsigned i, j, k;
|
||||
@@ -102,6 +103,7 @@ static void KeyExpansion(uint8_t* RoundKey, const uint8_t* Key)
|
||||
}
|
||||
|
||||
// AddRoundKey
|
||||
/* 函数作用:将当前状态矩阵与指定轮密钥进行异或。 */
|
||||
static void AddRoundKey(uint8_t round, uint8_t* state, const uint8_t* RoundKey)
|
||||
{
|
||||
uint8_t i, j;
|
||||
@@ -113,6 +115,7 @@ static void AddRoundKey(uint8_t round, uint8_t* state, const uint8_t* RoundKey)
|
||||
}
|
||||
|
||||
// SubBytes
|
||||
/* 函数作用:使用 AES S 盒对状态矩阵进行字节替换。 */
|
||||
static void SubBytes(uint8_t* state)
|
||||
{
|
||||
uint8_t i;
|
||||
@@ -122,6 +125,7 @@ static void SubBytes(uint8_t* state)
|
||||
}
|
||||
|
||||
// ShiftRows
|
||||
/* 函数作用:执行 AES 加密流程中的行移位变换。 */
|
||||
static void ShiftRows(uint8_t* state)
|
||||
{
|
||||
uint8_t temp;
|
||||
@@ -150,12 +154,14 @@ static void ShiftRows(uint8_t* state)
|
||||
}
|
||||
|
||||
// xtime (用于MixColumns)
|
||||
/* 函数作用:在 AES 有限域中执行乘以 2 的运算。 */
|
||||
static uint8_t xtime(uint8_t x)
|
||||
{
|
||||
return ((x << 1) ^ (((x >> 7) & 1) * 0x1b));
|
||||
}
|
||||
|
||||
// MixColumns
|
||||
/* 函数作用:执行 AES 加密流程中的列混淆变换。 */
|
||||
static void MixColumns(uint8_t* state)
|
||||
{
|
||||
uint8_t i;
|
||||
@@ -184,6 +190,7 @@ static void MixColumns(uint8_t* state)
|
||||
}
|
||||
|
||||
// Cipher - AES加密核心
|
||||
/* 函数作用:对 16 字节状态块执行完整 AES 加密。 */
|
||||
static void Cipher(uint8_t* state, const uint8_t* RoundKey)
|
||||
{
|
||||
uint8_t round = 0;
|
||||
@@ -205,6 +212,7 @@ static void Cipher(uint8_t* state, const uint8_t* RoundKey)
|
||||
AddRoundKey(10, state, RoundKey);
|
||||
}
|
||||
|
||||
/* 函数作用:在 AES 有限域中执行两个字节的乘法。 */
|
||||
static uint8_t Multiply(uint8_t x, uint8_t y)
|
||||
{
|
||||
return (((y & 1) * x) ^
|
||||
@@ -214,6 +222,7 @@ static uint8_t Multiply(uint8_t x, uint8_t y)
|
||||
(((y >> 4) & 1) * xtime(xtime(xtime(xtime(x))))));
|
||||
}
|
||||
|
||||
/* 函数作用:使用 AES 逆 S 盒对状态矩阵进行逆字节替换。 */
|
||||
static void InvSubBytes(uint8_t* state)
|
||||
{
|
||||
uint8_t i;
|
||||
@@ -222,6 +231,7 @@ static void InvSubBytes(uint8_t* state)
|
||||
}
|
||||
}
|
||||
|
||||
/* 函数作用:执行 AES 解密流程中的逆行移位变换。 */
|
||||
static void InvShiftRows(uint8_t* state)
|
||||
{
|
||||
uint8_t temp;
|
||||
@@ -245,6 +255,7 @@ static void InvShiftRows(uint8_t* state)
|
||||
state[15] = temp;
|
||||
}
|
||||
|
||||
/* 函数作用:执行 AES 解密流程中的逆列混淆变换。 */
|
||||
static void InvMixColumns(uint8_t* state)
|
||||
{
|
||||
uint8_t i;
|
||||
@@ -263,6 +274,7 @@ static void InvMixColumns(uint8_t* state)
|
||||
}
|
||||
}
|
||||
|
||||
/* 函数作用:对 16 字节状态块执行完整 AES 解密。 */
|
||||
static void InvCipher(uint8_t* state, const uint8_t* RoundKey)
|
||||
{
|
||||
uint8_t round;
|
||||
@@ -282,22 +294,26 @@ static void InvCipher(uint8_t* state, const uint8_t* RoundKey)
|
||||
|
||||
// ========== 公开API函数 ==========
|
||||
|
||||
/* 函数作用:初始化 AES 上下文并生成轮密钥。 */
|
||||
void AES_init_ctx(struct AES_ctx* ctx, const uint8_t* key)
|
||||
{
|
||||
KeyExpansion(ctx->RoundKey, key);
|
||||
}
|
||||
|
||||
/* 函数作用:初始化 AES 上下文并设置 CBC 模式 IV。 */
|
||||
void AES_init_ctx_iv(struct AES_ctx* ctx, const uint8_t* key, const uint8_t* iv)
|
||||
{
|
||||
KeyExpansion(ctx->RoundKey, key);
|
||||
memcpy(ctx->Iv, iv, AES_BLOCKLEN);
|
||||
}
|
||||
|
||||
/* 函数作用:对单个 16 字节数据块执行 AES-ECB 加密。 */
|
||||
void AES_ECB_encrypt(const struct AES_ctx* ctx, uint8_t* buf)
|
||||
{
|
||||
Cipher(buf, ctx->RoundKey);
|
||||
}
|
||||
|
||||
/* 函数作用:使用 AES-CBC 模式加密指定长度的数据缓冲区。 */
|
||||
void AES_CBC_encrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, size_t length)
|
||||
{
|
||||
size_t i;
|
||||
@@ -317,11 +333,13 @@ void AES_CBC_encrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, size_t length)
|
||||
}
|
||||
}
|
||||
|
||||
/* 函数作用:对单个 16 字节数据块执行 AES-ECB 解密。 */
|
||||
void AES_ECB_decrypt(const struct AES_ctx* ctx, uint8_t* buf)
|
||||
{
|
||||
InvCipher(buf, ctx->RoundKey);
|
||||
}
|
||||
|
||||
/* 函数作用:使用 AES-CBC 模式解密指定长度的数据缓冲区。 */
|
||||
void AES_CBC_decrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, size_t length)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
@@ -20,6 +20,7 @@ struct AES_ctx
|
||||
* @param ctx AES上下文结构
|
||||
* @param key 16字节密钥
|
||||
*/
|
||||
// 函数作用:根据 16 字节密钥初始化 AES 上下文并生成轮密钥。
|
||||
void AES_init_ctx(struct AES_ctx* ctx, const uint8_t* key);
|
||||
|
||||
/**
|
||||
@@ -27,6 +28,7 @@ void AES_init_ctx(struct AES_ctx* ctx, const uint8_t* key);
|
||||
* @param ctx AES上下文
|
||||
* @param buf 要加密的数据(16字节块)
|
||||
*/
|
||||
// 函数作用:使用 AES-ECB 模式加密单个 16 字节数据块。
|
||||
void AES_ECB_encrypt(const struct AES_ctx* ctx, uint8_t* buf);
|
||||
|
||||
/**
|
||||
@@ -34,6 +36,7 @@ void AES_ECB_encrypt(const struct AES_ctx* ctx, uint8_t* buf);
|
||||
* @param ctx AES上下文
|
||||
* @param buf 要解密的数据(16字节块)
|
||||
*/
|
||||
// 函数作用:使用 AES-ECB 模式解密单个 16 字节数据块。
|
||||
void AES_ECB_decrypt(const struct AES_ctx* ctx, uint8_t* buf);
|
||||
|
||||
/**
|
||||
@@ -43,6 +46,7 @@ void AES_ECB_decrypt(const struct AES_ctx* ctx, uint8_t* buf);
|
||||
* @param length 数据长度(必须是16的倍数)
|
||||
* @param iv 初始化向量(16字节)
|
||||
*/
|
||||
// 函数作用:使用上下文中的 IV 按 AES-CBC 模式加密指定长度的缓冲区。
|
||||
void AES_CBC_encrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, size_t length);
|
||||
|
||||
/**
|
||||
@@ -52,6 +56,7 @@ void AES_CBC_encrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, size_t length);
|
||||
* @param length 数据长度(必须是16的倍数)
|
||||
* @param iv 初始化向量(16字节)
|
||||
*/
|
||||
// 函数作用:使用上下文中的 IV 按 AES-CBC 模式解密指定长度的缓冲区。
|
||||
void AES_CBC_decrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, size_t length);
|
||||
|
||||
/**
|
||||
@@ -60,6 +65,7 @@ void AES_CBC_decrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, size_t length);
|
||||
* @param key 16字节密钥
|
||||
* @param iv 16字节初始化向量
|
||||
*/
|
||||
// 函数作用:使用 16 字节密钥和 16 字节 IV 初始化 AES-CBC 上下文。
|
||||
void AES_init_ctx_iv(struct AES_ctx* ctx, const uint8_t* key, const uint8_t* iv);
|
||||
|
||||
#endif // _AES_H_
|
||||
|
||||
@@ -34,6 +34,7 @@ static const uint8_t Rcon[11] = {
|
||||
#define getSBoxValue(num) (sbox[(num)])
|
||||
|
||||
// 密钥扩展
|
||||
/* 函数作用:根据 AES-128 主密钥生成每轮加密使用的轮密钥。 */
|
||||
static void KeyExpansion(uint8_t* RoundKey, const uint8_t* Key)
|
||||
{
|
||||
unsigned i, j, k;
|
||||
@@ -82,6 +83,7 @@ static void KeyExpansion(uint8_t* RoundKey, const uint8_t* Key)
|
||||
}
|
||||
|
||||
// AddRoundKey
|
||||
/* 函数作用:将当前状态矩阵与指定轮密钥进行异或。 */
|
||||
static void AddRoundKey(uint8_t round, uint8_t* state, const uint8_t* RoundKey)
|
||||
{
|
||||
uint8_t i, j;
|
||||
@@ -93,6 +95,7 @@ static void AddRoundKey(uint8_t round, uint8_t* state, const uint8_t* RoundKey)
|
||||
}
|
||||
|
||||
// SubBytes
|
||||
/* 函数作用:使用 AES S 盒对状态矩阵进行字节替换。 */
|
||||
static void SubBytes(uint8_t* state)
|
||||
{
|
||||
uint8_t i;
|
||||
@@ -102,6 +105,7 @@ static void SubBytes(uint8_t* state)
|
||||
}
|
||||
|
||||
// ShiftRows
|
||||
/* 函数作用:执行 AES 加密流程中的行移位变换。 */
|
||||
static void ShiftRows(uint8_t* state)
|
||||
{
|
||||
uint8_t temp;
|
||||
@@ -130,12 +134,14 @@ static void ShiftRows(uint8_t* state)
|
||||
}
|
||||
|
||||
// xtime (用于MixColumns)
|
||||
/* 函数作用:在 AES 有限域中执行乘以 2 的运算。 */
|
||||
static uint8_t xtime(uint8_t x)
|
||||
{
|
||||
return ((x << 1) ^ (((x >> 7) & 1) * 0x1b));
|
||||
}
|
||||
|
||||
// MixColumns
|
||||
/* 函数作用:执行 AES 加密流程中的列混淆变换。 */
|
||||
static void MixColumns(uint8_t* state)
|
||||
{
|
||||
uint8_t i;
|
||||
@@ -164,6 +170,7 @@ static void MixColumns(uint8_t* state)
|
||||
}
|
||||
|
||||
// Cipher - AES加密核心
|
||||
/* 函数作用:对 16 字节状态块执行完整 AES 加密。 */
|
||||
static void Cipher(uint8_t* state, const uint8_t* RoundKey)
|
||||
{
|
||||
uint8_t round = 0;
|
||||
@@ -187,22 +194,26 @@ static void Cipher(uint8_t* state, const uint8_t* RoundKey)
|
||||
|
||||
// ========== 公开API函数 ==========
|
||||
|
||||
/* 函数作用:初始化 AES 上下文并生成轮密钥。 */
|
||||
void AES_init_ctx(struct AES_ctx* ctx, const uint8_t* key)
|
||||
{
|
||||
KeyExpansion(ctx->RoundKey, key);
|
||||
}
|
||||
|
||||
/* 函数作用:初始化 AES 上下文并设置 CBC 模式 IV。 */
|
||||
void AES_init_ctx_iv(struct AES_ctx* ctx, const uint8_t* key, const uint8_t* iv)
|
||||
{
|
||||
KeyExpansion(ctx->RoundKey, key);
|
||||
memcpy(ctx->Iv, iv, AES_BLOCKLEN);
|
||||
}
|
||||
|
||||
/* 函数作用:对单个 16 字节数据块执行 AES-ECB 加密。 */
|
||||
void AES_ECB_encrypt(const struct AES_ctx* ctx, uint8_t* buf)
|
||||
{
|
||||
Cipher(buf, ctx->RoundKey);
|
||||
}
|
||||
|
||||
/* 函数作用:使用 AES-CBC 模式加密指定长度的数据缓冲区。 */
|
||||
void AES_CBC_encrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, size_t length)
|
||||
{
|
||||
size_t i;
|
||||
@@ -223,12 +234,14 @@ void AES_CBC_encrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, size_t length)
|
||||
}
|
||||
|
||||
// STM32端不需要解密功能
|
||||
/* 函数作用:对单个 16 字节数据块执行 AES-ECB 解密。 */
|
||||
void AES_ECB_decrypt(const struct AES_ctx* ctx, uint8_t* buf)
|
||||
{
|
||||
(void)ctx;
|
||||
(void)buf;
|
||||
}
|
||||
|
||||
/* 函数作用:使用 AES-CBC 模式解密指定长度的数据缓冲区。 */
|
||||
void AES_CBC_decrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, size_t length)
|
||||
{
|
||||
(void)ctx;
|
||||
|
||||
@@ -20,6 +20,7 @@ struct AES_ctx
|
||||
* @param ctx AES上下文结构
|
||||
* @param key 16字节密钥
|
||||
*/
|
||||
// 函数作用:根据 16 字节密钥初始化 AES 上下文并生成轮密钥。
|
||||
void AES_init_ctx(struct AES_ctx* ctx, const uint8_t* key);
|
||||
|
||||
/**
|
||||
@@ -28,6 +29,7 @@ void AES_init_ctx(struct AES_ctx* ctx, const uint8_t* key);
|
||||
* @param key 16字节密钥
|
||||
* @param iv 16字节初始化向量
|
||||
*/
|
||||
// 函数作用:使用 16 字节密钥和 16 字节 IV 初始化 AES-CBC 上下文。
|
||||
void AES_init_ctx_iv(struct AES_ctx* ctx, const uint8_t* key, const uint8_t* iv);
|
||||
|
||||
/**
|
||||
@@ -35,6 +37,7 @@ void AES_init_ctx_iv(struct AES_ctx* ctx, const uint8_t* key, const uint8_t* iv)
|
||||
* @param ctx AES上下文
|
||||
* @param buf 要加密的数据(16字节块)
|
||||
*/
|
||||
// 函数作用:使用 AES-ECB 模式加密单个 16 字节数据块。
|
||||
void AES_ECB_encrypt(const struct AES_ctx* ctx, uint8_t* buf);
|
||||
|
||||
/**
|
||||
@@ -42,6 +45,7 @@ void AES_ECB_encrypt(const struct AES_ctx* ctx, uint8_t* buf);
|
||||
* @param ctx AES上下文
|
||||
* @param buf 要解密的数据(16字节块)
|
||||
*/
|
||||
// 函数作用:使用 AES-ECB 模式解密单个 16 字节数据块。
|
||||
void AES_ECB_decrypt(const struct AES_ctx* ctx, uint8_t* buf);
|
||||
|
||||
/**
|
||||
@@ -50,6 +54,7 @@ void AES_ECB_decrypt(const struct AES_ctx* ctx, uint8_t* buf);
|
||||
* @param buf 要加密的数据
|
||||
* @param length 数据长度(必须是16的倍数)
|
||||
*/
|
||||
// 函数作用:使用上下文中的 IV 按 AES-CBC 模式加密指定长度的缓冲区。
|
||||
void AES_CBC_encrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, size_t length);
|
||||
|
||||
/**
|
||||
@@ -58,6 +63,7 @@ void AES_CBC_encrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, size_t length);
|
||||
* @param buf 要解密的数据
|
||||
* @param length 数据长度(必须是16的倍数)
|
||||
*/
|
||||
// 函数作用:使用上下文中的 IV 按 AES-CBC 模式解密指定长度的缓冲区。
|
||||
void AES_CBC_decrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, size_t length);
|
||||
|
||||
#endif // _AES_H_
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
static const char base64_chars[] =
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
|
||||
/* 函数作用:将二进制数据编码为 Base64 字符串。 */
|
||||
uint32_t base64_encode(const uint8_t* input, uint32_t input_length, char* output)
|
||||
{
|
||||
uint32_t i, j;
|
||||
@@ -41,6 +42,7 @@ uint32_t base64_encode(const uint8_t* input, uint32_t input_length, char* output
|
||||
return output_length;
|
||||
}
|
||||
|
||||
/* 函数作用:把 Base64 字符转换为 6 位数值。 */
|
||||
static uint8_t base64_char_value(char c)
|
||||
{
|
||||
if (c >= 'A' && c <= 'Z') return c - 'A';
|
||||
@@ -51,6 +53,7 @@ static uint8_t base64_char_value(char c)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 函数作用:将 Base64 字符串解码为二进制数据。 */
|
||||
uint32_t base64_decode(const char* input, uint32_t input_length, uint8_t* output)
|
||||
{
|
||||
uint32_t i, j;
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
*
|
||||
* 注意:output缓冲区大小至少需要 ((input_length + 2) / 3) * 4 + 1 字节
|
||||
*/
|
||||
// 函数作用:将二进制输入数据编码为 Base64 字符串并返回输出长度。
|
||||
uint32_t base64_encode(const uint8_t* input, uint32_t input_length, char* output);
|
||||
|
||||
/**
|
||||
@@ -22,6 +23,7 @@ uint32_t base64_encode(const uint8_t* input, uint32_t input_length, char* output
|
||||
* @param output 输出缓冲区
|
||||
* @return 解码后的数据长度
|
||||
*/
|
||||
// 函数作用:将 Base64 字符串解码为二进制数据并返回解码长度。
|
||||
uint32_t base64_decode(const char* input, uint32_t input_length, uint8_t* output);
|
||||
|
||||
/**
|
||||
|
||||
@@ -25,11 +25,13 @@ extern const char* HTTP_API_TOKEN;
|
||||
extern uint8_t g_server_connected;
|
||||
|
||||
/* --- 私有辅助函数 --- */
|
||||
/* 函数作用:向 ESP8266 串口发送一条 AT 指令并追加回车换行。 */
|
||||
static void ESP8266_SendCmd(const char* cmd) {
|
||||
Serial_SendString(cmd);
|
||||
Serial_SendString("\r\n");
|
||||
}
|
||||
|
||||
/* 函数作用:在指定超时时间内等待 ESP8266 返回期望关键字。 */
|
||||
static uint8_t ESP8266_WaitForAck(const char* ack, uint32_t timeout) {
|
||||
uint32_t time_start = Get_Time_ms();
|
||||
uint32_t time_elapsed;
|
||||
@@ -57,11 +59,13 @@ static uint8_t ESP8266_WaitForAck(const char* ack, uint32_t timeout) {
|
||||
return ESP8266_FAIL;
|
||||
}
|
||||
|
||||
/* 函数作用:判断指定年份是否为闰年。 */
|
||||
static uint8_t IsLeapYear(uint16_t year)
|
||||
{
|
||||
return ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)) ? 1 : 0;
|
||||
}
|
||||
|
||||
/* 函数作用:返回指定年份月份的天数。 */
|
||||
static uint8_t DaysInMonth(uint16_t year, uint8_t month)
|
||||
{
|
||||
static const uint8_t days_table[12] = {31,28,31,30,31,30,31,31,30,31,30,31};
|
||||
@@ -69,6 +73,7 @@ static uint8_t DaysInMonth(uint16_t year, uint8_t month)
|
||||
return days_table[month - 1];
|
||||
}
|
||||
|
||||
/* 函数作用:把 Unix 时间戳转换为北京时间结构体。 */
|
||||
static void EpochToDateTimeUTC8(uint32_t epoch_seconds, ESP8266_DateTime* datetime)
|
||||
{
|
||||
// 转为北京时间(UTC+8)
|
||||
@@ -99,6 +104,7 @@ static void EpochToDateTimeUTC8(uint32_t epoch_seconds, ESP8266_DateTime* dateti
|
||||
datetime->day = (uint8_t)(days + 1U);
|
||||
}
|
||||
|
||||
/* 函数作用:从 HTTP 响应中复制正文,并在响应加密时完成解密。 */
|
||||
static uint8_t ESP8266_CopyDecryptedBody(char* response,
|
||||
char* body_start,
|
||||
uint16_t body_len,
|
||||
@@ -137,6 +143,7 @@ static uint8_t ESP8266_CopyDecryptedBody(char* response,
|
||||
|
||||
/* --- 公共接口函数实现 --- */
|
||||
|
||||
/* 函数作用:初始化 ESP8266 AT 模式、WiFi 模式和 TCP 单连接模式。 */
|
||||
uint8_t ESP8266_Setup(void)
|
||||
{
|
||||
ESP8266_SendCmd("+++");
|
||||
@@ -159,6 +166,7 @@ uint8_t ESP8266_Setup(void)
|
||||
return ESP8266_SUCCESS;
|
||||
}
|
||||
|
||||
/* 函数作用:连接指定 SSID 和密码的 WiFi 热点。 */
|
||||
uint8_t ESP8266_ConnectWiFi(const char* ssid, const char* password)
|
||||
{
|
||||
char cmd_buffer[128];
|
||||
@@ -172,6 +180,7 @@ uint8_t ESP8266_ConnectWiFi(const char* ssid, const char* password)
|
||||
/**
|
||||
* @brief 连接到HTTP服务器(新增)
|
||||
*/
|
||||
/* 函数作用:与服务端建立 TCP 连接,为后续 HTTP 请求做准备。 */
|
||||
uint8_t ESP8266_ConnectHTTPServer(const char* ip, const char* port)
|
||||
{
|
||||
char cmd_buffer[100];
|
||||
@@ -215,6 +224,7 @@ uint8_t ESP8266_ConnectHTTPServer(const char* ip, const char* port)
|
||||
/**
|
||||
* @brief 发送加密数据(HTTP POST请求)
|
||||
*/
|
||||
/* 函数作用:通过 HTTP POST 上传 AES 加密后的考勤 JSON 数据。 */
|
||||
uint8_t ESP8266_SendEncryptedData(const char* encrypted_data)
|
||||
{
|
||||
char http_request[1024];
|
||||
@@ -353,6 +363,7 @@ uint8_t ESP8266_SendEncryptedData(const char* encrypted_data)
|
||||
return ESP8266_FAIL;
|
||||
}
|
||||
|
||||
/* 函数作用:从服务端时间接口获取 Unix 时间戳并转换为本地时间。 */
|
||||
uint8_t ESP8266_GetNetworkTime(ESP8266_DateTime* datetime)
|
||||
{
|
||||
char response_buffer[256];
|
||||
@@ -405,6 +416,7 @@ uint8_t ESP8266_GetNetworkTime(ESP8266_DateTime* datetime)
|
||||
return ESP8266_SUCCESS;
|
||||
}
|
||||
|
||||
/* 函数作用:查询 ESP8266 当前 TCP/WiFi 连接状态。 */
|
||||
uint8_t ESP8266_CheckConnection(void)
|
||||
{
|
||||
char* response;
|
||||
@@ -442,6 +454,7 @@ uint8_t ESP8266_CheckConnection(void)
|
||||
/**
|
||||
* @brief 发送HTTP GET请求并接收响应
|
||||
*/
|
||||
/* 函数作用:发送带 Token 的 HTTP GET 请求,并返回解密后的响应正文。 */
|
||||
uint8_t ESP8266_HttpGet(const char* path, char* response_buffer, uint16_t buffer_size)
|
||||
{
|
||||
char http_request[320];
|
||||
@@ -592,6 +605,7 @@ uint8_t ESP8266_HttpGet(const char* path, char* response_buffer, uint16_t buffer
|
||||
* @param slot_end 时段结束时间(格式"HH:MM")
|
||||
* @retval 1 表示已签到(重复), 0 表示未签到
|
||||
*/
|
||||
/* 函数作用:向服务器查询指定学生在给定时段内是否重复签到。 */
|
||||
uint8_t ESP8266_CheckDuplicate(const char* student_id, const char* slot_start, const char* slot_end)
|
||||
{
|
||||
char path[128];
|
||||
@@ -639,6 +653,7 @@ uint8_t ESP8266_CheckDuplicate(const char* student_id, const char* slot_start, c
|
||||
* @param buffer_size 缓冲区大小
|
||||
* @retval 成功下载的学号数量
|
||||
*/
|
||||
/* 函数作用:下载指定时间段内今日已签到学生列表,用于本地查重缓存。 */
|
||||
uint8_t ESP8266_DownloadTodayRecords(const char* slot_start, const char* slot_end, char* student_ids, uint16_t buffer_size)
|
||||
{
|
||||
char path[128];
|
||||
|
||||
@@ -25,6 +25,7 @@ typedef struct {
|
||||
* @brief 对ESP8266模块进行基础设置
|
||||
* @retval 1 表示成功, 0 表示失败
|
||||
*/
|
||||
// 函数作用:初始化 ESP8266 模块并配置基础 AT 工作模式。
|
||||
uint8_t ESP8266_Setup(void);
|
||||
|
||||
/**
|
||||
@@ -33,6 +34,7 @@ uint8_t ESP8266_Setup(void);
|
||||
* @param password WiFi的密码
|
||||
* @retval 1 表示成功, 0 表示失败
|
||||
*/
|
||||
// 函数作用:控制 ESP8266 连接指定 WiFi 网络。
|
||||
uint8_t ESP8266_ConnectWiFi(const char* ssid, const char* password);
|
||||
|
||||
/**
|
||||
@@ -41,6 +43,7 @@ uint8_t ESP8266_ConnectWiFi(const char* ssid, const char* password);
|
||||
* @param port 服务器的端口号
|
||||
* @retval 1 表示成功, 0 表示失败
|
||||
*/
|
||||
// 函数作用:控制 ESP8266 与指定 HTTP 服务器建立 TCP 连接。
|
||||
uint8_t ESP8266_ConnectHTTPServer(const char* ip, const char* port);
|
||||
|
||||
/**
|
||||
@@ -48,12 +51,14 @@ uint8_t ESP8266_ConnectHTTPServer(const char* ip, const char* port);
|
||||
* @param encrypted_data 加密后的Base64数据
|
||||
* @retval 1 表示成功, 0 表示失败
|
||||
*/
|
||||
// 函数作用:通过 ESP8266 发送加密后的考勤 HTTP POST 数据。
|
||||
uint8_t ESP8266_SendEncryptedData(const char* encrypted_data);
|
||||
|
||||
/**
|
||||
* @brief 检查TCP连接是否正常
|
||||
* @retval 1 表示已连接, 0 表示未连接
|
||||
*/
|
||||
// 函数作用:检查 ESP8266 当前 TCP/网络连接是否仍然可用。
|
||||
uint8_t ESP8266_CheckConnection(void);
|
||||
|
||||
/**
|
||||
@@ -61,6 +66,7 @@ uint8_t ESP8266_CheckConnection(void);
|
||||
* @param datetime 时间结构体指针,用于存储获取的时间
|
||||
* @retval 1 表示成功, 0 表示失败
|
||||
*/
|
||||
// 函数作用:从网络时间服务或服务端接口获取当前日期时间。
|
||||
uint8_t ESP8266_GetNetworkTime(ESP8266_DateTime* datetime);
|
||||
|
||||
/**
|
||||
@@ -70,6 +76,7 @@ uint8_t ESP8266_GetNetworkTime(ESP8266_DateTime* datetime);
|
||||
* @param buffer_size 缓冲区大小
|
||||
* @retval 1 表示成功, 0 表示失败
|
||||
*/
|
||||
// 函数作用:通过 ESP8266 发起 HTTP GET 请求并把响应写入缓冲区。
|
||||
uint8_t ESP8266_HttpGet(const char* path, char* response_buffer, uint16_t buffer_size);
|
||||
|
||||
/**
|
||||
@@ -79,6 +86,7 @@ uint8_t ESP8266_HttpGet(const char* path, char* response_buffer, uint16_t buffer
|
||||
* @param slot_end 时段结束时间(格式"HH:MM")
|
||||
* @retval 1 表示已签到(重复), 0 表示未签到
|
||||
*/
|
||||
// 函数作用:调用服务端查重接口,判断学生在指定时段是否已签到。
|
||||
uint8_t ESP8266_CheckDuplicate(const char* student_id, const char* slot_start, const char* slot_end);
|
||||
|
||||
/**
|
||||
@@ -89,6 +97,7 @@ uint8_t ESP8266_CheckDuplicate(const char* student_id, const char* slot_start, c
|
||||
* @param buffer_size 缓冲区大小
|
||||
* @retval 成功下载的学号数量
|
||||
*/
|
||||
// 函数作用:下载指定时段内今日已签到学生列表,用于本地去重缓存恢复。
|
||||
uint8_t ESP8266_DownloadTodayRecords(const char* slot_start, const char* slot_end, char* student_ids, uint16_t buffer_size);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -10,6 +10,7 @@ static char debug_buffer[256];
|
||||
/**
|
||||
* @brief 初始化调试串口(USART2, PA2=TX, PA3=RX)
|
||||
*/
|
||||
/* 函数作用:初始化 USART2 调试串口并输出启动日志。 */
|
||||
void Debug_Init(void)
|
||||
{
|
||||
GPIO_InitTypeDef GPIO_InitStructure;
|
||||
@@ -52,6 +53,7 @@ void Debug_Init(void)
|
||||
/**
|
||||
* @brief 通过调试串口发送一个字节
|
||||
*/
|
||||
/* 函数作用:通过 USART2 发送单个调试字节。 */
|
||||
static void Debug_SendByte(uint8_t byte)
|
||||
{
|
||||
while (USART_GetFlagStatus(USART2, USART_FLAG_TXE) == RESET);
|
||||
@@ -61,6 +63,7 @@ static void Debug_SendByte(uint8_t byte)
|
||||
/**
|
||||
* @brief 通过调试串口发送字符串
|
||||
*/
|
||||
/* 函数作用:通过 USART2 连续发送调试字符串。 */
|
||||
static void Debug_SendString(const char* str)
|
||||
{
|
||||
while (*str)
|
||||
@@ -72,6 +75,7 @@ static void Debug_SendString(const char* str)
|
||||
/**
|
||||
* @brief 格式化调试信息输出(类似printf)
|
||||
*/
|
||||
/* 函数作用:格式化调试信息并通过串口输出。 */
|
||||
void Debug_Printf(const char* format, ...)
|
||||
{
|
||||
va_list args;
|
||||
@@ -85,7 +89,9 @@ void Debug_Printf(const char* format, ...)
|
||||
#else
|
||||
|
||||
// 调试关闭时,提供空实现
|
||||
/* 函数作用:调试关闭时提供空初始化函数,保持接口兼容。 */
|
||||
void Debug_Init(void) {}
|
||||
/* 函数作用:调试关闭时丢弃格式化输出,保持接口兼容。 */
|
||||
void Debug_Printf(const char* format, ...) {}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -8,9 +8,11 @@
|
||||
#define DEBUG_ENABLE 1 // 暂时关闭,添加Debug.c后改回1
|
||||
|
||||
// 调试串口初始化(使用USART2,PA2/PA3)
|
||||
// 函数作用:初始化调试串口 USART2,供 DEBUG_PRINT 输出日志使用。
|
||||
void Debug_Init(void);
|
||||
|
||||
// 调试信息输出函数
|
||||
// 函数作用:按 printf 格式输出调试日志到调试串口。
|
||||
void Debug_Printf(const char* format, ...);
|
||||
|
||||
// 快捷宏定义
|
||||
|
||||
@@ -8,6 +8,7 @@ volatile uint32_t g_time_ms = 0;
|
||||
/**
|
||||
* @brief 初始化SysTick定时器,配置为1ms中断一次
|
||||
*/
|
||||
/* 函数作用:初始化 SysTick,使系统每 1ms 进入一次中断。 */
|
||||
void Delay_Init(void)
|
||||
{
|
||||
// 配置SysTick为1ms中断一次。SystemCoreClock通常是72MHz。
|
||||
@@ -22,6 +23,7 @@ void Delay_Init(void)
|
||||
* @brief 获取自启动以来的毫秒数
|
||||
* @retval 当前的毫秒时间戳
|
||||
*/
|
||||
/* 函数作用:返回系统启动以来累计的毫秒计数。 */
|
||||
uint32_t Get_Time_ms(void)
|
||||
{
|
||||
return g_time_ms;
|
||||
@@ -30,6 +32,7 @@ uint32_t Get_Time_ms(void)
|
||||
|
||||
// --- 以下是你原本就有的阻塞式延时函数,保持不变 ---
|
||||
|
||||
/* 函数作用:使用 SysTick 实现微秒级阻塞延时。 */
|
||||
void Delay_us(uint32_t xus)
|
||||
{
|
||||
SysTick->LOAD = 72 * xus;
|
||||
@@ -39,6 +42,7 @@ void Delay_us(uint32_t xus)
|
||||
SysTick->CTRL = 0x00000004;
|
||||
}
|
||||
|
||||
/* 函数作用:基于毫秒 tick 实现毫秒级阻塞延时。 */
|
||||
void Delay_ms(uint32_t xms)
|
||||
{
|
||||
// 使用SysTick计时器实现精确延时
|
||||
@@ -48,6 +52,7 @@ void Delay_ms(uint32_t xms)
|
||||
}
|
||||
}
|
||||
|
||||
/* 函数作用:通过循环调用毫秒延时实现秒级阻塞延时。 */
|
||||
void Delay_s(uint32_t xs)
|
||||
{
|
||||
while(xs--)
|
||||
|
||||
@@ -5,12 +5,17 @@
|
||||
#include "stm32f10x.h" // 确保包含了这个
|
||||
|
||||
// 添加下面两个函数的声明
|
||||
// 函数作用:初始化 SysTick 延时模块和毫秒计时基准。
|
||||
void Delay_Init(void);
|
||||
// 函数作用:返回系统启动以来经过的毫秒数。
|
||||
uint32_t Get_Time_ms(void);
|
||||
|
||||
// 保留你原有的函数声明
|
||||
// 函数作用:阻塞延时指定微秒数。
|
||||
void Delay_us(uint32_t us);
|
||||
// 函数作用:阻塞延时指定毫秒数。
|
||||
void Delay_ms(uint32_t ms);
|
||||
// 函数作用:阻塞延时指定秒数。
|
||||
void Delay_s(uint32_t s);
|
||||
|
||||
#endif
|
||||
|
||||
+18
@@ -84,11 +84,13 @@ uint32_t last_process_time = 0;
|
||||
#define PROCESS_INTERVAL_MS 2000
|
||||
|
||||
/* --- 辅助函数 --- */
|
||||
/* 函数作用:判断指定年份是否为闰年。 */
|
||||
static uint8_t IsLeapYear(uint16_t year)
|
||||
{
|
||||
return ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)) ? 1 : 0;
|
||||
}
|
||||
|
||||
/* 函数作用:返回指定年份和月份对应的天数。 */
|
||||
static uint8_t DaysInMonth(uint16_t year, uint8_t month)
|
||||
{
|
||||
static const uint8_t days_table[12] = {31,28,31,30,31,30,31,31,30,31,30,31};
|
||||
@@ -97,6 +99,7 @@ static uint8_t DaysInMonth(uint16_t year, uint8_t month)
|
||||
return days_table[month - 1];
|
||||
}
|
||||
|
||||
/* 函数作用:在没有 DS1302 可用时,用系统 tick 推进软件时钟一秒。 */
|
||||
static void AdvanceSoftwareClockOneSecond(void)
|
||||
{
|
||||
g_current_time.second++;
|
||||
@@ -122,6 +125,7 @@ static void AdvanceSoftwareClockOneSecond(void)
|
||||
g_current_time.year++;
|
||||
}
|
||||
|
||||
/* 函数作用:启动签到成功提示音和成功指示灯。 */
|
||||
void StartBeepSuccess(void)
|
||||
{
|
||||
BUZZER_OFF();
|
||||
@@ -133,6 +137,7 @@ void StartBeepSuccess(void)
|
||||
LED_SUCCESS_ON();
|
||||
}
|
||||
|
||||
/* 函数作用:启动重复签到提示音和指示灯闪烁序列。 */
|
||||
void StartBeepDuplicate(void)
|
||||
{
|
||||
BUZZER_OFF();
|
||||
@@ -144,6 +149,7 @@ void StartBeepDuplicate(void)
|
||||
LED_SUCCESS_ON();
|
||||
}
|
||||
|
||||
/* 函数作用:在主循环中推进蜂鸣器和指示灯的非阻塞提示状态。 */
|
||||
void ProcessBeep(void)
|
||||
{
|
||||
if (beep_state == BEEP_IDLE) {
|
||||
@@ -185,6 +191,7 @@ void ProcessBeep(void)
|
||||
}
|
||||
}
|
||||
|
||||
/* 函数作用:刷新空闲状态下 OLED 显示的系统状态和待同步数量。 */
|
||||
void UpdateIdleScreen(void)
|
||||
{
|
||||
OLED_Clear();
|
||||
@@ -203,6 +210,7 @@ void UpdateIdleScreen(void)
|
||||
/**
|
||||
* @brief 发送加密的考勤记录到服务器(新版本)
|
||||
*/
|
||||
/* 函数作用:将一条考勤记录组装为 JSON,加密后通过 ESP8266 上传服务器。 */
|
||||
uint8_t SendEncryptedAttendance(AttendanceRecord* record)
|
||||
{
|
||||
char json_data[256];
|
||||
@@ -244,6 +252,7 @@ uint8_t SendEncryptedAttendance(AttendanceRecord* record)
|
||||
}
|
||||
}
|
||||
|
||||
/* 函数作用:批量同步本地待上传考勤记录,并根据结果更新同步状态。 */
|
||||
void SyncPendingRecords(void)
|
||||
{
|
||||
uint32_t current_time = Get_Time_ms();
|
||||
@@ -312,6 +321,7 @@ void SyncPendingRecords(void)
|
||||
synced_count, failed_count);
|
||||
}
|
||||
|
||||
/* 函数作用:从服务器同步网络时间,并在 DS1302 可用时写入 RTC。 */
|
||||
uint8_t SyncNetworkTime(void)
|
||||
{
|
||||
DEBUG_INFO("Syncing time from network...");
|
||||
@@ -376,6 +386,7 @@ uint8_t SyncNetworkTime(void)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 函数作用:从同步状态 JSON 中提取服务端版本号。 */
|
||||
static uint32_t ParseSyncVersion(const char* json)
|
||||
{
|
||||
const char* p;
|
||||
@@ -395,6 +406,7 @@ static uint32_t ParseSyncVersion(const char* json)
|
||||
return version;
|
||||
}
|
||||
|
||||
/* 函数作用:从服务端下载 STM32 需要的同步状态并更新时间段配置。 */
|
||||
uint8_t DownloadTimeSlots(void)
|
||||
{
|
||||
char response_buffer[768];
|
||||
@@ -444,6 +456,7 @@ uint8_t DownloadTimeSlots(void)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 函数作用:按固定间隔自动从服务端刷新时间段配置。 */
|
||||
void UpdateTimeSlotConfig(void)
|
||||
{
|
||||
uint32_t current_time = Get_Time_ms();
|
||||
@@ -471,6 +484,7 @@ void UpdateTimeSlotConfig(void)
|
||||
}
|
||||
}
|
||||
|
||||
/* 函数作用:更新当前时间来源,优先读取 DS1302,失败时使用软件时钟并定期 NTP 校时。 */
|
||||
void UpdateCurrentTime(void)
|
||||
{
|
||||
uint32_t current_ms = Get_Time_ms();
|
||||
@@ -530,6 +544,7 @@ void UpdateCurrentTime(void)
|
||||
}
|
||||
}
|
||||
|
||||
/* 函数作用:按重连间隔尝试恢复 WiFi,并在成功后连接 HTTP 服务器和同步配置。 */
|
||||
uint8_t TryReconnectWiFi(void)
|
||||
{
|
||||
uint32_t current_time = Get_Time_ms();
|
||||
@@ -585,6 +600,7 @@ uint8_t TryReconnectWiFi(void)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 函数作用:按重连间隔尝试恢复 HTTP 服务器连接。 */
|
||||
uint8_t TryReconnectServer(void)
|
||||
{
|
||||
uint32_t current_time = Get_Time_ms();
|
||||
@@ -616,6 +632,7 @@ uint8_t TryReconnectServer(void)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 函数作用:周期性检查服务器连接状态,断线时触发自动重连。 */
|
||||
void CheckServerConnection(void)
|
||||
{
|
||||
uint32_t current_time = Get_Time_ms();
|
||||
@@ -653,6 +670,7 @@ void CheckServerConnection(void)
|
||||
// ======================================================================
|
||||
// 主函数
|
||||
// ======================================================================
|
||||
/* 函数作用:STM32 固件入口,完成硬件初始化并循环处理识别、考勤、联网和显示任务。 */
|
||||
int main(void)
|
||||
{
|
||||
AttendanceStatus att_status;
|
||||
|
||||
@@ -67,6 +67,7 @@
|
||||
*/
|
||||
#define assert_param(expr) ((expr) ? (void)0 : assert_failed((uint8_t *)__FILE__, __LINE__))
|
||||
/* Exported functions ------------------------------------------------------- */
|
||||
/* 函数作用:在开启断言检查时报告参数断言失败所在的文件和行号。 */
|
||||
void assert_failed(uint8_t* file, uint32_t line);
|
||||
#else
|
||||
#define assert_param(expr) ((void)0)
|
||||
|
||||
@@ -13,6 +13,7 @@ extern volatile uint32_t g_time_ms;
|
||||
* @brief SysTick中断处理函数(1ms中断一次)
|
||||
* @note 用于维护系统时间戳和延时功能
|
||||
*/
|
||||
/* 函数作用:每 1ms 递增系统毫秒计数,为延时和定时任务提供时间基准。 */
|
||||
void SysTick_Handler(void)
|
||||
{
|
||||
g_time_ms++; // 每1ms递增一次
|
||||
@@ -21,6 +22,7 @@ void SysTick_Handler(void)
|
||||
//... HardFault_Handler 等其他中断保持不变 ...
|
||||
|
||||
// USART3中断(来自K210,根据连接图.md: K210使用USART3 PB10/PB11)
|
||||
/* 函数作用:接收 K210 串口发送的人脸识别结果,并按换行符组帧。 */
|
||||
void USART3_IRQHandler(void)
|
||||
{
|
||||
if (USART_GetITStatus(USART3, USART_IT_RXNE) == SET)
|
||||
|
||||
@@ -21,18 +21,29 @@
|
||||
|
||||
/* Exported functions ------------------------------------------------------- */
|
||||
|
||||
/* 函数作用:处理不可屏蔽中断,当前项目中保留默认异常入口。 */
|
||||
void NMI_Handler(void);
|
||||
/* 函数作用:处理硬件错误异常,进入错误保护循环便于定位严重故障。 */
|
||||
void HardFault_Handler(void);
|
||||
/* 函数作用:处理内存管理异常,当前项目中保留默认异常入口。 */
|
||||
void MemManage_Handler(void);
|
||||
/* 函数作用:处理总线错误异常,当前项目中保留默认异常入口。 */
|
||||
void BusFault_Handler(void);
|
||||
/* 函数作用:处理未定义指令等用法错误异常,当前项目中保留默认异常入口。 */
|
||||
void UsageFault_Handler(void);
|
||||
/* 函数作用:处理系统服务调用异常,当前项目中保留默认异常入口。 */
|
||||
void SVC_Handler(void);
|
||||
/* 函数作用:处理调试监视异常,当前项目中保留默认异常入口。 */
|
||||
void DebugMon_Handler(void);
|
||||
/* 函数作用:处理可挂起系统服务异常,当前项目中保留默认异常入口。 */
|
||||
void PendSV_Handler(void);
|
||||
/* 函数作用:处理系统滴答定时器中断,用于毫秒计时和延时基准。 */
|
||||
void SysTick_Handler(void);
|
||||
|
||||
// 外设中断
|
||||
/* 函数作用:处理 USART1 中断,用于 ESP8266 串口通信接收。 */
|
||||
void USART1_IRQHandler(void);
|
||||
/* 函数作用:处理 USART3 中断,用于 K210 人脸识别结果接收。 */
|
||||
void USART3_IRQHandler(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
Reference in New Issue
Block a user