CC2640R2F学习笔记(25)——RFID SI522使用

一、简介

ACD(低功耗寻卡)只是一种模式,和 PCD(普通寻卡)是一样的,在使用到 ACD 模式时,其实就是普通的读写模式和低功耗模式的切换,可以理解为“ACD 函数”中嵌套了一个“PCD 函数”,达到触发条件后进入“PCD 函 数”,执行函数内容,最后回到“ACD 函数”,等待下一次的触发。在使能 ACD 后,一旦检测到场强的变弱,触发中断,就认为有卡进入,此时清除中断后,需要再次初始化 ACD,防止多次进中断,从而导致读卡不稳定。

SI522 ACD模式与读写模式状态切换

二、硬件连接

功能口 引脚
MISO DIO_8
MOSI DIO_9
CLK DIO_10
CSN DIO_11
RST DIO_1
IRQ DIO_5

三、添加SPI驱动

查看 CC2640R2F学习笔记(22)——SPI接口使用

四、移植文件

链接:https://pan.baidu.com/s/1ztR1SZrRsv7Bxhj1Vnpbaw 提取码:lasq
board_si522.cboard_si522.huser_si522.cuser_si522.h 四个文件拖拽至CCS工程的Application文件夹下

添加文件过程中,选项选择如下



4.1 board_si522.c

/*********************************************************************
 * INCLUDES
 */
#include "_hal_types.h"
#include <ti/sysbios/knl/Clock.h>
#include <ti/drivers/pin/PINCC26XX.h>
#include "util.h"

#include "board_si522.h"

static void irqCallbackFunc(PIN_Handle pinHandle, PIN_Id pinId);
static void timer_irqDebounceCallback(UArg arg);

/*********************************************************************
 * LOCAL VARIABLES
 */
// RST引脚配置
static PIN_State s_si522RstState;
static PIN_Handle s_si522RstHandle;
static PIN_Config s_si522RstPinConfig[] =
{
    SI522_RST_IO | PIN_GPIO_OUTPUT_EN | PIN_GPIO_HIGH | PIN_PUSHPULL | PIN_DRVSTR_MIN,
    PIN_TERMINATE
};

// IRQ引脚配置
static PIN_State s_si522IrqState;
static PIN_Handle s_si522IrqHandle;
static PIN_Config s_si522IrqPinConfig[] =
{
    SI522_IRQ_IO | PIN_GPIO_OUTPUT_DIS | PIN_INPUT_EN | PIN_PULLDOWN,           // 默认下拉
    PIN_TERMINATE
};
static uint8 s_irqValue;                                                        // 中断值
static Clock_Struct s_irqDebounceTimer;                                         // 中断消抖的定时器
static IrqCallbackFunc_t s_appIrqChangeHandler = NULL;                          // 对应应用层回调函数的函数指针

/*********************************************************************
 * PUBLIC FUNCTIONS
 */

/**
 @brief SI522的RST引脚初始化
 @param 无
 @return 无
*/
void Board_Si522RstPinInit(void)
{
    s_si522RstHandle = PIN_open(&s_si522RstState, s_si522RstPinConfig);
}

/**
 @brief SI522的RST引脚设置
 @param pinId -[in] 引脚号
 @param pinState -[in] 引脚状态
 @return 无
*/
void Board_Si522RstPinSet(PIN_Id pinId, bool pinState)
{
    if(pinState == SI522_RST_OFF)
    {
        PIN_setOutputValue(s_si522RstHandle, pinId, 1);
    }
    else if(pinState == SI522_RST_ON)
    {
        PIN_setOutputValue(s_si522RstHandle, pinId, 0);
    }
}

/**
 @brief SI522中断初始化函数
 @param irqAppCallback -[in] 中断回调函数
 @return 无
*/
void Board_Si522IrqInit(IrqCallbackFunc_t irqAppCallback)
{
    s_si522IrqHandle = PIN_open(&s_si522IrqState, s_si522IrqPinConfig);         // 初始化中断的IO

    PIN_registerIntCb(s_si522IrqHandle, irqCallbackFunc);                       // 注册回调函数

    // 修改成双沿中断触发,以避免睡眠之后中断不灵
    PIN_setConfig(s_si522IrqHandle, PIN_BM_IRQ, SI522_IRQ_IO | PIN_IRQ_BOTHEDGES);  // PIN_IRQ_NEGEDGE

#ifdef POWER_SAVING                                                             // 低功耗下的配置
    PIN_setConfig(s_si522IrqHandle, PINCC26XX_BM_WAKEUP, SI522_IRQ_IO | PINCC26XX_WAKEUP_NEGEDGE);
#endif // POWER_SAVING
                                                                                // 初始化中断消抖的定时器
    Util_constructClock(&s_irqDebounceTimer, timer_irqDebounceCallback, IRQ_DEBOUNCE_TIMEOUT, 0, false, 0);

    s_appIrqChangeHandler = irqAppCallback;                                     // 保存应用层的函数指针
}


/*********************************************************************
 * LOCAL FUNCTIONS
 */

/**
 @brief IRQ中断回调函数
 @param pinHandle -[in] 引脚句柄
 @param pinId -[in] 引脚号
 @return 无
*/
static void irqCallbackFunc(PIN_Handle pinHandle, PIN_Id pinId)
{
    s_irqValue = 0;                                                             // 清除中断值

    if(PIN_getInputValue(SI522_IRQ_IO) == SI522_IRQ_ON)                         // 判断卡片是否接近
    {
        s_irqValue |= SI522_IRQ_VALUE;                                          // 保存中断值
//        BeepSeriesOfShortSounds();
    }

    Util_startClock(&s_irqDebounceTimer);                                       // 启动中断消抖的定时器
}

/**
 @brief IRQ中断消抖定时器的回调函数
 @param 无
 @return 无
*/
static void timer_irqDebounceCallback(UArg arg)
{
    if(s_appIrqChangeHandler != NULL)                                           // 判断是否有注册
    {
        if(PIN_getInputValue(SI522_IRQ_IO) == SI522_IRQ_ON)                     // 消抖
        {
            s_irqValue |= SI522_IRQ_VALUE;
        }

        (*s_appIrqChangeHandler)(s_irqValue);                                   // 调用中断应用层处理函数
    }
}

/*************************************END OF FILE*************************************/

4.2 board_si522.h

#ifndef _BOARD_SI522_H_
#define _BOARD_SI522_H_

/*********************************************************************
 * INCLUDES
 */
#include "_hal_types.h"
#include <ti/drivers/pin/PINCC26XX.h>

/*********************************************************************
 * DEFINITIONS
 */
#define SI522_RST_ON                0
#define SI522_RST_OFF               1

#define SI522_IRQ_ON                1
#define SI522_IRQ_OFF               0
#define SI522_IRQ_VALUE             0x0001                  // 中断值

#define SI522_RST_IO                IOID_1
#define SI522_IRQ_IO                IOID_5

#define IRQ_DEBOUNCE_TIMEOUT        20                      // 超时时间

#define SI522_RST_HIGH              Board_Si522RstPinSet(SI522_RST_IO, SI522_RST_OFF)
#define SI522_RST_LOW               Board_Si522RstPinSet(SI522_RST_IO, SI522_RST_ON)

/*********************************************************************
 * TYPEDEFS
 */
typedef void (*IrqCallbackFunc_t)(uint8 irqValue);

/*********************************************************************
 * API FUNCTIONS
 */
void Board_Si522RstPinInit(void);
void Board_Si522RstPinSet(PIN_Id pinId, bool pinState);
void Board_Si522IrqInit(IrqCallbackFunc_t irqAppCallback);

#endif /* _BOARD_SI522_H_ */

4.3 user_si522.c

/*********************************************************************
 * INCLUDES
 */
#include "_hal_types.h"
#include <string.h>
#include <ti/sysbios/knl/Clock.h>
#include <ti/drivers/pin/PINCC26XX.h>
#include "util.h"

#include "Board/board_spi.h"
#include "Board/board_si522.h"
#include "user_beep.h"
#include "user_lock.h"
#include "user_config.h"
#include "user_si522.h"

#include "Board/board_uart.h"

static void pcdInit(void);
static void pcdSi522ACDInit(void);
static char pcdRequest(uint8 reqCode, uint8 *pTagType);
static char pcdAnticoll(uint8 *pSnr);
static char pcdSelect(uint8 *pSnr);
static char pcdAuthState(uint8 authMode, uint8 blockAddr, uint8 *pKey, uint8 *pSnr);
static char pcdRead(uint8 blockAddr, uint8 *pReadData);
static char pcdWrite(uint8 blockAddr, uint8 *pWriteData);
static void pcdCalulateCRC(uint8 *pInData, uint8 len, uint8 *pOutData);
static char pcdComSi522(uint8 command, uint8 *pInData, uint8 inLenByte, uint8 *pOutData, uint32_t *pOutLenBit);
static void pcdAntennaOn(void);
static void pcdAntennaOff(void);
static void setRegisterBitMask(uint8 registerAddr, uint8 mask);
static void clearRegisterBitMask(uint8 registerAddr, uint8 mask);
static uint8 readRegister(uint8 registerAddr);
static void writeRegister(uint8 registerAddr, uint8 writeData);
static void delayMs(uint8 time);

/*********************************************************************
 * LOCAL VARIABLES
 */
static uint8 s_cardType[2];                                                     // 卡类型
static uint8 s_cardSerialNo[4];                                                 // 卡序列号

static uint8 s_defaultKeyA[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};           // 默认密码A
static uint8 s_defaultKeyB[6] = {0x5A, 0x89, 0xE5, 0x4B, 0x71, 0x32};           // 默认密码B
static uint8 s_userInfo[32];                                                    // 存放用户数据,16字节数据

/*********************************************************************
 * PUBLIC FUNCTIONS
 */

/**
 @brief SI522的初始化函数
 @param 无
 @return 无
*/
void SI522_Init(void)
{
    SPI_Init();                                                                 // SPI初始化
    Board_Si522RstPinInit();                                                    // 复位引脚初始化
    pcdInit();                                                                  // PCD寻卡模式初始化

    delayMs(5);
//    pcdAntennaOn();                                                             // 开启天线发射

    pcdSi522ACDInit();                                                          // 进入ACD寻卡模式
}

/**
 @brief SI522读取卡片序列号
 @param pCardSerialNo -[out] 卡片序列号
 @return 0 - 读卡成功;2 - 无卡
*/
uint8 SI522_ReadCardSerialNo(uint8 *pCardSerialNo)
{
    uint8 status = SI522_ReadCardDataBlock(4);
    memcpy(pCardSerialNo, s_cardSerialNo, 4);
    return status;
}

/**
 @brief SI522读取卡片固定块4的姓名
 @param pName -[out] 固定块4的姓名
 @return 无
*/
void SI522_ReadName(uint8 *pName)
{
    uint8 readFlag;
    readFlag = SI522_ReadCardDataBlock(4);

    if(readFlag == 0)                                                           // 密码成功,读取姓名
    {
        uint8 i = 0;
        if(pcdRead(4, s_userInfo) == MI_OK)                                     // 读取成功
        {                                                                       // s_userInfo[0] - 姓名字节数,一个汉字=2字节,最多14字节
            for(i = 1; 1 <= s_userInfo[0]; i++)                                 // s_userInfo[1]~s_userInfo[14] - 姓名
            {                                                                   // s_userInfo[5] - 校验,暂时不用
                pName[i - 1] = s_userInfo[i];
            }

            pName[i] = 0x00;

            if(pcdRead(6, &pName[16]) == MI_OK)                                 // 读取块6 场馆名称,有效时间
            {
                i = i + 100;
            }
            else
            {
                i = 7;
            }
        }
        else
        {
            i = 6;
        }
    }
}

/**
 @brief SI522读取卡片块数据
 @param blockAddr -[in] 块地址
 @return 状态值,0 - 成功;2 - 无卡;3 - 防冲撞失败;4 - 选卡失败;5 - 密码错误
*/
uint8 SI522_ReadCardDataBlock(uint8 blockAddr)
{
    clearRegisterBitMask(0x01, 0x20);                                           // Turn on the analog part of receiver
    pcdInit();                                                                  // PCD寻卡模式初始化

    memset(s_cardSerialNo, 0, 4);

    if(pcdRequest(PICC_REQALL, s_cardType) == MI_OK)
    {
    }
    else
    {
        pcdSi522ACDInit();
        return 2;                                                               // 无卡
    }

    if(pcdAnticoll(s_cardSerialNo) == MI_OK)
    {
    }
    else
    {
        return 3;                                                               // 防冲撞失败
    }

    if(pcdSelect(s_cardSerialNo) == MI_OK)
    {
    }
    else
    {
        return 4;                                                               // 选卡失败
    }

    if(pcdAuthState(0x60, blockAddr, s_defaultKeyA, s_cardSerialNo) == MI_OK)
    {
        pcdSi522ACDInit();
        return 0;
    }
    else
    {
        pcdSi522ACDInit();
        return 5;                                                               // 密码错误
    }
}

/**
 @brief 检查RFID卡
 @param 无
 @return 无
*/
void SI522_CheckRfidCard(void)
{
    uint8 cardSerialNumber[4];
    uint8 status = SI522_ReadCardSerialNo(cardSerialNumber);

    if(status == 2)                                                             // 无卡
    {
        return ;
    }

    if(VerifyAdministratorCard(cardSerialNumber))                               // 验证管理员卡
    {
        if(GetLockCooldownFlag() == LOCK_UNCOOLED)                              // 未在冷却中
        {
            uint8 switchDelayTime = GetSwitchDelayTime();
            if(switchDelayTime == 0)
            {
                switchDelayTime = 3;
            }
            OpenLockAndDelayClose(switchDelayTime * 1000);                      // 开锁并延迟关闭
            SetLockCooldownFlag(LOCK_COOLING);                                  // 进入冷却
        }
    }
    else if(VerifyMembershipCard(cardSerialNumber))                             // 验证会员卡
    {
        if(GetLockCooldownFlag() == LOCK_UNCOOLED)
        {
            uint8 switchDelayTime = GetSwitchDelayTime();
            if(switchDelayTime == 0)
            {
                switchDelayTime = 3;
            }
            OpenLockAndDelayClose(switchDelayTime * 1000);
            SetLockCooldownFlag(LOCK_COOLING);
        }
    }
    else                                                                        // 无效卡
    {
        BeepSeriesOfShortSounds();                                              // 蜂鸣器连串短叫
    }
}


/*********************************************************************
 * LOCAL FUNCTIONS
 */

/**
 @brief SI522芯片初始化
 @param 无
 @return 无
*/
static void pcdInit(void)
{
    // RST复位脚需先保持高电平,后给个下降沿
    SI522_RST_LOW;
    delayMs(5);
    SI522_RST_HIGH;
    delayMs(10);

    writeRegister(CommandReg, PCD_RESETPHASE);                                  // Perform soft reset
    delayMs(1);
    writeRegister(TxModeReg, 0x00);                                             // Reset baud rates
    writeRegister(RxModeReg, 0x00);                                             // Reset baud rates
    writeRegister(ModWidthReg, 0x26);                                           // Reset ModWidthReg
    writeRegister(RFCfgReg, 0x68);                                              // RxGain:110,43dB by default
//    writeRegister(TModeReg, 0x8D);
    writeRegister(TModeReg, 0x80);                                              // TAuto=1; timer starts automatically at the end of the transmission in all communication modes at all speeds
//    writeRegister(TPrescalerReg, 0x3E);
    writeRegister(TPrescalerReg, 0xa9);                                         // TPreScaler = TModeReg[3..0]:TPrescalerReg
//    writeRegister(TReloadRegL, 30);
//    writeRegister(TReloadRegH, 0);
    writeRegister(TReloadRegH, 0x03);                                           // Reload timer
    writeRegister(TReloadRegL, 0xe8);                                           // Reload timer
    writeRegister(TxASKReg, 0x40);
    writeRegister(ModeReg, 0x3D);
    writeRegister(CommandReg, PCD_IDLE);                                        // Turn on the analog part of receiver
    pcdAntennaOn();                                                             // Enable the antenna driver pins TX1 and TX2 (they were disabled by the reset)
}

