【极海APM32F407 Tiny Board】RFID读写IC卡(RC522)
[复制链接]
这段时间正在研究IC卡操作,正好借助APM32开发板做一次完整的读写验证。
刚开始接触相关产品,主要研究M1类IC卡。本测试基于RC522进行学习测试,手上的卡片是NXP的S50卡。
RC522芯片支持UART,IIC,SPI三种通信,手上的模组是SPI接口,相比IIC和UART,SPI操作速度应该是最快的。
板子连接如下图所示:
RC522模块连接在SPI1,管脚配置如下:
// SPI1-PB3:SCK-PB4:MISO-PB5:MOSI
{GPIOB, GPIO_MODE_AF, GPIO_OTYPE_PP, GPIO_PUPD_NOPULL, GPIO_AF_SPI1, GPIO_SPEED_50MHz, 1, GPIO_PIN_3 | GPIO_PIN_4 | GPIO_PIN_5},
// SPI1 AUX
{GPIOB, GPIO_MODE_OUT, GPIO_OTYPE_PP, GPIO_PUPD_NOPULL, 0, GPIO_SPEED_25MHz, 1, GPIO_PIN_6 | GPIO_PIN_7 | GPIO_PIN_8 },
port_io_pin_make(rfid_cs, PB, 6);
port_io_pin_make(rfid_rst, PB, 7);
控制RC522通信除了SPI接口之外还需要一个RST脚控制其复位。
M1卡的结构这里不做赘述,可以将其看作是无线通信且有密钥保护的EEPROM。
对M1卡的操作主要包含:选卡、密钥验证、读卡、写卡。
卡的读写以块为基本单位,每块16B。每扇区4块,4块中的最后一块为密钥及保护信息。
GITHUB上有基于LPC4xxx的操作代码,不过嵌入了fls驱动库,需要对底层操作进行替换。
为方便移植,本测试将底层操作全部使用函数指针实现,底层主要包含如下操作:
void MFRC522_SS_enable(void)
{
rfid_cs.dis();
}
void MFRC522_SS_disable(void)
{
rfid_cs.en();
}
void MFRC522_RST_enable(void)
{
rfid_rst.dis();
}
void MFRC522_RST_disable(void)
{
rfid_rst.en();
}
const struct MFRC522_HAL_T rc522_hal = {
MFRC522_SS_enable, // 片选生效
MFRC522_SS_disable, // 片选取消
MFRC522_RST_enable, // 复位生效
MFRC522_RST_disable, // 复位取消
ext_spi_rw}; // SPI读写
应用代码如下,一个串口命令输入处理函数和IC扫描程序:
/*******************************************************************************
* @brief: rc522 lib app
******************************************************************************/
struct MFRC522_T rfid_obj;
int last_balance = 0;
unsigned int last_user_ID;
uu08 rc522_scan_en = 0;
uu08 rc522_scan_mode = 0;
#define MIN_BALANCE 300
/*******************************************************************************
* @brief: rc522 lib start
* @param argc 参数数量
* @param argv 参数指针
* [url=home.php?mod=space&uid=784970]@return[/url] 错误代码
******************************************************************************/
int rc522_lib_init(int argc, char *argv[])
{
uu08 buff[80];
rfid_cs.dir_out_pp();
rfid_rst.dir_out_pp();
rc522_scan_mode = 0;
if(argc > 1)
{
if(argv[1][0] == 'w')
rc522_scan_mode = 1;
}
rfid_obj.hal = &rc522_hal;
setupRFID(&rfid_obj);
rc522_scan_en = 1;
dbg_puts(SYMBOL_LINE_DOT PTCR);
return 0;
}
DBG_FUNX_EXPORT(rc522_lib_init, "[r/w]");
/*******************************************************************************
* @brief: rc522 scan
* @param none
* @return none
******************************************************************************/
void MFRC522_scan(void)
{
if (rc522_scan_en)
{
// Look for new cards in RFID2
if (PICC_IsNewCardPresent(&rfid_obj))
{
// Select one of the cards
if (PICC_ReadCardSerial(&rfid_obj))
{
// int status = writeCardBalance(rfid_obj, 100000); // used to recharge the card
ic_card_opt();
}
}
}
}
obj_app_order(MFRC522_scan);
/*******************************************************************************
* @brief: rc522 opt
* @param none
* @return none
******************************************************************************/
void ic_card_opt(void)
{
// show card UID
dbg_print("\nCard uid bytes: ");
for (uint8_t i = 0; i < rfid_obj.uid.size; i++)
{
dbg_print(" %X", rfid_obj.uid.uidByte[i]);
}
dbg_print("\n\r");
// Convert the uid bytes to an integer, byte[0] is the MSB
last_user_ID =
(int)rfid_obj.uid.uidByte[3] |
(int)rfid_obj.uid.uidByte[2] << 8 |
(int)rfid_obj.uid.uidByte[1] << 16 |
(int)rfid_obj.uid.uidByte[0] << 24;
dbg_print("\nCard Read user ID: %X ", last_user_ID);
if(0 == rc522_scan_mode)
{
// Read the user balance
last_balance = readCardBalance(&rfid_obj);
if (last_balance == (-999))
{
// Error handling, the card does not have proper balance data inside
}
else
{
// Check for minimum balance
if (last_balance < MIN_BALANCE)
{
dbg_print("%08X, Insufficient balance\n", last_balance);
}
else
{
dbg_print("%08X, Balance OK\n", last_balance);
}
}
}
else
{
writeCardBalance(&rfid_obj, 0);
}
dbg_puts(SYMBOL_LINE_ADD PTCR);
}
演示视频:
调试串口启动IC卡扫描程序。
rc522_lib_init w 命令指示扫描到IC卡后执行写操作,具体是将IC的块0读出,将读取的数据写入块4
rc522_lib_init r 命令指示扫描到IC卡后执行读操作,具体是分别读取IC卡的块0和块4
发送rc522_lib_init w后将卡片开机驱动板,提示写操作后拿开IC卡。然后发送rc522_lib_init r指令,再次将卡片接近驱动板完成读卡操作。
rfid
测试代码托管在gitee平台:
|