FM17580.h
#ifndef _FM17580_H_
#define _FM17580_H_
#include "stm32g0xx_hal.h"
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "usart.h"
#include "spi.h"
#include "gpio.h"
#define RC522_RST_GPIO_Port GPIOC
#define RC522_RST_Pin GPIO_PIN_5
#define RC522_SDA_GPIO_Port GPIOA
#define RC522_SDA_Pin GPIO_PIN_4
/***********************************************************************************
* MFRC522驱动程序 *
************************************************************************************/
/*MFRC522寄存器定义*/
//PAGE0
#define MFRC_RFU00 0x00
#define MFRC_CommandReg 0x01
#define MFRC_ComIEnReg 0x02
#define MFRC_DivlEnReg 0x03
#define MFRC_ComIrqReg 0x04
#define MFRC_DivIrqReg 0x05
#define MFRC_ErrorReg 0x06
#define MFRC_Status1Reg 0x07
#define MFRC_Status2Reg 0x08
#define MFRC_FIFODataReg 0x09
#define MFRC_FIFOLevelReg 0x0A
#define MFRC_WaterLevelReg 0x0B
#define MFRC_ControlReg 0x0C
#define MFRC_BitFramingReg 0x0D
#define MFRC_CollReg 0x0E
#define MFRC_RFU0F 0x0F
//PAGE1
#define MFRC_RFU10 0x10
#define MFRC_ModeReg 0x11
#define MFRC_TxModeReg 0x12
#define MFRC_RxModeReg 0x13
#define MFRC_TxControlReg 0x14
#define MFRC_TxAutoReg 0x15 //中文手册有误
#define MFRC_TxSelReg 0x16
#define MFRC_RxSelReg 0x17
#define MFRC_RxThresholdReg 0x18
#define MFRC_DemodReg 0x19
#define MFRC_RFU1A 0x1A
#define MFRC_RFU1B 0x1B
#define MFRC_MifareReg 0x1C
#define MFRC_RFU1D 0x1D
#define MFRC_RFU1E 0x1E
#define MFRC_SerialSpeedReg 0x1F
//PAGE2
#define MFRC_RFU20 0x20
#define MFRC_CRCResultRegM 0x21
#define MFRC_CRCResultRegL 0x22
#define MFRC_RFU23 0x23
#define MFRC_ModWidthReg 0x24
#define MFRC_RFU25 0x25
#define MFRC_RFCfgReg 0x26
#define MFRC_GsNReg 0x27
#define MFRC_CWGsCfgReg 0x28
#define MFRC_ModGsCfgReg 0x29
#define MFRC_TModeReg 0x2A
#define MFRC_TPrescalerReg 0x2B
#define MFRC_TReloadRegH 0x2C
#define MFRC_TReloadRegL 0x2D
#define MFRC_TCounterValueRegH 0x2E
#define MFRC_TCounterValueRegL 0x2F
//PAGE3
#define MFRC_RFU30 0x30
#define MFRC_TestSel1Reg 0x31
#define MFRC_TestSel2Reg 0x32
#define MFRC_TestPinEnReg 0x33
#define MFRC_TestPinValueReg 0x34
#define MFRC_TestBusReg 0x35
#define MFRC_AutoTestReg 0x36
#define MFRC_VersionReg 0x37
#define MFRC_AnalogTestReg 0x38
#define MFRC_TestDAC1Reg 0x39
#define MFRC_TestDAC2Reg 0x3A
#define MFRC_TestADCReg 0x3B
#define MFRC_RFU3C 0x3C
#define MFRC_RFU3D 0x3D
#define MFRC_RFU3E 0x3E
#define MFRC_RFU3F 0x3F
/*MFRC522的FIFO长度定义*/
#define MFRC_FIFO_LENGTH 64
/*MFRC522传输的帧长定义*/
#define MFRC_MAXRLEN 18
/*MFRC522命令集,中文手册P59*/
#define MFRC_IDLE 0x00 //取消当前命令的执行
#define MFRC_CALCCRC 0x03 //激活CRC计算
#define MFRC_TRANSMIT 0x04 //发送FIFO缓冲区内容
#define MFRC_NOCMDCHANGE 0x07 //无命令改变
#define MFRC_RECEIVE 0x08 //激活接收器接收数据
#define MFRC_TRANSCEIVE 0x0C //发送并接收数据
#define MFRC_AUTHENT 0x0E //执行Mifare认证(验证密钥)
#define MFRC_RESETPHASE 0x0F //复位MFRC522
/*MFRC522通讯时返回的错误代码*/
#define MFRC_OK (char)0
#define MFRC_NOTAGERR (char)(-1)
#define MFRC_ERR (char)(-2)
/*MFRC522函数声明*/
void MFRC_Init(void);
void MFRC_WriteReg(uint8_t addr, uint8_t data);
uint8_t MFRC_ReadReg(uint8_t addr);
void MFRC_SetBitMask(uint8_t addr, uint8_t mask);
void MFRC_ClrBitMask(uint8_t addr, uint8_t mask);
void MFRC_CalulateCRC(uint8_t *pInData, uint8_t len, uint8_t *pOutData);
char MFRC_CmdFrame(uint8_t cmd, uint8_t *pInData, uint8_t InLenByte, uint8_t *pOutData, uint16_t *pOutLenBit);
/***********************************************************************************
* MFRC552与MF1卡通讯接口程序 *
************************************************************************************/
/*Mifare1卡片命令字*/
#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_TRANSFER 0xB0 //转存(传送)
#define PICC_RESTORE 0xC2 //恢复(重储)
#define PICC_HALT 0x50 //休眠
/*PCD通讯时返回的错误代码*/
#define PCD_OK (char)0 //成功
#define PCD_NOTAGERR (char)(-1) //无卡
#define PCD_ERR (char)(-2) //出错
/*PCD函数声明*/
void PCD_Init(void);
void PCD_Reset(void);
void PCD_AntennaOn(void);
void PCD_AntennaOff(void);
char PCD_Request(uint8_t RequestMode, uint8_t *pCardType); //寻卡,并返回卡的类型
char PCD_Anticoll(uint8_t *pSnr); //防冲突,返回卡号
char PCD_Select(uint8_t *pSnr); //选卡
char PCD_AuthState(uint8_t AuthMode, uint8_t BlockAddr, uint8_t *pKey, uint8_t *pSnr); //验证密码(密码A和密码B)
char PCD_WriteBlock(uint8_t BlockAddr, uint8_t *pData); //写数据
char PCD_ReadBlock(uint8_t BlockAddr, uint8_t *pData); //读数据
char PCD_Value(uint8_t mode, uint8_t BlockAddr, uint8_t *pValue);
char PCD_BakValue(uint8_t sourceBlockAddr, uint8_t goalBlockAddr);
char PCD_Halt(void);
void StartIDcardTask(void const * argument);
#endif
FM17580.c
/**
MFRC522-AN模块采用Philips MFRC522芯片设计读卡电路,使用方便,成本低廉,适用
于设备开发、读卡器开发等高级应用的用户、需要进行射频卡终端设计/生产的用户。
模块参数:
①工作电压:3.3v
②工作频率:13.56MHz
③支持卡类型:mifare1 s50、mifare1s70、 mifareUltraLight、mifare Pro, mifare Desfire
④通信方式:SPI协议
⑤环境工作温度:-20°C——80°C
M1卡分为16个扇区,每个扇区由四个块(块0、块1、块2、块3)组成
将16个扇区的64个块按绝对地址编号为:0~63
第0个扇区的块0(即绝对地址0块),用于存放厂商代码,已经固化不可更改
每个扇区的块0、块1、块2为数据块,可用于存放数据
每个扇区的块3为控制块(绝对地址为:块3、块7、块11.....)包括密码A,存取控制、密码B等
1、CPU选择
STM32F446RE,内部时钟180MHz
2、STM32CubeMX 定义任意两个引脚,作为复位脚和片选脚,并对引脚作出如下配置:
GPlO output level --High
GPIO mode --Output Push Pull
GPIO Pull-up/Pull-down --No pull-up and no pull-down
Maximum output speed --LOW
User label --RC522_RST/RC522_SDA
---------------------------------------------------------
开启SPI功能,模式选择-->Full-Duplex Master(全双工),其他配置如下:
Basic Parameters
Frame format-->Motorola
Data size -->8 Bits
First bit -->MSB First
Clock Parameters
Prescaler(for Baud Rate)-->8
Baud rate -->5.625MBits/s【RC522中的SPI最高速率为10MHz/s】
Clock Polarity(CPOL) -->LOW
Clock Phase(CPHA) -->1 Edge
Advanced Parameters
CRC Calculation -->Disabled
NSS Signal Type -->Software
3、接线方式:
SPI_MISO(MUC)--> MISO(器件)
SPI_MOSI(MUC)--> MOSI(器件)
其他引脚一一对应
//! Nucleo-F446RE接口
//SPI2_SCK PB10---(接Arduino D6)
//SPI2_MISO PC2----(接CN7左下2)
//SPI2_MOSI PC1----(接Arduino A4)
//RCC522_RST(CE) PC7----(接Arduino D9)
//RCC522_NSS(SDA) PB6----(接Arduino D10)
//RCC522_IRQ 悬空
4、SPI模式说明:SPI总线传输的四种模式:
* SPI传输的模式由CPOL:clock polarity 时钟的极性,和CPHA:clock phase 时钟的相位控制。
* RC522采用的是CPOL=0,CPHA=0的工作模式。在CubeMX中,SPI_CPHA设置为1Edge。
* ┌─────────┬───────┬───────┬─────────────────┬─────────────────┐
* │ SPI模式 │ CPOL │ CPHA │ 空闲时间SCLK状态 │ 采样时刻 │
* │ 0 │ 0 │ 0 │ 低电平 │ 奇数边沿(上升沿) │
* │ 1 │ 0 │ 1 │ 低电平 │ 偶数边沿(下降沿) │
* │ 2 │ 1 │ 0 │ 高电平 │ 奇数边沿(下降沿) │
* │ 3 │ 1 │ 1 │ 高电平 │ 偶数边沿(上升沿) │
* └─────────┴───────┴───────┴─────────────────┴─────────────────┘
5、应用函数
MFRC_Init();//初始化
PCD_Reset();//器件复位
PCD_Request(PICC_REQALL, RxBuffer);//返回值为0,代表寻卡成功;并把卡类型存入RxBuffer中
PCD_Anticoll(RxBuffer); //把(十六进制)的4个字节卡号存储在数组RxBuffer中
***********************************/
// #define RC522_SDA GPIO_Port GPIOB
// #define RC522_SDA Pin GPIO_PIN_6 //cs、nss、SDA指同一个口
// #define RC522_RST GPIO_Port GPIOC
// #define RC522_RST Pin GPIO_PIN_7
#include "FM17580.h"
extern SPI_HandleTypeDef hspi2;
// #define osDelay HAL_Delay
#define osDelay delay_ms
#define RS522_RST(N) HAL_GPIO_WritePin(RC522_RST_GPIO_Port, RC522_RST_Pin, N == 1 ? GPIO_PIN_SET : GPIO_PIN_RESET)
#define RC522_SDA(N) HAL_GPIO_WritePin(RC522_SDA_GPIO_Port, RC522_SDA_Pin, N == 1 ? GPIO_PIN_SET : GPIO_PIN_RESET)
/**************************************************************************************
* 函数名称:MFRC_Init
* 功能描述:MFRC初始化
* 入口参数:无
* 出口参数:无
* 返 回 值:无
* 说 明:MFRC的SPI接口速率为0~10Mbps
***************************************************************************************/
void MFRC_Init(void)
{
RC522_SDA(1);
RS522_RST(1);
}
/**************************************************************************************
* 函数名称: SPI_RW_Byte
* 功能描述: 模拟SPI读写一个字节
* 入口参数: -byte:要发送的数据
* 出口参数: -byte:接收到的数据
***************************************************************************************/
static uint8_t ret; // 这些函数是HAL与标准库不同的地方【读写函数】
uint8_t SPI2_RW_Byte(uint8_t byte)
{
//硬件SPI
HAL_SPI_TransmitReceive(&hspi2, &byte, &ret, 1, 10); // 把byte 写入,并读出一个值,把它存入ret
return ret; // 入口是byte 的地址,读取时用的也是ret地址,一次只写入一个值10
//下面是模拟SPI
// if (byte == 0x00) // 读数据时
// {
// ret = RC522_SPI_ReadByte();
// }
// RC522_SPI_SendByte(byte);
// return ret;
}
/**************************************************************************************
* 函数名称:MFRC_WriteReg
* 功能描述:写一个寄存器
* 入口参数:-addr:待写的寄存器地址
* -data:待写的寄存器数据
* 出口参数:无
* 返 回 值:无
* 说 明:无
***************************************************************************************/
void MFRC_WriteReg(uint8_t addr, uint8_t data)
{
uint8_t AddrByte;
AddrByte = (addr << 1) & 0x7E; // 求出地址字节
RC522_SDA(0); // NSS拉低
SPI2_RW_Byte(AddrByte); // 写地址字节
SPI2_RW_Byte(data); // 写数据
RC522_SDA(1); // NSS拉高
}
/**************************************************************************************
* 函数名称:MFRC_ReadReg
* 功能描述:读一个寄存器
* 入口参数:-addr:待读的寄存器地址
* 出口参数:无
* 返 回 值:-data:读到寄存器的数据
* 说 明:无
***************************************************************************************/
uint8_t MFRC_ReadReg(uint8_t addr)
{
uint8_t AddrByte, data;
AddrByte = ((addr << 1) & 0x7E) | 0x80; // 求出地址字节
RC522_SDA(0); // NSS拉低
SPI2_RW_Byte(AddrByte); // 写地址字节
data = SPI2_RW_Byte(0x00); // 读数据
RC522_SDA(1); // NSS拉高
return data;
}
/**************************************************************************************
* 函数名称:MFRC_SetBitMask
* 功能描述:设置寄存器的位
* 入口参数:-addr:待设置的寄存器地址
* -mask:待设置寄存器的位(可同时设置多个bit)
* 出口参数:无
* 返 回 值:无
* 说 明:无
***************************************************************************************/
void MFRC_SetBitMask(uint8_t addr, uint8_t mask)
{
uint8_t temp;
temp = MFRC_ReadReg(addr); // 先读回寄存器的值
MFRC_WriteReg(addr, temp | mask); // 处理过的数据再写入寄存器
}
/**************************************************************************************
* 函数名称:MFRC_ClrBitMask
* 功能描述:清除寄存器的位
* 入口参数:-addr:待清除的寄存器地址
* -mask:待清除寄存器的位(可同时清除多个bit)
* 出口参数:无
* 返 回 值:无
* 说 明:无
***************************************************************************************/
void MFRC_ClrBitMask(uint8_t addr, uint8_t mask)
{
uint8_t temp;
temp = MFRC_ReadReg(addr); // 先读回寄存器的值
MFRC_WriteReg(addr, temp & ~mask); // 处理过的数据再写入寄存器
}
/**************************************************************************************
* 函数名称:MFRC_CalulateCRC
* 功能描述:用MFRC计算CRC结果
* 入口参数:-pInData:带进行CRC计算的数据
* -len:带进行CRC计算的数据长度
* -pOutData:CRC计算结果
* 出口参数:-pOutData:CRC计算结果
* 返 回 值:无
* 说 明:无
***************************************************************************************/
void MFRC_CalulateCRC(uint8_t *pInData, uint8_t len, uint8_t *pOutData)
{
// 0xc1 1 2 pInData[2]
uint8_t temp;
uint32_t i;
MFRC_ClrBitMask(MFRC_DivIrqReg, 0x04); // 使能CRC中断
MFRC_WriteReg(MFRC_CommandReg, MFRC_IDLE); // 取消当前命令的执行
MFRC_SetBitMask(MFRC_FIFOLevelReg, 0x80); // 清除FIFO及其标志位
for (i = 0; i < len; i++) // 将待CRC计算的数据写入FIFO
{
MFRC_WriteReg(MFRC_FIFODataReg, *(pInData + i));
}
MFRC_WriteReg(MFRC_CommandReg, MFRC_CALCCRC); // 执行CRC计算
i = 100000;
do
{
temp = MFRC_ReadReg(MFRC_DivIrqReg); // 读取DivIrqReg寄存器的值
i--;
} while ((i != 0) && !(temp & 0x04)); // 等待CRC计算完成
pOutData[0] = MFRC_ReadReg(MFRC_CRCResultRegL); // 读取CRC计算结果
pOutData[1] = MFRC_ReadReg(MFRC_CRCResultRegM);
}
/**************************************************************************************
* 函数名称:MFRC_CmdFrame
* 功能描述:MFRC522和ISO14443A卡通讯的命令帧函数
* 入口参数:-cmd:MFRC522命令字
* -pIndata:MFRC522发送给MF1卡的数据的缓冲区首地址
* -InLenByte:发送数据的字节长度
* -pOutdata:用于接收MF1卡片返回数据的缓冲区首地址
* -pOutLenBit:MF1卡返回数据的位长度
* 出口参数:-pOutdata:用于接收MF1卡片返回数据的缓冲区首地址
* -pOutLenBit:用于MF1卡返回数据位长度的首地址
* 返 回 值:-status:错误代码(MFRC_OK、MFRC_NOTAGERR、MFRC_ERR)
* 说 明:无
***************************************************************************************/
char MFRC_CmdFrame(uint8_t cmd, uint8_t *pInData, uint8_t InLenByte, uint8_t *pOutData, uint16_t *pOutLenBit)
{
uint8_t lastBits;
uint8_t n;
uint32_t i;
char status = MFRC_ERR;
uint8_t irqEn = 0x00;
uint8_t waitFor = 0x00;
/*根据命令设置标志位*/
switch (cmd)
{
case MFRC_AUTHENT: // Mifare认证
irqEn = 0x12;
waitFor = 0x10; // idleIRq中断标志
break;
case MFRC_TRANSCEIVE: // 发送并接收数据
irqEn = 0x77;
waitFor = 0x30; // RxIRq和idleIRq中断标志
break;
}
/*发送命令帧前准备*/
MFRC_WriteReg(MFRC_ComIEnReg, irqEn | 0x80); // 开中断
MFRC_ClrBitMask(MFRC_ComIrqReg, 0x80); // 清除中断标志位SET1
MFRC_WriteReg(MFRC_CommandReg, MFRC_IDLE); // 取消当前命令的执行
MFRC_SetBitMask(MFRC_FIFOLevelReg, 0x80); // 清除FIFO缓冲区及其标志位
/*发送命令帧*/
for (i = 0; i < InLenByte; i++) // 写入命令参数
{
MFRC_WriteReg(MFRC_FIFODataReg, pInData[i]); // 写数据进 FIFODataReg
}
MFRC_WriteReg(MFRC_CommandReg, cmd); // 执行命令
if (cmd == MFRC_TRANSCEIVE)
{
MFRC_SetBitMask(MFRC_BitFramingReg, 0x80); // 启动发送
}
i = 300000; // 根据时钟频率调整,操作M1卡最大等待时间25ms
do // 认证 与寻卡等待时间
{
n = MFRC_ReadReg(MFRC_ComIrqReg); // 查询事件中断
i--;
} while ((i != 0) && !(n & 0x01) && !(n & waitFor)); // 等待命令完成
MFRC_ClrBitMask(MFRC_BitFramingReg, 0x80); // 停止发送
/*处理接收的数据*/
if (i != 0)
{
// 读错误标志寄存器BufferOfI CollErr ParityErr ProtocolErr
if (!(MFRC_ReadReg(MFRC_ErrorReg) & 0x1B))
{
status = MFRC_OK;
if (n & irqEn & 0x01) // 是否发生定时器中断
{
status = MFRC_NOTAGERR;
}
if (cmd == MFRC_TRANSCEIVE)
{
// 读FIFO中保存的字节数
n = MFRC_ReadReg(MFRC_FIFOLevelReg);
lastBits = MFRC_ReadReg(MFRC_ControlReg) & 0x07; // 最后接收到得字节的有效位数
if (lastBits)
{
*pOutLenBit = (n - 1) * 8 + lastBits; // N个字节数减去1(最后一个字节)+最后一位的位数 读取到的数据总位数
}
else
{
*pOutLenBit = n * 8; // 最后接收到的字节整个字节有效
}
if (n == 0)
{
n = 1;
}
if (n > MFRC_MAXRLEN)
{
n = MFRC_MAXRLEN;
}
for (i = 0; i < n; i++)
{
pOutData[i] = MFRC_ReadReg(MFRC_FIFODataReg);
}
}
}
else
{
status = MFRC_ERR;
}
}
MFRC_SetBitMask(MFRC_ControlReg, 0x80); // 停止定时器运行
MFRC_WriteReg(MFRC_CommandReg, MFRC_IDLE); // 取消当前命令的执行
return status;
}
/**************************************************************************************
* 函数名称:PCD_Reset
* 功能描述:PCD复位
* 入口参数:无
* 出口参数:无
* 返 回 值:无
* 说 明:无
***************************************************************************************/
void PCD_Reset(void)
{
/*硬复位*/
RS522_RST(1); // 用到复位引脚
osDelay(2);
RS522_RST(0);
osDelay(2);
RS522_RST(1);
osDelay(2);
/*软复位*/
MFRC_WriteReg(MFRC_CommandReg, MFRC_RESETPHASE);
osDelay(2);
/*复位后的初始化配置*/
MFRC_WriteReg(MFRC_ModeReg, 0x3D); // CRC初始值0x6363
MFRC_WriteReg(MFRC_TReloadRegL, 30); // 定时器重装值
MFRC_WriteReg(MFRC_TReloadRegH, 0);
MFRC_WriteReg(MFRC_TModeReg, 0x8D); // 定义内部定时器的设置
MFRC_WriteReg(MFRC_TPrescalerReg, 0x3E); // 设置定时器预分频值
MFRC_WriteReg(MFRC_TxAutoReg, 0x40); // 调制发送信号为100%ASK
PCD_AntennaOff(); // 关天线
osDelay(2);
PCD_AntennaOn(); // 开天线
}
/**************************************************************************************
* 函数名称:PCD_AntennaOn
* 功能描述:开启天线,使能PCD发送能量载波信号
* 入口参数:无
* 出口参数:无
* 返 回 值:无
* 说 明:每次开启或关闭天线之间应至少有1ms的间隔
***************************************************************************************/
void PCD_AntennaOn(void)
{
uint8_t temp;
temp = MFRC_ReadReg(MFRC_TxControlReg);
if (!(temp & 0x03))
{
MFRC_SetBitMask(MFRC_TxControlReg, 0x03);
}
}
/**************************************************************************************
* 函数名称:PCD_AntennaOff
* 功能描述:关闭天线,失能PCD发送能量载波信号
* 入口参数:无
* 出口参数:无
* 返 回 值:无
* 说 明:每次开启或关闭天线之间应至少有1ms的间隔
***************************************************************************************/
void PCD_AntennaOff(void)
{
MFRC_ClrBitMask(MFRC_TxControlReg, 0x03);
}
/***************************************************************************************
* 函数名称:PCD_Init
* 功能描述:读写器初始化
* 入口参数:无
* 出口参数:无
* 返 回 值:无
* 说 明:无
***************************************************************************************/
void PCD_Init(void)
{
MFRC_Init(); // MFRC管脚配置
PCD_Reset(); // PCD复位 并初始化配置
PCD_AntennaOff(); // 关闭天线
PCD_AntennaOn(); // 开启天线
}
/***************************************************************************************
* 函数名称:PCD_Request
* 功能描述:寻卡
* 入口参数: -RequestMode:寻卡方式
* PICC_REQIDL:寻天线区内未进入休眠状态
* PICC_REQALL:寻天线区内全部卡
* -pCardType: 用于保存卡片类型
* 出口参数:-pCardType:卡片类型
* 0x4400:Mifare_UltraLight
* 0x0400:Mifare_One(S50)
* 0x0200:Mifare_One(S70)
* 0x0800:Mifare_Pro(X)
* 0x4403:Mifare_DESFire
* 返 回 值:-status:错误代码(PCD_OK、PCD_NOTAGERR、PCD_ERR)
* 说 明:无
***************************************************************************************/
char PCD_Request(uint8_t RequestMode, uint8_t *pCardType)
{
int status;
uint16_t unLen;
uint8_t CmdFrameBuf[MFRC_MAXRLEN];
MFRC_ClrBitMask(MFRC_Status2Reg, 0x08); // 关内部温度传感器
MFRC_WriteReg(MFRC_BitFramingReg, 0x07); // 存储模式,发送模式,是否启动发送等
MFRC_SetBitMask(MFRC_TxControlReg, 0x03); // 配置调制信号13.56MHZ
CmdFrameBuf[0] = RequestMode; // 存入 卡片命令字
status = MFRC_CmdFrame(MFRC_TRANSCEIVE, CmdFrameBuf, 1, CmdFrameBuf, &unLen);
if ((status == PCD_OK) && (unLen == 0x10)) // 寻卡成功返回卡类型
{
*pCardType = CmdFrameBuf[0];
*(pCardType + 1) = CmdFrameBuf[1];
}
return status;
}
/***************************************************************************************
* 函数名称:PCD_Anticoll
* 功能描述:防冲撞,获取卡号
* 入口参数:-pSnr:用于保存卡片序列号,4字节
* 出口参数:-pSnr:卡片序列号,4字节
* 返 回 值:-status:错误代码(PCD_OK、PCD_NOTAGERR、PCD_ERR)
* 说 明:无
***************************************************************************************/
char PCD_Anticoll(uint8_t *pSnr)
{
char status;
uint8_t i, snr_check = 0;
uint16_t unLen;
uint8_t CmdFrameBuf[MFRC_MAXRLEN];
MFRC_ClrBitMask(MFRC_Status2Reg, 0x08); // 清MFCryptol On位 只有成功执行MFAuthent命令后,该位才能置位
MFRC_WriteReg(MFRC_BitFramingReg, 0x00); // 清理寄存器 停止收发
MFRC_ClrBitMask(MFRC_CollReg, 0x80); // 清ValuesAfterColl所有接收的位在冲突后被清除
CmdFrameBuf[0] = PICC_ANTICOLL1; // 卡片防冲突命令
CmdFrameBuf[1] = 0x20;
status = MFRC_CmdFrame(MFRC_TRANSCEIVE, CmdFrameBuf, 2, CmdFrameBuf, &unLen); // 与卡片通信
if (status == PCD_OK) // 通信成功
{
for (i = 0; i < 4; i++)
{
*(pSnr + i) = CmdFrameBuf[i]; // 读出UID
snr_check ^= CmdFrameBuf[i];
}
if (snr_check != CmdFrameBuf[i])
{
status = PCD_ERR;
}
}
MFRC_SetBitMask(MFRC_CollReg, 0x80);
return status;
}
/***************************************************************************************
* 函数名称:PCD_Select
* 功能描述:选定卡片
* 入口参数:-pSnr:卡片序列号,4字节
* 出口参数:无
* 返 回 值:-status:错误代码(PCD_OK、PCD_NOTAGERR、PCD_ERR)
* 说 明:无
***************************************************************************************/
char PCD_Select(uint8_t *pSnr)
{
char status;
uint8_t i;
uint16_t unLen;
uint8_t CmdFrameBuf[MFRC_MAXRLEN];
CmdFrameBuf[0] = PICC_ANTICOLL1;
CmdFrameBuf[1] = 0x70;
CmdFrameBuf[6] = 0;
for (i = 0; i < 4; i++)
{
CmdFrameBuf[i + 2] = *(pSnr + i);
CmdFrameBuf[6] ^= *(pSnr + i);
}
MFRC_CalulateCRC(CmdFrameBuf, 7, &CmdFrameBuf[7]);
MFRC_ClrBitMask(MFRC_Status2Reg, 0x08);
status = MFRC_CmdFrame(MFRC_TRANSCEIVE, CmdFrameBuf, 9, CmdFrameBuf, &unLen);
if ((status == PCD_OK) && (unLen == 0x18))
{
status = PCD_OK;
}
else
{
status = PCD_ERR;
}
return status;
}
/***************************************************************************************
* 函数名称:PCD_AuthState
* 功能描述:验证卡片密码
* 入口参数:-AuthMode:验证模式
* PICC_AUTHENT1A:验证A密码
* PICC_AUTHENT1B:验证B密码
* -BlockAddr:块地址(0~63)
* -pKey:密码
* -pSnr:卡片序列号,4字节
* 出口参数:无
* 返 回 值:-status:错误代码(PCD_OK、PCD_NOTAGERR、PCD_ERR)
* 说 明:验证密码时,以扇区为单位,BlockAddr参数可以是同一个扇区的任意块
***************************************************************************************/
char PCD_AuthState(uint8_t AuthMode, uint8_t BlockAddr, uint8_t *pKey, uint8_t *pSnr)
{
char status;
uint16_t unLen;
uint8_t i, CmdFrameBuf[MFRC_MAXRLEN];
CmdFrameBuf[0] = AuthMode;
CmdFrameBuf[1] = BlockAddr;
for (i = 0; i < 6; i++)
{
CmdFrameBuf[i + 2] = *(pKey + i);
}
for (i = 0; i < 4; i++)
{
CmdFrameBuf[i + 8] = *(pSnr + i);
}
status = MFRC_CmdFrame(MFRC_AUTHENT, CmdFrameBuf, 12, CmdFrameBuf, &unLen);
if ((status != PCD_OK) || (!(MFRC_ReadReg(MFRC_Status2Reg) & 0x08)))
{
status = PCD_ERR;
}
return status;
}
/***************************************************************************************
* 函数名称:PCD_WriteBlock
* 功能描述:写MF1卡数据块
* 入口参数:-BlockAddr:块地址。M1卡总共有16个扇区(每个扇区有:3个数据块+1个控制块),共64个块
* -pData: 用于保存待写入的数据,16字节
* 出口参数:无
* 返 回 值:-status:错误代码(PCD_OK、PCD_NOTAGERR、PCD_ERR)
* 说 明:无
***************************************************************************************/
char PCD_WriteBlock(uint8_t BlockAddr, uint8_t *pData)
{
char status;
uint16_t unLen;
uint8_t i, CmdFrameBuf[MFRC_MAXRLEN];
CmdFrameBuf[0] = PICC_WRITE;
CmdFrameBuf[1] = BlockAddr;
MFRC_CalulateCRC(CmdFrameBuf, 2, &CmdFrameBuf[2]);
status = MFRC_CmdFrame(MFRC_TRANSCEIVE, CmdFrameBuf, 4, CmdFrameBuf, &unLen);
if ((status != PCD_OK) || (unLen != 4) || ((CmdFrameBuf[0] & 0x0F) != 0x0A))
{
status = PCD_ERR;
}
if (status == PCD_OK)
{
for (i = 0; i < 16; i++)
{
CmdFrameBuf[i] = *(pData + i);
}
MFRC_CalulateCRC(CmdFrameBuf, 16, &CmdFrameBuf[16]);
status = MFRC_CmdFrame(MFRC_TRANSCEIVE, CmdFrameBuf, 18, CmdFrameBuf, &unLen);
if ((status != PCD_OK) || (unLen != 4) || ((CmdFrameBuf[0] & 0x0F) != 0x0A))
{
status = PCD_ERR;
}
}
return status;
}
/***************************************************************************************
* 函数名称:PCD_ReadBlock
* 功能描述:读MF1卡数据块
* 入口参数:-BlockAddr:块地址
* -pData: 用于保存读出的数据,16字节
* 出口参数:-pData: 用于保存读出的数据,16字节
* 返 回 值:-status:错误代码(PCD_OK、PCD_NOTAGERR、PCD_ERR)
* 说 明:无
***************************************************************************************/
char PCD_ReadBlock(uint8_t BlockAddr, uint8_t *pData)
{
char status;
uint16_t unLen;
uint8_t i, CmdFrameBuf[MFRC_MAXRLEN];
CmdFrameBuf[0] = PICC_READ;
CmdFrameBuf[1] = BlockAddr;
MFRC_CalulateCRC(CmdFrameBuf, 2, &CmdFrameBuf[2]);
status = MFRC_CmdFrame(MFRC_TRANSCEIVE, CmdFrameBuf, 4, CmdFrameBuf, &unLen);
if ((status == PCD_OK) && (unLen == 0x90))
{
for (i = 0; i < 16; i++)
{
*(pData + i) = CmdFrameBuf[i];
}
}
else
{
status = PCD_ERR;
}
return status;
}
/***************************************************************************************
* 函数名称:PCD_Value
* 功能描述:对MF1卡数据块增减值操作
* 入口参数:
* -BlockAddr:块地址
* -pValue:四字节增值的值,低位在前
* -mode:数值块操作模式
* PICC_INCREMENT:增值
* PICC_DECREMENT:减值
* 出口参数:无
* 返 回 值:-status:错误代码(PCD_OK、PCD_NOTAGERR、PCD_ERR)
* 说 明:无
***************************************************************************************/
char PCD_Value(uint8_t mode, uint8_t BlockAddr, uint8_t *pValue)
{
// 0XC1 1 Increment[4]={0x03, 0x01, 0x01, 0x01};
char status;
uint16_t unLen;
uint8_t i, CmdFrameBuf[MFRC_MAXRLEN];
CmdFrameBuf[0] = mode;
CmdFrameBuf[1] = BlockAddr;
MFRC_CalulateCRC(CmdFrameBuf, 2, &CmdFrameBuf[2]);
status = MFRC_CmdFrame(MFRC_TRANSCEIVE, CmdFrameBuf, 4, CmdFrameBuf, &unLen);
if ((status != PCD_OK) || (unLen != 4) || ((CmdFrameBuf[0] & 0x0F) != 0x0A))
{
status = PCD_ERR;
}
if (status == PCD_OK)
{
for (i = 0; i < 16; i++)
{
CmdFrameBuf[i] = *(pValue + i);
}
MFRC_CalulateCRC(CmdFrameBuf, 4, &CmdFrameBuf[4]);
unLen = 0;
status = MFRC_CmdFrame(MFRC_TRANSCEIVE, CmdFrameBuf, 6, CmdFrameBuf, &unLen);
if (status != PCD_ERR)
{
status = PCD_OK;
}
}
if (status == PCD_OK)
{
CmdFrameBuf[0] = PICC_TRANSFER;
CmdFrameBuf[1] = BlockAddr;
MFRC_CalulateCRC(CmdFrameBuf, 2, &CmdFrameBuf[2]);
status = MFRC_CmdFrame(MFRC_TRANSCEIVE, CmdFrameBuf, 4, CmdFrameBuf, &unLen);
if ((status != PCD_OK) || (unLen != 4) || ((CmdFrameBuf[0] & 0x0F) != 0x0A))
{
status = PCD_ERR;
}
}
return status;
}
/***************************************************************************************
* 函数名称:PCD_BakValue
* 功能描述:备份钱包(块转存)
* 入口参数:-sourceBlockAddr:源块地址
* -goalBlockAddr :目标块地址
* 出口参数:无
* 返 回 值:-status:错误代码(PCD_OK、PCD_NOTAGERR、PCD_ERR)
* 说 明:只能在同一个扇区内转存
***************************************************************************************/
char PCD_BakValue(uint8_t sourceBlockAddr, uint8_t goalBlockAddr)
{
char status;
uint16_t unLen;
uint8_t CmdFrameBuf[MFRC_MAXRLEN];
CmdFrameBuf[0] = PICC_RESTORE;
CmdFrameBuf[1] = sourceBlockAddr;
MFRC_CalulateCRC(CmdFrameBuf, 2, &CmdFrameBuf[2]);
status = MFRC_CmdFrame(MFRC_TRANSCEIVE, CmdFrameBuf, 4, CmdFrameBuf, &unLen);
if ((status != PCD_OK) || (unLen != 4) || ((CmdFrameBuf[0] & 0x0F) != 0x0A))
{
status = PCD_ERR;
}
if (status == PCD_OK)
{
CmdFrameBuf[0] = 0;
CmdFrameBuf[1] = 0;
CmdFrameBuf[2] = 0;
CmdFrameBuf[3] = 0;
MFRC_CalulateCRC(CmdFrameBuf, 4, &CmdFrameBuf[4]);
status = MFRC_CmdFrame(MFRC_TRANSCEIVE, CmdFrameBuf, 6, CmdFrameBuf, &unLen);
if (status != PCD_ERR)
{
status = PCD_OK;
}
}
if (status != PCD_OK)
{
return PCD_ERR;
}
CmdFrameBuf[0] = PICC_TRANSFER;
CmdFrameBuf[1] = goalBlockAddr;
MFRC_CalulateCRC(CmdFrameBuf, 2, &CmdFrameBuf[2]);
status = MFRC_CmdFrame(MFRC_TRANSCEIVE, CmdFrameBuf, 4, CmdFrameBuf, &unLen);
if ((status != PCD_OK) || (unLen != 4) || ((CmdFrameBuf[0] & 0x0F) != 0x0A))
{
status = PCD_ERR;
}
return status;
}
/***************************************************************************************
* 函数名称:PCD_Halt
* 功能描述:命令卡片进入休眠状态
* 入口参数:无
* 出口参数:无
* 返 回 值:-status:错误代码(PCD_OK、PCD_NOTAGERR、PCD_ERR)
* 说 明:无
***************************************************************************************/
char PCD_Halt(void)
{
char status;
uint16_t unLen;
uint8_t CmdFrameBuf[MFRC_MAXRLEN];
CmdFrameBuf[0] = PICC_HALT;
CmdFrameBuf[1] = 0;
MFRC_CalulateCRC(CmdFrameBuf, 2, &CmdFrameBuf[2]);
status = MFRC_CmdFrame(MFRC_TRANSCEIVE, CmdFrameBuf, 4, CmdFrameBuf, &unLen);
return status;
}
SPI_Init
void MX_SPI1_Init(void)
{
/* USER CODE BEGIN SPI1_Init 0 */
/* USER CODE END SPI1_Init 0 */
/* USER CODE BEGIN SPI1_Init 1 */
/* USER CODE END SPI1_Init 1 */
hspi1.Instance = SPI1;
hspi1.Init.Mode = SPI_MODE_MASTER;
hspi1.Init.Direction = SPI_DIRECTION_2LINES;
hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;
hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;
hspi1.Init.NSS = SPI_NSS_SOFT;
hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_8;
hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
hspi1.Init.CRCPolynomial = 7;
hspi1.Init.CRCLength = SPI_CRC_LENGTH_DATASIZE;
hspi1.Init.NSSPMode = SPI_NSS_PULSE_ENABLE;
if (HAL_SPI_Init(&hspi1) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN SPI1_Init 2 */
/* USER CODE END SPI1_Init 2 */
}
GPIO_InitStruct.Pin = GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.Alternate = GPIO_AF0_SPI1;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/* SPI1 interrupt Init */
HAL_NVIC_SetPriority(SPI1_IRQn, 1, 0);
HAL_NVIC_EnableIRQ(SPI1_IRQn);
M1_Card.h
#ifndef _M1_CARD_H_
#define _M1_CARD_H_
#include "stm32g0xx_hal.h"
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "usart.h"
#include "FM17580.h"
extern uint8_t readUid[5];
extern uint8_t UID[5]; //定义一张已知卡号,可以通过串口打印通过下面读取到的打印到上位机,再把那个读取的卡号填入数组
extern uint8_t DefaultKey[6]; // 默认秘钥
/*函数声明*/
void RC522_Init(void);
uint8_t EntranceGuard(uint8_t *readUid,void(*funCallBack)(void));
//void DoorSensor(void);
void RfidIndicator(void);
//void notarize_type1(void);
char WriteAmount(uint8_t addr, uint32_t pData);
char ReadAmount(uint8_t addr, uint32_t *pData);
char ReadAmount(uint8_t addr, uint32_t *pData);
char WriteDataBlock(uint8_t addr, uint8_t *pData, uint8_t Len);
char ReadDataBlock(uint8_t addr, uint8_t *pData);
#endif
M1_Card.c
#include "M1_card.h"
uint8_t readUid[5];
uint8_t UID[5] = {0x37, 0x7e, 0xbc, 0xfd}; // 自定义的卡号,用于比较
uint8_t DefaultKey[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; // 默认秘钥
/***************************************************************************************
* 函数名称:RC522_Init
* 功能描述:初始化
* 入口参数:无
* 出口参数:无
* 返 回 值:无
* 说 明:无
***************************************************************************************/
void RC522_Init(void)
{
MFRC_Init();
PCD_Reset();
printf("RC522初始化完成\n");
}
/***************************************************************************************
* 函数名称:门禁开门
* 功能描述:只读取并显示卡号,成功读取到卡号就退出,并调用回调功能函数
* 入口参数:-readUid:用于保存卡片序列号,4字节
-funCallBack:函数传参,无需会掉功能函数时填NULL即可
* 出口参数:
* 返 回 值:读到卡号返回0,失败返回1
* 说 明:无
***************************************************************************************/
uint8_t EntranceGuard(uint8_t *readUid, void (*funCallBack)(void))
{
uint8_t Temp[5]; // 存放IC卡的类型和UID(IC卡序列号)
if (PCD_Request(PICC_REQALL, Temp) == PCD_OK) // 寻卡
{//成功
if (Temp[0] == 0x04 && Temp[1] == 0x00)
printf("Mifare1-S50\n");
else if (Temp[0] == 0x02 && Temp[1] == 0x00)
printf("Mifare1-S70");
else if (Temp[0] == 0x44 && Temp[1] == 0x00)
printf("Mifare-UltraLight(MF0)");
else if (Temp[0] == 0x08 && Temp[1] == 0x00)
printf("Mifare-Pro(MF2)");
else if (Temp[0] == 0x44 && Temp[1] == 0x03)
printf("Mifare Desire(MF3)");
else
printf("Unknown");
if (PCD_Anticoll(readUid) == PCD_OK) // 防冲撞,获取卡号,存入readUid
{
printf("防冲撞成功\r\n");
// 防冲撞成功
if (funCallBack != NULL)
funCallBack(); // 调用功能执行函数,如指示灯信号
return 0;
}
}
return 1;
}
/**
* @brief 判断 addr 是否数据块
* @param addr,块绝对地址(0-63)
* @retval 返回值 1:是数据块;0:不是数据块
*/
char IsDataBlock(uint8_t addr)
{
if (addr == 0)
{
printf("第0扇区的块0不可更改,不应对其进行操作\n");
return 0;
}
// 如果是数据块(不包含数据块0)
if ((addr < 64) && (((addr + 1) % 4) != 0))
{
return 1;
}
printf("块地址不是指向数据块\n");
return 0;
}
/**
* @brief 写 pData 字符串到M1卡中的数据块
* @param addr,数据块地址(不能写入控制块)
* @param pData,写入的数据,16字节
* @retval 状态值= PCD_OK,成功
*/
char PCD_WriteString(uint8_t addr, uint8_t *pData)
{
/* 如果是数据块(不包含数据块0),则写入 */
if (IsDataBlock(addr))
{
return PCD_WriteBlock(addr, pData);
}
return PCD_ERR;
}
/**
* @brief 读取M1卡中的一块数据到 pData
* @param addr,数据块地址(不读取控制块)
* @param pData,读出的数据,16字节
* @retval 状态值= PCD_OK,成功
*/
char PCD_ReadString(uint8_t addr, uint8_t *pData)
{
/* 如果是数据块(不包含数据块0),则读取 */
if (IsDataBlock(addr))
{
return PCD_ReadBlock(addr, pData);
}
return PCD_ERR;
}
/**
* @DESCRIPTION: 写入钱包金额
* @INPUT ARGS: none
* @OUTPUT ARGS: none
* @NOTE : none
* @param {uint8_t} addr:块地址
* @param {uint32_t} pData:写入的金额
* @return {*} 成功返回PCD_OK
*/
char WriteAmount(uint8_t addr, uint32_t pData)
{
char status;
uint8_t ucComMF522Buf[16];
ucComMF522Buf[0] = (pData & ((uint32_t)0x000000ff));
ucComMF522Buf[1] = (pData & ((uint32_t)0x0000ff00)) >> 8;
ucComMF522Buf[2] = (pData & ((uint32_t)0x00ff0000)) >> 16;
ucComMF522Buf[3] = (pData & ((uint32_t)0xff000000)) >> 24;
ucComMF522Buf[4] = ~(pData & ((uint32_t)0x000000ff));
ucComMF522Buf[5] = ~(pData & ((uint32_t)0x0000ff00)) >> 8;
ucComMF522Buf[6] = ~(pData & ((uint32_t)0x00ff0000)) >> 16;
ucComMF522Buf[7] = ~(pData & ((uint32_t)0xff000000)) >> 24;
ucComMF522Buf[8] = (pData & ((uint32_t)0x000000ff));
ucComMF522Buf[9] = (pData & ((uint32_t)0x0000ff00)) >> 8;
ucComMF522Buf[10] = (pData & ((uint32_t)0x00ff0000)) >> 16;
ucComMF522Buf[11] = (pData & ((uint32_t)0xff000000)) >> 24;
ucComMF522Buf[12] = addr;
ucComMF522Buf[13] = ~addr;
ucComMF522Buf[14] = addr;
ucComMF522Buf[15] = ~addr;
status = PCD_WriteBlock(addr, ucComMF522Buf);
return status;
}
/**
* @DESCRIPTION: 读取钱包金额
* @INPUT ARGS: none
* @OUTPUT ARGS: none
* @NOTE : none
* @param {uint8_t} addr:块地址
* @param {uint32_t} *pData:读出的金额
* @return {*}: 成功返回PCD_OK
*/
char ReadAmount(uint8_t addr, uint32_t *pData)
{
char status = PCD_ERR;
uint8_t j;
uint8_t ucComMF522Buf[16];
status = PCD_ReadBlock(addr, ucComMF522Buf);
if (status != PCD_OK)
return status;
for (j = 0; j < 4; j++)
{
if ((ucComMF522Buf[j] != ucComMF522Buf[j + 8]) && (ucComMF522Buf[j] != ~ucComMF522Buf[j + 4])) // 验证一下是不是钱包的数据
break;
}
if (j == 4)
{
status = PCD_OK;
*pData = ucComMF522Buf[0] + (ucComMF522Buf[1] << 8) + (ucComMF522Buf[2] << 16) + (ucComMF522Buf[3] << 24);
}
else
{
status = PCD_ERR;
*pData = 0;
}
return status;
}
/**
* @brief 修改控制块 addr 的密码A。注意 addr 指的是控制块的地址。
* 必须要校验密码B,密码B默认为6个0xFF,如果密码B也忘记了,那就改不了密码A了
* @note 注意:该函数仅适用于默认的存储控制模式,若是其他的话可能出现问题
* @param addr:[控制块]所在的地址。M1卡总共有16个扇区(每个扇区有:3个数据块+1个控制块),共64个块
* @param pKeyA:指向新的密码A字符串,六个字符,比如 "123456"
* @retval 成功返回 PCD_OK
*/
char ChangeKeyA(uint8_t addr, uint8_t *pKeyA)
{
uint8_t KeyBValue[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; // B密钥
uint8_t ucArrayID[4]; // 先后存放IC卡的类型和UID(IC卡序列号)
uint8_t ucComMF522Buf[16];
uint8_t j;
// 寻卡
while (PCD_Request(PICC_REQALL, ucArrayID) != PCD_OK)
{
printf("寻卡失败\n");
delay_ms(1000);
}
printf("寻卡成功\n");
// 防冲突(当有多张卡进入读写器操作范围时,防冲突机制会从其中选择一张进行操作)
if (PCD_Anticoll(ucArrayID) == PCD_OK)
{
// 选中卡
PCD_Select(ucArrayID);
// 校验 B 密码
if (PCD_AuthState(PICC_AUTHENT1B, addr, KeyBValue, ucArrayID) != PCD_OK)
{
printf("检验密码B失败\n");
}
// 读取控制块里原本的数据(只要修改密码A,其他数据不改)
if (PCD_ReadBlock(addr, ucComMF522Buf) != PCD_OK)
{
printf("读取控制块数据失败\n");
return PCD_ERR;
}
// 修改密码A
for (j = 0; j < 6; j++)
ucComMF522Buf[j] = pKeyA[j];
if (PCD_WriteBlock(addr, ucComMF522Buf) != PCD_OK)
{
printf("写入数据到控制块失败\n");
return PCD_ERR;
}
printf("密码A修改成功!\n");
PCD_Halt();
return PCD_OK;
}
return PCD_ERR;
}
/**
* @brief 按照RC522操作流程写入16字节数据到块 addr
* 函数里校验的是密码B,密码B默认为6个0xFF,也可以校验密码A
* 用法:WriteDataBlock( 1, "123456789\n", 10); //字符串不够16个字节的后面补零写入
* @note 注意:该函数仅适用于默认的存储控制模式,若是其他的话可能出现问题
* 注意:使用该函数要注意 addr 是块0、数据块还是控制块,该函数内部不对此做判断
* @param addr:任意块地址。M1卡总共有16个扇区(每个扇区有:3个数据块+1个控制块),共64个块
* @param pData:指向要写入的数据,最大16个字符
* @param Len:要写入数据的字节数
* @retval 成功返回 PCD_OK
*/
char WriteDataBlock(uint8_t addr, uint8_t *pData, uint8_t Len)
{
uint8_t KeyBValue[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; // B密钥
uint8_t ucArrayID[4]; // 先后存放IC卡的类型和UID(IC卡序列号)
uint8_t ucComMF522Buf[16];
uint8_t j;
// 寻卡
while (PCD_Request(PICC_REQALL, ucArrayID) != PCD_OK)
{
printf("寻卡失败\n");
delay_ms(1000);
}
printf("寻卡成功\n");
// 防冲突(当有多张卡进入读写器操作范围时,防冲突机制会从其中选择一张进行操作)
if (PCD_Anticoll(ucArrayID) == PCD_OK)
{
// 选中卡
PCD_Select(ucArrayID);
// 校验 B 密码
if (PCD_AuthState(PICC_AUTHENT1B, addr, KeyBValue, ucArrayID) != PCD_OK)
{
printf("检验密码B失败\n");
}
// 拷贝 pData 里的 Len 个字符到 ucComMF522Buf
for (j = 0; j < 16; j++)
{
if (j < Len)
ucComMF522Buf[j] = pData[j];
else
ucComMF522Buf[j] = 0; // 16个字节若是未填满的字节置0
}
// 写入字符串
if (PCD_WriteBlock(addr, ucComMF522Buf) != PCD_OK)
{
printf("写入数据到数据块失败\n");
return PCD_ERR;
}
printf("写入数据成功!\n");
PCD_Halt();
return PCD_OK;
}
return PCD_ERR;
}
/**
* @brief 按照RC522操作流程读取块 addr
* 函数里校验的是密码B,密码B默认为6个0xFF,也可以校验密码A
* 用法:ReadDataBlock( 1, databuf); // databuf 至少为16字节:uint8_t databuf[16];
* @note 注意:该函数仅适用于默认的存储控制模式,若是其他的话可能出现问题
* 注意:使用该函数要注意 addr 是块0、数据块还是控制块,该函数内部不对此做判断
* @param addr:任意块地址。M1卡总共有16个扇区(每个扇区有:3个数据块+1个控制块),共64个块
* @param pData:指向读取到的数据,包含16个字符
* @retval 成功返回 PCD_OK
*/
char ReadDataBlock(uint8_t addr, uint8_t *pData)
{
uint8_t KeyBValue[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; // B密钥
uint8_t ucArrayID[4]; // 先后存放IC卡的类型和UID(IC卡序列号)
// 寻卡
while (PCD_Request(PICC_REQALL, ucArrayID) != PCD_OK)
{
printf("寻卡失败\n");
delay_ms(1000);
}
printf("寻卡成功\n");
// 防冲突(当有多张卡进入读写器操作范围时,防冲突机制会从其中选择一张进行操作)
if (PCD_Anticoll(ucArrayID) == PCD_OK)
{
// 选中卡
PCD_Select(ucArrayID);
// 校验 B 密码
if (PCD_AuthState(PICC_AUTHENT1B, addr, KeyBValue, ucArrayID) != PCD_OK)
{
printf("检验密码B失败\n");
}
// 读取数据块里的数据到 pData
if (PCD_ReadBlock(addr, pData) != PCD_OK)
{
printf("读取数据块失败\n");
return PCD_ERR;
}
printf("读取数据成功!\n");
PCD_Halt();
return PCD_OK;
}
return PCD_ERR;
}
这是所有的代码现在就是一直寻卡失败然后进行不了下一步,用的是M1的卡
用的RC522的源码作者是基础篇010.2 STM32驱动RC522 RFID模块之二:STM32硬件SPI驱动RC522_stm32 rc522-CSDN博客