STM32+RC522实现一个简单的读IC的唯一识别号的功能,使用的时STM32F103C8T6RC522两块板子,使用STM32CubeMX创建项目,Keil 5编辑项目。

一、STM32CubeMX 配置清单

1. 新建工程

  • 芯片选择 STM32F103C8Tx(LQFP48 封装)


2. System Core

配置项 原因
SYS → Debug Serial Wire ⚠️ 必选!否则 ST-Link 烧录一次后芯片被锁
RCC → HSE Crystal/Ceramic Resonator 使用板载 8MHz 晶振


3. 时钟树(Clock Configuration 页)

  • HSE = 8MHz → PLL Source = HSE → ×9 → SYSCLK = 72MHz
  • AHB = 72MHz、APB1 = 36MHz(÷2)、APB2 = 72MHz


4. Connectivity → SPI1(Full-Duplex Master)

参数 关键说明
Frame Format Motorola
Data Size 8 Bits
First Bit MSB First RC522 寄存器地址高位在前
Prescaler 32(APB2 72MHz÷32 = 2.25MHz RC522 手册上限 10MHz,调试期保守些更稳
CPOL Low 模式 0
CPHA 1 Edge 模式 0
NSS Software 片选必须手动拉低/拉高,不能用硬件 NSS
CRC Disabled


5. GPIO 手动配置(在芯片图上点引脚)

引脚 配置 说明
PA3 → GPIO_Output 上电默认 High 接 RC522 的 RST(驱动里会主动复位)
PA4 → GPIO_Output 上电默认 High 接 RC522 的 SDA/NSS,高电平=不选中
PA5/PA6/PA7 SPI1 自动分配(SCK/MISO/MOSI)
PA9/PA10 USART1 自动分配(TX/RX)


6. Connectivity → USART1

  • Connectivity → USART1 → Mode 选 Asynchronous

Parameter Settings:

  • Baud Rate:115200
  • Word Length:8 Bits
  • Parity:None
  • Stop Bits:1


7. Project Manager

  • Toolchain/IDE = MDK-ARM V5
  • Code Generator 页:勾选 "Generate peripheral initialization as a pair of .c/.h files"(推荐,spi.c/usart.c 分开便于阅读)
  • Minimal Heap Size = 0x200,Stack = 0x400(printf 需要一定栈空间)

点击 GENERATE CODE



二、Keil5 打开后:添加文件


1. 创建驱动文件rc522.crc522.h

rc522.h文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
#ifndef __RC522_H
#define __RC522_H

#include "main.h"

/* ==================== Pin Definitions ==================== */
/* Modify these to match your CubeMX GPIO configuration */
#define RC522_CS_PORT GPIOA
#define RC522_CS_PIN GPIO_PIN_4

#define RC522_RST_PORT GPIOA
#define RC522_RST_PIN GPIO_PIN_3

/* ==================== RC522 Register Addresses ==================== */
#define RC522_REG_COMMAND 0x01
#define RC522_REG_COMIEN 0x02
#define RC522_REG_DIVIEN 0x03
#define RC522_REG_COMIRQ 0x04
#define RC522_REG_DIVIRQ 0x05
#define RC522_REG_ERROR 0x06
#define RC522_REG_STATUS1 0x07
#define RC522_REG_STATUS2 0x08
#define RC522_REG_FIFODATA 0x09
#define RC522_REG_FIFOLEVEL 0x0A
#define RC522_REG_WATERLEVEL 0x0B
#define RC522_REG_CONTROL 0x0C
#define RC522_REG_BITFRAMING 0x0D
#define RC522_REG_COLL 0x0E
#define RC522_REG_MODE 0x11
#define RC522_REG_TXCONTROL 0x14
#define RC522_REG_TXASK 0x15
#define RC522_REG_TXSEL 0x16
#define RC522_REG_RXSEL 0x17
#define RC522_REG_RXTHRESHOLD 0x18
#define RC522_REG_DEMOD 0x19
#define RC522_REG_RFCFG 0x26
#define RC522_REG_TMOD 0x2A
#define RC522_REG_TPRESCALER 0x2B
#define RC522_REG_TReloadRegH 0x2C
#define RC522_REG_TReloadRegL 0x2D
#define RC522_REG_VERSION 0x37
#define RC522_REG_CRCRESULT_M 0x21
#define RC522_REG_CRCRESULT_L 0x22

/* ==================== PCD Commands ==================== */
#define PCD_IDLE 0x00
#define PCD_CALCCRC 0x03
#define PCD_TRANSCEIVE 0x0C
#define PCD_RESETPHASE 0x0F

/* ==================== PICC Commands ==================== */
#define PICC_REQIDL 0x26 /* REQ-A: query cards in IDLE state */
#define PICC_REQALL 0x52 /* WUP-A: query cards in IDLE/READY state */
#define PICC_ANTICOLL 0x93 /* Anti-collision level 1 */
#define PICC_HALT 0x50 /* Halt card */

/* ==================== Function Declarations ==================== */
void RC522_Init(void);
uint8_t RC522_ReadUID(uint8_t *uid);
/* Low-level functions; may be useful during debugging */
void RC522_WriteReg(uint8_t addr, uint8_t value);
uint8_t RC522_ReadReg(uint8_t addr);
void RC522_SetBitMask(uint8_t addr, uint8_t mask);
void RC522_ClearBitMask(uint8_t addr, uint8_t mask);
void RC522_AntennaOn(void);
void RC522_AntennaOff(void);
void RC522_Reset(void);
uint8_t RC522_ToCard(uint8_t command, uint8_t *sendData, uint8_t sendLen,
uint8_t *recvData, uint16_t *recvLen);
uint8_t RC522_Request(uint8_t reqMode, uint8_t *tagType);
uint8_t RC522_Anticoll(uint8_t *uid);
void RC522_CalcCRC(uint8_t *data, uint8_t len, uint8_t *crcOut);
/* Single-shot WUPA probe; always drains FIFO. rx[8] out, returns status */
uint8_t RC522_DebugRequest(uint8_t *rx, uint8_t *err, uint8_t *lastBits, uint8_t *level);

#endif /* __RC522_H */

rc522.c文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
#include "rc522.h"
#include "spi.h" /* SPI handle from CubeMX-generated spi.c */

extern SPI_HandleTypeDef hspi1; /* SPI instance used by this driver */

/* ==================== Low-level SPI Read/Write ==================== */

/**
* @brief Write one byte to an RC522 register
* @param addr Register address (0x00 ~ 0x3F)
* @param value Data to write
*/
void RC522_WriteReg(uint8_t addr, uint8_t value)
{
uint8_t txBuf[2];
/* RC522 write frame: address shifted left 1 bit, LSB = 0 */
txBuf[0] = (addr << 1) & 0x7E;
txBuf[1] = value;

HAL_GPIO_WritePin(RC522_CS_PORT, RC522_CS_PIN, GPIO_PIN_RESET);
HAL_SPI_Transmit(&hspi1, txBuf, 2, 100);
HAL_GPIO_WritePin(RC522_CS_PORT, RC522_CS_PIN, GPIO_PIN_SET);
}

/**
* @brief Read one byte from an RC522 register
* @param addr Register address
* @retval Read data byte
*/
uint8_t RC522_ReadReg(uint8_t addr)
{
uint8_t txBuf[2], rxBuf[2];
/* RC522 read frame: address shifted left 1 bit, LSB = 1 */
txBuf[0] = ((addr << 1) & 0x7E) | 0x80;
txBuf[1] = 0x00;

HAL_GPIO_WritePin(RC522_CS_PORT, RC522_CS_PIN, GPIO_PIN_RESET);
HAL_SPI_TransmitReceive(&hspi1, txBuf, rxBuf, 2, 100);
HAL_GPIO_WritePin(RC522_CS_PORT, RC522_CS_PIN, GPIO_PIN_SET);

return rxBuf[1];
}

/**
* @brief Set specific bits in a register
*/
void RC522_SetBitMask(uint8_t addr, uint8_t mask)
{
uint8_t temp = RC522_ReadReg(addr);
RC522_WriteReg(addr, temp | mask);
}

/**
* @brief Clear specific bits in a register
*/
void RC522_ClearBitMask(uint8_t addr, uint8_t mask)
{
uint8_t temp = RC522_ReadReg(addr);
RC522_WriteReg(addr, temp & (~mask));
}

/* ==================== Antenna Control ==================== */

/**
* @brief Turn on antenna (TX1 + TX2)
*/
void RC522_AntennaOn(void)
{
RC522_SetBitMask(RC522_REG_TXCONTROL, 0x03);
}

/**
* @brief Turn off antenna
*/
void RC522_AntennaOff(void)
{
RC522_ClearBitMask(RC522_REG_TXCONTROL, 0x03);
}

/* ==================== Reset ==================== */

/**
* @brief Hardware reset RC522 via RST pin
*/
void RC522_Reset(void)
{
HAL_GPIO_WritePin(RC522_RST_PORT, RC522_RST_PIN, GPIO_PIN_RESET);
HAL_Delay(10);
HAL_GPIO_WritePin(RC522_RST_PORT, RC522_RST_PIN, GPIO_PIN_SET);
HAL_Delay(50);
}

/* ==================== Initialization ==================== */

/**
* @brief Initialize RC522
* @note Call after SPI peripheral is initialized
*/
void RC522_Init(void)
{
/* 1. Hardware reset */
RC522_Reset();

/* 2. Soft reset */
RC522_WriteReg(RC522_REG_COMMAND, PCD_RESETPHASE);
HAL_Delay(10);

/* Safety-critical registers */
RC522_WriteReg(0x10, 0x00); /* TxModeReg: 106 kbps */
RC522_WriteReg(0x12, 0x00); /* RxModeReg: 106 kbps */
RC522_WriteReg(0x24, 0x26); /* ModWidthReg: modulation width */

/* 3. Timer configuration */
RC522_WriteReg(RC522_REG_TMOD, 0x8D); /* Timer: auto restart, clk = fOSC/16 */
RC522_WriteReg(RC522_REG_TPRESCALER, 0x3E); /* Prescaler = 62 */
RC522_WriteReg(RC522_REG_TReloadRegL, 30); /* Reload value low byte */
RC522_WriteReg(RC522_REG_TReloadRegH, 0); /* Reload value high byte */

/* 4. TX/RX mode: 106 kbps, NRZ encoding */
RC522_WriteReg(RC522_REG_TXASK, 0x40);
RC522_WriteReg(RC522_REG_MODE, 0x3D); /* CRC preset 0x6363 */
RC522_WriteReg(RC522_REG_RFCFG, 0x40); /* RxGain 33dB (safe default) */

/* 5. Turn on antenna */
RC522_AntennaOn();
}

/* ==================== Card Communication ==================== */

/**
* @brief Transceive: send command to card and receive response
* @param command PCD command (e.g. PCD_TRANSCEIVE)
* @param sendData Data to send
* @param sendLen Number of bytes to send
* @param recvData Receive buffer
* @param recvLen Input: max buffer size; Output: actual received byte count
* @retval 0 = success, non-zero = failure
*/
uint8_t RC522_ToCard(uint8_t command, uint8_t *sendData, uint8_t sendLen,
uint8_t *recvData, uint16_t *recvLen)
{
uint8_t status = 1; /* default: failure */
uint8_t irqEn = 0x00;
uint8_t waitIRq = 0x00;
uint16_t i;

if (command == PCD_TRANSCEIVE) {
irqEn = 0x77; /* Enable RxIrq, TxIrq, IdleIrq */
waitIRq = 0x30; /* Wait for RxIrq or IdleIrq */
}

/* enable & clear IRQs */
RC522_WriteReg(RC522_REG_COMIEN, irqEn | 0x80);
/* ComIrq bits are write-1-to-clear: write 0x7F to clear ALL pending
flags. Writing 0x80 (reserved bit) leaves stale RxIrq/IdleIrq set,
which makes the next transceive "succeed" immediately on garbage. */
RC522_WriteReg(RC522_REG_COMIRQ, 0x7F);
RC522_SetBitMask(RC522_REG_FIFOLEVEL, 0x80); /* Flush FIFO */

/* Set idle, write data to FIFO */
RC522_WriteReg(RC522_REG_COMMAND, PCD_IDLE);
for (i = 0; i < sendLen; i++) {
RC522_WriteReg(RC522_REG_FIFODATA, sendData[i]);
}
/* Execute command */
RC522_WriteReg(RC522_REG_COMMAND, command);
if (command == PCD_TRANSCEIVE) {
RC522_SetBitMask(RC522_REG_BITFRAMING, 0x80); /* StartSend */
}

/* Wait for completion */
i = 2000;
do {
uint8_t irq = RC522_ReadReg(RC522_REG_COMIRQ);
if (irq & waitIRq) {
status = 0;
break;
}
if (irq & 0x01) { /* TimerIrq */
status = 1;
break;
}
} while (--i);

RC522_ClearBitMask(RC522_REG_BITFRAMING, 0x80);

if (status == 0) {
uint8_t error = RC522_ReadReg(RC522_REG_ERROR);
if (error & 0x1B) { /* Error bits set */
status = 1;
}
}

if (status == 0 && recvData != NULL) {
uint8_t fifoLevel = RC522_ReadReg(RC522_REG_FIFOLEVEL);
uint8_t lastBits = RC522_ReadReg(RC522_REG_CONTROL) & 0x07;
if (lastBits) {
*recvLen = (fifoLevel - 1) * 8 + lastBits;
} else {
*recvLen = fifoLevel * 8;
}
if (fifoLevel == 0) {
fifoLevel = 1;
}
if (fifoLevel > *recvLen / 8) {
fifoLevel = *recvLen / 8;
}
for (i = 0; i < fifoLevel; i++) {
recvData[i] = RC522_ReadReg(RC522_REG_FIFODATA);
}
/* NOTE: on return *recvLen is in BYTES (see conversion below).
Callers must compare 2 for ATQA and 5 for UID+BCC, not 16/40. */
*recvLen = (*recvLen + 7) / 8; /* Convert to byte count */
}

return status;
}

/* ==================== Request (寻卡) ==================== */

/**
* @brief Request card type (REQA/WUPA)
* @param reqMode Request mode (PICC_REQIDL or PICC_REQALL)
* @param tagType Output: card type (2 bytes ATQA)
* @retval 0 = success, non-zero = failure
*/
uint8_t RC522_Request(uint8_t reqMode, uint8_t *tagType)
{
uint8_t status;
uint16_t backBits = 0;
uint8_t retry;
uint8_t txCmd; /* dedicated TX buffer, must NOT reuse tagType */

tagType[0] = 0;
tagType[1] = 0;
txCmd = reqMode;

for (retry = 0; retry < 5; retry++) {
RC522_WriteReg(RC522_REG_BITFRAMING, 0x07); /* 7-bit short frame */
status = RC522_ToCard(PCD_TRANSCEIVE, &txCmd, 1, tagType, &backBits);
if (status == 0 && backBits == 2) { /* ToCard returns BYTES: 2-byte ATQA */
return 0;
}
HAL_Delay(10);
}
return 1;
}

/*
* Single-shot WUPA (0x52) diagnostic probe, self-contained (does NOT use
* RC522_ToCard) so that error/lastBits/FIFOLevel are captured immediately
* after the transceive and the raw FIFO is ALWAYS drained.
*
* Success requires ALL of: RxIrq observed, no protocol/parity/collision/
* overflow error, and exactly 2 full bytes (16-bit ATQA). This rejects
* noise-triggered 1-bit phantom frames at high gain.
*/
uint8_t RC522_DebugRequest(uint8_t *rx, uint8_t *err, uint8_t *lastBits, uint8_t *level)
{
uint8_t cmd = PICC_REQALL; /* WUPA: answered in IDLE and READY */
uint16_t n;
uint8_t irq;
uint8_t i, got;

for (i = 0; i < 8; i++) {
rx[i] = 0;
}

RC522_WriteReg(RC522_REG_COMIEN, 0x77 | 0x80); /* Rx/Tx/Idle/Timer irq */
RC522_WriteReg(RC522_REG_COMIRQ, 0x7F); /* clear all (w1c) */
RC522_SetBitMask(RC522_REG_FIFOLEVEL, 0x80); /* flush FIFO */
RC522_WriteReg(RC522_REG_COMMAND, PCD_IDLE);
RC522_WriteReg(RC522_REG_BITFRAMING, 0x07); /* 7-bit short frame */
RC522_WriteReg(RC522_REG_FIFODATA, cmd);
RC522_WriteReg(RC522_REG_COMMAND, PCD_TRANSCEIVE);
RC522_SetBitMask(RC522_REG_BITFRAMING, 0x80); /* StartSend */

n = 2000;
do {
irq = RC522_ReadReg(RC522_REG_COMIRQ);
} while (!(irq & 0x30) && !(irq & 0x01) && --n); /* Rx/Idle or Timer */

RC522_ClearBitMask(RC522_REG_BITFRAMING, 0x80);

*err = RC522_ReadReg(RC522_REG_ERROR);
*lastBits = RC522_ReadReg(RC522_REG_CONTROL) & 0x07;
*level = RC522_ReadReg(RC522_REG_FIFOLEVEL) & 0x3F;

got = (*level > 8) ? 8 : *level;
for (i = 0; i < got; i++) {
rx[i] = RC522_ReadReg(RC522_REG_FIFODATA);
}

if ((irq & 0x20) && (*err & 0x1B) == 0 &&
*level == 2 && *lastBits == 0) {
return 0; /* valid 16-bit ATQA */
}
return 1;
}

/* ==================== Anti-collision ==================== */

/**
* @brief Anti-collision: read card UID
* @param uid Output buffer (5 bytes: 4-byte UID + 1-byte BCC)
* @retval 0 = success, 1 = failure, 2 = BCC check failed
*/
uint8_t RC522_Anticoll(uint8_t *uid)
{
uint8_t status;
uint8_t sendData[2];
uint16_t recvBits = 0;

RC522_WriteReg(RC522_REG_BITFRAMING, 0x00); /* 8-bit frame */
sendData[0] = PICC_ANTICOLL;
sendData[1] = 0x20; /* Anti-collision NVB */

status = RC522_ToCard(PCD_TRANSCEIVE, sendData, 2, uid, &recvBits);

if (status == 0 && recvBits == 5) { /* 5 bytes = 40 bits */
/* Verify BCC checksum */
uint8_t check = uid[0] ^ uid[1] ^ uid[2] ^ uid[3];
if (check != uid[4]) {
return 2; /* BCC check failed */
}
return 0;
}
return 1;
}

/* ==================== Read UID (main interface) ==================== */

/* Calculate ISO14443 CRC-A using the RC522 hardware CRC unit. */
void RC522_CalcCRC(uint8_t *data, uint8_t len, uint8_t *crcOut)
{
uint8_t i;
uint16_t wait = 1000;

RC522_WriteReg(RC522_REG_COMMAND, PCD_IDLE);
RC522_WriteReg(RC522_REG_DIVIRQ, 0x04); /* clear CRCIRq (w1c) */
RC522_SetBitMask(RC522_REG_FIFOLEVEL, 0x80); /* flush FIFO */
for (i = 0; i < len; i++) {
RC522_WriteReg(RC522_REG_FIFODATA, data[i]);
}
RC522_WriteReg(RC522_REG_COMMAND, PCD_CALCCRC);
do {
} while (!(RC522_ReadReg(RC522_REG_DIVIRQ) & 0x04) && --wait);

crcOut[0] = RC522_ReadReg(RC522_REG_CRCRESULT_L); /* LSB @0x22 */
crcOut[1] = RC522_ReadReg(RC522_REG_CRCRESULT_M); /* MSB @0x21 */
RC522_WriteReg(RC522_REG_COMMAND, PCD_IDLE);
}

uint8_t RC522_ReadUID(uint8_t *uid)
{
uint8_t tagType[2];
uint8_t uidBuf[5];

/* 1. Request (WUPA) */
if (RC522_Request(PICC_REQALL, tagType) != 0) {
return 1; /* Request failed */
}

/* 2. Anti-collision to get UID */
if (RC522_Anticoll(uidBuf) != 0) {
return 2; /* Anti-collision failed */
}

/* 3. Copy 4-byte UID to output */
uid[0] = uidBuf[0];
uid[1] = uidBuf[1];
uid[2] = uidBuf[2];
uid[3] = uidBuf[3];

/* 4. Halt card to prevent repeated reads */
uint8_t haltCmd[4];
haltCmd[0] = PICC_HALT;
haltCmd[1] = 0;
RC522_CalcCRC(haltCmd, 2, &haltCmd[2]); /* append CRC-A over 50 00 */
uint16_t haltLen = 0;
RC522_ToCard(PCD_TRANSCEIVE, haltCmd, 4, NULL, &haltLen);

return 0;
}

2. 添加驱动组

把驱动文件夹拷进工程(在项目根目录的MDK-ARM\目录创建rc522_driver\文件夹并进入,创建两个文件 rc522.c / rc522.h),然后: - Project 窗口 → 右键 Target → Add Group → 命名 rc522_driver → 组内 Add Existing Files 加入 rc522.c


3. 添加头文件搜索路径

  • 魔法棒 → C/C++Include Paths → 添加 ..\MDK-ARM\rc522_driver(相对路径按工程文件位置写)


4. 勾选 MicroLIB

  • 魔法棒 → Target → 勾选 ✅ Use MicroLIB
  • 因为我们用自定义 fputc 重定向 printf,MicroLIB 可避免标准库半主机(semihosting)导致的卡死



三、需要修改的文件(只有 main.c)


1. printf 重定向(USER CODE 0 区,main.c:62


1
2
3
4
5
6
7
8
9
/* USER CODE BEGIN 0 */
#include <stdio.h>

int fputc(int ch, FILE *f)
{
HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 10);
return ch;
}
/* USER CODE END 0 */


2. 初始化(USER CODE 2 区)

⚠️ 重点:所有用户初始化代码必须写在 /* USER CODE BEGIN 2 *//* USER CODE END 2 */ 之间

1
2
3
4
5
/* USER CODE BEGIN 2 */
RC522_Init();
printf("Version: 0x%02X\r\n", RC522_ReadReg(RC522_REG_VERSION));
printf("Waiting for 13.56MHz ISO14443A tag...\r\n");
/* USER CODE END 2 */


3. 主循环(USER CODE WHILE 区,当前 main.c:123 的最终形态)

1
2
3
4
5
6
7
8
9
10
11
12
/* USER CODE BEGIN WHILE */
while (1)
{
uint8_t uid[5] = {0};
if (RC522_ReadUID(uid) == 0) {
printf("Card found UID: %02X %02X %02X %02X\r\n",
uid[0], uid[1], uid[2], uid[3]);
HAL_Delay(800);
}
HAL_Delay(100);
/* USER CODE END WHILE */
}

注意:RC522_Init() 的调用、RC522 的头文件引用等,都必须套在 USER CODE BEGIN/END 注释对内,CubeMX 重新生成时只保留这些区内的内容。

⬆︎TOP