/**
 @brief 配置SI522芯片ACD模式,双边沿触发
 @param 无
 @return 无
*/
static void pcdSi522ACDInit(void)
{
    writeRegister(ACDConfigSelReg, 0x00);                                       // Access register ACDConfigA
    writeRegister(ACDConfigReg, 0x02);                                          // Set interval:xx ms
    writeRegister(ACDConfigSelReg, 0x01);                                       // Access register ACDConfigB
    writeRegister(ACDConfigReg, 0x04);                                          // Set ACD mode:edge trigger,ACDConfigB
    writeRegister(ACDConfigSelReg, 0x03);                                       // Access register ACDConfigD
    writeRegister(ACDConfigReg, 0x03);                                          // Set delta:x
    writeRegister(ACDConfigSelReg, 0x04);                                       // Access register ACDConfigE
    writeRegister(ACDConfigReg, 0x07);                                          // Set ACDConfigE
    writeRegister(ComIEnReg, 0x00);                                             // Reset ComIEnReg.IRqInv bit
    writeRegister(DivIEnReg, 0xc0);                                             // Enable ACDIRq,set pin IRQ is a standard CMOS output pin
    writeRegister(CommandReg, 0xB0);                                            // Enable and enter ACD mode
}

/**
 @brief 寻卡
 @param reqCode -[in] 寻卡方式,0x52 寻感应区内所有符合1443A标准的卡,0x26 寻未进入休眠状态的卡
 @param pTagType -[out] 卡片类型代码
                        0x4400 = Mifare_UltraLight
                        0x0400 = Mifare_One(S50)
                        0x0200 = Mifare_One(S70)
                        0x0800 = Mifare_Pro(X)
                        0x4403 = Mifare_DESFire
 @return 状态值,MI OK - 成功;MI_ERR - 失败
*/
static char pcdRequest(uint8 reqCode, uint8 *pTagType)
{
    char status;
    uint32_t len;
    uint8 comSi522Buf[MAXRLEN];

    clearRegisterBitMask(Status2Reg, 0x08);
    writeRegister(BitFramingReg, 0x07);
    setRegisterBitMask(TxControlReg, 0x03);

    comSi522Buf[0] = reqCode;

    status = pcdComSi522(PCD_TRANSCEIVE, comSi522Buf, 1, comSi522Buf, &len);    // 发送并接收数据
    if((status == MI_OK) && (len == 0x10))
    {
        *pTagType = comSi522Buf[0];
        *(pTagType+1) = comSi522Buf[1];
    }
    else
    {
        status = MI_ERR;
    }

    return status;
}

/**
 @brief 防冲撞
 @param pSnr -[out] 卡片序列号,4字节
 @return 状态值,MI OK - 成功;MI_ERR - 失败
*/
static char pcdAnticoll(uint8 *pSnr)
{
    char status;
    uint8 i, snrCheck = 0;
    uint32_t len;
    uint8 comSi522Buf[MAXRLEN];

    clearRegisterBitMask(Status2Reg, 0x08);                                     // 寄存器包含接收器和发送器和数据模式检测器的状态标志
    writeRegister(BitFramingReg, 0x00);                                         // 不启动数据发送,接收的LSB位存放在位0,接收到的第二位放在位1,定义发送的最后一个字节位数为8
    clearRegisterBitMask(CollReg, 0x80);                                        // 所有接收的位在冲突后将被清除

    comSi522Buf[0] = PICC_ANTICOLL1;
    comSi522Buf[1] = 0x20;

    status = pcdComSi522(PCD_TRANSCEIVE, comSi522Buf, 2, comSi522Buf, &len);

    if(status == MI_OK)
    {
        for(i = 0; i < 4; i++)
        {
            *(pSnr + i) = comSi522Buf[i];
            snrCheck ^= comSi522Buf[i];
        }
        if(snrCheck != comSi522Buf[i])                                          // 返回四个字节,最后一个字节为校验位
        {
            status = MI_ERR;
        }
    }

    setRegisterBitMask(CollReg, 0x80);

    return status;
}

/**
 @brief 选定卡片
 @param pSnr -[in] 卡片序列号,4字节
 @return 状态值,MI OK - 成功;MI_ERR - 失败
*/
static char pcdSelect(uint8 *pSnr)
{
    char status;
    uint8 i;
    uint8 comSi522Buf[MAXRLEN];
    uint32_t len;

    comSi522Buf[0] = PICC_ANTICOLL1;
    comSi522Buf[1] = 0x70;
    comSi522Buf[6] = 0;

    for(i = 0; i < 4; i++)
    {
        comSi522Buf[i + 2] = *(pSnr + i);
        comSi522Buf[6] ^= *(pSnr + i);
    }

    pcdCalulateCRC(comSi522Buf, 7, &comSi522Buf[7]);

    clearRegisterBitMask(Status2Reg, 0x08);

    status = pcdComSi522(PCD_TRANSCEIVE, comSi522Buf, 9, comSi522Buf, &len);

    if((status == MI_OK ) && (len == 0x18))
    {
        status = MI_OK;
    }
    else
    {
        status = MI_ERR;
    }

    return status;
}

