近场通信(NEAR FIELD COMMUNICATION, NFC),又称近距离无线通信,是一种短距离的高频无线通信技术,允许电子设备之间进行非接触式点对点数据传输(在十厘米内)交换数据。这个技术由免接触式射频识别(RFID)演变而来,并向下兼容RFID,最早由SONY和PHILIPS各自开发成功,主要用于手机等手持设备中提供M2M(MACHINE TO MACHINE)的通信。由于近场通讯具有天然的安全性,因此,NFC技术被认为在手机支付等领域具有很大的应用前景。同时,NFC也因为其相比于其他无线通讯技术较好的安全性被中国物联网校企联盟比作机器之间的“安全对话”。
模块来源
规格参数
工作电压:3.3V
工作电流:10-26mA
模块尺寸:40mm×60mm
支持的卡类型:mifare1 S50、mifare1 S70、mifare UltraLight、mifare Pro、mifare Desfire
控制方式:SPI
以上信息见厂家资料文件
移植过程
我们的目标是将例程移植至开发板上【能够识别IC卡的ID并进行读写的功能】。首先要获取资料,查看数据手册应如何实现读取数据,再移植至我们的工程。
查看资料
S50非接触式IC卡,分为16个扇区,每个扇区由4块(块0、块1、块2、块3)组成,(我们也将16个扇区的64个块按绝对地址编号为0~63,存贮结构如下图所示:
第0扇区的块0(即绝对地址0块),它用于存放厂商代码,已经固化,不可更改。
每个扇区的块0、块1、块2为数据块,可用于存贮数据。数据块可作两种应用:
- 用作一般的数据保存,可以进行读、写操作。
- 用作数据值,可以进行初始化值、加值、减值、读值操作。
每个扇区的块3为控制块,包括了密码A、存取控制、密码B。具体结构如下:
- 每个扇区的密码和存取控制都是独立的,可以根据实际需要设定各自的密码及存取控制。存取控制为4个字节,共32位,扇区中的每个块(包括数据块和控制块)的存取条件是由密码和存取控制共同决定的,在存取控制中每个块都有相应的三个控制位,定义如下:
块0: C10 C20 C30
块1: C11 C21 C31
块2: C12 C22 C32
块3: C13 C23 C33
2
3
4
三个控制位以正和反两种形式存在于存取控制字节中,决定了该块的访问权限(如进行减值操作必须验证KEY A,进行加值操作必须验证KEY B,等等)。
三个控制位在存取控制字节中的位置,以块0为例(对块0的控制):
- 数据块(块0、块1、块2)的存取控制如下:
(KeyA|B 表示密码A或密码B,Never表示任何条件下不能实现)
例如:当块0的存取控制位C10 C20 C30=1 0 0时,验证密码A或密码B正确后可读;验证密码B正确后可写;不能进行加值、减值操作。
- 控制块块3的存取控制与数据块(块0、1、2)不同,它的存取控制如下:
例如:当块3的存取控制位C13 C23 C33=1 0 0时,表示:
密码A:不可读,验证KEYA或KEYB正确后,可写(更改)。
存取控制:验证KEYA或KEYB正确后,可读、可写。
密码B:验证KEYA或KEYB正确后,可读、可写。
- M1射频卡与读写器的通讯
引脚选择
这里选择的引脚见引脚接线表
代码移植
下载为大家准备的驱动代码文件夹,复制到自己工程中\luban-lite\application\rt-thread\helloworld\user-bsp
文件夹下
提示
如果未找到 user-bsp
这个文件夹,说明你未进行模块移植的前置操作。请转移到手册使用必要操作(点击跳转)中进行必要的配置操作!!!
接下来打开自己的工程,开始修改Kconfig文件。
1、在 VSCode 中打开 application\rt-thread\helloworld\Kconfig 文件
2、在该文件的 #endif
前面添加该模块的 Kconfig路径语句
# RC522射频IC卡识别模块
source "application/rt-thread/helloworld/user-bsp/rc522-rf-ic-card-identification-module/Kconfig"
2
menuconfig操作
1、我们 双击 luban-lite
文件夹下的 win_env.bat
脚本打开env工具:
2、输入以下命令列出所有可用的默认配置:
scons --list-def
3、选择 d13x_JLC_rt-thread_helloworld
这个配置!这个是我们衡山派开发板的默认配置!输入以下命令即可:
scons --apply-def=7
或者
scons --apply-def=d13x_JLC_rt-thread_helloworld_defconfig
这两个命令作用是一样的,一个是 文件名 ,一个是 编号 !!!
4、输入以下命令进入menuconfig菜单
scons --menuconfig
进入以下界面:
5、选中 Porting code using the LCKFB module
按
Y
选中按
N
取消选中方向键
左右
调整 最下面菜单的选项方向键
上下
调整 列表的选项
回车
执行最下面菜单的选项
6、回车进入 Porting code using the LCKFB module
菜单
7、按方向键 上下
选中 Using RC522 Radio Frequency IC Card Recognition Module
后按 Y
键,看到前面括号中出现一个 *
号,就可以下一步了。
8、按方向键 左右
选中 <Save>
然后一路回车
,然后 退出
即可
编译
我们 保存并退出menuconfig菜单 之后,输入以下命令进行编译:
scons
或
scons -j16
-j 用来选择参与编译的核心数: 我这里是选择16
大家可以根据自己的电脑来选择
核心越多编译越快
如果写的数量高于电脑本身,那么就自动按照最高可用的来运行!
镜像烧录
编译完成之后会在 \luban-lite\output\d13x_JLC_rt-thread_helloworld\images
文件夹下生成一个 d13x_JLC_v1.0.0.img
镜像文件!
然后我们烧录镜像,具体的教程请查看:镜像烧录(点击跳转🚀)
到这里完成了,请移步到 最后一节 进行移植验证。
工程代码解析
bsp_rc522.c
/*
* 立创开发板软硬件资料与相关扩展板软硬件资料官网全部开源
* 开发板官网:www.lckfb.com
* 文档网站:wiki.lckfb.com
* 技术支持常驻论坛,任何技术问题欢迎随时交流学习
* 嘉立创社区问答:https://www.jlc-bbs.com/lckfb
* 关注bilibili账号:【立创开发板】,掌握我们的最新动态!
* 不靠卖板赚钱,以培养中国工程师为己任
*/
#include <getopt.h>
#include <string.h>
#include <rtthread.h>
#include <aic_core.h>
#include <stdlib.h>
#include <sys/time.h>
#include "hal_adcim.h"
#include "rtdevice.h"
#include "aic_log.h"
#include "hal_gpai.h"
#include <stdio.h>
#include "aic_hal_gpio.h"
#include "bsp_rc522.h"
#define QPSI_BUS_NANE "qspi3" // qspi总线名称
#define RC522_DEVICE_NAME "rc522_ic"
#define RC522_SPI_CS_PIN rt_pin_get("PC.9")
#define RC522_RST_PIN rt_pin_get("PE.14")
#define RC522_CS_Enable() rt_pin_write(RC522_SPI_CS_PIN, PIN_LOW)
#define RC522_CS_Disable() rt_pin_write(RC522_SPI_CS_PIN, PIN_HIGH)
#define RC522_Reset_Enable() rt_pin_write(RC522_RST_PIN, PIN_LOW)
#define RC522_Reset_Disable() rt_pin_write(RC522_RST_PIN, PIN_HIGH)
struct rt_qspi_device *rc522_dev = RT_NULL;
static void delay_us(uint32_t us){ aicos_udelay(us); }
static void delay_ms(uint32_t ms){ rt_thread_mdelay(ms); }
/**********************************************************
* 函 数 名 称:RC522_Qspi_Attach
* 函 数 功 能:挂载qspi的设备
* 传 入 参 数:无
* 函 数 返 回:无
* 作 者:LC
* 备 注:LP
**********************************************************/
static int RC522_Qspi_Attach(void)
{
static struct rt_qspi_device *qspi_device = RT_NULL; // qspi设备结构体
// 为spi结构体申请一片空间
qspi_device = (struct rt_qspi_device *)rt_malloc(sizeof(struct rt_qspi_device));
if(RT_NULL == qspi_device)
{
LOG_E("Failed to malloc the qspi device.");
LOG_E("file: %s",__FILE__);
LOG_E("line: %d",__LINE__);
return -RT_ENOMEM;
}
rt_kprintf("malloc the qspi succeed.\r\n");
qspi_device->enter_qspi_mode = RT_NULL;
qspi_device->exit_qspi_mode = RT_NULL;
qspi_device->config.qspi_dl_width = 1;
int ret = rt_spi_bus_attach_device(&qspi_device->parent, RC522_DEVICE_NAME, QPSI_BUS_NANE, RT_NULL);
if(ret != RT_EOK)
{
LOG_E("Failed to rt_spi_bus_attach_device.");
LOG_E("file: %s",__FILE__);
LOG_E("line: %d",__LINE__);
return ret;
}
return RT_EOK;
}
/**********************************************************
* 函 数 名 称:RC522_Qspi_Init
* 函 数 功 能:配置qspi进行初始化
* 传 入 参 数:无
* 函 数 返 回:无
* 作 者:LCKFB
* 备 注:LP
**********************************************************/
static int RC522_Qspi_Init(void)
{
// struct rt_qspi_device *g_qspi;
struct rt_qspi_configuration qspi_cfg;
struct rt_device *dev;
char *name;
int ret = 0;
name = RC522_DEVICE_NAME;
rc522_dev = (struct rt_qspi_device *)rt_device_find(name);
if (!rc522_dev)
{
LOG_E("Failed to get device in name %s\n", name);
return -RT_ERROR;
}
dev = (struct rt_device *)rc522_dev;
if (dev->type != RT_Device_Class_SPIDevice)
{
rc522_dev = RT_NULL;
LOG_E("%s is not SPI device.\n", name);
return -RT_ERROR;
}
rt_memset(&qspi_cfg, 0, sizeof(qspi_cfg));
qspi_cfg.qspi_dl_width = 1; // QSPI 总线位宽,单线模式 1 位、双线模式 2 位,4 线模式 4 位
qspi_cfg.parent.mode = RT_SPI_MASTER | RT_SPI_MODE_0 | RT_SPI_MSB;
qspi_cfg.parent.data_width = 8;
qspi_cfg.parent.max_hz = 1 * 1000 * 1000; // 1M
ret = rt_qspi_configure(rc522_dev, &qspi_cfg);
if (ret < 0)
{
LOG_E("qspi configure failure.");
return -RT_ERROR;;
}
return RT_EOK;
}
/**********************************************************
* 函 数 名 称:RC522_Init
* 函 数 功 能:初始化模块
* 传 入 参 数:无
* 函 数 返 回:RT_EOK成功 -RT_ERROR失败
* 作 者:LCKFB
* 备 注:
**********************************************************/
int RC522_Init(void)
{
/* 设备挂载 */
if(RT_EOK != RC522_Qspi_Attach())
{
LOG_E("RC522_Init-->RC522_Qspi_Attach failed !!!");
return -RT_ERROR;
}
/* 设备初始化 */
if(RT_EOK != RC522_Qspi_Init())
{
LOG_E("RC522_Init-->RC522_Qspi_Init failed !!!");
return -RT_ERROR;
}
/* 软件CS */
rt_pin_mode(RC522_SPI_CS_PIN, PIN_MODE_OUTPUT);
/* RST引脚 */
rt_pin_mode(RC522_RST_PIN, PIN_MODE_OUTPUT);
return RT_EOK;
}
////////////////SPI与RC522通信///////////////////////////////////////////
/*
读写寄存器:
cmd = 1 读数据
cmd = 0 写数据
*/
static uint8_t RC522_Writ_Read_REG(uint8_t cmd, uint8_t dat)
{
struct rt_qspi_message msg;
uint8_t recv_buff = 0;
rt_memset(&msg, 0, sizeof(msg));
msg.instruction.content = 0; /* 指令内容 */
msg.instruction.qspi_lines = 0; /* 指令模式,单线模式 1 位、双线模式 2 位,4 线模式 4 位 */
msg.alternate_bytes.content = 0; /* 地址/交替字节 内容 */
msg.alternate_bytes.size = 0; /* 地址/交替字节 长度 */
msg.alternate_bytes.qspi_lines = 0; /* 地址/交替字节 模式,单线模式 1 位、双线模式 2 位,4 线模式 4 位 */
msg.dummy_cycles = 0; /* 空指令周期阶段 */
msg.qspi_data_lines = 1; /* QSPI 总线位宽 */
// 传输一条消息,开始发送数据时片选选中,函数返回时释放片选。
msg.parent.length = 1; /* 发送 / 接收 数据字节数 */
msg.parent.next = RT_NULL; /* 指向继续发送的下一条消息的指针 */
msg.parent.cs_take = 1; /* 片选选中 */
msg.parent.cs_release = 1; /* 释放片选 */
if(cmd) // read数据
{
msg.parent.send_buf = RT_NULL; /* 发送缓冲区指针 */
msg.parent.recv_buf = &recv_buff; /* 接收缓冲区指针 */
}
else // write
{
msg.parent.send_buf = &dat; /* 发送缓冲区指针 */
msg.parent.recv_buf = RT_NULL; /* 接收缓冲区指针 */
}
int ret = rt_qspi_transfer_message(rc522_dev, &msg);;
if(ret != msg.parent.length)
{
LOG_E("rt_qspi_transfer_message failed!!");
}
return recv_buff;
}
//////////////////////////对RC522寄存器的操作//////////////////////////////////
/* 读取RC522指定寄存器的值
向RC522指定寄存器中写入指定的数据
置位RC522指定寄存器的指定位
清位RC522指定寄存器的指定位
*/
/**
* @brief :读取RC522指定寄存器的值
* @param :Address:寄存器的地址
* @retval :寄存器的值
*/
uint8_t RC522_Read_Register( uint8_t Address )
{
uint8_t data = 0;
uint8_t Addr = 0;
Addr = ( (Address<<1)&0x7E )|0x80;
rt_spi_take_bus((struct rt_spi_device *)rc522_dev);
RC522_CS_Enable();
RC522_Writ_Read_REG(0, Addr); // 写数据
data = RC522_Writ_Read_REG(1, 0xff); // 读数据
RC522_CS_Disable();
rt_spi_release_bus((struct rt_spi_device *)rc522_dev);
return data;
}
/**
* @brief :向RC522指定寄存器中写入指定的数据
* @param :Address:寄存器地址
ata:要写入寄存器的数据
* @retval :无
*/
void RC522_Write_Register( uint8_t Address, uint8_t data )
{
uint8_t Addr;
Addr = ( Address<<1 )&0x7E;
rt_spi_take_bus((struct rt_spi_device *)rc522_dev);
RC522_CS_Enable();
RC522_Writ_Read_REG(0, Addr);
RC522_Writ_Read_REG(0, data);
RC522_CS_Disable();
rt_spi_release_bus((struct rt_spi_device *)rc522_dev);
}
/**
* @brief :置位RC522指定寄存器的指定位
* @param :Address:寄存器地址
mask:置位值
* @retval :无
*/
void RC522_SetBit_Register( uint8_t Address, uint8_t mask )
{
uint8_t temp;
/* 获取寄存器当前值 */
temp = RC522_Read_Register( Address );
/* 对指定位进行置位操作后,再将值写入寄存器 */
RC522_Write_Register( Address, temp|mask );
}
/**
* @brief :清位RC522指定寄存器的指定位
* @param :Address:寄存器地址
mask:清位值
* @retval :无
*/
void RC522_ClearBit_Register( uint8_t Address, uint8_t mask )
{
uint8_t temp;
/* 获取寄存器当前值 */
temp = RC522_Read_Register( Address );
/* 对指定位进行清位操作后,再将值写入寄存器 */
RC522_Write_Register( Address, temp&(~mask) );
}
///////////////////对RC522的基础通信///////////////////////////////////
/*
开启天线
关闭天线
复位RC522
设置RC522工作方式
*/
/**
* @brief :开启天线
* @param :无
* @retval :无
*/
void RC522_Antenna_On( void )
{
uint8_t k;
k = RC522_Read_Register( TxControlReg );
/* 判断天线是否开启 */
if( !( k&0x03 ) )
RC522_SetBit_Register( TxControlReg, 0x03 );
}
/**
* @brief :关闭天线
* @param :无
* @retval :无
*/
void RC522_Antenna_Off( void )
{
/* 直接对相应位清零 */
RC522_ClearBit_Register( TxControlReg, 0x03 );
}
/**
* @brief :复位RC522
* @param :无
* @retval :无
*/
void RC522_Rese( void )
{
long timeOut = 100000;
RC522_Reset_Disable();
delay_ms(1);
RC522_Reset_Enable();
delay_ms(1);
RC522_Reset_Disable();
delay_ms(1);
RC522_Write_Register( CommandReg, 0x0F );
while( (RC522_Read_Register( CommandReg )&0x10) && timeOut )
{
timeOut--;
aicos_udelay(1);
}
if(!timeOut)
{
LOG_E("RC522_Rese failed !!");
}
/* 缓冲一下 */
delay_us ( 1 );
RC522_Write_Register( ModeReg, 0x3D ); //定义发送和接收常用模式
RC522_Write_Register( TReloadRegL, 30 ); //16位定时器低位
RC522_Write_Register( TReloadRegH, 0 ); //16位定时器高位
RC522_Write_Register( TModeReg, 0x8D ); //内部定时器的设置
RC522_Write_Register( TPrescalerReg, 0x3E ); //设置定时器分频系数
RC522_Write_Register( TxAutoReg, 0x40 ); //调制发送信号为100%ASK
}
/**
* @brief :设置RC522的工作方式
* @param :Type:工作方式
* @retval :无
M500PcdConfigISOType
*/
void RC522_Config_Type( char Type )
{
if( Type=='A' )
{
RC522_ClearBit_Register( Status2Reg, 0x08 );
RC522_Write_Register( ModeReg, 0x3D );
RC522_Write_Register( RxSelReg, 0x86 );
RC522_Write_Register( RFCfgReg, 0x7F );
RC522_Write_Register( TReloadRegL, 30 );
RC522_Write_Register( TReloadRegH, 0 );
RC522_Write_Register( TModeReg, 0x8D );
RC522_Write_Register( TPrescalerReg, 0x3E );
delay_us(2);
/* 开天线 */
RC522_Antenna_On();
}
}
/////////////////////////控制RC522与M1卡的通信///////////////////////////////////////
/*
通过RC522和M1卡通讯(数据的双向传输)
寻卡
防冲突
用RC522计算CRC16(循环冗余校验)
选定卡片
校验卡片密码
在M1卡的指定块地址写入指定数据
读取M1卡的指定块地址的数据
让卡片进入休眠模式
*/
/**
* @brief :通过RC522和ISO14443卡通讯
* @param :ucCommand:RC522命令字
* pInData:通过RC522发送到卡片的数据
* ucInLenByte:发送数据的字节长度
* pOutData:接收到的卡片返回数据
* pOutLenBit:返回数据的位长度
* @retval :状态值MI_OK,成功
*/
char PcdComMF522 ( uint8_t ucCommand, uint8_t * pInData, uint8_t ucInLenByte, uint8_t * pOutData, uint32_t * pOutLenBit )
{
char cStatus = MI_ERR;
uint8_t ucIrqEn = 0x00;
uint8_t ucWaitFor = 0x00;
uint8_t ucLastBits;
uint8_t ucN;
uint32_t ul;
switch ( ucCommand )
{
case PCD_AUTHENT: //Mifare认证
ucIrqEn = 0x12; //允许错误中断请求ErrIEn 允许空闲中断IdleIEn
ucWaitFor = 0x10; //认证寻卡等待时候 查询空闲中断标志位
break;
case PCD_TRANSCEIVE: //接收发送 发送接收
ucIrqEn = 0x77; //允许TxIEn RxIEn IdleIEn LoAlertIEn ErrIEn TimerIEn
ucWaitFor = 0x30; //寻卡等待时候 查询接收中断标志位与 空闲中断标志位
break;
default:
break;
}
RC522_Write_Register ( ComIEnReg, ucIrqEn | 0x80 ); //IRqInv置位管脚IRQ与Status1Reg的IRq位的值相反
RC522_ClearBit_Register ( ComIrqReg, 0x80 ); //Set1该位清零时,CommIRqReg的屏蔽位清零
RC522_Write_Register ( CommandReg, PCD_IDLE ); //写空闲命令
RC522_SetBit_Register ( FIFOLevelReg, 0x80 ); //置位FlushBuffer清除内部FIFO的读和写指针以及ErrReg的BufferOvfl标志位被清除
for ( ul = 0; ul < ucInLenByte; ul ++ )
RC522_Write_Register ( FIFODataReg, pInData [ ul ] ); //写数据进FIFOdata
RC522_Write_Register ( CommandReg, ucCommand ); //写命令
if ( ucCommand == PCD_TRANSCEIVE )
RC522_SetBit_Register(BitFramingReg,0x80); //StartSend置位启动数据发送 该位与收发命令使用时才有效
ul = 1000;//根据时钟频率调整,操作M1卡最大等待时间25ms
do //认证 与寻卡等待时间
{
ucN = RC522_Read_Register ( ComIrqReg ); //查询事件中断
aicos_udelay(1);
ul --;
} while ( ( ul != 0 ) && ( ! ( ucN & 0x01 ) ) && ( ! ( ucN & ucWaitFor ) ) ); //退出条件i=0,定时器中断,与写空闲命令
RC522_ClearBit_Register ( BitFramingReg, 0x80 ); //清理允许StartSend位
if ( ul != 0 )
{
if ( ! ( RC522_Read_Register ( ErrorReg ) & 0x1B ) ) //读错误标志寄存器BufferOfI CollErr ParityErr ProtocolErr
{
cStatus = MI_OK;
if ( ucN & ucIrqEn & 0x01 ) //是否发生定时器中断
cStatus = MI_NOTAGERR;
if ( ucCommand == PCD_TRANSCEIVE )
{
ucN = RC522_Read_Register ( FIFOLevelReg ); //读FIFO中保存的字节数
ucLastBits = RC522_Read_Register ( ControlReg ) & 0x07; //最后接收到得字节的有效位数
if ( ucLastBits )
* pOutLenBit = ( ucN - 1 ) * 8 + ucLastBits; //N个字节数减去1(最后一个字节)+最后一位的位数 读取到的数据总位数
else
* pOutLenBit = ucN * 8; //最后接收到的字节整个字节有效
if ( ucN == 0 )
ucN = 1;
if ( ucN > MAXRLEN )
ucN = MAXRLEN;
for ( ul = 0; ul < ucN; ul ++ )
pOutData [ ul ] = RC522_Read_Register ( FIFODataReg );
}
}
else
cStatus = MI_ERR;
}
RC522_SetBit_Register ( ControlReg, 0x80 ); // stop timer now
RC522_Write_Register ( CommandReg, PCD_IDLE );
return cStatus;
}
/**
* @brief :寻卡
* @param ucReq_code,寻卡方式
* = 0x52:寻感应区内所有符合14443A标准的卡
* = 0x26:寻未进入休眠状态的卡
* pTagType,卡片类型代码
* = 0x4400:Mifare_UltraLight
* = 0x0400:Mifare_One(S50)
* = 0x0200:Mifare_One(S70)
* = 0x0800:Mifare_Pro(X))
* = 0x4403:Mifare_DESFire
* @retval :状态值MI_OK,成功
*/
char PcdRequest ( uint8_t ucReq_code, uint8_t * pTagType )
{
char cStatus;
uint8_t ucComMF522Buf [ MAXRLEN ];
uint32_t ulLen;
RC522_ClearBit_Register ( Status2Reg, 0x08 ); //清理指示MIFARECyptol单元接通以及所有卡的数据通信被加密的情况
RC522_Write_Register ( BitFramingReg, 0x07 ); // 发送的最后一个字节的 七位
RC522_SetBit_Register ( TxControlReg, 0x03 ); //TX1,TX2管脚的输出信号传递经发送调制的13.46的能量载波信号
ucComMF522Buf [ 0 ] = ucReq_code; //存入寻卡方式
/* PCD_TRANSCEIVE:发送并接收数据的命令,RC522向卡片发送寻卡命令,卡片返回卡的型号代码到ucComMF522Buf中 */
cStatus = PcdComMF522 ( PCD_TRANSCEIVE, ucComMF522Buf, 1, ucComMF522Buf, &ulLen ); //寻卡
// rt_kprintf("\nReturn Code: cStatus[%x] ulLen[%x]\n",cStatus,ulLen);
if ( ( cStatus == MI_OK ) && ( ulLen == 0x10 ) ) //寻卡成功返回卡类型
{
/* 接收卡片的型号代码 */
* pTagType = ucComMF522Buf [ 0 ];
* ( pTagType + 1 ) = ucComMF522Buf [ 1 ];
}
else
cStatus = MI_ERR;
return cStatus;
}
/**
* @brief :防冲突
* @param :Snr:卡片序列,4字节,会返回选中卡片的序列
* @retval :状态值MI_OK,成功
*/
char PcdAnticoll ( uint8_t * pSnr )
{
char cStatus;
uint8_t uc, ucSnr_check = 0;
uint8_t ucComMF522Buf [ MAXRLEN ];
uint32_t ulLen;
RC522_ClearBit_Register ( Status2Reg, 0x08 ); //清MFCryptol On位 只有成功执行MFAuthent命令后,该位才能置位
RC522_Write_Register ( BitFramingReg, 0x00); //清理寄存器 停止收发
RC522_ClearBit_Register ( CollReg, 0x80 ); //清ValuesAfterColl所有接收的位在冲突后被清除
ucComMF522Buf [ 0 ] = 0x93; //卡片防冲突命令
ucComMF522Buf [ 1 ] = 0x20;
/* 将卡片防冲突命令通过RC522传到卡片中,返回的是被选中卡片的序列 */
cStatus = PcdComMF522 ( PCD_TRANSCEIVE, ucComMF522Buf, 2, ucComMF522Buf, & ulLen);//与卡片通信
if ( cStatus == MI_OK) //通信成功
{
for ( uc = 0; uc < 4; uc ++ )
{
* ( pSnr + uc ) = ucComMF522Buf [ uc ]; //读出UID
ucSnr_check ^= ucComMF522Buf [ uc ];
}
if ( ucSnr_check != ucComMF522Buf [ uc ] )
cStatus = MI_ERR;
}
RC522_SetBit_Register ( CollReg, 0x80 );
return cStatus;
}
/**
* @brief :用RC522计算CRC16(循环冗余校验)
* @param :pIndata:计算CRC16的数组
* ucLen:计算CRC16的数组字节长度
* pOutData:存放计算结果存放的首地址
* @retval :状态值MI_OK,成功
*/
void CalulateCRC ( uint8_t * pIndata, u8 ucLen, uint8_t * pOutData )
{
uint8_t uc, ucN;
RC522_ClearBit_Register(DivIrqReg,0x04);
RC522_Write_Register(CommandReg,PCD_IDLE);
RC522_SetBit_Register(FIFOLevelReg,0x80);
for ( uc = 0; uc < ucLen; uc ++)
RC522_Write_Register ( FIFODataReg, * ( pIndata + uc ) );
RC522_Write_Register ( CommandReg, PCD_CALCCRC );
uc = 0xFF;
do
{
ucN = RC522_Read_Register ( DivIrqReg );
aicos_udelay(1);
uc --;
} while ( ( uc != 0 ) && ! ( ucN & 0x04 ) );
pOutData [ 0 ] = RC522_Read_Register ( CRCResultRegL );
pOutData [ 1 ] = RC522_Read_Register ( CRCResultRegM );
}
/**
* @brief :选定卡片
* @param :pSnr:卡片序列号,4字节
* @retval :状态值MI_OK,成功
*/
char PcdSelect ( uint8_t * pSnr )
{
char ucN;
uint8_t uc;
uint8_t ucComMF522Buf [ MAXRLEN ];
uint32_t ulLen;
/* PICC_ANTICOLL1:防冲突命令 */
ucComMF522Buf [ 0 ] = PICC_ANTICOLL1;
ucComMF522Buf [ 1 ] = 0x70;
ucComMF522Buf [ 6 ] = 0;
for ( uc = 0; uc < 4; uc ++ )
{
ucComMF522Buf [ uc + 2 ] = * ( pSnr + uc );
ucComMF522Buf [ 6 ] ^= * ( pSnr + uc );
}
CalulateCRC ( ucComMF522Buf, 7, & ucComMF522Buf [ 7 ] );
RC522_ClearBit_Register ( Status2Reg, 0x08 );
ucN = PcdComMF522 ( PCD_TRANSCEIVE, ucComMF522Buf, 9, ucComMF522Buf, & ulLen );
if ( ( ucN == MI_OK ) && ( ulLen == 0x18 ) )
ucN = MI_OK;
else
ucN = MI_ERR;
return ucN;
}
/**
* @brief :校验卡片密码
* @param :ucAuth_mode:密码验证模式
* = 0x60,验证A密钥
* = 0x61,验证B密钥
* ucAddr:块地址
* pKey:密码
* pSnr:卡片序列号,4字节
* @retval :状态值MI_OK,成功
*/
char PcdAuthState ( uint8_t ucAuth_mode, uint8_t ucAddr, uint8_t * pKey, uint8_t * pSnr )
{
char cStatus;
uint8_t uc, ucComMF522Buf [ MAXRLEN ];
uint32_t ulLen;
ucComMF522Buf [ 0 ] = ucAuth_mode;
ucComMF522Buf [ 1 ] = ucAddr;
/* 前俩字节存储验证模式和块地址,2~8字节存储密码(6个字节),8~14字节存储序列号 */
for ( uc = 0; uc < 6; uc ++ )
ucComMF522Buf [ uc + 2 ] = * ( pKey + uc );
for ( uc = 0; uc < 6; uc ++ )
ucComMF522Buf [ uc + 8 ] = * ( pSnr + uc );
/* 进行冗余校验,14~16俩个字节存储校验结果 */
cStatus = PcdComMF522 ( PCD_AUTHENT, ucComMF522Buf, 12, ucComMF522Buf, & ulLen );
/* 判断验证是否成功 */
if ( ( cStatus != MI_OK ) || ( ! ( RC522_Read_Register ( Status2Reg ) & 0x08 ) ) )
cStatus = MI_ERR;
return cStatus;
}
/**
* @brief :在M1卡的指定块地址写入指定数据
* @param :ucAddr:块地址
* pData:写入的数据,16字节
* @retval :状态值MI_OK,成功
*/
char PcdWrite ( uint8_t ucAddr, uint8_t * pData )
{
char cStatus;
uint8_t uc, ucComMF522Buf [ MAXRLEN ];
uint32_t ulLen;
ucComMF522Buf [ 0 ] = PICC_WRITE;//写块命令
ucComMF522Buf [ 1 ] = ucAddr;//写块地址
/* 进行循环冗余校验,将结果存储在& ucComMF522Buf [ 2 ] */
CalulateCRC ( ucComMF522Buf, 2, & ucComMF522Buf [ 2 ] );
/* PCD_TRANSCEIVE:发送并接收数据命令,通过RC522向卡片发送写块命令 */
cStatus = PcdComMF522 ( PCD_TRANSCEIVE, ucComMF522Buf, 4, ucComMF522Buf, & ulLen );
/* 通过卡片返回的信息判断,RC522是否与卡片正常通信 */
if ( ( cStatus != MI_OK ) || ( ulLen != 4 ) || ( ( ucComMF522Buf [ 0 ] & 0x0F ) != 0x0A ) )
cStatus = MI_ERR;
if ( cStatus == MI_OK )
{
//memcpy(ucComMF522Buf, pData, 16);
/* 将要写入的16字节的数据,传入ucComMF522Buf数组中 */
for ( uc = 0; uc < 16; uc ++ )
ucComMF522Buf [ uc ] = * ( pData + uc );
/* 冗余校验 */
CalulateCRC ( ucComMF522Buf, 16, & ucComMF522Buf [ 16 ] );
/* 通过RC522,将16字节数据包括2字节校验结果写入卡片中 */
cStatus = PcdComMF522 ( PCD_TRANSCEIVE, ucComMF522Buf, 18, ucComMF522Buf, & ulLen );
/* 判断写地址是否成功 */
if ( ( cStatus != MI_OK ) || ( ulLen != 4 ) || ( ( ucComMF522Buf [ 0 ] & 0x0F ) != 0x0A ) )
cStatus = MI_ERR;
}
return cStatus;
}
/**
* @brief :读取M1卡的指定块地址的数据
* @param :ucAddr:块地址
* pData:读出的数据,16字节
* @retval :状态值MI_OK,成功
*/
char PcdRead ( uint8_t ucAddr, uint8_t * pData )
{
char cStatus;
uint8_t uc, ucComMF522Buf [ MAXRLEN ];
uint32_t ulLen;
ucComMF522Buf [ 0 ] = PICC_READ;
ucComMF522Buf [ 1 ] = ucAddr;
/* 冗余校验 */
CalulateCRC ( ucComMF522Buf, 2, & ucComMF522Buf [ 2 ] );
/* 通过RC522将命令传给卡片 */
cStatus = PcdComMF522 ( PCD_TRANSCEIVE, ucComMF522Buf, 4, ucComMF522Buf, & ulLen );
/* 如果传输正常,将读取到的数据传入pData中 */
if ( ( cStatus == MI_OK ) && ( ulLen == 0x90 ) )
{
for ( uc = 0; uc < 16; uc ++ )
* ( pData + uc ) = ucComMF522Buf [ uc ];
}
else
cStatus = MI_ERR;
return cStatus;
}
/**
* @brief :让卡片进入休眠模式
* @param :无
* @retval :状态值MI_OK,成功
*/
char PcdHalt( void )
{
uint8_t ucComMF522Buf [ MAXRLEN ];
uint32_t ulLen;
ucComMF522Buf [ 0 ] = PICC_HALT;
ucComMF522Buf [ 1 ] = 0;
CalulateCRC ( ucComMF522Buf, 2, & ucComMF522Buf [ 2 ] );
PcdComMF522 ( PCD_TRANSCEIVE, ucComMF522Buf, 4, ucComMF522Buf, & ulLen );
return MI_OK;
}
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
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
bsp_rc522.h
/*
* 立创开发板软硬件资料与相关扩展板软硬件资料官网全部开源
* 开发板官网:www.lckfb.com
* 文档网站:wiki.lckfb.com
* 技术支持常驻论坛,任何技术问题欢迎随时交流学习
* 嘉立创社区问答:https://www.jlc-bbs.com/lckfb
* 关注bilibili账号:【立创开发板】,掌握我们的最新动态!
* 不靠卖板赚钱,以培养中国工程师为己任
*/
#ifndef __BSP_RC522_H__
#define __BSP_RC522_H__
#include <getopt.h>
#include <string.h>
#include <rtthread.h>
#include <aic_core.h>
//RC522命令字
#define PCD_IDLE 0x00 //取消当前命令
#define PCD_AUTHENT 0x0E //验证密钥
#define PCD_RECEIVE 0x08 //接收数据
#define PCD_TRANSMIT 0x04 //发送数据
#define PCD_TRANSCEIVE 0x0C //发送并接收数据
#define PCD_RESETPHASE 0x0F //复位
#define PCD_CALCCRC 0x03 //CRC计算
//Mifare_One卡片命令字
#define PICC_REQIDL 0x26 //寻天线区内未进入休眠状态
#define PICC_REQALL 0x52 //寻天线区内全部卡
#define PICC_ANTICOLL1 0x93 //防冲撞
#define PICC_ANTICOLL2 0x95 //防冲撞
#define PICC_AUTHENT1A 0x60 //验证A密钥
#define PICC_AUTHENT1B 0x61 //验证B密钥
#define PICC_READ 0x30 //读块
#define PICC_WRITE 0xA0 //写块
#define PICC_DECREMENT 0xC0 //扣款
#define PICC_INCREMENT 0xC1 //充值
#define PICC_RESTORE 0xC2 //调块数据到缓冲区
#define PICC_TRANSFER 0xB0 //保存缓冲区中数据
#define PICC_HALT 0x50 //休眠
/* RC522 FIFO长度定义 */
#define DEF_FIFO_LENGTH 64 //FIFO size=64byte
#define MAXRLEN 18
/* RC522寄存器定义 */
// PAGE 0
#define RFU00 0x00 //保留
#define CommandReg 0x01 //启动和停止命令的执行
#define ComIEnReg 0x02 //中断请求传递的使能(Enable/Disable)
#define DivlEnReg 0x03 //中断请求传递的使能
#define ComIrqReg 0x04 //包含中断请求标志
#define DivIrqReg 0x05 //包含中断请求标志
#define ErrorReg 0x06 //错误标志,指示执行的上个命令的错误状态
#define Status1Reg 0x07 //包含通信的状态标识
#define Status2Reg 0x08 //包含接收器和发送器的状态标志
#define FIFODataReg 0x09 //64字节FIFO缓冲区的输入和输出
#define FIFOLevelReg 0x0A //指示FIFO中存储的字节数
#define WaterLevelReg 0x0B //定义FIFO下溢和上溢报警的FIFO深度
#define ControlReg 0x0C //不同的控制寄存器
#define BitFramingReg 0x0D //面向位的帧的调节
#define CollReg 0x0E //RF接口上检测到的第一个位冲突的位的位置
#define RFU0F 0x0F //保留
// PAGE 1
#define RFU10 0x10 //保留
#define ModeReg 0x11 //定义发送和接收的常用模式
#define TxModeReg 0x12 //定义发送过程的数据传输速率
#define RxModeReg 0x13 //定义接收过程中的数据传输速率
#define TxControlReg 0x14 //控制天线驱动器管教TX1和TX2的逻辑特性
#define TxAutoReg 0x15 //控制天线驱动器的设置
#define TxSelReg 0x16 //选择天线驱动器的内部源
#define RxSelReg 0x17 //选择内部的接收器设置
#define RxThresholdReg 0x18 //选择位译码器的阈值
#define DemodReg 0x19 //定义解调器的设置
#define RFU1A 0x1A //保留
#define RFU1B 0x1B //保留
#define MifareReg 0x1C //控制ISO 14443/MIFARE模式中106kbit/s的通信
#define RFU1D 0x1D //保留
#define RFU1E 0x1E //保留
#define SerialSpeedReg 0x1F //选择串行UART接口的速率
// PAGE 2
#define RFU20 0x20 //保留
#define CRCResultRegM 0x21 //显示CRC计算的实际MSB值
#define CRCResultRegL 0x22 //显示CRC计算的实际LSB值
#define RFU23 0x23 //保留
#define ModWidthReg 0x24 //控制ModWidth的设置
#define RFU25 0x25 //保留
#define RFCfgReg 0x26 //配置接收器增益
#define GsNReg 0x27 //选择天线驱动器管脚(TX1和TX2)的调制电导
#define CWGsCfgReg 0x28 //选择天线驱动器管脚的调制电导
#define ModGsCfgReg 0x29 //选择天线驱动器管脚的调制电导
#define TModeReg 0x2A //定义内部定时器的设置
#define TPrescalerReg 0x2B //定义内部定时器的设置
#define TReloadRegH 0x2C //描述16位长的定时器重装值
#define TReloadRegL 0x2D //描述16位长的定时器重装值
#define TCounterValueRegH 0x2E
#define TCounterValueRegL 0x2F //显示16位长的实际定时器值
// PAGE 3
#define RFU30 0x30 //保留
#define TestSel1Reg 0x31 //常用测试信号配置
#define TestSel2Reg 0x32 //常用测试信号配置和PRBS控制
#define TestPinEnReg 0x33 //D1-D7输出驱动器的使能管脚(仅用于串行接口)
#define TestPinValueReg 0x34 //定义D1-D7用作I/O总线时的值
#define TestBusReg 0x35 //显示内部测试总线的状态
#define AutoTestReg 0x36 //控制数字自测试
#define VersionReg 0x37 //显示版本
#define AnalogTestReg 0x38 //控制管脚AUX1和AUX2
#define TestDAC1Reg 0x39 //定义TestDAC1的测试值
#define TestDAC2Reg 0x3A //定义TestDAC2的测试值
#define TestADCReg 0x3B //显示ADCI和Q通道的实际值
#define RFU3C 0x3C //保留
#define RFU3D 0x3D //保留
#define RFU3E 0x3E //保留
#define RFU3F 0x3F //保留
/* ----------------------------------- */
/* 和RC522通信时返回的错误代码 */
#define MI_OK 0x26
#define MI_NOTAGERR 0xcc
#define MI_ERR 0xbb
int RC522_Init(void); // 模块初始化
////////////////与RC522通信///////////////////////////////////////////
uint8_t RC522_Read_Register( uint8_t Address );//读取RC522指定寄存器的值
void RC522_Write_Register( uint8_t Address, uint8_t data );//向RC522指定寄存器中写入指定的数据
void RC522_SetBit_Register( uint8_t Address, uint8_t mask );//置位RC522指定寄存器的指定位
void RC522_ClearBit_Register( uint8_t Address, uint8_t mask );//清位RC522指定寄存器的指定位
/////////////////////RC522的基础通信///////////////////////////////////
void RC522_Antenna_On( void );//开启天线
void RC522_Antenna_Off( void );//关闭天线
void RC522_Rese( void );//复位RC522
void RC522_Config_Type( char Type );//设置RC522的工作方式
/////////////////////////控制RC522与M1的通信///////////////////////////////////////
char PcdComMF522 ( uint8_t ucCommand, uint8_t * pInData, uint8_t ucInLenByte, uint8_t * pOutData, uint32_t * pOutLenBit );//通过RC522和ISO14443卡通讯
char PcdRequest ( uint8_t ucReq_code, uint8_t * pTagType );//寻卡
char PcdAnticoll ( uint8_t * pSnr );//防冲突
void CalulateCRC ( uint8_t * pIndata, u8 ucLen, uint8_t * pOutData );//用RC522计算CRC16(循环冗余校验)
char PcdSelect ( uint8_t * pSnr );//选定卡片
char PcdAuthState ( uint8_t ucAuth_mode, uint8_t ucAddr, uint8_t * pKey, uint8_t * pSnr );//校验卡片密码
char PcdWrite ( uint8_t ucAddr, uint8_t * pData );//在M1卡的指定块地址写入指定数据
char PcdRead ( uint8_t ucAddr, uint8_t * pData );//读取M1卡的指定块地址的数据
char PcdHalt( void );//让卡片进入休眠模式
#endif
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
Kconfig
这个是一个menuconfig中的选项,如果在菜单中选中该选项,就会在rtconfig.h
中定义一个语句,用来if判断条件编译之类的。
config LCKFB_RC522_RT_IC_READER_MODULE
bool "Using RC522 Radio Frequency IC Card Recognition Module"
select AIC_USING_QSPI3
default n
help
More information is available at: https://wiki.lckfb.com/
2
3
4
5
6
SConscript
自动化构建文件,如果定义了 LCKFB_RC522_RT_IC_READER_MODULE
和 USING_LCKFB_TRANSPLANT_CODE
就自动编译当前目录下的文件!!
Import('RTT_ROOT')
Import('rtconfig')
import rtconfig
from building import *
cwd = GetCurrentDir()
CPPPATH = [cwd]
src = []
if GetDepend('LCKFB_RC522_RT_IC_READER_MODULE') and GetDepend('USING_LCKFB_TRANSPLANT_CODE'):
src = Glob(os.path.join(cwd, '*.c'))
group = DefineGroup('lckfb-rc522-rf-ic-card-identification-module', src, depend = [''], CPPPATH = CPPPATH)
Return('group')
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
test_rc522_rf_ic_card_identification_module.c
这个文件定义了一个处理RC522射频IC卡识别模块的线程,初始化了RC522模块的板级支持包(BSP),并设置了线程的优先级、栈大小和时间片。
线程的主要任务是循环检测RC522模块是否检测到IC卡,如果是,则读取卡的ID,执行防冲突操作,选择卡片,校验卡片密码,然后读写卡片数据块,并打印相关信息到控制台。
/*
* 立创开发板软硬件资料与相关扩展板软硬件资料官网全部开源
* 开发板官网:www.lckfb.com
* 文档网站:wiki.lckfb.com
* 技术支持常驻论坛,任何技术问题欢迎随时交流学习
* 嘉立创社区问答:https://www.jlc-bbs.com/lckfb
* 关注bilibili账号:【立创开发板】,掌握我们的最新动态!
* 不靠卖板赚钱,以培养中国工程师为己任
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <getopt.h>
#include <sys/time.h>
#include <rtthread.h>
#include "rtdevice.h"
#include "aic_core.h"
#include "aic_hal_gpio.h"
#include "bsp_rc522.h"
#define THREAD_PRIORITY 25 // 线程优先级
#define THREAD_STACK_SIZE 4096 // 线程大小
#define THREAD_TIMESLICE 20 // 时间片
static rt_thread_t rc522_thread = RT_NULL; // 线程控制块
// 线程入口函数
static void rc522_thread_entry(void *param)
{
uint8_t ucArray_ID[4] = {0}; // 卡的ID存储,32位,4字节
uint8_t ucStatusReturn = 0; // 返回状态
uint8_t read_write_data[16] = {0}; // 读写数据缓存
uint8_t card_KEY[6] = {0xff,0xff,0xff,0xff,0xff,0xff}; // 默认密码
rt_kprintf("\nrc522_thread_entry Run.......\n");
while(1)
{
ucStatusReturn = PcdRequest( PICC_REQALL, ucArray_ID );
/* 寻卡(方式:范围内全部),第一次寻卡失败后再进行一次,寻卡成功时卡片序列传入数组ucArray_ID中 */
if ( ucStatusReturn != MI_OK )
{
// LOG_E("Failed to PcdRequest !!! [%x]",ucStatusReturn);
ucStatusReturn = PcdRequest ( PICC_REQALL, ucArray_ID );
}
if ( ucStatusReturn == MI_OK )
{
/* 防冲突操作,被选中的卡片序列传入数组ucArray_ID中 */
if ( PcdAnticoll ( ucArray_ID ) == MI_OK )
{
rt_kprintf("\n\n=============================================\n");
//输出卡ID
rt_kprintf("ID: %X %X %X %X\n", ucArray_ID [ 0 ], ucArray_ID [ 1 ], ucArray_ID [ 2 ], ucArray_ID [ 3 ]);
//选卡
if( PcdSelect(ucArray_ID) != MI_OK )
{ rt_kprintf("PcdSelect failure\n"); }
//校验卡片密码
//数据块6的密码A进行校验(所有密码默认为16个字节的0xff)
if( PcdAuthState(PICC_AUTHENT1B, 6, card_KEY, ucArray_ID) != MI_OK )
{ rt_kprintf("PcdAuthState failure\n"); }
//往数据块4写入数据read_write_data
read_write_data[0] = 0xaa;//将read_write_data的第一位数据改为0xaa
if( PcdWrite(4,read_write_data) != MI_OK )
{ rt_kprintf("PcdWrite failure\n"); }
//将read_write_data的16位数据,填充为0(清除数据的意思)
memset(read_write_data,0,16);
aicos_udelay(8);
//读取数据块4的数据
if( PcdRead(4,read_write_data) != MI_OK )
{ rt_kprintf("PcdRead failure\n"); }
//输出读出的数据
for(int i = 0; i < 16; i++ )
{
rt_kprintf("%x ",read_write_data[i]);
}
rt_kprintf("\n=============================================\n");
}
}
rt_thread_mdelay(1000);
}
}
static void test_rc522_rf_ic_module(int argc, char **argv)
{
int ret = RC522_Init(); // 模块初始化
if(ret != RT_EOK)
{
LOG_E("Failed to [RC522_Init] !!!");
LOG_E("file: %s line: %d", __FILE__, __LINE__);
return;
}
rt_kprintf("RC522_Init run END!!\n");
rt_thread_mdelay(100);
RC522_Rese( ); //复位RC522
rt_thread_mdelay(100);
rt_kprintf("RC522_Rese run END!!\n");
/* 创建线程,名称是 rc522_thread,入口是 rc522_thread_entry */
rc522_thread = rt_thread_create("rc522_thread",
rc522_thread_entry, RT_NULL,
THREAD_STACK_SIZE,
THREAD_PRIORITY, THREAD_TIMESLICE);
/* 如果获得线程控制块,启动这个线程 */
if (rc522_thread != RT_NULL)
rt_thread_startup(rc522_thread);
rt_kprintf("test_rc522_rf_ic_module run END!!\n");
}
// 导出函数为命令
MSH_CMD_EXPORT(test_rc522_rf_ic_module, RC522 Radio Frequency IC Card Recognition Module);
static void test_exit_rc522_rf_ic_module(void)
{
int ret = rt_thread_delete(rc522_thread);
if(ret != RT_EOK)
{
LOG_E("failed to test_exit_rc522_rf_ic_module !!");
}
else
{
rt_kprintf("\n========RC522 RFID exit successful !!========\n");
}
}
// 导出函数为命令
MSH_CMD_EXPORT(test_exit_rc522_rf_ic_module, Exit RC522 Radio Frequency IC Card Recognition Module);
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
线程入口函数逻辑
- 定义了几个变量用于存储卡ID、返回状态、读写数据缓存和卡片密码。
- 在一个无限循环中,首先尝试请求所有范围内的卡片。
- 如果请求成功,执行防冲突操作并打印卡ID。
- 接着选择卡片,校验卡片密码,然后向数据块写入数据,并读取出来,打印到控制台。
- 在每次循环结束时,线程会挂起一段时间,这里是1000毫秒。
此外,该文件还定义了两个命令,test_rc522_rf_ic_module
和test_exit_rc522_rf_ic_module
,它们分别用于启动和退出RC522射频IC卡识别模块线程。
test_rc522_rf_ic_module
命令首先调用RC522_Init
函数来初始化RC522模块。如果初始化成功,则创建并启动一个名为rc522_thread
的线程,该线程将执行rc522_thread_entry
函数。test_exit_rc522_rf_ic_module
命令用于删除rc522_thread
线程,从而退出IC卡识别模块的数据处理循环,并打印退出成功的消息。
以下是代码中的关键点:
PcdRequest
函数用于请求卡片。PcdAnticoll
函数用于执行防冲突操作。PcdSelect
函数用于选择卡片。PcdAuthState
函数用于校验卡片密码。PcdWrite
函数用于向卡片写入数据。PcdRead
函数用于从卡片读取数据。aicos_mdelay
函数用于实现毫秒级的延时。LOG_E
宏用于打印错误信息。MSH_CMD_EXPORT
宏用于将函数导出为命令行接口,使得用户可以在命令行中直接调用这些函数。
通过在命令行中输入test_rc522_rf_ic_module
,用户可以启动RC522射频IC卡识别模块的数据处理线程,并且每当检测到IC卡时,都会在控制台上看到相应的输出信息。输入test_exit_rc522_rf_ic_module
命令可以退出数据处理线程。
/*
* 立创开发板软硬件资料与相关扩展板软硬件资料官网全部开源
* 开发板官网:www.lckfb.com
* 文档网站:wiki.lckfb.com
* 技术支持常驻论坛,任何技术问题欢迎随时交流学习
* 嘉立创社区问答:https://www.jlc-bbs.com/lckfb
* 关注bilibili账号:【立创开发板】,掌握我们的最新动态!
* 不靠卖板赚钱,以培养中国工程师为己任
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <getopt.h>
#include <sys/time.h>
#include <rtthread.h>
#include "rtdevice.h"
#include "aic_core.h"
#include "aic_hal_gpio.h"
#include "bsp_rc522.h"
#define THREAD_PRIORITY 25 // 线程优先级
#define THREAD_STACK_SIZE 4096 // 线程大小
#define THREAD_TIMESLICE 20 // 时间片
static rt_thread_t rc522_thread = RT_NULL; // 线程控制块
// 线程入口函数
static void rc522_thread_entry(void *param)
{
uint8_t ucArray_ID[4] = {0}; // 卡的ID存储,32位,4字节
uint8_t ucStatusReturn = 0; // 返回状态
uint8_t read_write_data[16] = {0}; // 读写数据缓存
uint8_t card_KEY[6] = {0xff,0xff,0xff,0xff,0xff,0xff}; // 默认密码
rt_kprintf("\nrc522_thread_entry Run.......\n");
while(1)
{
ucStatusReturn = PcdRequest( PICC_REQALL, ucArray_ID );
/* 寻卡(方式:范围内全部),第一次寻卡失败后再进行一次,寻卡成功时卡片序列传入数组ucArray_ID中 */
if ( ucStatusReturn != MI_OK )
{
// LOG_E("Failed to PcdRequest !!! [%x]",ucStatusReturn);
ucStatusReturn = PcdRequest ( PICC_REQALL, ucArray_ID );
}
if ( ucStatusReturn == MI_OK )
{
/* 防冲突操作,被选中的卡片序列传入数组ucArray_ID中 */
if ( PcdAnticoll ( ucArray_ID ) == MI_OK )
{
rt_kprintf("\n\n=============================================\n");
//输出卡ID
rt_kprintf("ID: %X %X %X %X\n", ucArray_ID [ 0 ], ucArray_ID [ 1 ], ucArray_ID [ 2 ], ucArray_ID [ 3 ]);
//选卡
if( PcdSelect(ucArray_ID) != MI_OK )
{ rt_kprintf("PcdSelect failure\n"); }
//校验卡片密码
//数据块6的密码A进行校验(所有密码默认为16个字节的0xff)
if( PcdAuthState(PICC_AUTHENT1B, 6, card_KEY, ucArray_ID) != MI_OK )
{ rt_kprintf("PcdAuthState failure\n"); }
//往数据块4写入数据read_write_data
read_write_data[0] = 0xaa;//将read_write_data的第一位数据改为0xaa
if( PcdWrite(4,read_write_data) != MI_OK )
{ rt_kprintf("PcdWrite failure\n"); }
//将read_write_data的16位数据,填充为0(清除数据的意思)
memset(read_write_data,0,16);
aicos_udelay(8);
//读取数据块4的数据
if( PcdRead(4,read_write_data) != MI_OK )
{ rt_kprintf("PcdRead failure\n"); }
//输出读出的数据
for(int i = 0; i < 16; i++ )
{
rt_kprintf("%x ",read_write_data[i]);
}
rt_kprintf("\n=============================================\n");
}
}
aicos_mdelay(1000);
}
}
static void test_rc522_rf_ic_module(int argc, char **argv)
{
int ret = RC522_Init(); // 模块初始化
if(ret != RT_EOK)
{
LOG_E("Failed to [RC522_Init] !!!");
LOG_E("file: %s line: %d", __FILE__, __LINE__);
return;
}
rt_kprintf("RC522_Init run END!!\n");
aicos_mdelay(100);
RC522_Rese( ); //复位RC522
aicos_mdelay(100);
rt_kprintf("RC522_Rese run END!!\n");
/* 创建线程,名称是 rc522_thread,入口是 rc522_thread_entry */
rc522_thread = rt_thread_create("rc522_thread",
rc522_thread_entry, RT_NULL,
THREAD_STACK_SIZE,
THREAD_PRIORITY, THREAD_TIMESLICE);
/* 如果获得线程控制块,启动这个线程 */
if (rc522_thread != RT_NULL)
rt_thread_startup(rc522_thread);
rt_kprintf("test_rc522_rf_ic_module run END!!\n");
}
// 导出函数为命令
MSH_CMD_EXPORT(test_rc522_rf_ic_module, RC522 Radio Frequency IC Card Recognition Module);
static void test_exit_rc522_rf_ic_module(void)
{
int ret = rt_thread_delete(rc522_thread);
if(ret != RT_EOK)
{
LOG_E("failed to test_exit_rc522_rf_ic_module !!");
}
else
{
rt_kprintf("\n========RC522 RFID exit successful !!========\n");
}
}
// 导出函数为命令
MSH_CMD_EXPORT(test_exit_rc522_rf_ic_module, Exit RC522 Radio Frequency IC Card Recognition Module);
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
移植验证
我们使用串口调试,将 USB转TTL模块 连接到衡山派开发板上面!!
具体的教程查看:串口调试(点击跳转🚀)
串口波特率默认为
115200
我们在输入下面的命令运行该模块的线程:
输入的时候按下
TAB键
会进行命令补全!!
test_rc522_rf_ic_module
模块上电效果: