NRF52832學習筆記(18)——RFID SI522使用

一、簡介

ACD(低功耗尋卡)只是一種模式,和 PCD(普通尋卡)是一樣的,在使用到 ACD 模式時,其實就是普通的讀寫模式和低功耗模式的切換,可以理解爲“ACD 函數”中嵌套了一個“PCD 函數”,達到觸發條件後進入“PCD 函 數”,執行函數內容,最後回到“ACD 函數”,等待下一次的觸發。在使能 ACD 後,一旦檢測到場強的變弱,觸發中斷,就認爲有卡進入,此時清除中斷後,需要再次初始化 ACD,防止多次進中斷,從而導致讀卡不穩定。

SI522 ACD模式與讀寫模式狀態切換

二、硬件連接

功能口 引腳
MISO 17
MOSI 18
CLK 20
CSN 19
RST 13
IRQ 22

三、添加SPI驅動

查看 NRF52832學習筆記(5)——SPI接口使用
不勾選EasyDMA

四、移植文件

注意:以下出現缺失common.h文件錯誤,去除即可。uint8改爲uint8_t或unsigned char或自己宏定義
鏈接:https://pan.baidu.com/s/1hDFeaS8s-HRk4m2gWNkz8g 提取碼:wlqd
board_si522.cboard_si522.h 兩個文件加入工程的Application文件夾下

4.1 board_si522.c

/*********************************************************************
 * INCLUDES
 */
#include <string.h>
#include "nrf_gpio.h"
#include "nrf_delay.h"
#include "nrf_drv_gpiote.h"
#include "app_timer.h" 

#include "board_si522.h"
#include "board_spi.h"