/**
 @brief 验证卡片密码
 @param authMode -[in] 密码验证模式,0x60 验证A密钥,0x61 验证B密钥
 @param blockAddr -[in] 块地址
 @param pKey -[in] 密码
 @param pSnr -[in] 卡片序列号,4字节
 @return 状态值,MI OK - 成功;MI_ERR - 失败
*/
static char pcdAuthState(uint8 authMode, uint8 blockAddr, uint8 *pKey, uint8 *pSnr)
{
    char status;
    uint8 i, comSi522Buf[MAXRLEN];
    uint32_t len;

    comSi522Buf[0] = authMode;
    comSi522Buf[1] = blockAddr;

    for(i = 0; i < 6; i++)
    {
        comSi522Buf[i + 2] = *(pKey + i);
    }

    for(i = 0; i < 6; i++)
    {
        comSi522Buf[i + 8] = *(pSnr + i);
    }

    status = pcdComSi522(PCD_AUTHENT, comSi522Buf, 12, comSi522Buf, &len);

    if((status != MI_OK ) || ( ! (readRegister(Status2Reg) & 0x08)))
    {
        status = MI_ERR;
    }

    return status;
}

/**
 @brief 读取M1卡一块数据
 @param blockAddr -[in] 块地址
 @param pReadData -[out] 读出的数据,16字节
 @return 状态值,MI OK - 成功;MI_ERR - 失败
*/
static char pcdRead(uint8 blockAddr, uint8 *pReadData)
{
    char status;
    uint8 i, comSi522Buf[MAXRLEN];
    uint32_t len;

    comSi522Buf[0] = PICC_READ;
    comSi522Buf[1] = blockAddr;

    pcdCalulateCRC(comSi522Buf, 2, &comSi522Buf[2]);

    status = pcdComSi522(PCD_TRANSCEIVE, comSi522Buf, 4, comSi522Buf, &len);

    if((status == MI_OK) && (len == 0x90))
    {
        for(i = 0; i < 16; i++)
        {
            *(pReadData + i) = comSi522Buf[i];
        }
    }
    else
    {
        status = MI_ERR;
    }

    return status;
}

/**
 @brief 写入M1卡一块数据
 @param blockAddr -[in] 块地址
 @param pWriteData -[out] 写入的数据,16字节
 @return 状态值,MI OK - 成功;MI_ERR - 失败
*/
static char pcdWrite(uint8 blockAddr, uint8 *pWriteData)
{
    char status;
    uint8 i, comSi522Buf[MAXRLEN];
    uint32_t len;

    comSi522Buf[0] = PICC_WRITE;
    comSi522Buf[1] = blockAddr;

    pcdCalulateCRC(comSi522Buf, 2, &comSi522Buf[2]);

    status = pcdComSi522(PCD_TRANSCEIVE, comSi522Buf, 4, comSi522Buf, &len);
    if((status != MI_OK) || (len != 4) || ((comSi522Buf[0] & 0x0F) != 0x0A))
    {
        status = MI_ERR;
    }

    if(status == MI_OK)
    {
        for(i = 0; i < 16; i++)
        {
            comSi522Buf[i] = *(pWriteData + i);
        }
        pcdCalulateCRC(comSi522Buf, 16, &comSi522Buf[16]);

        status = pcdComSi522(PCD_TRANSCEIVE, comSi522Buf, 18, comSi522Buf, &len);
        if((status != MI_OK) || (len != 4) || ((comSi522Buf[0] & 0x0F) != 0x0A))
        {
            status = MI_ERR;
        }
    }

    return status;
}

/**
 @brief 计算CRC16
 @param pInData -[in] 计算CRC16的数组
 @param len -[in] 计算CRC16的数组字节长度
 @param pOutData -[out] 存放计算结果存放的首地址
 @return 无
*/
static void pcdCalulateCRC(uint8 *pInData, uint8 len, uint8 *pOutData)
{
    uint8 i, n;

    clearRegisterBitMask(DivIrqReg, 0x04);
    writeRegister(CommandReg, PCD_IDLE);
    setRegisterBitMask(FIFOLevelReg, 0x80);

    for(i = 0; i < len; i++)
    {
        writeRegister(FIFODataReg, *(pInData + i));
    }

    writeRegister(CommandReg, PCD_CALCCRC);

    i = 0xFF;

    do
    {
        n = readRegister(DivIrqReg);
        i--;
    }
    while((i != 0) && ! (n & 0x04));

    pOutData[0] = readRegister(CRCResultRegL);
    pOutData[1] = readRegister(CRCResultRegH);
}

