本文為上篇,介紹如何將 AT32F403A 微控制器搭配 DM9058 網路控制器作為 PTP End Slave 的完整環境建置流程,包括硬體連接、軟體初始化、以及平台 bring-up 的詳細步驟。
關鍵特色:
適用讀者:
在現代工業自動化、測試測量、音視頻傳輸等領域,多個設備之間的高精度時間同步至關重要。傳統的 NTP(Network Time Protocol)只能提供毫秒級精度,無法滿足許多應用需求。
IEEE 1588 PTP 能夠實現:
建立一個完整可驗證的 PTP 時間同步系統,並著重介紹 AT32F403A 作為 End Slave 的實作細節,包括:
本文使用以下固定拓撲(Topology)進行實作:
| 層次 | 設備 | 角色 | 協定 |
|---|---|---|---|
| 時間源 | Raspberry Pi 3 + DM9058 | Grandmaster (GM) | PTPv2 One-Step, UDP/IPv4 |
| 網路中繼 | Microchip KSZ9477 | E2E Transparent Clock (TC) | 硬體 PTP,CF 自動修正 |
| 同步目標 | AT32F403A + DM9058 | End Slave (ES) | PTPv2 One-Step, UDP/IPv4 |
文章重點分配:
以下圖展示完整的 PTP 時間同步拓撲(Topology),包含 Sync 與 Delay 訊息的雙向流向。

圖說:
Sync 在透明時鐘(TC)內部的駐留時間(Sync Residence Time)。Delay_Req 在透明時鐘(TC)內部的駐留時間(Delay Residence Time)。Sync 封包進入 TC 的入埠時間戳(Ingress) 與離開 TC 的出埠時間戳(Egress) 之差,即 S1 = Egress - Ingress。TC 會把 S1 累加到該 Sync 的 CorrectionField(Forward path 修正)。Delay_Req 封包進入 TC 的入埠時間戳(Ingress) 與離開 TC 的出埠時間戳(Egress) 之差,即 S2 = Egress - Ingress。TC 會把 S2 累加到該封包的 CorrectionField(Reverse path 修正),讓後續計算能把「交換器內部造成的額外延遲」扣除掉,避免把它誤當成線路傳播延遲。以下圖展示完整的 PTP 時間同步拓撲(Topology),包含一個 Grandmaster、一個 Transparent Clock,以及兩個 End Slave 的架構。

圖說:
多 Slave 拓撲(Topology)特點:
PTP One-Step 模式的關鍵是時間戳記直接嵌入 Sync 訊息,因此本專案不使用 Two-Step 訊息。
四步時間交換:
Slave 計算 Offset:
Offset = (t2 - t1 - CF_sync) - Mean_Path_Delay
Mean_Path_Delay = [(t2-t1-CF_sync) + (t4-t3-CF_delay)] / 2
E2E Transparent Clock 的核心功能是**累加網路駐留時間(Residence Time)**到 Correction Field。
CF 修正流程:
訊息進入 TC → 記錄 Ingress 時間 → 處理與轉發 → 記錄 Egress 時間
→ 計算 Residence Time (RT = Egress - Ingress)
→ 更新 CF (CF_new = CF_old + RT) → 轉發修正後的訊息
修正範例:
| 訊息 | 進入 TC 時 CF | TC Residence | 離開 TC 時 CF |
|---|---|---|---|
| Sync (GM→ES) | 0 ns | 350 ns | 350 ns |
| Delay_Req (ES→GM) | 0 ns | 380 ns | 380 ns |
| Delay_Resp (GM→ES) | 0 ns | 340 ns | 340 ns |
重要:Slave 在計算 Offset 時必須考慮所有 CF 修正值,才能獲得準確的時間偏移量。
角色:提供標準時間源(Master Clock)
硬體配置:
ptp4l)設定重點:
# RPi3 上的 ptp4l 配置
sudo ptp4l -i eth0 -m -S -l 6 -f master_config.cfg
master_config.cfg:
[global]
clockClass 0
priority1 128
slaveOnly 0
twoStepFlag 0 # One-Step 模式
network_transport UDPv4
time_stamping hardware # 硬體時間戳記
delay_mechanism E2E # End-to-End 延遲機制
角色:E2E TC,自動修正網路延遲
特性:
配置方式:
透過交換機管理介面啟用 PTP E2E TC 模式,具體步驟依廠商文件而定。
核心規格:
核心規格:
為什麼選擇 DM9058?
接腳連接表(⭐ 實際專案配置):
| AT32F403A Pin | 功能 | DM9058 Pin | 說明 |
|---|---|---|---|
| PA5 | SPI1_SCK | SCK | SPI 時鐘(25 MHz,可配置) |
| PA6 | SPI1_MISO | MISO | 主機輸入(Master In Slave Out) |
| PA7 | SPI1_MOSI | MOSI | 主機輸出(Master Out Slave In) |
| PA15 | SPI1_CS | CS | 片選(GPIO 控制,低電位有效) |
| PC7 | GPIO EXINT7 | INT | 外部中斷 7(下降沿觸發) |
| GND | Ground | GND | 共地 |
| 3.3V | Power | VCC | 電源供應 |
硬體架構示意圖:

