rfid nxp iso14443 的 編碼方式
https://ww1.microchip.com/downloads/en/devicedoc/doc2056.pdf
import tkinter as tk
from tkinter import ttk, messagebox
# 建立主視窗
root = tk.Tk()
root.title("ISO 14443 Type A 規範與訊號模擬教學")
root.geometry("800x700")
root.configure(bg="#f5f5f5")
# 設定樣式
style = ttk.Style()
style.configure("TLabel", background="#f5f5f5", font=("Microsoft JhengHei", 12))
style.configure("Header.TLabel", font=("Microsoft JhengHei", 16, "bold"), foreground="#003366")
style.configure("Action.TButton", font=("Microsoft JhengHei", 11, "bold"))
# ==================== 上半部:保留原先的規範說明 ====================
title_label = ttk.Label(root, text="RFID / NFC - ISO 14443 Type A 標準與訊號模擬", style="Header.TLabel")
title_label.pack(pady=15)
intro_text = "• Type A 是 NXP 最具代表性的標準(廣泛應用於 MIFARE Classic, Ultralight 等)。"
intro_label = ttk.Label(root, text=intro_text, wraplength=700, justify="left", foreground="#333333")
intro_label.pack(pady=5, padx=20, anchor="w")
ttk.Separator(root, orient='horizontal').pack(fill='x', padx=20, pady=10)
display_frame = tk.LabelFrame(root, text="靜態規範說明(點選按鈕切換)", font=("Microsoft JhengHei", 11, "bold"), bg="#ffffff", fg="#333333", padx=15, pady=10)
display_frame.pack(fill="x", padx=20, pady=5)
detail_title = tk.Label(display_frame, text="請選擇通訊方向...", font=("Microsoft JhengHei", 13, "bold"), bg="#ffffff", anchor="w")
detail_title.pack(fill="x", pady=(0, 5))
detail_content = tk.Label(display_frame, text="點選下方按鈕查看讀寫器或標籤的調變與編碼規範。", font=("Microsoft JhengHei", 11), bg="#ffffff", justify="left", anchor="nw")
detail_content.pack(fill="x")
def show_pcd_to_picc():
detail_title.config(text="讀寫器 到 標籤 (Reader to Card, PCD -> PICC)", fg="#d9534f")
content = "【調變方式】100% ASK (振幅鍵移調變) —— 訊號完全中斷來代表資料邏輯。\n【編碼方式】修正密勒編碼 (Modified Miller Coding) —— 於特定位置加入脈衝,維持能量。"
detail_content.config(text=content)
def show_picc_to_pcd():
detail_title.config(text="標籤 到 讀寫器 (Card to Reader, PICC -> PCD)", fg="#0275d8")
content = "【調變方式】負載調變 (Load Modulation) —— 改變天線阻抗影響磁場。\n【編碼與副載波】曼徹斯特編碼 (Manchester Coding) / 副載波 847.5 kHz —— 抗干擾能力強。\n【通信速率】標準速率為 106 kbit/s"
detail_content.config(text=content)
btn_frame = ttk.Frame(root)
btn_frame.pack(pady=5)
btn_pcd = ttk.Button(btn_frame, text="1. 讀寫器 -> 標籤 (PCD->PICC)", command=show_pcd_to_picc, style="Action.TButton")
btn_pcd.pack(side="left", padx=15)
btn_picc = ttk.Button(btn_frame, text="2. 標籤 -> 讀寫器 (PICC->PCD)", command=show_picc_to_pcd, style="Action.TButton")
btn_picc.pack(side="left", padx=15)
ttk.Separator(root, orient='horizontal').pack(fill='x', padx=20, pady=10)
# ==================== 下半部:延伸設計 - 訊號波形模擬區 ====================
simulation_frame = tk.LabelFrame(root, text="延伸設計:互動式訊號波形模擬器", font=("Microsoft JhengHei", 11, "bold"), bg="#fcfcfc", fg="#006633", padx=15, pady=10)
simulation_frame.pack(fill="both", expand=True, padx=20, pady=5)
# 輸入區域
input_frame = ttk.Frame(simulation_frame)
input_frame.pack(fill="x", pady=5)
input_label = ttk.Label(input_frame, text="請輸入二進位信號 (例如 01011011):", font=("Microsoft JhengHei", 11))
input_label.pack(side="left", padx=5)
signal_entry = ttk.Entry(input_frame, font=("Consolas", 12), width=20)
signal_entry.insert(0, "01011011") # 預設值
signal_entry.pack(side="left", padx=5)
# 畫布(用於繪製文字與偽波形)
canvas = tk.Canvas(simulation_frame, bg="#1e1e1e", height=320)
canvas.pack(fill="both", expand=True, pady=5)
def draw_wave():
# 驗證輸入
bit_string = signal_entry.get().strip()
if not bit_string or not all(b in '01' for b in bit_string):
messagebox.showerror("輸入錯誤", "請輸入有效的二進位字串(僅包含 0 與 1)!")
return
if len(bit_string) > 12:
messagebox.showwarning("字串過長", "為了最佳顯示效果,請輸入 12 位元以內的訊號。")
bit_string = bit_string[:12]
canvas.delete("all") # 清空畫布
# 畫布參數
start_x = 180
bit_width = 45
# 繪製時間軸與格線
for i in range(len(bit_string) + 1):
x = start_x + i * bit_width
canvas.create_line(x, 20, x, 300, fill="#333333", dash=(2, 2))
# 寫出上方原始資料位元
for i, bit in enumerate(bit_string):
x = start_x + i * bit_width + (bit_width / 2)
canvas.create_text(x, 15, text=bit, fill="#ffffff", font=("Consolas", 12, "bold"))
# --- 1) 100% ASK ---
canvas.create_text(85, 50, text="1) 100% ASK", fill="#ff9999", font=("Microsoft JhengHei", 11, "bold"), anchor="w")
# 示意圖:用密集波形代表有能量,完全平線代表100%中斷
x_curr = start_x
for bit in bit_string:
if bit == '1': # 假設 1 有訊號,0 由於 Modified Miller 配合會全斷或局部斷,此處簡化示意 ASK 本身特性
canvas.create_line(x_curr, 50, x_curr + bit_width, 50, fill="#ff4d4d", width=3)
else:
canvas.create_line(x_curr, 65, x_curr + bit_width, 65, fill="#ff4d4d", width=1, dash=(4,4))
x_curr += bit_width
canvas.create_text(start_x, 80, text="說明: 邏輯由載波的「有/無(100%斷開)」決定,斷開時卡片完全接收不到能量。", fill="#aaaaaa", font=("Microsoft JhengHei", 9), anchor="w")
# --- 2) Modified Miller ---
canvas.create_text(85, 120, text="2) Modified Miller", fill="#ffcc99", font=("Microsoft JhengHei", 11, "bold"), anchor="w")
x_curr = start_x
last_bit = '1'
for bit in bit_string:
# 修正密勒編碼:
# '1' -> 位元中間有一個下降脈衝下降
# '0' -> 若前一個是'1'則整段高電平;若前一個是'0'則在位元起始點有下降脈衝
y_high, y_low = 120, 135
if bit == '1':
canvas.create_line(x_curr, y_high, x_curr + bit_width/2, y_high, fill="#ffa64d", width=2)
canvas.create_line(x_curr + bit_width/2, y_high, x_curr + bit_width/2, y_low, fill="#ffa64d", width=2)
canvas.create_line(x_curr + bit_width/2, y_low, x_curr + bit_width, y_low, fill="#ffa64d", width=2)
else: # bit == '0'
if last_bit == '1':
canvas.create_line(x_curr, y_high, x_curr + bit_width, y_high, fill="#ffa64d", width=2)
else:
canvas.create_line(x_curr, y_low, x_curr, y_high, fill="#ffa64d", width=2)
canvas.create_line(x_curr, y_high, x_curr + bit_width, y_high, fill="#ffa64d", width=2)
x_curr += bit_width
last_bit = bit
canvas.create_text(start_x, 150, text="說明: 脈衝位置取決於目前與前一個位元。特點是省電且能保持同步。", fill="#aaaaaa", font=("Microsoft JhengHei", 9), anchor="w")
# --- 3) Load Modulation ---
canvas.create_text(85, 190, text="3) Load Modulation", fill="#99ccff", font=("Microsoft JhengHei", 11, "bold"), anchor="w")
x_curr = start_x
for bit in bit_string:
# 用振幅大小變化來展示阻抗切換帶來的負載調變
y_max, y_min = 185, 195
if bit == '1':
canvas.create_line(x_curr, y_max, x_curr + bit_width, y_max, fill="#3399ff", width=4)
else:
canvas.create_line(x_curr, y_min, x_curr + bit_width, y_min, fill="#3399ff", width=2)
x_curr += bit_width
canvas.create_text(start_x, 220, text="說明: 卡片主動切換內部負載阻抗,使讀寫器天線端感應到微弱的電壓振幅起伏。", fill="#aaaaaa", font=("Microsoft JhengHei", 9), anchor="w")
# --- 4) Manchester Coding ---
canvas.create_text(85, 260, text="4) Manchester", fill="#99ff99", font=("Microsoft JhengHei", 11, "bold"), anchor="w")
x_curr = start_x
for bit in bit_string:
y_high, y_low = 255, 270
# Type A 採用的 Manchester:
# 邏輯 1 = 位元前半段為低,後半段為高 (自低往高跳變)
# 邏輯 0 = 位元前半段為高,後半段為低 (自高往低跳變)
if bit == '1':
canvas.create_line(x_curr, y_low, x_curr + bit_width/2, y_low, fill="#5cd65c", width=2)
canvas.create_line(x_curr + bit_width/2, y_low, x_curr + bit_width/2, y_high, fill="#5cd65c", width=2)
canvas.create_line(x_curr + bit_width/2, y_high, x_curr + bit_width, y_high, fill="#5cd65c", width=2)
else:
canvas.create_line(x_curr, y_high, x_curr + bit_width/2, y_high, fill="#5cd65c", width=2)
canvas.create_line(x_curr + bit_width/2, y_high, x_curr + bit_width/2, y_low, fill="#5cd65c", width=2)
canvas.create_line(x_curr + bit_width/2, y_low, x_curr + bit_width, y_low, fill="#5cd65c", width=2)
# 連接相鄰位元的轉折線
if x_curr > start_x:
canvas.create_line(x_curr, 255, x_curr, 270, fill="#225522", width=1)
x_curr += bit_width
canvas.create_text(start_x, 292, text="說明: 以位元正中央的「跳變方向」決定邏輯。1為上升沿,0為下降沿,抗噪性極佳。", fill="#aaaaaa", font=("Microsoft JhengHei", 9), anchor="w")
# 模擬按鈕
btn_sim = ttk.Button(input_frame, text="產生模擬波形", command=draw_wave, style="Action.TButton")
btn_sim.pack(side="left", padx=10)
# 初始化繪圖
draw_wave()
# 啟動主程式迴圈
root.mainloop()