/**
 @brief 通过SI522和ISO14443卡通讯
 @param command -[in] SI522命令字
 @param pInData -[in] 通过SI522发送到卡片的数据
 @param inLenByte -[in] 发送数据的字节长度
 @param pOutData -[out] 接收到的卡片返回数据
 @param pOutLenBit -[out] 返回数据的位长度
 @return 状态值,MI OK - 成功;MI_ERR - 失败
*/
static char pcdComSi522(uint8 command, uint8 *pInData, uint8 inLenByte, uint8 *pOutData, uint32_t *pOutLenBit)
{
    char status = MI_ERR;
    uint8 irqEn = 0x00;
    uint8 waitFor = 0x00;
    uint8 lastBits;
    uint8 n;
    uint32_t i;
    uint8 j;

    switch(command)
    {
    case PCD_AUTHENT:
        irqEn = 0x12;
        waitFor = 0x10;
        break;
    case PCD_TRANSCEIVE:
        irqEn = 0x77;
        waitFor = 0x30;
        break;
    default:
        break;
    }

    writeRegister(ComIEnReg, irqEn | 0x80);
    clearRegisterBitMask(ComIrqReg, 0x80);
    writeRegister(CommandReg, PCD_IDLE);
    setRegisterBitMask(FIFOLevelReg, 0x80);                                     // 清空FIFO

    for(i = 0; i < inLenByte; i++)
    {
        writeRegister(FIFODataReg, pInData[i]);                                 // 数据写入FIFO
    }
    writeRegister(CommandReg, command);                                         // 命令写入命令寄存器

    if(command == PCD_TRANSCEIVE)
    {
        setRegisterBitMask(BitFramingReg, 0x80);                                // 开始发送
    }

    i = 6000;                                                                   // 根据时钟频率调整,操作M1卡最大等待时间25ms
    do
    {
        n = readRegister(ComIrqReg);
        i--;
    }
    while((i != 0) && !(n & 0x01) && !(n & waitFor));
    clearRegisterBitMask(BitFramingReg, 0x80);

    if(i != 0)
    {
        j = readRegister(ErrorReg);
        if(!(j & 0x1B))
        {
            status = MI_OK;
            if(n & irqEn & 0x01)
            {
                status = MI_NOTAGERR;
            }
            if(command == PCD_TRANSCEIVE)
            {
                n = readRegister(FIFOLevelReg);
                lastBits = readRegister(ControlReg) & 0x07;
                if(lastBits)
                {
                    *pOutLenBit = (n - 1) * 8 + lastBits;
                }
                else
                {
                    *pOutLenBit = n * 8;
                }
                if(n == 0)
                {
                    n = 1;
                }
                if(n > MAXRLEN)
                {
                    n = MAXRLEN;
                }
                for(i = 0; i < n; i++)
                {
                    pOutData[i] = readRegister(FIFODataReg);
                }
            }
        }
        else
        {
          status = MI_ERR;
        }
    }

    setRegisterBitMask(ControlReg, 0x80);                                       // stop timer now
    writeRegister(CommandReg, PCD_IDLE);

    return status;
}

/**
 @brief 开启天线【每次启动或关闭天线发射之间至少有1ms的间隔】
 @param 无
 @return 无
*/
static void pcdAntennaOn(void)
{
    uint8 temp;
    temp = readRegister(TxControlReg);
    if(!(temp & 0x03))
    {
        setRegisterBitMask(TxControlReg, 0x03);
    }
}

/**
 @brief 关闭天线
 @return 无
*/
static void pcdAntennaOff(void)
{
    clearRegisterBitMask(TxControlReg, 0x03);
}

/**
 @brief 置SI522寄存器位
 @param registerAddr -[in] 寄存器地址
 @param mask -[in] 置位值
 @return 无
*/
static void setRegisterBitMask(uint8 registerAddr, uint8 mask)
{
    char temp = 0x00;
    temp = readRegister(registerAddr) | mask;
    writeRegister(registerAddr, temp | mask);                                   // set bit mask
}

/**
 @brief 清SI522寄存器位
 @param registerAddr -[in] 寄存器地址
 @param mask -[in] 清位值
 @return 无
*/
static void clearRegisterBitMask(uint8 registerAddr, uint8 mask)
{
    char temp = 0x00;
    temp = readRegister(registerAddr) & (~mask);
    writeRegister(registerAddr, temp);                                          // clear bit mask
}

/**
 @brief 写SI522寄存器
 @param registerAddr -[in] 寄存器地址
 @param writeData -[in] 写入数据
 @return 无
*/
static void writeRegister(uint8 registerAddr, uint8 writeData)
{
    SPI_CS_LOW;

    registerAddr <<= 1;
    registerAddr &= 0x7e;

    SPI_ReadWriteData(&registerAddr, NULL, sizeof(uint8));
    SPI_ReadWriteData(&writeData, NULL, sizeof(uint8));

    SPI_CS_HIGH;
}

/**
 @brief 读SI522寄存器
 @param registerAddr -[in] 寄存器地址
 @return 读出一字节数据
*/
static uint8 readRegister(uint8 registerAddr)
{
    uint8 writeData = 0xff;
    uint8 readData;

    SPI_CS_LOW;

    registerAddr <<= 1;
    registerAddr |= 0x80;

    SPI_ReadWriteData(&registerAddr, NULL, sizeof(uint8));
    SPI_ReadWriteData(&writeData, &readData, sizeof(uint8));

    SPI_CS_HIGH;

    return readData;
}

/**
 @brief 毫秒级延时函数
 @param time -[in] 延时时间(毫秒)
 @return 无
*/
static void delayMs(uint8 time)
{
    CPUdelay(12000 * time);
}

4.4 user_si522.h

#ifndef _USER_SI522_H_
#define _USER_SI522_H_

/*********************************************************************
 * INCLUDES
 */
#include "_hal_types.h"

/*********************************************************************
 * DEFINITIONS
 */
#define MAXRLEN                   18
#define MIN_STRENGTH              228
#define DEF_FIFO_LENGTH           64             // FIFO size = 64byte;

//******************************************************************/
//                       SI522命令字                                                                 /
//******************************************************************/
#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           // 休眠

