根據您提供的 MFRC522 技術文件截圖(圖 8.2.5 CRC coprocessor 和表 17)
一步一步(step by step)地概念性地說明 16-bit CRC 的動作原理。
由於 CRC 運算本質上是基於二進制的多項式除法,我們將使用 tkinter 的標籤和文本來展示每一步的異或 (XOR) 運算過程,並遵循 MFRC522 的規格:
多項式 (Polynomial): x^16 + x^12 + x^5 + 1 (即 10001000000100001(2) )。
數據順序: MSB (最高有效位) First。
💻 Python Tkinter CRC 運算說明程式碼
這個程式會示範如何對一個 8-bit 的輸入數據進行 16-bit CRC 運算。
基礎原理
CRC 運算是一種錯誤偵測碼,其原理是將數據視為一個二進制多項式,並對其進行多項式除法。剩餘的 16 位數就是 CRC 校驗碼。
MFRC522 CRC 規格:
多項式 P(x): x^16 + x^12 + x^5 + 1 (ISO/IEC 14443A 標準)。
暫存器長度: 16-bit。
數據順序: MSB (最高有效位) First。
初始值 (Preset): 可選 0000h (本範例使用)、6363h、A671h 或 FFFFh。
CRC 暫存器步驟動作 (每個輸入位元)
對於 MFRC522 協處理器,每個輸入數據位元的處理步驟如下:
Step 1: 取得輸入位元
從輸入數據流中取出一個位元 (MSB First)。
Step 2: 判斷是否需要 XOR (除法)
判斷當前 CRC 暫存器的 MSB (第 15 位) 與 新輸入的位元 進行 異或 (XOR) 的結果。
Do XOR = CRC[15] ⊕ Input Bit
Step 3: CRC 暫存器左移 (乘 $x$)
將 16-bit CRC 暫存器的所有位元向左移動一位。這相當於在多項式除法中將數據乘以 $x$。
左移會將 CRC 暫存器的 MSB (第 15 位) 移出。
Step 4: 執行條件式 XOR 運算 (除法)
如果 Do XOR = 1:
表示當前的 CRC 餘數 (在左移前) 大於或等於多項式。
將 CRC 暫存器與多項式的低 16 位進行異或運算。
多項式的低 16 位 (即 x^15 到 x^0) 是 x^12 + x^5 + 1,對應二進制 0001000000100001(2) (1021h)。
如果 Do XOR = 0:
表示餘數小於多項式,本次不需要進行 XOR 運算。
Step 5: 重複
對所有輸入位元重複 Step 1 到 Step 4。
最終,CRC 暫存器中剩下的 16 位數,即是 CRCResultH 和CRCResultL 暫存器所指示的 CRC 校驗碼。
import tkinter as tk
from tkinter import ttk
from collections import deque
# --- MFRC522 CRC 規格參數 ---
# P(x) = x^16 + x^12 + x^5 + 1
# 17-bit 二進制表示 (最高位是 x^16, 最低位是 x^0)
POLYNOMIAL = 0b10001000000100001
POLYNOMIAL_STR = format(POLYNOMIAL, '017b')
CRC_WIDTH = 16
class CRCCalculatorApp:
def __init__(self, master):
self.master = master
master.title("MFRC522 16-bit CRC 動作原理說明 (Step-by-Step)")
# 選擇預設值 (ModeReg's CRCPreset[1:0] = 00)
self.preset_value = 0x0000
# 範例輸入數據 (例如: 1 0 1 1 0 0 1 1)
self.input_data_hex = tk.StringVar(value="B3")
self.input_data_bin = format(int(self.input_data_hex.get(), 16), '08b')
self.data_queue = deque([int(b) for b in self.input_data_bin])
# 16-bit CRC 暫存器 (初始為 Preset Value)
self.crc_register = self.preset_value
self.setup_ui(master)
self.step = 0
self.max_steps = 8 # 處理 8 個輸入位元
def setup_ui(self, master):
"""建立應用程式的用戶界面"""
main_frame = ttk.Frame(master, padding="15")
main_frame.pack(fill='both', expand=True)
ttk.Label(main_frame, text="⚙️ MFRC522 CRC 動作原理 ⚙️",
font=("Arial", 16, "bold"), foreground="#0055AA").pack(pady=5)
# --- 參數顯示區 ---
param_frame = ttk.LabelFrame(main_frame, text="CRC 規格參數", padding="10")
param_frame.pack(fill='x', pady=10)
ttk.Label(param_frame, text=f"**多項式 P(x):** x¹⁶ + x¹² + x⁵ + 1", font=("Arial", 10)).grid(row=0, column=0, sticky='w')
ttk.Label(param_frame, text=f"**二進制形式 (17-bit):** {POLYNOMIAL_STR}", font=("Arial", 10)).grid(row=0, column=1, sticky='w', padx=10)
ttk.Label(param_frame, text=f"**預設值 (Preset):** {format(self.preset_value, '04X')}h", font=("Arial", 10)).grid(row=1, column=0, sticky='w')
ttk.Label(param_frame, text=f"**輸入數據 (MSB First):** {self.input_data_hex.get()}h ({self.input_data_bin})", font=("Arial", 10)).grid(row=1, column=1, sticky='w', padx=10)
# --- 運算顯示區 ---
self.calc_frame = ttk.LabelFrame(main_frame, text="CRC 步驟運算", padding="10")
self.calc_frame.pack(fill='both', expand=True, pady=10)
# 標題
ttk.Label(self.calc_frame, text="步驟", font=("Arial", 10, "bold")).grid(row=0, column=0, padx=5)
ttk.Label(self.calc_frame, text="當前輸入位元", font=("Arial", 10, "bold")).grid(row=0, column=1, padx=5)
ttk.Label(self.calc_frame, text="CRC 暫存器狀態 (16-bit)", font=("Arial", 10, "bold")).grid(row=0, column=2, padx=10)
ttk.Label(self.calc_frame, text="操作/條件", font=("Arial", 10, "bold")).grid(row=0, column=3, padx=10)
# 結果和步驟控制
self.result_label = ttk.Label(main_frame, text="點擊 '下一步' 開始 CRC 運算。", font=("Arial", 11))
self.result_label.pack(pady=10)
self.next_button = ttk.Button(main_frame, text="下一步 (處理第 1 個位元)", command=self.next_step)
self.next_button.pack(pady=5)
ttk.Button(main_frame, text="重置", command=self.reset).pack(pady=5)
def reset(self):
"""重置運算狀態"""
self.step = 0
self.crc_register = self.preset_value
self.data_queue = deque([int(b) for b in self.input_data_bin])
# 清除舊的運算步驟
for widget in self.calc_frame.winfo_children():
if int(widget.grid_info().get("row")) > 0:
widget.destroy()
self.result_label.config(text="點擊 '下一步' 開始 CRC 運算。")
self.next_button.config(text=f"下一步 (處理第 1 個位元)", state=tk.NORMAL)
def next_step(self):
"""執行 CRC 運算的下一步"""
if self.step >= self.max_steps:
self.next_button.config(state=tk.DISABLED, text="運算完成")
return
self.step += 1
row = self.step
# 1. 取得輸入位元 (MSB First)
input_bit = self.data_queue.popleft()
# 2. 獲取 CRC 暫存器的 MSB (第 15 位)
# 由於是 16-bit 暫存器,最高位是 x^15
crc_msb = (self.crc_register >> (CRC_WIDTH - 1)) & 0x1
# 3. 將新的輸入位元 XOR 暫存器的 MSB (決定是否進行多項式除法)
do_xor = crc_msb ^ input_bit
# 4. 暫存器左移 1 位 (相當於乘以 x)
# 由於是 16-bit 暫存器,左移後,位元 x^0 被移出,x^16 進入
self.crc_register = (self.crc_register << 1) & 0xFFFF
operation_text = ""
# 5. 執行條件式 XOR (除法)
if do_xor == 1:
# 進行 XOR 運算 (異或多項式)
# 注意:這裡多項式是 17-bit,但只對 CRC 暫存器 (16-bit) 進行異或。
# MFRC522 的 CRC 多項式是 x^16 + x^12 + x^5 + 1 (17-bit)。
# 在 XOR 階段,我們只 XOR 它的低 16 位 ($x^{15}$ 到 $x^0$),即 $x^{12} + x^5 + 1$。
# P_low = 0001000000100001 (16-bit)
P_low = (POLYNOMIAL >> 1) & 0xFFFF # 取得多項式的低 16 位
self.crc_register ^= P_low
operation_text = f"CRC[15] ^ Input = 1。CRC ^= P_low ({format(P_low, '04X')}h)"
else:
operation_text = f"CRC[15] ^ Input = 0。只進行左移。"
# 6. 更新 UI 顯示
# 顯示當前輸入位元
ttk.Label(self.calc_frame, text=f"第 {row} 位", font=("Arial", 10)).grid(row=row, column=0, padx=5, pady=2)
ttk.Label(self.calc_frame, text=f"**{input_bit}**", font=("Arial", 10, "bold"), foreground="blue").grid(row=row, column=1, padx=5, pady=2)
# 顯示運算結果 (CRC 暫存器狀態)
crc_bin_str = format(self.crc_register, '016b')
crc_hex_str = format(self.crc_register, '04X')
ttk.Label(self.calc_frame, text=f"**{crc_bin_str}** ({crc_hex_str}h)",
font=("Courier", 9, "bold"), foreground="green").grid(row=row, column=2, padx=10, pady=2, sticky='w')
# 顯示操作/條件
ttk.Label(self.calc_frame, text=operation_text, font=("Arial", 9)).grid(row=row, column=3, padx=10, pady=2, sticky='w')
# 7. 更新步驟按鈕和結果標籤
if self.step < self.max_steps:
self.next_button.config(text=f"下一步 (處理第 {self.step + 1} 個位元)")
else:
final_crc_bin = format(self.crc_register, '016b')
final_crc_hex = format(self.crc_register, '04X')
# MFRC522 的 CRCResult 暫存器是 CRCResultH (高 8 bit) 和 CRCResultL (低 8 bit)
crc_result_h = (self.crc_register >> 8) & 0xFF
crc_result_l = self.crc_register & 0xFF
self.result_label.config(text=f"✅ 運算完成!最終 16-bit CRC: **{final_crc_hex}h**\n (CRCResultH: {format(crc_result_h, '02X')}h, CRCResultL: {format(crc_result_l, '02X')}h)",
font=("Arial", 12, "bold"), foreground="purple")
self.next_button.config(state=tk.DISABLED, text="運算完成")
# --- 運行 Tkinter 程式 ---
if __name__ == "__main__":
root = tk.Tk()
app = CRCCalculatorApp(root)
root.mainloop()


沒有留言:
張貼留言