2026年1月19日 星期一

ISO/IEC 14443A (MIFARE) 通訊協定中,讀卡機(Reader)與卡片(Card)之間的信號傳輸特性

ISO/IEC 14443A (MIFARE) 通訊協定中,讀卡機(Reader)與卡片(Card)之間的信號傳輸特性 

  • ISO14443A 的通訊機制如下:

    1. 讀卡機到卡片 (Reader to Card)

    • 調變:100% ASK (Amplitude Shift Keying)。這意味著讀卡機透過完全切換載波的開啟與關閉來傳遞數位訊號「0」與「1」。

    • 編碼:修正型米勒編碼 (Modified Miller Coded)。這種編碼方式能確保在傳輸數據的同時,卡片仍能獲得足夠的能量供應。

    • 速率:支援 106 kbit/s 至 848 kbit/s。

    2. 卡片到讀卡機 (Card to Reader)

    • 調變:副載波負載調變 (Subcarrier Load Modulation)。由於卡片本身不發射無線電,它透過切換自身線圈的負載,造成讀卡機磁場的微小擾動,讓讀卡機感應到訊號。

    • 編碼:曼徹斯特編碼 (Manchester Coded) 或 BPSK。曼徹斯特編碼具有自同步特性,非常適合位移式感應環境。

  • Reader to Card (Modified Miller)

    • 根據您上傳的圖片,此方向使用 100% ASK

    • Modified Miller 編碼的視覺特徵是載波在大部分時間都是「開啟」的,只有在特定位元定義位置會出現極短的「關閉」(Gap),這確保了被動式卡片能持續獲得能量而不斷電。

  • Card to Reader (Manchester)

    • 此方向使用 Subcarrier Load Modulation

    • Manchester 編碼(曼徹斯特編碼)的特徵是位元中間必有跳變。在我的模擬中:

      • 從「低電位跳到高電位」代表邏輯 1

      • 從「高電位跳到低電位」代表邏輯 0

    • 這種編碼的好處是時脈與數據合一,讀卡機能非常精準地同步數據傳輸速率(106 kbit/s 等)。

  • 按照 ISO/IEC 14443A 標準的編碼規則進行繪圖。

    核心編碼邏輯修正:

    1. Modified Miller (Reader to Card):

      • Bit 1: 在位元週期的中間(Mid-bit)產生一個電位下降(Gap)。

      • Bit 0: 保持高電位(除非連續出現 0,則在位元開始處產生 Gap,此處已包含此邏輯)。

    2. Manchester (Card to Reader):

      • Bit 1: 位元中間由 低跳變至高 (Rising edge)。

      • Bit 0: 位元中間由 高跳變至低 (Falling edge)。




    import tkinter as tk

    from tkinter import ttk


    class RFIDProtocolApp:

        def __init__(self, root):

            self.root = root

            self.root.title("ISO/IEC 14443A RFID 通訊協定顯示器")

            self.root.geometry("800x500")

            self.root.configure(bg="#f5f5f5")


            # 標題

            title_label = tk.Label(

                self.root, 

                text="ISO/IEC 14443A / MIFARE 通訊架構", 

                font=("Arial", 18, "bold"),

                bg="#f5f5f5",

                fg="#005b96"

            )

            title_label.pack(pady=20)


            # 畫布區域 (繪製通訊箭頭)

            self.canvas = tk.Canvas(self.root, width=700, height=150, bg="white", highlightthickness=1)

            self.canvas.pack(pady=10)

            self.draw_diagram()


            # 詳細參數表格

            table_frame = tk.Frame(self.root, bg="#f5f5f5")

            table_frame.pack(pady=20, padx=50, fill="x")


            columns = ("direction", "modulation", "coding", "speed")

            self.tree = ttk.Treeview(table_frame, columns=columns, show="headings", height=2)

            

            self.tree.heading("direction", text="傳輸方向")

            self.tree.heading("modulation", text="調變方式 (Modulation)")

            self.tree.heading("coding", text="編碼方式 (Coding)")

            self.tree.heading("speed", text="傳輸速率")


            self.tree.column("direction", width=150, anchor="center")

            self.tree.column("modulation", width=200, anchor="center")

            self.tree.column("coding", width=150, anchor="center")

            self.tree.column("speed", width=150, anchor="center")


            # 插入您提供的資料

            self.tree.insert("", "end", values=(

                "Reader → Card", "100% ASK", "Miller Coded", "106 - 848 kbit/s"

            ))

            self.tree.insert("", "end", values=(

                "Card → Reader", "Subcarrier Load Modulation", "Manchester / BPSK", "106 - 848 kbit/s"

            ))


            self.tree.pack(fill="x")


            # 備註說明

            note_text = (

                "技術重點:\n"

                "1. 讀卡機 (RC522) 使用 13.56MHz 載波傳送能量與數據。\n"

                "2. 卡片端無電池,透過『負載調變』(Load Modulation) 改變線圈阻抗來回傳訊號。"

            )

            note_label = tk.Label(self.root, text=note_text, font=("Microsoft JhengHei", 10), justify="left", bg="#f5f5f5")

            note_label.pack(pady=10)


        def draw_diagram(self):

            # 繪製 Reader 方塊

            self.canvas.create_rectangle(50, 30, 180, 120, fill="#d3d3d3", outline="black", width=2)

            self.canvas.create_text(115, 75, text="RC522\nReader", font=("Arial", 12, "bold"))


            # 繪製 Card 方塊

            self.canvas.create_rectangle(520, 30, 650, 120, fill="white", outline="black", width=2)

            self.canvas.create_text(585, 75, text="ISO14443A\nCard", font=("Arial", 12))


            # 繪製向右箭頭 (Reader to Card)

            self.canvas.create_line(200, 50, 500, 50, arrow=tk.LAST, width=2, fill="blue")

            self.canvas.create_text(350, 40, text="1. 100% ASK / Miller", fill="blue", font=("Arial", 9))


            # 繪製向左箭頭 (Card to Reader)

            self.canvas.create_line(500, 100, 200, 100, arrow=tk.LAST, width=2, fill="red")

            self.canvas.create_text(350, 115, text="2. Load Modulation / Manchester", fill="red", font=("Arial", 9))


    if __name__ == "__main__":

        root = tk.Tk()

        app = RFIDProtocolApp(root)

        root.mainloop()

    波形圖觀察重點:

    1. 100% ASK (Reader to Card):

      • 波形特徵:你會看到藍色的 13.56MHz 高頻載波在某些位置「突然消失」。

      • 對應關係:消失的位置就是 Miller 編碼中的 Gap(例如位元 1 的中間)。這就是為什麼叫 100% ASK,因為振幅直接降到 0。

    2. Subcarrier Load Modulation (Card to Reader):

      • 波形特徵:你會看到紅色的波形呈現「包絡線 (Envelope)」的變化。基礎是 13.56MHz,但在 Manchester 為 High 的時段,波形會變得更密集或振幅疊加(模擬 848kHz 的副載波)。

      • 物理意義:卡片並不發射能量,它只是「開關」負載。讀卡機偵測到的是這兩組頻率($13.56 \text{ MHz} \pm 848 \text{ kHz}$)產生的邊帶訊號。

    總結

    • 100% ASK: 讀卡機很霸道,直接把能量關掉來傳數字。

    • Subcarrier Load Modulation: 卡片很優雅,透過在載波上「跳舞」(加入副載波)來回傳訊息。



  • 數位與物理的絕對同步

    • Miller 模式:上方圖會顯示在位元中間或起始處的「窄脈衝」。下方圖的載波則會在這些「窄脈衝」出現時完全消失(ASK 100%)。

    • Manchester 模式:上方圖呈現標準的跳變波形(1 為低到高,0 為高到低)。下方圖在數位波形為 High 的區域,會疊加出更密集的副載波震盪。

  • Miller 編碼修正

    • 修正了數位流的呈現方式,不再是方塊,而是精確的 Modified Miller 電位。這對於解釋「為什麼卡片在傳輸數據時不會斷電」非常有幫助,因為電位下降的時間極短。


  • import tkinter as tk
    from tkinter import messagebox
    import matplotlib.pyplot as plt
    from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
    import numpy as np
    import platform
    import matplotlib

    # 1. 環境字體設定
    def set_font():
        system = platform.system()
        if system == "Windows":
            matplotlib.rc('font', family='Microsoft JhengHei')
        elif system == "Darwin":
            matplotlib.rc('font', family='Arial Unicode MS')
        matplotlib.rcParams['axes.unicode_minus'] = False

    set_font()

    class RFIDSigSimApp:
        def __init__(self, root):
            self.root = root
            self.root.title("RFID 數位編碼與物理波形同步模擬器")
            self.root.geometry("1100x850")

            # 控制面板
            ctrl_frame = tk.Frame(self.root, pady=15)
            ctrl_frame.pack()

            tk.Label(ctrl_frame, text="二進位序列 (Binary):", font=("Arial", 12)).pack(side="left")
            self.entry_data = tk.Entry(ctrl_frame, font=("Arial", 12), width=12)
            self.entry_data.insert(0, "101101")
            self.entry_data.pack(side="left", padx=10)

            btn_frame = tk.Frame(self.root)
            btn_frame.pack(pady=5)

            tk.Button(btn_frame, text="Reader to Card (Miller)", command=self.plot_ask, 
                      bg="#005b96", fg="white", font=("Arial", 10, "bold"), width=30).pack(side="left", padx=10)
            
            tk.Button(btn_frame, text="Card to Reader (Manchester)", command=self.plot_load_mod, 
                      bg="#c94c4c", fg="white", font=("Arial", 10, "bold"), width=30).pack(side="left", padx=10)

            # 繪圖區域
            self.fig, (self.ax1, self.ax2) = plt.subplots(2, 1, figsize=(10, 7), 
                                                          gridspec_kw={'height_ratios': [1, 2]})
            self.canvas = FigureCanvasTkAgg(self.fig, master=self.root)
            self.canvas.get_tk_widget().pack(fill="both", expand=True, padx=20, pady=10)

            self.plot_ask()

        def get_bits(self):
            data = self.entry_data.get().strip()
            if not data or not all(c in '01' for c in data):
                messagebox.showerror("錯誤", "請輸入 0 或 1 的組合")
                return None
            return [int(b) for b in data]

        def plot_ask(self):
            """繪製 Reader to Card: 上方 Miller 編碼 / 下方 100% ASK"""
            bits = self.get_bits()
            if not bits: return
            self.ax1.clear()
            self.ax2.clear()

            t = np.linspace(0, len(bits), 4000)
            miller_logic = np.ones_like(t)

            # 生成 Miller 邏輯 (用於上方數位圖與下方調變)
            for i, bit in enumerate(bits):
                if bit == 1: # 1: 位元中間下陷
                    miller_logic[(t >= i + 0.45) & (t <= i + 0.55)] = 0
                elif i > 0 and bits[i-1] == 0: # 連續 0: 位元開始處下陷
                    miller_logic[(t >= i) & (t <= i + 0.1)] = 0
                elif i == 0 and bit == 0: # 第一個是 0 的起始處理
                    pass 

            # 上方圖:Miller 數位波形
            self.ax1.plot(t, miller_logic, color='blue', linewidth=2)
            self.ax1.set_title(f"上方:Modified Miller 數位編碼 (Reader 傳出)")
            self.ax1.set_ylim(-0.2, 1.2)

            # 下方圖:100% ASK 載波
            carrier = np.sin(2 * np.pi * 15 * t)
            ask_signal = carrier * miller_logic
            self.ax2.plot(t, ask_signal, color='#005b96', linewidth=0.7)
            self.ax2.set_title("下方:100% ASK 物理載波 (13.56MHz)")

            self.finalize_plot(bits)

        def plot_load_mod(self):
            """繪製 Card to Reader: 上方 Manchester 編碼 / 下方 Subcarrier 調變"""
            bits = self.get_bits()
            if not bits: return
            self.ax1.clear()
            self.ax2.clear()

            t = np.linspace(0, len(bits), 5000)
            manchester_logic = np.zeros_like(t)

            # 生成 Manchester 邏輯
            for i, bit in enumerate(bits):
                if bit == 1: # 1: 低跳高
                    manchester_logic[(t >= i + 0.5) & (t <= i + 1.0)] = 1
                else: # 0: 高跳低
                    manchester_logic[(t >= i) & (t <= i + 0.5)] = 1

            # 上方圖:Manchester 數位波形
            self.ax1.step(t, manchester_logic, where='post', color='red', linewidth=2)
            self.ax1.set_title(f"上方:Manchester 數位編碼 (Card 回傳)")
            self.ax1.set_ylim(-0.2, 1.2)

            # 下方圖:Load Modulation (載波 + 副載波)
            carrier = 0.5 * np.sin(2 * np.pi * 10 * t)
            subcarrier = 0.3 * np.sin(2 * np.pi * 80 * t) * manchester_logic
            load_mod_signal = carrier + subcarrier
            
            self.ax2.plot(t, load_mod_signal, color='#c94c4c', linewidth=0.7)
            self.ax2.set_title("下方:Subcarrier Load Modulation 物理波形")

            self.finalize_plot(bits)

        def finalize_plot(self, bits):
            for ax in [self.ax1, self.ax2]:
                ax.set_xlim(0, len(bits))
                for i in range(len(bits) + 1):
                    ax.axvline(x=i, color='gray', linestyle=':', alpha=0.5)
                # 在上方圖標註原始 bit
                if ax == self.ax1:
                    for i, b in enumerate(bits):
                        ax.text(i+0.5, 1.05, f"bit {b}", ha='center', fontsize=10, fontweight='bold')
            
            self.ax1.set_yticks([0, 1])
            self.ax1.set_yticklabels(['L', 'H'])
            self.fig.tight_layout()
            self.canvas.draw()

    if __name__ == "__main__":
        root = tk.Tk()
        app = RFIDSigSimApp(root)
        root.mainloop()



    沒有留言:

    張貼留言

    Dynamic Framed Slotted ALOHA (DFSA) 防撞方法

     Dynamic Framed Slotted ALOHA (DFSA)防撞方法 DFSA 的核心邏輯: 動態框長 (Dynamic Frame Size) :閱讀器會根據上一輪的結果(碰撞次數、空閒次數)來 動態調整 下一輪的時槽數(框長 $L$ )。 效率優化 :當標籤很多時...