※注意事項

この説明においてSM130は、Evaluation Kit基盤を介してPCと接続してある状態か、
またはPCのRS-232のTx/Rxを、レベルコンバータを通じてSM130のRx/Txピンにそれぞれ繋いでいるものとする。
RS-232は±12Vの回路であり、0〜5VのTTL入出力レベルのSM130と直接接続すると、ICが焼ける怖れがあるので注意すること。
(最近のはそんなにヤワではないが、それでも壊れる可能性が十分にある)

下の写真は、RS-232とSM130を繋げたものである。(基板のVccやGNDは未接続)

RS232と接続したSM130とEvaluationKit    レベルコンバータを通じてRS232とSM130を接続    MAX232を使ったレベルコンバータ例

この項目では、SM130用のライブラリを用いずにSM130を制御する方法を説明する。
一部デバイスの制御を簡単にするため、Windows APIを用いている。
プログラムを開発した環境は、VisualStudio.netを用いた。
また、CやVisualStudioの文法や仕様に関しての説明は含んでいない。


Windos OSではなく他の組み込みデバイスを用いる場合には、そのデバイス独自のI/Oを用いて以下のパケットをSM130に投げれば制御することができる。


Windows APIの読み込み

OSが管理しているデバイスを使うために、Windows APIを使うようプログラム冒頭で宣言する。
とは言っても、使用しているAPIはCreateFile()ReadFile()WriteFile()程度なので、特に難しいことはない。
VisualStuioやVisualC++等を使っているのであれば、インクルード宣言部に以下の一行を加えれば良い。


#include <windows.h>



SM130とのデータ送受信

Windowsでは、COMxやUSBなどのIOデバイスはファイルストリームと同じ感覚でデータの送受信を行うことが出来る。
RS-232は基本COM1であるので、同じく簡単に送受信が行える。
BluetoothもWindowsではCOM3〜COM10などの名前が割り振られるので、認証をBluetooth通信のツールで行ってしまえば同様の通信が可能になる。

通信手順はファイルストリーム同様、対象のデバイスをオープンしてからRead関数とWrite関数を用いればよい。
ここではデバイスの使用するためにCreateFile()を、 Read関数にReadFile()を、 Write関数にWriteFile()を使っている。
ReadFile()とWriteFile()に関しては以降の説明で多用するので、ここで使用例を紹介する。


// HANDLE mifareHandle;
unsigned char readPacket[20];         // array for receive
unsigned char writePacket[] = "hoge"; // array for send
DWORD size = 5; // 目的の長さ
DWORD receivedSize; // 実際に読み取ったバッファの長さ

// send "size" size "writePacket" data
if( !WriteFile(mifareHandle, writePacket, size, &receivedSize, NULL) ) {
	return; // return error
}
size = 20;
// receive "size" size data to "readPacket"
if( !ReadFile(mifareHandle, readPacket, size, &receivedSize, NULL) ) {
	return; // return error
}


また、SM130のパケットフォーマットは次のようになっている。
HeaderReservedLengthCommandDataCheck Sum
1 byte1 byte1 byte1 byteN byte1 byte

このうち、Header部は0xFF、Reserved部は0x00に固定される。
Length部はCommand部とData部のみの長さであり、Header部、Reserved部、Check sum部の長さは含まれない。
Data部は各コマンドによって内容が変わる。例えば認証であれば認証鍵を、タグの内容を書き換えるのであればその内容を格納する。
Check sum部は単純な加算値であり、Header部からData部までのN+4バイトの値を全て合計した値になる。
つまり、例えば{0x86,0x03}というコマンドを出す場合、実際に送出するパケットは{0xFF,0x00,0x02,0x86,0x03,0x8A}となる。

SM130の各コマンドに関する情報は、仕様書のデータシートに詳しく記載されている。


ポートのオープン

RS-232はUSB変換器等を使っていない限り、COM1としてPCに装着されている。
このデバイスを使うには、CreateFile()関数を使う。
CreateFile()の返り値はHANDLE型であり、この値を以降の処理ではファイルポインタのように扱う。
また、RS-232で通信する際のボーレートをここで設定する必要がある。
初期値はデバイスマネージャに設定された値になっているが、ここで再設定した方が無難であろう。
次のプログラムでは、mifareHandleというハンドルでCOM1を開いた後に、19200bpsの8n1に設定している。


HANDLE mifareHandle;
mifareHandle = CreateFile(
	"COM1:", // open COM1 port
	GENERIC_READ|GENERIC_WRITE,
	0,
	0,
	OPEN_EXISTING,
	0,
	NULL );

DCB dcb; // status of device
if( !GetCommState( mifareHandle, &dcb ) ) {
	printf("error: can not get dcb");
	CloseHandle(mifareHandle);
	return;
}
dcb.BaudRate = CBR_19200; // 19200 bps
dcb.ByteSize = 8;
dcb.Parity = NOPARITY;    // no parity check
dcb.StopBits = ONESTOPBIT;
if( !SetCommState( mifareHandle, &dcb ) ) {
	printf("error: can not set dcb");
	CloseHandle(mifareHandle);
	return;
}


これでCOM1からデータの送出を行えるようになる。