圖說明:
本專案採用分層架構設計,從應用層到硬體層清晰分離,各層透過定義良好的介面進行通訊。

圖說:
本章節僅提供概略導讀:先告訴你「每一層主要看哪些檔案」,以及遇到問題時該從哪裡往下追。
| 層次 | 代表模組/檔案 | 主要責任 |
|---|---|---|
| 應用層 | utilities/sc0101_ptp_daemon/src/main.c |
系統初始化、主迴圈排程、PTP daemon 啟停(等待網路就緒後初始化) |
| PTP 協定層(ptpd) | middlewares/3rd_party/ptpd-2.0.0/src/ptpd.cprotocol.cdep/servo.cbmc.c |
協定狀態機、PTP 訊息處理、Clock Servo 校時(PI)、BMC 選主 |
| 網路層(lwIP 整合) | middlewares/3rd_party/ptpd-2.0.0/src/dep/net.cutilities/dm9058_u2510_if/ethif.c |
UDP 319/320 收發、把硬體時間戳透過 pbuf 傳遞到協定層 |
| 驅動層(DM9058) | utilities/dm9058_edriver_v1.6.1a/core/dm9058_beta.c.../dm9058_ptp.cutilities/dm9058_u2510_if/.../devif_ptp.c |
DM9058 初始化、PTP 時鐘(get/set/adjfreq)、TX/RX 硬體時間戳擷取與封包關聯 |
| HAL/硬體層 | utilities/dm9058_edriver_v1.6.1a/hal/*libraries/drivers/* |
SPI/DMA、EXINT 中斷、時鐘/定時器等底層硬體抽象 |
主迴圈的概念:
lwip_rx_loop_handler()、ptp_daemon_state_machine()、lwip_periodic_handle() 這類呼叫在主迴圈中反覆出現。除錯建議(從上往下追):
ethif.c / devif_ptp.c(是否收得到、是否判定為 PTP)dep/net.c(UDP 319/320 是否 bind、是否把 pbuf 丟給 ptpd)dep/servo.c 與 dm9058_ptp.c(時間戳是否正確、校時介面是否生效)servo.c dm9058_ptp.c
──────── ────────────
getTime() ──────────────> dm9058_ptptime_gettime()
setTime() ──────────────> dm9058_ptptime_settime()
updateTime() ──────────────> dm9058_ptptime_updateoffset()
adjFreq() ──────────────> v51_ptp_time_adj_freq()
pbuf->time_sec/nsec → dep/net.c 把它交給 ptpd 協定處理(handle*())。想追完整 RX/TX 呼叫鏈時,再回頭看
devif_ptp.c(封包識別/時間戳關聯)與dm9058_ptp.c(PTP clock + timestamp)。
at32f403a_dm9051a_ptp_daemon_trace/
├── project/ # 範例工程(BSP/board 支援檔)
├── utilities/ # 應用程式 + DM9058 驅動(主要工作區)
│ ├── sc0101_ptp_daemon/ # PTP 主程式(main / 網路啟動流程)
│ │ └── mdk_v5/ # Keil MDK 專案檔(建置入口:*.uvprojx)
│ ├── dm9058_edriver_v1.6.1a/ # DM9058 驅動(含 PTP clock + timestamp)
│ └── dm9058_u2510_if/ # lwIP netif 與 PTP 封包關聯
├── middlewares/ # lwIP + ptpd(協定核心)
│ ├── lwip_2.1.2/
│ └── 3rd_party/ptpd-2.0.0/
├── libraries/ # AT32 官方 HAL/SDK
└── docs/ # 文件與圖表
快速定位(建議只記這幾個):
utilities/sc0101_ptp_daemon/src/main.c
middlewares/3rd_party/ptpd-2.0.0/src/
utilities/dm9058_edriver_v1.6.1a/dm9058_edriver_extend/dm9058_ptp.c
utilities/dm9058_u2510_if/ethif.c、utilities/dm9058_u2510_if/dm9058_edriver_extend/devif_ptp.c
⭐ 本章節為文章重點,提供完整的環境建置流程與程式碼範例。
1. IDE 與編譯器
2. 調試與燒錄工具
3. 軟體套件
清單:
步驟 1:SPI 接線
| 從 AT32F403A | 到 DM9058 | 注意事項 |
|---|---|---|
| PA5 | SCK | 時鐘線,需短且直 |
| PA6 | MISO | 主機輸入 |
| PA7 | MOSI | 主機輸出 |
| PA15 | CS | 片選,低電位有效 |
步驟 2:中斷接線
| 從 AT32F403A | 到 DM9058 | 說明 |
|---|---|---|
| PC7 | INT | 下降沿觸發,需上拉 |
步驟 3:電源與地線
| 從 AT32F403A | 到 DM9058 |
|---|---|
| 3.3V | VCC |
| GND | GND |
⚠️ 重要:確保所有連接牢固,特別是 SPI 時鐘線和地線。不良連接會導致 SPI 通訊錯誤!
步驟 1:開啟專案
開啟檔案:utilities/sc0101_ptp_daemon/mdk_v5/ptp_daemon_f403a.uvprojx
步驟 2:配置 Target
AT32F403ACGT7
CMSIS-DAP Debugger
AT-Link-EZ CMSIS-DAP
SW,Max Clock: 1MHz
步驟 3:編譯
選單 → Project → Build Target (F7)
步驟 4:燒錄
選單 → Flash → Download (F8)
本專案的初始化分為四個主要部分:系統時鐘、SPI 通訊、外部中斷、DM9058 驅動。以下以文字說明配置要點,完整程式碼請參考 GitHub 專案。
配置檔案:utilities/sc0101_ptp_daemon/src/at32f403a_407_clock.c
時鐘來源與 PLL 配置:
匯流排時鐘分配:
| 匯流排 | 分頻器 | 頻率 | 用途 |
|---|---|---|---|
| AHB | ÷1 | 200 MHz | CPU 核心 |
| APB1 | ÷2 | 100 MHz | 低速周邊 |
| APB2 | ÷2 | 100 MHz | 高速周邊(SPI1) |
時鐘樹:
HSE (8MHz) → PLL (*50÷2) → SYSCLK (200MHz)
↓
┌───────────────────┼───────────────────┐
↓ ↓ ↓
AHB (÷1, 200MHz) APB1 (÷2, 100MHz) APB2 (÷2, 100MHz)
↓ ↓ ↓
Core 低速周邊 SPI1 (25MHz)
關鍵配置函式:
crm_pll_config(CRM_PLL_SOURCE_HEXT_DIV, CRM_PLL_MULT_50, CRM_PLL_OUTPUT_RANGE_GT72MHZ) - 配置 PLL 倍頻 ×50crm_ahb_div_set(CRM_AHB_DIV_1) - 設定 AHB 分頻器 ÷1crm_apb1_div_set(CRM_APB1_DIV_2) - 設定 APB1 分頻器 ÷2crm_apb2_div_set(CRM_APB2_DIV_2) - 設定 APB2 分頻器 ÷2(影響 SPI 時鐘)配置檔案:utilities/dm9058_edriver_v1.6.1a/hal/SPI1/at32f403a_spi1_dma.c
SPI 參數配置:
| 參數 | 設定值 | 說明 |
|---|---|---|
| 模式 | Master | AT32F403A 為主機 |
| 時鐘速度 | 25 MHz | APB2 (100MHz) ÷ 4 |
| 數據寬度 | 8-bit | 單字節傳輸 |
| 時鐘極性(CPOL) | Low (0) | 空閒時時鐘為低電平 |
| 時鐘相位(CPHA) | 1 Edge (0) | 第一個邊沿採樣 |
| CS 控制 | 軟體模式 | GPIO PA15 控制 |
| 傳輸方向 | 全雙工 | 同時收發 |
GPIO 配置:
特殊配置:
// AT32F403A 需釋放 PA15 的 JTAG 功能
gpio_pin_remap_config(SWJTAG_GMUX_010, TRUE);
SPI 通訊流程:
配置檔案:at32f403a_int7.c
中斷參數配置:
| 參數 | 設定值 | 說明 |
|---|---|---|
| GPIO 腳位 | PC7 | 連接 DM9058 INT 信號 |
| 中斷線 | EXINT_LINE_7 | 外部中斷線 7 |
| 觸發方式 | 下降沿 | DM9058 INT 為低電位有效 |
| GPIO 模式 | 輸入 + 上拉 | 空閒時保持高電平 |
| 中斷向量 | EXINT9_5_IRQn | EXINT5-9 共用 |
| 優先級組 | NVIC_PRIORITY_GROUP_4 | 4 位 preemption priority |
中斷處理流程:
dm9058_interrupt_event = 1(通知主迴圈)中斷事件:
配置檔案:dm9058_beta.c
初始化步驟:
晶片 ID 驗證
DM9058_PIDL
MAC 位址配置
PTP 功能啟用(依晶片版本而定)
初始化成功標誌:
[DRIVER INT mode] DM9058 found: 9058
PTP Transport: UDP over IPv4
LWIP: Network interface added successfully
LWIP: Getting MAC address from DM9058...
reg mac 00 60 6e e2 87 6b
LWIP: Network interface MAC address updated
LWIP: Setting default network interface...
💡 技巧:完整的初始化程式碼(含錯誤處理、DMA 配置等)約 500 行,建議直接參考專案原始碼。本節僅列出關鍵配置參數,幫助理解整體架構。
PTP 協定參數來自 PTPd 的常數與 ptpClock->defaultDS,於 PTPd 初始化時由 initData() 寫入。
實際配置來源:
| 參數概念 | 實際位置 | 說明 |
|---|---|---|
| One-Step / Two-Step | constants.h → DEFAULT_TWO_STEP_FLAG |
FALSE = One-Step(時間戳直接嵌入 Sync) |
| 僅 Slave 模式 | constants.h → SLAVE_ONLY |
TRUE = 僅從時鐘,不參與 BMC 競選 Master |
| 延遲機制 | constants.h → DEFAULT_DELAY_MECHANISM |
E2E |
| Sync 間隔 | constants.h → DEFAULT_SYNC_INTERVAL |
0 → 2^0 = 1 秒 |
| twoStepFlag 寫入 | bmc.c → initData() |
ptpClock->defaultDS.twoStepFlag = DEFAULT_TWO_STEP_FLAG; |
| DefaultDS 結構 | datatypes.h → DefaultDS |
含 twoStepFlag、slaveOnly、domainNumber 等 |
constants.h 摘錄(middlewares/3rd_party/ptpd-2.0.0/src/constants.h):
#define DEFAULT_TWO_STEP_FLAG FALSE /* One-Step:僅 SYNC,時間戳在 Sync 內 */
#define SLAVE_ONLY TRUE /* 僅 Slave 模式 */
#define DEFAULT_DELAY_MECHANISM E2E
#define DEFAULT_SYNC_INTERVAL 0 /* 2^0 = 1 秒 */
bmc.c 摘錄(initData() 內):
ptpClock->defaultDS.twoStepFlag = DEFAULT_TWO_STEP_FLAG;
main.c 主要流程(參考 utilities/sc0101_ptp_daemon/src/main.c):
int main(void)
{
/* 1. 系統初始化 */
system_clock_config(); // 200MHz 時鐘
at32_board_init();
uart_print_init(115200);
print_version_info();
nvic_priority_group_config(NVIC_PRIORITY_GROUP_4);
/* 2. 網路驅動初始化 */
emac_tmr_init(); // TMR6 用於 local_time 更新
tcpip_stack_init(); // lwIP 協定棧
printf("[MAIN] Waiting for network connection...\r\n");
/* 3. 主迴圈 */
for (;;)
{
lwip_rx_loop_handler(); // 網路封包接收
ptp_daemon_state_machine(); // PTP 狀態機(DHCP 後啟動)
lwip_periodic_handle(local_time); /* lwIP timeout handle - 包含 PTP 協定計時器 */
}
}
PTP Daemon 狀態機(簡化版):
/* 狀態:WAITING_NETWORK → INITIALIZING → RUNNING */
static void ptp_daemon_state_machine(void)
{
switch (ptp_daemon_state)
{
case PTP_STATE_WAITING_NETWORK:
if (network_dhcp_is_bound()) {
ptp_daemon_state = PTP_STATE_INITIALIZING;
}
break;
case PTP_STATE_INITIALIZING:
if (PTPd_Init() == 0) {
ptp_daemon_state = PTP_STATE_RUNNING;
ptp_rtc_sync_enable(true);
}
break;
case PTP_STATE_RUNNING:
ptpd_Periodic_Handle(local_time);
break;
}
}
完整代碼:請參考
utilities/sc0101_ptp_daemon/src/main.c,包含錯誤處理與重試機制。
正常編譯後,輸出應類似:
Build target 'AT32F403A_PTP'
compiling main.c...
compiling dm9058_beta.c...
linking...
Program Size: Code=124568 RO-data=8756 RW-data=2048 ZI-data=51200
".\Objects\ptp_daemon.axf" - 0 Error(s), 0 Warning(s).
步驟 1:連接 UART(115200 baud, 8N1)
步驟 2:燒錄韌體
步驟 3:觀察序列埠輸出
========================================
AT32F403A DM9058 PTP Daemon
========================================
Version: 2.0.0
Build Date: Jan 23 2026
Build Time: 13:48:28
Git Commit: 0dda3de35c76
Git Date: Fri Jan 23 2026 13:40:33 GMT+08:00 (CST)
========================================
[INIT] Configuring TMR6 for local_time updates
LWIP: Initializing lwIP stack...
LWIP: DHCP mode - setting IP addresses to 0.0.0.0
[AT32F403a]
tcpip_stack_init
[SPI Instance]
at32f403a_spi1_dma.c
AT32F403A ETHERNET SPI1 DMA
[SPI Pins]
sck/mosi/miso/ pa5/pa7/pa6, cs/ pa15
[DRIVER init] AT32F403A INT Running...
[DRIVER init] AT32F403A INTERRUPT GPIO
[DRIVER init] int/ pc7
[hal] at32f403a_spi1 'spi' Running 25Mhz...
[core] dm9058_constants 'INT' Running...
(apb2_freq) 100Mhz, set SPI CLK 25Mhz
[DRIVER INT mode] CLK:200000000(sclk_freq) 200000000 100000000(apb2_freq) 100000000(apb1_freq)
[DRIVER INT mode] SPI CLK use apb2_freq 100Mhz, set to 25Mhz
[DRIVER INT mode] DM9058 found: 9058
PTP Transport: UDP over IPv4
****** dm9058 Rate: +0x00000000 (+0) - Clock adjusted to be faster
LWIP: Network interface added successfully
LWIP: Getting MAC address from DM9058...
reg mac 00 60 6e e2 87 6b
LWIP: Network interface MAC address updated
LWIP: Setting default network interface...
LWIP: Bringing up network interface...
LWIP: Setting link callback...
LWIP: Checking initial link status...
LWIP: Link is UP - network ready
LWIP: Network stack initialization completed
[INIT] System initialization completed
[INIT] Waiting for network connection...
DHCP: No DHCP client data available
Link: State changed from UNKNOWN to UP
如果看到以上輸出,恭喜!AT32F403A + DM9058 硬體平台已準備就緒。
以下為本專案實際使用的硬體平台實體設置圖,展示了完整的 PTP 時間同步測試環境:

硬體配置說明:
本實驗平台主要由三個核心元件組成,透過乙太網路線和信號跳線連接,構成一個完整的 PTP 時間同步驗證系統:
Raspberry Pi 3 + DM9058(左上角)
Microchip KSZ9477 乙太網路交換器(右側中央)
AT32F403A + DM9058(左下角)
系統拓撲(Topology)說明:
此設置旨在驗證 AT32F403A 和 Raspberry Pi 搭配 DM9058 乙太網路模組,在通過 Microchip KSZ9477 交換機連接的網路環境下,實現 PTP 精確時間同步的功能及效能。
這一篇的目標,是把「AT32F403A + DM9058 + lwIP + PTP daemon」這條鏈路先跑起來、再跑穩:
到這一步,已經完成工程現場最困難的部份:把平台 bring-up 變成可運作的流程。
在中篇中,我們將深入探討:
PTP 系統運行成果展示:
時間同步精度驗證:
常見問題與除錯技巧:
系統整合測試:
中篇將幫助您:
在下篇中,我們將深入探討:
系統整合與測試:
兩點抓包驗證 TC CorrectionField(⭐ 下篇核心):
工程實作導向的驗證方法:
下篇將幫助您:
下篇定位:工程實作導向(可重現、可量化、可除錯),使用「兩點抓包」證明
correctionField在路徑中被累加。
| 中文 | 英文 | 縮寫 | 說明 |
|---|---|---|---|
| 拓撲 | Topology | - | 網路設備的連接結構 |
| 精確時間協定 | Precision Time Protocol | PTP | IEEE 1588 標準 |
| 透明時鐘 | Transparent Clock | TC | 修正網路延遲的中間設備 |
| 修正欄位 | Correction Field | CF | PTP 訊息中累加延遲的欄位 |
| 駐留時間 | Residence Time | - | 訊息在 TC 內部的停留時間 |
| 主時鐘 | Grandmaster | GM | 提供時間源的 PTP Master |
| 從時鐘 | Slave Clock | - | 同步到 Master 的設備 |
| 時間戳記 | Timestamp | - | 精確的時間記錄 |
| 偏移量 | Offset | - | Slave 與 Master 的時間差 |
| 單步模式 | One-Step | - | 時間戳記直接嵌入 Sync 訊息 |
| 端對端 | End-to-End | E2E | 延遲測量機制 |
SC0101_AT32F407_437_PTP_Daemon_V2.0.0.zip(本專案以其 PTP daemon 整合架構為基礎,移植到 AT32F403A 平台)SC0101_AT32F407_437_PTP_Daemon_V2.0.0.zip(移植基礎)dm9058_edriver_v1.6.1a
下一篇:AT32F403A + DM9058 實現高精度 PTP 時間同步(中篇):成果展示與驗證