RFID門禁系統(tǒng):雙因子鑒權(quán)與掉電保護(hù)設(shè)計(jì))
簡介這份資源面向電子信息、自動(dòng)化等專業(yè)的學(xué)生及單片機(jī)初學(xué)者提供一套完整的基于51單片機(jī)的RFID門禁系統(tǒng)設(shè)計(jì)方案涵蓋刷卡識別與密碼輸入兩種開鎖方式可用于課程設(shè)計(jì)、畢業(yè)設(shè)計(jì)或電子競賽參考。壓縮包共149個(gè)文件約34.23MB包含C語言源程序與頭文件、Keil工程配置、PCB圖與原理圖、論文文檔及大量實(shí)物與調(diào)試截圖從代碼到硬件圖紙一應(yīng)俱全。目前已有3386人學(xué)習(xí)下載說明方案經(jīng)過一定范圍的驗(yàn)證。讀者可從中獲得RC522讀卡、LCD12864顯示、EEPROM存儲授權(quán)信息、蜂鳴器報(bào)警等模塊的完整實(shí)現(xiàn)并對照原理圖與PCB完成實(shí)物搭建理解門禁系統(tǒng)的工作流程與排錯(cuò)思路為后續(xù)智能安防類項(xiàng)目開發(fā)打下基礎(chǔ)。1. 刷卡到開鎖之間那 200 毫秒里到底發(fā)生了什么門禁系統(tǒng)看起來是最沒有技術(shù)含量的設(shè)備貼卡、嘀一聲、門開了。但如果你把耳朵貼在讀卡器旁邊會聽到繼電器吸合的輕微咔噠聲這聲咔噠和前面那聲嘀之間隔著一段幾十到幾百毫秒的空白。這段空白就是整套系統(tǒng)的全部秘密所在——射頻場里正在完成一次能量耦合、一次防沖突仲裁、一次密鑰比對以及一次掉電前的狀態(tài)落盤?;?51 單片機(jī)的 RFID 門禁系統(tǒng)刷卡加密碼雙因子之所以值得拆開看不是因?yàn)樗鼜?fù)雜恰恰是因?yàn)樗焉漕l識別、人機(jī)交互、執(zhí)行機(jī)構(gòu)控制、數(shù)據(jù)持久化這四件事壓縮進(jìn)了一顆只有幾 KB 程序空間、128 字節(jié) RAM 的芯片里。資源越緊設(shè)計(jì)取舍就越赤裸每一個(gè)字節(jié)放在哪、每一次輪詢間隔多久、掉電瞬間先寫什么后寫什么都沒有含糊的余地。搞懂這套機(jī)制你再看任何嵌入式門禁、考勤機(jī)、儲物柜鎖底層邏輯都是同一套骨架換皮。2. 射頻前端讀卡器為什么能在不接觸的情況下認(rèn)出卡2.1 13.56 MHz 場里的能量與數(shù)據(jù)是同一對線無源卡自己沒有電池它的一切電能都來自讀卡器天線輻射的交變磁場。讀卡器驅(qū)動(dòng)天線產(chǎn)生 13.56 MHz 的正弦載波卡靠近后卡片內(nèi)部的 LC 諧振回路與這個(gè)頻率共振通過電磁感應(yīng)耦合出交流電壓再整流濾波成直流給芯片供電。這里有個(gè)容易被忽略的點(diǎn)能量傳輸和數(shù)據(jù)回傳走的是同一對天線線圈只是方向相反。讀卡器發(fā)命令時(shí)它調(diào)制自己載波的幅度ASK卡通過檢測場強(qiáng)變化解出比特卡回?cái)?shù)據(jù)時(shí)采用負(fù)載調(diào)制——卡內(nèi)部切換一個(gè)負(fù)載電阻改變線圈的等效阻抗這個(gè)變化反射回讀卡器天線表現(xiàn)為天線兩端電壓的微小波動(dòng)讀卡器再把這個(gè)波動(dòng)解調(diào)出來。所以你會看到讀卡器電路里那組天線匹配網(wǎng)絡(luò)和包絡(luò)檢波電路不是可選項(xiàng)它們直接決定了讀卡距離和誤碼率。常見的實(shí)現(xiàn)思路是天線端并聯(lián)電容做諧振匹配檢波后經(jīng)過比較器整形再送單片機(jī)這一級如果參數(shù)漂移表現(xiàn)就是「卡要貼得很近才靈」或者「隔空亂讀」。2.2 防沖突多張卡同時(shí)進(jìn)場時(shí)憑什么只認(rèn)一張如果兩張卡同時(shí)進(jìn)入射頻場它們會同時(shí)響應(yīng)讀卡器收到的就是兩路負(fù)載調(diào)制的疊加解出來是亂碼。ISO14443 協(xié)議族為此設(shè)計(jì)了防沖突機(jī)制核心思想是讀卡器廣播一個(gè)請求所有卡用自己的唯一序列號UID參與仲裁逐位比較沖突位被讀卡器置為特定值后再次廣播直到只剩一張卡能完整應(yīng)答。在 51 平臺上實(shí)現(xiàn)這套邏輯通常不會去啃完整的協(xié)議棧而是依賴專用讀卡芯片如 MFRC522 一類在硬件層完成幀處理和 CRC單片機(jī)只負(fù)責(zé)發(fā)命令、讀 FIFO。這是典型的資源受限下的分層取舍把時(shí)序苛刻的射頻物理層交給專用芯片把業(yè)務(wù)邏輯留在主控。下面這段是讀卡器初始化與尋卡的典型流程骨架重點(diǎn)看命令字和寄存器操作順序/* 讀卡芯片初始化與尋卡寄存器名按常見讀卡芯片抽象 */ void rfid_init(void) { spi_init(); /* 建立與讀卡芯片的通信 */ rfid_write_reg(REG_TX_CONTROL, 0x03); /* 開啟天線驅(qū)動(dòng) */ rfid_write_reg(REG_TX_ASK, 0x40); /* 調(diào)制深度配置 */ rfid_write_reg(REG_RX_GAIN, 0x48); /* 接收增益影響讀距 */ rfid_write_reg(REG_MODE, 0x3D); /* CRC 初值等模式位 */ rfid_antenna_on(); /* 天線使能此時(shí)場才建立 */ } /* 尋卡返回 0 表示有卡且 UID 已讀入 uid_buf */ uint8_t rfid_request(uint8_t *uid_buf) { uint8_t status; rfid_write_reg(REG_COMMAND, CMD_TRANSCEIVE); /* 收發(fā)一體命令 */ rfid_write_reg(REG_FIFO_LEVEL, 0x07); /* 清空 FIFO 指針 */ rfid_write_fifo(REQ_ALL); /* 請求所有卡應(yīng)答 */ status rfid_wait_irq(IRQ_RX_DONE, 50); /* 50ms 超時(shí)等待 */ if (status ! OK) return status; rfid_read_fifo(uid_buf, 5); /* 讀回 UID 與校驗(yàn) */ return OK; }REG_RX_GAIN這個(gè)參數(shù)值得單獨(dú)說它直接決定接收鏈路放大倍數(shù)調(diào)大了讀距變遠(yuǎn)但容易把環(huán)境噪聲一起放大調(diào)小了穩(wěn)定但卡必須貼近。血淚經(jīng)驗(yàn)是同一套代碼換一個(gè)外殼、換一種天線走線這個(gè)值就得重新標(biāo)定不能照抄。rfid_wait_irq的超時(shí)設(shè)為 50ms 也不是隨便定的——它要大于一幀完整應(yīng)答的時(shí)間又不能長到讓用戶覺得「刷了沒反應(yīng)」這個(gè)窗口和后面要講的整機(jī)響應(yīng)時(shí)間預(yù)算是聯(lián)動(dòng)的。2.3 UID 只是入場券不是身份本身很多初學(xué)者會把讀到的 UID 直接當(dāng)卡號存進(jìn)白名單這在演示項(xiàng)目里沒問題但在真實(shí)場景里是個(gè)坑UID 可以被復(fù)制卡克隆光憑 UID 判斷等于沒有安全性。刷卡加密碼的雙因子設(shè)計(jì)本質(zhì)上就是在補(bǔ)這個(gè)洞——卡證明「你持有某物」密碼證明「你知道某事」兩者疊加才把冒用成本抬上去。系統(tǒng)里 UID 的作用退化為索引鍵真正的鑒權(quán)在密碼比對那一步完成。3. 雙因子鑒權(quán)狀態(tài)機(jī)怎么把刷卡和密碼串成一條鏈3.1 為什么必須用狀態(tài)機(jī)而不是順序代碼如果寫成「讀卡→比對→等密碼→比對→開門」的順序代碼一旦用戶在等密碼階段走開程序就卡死在那一步后面的人刷不了卡。門禁是典型的多事件并發(fā)系統(tǒng)讀卡事件、按鍵事件、超時(shí)事件隨時(shí)可能到來必須用一個(gè)顯式狀態(tài)機(jī)來管理「當(dāng)前處于哪個(gè)鑒權(quán)階段」。常見的實(shí)現(xiàn)思路是定義四個(gè)狀態(tài)空閑等待卡、已識別卡等待密碼、鑒權(quán)通過執(zhí)行開鎖、以及一個(gè)錯(cuò)誤鎖定態(tài)。每個(gè)狀態(tài)只響應(yīng)自己關(guān)心的事件其余事件丟棄或觸發(fā)狀態(tài)遷移。下面是一個(gè)精簡的狀態(tài)機(jī)骨架typedef enum { ST_IDLE 0, /* 等待刷卡 */ ST_WAIT_PWD, /* 卡已識別等待輸入密碼 */ ST_UNLOCK, /* 鑒權(quán)通過驅(qū)動(dòng)開鎖 */ ST_LOCKOUT /* 連續(xù)錯(cuò)誤臨時(shí)鎖定 */ } auth_state_t; static auth_state_t state ST_IDLE; static uint8_t pwd_input[6]; static uint8_t pwd_len 0; static uint8_t err_cnt 0; void auth_fsm_tick(event_t ev) { switch (state) { case ST_IDLE: if (ev EV_CARD_OK) { /* UID 命中白名單 */ pwd_len 0; state ST_WAIT_PWD; start_timer(TIMEOUT_PWD, 10000); /* 10s 輸入窗口 */ } break; case ST_WAIT_PWD: if (ev EV_KEY_DIGIT) { pwd_input[pwd_len] last_key; if (pwd_len PWD_LEN) { if (pwd_match(pwd_input)) { err_cnt 0; state ST_UNLOCK; } else { err_cnt; state (err_cnt 3) ? ST_LOCKOUT : ST_IDLE; } } } else if (ev EV_TIMEOUT) { state ST_IDLE; /* 超時(shí)回空閑不鎖死 */ } break; case ST_UNLOCK: relay_on(); start_timer(TIMEOUT_DOOR, 3000); /* 開門保持 3s */ if (ev EV_TIMEOUT) { relay_off(); state ST_IDLE; } break; case ST_LOCKOUT: if (ev EV_TIMEOUT) { err_cnt 0; state ST_IDLE; } break; } }這段代碼里有兩個(gè)設(shè)計(jì)點(diǎn)值得摳。第一ST_WAIT_PWD收到超時(shí)事件時(shí)回到ST_IDLE而不是鎖定因?yàn)橛脩艨赡苤皇撬⒘丝ㄓ肿唛_這不是攻擊行為而密碼錯(cuò)誤累計(jì)到 3 次才進(jìn)ST_LOCKOUT這才是防暴力試密碼的閘門。第二ST_UNLOCK里開門保持時(shí)間用獨(dú)立定時(shí)器和密碼輸入窗口的定時(shí)器分開避免一個(gè)定時(shí)器被兩種語義復(fù)用導(dǎo)致時(shí)序錯(cuò)亂——這種復(fù)用錯(cuò)誤在 51 項(xiàng)目里極其常見因?yàn)槎〞r(shí)器資源本來就緊張。3.2 密碼存儲明文、哈希還是加鹽在 51 這種平臺上完整的哈希運(yùn)算如 SHA-256跑一次要幾十毫秒且占不少代碼空間很多項(xiàng)目就直接存明文密碼。從工程角度這可以接受但前提是密碼存儲區(qū)與業(yè)務(wù)邏輯隔離且調(diào)試接口在量產(chǎn)版本里關(guān)閉。如果要做輕量加固常見的折中是存一個(gè)簡單的校驗(yàn)值而非原文比對時(shí)對輸入做同樣運(yùn)算再比較至少讓直接讀 Flash dump 的人拿不到明文。這個(gè)取舍沒有標(biāo)準(zhǔn)答案取決于設(shè)備物理暴露程度——門禁主機(jī)通常裝在門內(nèi)側(cè)物理接觸門檻比消費(fèi)電子高這也是很多項(xiàng)目敢用明文的現(xiàn)實(shí)理由。3.3 白名單與密碼的存儲布局51 單片機(jī)片內(nèi) Flash 通常只有 4KB 到 8KB存不了幾張卡。實(shí)際項(xiàng)目一般外掛一顆 I2C EEPROM如 24C 系列按固定長度記錄存儲每條記錄包含 UID4 到 7 字節(jié)、權(quán)限標(biāo)志、密碼校驗(yàn)值、以及一個(gè)有效位。關(guān)鍵設(shè)計(jì)是記錄之間留出對齊邊界刪除卡時(shí)只清有效位而不搬移數(shù)據(jù)避免寫放大和掉電時(shí)半條記錄損壞。下面是一個(gè)記錄結(jié)構(gòu)的示意/* EEPROM 中每條卡記錄的布局總長 16 字節(jié)對齊 */ typedef struct { uint8_t valid; /* 0xFF 表示空槽0x01 表示有效 */ uint8_t uid[7]; /* 卡序列號不足補(bǔ) 0 */ uint8_t perm; /* 權(quán)限等級普通/管理/常開 */ uint8_t pwd_hash; /* 密碼輕量校驗(yàn)值 */ uint8_t reserved[6]; /* 預(yù)留便于后續(xù)擴(kuò)展字段 */ } card_record_t;valid用 0xFF 表示空槽而不是 0x00是因?yàn)?EEPROM 擦除后默認(rèn)就是 0xFF新片子上電無需初始化就能直接判空省掉一次全片寫操作。這種「讓默認(rèn)態(tài)等于擦除態(tài)」的思路在嵌入式存儲里非常普遍能顯著降低首次配置的失敗率。4. 執(zhí)行機(jī)構(gòu)與掉電保護(hù)繼電器吸合前后系統(tǒng)在忙什么4.1 繼電器驅(qū)動(dòng)為什么要隔離以及續(xù)流二極管不是裝飾單片機(jī) IO 口驅(qū)動(dòng)能力通常只有十幾毫安帶不動(dòng)繼電器線圈中間必須加三極管或達(dá)林頓管。更關(guān)鍵的是繼電器線圈是感性負(fù)載斷電瞬間會產(chǎn)生反向電動(dòng)勢幅值可能達(dá)到電源電壓的數(shù)倍直接打穿三極管甚至反灌到單片機(jī)。所以線圈兩端并聯(lián)續(xù)流二極管是硬性要求不是可選項(xiàng)。翻車的典型現(xiàn)象是調(diào)試時(shí)一切正常裝到現(xiàn)場運(yùn)行幾天后單片機(jī)莫名復(fù)位查半天電源最后發(fā)現(xiàn)是繼電器動(dòng)作時(shí)的尖峰通過共地路徑耦合進(jìn)了復(fù)位電路。隔離的另一個(gè)層面是強(qiáng)電與弱電的物理分隔。門鎖如果是電磁鎖工作電流可能上安培走線必須和單片機(jī)信號線分開繼電器觸點(diǎn)側(cè)和線圈側(cè)在 PCB 上要有足夠的爬電距離。原理圖里那部分看起來只是幾個(gè)元件實(shí)際是整機(jī)可靠性的分水嶺。4.2 開鎖時(shí)序先記錄還是先動(dòng)作鑒權(quán)通過后系統(tǒng)要做兩件事驅(qū)動(dòng)繼電器開鎖以及把這次開門事件寫入日志如果有。順序很講究。如果先寫日志再開鎖寫 EEPROM 的幾毫秒會讓用戶感覺遲鈍如果先開鎖再寫日志萬一寫日志過程中掉電這次開門就沒有記錄。常見的折中是先開鎖把日志寫進(jìn) RAM 緩沖等門關(guān)閉、系統(tǒng)回到空閑態(tài)時(shí)再批量落盤。這樣既保證了響應(yīng)速度又把掉電丟日志的窗口壓縮到最小。掉電檢測本身也需要硬件配合電源端分壓后接一個(gè)比較器或直接接單片機(jī)的外部中斷腳電壓跌到閾值以下時(shí)觸發(fā)中斷中斷里只做最緊急的事——關(guān)繼電器、把關(guān)鍵狀態(tài)標(biāo)志寫進(jìn) EEPROM 的固定位置。這里有個(gè)細(xì)節(jié)EEPROM 單字節(jié)寫入需要幾毫秒掉電后儲能電容能撐的時(shí)間有限所以中斷里寫的字節(jié)數(shù)必須嚴(yán)格控制通常不超過十幾個(gè)字節(jié)。我一般會這么理解掉電保護(hù)不是「把所有東西存下來」而是「只存那些丟了會導(dǎo)致下次上電行為異常的東西」比如當(dāng)前是否處于鎖定態(tài)、錯(cuò)誤計(jì)數(shù)是多少。4.3 門磁與防尾隨的聯(lián)動(dòng)完整的門禁還會接門磁開關(guān)用來判斷門是真的開了還是只是繼電器動(dòng)作了。如果繼電器吸合后一段時(shí)間內(nèi)門磁沒有變化說明門沒被推開或者門磁故障系統(tǒng)可以報(bào)警或記錄異常。這個(gè)邏輯在狀態(tài)機(jī)里表現(xiàn)為ST_UNLOCK之后增加一個(gè)「等待門開」的子狀態(tài)超時(shí)未開則回空閑并置異常標(biāo)志。防尾隨則更復(fù)雜需要進(jìn)出雙向讀卡器配合單讀卡器系統(tǒng)一般只做「開門超時(shí)未關(guān)」告警。這些擴(kuò)展不影響核心鑒權(quán)鏈路但決定了這套系統(tǒng)是玩具還是能上現(xiàn)場的設(shè)備。5. 避坑與排查那些調(diào)試時(shí)才暴露的坑5.1 刷卡偶爾失靈重啟就好現(xiàn)象設(shè)備運(yùn)行一段時(shí)間后讀卡距離明顯變短斷電重啟恢復(fù)。原因多半是讀卡芯片的接收增益寄存器被意外改寫或者天線匹配網(wǎng)絡(luò)受溫度影響失諧。排查方法是周期性回讀關(guān)鍵寄存器并與初始化值比對不一致就重新初始化。解決上在空閑任務(wù)里加一個(gè)低頻的寄存器自檢發(fā)現(xiàn)漂移就重配成本極低但能消掉大部分玄學(xué)故障。5.2 密碼輸入到一半被刷卡事件打斷現(xiàn)象用戶正在輸密碼旁邊有人刷了一下卡密碼輸入被清空或串到別人的鑒權(quán)流程里。原因是在ST_WAIT_PWD狀態(tài)下沒有屏蔽新的刷卡事件。解決是狀態(tài)機(jī)在每個(gè)狀態(tài)只處理白名單內(nèi)的事件ST_WAIT_PWD期間收到的EV_CARD_OK直接丟棄而不是觸發(fā)狀態(tài)遷移。這個(gè)坑的本質(zhì)是事件過濾沒做在狀態(tài)入口而是散落在各處判斷里。5.3 EEPROM 寫壞導(dǎo)致白名單全丟現(xiàn)象某次掉電后再上電所有卡都刷不開。原因是寫記錄時(shí)沒有做「先寫數(shù)據(jù)區(qū)、最后寫有效位」的順序保證掉電發(fā)生在中間有效位已經(jīng)是 0x01 但數(shù)據(jù)是半截。解決是把有效位的寫入放在整條記錄的最后一步并且上電時(shí)對每條記錄做一次簡單的一致性校驗(yàn)比如保留字段的固定魔數(shù)校驗(yàn)不過就判為無效槽。5.4 繼電器動(dòng)作時(shí)單片機(jī)復(fù)位現(xiàn)象每次開鎖瞬間單片機(jī)重啟日志里出現(xiàn)異常復(fù)位記錄。原因是繼電器線圈續(xù)流回路不完整或者繼電器與單片機(jī)共用一條地線且走線過長動(dòng)作電流在地線阻抗上產(chǎn)生壓降把單片機(jī)的地電位抬起來。解決是續(xù)流二極管就近焊在線圈引腳上強(qiáng)電地與弱電地單點(diǎn)連接必要時(shí)給單片機(jī)電源加去耦電容并縮短地回路。5.5 讀卡距離標(biāo)稱 5 厘米實(shí)際只有 1 厘米現(xiàn)象實(shí)驗(yàn)室里讀卡正常裝進(jìn)金屬外殼后必須貼到面板上。原因是金屬外殼吸收了射頻場能量天線附近有金屬會嚴(yán)重改變諧振特性。解決是天線與金屬面板之間保持足夠間距或者在外殼內(nèi)側(cè)貼吸波材料同時(shí)重新標(biāo)定接收增益。這個(gè)坑幾乎每個(gè)做門禁硬件的都踩過屬于結(jié)構(gòu)設(shè)計(jì)與射頻設(shè)計(jì)沒對齊的典型。6. 資源受限下的設(shè)計(jì)取舍為什么這套架構(gòu)二十年不過時(shí)把整套系統(tǒng)攤開看它做的每一個(gè)選擇幾乎都是「在約束下找最不壞的解」。51 單片機(jī)沒有硬件乘法器、沒有 DMA、RAM 以字節(jié)計(jì)所以射頻協(xié)議棧被外包給專用芯片主控只做狀態(tài)機(jī)和存儲管理Flash 小所以白名單外掛 EEPROM用記錄對齊和有效位設(shè)計(jì)來對抗掉電IO 驅(qū)動(dòng)弱所以繼電器必須經(jīng)三極管隔離續(xù)流和保護(hù)電路一個(gè)不能少。這些不是教科書上的最優(yōu)解而是在成本、可靠性、開發(fā)周期三角里反復(fù)權(quán)衡后的工程解。值得多想一層的是雙因子這個(gè)設(shè)計(jì)本身。單刷卡系統(tǒng)的安全性完全押在卡的不可克隆性上而低頻 RFID 卡的 UID 克隆成本極低單密碼系統(tǒng)則敗在密碼會被偷看和分享。刷卡加密碼把兩種失效模式錯(cuò)開了卡丟了沒有密碼進(jìn)不去密碼泄露了沒有卡也進(jìn)不去。這種「讓兩個(gè)獨(dú)立因子各自覆蓋對方的短板」的思路比單純提高某一個(gè)因子的強(qiáng)度更有效也是這套架構(gòu)能長期存在于門禁、考勤、儲物場景的根本原因。如果你要基于這份資源動(dòng)手我的建議是先跑通讀卡和繼電器兩條鏈路再用狀態(tài)機(jī)把鑒權(quán)串起來最后補(bǔ)掉電保護(hù)和 EEPROM 一致性。原理圖、PCB 圖、論文和源程序放在一起看能對照出每個(gè)元件在代碼里對應(yīng)的那一行操作——這種軟硬對照的讀法比單獨(dú)看代碼或單獨(dú)看電路圖收獲大得多。從那以后我每次做帶執(zhí)行機(jī)構(gòu)的嵌入式項(xiàng)目都會先把掉電時(shí)序和狀態(tài)機(jī)的超時(shí)分支在紙上畫一遍再寫代碼這個(gè)習(xí)慣幫我省掉了太多現(xiàn)場返工的麻煩。希望幫到你。本文還有配套的精品資源點(diǎn)擊獲取