//******************************************************************/
//                        SI522寄存器定义                                                        /
//******************************************************************/
// Si522 registers. Described in chapter 9 of the datasheet.
// When using SPI all addresses are shifted one bit left in the "SPI address unsigned char" (section 8.1.2.3)
// Page 0: Command and status
#define     RFU00                     0x00      // reserved for future use
#define     CommandReg                0x01      // starts and stops command execution
#define     ComIEnReg                 0x02      // enable and disable interrupt request control bits
#define     DivIEnReg                 0x03      // enable and disable interrupt request control bits
#define     ComIrqReg                 0x04      // interrupt request bits
#define     DivIrqReg                 0x05      // interrupt request bits
#define     ErrorReg                  0x06      // error bits showing the error status of the last command executed
#define     Status1Reg                0x07      // communication status bits
#define     Status2Reg                0x08      // receiver and transmitter status bits
#define     FIFODataReg               0x09      // input and output of 64 unsigned char FIFO buffer
#define     FIFOLevelReg              0x0A      // number of unsigned chars stored in the FIFO buffer
#define     WaterLevelReg             0x0B      // level for FIFO underflow and overflow warning
#define     ControlReg                0x0C      // miscellaneous control registers
#define     BitFramingReg             0x0D      // adjustments for bit-oriented frames
#define     CollReg                   0x0E      // bit position of the first bit-collision detected on the RF interface
#define     ACDConfigReg              0x0F      // configuration of ACD mode,alternate registers selected by ACDConfigSelReg
// Page 1: Command
#define     RFU10                     0x10      // reserved for future use
#define     ModeReg                   0x11      // defines general modes for transmitting and receiving
#define     TxModeReg                 0x12      // defines transmission data rate and framing
#define     RxModeReg                 0x13      // defines reception data rate and framing
#define     TxControlReg              0x14      // controls the logical behavior of the antenna driver pins TX1 and TX2
#define     TxASKReg                  0x15      // controls the setting of the transmission modulation
#define     TxSelReg                  0x16      // selects the internal sources for the antenna driver
#define     RxSelReg                  0x17      // selects internal receiver settings
#define     RxThresholdReg            0x18      // selects thresholds for the bit decoder
#define     DemodReg                  0x19      // defines demodulator settings
#define     RFU1A                     0x1A      // reserved for future use
#define     RFU1B                     0x1B      // reserved for future use
#define     MfTxReg                   0x1C      // controls some MIFARE communication transmit parameters
#define     MfRxReg                   0x1D      // controls some MIFARE communication receive parameters
#define     RFU1E                     0x1E      // reserved for future use
#define     SerialSpeedReg            0x1F      // selects the speed of the serial UART interface
// Page 2: Configuration
#define     ACDConfigSelReg           0x20      // used to select ACDConfigReg A-G
#define     CRCResultRegH             0x21      // shows the MSB and LSB values of the CRC calculation
#define     CRCResultRegL             0x22
#define     RFU23                     0x23      // reserved for future use
#define     ModWidthReg               0x24      // controls the ModWidth setting
#define     RFU25                     0x25      // reserved for future use
#define     RFCfgReg                  0x26      // configures the receiver gain
#define     GsNReg                    0x27      // selects the conductance of the antenna driver pins TX1 and TX2 for modulation
#define     CWGsPReg                  0x28      // defines the conductance of the p-driver output during periods of no modulation
#define     ModGsPReg                 0x29      // defines the conductance of the p-driver output during periods of modulation
#define     TModeReg                  0x2A      // defines settings for the internal timer
#define     TPrescalerReg             0x2B      // the lower 8 bits of the TPrescaler value. The 4 high bits are in TModeReg.
#define     TReloadRegH               0x2C      // defines the 16-bit timer reload value
#define     TReloadRegL               0x2D
#define     TCounterValueRegH         0x2E      // shows the 16-bit timer value
#define     TCounterValueRegL         0x2F
// Page 3: Test Register
#define     RFU30                     0x30      // reserved for future use
#define     TestSel1Reg               0x31      // general test signal configuration
#define     TestSel2Reg               0x32      // general test signal configuration
#define     TestPinEnReg              0x33      // enables pin output driver on pins D1 to D7
#define     TestPinValueReg           0x34      // defines the values for D1 to D7 when it is used as an I/O bus
#define     TestBusReg                0x35      // shows the status of the internal test bus
#define     AutoTestReg               0x36      // controls the digital self-test
#define     VersionReg                0x37      // shows the software version
#define     AnalogTestReg             0x38      // controls the pins AUX1 and AUX2
#define     TestDAC1Reg               0x39      // defines the test value for TestDAC1
#define     TestDAC2Reg               0x3A      // defines the test value for TestDAC2
#define     TestADCReg                0x3B      // shows the value of ADC I and Q channels
#define     RFU3C                     0x3C      // reserved for future use
#define     RFU3D                     0x3D      // reserved for future use
#define     RFU3E                     0x3E      // reserved for future use
#define     RFU3F                     0x3F      // reserved for future use

//******************************************************************/
//                    SI522通讯返回错误代码                                                      /
//******************************************************************/
#define MI_ERR                      0xFE
//#define MI_ERR                         //(-2)

// Mifare Error Codes
// Each function returns a status value, which corresponds to the
// mifare error codes.

#define MI_OK                          0
#define MI_CHK_OK                      0
#define MI_CRC_ZERO                    0

#define MI_CRC_NOTZERO                 1

#define MI_NOTAGERR                 0xFF
#define MI_CHK_FAILED               0xFF
#define MI_CRCERR                   0xFE
#define MI_CHK_COMPERR              0xFE
#define MI_EMPTY                    0xFD
#define MI_AUTHERR                  0xFC
#define MI_PARITYERR                0xFB
#define MI_CODEERR                  0xFA