static void irqCallbackFunc(nrf_drv_gpiote_pin_t pin, nrf_gpiote_polarity_t action);
static void timer_irqDebounceCallback(void *arg);
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 void pcdCalulateCRC(uint8 *pInData, uint8 len, uint8 *pOutData);
static char pcdComSi522(uint8 command, uint8 *pInData, uint8 inLenByte, uint8 *pOutData, uint32 *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(uint32 time);

/*********************************************************************
 * LOCAL VARIABLES
 */
APP_TIMER_DEF(s_irqDebounceTimer);												// 中斷消抖的定時器
static uint8 s_irqValue;                                                        // 中斷值

static uint8 s_cardType[2];                                                     // 卡類型
static uint8 s_cardSerialNo[4];                                                 // 卡序列號
static uint8 s_defaultKeyA[6] = {0x7A, 0x6F, 0x8C, 0xB3, 0xD9, 0x4E};			// 默認密碼A
//static uint8 s_defaultKeyB[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};         // 默認密碼B

/*********************************************************************
 * EXTERN FUNCTIONS
 */
// 在主函數定義
extern void SI522_HandleIrq(uint8 irqValue);									

/*********************************************************************
 * PUBLIC FUNCTIONS
 */
/**
 @brief SI522的初始化函數
 @param 無
 @return 無
*/
void SI522_Init(void)
{
    SPI_Init();                                                                 // SPI初始化
    Board_Si522RstPinInit();                                                    // 復位引腳初始化
    SI522_PcdInit();                                                            // PCD尋卡模式初始化

    delayMs(5);

    SI522_EnableAcdMode();                                                    	// 進入ACD尋卡模式
}

/**
 @brief SI522的RST引腳初始化
 @param 無
 @return 無
*/
void Board_Si522RstPinInit(void)
{
	nrf_gpio_cfg_output(SI522_RST_IO);
	SI522_RST_HIGH;
}

/**
 @brief SI522的RST引腳設置
 @param pinState -[in] 引腳狀態
 @return 無
*/
void Board_Si522RstPinSet(bool pinState)
{
    if(pinState == SI522_RST_OFF)
    {
		nrf_gpio_pin_write(SI522_RST_IO, 1);
    }
    else if(pinState == SI522_RST_ON)
    {
		nrf_gpio_pin_write(SI522_RST_IO, 0);
    }
}

/**
 @brief SI522中斷初始化函數
 @param 無
 @return 無
*/
void Board_Si522IrqInit(void)
{
	//	ret_code_t errCode = nrf_drv_gpiote_init();								// GPIOE驅動初始化
	//	APP_ERROR_CHECK(errCode);
	// 需先調用nrf_drv_gpiote_init(),但如果有其他GPIOE中斷程序已調用,在這裏可以不調用
	ret_code_t errCode;
	nrf_drv_gpiote_in_config_t inConfig = GPIOTE_CONFIG_IN_SENSE_LOTOHI(false);	// 上升沿觸發
	inConfig.pull = GPIO_PIN_CNF_PULL_Pulldown;									// 默認下拉
//	inConfig.sense = NRF_GPIOTE_POLARITY_TOGGLE;								// 雙邊沿中斷觸發
	
	errCode = nrf_drv_gpiote_in_init(SI522_IRQ_IO, &inConfig, irqCallbackFunc);
	APP_ERROR_CHECK(errCode);
	nrf_drv_gpiote_in_event_enable(SI522_IRQ_IO, true);						

	app_timer_create(&s_irqDebounceTimer, APP_TIMER_MODE_SINGLE_SHOT, timer_irqDebounceCallback);
}

/**
 @brief SI522芯片初始化
 @param 無
 @return 無
*/
void SI522_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 無
*/
void SI522_EnableAcdMode(void)
{
	// ACD mode,edge trigger
    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
}

void SI522_EnableAcdTimerMode(void)
{
	// ACD mode,edge trigger,turn timer
    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(ACDConfigSelReg, 0x07);// Access register ACDConfigH
    writeRegister(ACDConfigReg, 0x0a);  // x cycles,time: x * interval ms
    writeRegister(ComIEnReg, 0x00);	// Reset ComIEnReg.IRqInv bit
    writeRegister(DivIEnReg, 0xe0); // Enable ACDIRq,set pin IRQ is a standard CMOS output pin
    writeRegister(CommandReg, 0xB0);			// Enable and enter ACD mode
}

/**
 @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
    SI522_PcdInit();                                                           	// PCD尋卡模式初始化

    memset(s_cardSerialNo, 0, sizeof(s_cardSerialNo));

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

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

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

    if(pcdAuthState(PICC_AUTHENT1A, blockAddr, s_defaultKeyA, s_cardSerialNo) == MI_OK)
    {
        return 0;
    }
    else
    {
        return 5;                                                               // 密碼錯誤
    }
}

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

/**
 @brief 讀取M1卡一塊數據
 @param blockAddr -[in] 塊地址
 @param pReadData -[out] 讀出的數據,16字節
 @return 狀態值,MI OK - 成功;MI_ERR - 失敗
*/
char SI522_PcdRead(uint8 blockAddr, uint8 *pReadData)
{
    char status;
    uint8 i, comSi522Buf[MAXRLEN];
    uint32 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 - 失敗
*/
char SI522_PcdWrite(uint8 blockAddr, uint8 *pWriteData)
{
    char status;
    uint8 i, comSi522Buf[MAXRLEN];
    uint32 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 ACD中斷
 @param 無
 @return 1 - 成功;0 - 失敗
*/
bool SI522_IsAcdInterrupt(void)
{
    uint8 status = readRegister(0x05);                                          // Get the Si522 interrupt flag
    if(status & ACD_IRQ)
    {
        return true;
    }
    else
    {
        return false;
    }
}

bool SI522_IsAcdTimerInterrupt(void)
{
    uint8 status = readRegister(0x05);                                          // Get the Si522 interrupt flag
    if(status & ACDTIMER_IRQ)
    {
        return true;
    }
    else
    {
        return false;
    }
}


/*********************************************************************
 * LOCAL FUNCTIONS
 */
/**
 @brief IRQ中斷回調函數
 @param pin -[in] 引腳
 @param action -[in] 動作
 @return 無
*/
static void irqCallbackFunc(nrf_drv_gpiote_pin_t pin, nrf_gpiote_polarity_t action)
{
    s_irqValue = 0;                                                             // 清除中斷值

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

    app_timer_start(s_irqDebounceTimer, IRQ_DEBOUNCE_TIMEOUT, NULL);			// 啓動中斷消抖的定時器
}

/**
 @brief IRQ中斷消抖定時器的回調函數
 @param 無
 @return 無
*/
static void timer_irqDebounceCallback(void *arg)
{
	UNUSED_PARAMETER(arg);
	
	// 消抖後
	if(nrf_gpio_pin_read(SI522_IRQ_IO) == SI522_IRQ_ON)
	{
		s_irqValue |= SI522_IRQ_VALUE;
	}
	
	SI522_HandleIrq(s_irqValue);
}

/**
 @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 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 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 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;
	uint8 comSi522Buf[MAXRLEN];
    uint32 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 計算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 *pOutLenBit)
{
    char status = MI_ERR;
    uint8 irqEn = 0x00;
    uint8 waitFor = 0x00;
    uint8 lastBits;
    uint8 n;
    uint32 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 無
*/
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 讀出一字節數據
*/
uint8 readRegister(uint8 registerAddr)
{
    uint8 readData;

    SPI_CS_LOW;

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

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

    SPI_CS_HIGH;

    return readData;
}

/**
 @brief 毫秒級延時函數
 @param time -[in] 延時時間(毫秒)
 @return 無
*/
static void delayMs(uint32 time)
{
    nrf_delay_ms(time);
}

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

4.2 board_si522.h

#ifndef _BOARD_SI522_H_
#define _BOARD_SI522_H_

/*********************************************************************
 * INCLUDES
 */
#include <stdbool.h>

#include "common.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                13
#define SI522_IRQ_IO                22

#ifndef IRQ_DEBOUNCE_TIMEOUT
#define IRQ_DEBOUNCE_TIMEOUT        20          // 超時時間
#endif

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

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

// ACD interrupt bits
#define ACD_IRQ                     0x40
#define ACDTIMER_IRQ                0x20

//******************************************************************/
//                       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_ANTICOLL3            0x97          // 防衝撞
#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

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

/*********************************************************************
 * API FUNCTIONS
 */
void SI522_Init(void);
void Board_Si522RstPinInit(void);
void Board_Si522RstPinSet(bool pinState);
void Board_Si522IrqInit(void);
void SI522_PcdInit(void);
void SI522_EnableAcdMode(void);
uint8 SI522_ReadCardDataBlock(uint8 blockAddr);
uint8 SI522_ReadCardSerialNo(uint8 *pCardSerialNo);
char SI522_PcdRead(uint8 blockAddr, uint8 *pReadData);
char SI522_PcdWrite(uint8 blockAddr, uint8 *pWriteData);
bool SI522_IsAcdInterrupt(void);
bool SI522_IsAcdTimerInterrupt(void);
void SI522_EnableAcdTimerMode(void);

#endif /* _BOARD_SI522_H_ */

五、API調用

需包含頭文件 board_si522.h

SI522_Init

功能 SI522初始化函數
函數定義 void SI522_Init(void)
參數
返回

Board_Si522IrqInit

功能 SI522的中斷引腳初始化函數
函數定義 void Board_Si522IrqInit(void)
參數
返回

SI522_PcdInit

功能 SI522的PCD模式初始化函數
函數定義 void SI522_PcdInit(void)
參數
返回

SI522_EnableAcdMode

功能 進入SI522的ACD模式
函數定義 void SI522_EnableAcdMode(void)
參數
返回

SI522_ReadCardDataBlock

功能 SI522讀取卡片塊數據
函數定義 uint8_t SI522_ReadCardDataBlock(uint8_t blockAddr)
參數 blockAddr:塊地址
返回 狀態值,0 - 成功;2 - 無卡;3 - 防衝撞失敗;4 - 選卡失敗;5 - 密碼錯誤

SI522_IsAcdInterrupt

功能 判斷是否ACD中斷,卡片靠近有可能產生IRQ中斷,但不一定是ACD中斷
函數定義 bool SI522_IsAcdInterrupt(void)
參數
返回 true - ACD中斷;false - 普通IRQ中斷

六、使用例子

1)添加頭文件

#include "board_si522.h"

2)添加初始化代碼(main.c的main函數中)

/**@brief Function for application main entry.
 */
int main(void)
{
	bool erase_bonds;

    /*-------------------------- 外設驅初始化 ---------------------------*/
	// Initialize.
    log_init();																	// 日誌驅動初始化																	
    timers_init();																// 定時器驅動初始化(在此加入自定義定時器)
	Board_Si522IrqInit();														// SI522輸入中斷驅動初始化
	SI522_Init();																// SI522驅動初始化(含SPI)	
    ···
    ···
/*-------------------------- 開啓應用 ---------------------------*/
	// Start execution.
    NRF_LOG_INFO("Template example started."); 
    SPI_Disable();	// 禁用SPI,進入低功耗模式,等待讀卡時再開啓
}

3)添加中斷事件回調,產生中斷時讀取數據塊4,然後再初始化PCD和ACD模式