タグの選択

通信対象となるMifareタグを、Seek for Tagコマンドを使って通信可能範囲内から検索する。
Seekコマンドは{0x82}で表わされ、そのほかのデータを持たない。
つまり、パケットは常に{ 0xFF, 0x00, 0x01, 0x82, 0x83 }である。

また、Seekコマンドはタグを発見した場合に2つのレスポンスを返す。
一つ目のレスポンスはコマンドを受理したことを、二つ目は発見したタグの種類とシリアル番号を返す。


unsigned char packet[6];
unsigned char res[10];
DWORD size;

//-- set seek command
packet[0] = 0xFF;
packet[1] = 0x00;
packet[2] = 0x01;
packet[3] = 0x82;
packet[4] = 0x83;

// send packet to COM1
if( !WriteFile(mifareHandle, packet, 5, &size, NULL) ) {
	printf("seek error");
	return;
}

// receive 1st respoinse from COM1
for( int i=0; i<6; i++ ) { res[i] = 0x00; }
if( !ReadFile(mifareHandle, res, 6, &size, NULL) ) {
	printf("error: can not get Response1");
	return;
}
if( res[4]!= 0x4C ) {
	printf("SEEK command does not execute correctly");
	return;
}
// receive 2nd response from COM1
for( int i=0; i<10; i++ ) { res[i] = 0x00; }
if( !ReadFile(mifareHandle, res, 10, &size, NULL) ) {
	printf("error: can not get Response2");
	return;
}

認証とリード・ライト

Mifareでは、タグ内部のブロックごとに認証が必要となる。
つまり1つのブロックとデータのやり取りをする場合、認証処理とリード・ライト処理が必ずセットになって行われる。

認証はAuthenticateコマンドで行われる。
認証が終了した後に、Read BlockコマンドとWrite Blockコマンドでタグの内容を見ることができる。
Authenticateコマンドを送出する際には、パケットのData部に認証対象となるブロックの番号と認証鍵のタイプ、6バイトの認証鍵を共に送出する。
Read BlockコマンドはData部にブロック番号を指定し、レスポンスに読み取ったデータを含む。
Write BlockコマンドはData部にブロック番号と書き込むデータを指定し、レスポンスに実際に書き込んだブロックの番号とデータを含む。

次のコードは、NFCタグ(含Mifareタグ)に限定して認証を行った後に、書き込み処理を行っている。
また、読み込み処理をコメントで囲っている。


//--- authenticate ---
unsigned char packet[22];
unsigned char res[22];
int blockNum = 4; // access to 5th block

packet[0] = 0xFF; // Header
packet[1] = 0x00; // Reserved
packet[2] = 0x09; // Length
packet[3] = 0x85; // Command
packet[4] = blockNum;
packet[5] = 0xAA; // key type A
// set authenticate key
for( int i=0; i<3; i++ ) {
	packet[i*2  +6] = 0xD3;
	packet[i*2+1+6] = 0xF7;
}
// calculate check sum
packet[12] = 0x00; // check sum
for( int i=2; i<12; i++ ) { packet[12] += packet[i]; } //packet[12] = 0x9A;

// send packet to COM1
if( !WriteFile(mifareHandle, packet, 13, &size, NULL) ) {
	printf("authenticate error");
	return;
}

// receive response packet from COM1
for( int i=0; i<6; i++ ) { res[i] = 0x00; }
if( !ReadFile(mifareHandle, res, 6, &size, NULL) ) {
	printf("error: can not get Authenticate response");
	return;
}
if( res[4] != 0x4C ) {
	printf("error: Authentication failure");
	return;
}

//--- write block ---
packet[0] = 0xFF;
packet[1] = 0x00;
packet[2] = 0x12; // packet length
packet[3] = 0x89; // command
packet[4] = blockNum;
packet[21] = 0x12 + 0x89 + blockNum; // check sum;
for(int i=0; i<16; i++) { // writing data
	packet[5+i] = data[i];
	packet[21] += data[i];
}
//---
if( !WriteFile(mifareHandle, packet, 0x22, &size, NULL) ) {
	printf("write block error");
	return;
}
for( int i=0; i<22; i++ ) { res[i] = 0x00; }
if( !ReadFile(mifareHandle, res, 22, &size, NULL) ) {
	printf("error: can not get WriteBlock response");
	return;
}

/**
//--- read block ---
packet[0] = 0xFF;
packet[1] = 0x00;
packet[2] = 0x02;
packet[3] = 0x86;
packet[4] = blockNum;
packet[5] = 0x02 + 0x86 + blockNum;
//---
if( !WriteFile(mifareHandle, packet, 6, &size, NULL) ) {
	printf("read block error");
	return false;
}
for( int i=0; i<22; i++ ) { res[i] = 0x00; }
if( !ReadFile(mifareHandle, res, 22, &size, NULL) ) {
	printf("error: can not get ReadBlock response");
	return;
}
	
if( res[2]==2 ) {
	printf("error: can not Read Block");
	return;
}
*/

ポートのクローズ

通信が終了した後に、開いたポートを閉じる。


CloseHandle( mifareHandle );

おまけ: NDEFフォーマットでの書き込みプログラム