SONY製のNFCカードリーダ RC-S620/S をイニシエータとして使用し、NFC Type-A/Bと通信するライブラリです。FeliCaとは通信できません。(This library can't communicate with FeliCa! Only for NFC Type-A/B.)

Dependents:   DLC_STARTER

RCS620S_AB.cpp

Committer:
hmizuno
Date:
2020-06-13
Revision:
2:08ccf5062b69
Parent:
1:98c4a45b646a

File content as of revision 2:08ccf5062b69:

#include "mbed.h"
#include "RCS620S_AB.h"

//public function

    //rc-s620 デフォルトボーレート;115200
    RCS620S_AB::RCS620S_AB(PinName tx, PinName rx):
        _serial(tx, rx, 115200){
        initialize();
    };

/***************/
/**** 制御系 ****/
/***************/

    void RCS620S_AB::powerDown(){
        sendCommand_and_RecieveRes(wired_data_RFConfiguration_PowerDown,(uint16_t)sizeof(wired_data_RFConfiguration_PowerDown));
    }
    
    void RCS620S_AB::wakeUp(){
        //powerdown直後に呼ばれた場合でも10ms以上確保
        wait_ms(11);
        _serial.putc(0x55);
        wait_ms(50);
    }

    void RCS620S_AB::reset(){
         //”カードを”リセットするためにRFをOFF
        sendCommand_and_RecieveRes(wired_data_RFConfiguration_RFoff,(uint16_t)sizeof(wired_data_RFConfiguration_RFoff));
        wait_ms(50);
        sendCommand_and_RecieveRes(wired_data_Reset,(uint16_t)sizeof(wired_data_Reset));
    }
    
/***************/
/**** 受信系 ****/
/***************/
    /*受信バッファ配列 要素番号カンペ (ATQB受信例)
    
    [11]D5 [12]4B [13]Tg [14~][CardRes] DCS
    
    [ 0] 00 [ 1] 00 [ 2] FF [ 3] 00 [ 4] FF [ 5] 00  ~ACK
    [ 6] 00 [ 7] 00 [ 8] FF [ 9] 12 [10] EE ~LCS
    [11] D5 [12] 4B [13] 01 [14] 01 [15] 50 ・・・ [LEN-2] DCS [LEN-1] 00
    ・全部ほしい場合:[0] ~ [LEN-1]
    ・レスポンス部が欲しい場合:[11] ~ [LEN-3]
    ・カードのデータ部が欲しい場合:[14] ~ [LEN-3]
    */

    //重要!配列要素集は0始まりのためLENと1ずれる!

    //※デバッグ用 受信バッファーのデータを全て取得(ACKやチェックサム含むすべてのデータ)
    void RCS620S_AB::getAllRes(uint8_t ansArray[], int *ansLen){
        extractRes(0, recieveLEN-1 , ansArray, ansLen);
    }
    
    //ACKやチェックサムを除いたレスポンスを取得
    void RCS620S_AB::getRes(uint8_t ansArray[], int *ansLen){
        uint8_t dataLen = checkRxData_and_getLEN();
        if(dataLen > 0){
            extractRes(11, 11+dataLen-3 , ansArray, ansLen);
        }else{
            ansArray[0] = 0x00;
            *ansLen = 0;
        }
    }
    
    //inListPasseveTarget 及び inDataExchangeからカード情報だけを取得
    void RCS620S_AB::getCardRes(uint8_t ansArray[], int *ansLen){
        
        //inListPassiveTargetの場合 [11]D5 [12]4B [13]Tg [14~][CardRes] DCS
        //inDataExchangeの場合 [11]D5 [12]41 [13]Status [14~][CardRes] DCS
        //⇒共用可能
        
        if(checkCardRes()){
            extractRes(14, recieveLEN-3 , ansArray, ansLen);
        }else{
            ansArray[0] = 0x00;
            *ansLen = 0;
        }
    }

/***************/
/**** 送信系 ****/
/***************/
 
    //ICカードのコマンドのデータパケット部を受け取り、チェックサムなどの付加情報を付与し送信
    void RCS620S_AB::sendCommand_and_RecieveRes(const uint8_t wired_packet_data[], uint16_t wired_packet_data_len){
       
        //長すぎたらエラー
        if(wired_packet_data_len > 252){
            exit(1);
        }
        
        //0xD4で始まらなかったらエラー
        if(wired_packet_data[0] != 0xD4){
            exit(1);
        }
        
        //送信コマンド = 0x00 0x00 0xff LEN LCS [DATA PACKET] DCS 0x00
        
        uint8_t *packet_data_command;
        uint8_t LEN, LCS, DCS = 0x00;
        uint32_t data_sum = 0x00000000;
        
        int i = 0;
        
        //LEN・LCSを計算
        LEN = (uint8_t)(wired_packet_data_len);   //送信データの長さ
        LCS = (uint8_t)(0x0100 - LEN);
    
        //card_command+リーダライタコマンド分のメモリを確保
        
        packet_data_command = (uint8_t*)malloc(sizeof(uint8_t) * (LEN + 7));
        
        
        //カードリーダのコマンドを送信データに結合
        
        packet_data_command[0] = 0x00;   //Preamble
        packet_data_command[1] = 0x00;   //Start Of Packet_1
        packet_data_command[2] = 0xff;   //Start Of Packet_2
        
        packet_data_command[3] = LEN;   //LEN
        packet_data_command[4] = LCS;   //LCS
        
        //パケットデータを流し込む
        for(i = 0; i < LEN; i++){
            packet_data_command[i + 5] = wired_packet_data[i];   //DATA
            data_sum = data_sum + wired_packet_data[i];  //DCS計算用
        }
       
        //DCSを計算
        DCS = (uint8_t)(0x100 - (data_sum % 0x100));
    
        packet_data_command[5 + LEN] = DCS;   //DCS
        packet_data_command[5 + LEN + 1] = 0x00;   //Postamble
    
        //レスポンスバッファをクリア
        clearRecieveBuffer();
    
        //コマンドを送信
        //busy中に送り付けてしまっても自動リトライするよう改良 20/6/11
        //ACK応答が返ってくるまでコマンドを送り付ける
        //ただし配線が外れてたら無限ループ
        do{
            for(i = 0; i <= (5 + LEN + 1); i++){
                _serial.putc(packet_data_command[i]);
            }
        }while(!isACKrecieved(UART_TRANSMIT_FAIL_TIMEOUT_MS));
    
        //powerdownの時はレスポンスが返ってこないので受信待機が無限ループに入ってしまう
        //powerdown時はACKのみチェックする
        //コマンドリファレンス39ページ『ただしACKは返します』
        if(wired_packet_data[1] != wired_data_RFConfiguration_PowerDown[1]){
            //レスポンス受信完了まで待機
            waitWhileRead(5);
        }else{
            do{
                wait_ms(1);
            }while(recieveLEN < sizeof(wired_complete_ACK));
        }
    
        //メモリをfree
        free(packet_data_command);
    }


    //ICカードのコマンドを、リーダライタのInDataExchangeを使ってICカードへ送信する関数
    void RCS620S_AB::sendInDataExchange_and_RecieveRes(const uint8_t card_command[], uint16_t card_command_len){
        
        //card_command+リーダライタコマンド分のメモリを確保
        uint8_t *packet_command;
        packet_command = (uint8_t*)malloc(sizeof(uint8_t) * (card_command_len + 3));
        
        //無線コマンドの前にInDataExchange有線コマンド結合
        packet_command[0] = 0xD4;   //コマンドコード
        packet_command[1] = 0x40;   //サブコマンドコード
        packet_command[2] = 0x01;   //Tg(InListPassiveTargetであらかじめ1枚に指定)
        
        for(int i = 0; i < card_command_len ; i++){
            packet_command[i + 3] = card_command[i];
        }
    
        //カードリーダへ送信
        sendCommand_and_RecieveRes(packet_command, (card_command_len  + 3));
        
        //メモリをfree
        free(packet_command);
    }

    
    void RCS620S_AB::sendInListPasseveTarget_typeA(){
        sendCommand_and_RecieveRes(wired_data_InListPassiveTarget_typeA,(uint16_t)sizeof(wired_data_InListPassiveTarget_typeA));
    }
    
    void RCS620S_AB::sendInListPassiveTarget_typeB(){
        sendCommand_and_RecieveRes(wired_data_InListPassiveTarget_typeB,(uint16_t)sizeof(wired_data_InListPassiveTarget_typeB));
    }

//private functioin

/***************/
/**** 制御系 ****/
/***************/

    void RCS620S_AB::initialize(){
        //UART受信割り込み設定
        _serial.attach(callback(this,&RCS620S_AB::serialRxIrq), Serial::RxIrq);
        sendCommand_and_RecieveRes(wired_data_AdditionalTime24ms,(uint16_t)sizeof(wired_data_AdditionalTime24ms));
    } 
    
/***************/
/**** 受信系 ****/
/***************/

    //カードリーダ UART受信割込み
    //※この関数のなかでデバッグ用printf使用不可(処理中に取りこぼす)
    void RCS620S_AB::serialRxIrq(){
        recieveBuffer[recieveLEN] = (uint8_t)_serial.getc(); //0
        recieveLEN++; //1 LEN = 配列番号+1 になりOK
        if(recieveLEN >= READ_BUF_SIZE){
            exit(1);
        }
    }
    
    void RCS620S_AB::clearRecieveBuffer(){
        //受信バッファカーソル位置を先頭に
        recieveLEN = 0;
        
        //受信バッファをクリア
        for(int i = 0; i < READ_BUF_SIZE; i++){
            recieveBuffer[i] = 0x00;
        }
    }

    //ACK受信待ち
    bool RCS620S_AB::isACKrecieved(int timeout_ms){
        int elapsedTime = 0;
        do{
            wait_ms(1);
            elapsedTime++;
            if(elapsedTime > timeout_ms){
                return false;
            }
        }while(recieveLEN < sizeof(wired_complete_ACK));
        return true;
    }

    //読み取り完了まで待機
    //ACK受信後、一定時間経過しても受信文字数が増えなくなる(=RX割り込みがかからない=受信完了)まで待機
    void RCS620S_AB::waitWhileRead(int cycleTime_ms){
       
        int cTime;
        if(cycleTime_ms >= 1){
            cTime = cycleTime_ms;
        }else{
            cTime = 1;
        }
        
        //ackはmax3.5ms
        int l;
        do{
            l = recieveLEN;   //現在の受信文字数
            wait_ms(cTime);
        }while(recieveLEN <= sizeof(wired_complete_ACK) || l < recieveLEN);//一定時間後の受信文字数と比較
    }
    
    //受信バッファからrecieveBuffer[start] ~ recieveBuffer[end] を抜き出す
    void RCS620S_AB::extractRes(int start, int end, uint8_t ansArray[], int *ansLen){
        if(end > recieveLEN || start > end){
            *ansLen = 0;
        }else{
            int j = 0;
            for(int i = start; i <= end; i++){
                ansArray[j] = recieveBuffer[i];
                j++;
            }
            *ansLen = end - start + 1;
        }
    }

/***************/
/**** 検査系 ****/
/***************/
    
    //頭についているACKが仕様書記載通りのデータになっているか確認
    bool RCS620S_AB::checkACK(){
        bool flag = true;
        for(int i = 0; i<sizeof(wired_complete_ACK); i++){
            if(wired_complete_ACK[i] != recieveBuffer[i]){
                flag = false;
            }
        }
        return flag;
    }


    bool RCS620S_AB::checkCardRes(){
        //inListPassiveTargetの場合 D5 4B Tg [CardRes] DCS
        //inDataExchangeの場合 D5 41 Status [CardRes] DCS
    
        //そもそも受信できてなければエラー
        if(checkRxData_and_getLEN() < 1){
            return false;
        }
    
        //使う頻度が多いinDataExchangeを先に評価
         if(recieveBuffer[12] == 0x41 && recieveBuffer[13] == 0x00){
            //inListpassiveTargetレスポンス(0x41)ならばターゲットidが0x01かチェック
            return true;
        }else if(recieveBuffer[12] == 0x4B && recieveBuffer[13] == 0x01){
            //inListpassiveTargetレスポンス(0x4B)ならばターゲットidが0x01かチェック
            return true;
        }else{
            return false;
        }
    }
    
    
    //受信データをチェックしてOKならLENを返す、異常なら0を返す
    uint8_t RCS620S_AB::checkRxData_and_getLEN(){
        
        //頭にACKが付いているかチェック
        if(!checkACK()){
            return 0;
        }   
        
        //LEN+LCSをチェックしてLENを取得
        uint8_t data_len = 0;
    
        uint8_t recieved_LEN_check = (uint8_t)(0x00ff & (recieveBuffer[9]+recieveBuffer[10]));
        if(recieved_LEN_check == 0x00 && recieveBuffer[9] > 0x00){
            data_len = recieveBuffer[9];
        }else{
            //LEN受信エラー
            return 0;
        }
        
        //レスポンスコードをチェック
        if(recieveBuffer[11] != 0xD5){
            //レスポンスコードが 0xD5 でない
            return 0;
        }
        
        //DCSをチェック
        uint32_t data_sum = 0x00000000;
        for(int i = 11; i < 11 + data_len; i++){
            data_sum = data_sum + recieveBuffer[i];
        }
        data_sum = 0x000000FF & data_sum;
        //データ合計にDCSを足す
        data_sum = data_sum + recieveBuffer[11 + data_len];
        
        if(data_sum == 0x100){
            //LENを返す
            return data_len;
        }else{
            //データ受信エラー
            return 0;
        }
    }