// **************************************************************************************
//                                      SI522中斷事件
// **************************************************************************************
/**
 @brief SI522處理中斷函數
 @param irqValue -[in] 中斷值
 @return 無
*/
void SI522_HandleIrq(uint8 irqValue)
{
    // SPI_Enable(); // 從低功耗中回來,使能SPI
    if(irqValue & SI522_IRQ_VALUE)
    {
		if(SI522_IsAcdInterrupt() == true)
		{
			NRF_LOG_INFO("ACDIRq Occur");
			uint8 cardNo[4];;
			uint8 status = SI522_ReadCardSerialNo(cardNo);		
			NRF_LOG_INFO("status: %d", status);
			NRF_LOG_INFO("card: %d %d", cardNo[0], cardNo[1]);	
			SI522_EnableAcdTimerMode();                                                          // 重新進入ACD尋卡模式
		}
		else if(SI522_IsAcdTimerInterrupt() == true)
		{
			NRF_LOG_INFO("ACDTimerIRq Occur");
			uint8 cardNo[4];;
			uint8 status = SI522_ReadCardSerialNo(cardNo);		
			NRF_LOG_INFO("status: %d", status);
			NRF_LOG_INFO("card: %d %d", cardNo[0], cardNo[1]);	
			if(status == 2)
			{
				SI522_EnableAcdMode(); 
			}
			else if(status == 0)
			{
				SI522_EnableAcdTimerMode();    
			}
		}
		else
		{
			NRF_LOG_INFO("External Interrupt Occur");
			SI522_PcdInit();                                                                // PCD尋卡模式初始化
			SI522_EnableAcdMode();                                                          // 重新進入ACD尋卡模式
		}
        SPI_Disable();  // 禁用SPI,進入低功耗模式,等待讀卡時再開啓
    }
}

• 由 Leung 寫於 2020 年 4 月 13 日

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章