时间同步已经完成

This commit is contained in:
2026-08-28 16:42:44 +08:00
parent 615a9b13e9
commit 4e9ac0d9c3
20 changed files with 771 additions and 1282 deletions

138
App/time_sync.c Normal file
View File

@@ -0,0 +1,138 @@
/*
* 模块名称Time Sync — PC 本地时间推送接收TCP
* 模块功能:在独立任务 timeSyncTask 中以 TCP Server 方式监听端口,接收 PC 推送的本地时间包,
* 调用 sys_clock_set_unix() 更新系统墙钟并写回 SD2506 RTC
* 适用平台STM32F407ZGTx + CH395F
* 作者:王建锋
* 创建日期2026-08-27
* 修改记录:
* v1.0 2026-08-27 王建锋 创建初始版本UDP 推送netTask 内轮询)
* v1.1 2026-08-28 王建锋 改为 TCP Server迁入独立任务 timeSyncTask
* 所有 socket 操作经 net_socket 线程安全 API 走消息队列,
* netTask 仅负责 net_poll/net_process_messages多任务并发安全
*/
#include <string.h>
#include "time_sync.h"
#include "net_socket.h"
#include "sys_clock.h"
#define DBG_TAG "[TIME_SYNC]"
#include "dbg_log.h"
/* 监听端口PC 推送端TCP 客户端)需连到本端口 */
#define TIME_SYNC_PORT 8888
/*
* 函数功能:创建并监听 TCP 时间同步服务端口
* 入口参数:无
* 返回值:监听 Socket 描述符(>=0失败返回 -1
* 限定条件net_init() 已成功;可在非 netTask 上下文调用
* 函数说明net_socket/net_bind/net_listen 均为线程安全 API内部经消息队列由 netTask 串行处理。
* 单连接模式下监听 Socket 在收到连接后会转为数据通道,连接关闭后即被释放,
* 因此每次调用都会新建监听 Socket与 lftpd 同模式),失败自动重试。
*/
static int time_sync_open_listener(void)
{
int retry;
int s = -1;
for (retry = 0; retry < 5; retry++) {
s = net_socket(NET_AF_INET, NET_SOCK_STREAM);
if (s < 0) {
DBG_ERROR("time_sync: net_socket FAIL");
vTaskDelay(pdMS_TO_TICKS(500));
continue;
}
struct net_sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = NET_AF_INET;
addr.sin_port = net_htons(TIME_SYNC_PORT);
addr.sin_addr.s_addr = NET_INADDR_ANY;
if (net_bind(s, (struct net_sockaddr *)&addr, sizeof(addr)) != 0) {
DBG_ERROR("time_sync: bind port %d FAIL", TIME_SYNC_PORT);
net_close(s);
vTaskDelay(pdMS_TO_TICKS(2000));
continue;
}
if (net_listen(s) != 0) {
DBG_ERROR("time_sync: listen FAIL");
net_close(s);
vTaskDelay(pdMS_TO_TICKS(2000));
continue;
}
DBG_INFO("time_sync: TCP listen on :%d (sock=%d)", TIME_SYNC_PORT, s);
return s;
}
return -1;
}
/*
* 函数功能:处理一个已建立的时间同步连接
* 入口参数conn_sock - 已建立的连接 Socket 描述符(单连接模式下即监听 Socket 自身)
* 返回值:无
* 函数说明:阻塞读取 PC 发来的时间包("TIME"+uint32 大端 本地时间),解析后写回 RTC然后关闭连接
*/
static void time_sync_handle_conn(int conn_sock)
{
uint8_t buf[16];
int len;
len = net_recv(conn_sock, buf, sizeof(buf), 0);
if (len >= 8) {
if (buf[0] == 'T' && buf[1] == 'I' && buf[2] == 'M' && buf[3] == 'E') {
uint32_t epoch = ((uint32_t)buf[4] << 24) |
((uint32_t)buf[5] << 16) |
((uint32_t)buf[6] << 8) |
((uint32_t)buf[7]);
sys_clock_set_unix(epoch);
DBG_INFO("time_sync: wall clock updated (epoch=%lu)", (unsigned long)epoch);
}
}
net_close(conn_sock);
}
/*
* 函数功能:时间同步任务主体(独立 FreeRTOS 任务)
* 入口参数argument - 未使用
* 返回值:无
* 限定条件net_init() 已由 netTask 完成netTask 正在处理消息队列
* 函数说明:等待网络栈就绪后,循环:建监听 -> 等连接 -> 处理 -> 关闭 -> 重建监听。
* 全部 socket 操作经消息队列由 netTask 串行执行,与本任务/其它网络任务并发安全。
*/
void timeSyncTask(void *argument)
{
(void)argument;
/* 等网络栈起来(与 StartFtpTask 的 7s 延迟同风格,确保 netTask 已开始处理消息) */
vTaskDelay(pdMS_TO_TICKS(8000));
for (;;) {
int ls = time_sync_open_listener();
if (ls < 0) {
DBG_ERROR("time_sync: open listener FAIL, retry later");
vTaskDelay(pdMS_TO_TICKS(2000));
continue;
}
/* 等待 PC 连接net_accept 在无连接时立即返回 -1循环重试同一监听 Socket */
int conn = -1;
do {
conn = net_accept(ls, NULL, NULL);
if (conn < 0) {
vTaskDelay(pdMS_TO_TICKS(200));
}
} while (conn < 0);
time_sync_handle_conn(conn);
/* conn单连接模式下即 ls已被 net_close 释放,下次循环重建监听 */
vTaskDelay(pdMS_TO_TICKS(50));
}
}