#define MI_SERNRERR                 0xF8
#define MI_KEYERR                   0xF7
#define MI_NOTAUTHERR               0xF6
#define MI_BITCOUNTERR              0xF5
#define MI_BYTECOUNTERR             0xF4
#define MI_IDLE                     0xF3
#define MI_TRANSERR                 0xF2
#define MI_WRITEERR                 0xF1
#define MI_INCRERR                  0xF0
#define MI_DECRERR                  0xEF
#define MI_READERR                  0xEE
#define MI_OVFLERR                  0xED
#define MI_POLLING                  0xEC
#define MI_FRAMINGERR               0xEB
#define MI_ACCESSERR                0xEA
#define MI_UNKNOWN_COMMAND          0xE9
#define MI_COLLERR                  0xE8
#define MI_RESETERR                 0xE7
#define MI_INITERR                  0xE7
#define MI_INTERFACEERR             0xE7
#define MI_ACCESSTIMEOUT            0xE5
#define MI_NOBITWISEANTICOLL        0xE4
#define MI_QUIT                     0xE2

#define MI_RECBUF_OVERFLOW          0xCF
#define MI_SENDBYTENR               0xCE

#define MI_SENDBUF_OVERFLOW         0xCC
#define MI_BAUDRATE_NOT_SUPPORTED   0xCB
#define MI_SAME_BAUDRATE_REQUIRED   0xCA

#define MI_WRONG_PARAMETER_VALUE    0xC5

#define MI_BREAK                    0x9E
#define MI_NY_IMPLEMENTED           0x9D
#define MI_NO_MFRC                  0x9C
#define MI_MFRC_NOTAUTH             0x9B
#define MI_WRONG_DES_MODE           0x9A
#define MI_HOST_AUTH_FAILED         0x99

#define MI_WRONG_LOAD_MODE          0x97
#define MI_WRONG_DESKEY             0x96
#define MI_MKLOAD_FAILED            0x95
#define MI_FIFOERR                  0x94
#define MI_WRONG_ADDR               0x93
#define MI_DESKEYLOAD_FAILED        0x92

#define MI_WRONG_SEL_CNT            0x8F
#define MI_RC531_WRONG_READVALUE    0x8E // LI ADDED 09-4-24
#define MI_WRONG_TEST_MODE          0x8C
#define MI_TEST_FAILED              0x8B
#define MI_TOC_ERROR                0x8A
#define MI_COMM_ABORT               0x89
#define MI_INVALID_BASE             0x88
#define MI_MFRC_RESET               0x87
#define MI_WRONG_VALUE              0x86
#define MI_VALERR                   0x85

/*********************************************************************
 * API FUNCTIONS
 */
void SI522_Init(void);
uint8 SI522_ReadCardSerialNo(uint8 *pCardSerialNo);
void SI522_ReadName(uint8 *pName);
uint8 SI522_ReadCardDataBlock(uint8 blockAddr);
void SI522_CheckRfidCard(void);

#endif /* _USER_SI522_H_ */

五、API调用

需包含头文件 board_si522.huser_si522.h

Board_Si522IrqInit

功能 SI522的中断引脚初始化函数
函数定义 void Board_Si522IrqInit(IrqCallbackFunc_t irqAppCallback)
参数 irqAppCallback:中断应用层回调函数
返回

SI522_Init

功能 SI522初始化函数
函数定义 void SI522_Init(void)
参数
返回

SI522_ReadCardDataBlock

功能 SI522读取卡片块数据
函数定义 uint8_t SI522_ReadCardDataBlock(uint8_t blockAddr)
参数 blockAddr:块地址
返回 状态值,0 - 成功;2 - 无卡;3 - 防冲撞失败;4 - 选卡失败;5 - 密码错误

六、使用例子

1)添加头文件(例multi_role.c中)

#include "board_si522.h"
#include "user_si522.h"

2)添加初始化代码(multi_role.c的multi_role_init函数末尾中)

// SI522中断引脚初始化
Board_Si522IrqInit(si522IrqChangeHandler);
// SI522初始化
SI522_Init(); 

3)添加中断事件

#define SI522_IRQ_CHANGE_EVT                    11

static void multi_role_processAppMsg(spEvt_t *pMsg)
{
    ···
    case SI522_IRQ_CHANGE_EVT:
        si522HandleIrq(*(uint8_t*)(pMsg->pData));
        break;
    ···
}

// **************************************************************************************
//                                      SI522中断事件
// **************************************************************************************
/**
 @brief SI522中断的应用层处理函数
 @param irqValue -[in] 中断值
 @return 无
*/
static void si522IrqChangeHandler(uint8 irqValue)
{
    uint8_t *pValue = ICall_malloc(sizeof(uint8_t));

    if(pValue)
    {
        *pValue = irqValue;

        if(multi_role_enqueueMsg(SI522_IRQ_CHANGE_EVT, pValue) != SUCCESS)
        {
            ICall_free(pValue);
        }
    }
}

4)产生中断时读取数据块4,然后再初始化PCD和ACD模式

/**
 @brief SI522中断IRQ层处理函数
 @param irqValue -[in] 中断值
 @return 无
*/
static void si522HandleIrq(uint8 irqValue)
{
    if(irqValue & SI522_IRQ_VALUE)
    {
        if(PIN_getInputValue(SI522_IRQ_IO) == SI522_IRQ_ON)             // 检查卡片是否仍然接近
        {
            SI522_ReadCardDataBlock(4);
        }
    }
}

• 由 Leung 写于 2019 年 10 月 25 日

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,098评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,213评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,960评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,519评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,512评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,533评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,914评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,804评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,563评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,644评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,350评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,933评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,908评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,146评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,847评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,361评论 2 342

推荐阅读更多精彩内容