2014/10/06

libnfc example

1. Brief Introduction


在使用libnfc相關的code, 第一步一定要先呼叫 nfc_init(), 它會做初始設定和配置相關資源
void nfc_init(nfc_context **context)


接著呼叫 nfc_open(), 它會找第一個可用的 nfc device
nfc_device* nfc_open(nfc_context *context, const nfc_connstring connstring)


參數 context 就是在 nfc_init() 用到的那個
參數 connstring 表示你想找有特定名字的device, 像"usb"之類的, 如果沒有特定就帶NULL

它會依照下列順序找:
(1) 環境變數裡預設值
(2) libnfc.conf 設定的值
(3) /etc/nfc/devices.d 裡找到
(4) auto-detected 第一個找到的值

找到之後, 它會宣告目前它在使用這個device, 並回傳device pointer


然後記得對這個 device 做初始化
int nfc_initiator_init(nfc_device *pnd)


struct nfc_modulation 表示不同ISO規格的資料
typedef struct {
  nfc_modulation_type nmt;
  nfc_baud_rate nbr;
} nfc_modulation;
其中 nfc_modulation_type為ISO標準
typedef enum {
  NMT_ISO14443A = 1,
  NMT_JEWEL,
  NMT_ISO14443B,
  NMT_ISO14443BI, // pre-ISO14443B aka ISO/IEC 14443 B' or Type B'
  NMT_ISO14443B2SR, // ISO14443-2B ST SRx
  NMT_ISO14443B2CT, // ISO14443-2B ASK CTx
  NMT_FELICA,
  NMT_DEP,
} nfc_modulation_type;
然後 nfc_baud_rate 為 tag 的 baud rate, (每種規格有不同的baud rate)
typedef enum {
  NBR_UNDEFINED = 0,
  NBR_106,
  NBR_212,
  NBR_424,
  NBR_847,
} nfc_baud_rate;
nfc_modulation 主要用在描述現在想嘗試找什麼規格的 tag


struct nfc_target 則是用來儲存找到的 tag
typedef struct {
  nfc_target_info nti;
  nfc_modulation nm;
} nfc_target;
其中 nfc_modulation 只是紀錄它的ISO規格
union nfc_target_info 則是儲存找到的這張 tag 的內容
typedef union {
  nfc_iso14443a_info nai;
  nfc_felica_info nfi;
  nfc_iso14443b_info nbi;
  nfc_iso14443bi_info nii;
  nfc_iso14443b2sr_info nsi;
  nfc_iso14443b2ct_info nci;
  nfc_jewel_info nji;
  nfc_dep_info ndi;
} nfc_target_info;
不同的 ISO 規格, 它能提供的資訊也不同


我們有nfc device, nfc modulation, 就可以找 nfc target, 用底下這個function
int nfc_initiator_select_passive_target(nfc_device *pnd,
                                        const nfc_modulation nm,
                                        const uint8_t *pbtInitData,
                                        const size_t szInitData,
                                        nfc_target *pnt)
其中 pnd 是 nfc device
nm 是  nfc modulation
pbtInitData 可以帶 NULL, 有些 ISO 規格裡可以帶data給tag, 比如說ISO14443A裡, 可以帶 UID, 而 ISO14443B 可以帶 Application Family Identifier (AFI)
szInitData 則是前一項 pbtInitData的size
pnt 則是存找到的nfc target


最後記得要關device
void nfc_close(nfc_device *pnd)

以及要記得釋放nfc context資源
void nfc_exit(nfc_context *context)

2. Sample code


這份sample code從libnfc官方網頁上可以找到, 這是原始網頁
http://nfc-tools.org/index.php?title=Libnfc:quick_start_example

它可以找 ISO14443-A tag, (可能是最常見的tag)

#include <stdlib.h>
#include <nfc/nfc.h>

static void
print_hex(const uint8_t *pbtData, const size_t szBytes) {
 size_t szPos;

 for (szPos = 0; szPos < szBytes; szPos++) {
  printf("%02x ", pbtData[szPos]);
 }
 printf("\n");
}

int
main(int argc, const char *argv[]) {
 nfc_device *pnd;
 nfc_target nt;
 nfc_context *context;

 nfc_init(&context);
 if (context == NULL) {
  printf("Unable to init libnfc (malloc)\n");
  exit(EXIT_FAILURE);
 }

 pnd = nfc_open(context, NULL);
 if (pnd == NULL) {
  printf("ERROR: %s\n", "Unable to open NFC device.");
  exit(EXIT_FAILURE);
 }

 if (nfc_initiator_init(pnd) < 0) {
  nfc_perror(pnd, "nfc_initiator_init");
  exit(EXIT_FAILURE);
 }

 printf("NFC reader: %s opened\n", nfc_device_get_name(pnd));

 const nfc_modulation nmMifare = {
  .nmt = NMT_ISO14443A,
  .nbr = NBR_106,
 };
 if (nfc_initiator_select_passive_target(pnd, nmMifare, NULL, 0, &nt) > 0) {
  printf("The following (NFC) ISO14443A tag was found:\n");
  printf("    ATQA (SENS_RES): ");
  print_hex(nt.nti.nai.abtAtqa, 2);
  printf("       UID (NFCID%c): ", (nt.nti.nai.abtUid[0] == 0x08 ? '3' : '1'));
  print_hex(nt.nti.nai.abtUid, nt.nti.nai.szUidLen);
  printf("      SAK (SEL_RES): ");
  print_hex(&nt.nti.nai.btSak, 1);
  if (nt.nti.nai.szAtsLen) {
   printf("          ATS (ATR): ");
   print_hex(nt.nti.nai.abtAts, nt.nti.nai.szAtsLen);
  }
 }

 nfc_close(pnd);

 nfc_exit(context);
 exit(EXIT_SUCCESS);
}

然後 compile它
$ gcc -o quick_start_example1 quick_start_example1.c -lnfc

執行結果
$ ./quick_start_example1
NFC reader: pn532_spi:/dev/spidev0.0 opened
The following (NFC) ISO14443A tag was found:
    ATQA (SENS_RES): 00 04
       UID (NFCID1): 45 d1 a9 51
      SAK (SEL_RES): 08

3. Polling


前個例子是一執行馬上就掃當下讀的到的卡
也可以改成用 poll 的方式, 在一定的時間內讀到卡即可

int nfc_initiator_poll_target(nfc_device *pnd,
                              const nfc_modulation *pnmModulations,
                              const size_t szModulations,
                              const uint8_t uiPollNr,
                              const uint8_t uiPeriod,
                              nfc_target *pnt)
其中, pnd 是 nfc device, pnt 是 nfc target
pnmModulations 是 nfc_modulation array, 裡面放想掃的 modulation type
szModulations 是 pnmModulations 的 array 長度
uiPollNr 指要 poll 幾次, 可以 1~254 次, 如果是 0xFF 表示要永遠 poll
uiPeriod 表是每次 poll 間隔幾個單位, 1個單位內定是150ms, 2個單位就是300ms, 以此類推
回傳值是 poll 掃到的 nfc target 數目, <0 表示error, =0表示沒掃到

可以 poll 卡是不是接近之後, 也可以檢查卡是不是已經拿開
int nfc_initiator_target_is_present(nfc_device *pnd, const nfc_target *pnt)
回傳 0 表示還在, 其它的值表示 error (即離開或讀不到了)

3.1 Polling sample code


相關的 sample code 可以參考 libnfc 裡的 nfc-poll.c
底下這個例子稍微簡化 nfc-poll.c,

#include <signal.h>
#include <stdio.h>
#include <stdlib.h>

#include <nfc/nfc.h>
#include <nfc/nfc-types.h>

static nfc_device *pnd = NULL;
static nfc_context *context;

static void print_hex(const uint8_t *pbtData, const size_t szBytes) {
 size_t szPos;
 for (szPos = 0; szPos < szBytes; szPos++) {
  printf("%02x ", pbtData[szPos]);
 }
 printf("\n");
}

static void stop_polling(int sig) {
 if (pnd != NULL) {
  nfc_abort_command(pnd);
 } else {
  nfc_exit(context);
  exit(EXIT_FAILURE);
 }
}

int main(int argc, const char *argv[]) {
 signal(SIGINT, stop_polling);

 const uint8_t uiPollNr = 20;
 const uint8_t uiPeriod = 2;
 const nfc_modulation nmModulations[1] = {
  { .nmt = NMT_ISO14443A, .nbr = NBR_106 },
 };
 const size_t szModulations = 1;

 nfc_target nt;
 int res = 0;

 nfc_init(&context);
 if (context == NULL) {
  printf("Unable to init libnfc (malloc)\n");
  exit(EXIT_FAILURE);
 }

 pnd = nfc_open(context, NULL);
 if (pnd == NULL) {
  printf("Unable to open NFC device\n");
  exit(EXIT_FAILURE);
 }

 if (nfc_initiator_init(pnd) < 0) {
  nfc_perror(pnd, "nfc_initiator_init");
  nfc_close(pnd);
  nfc_exit(context);
  exit(EXIT_FAILURE);
 }

 printf("NFC reader: %s opened\n", nfc_device_get_name(pnd));
 printf("NFC device will poll %d ms\n", uiPollNr * szModulations * uiPeriod * 150);
 res = nfc_initiator_poll_target(pnd, nmModulations, szModulations, uiPollNr, uiPeriod, &nt);
 if (res < 0) {
  nfc_perror(pnd, "nfc_initiator_poll_target");
  nfc_close(pnd);
  nfc_exit(context);
  exit(EXIT_FAILURE);
 } else if (res == 0) {
  printf("No target found\n");
 } else {
  printf("The following (NFC) ISO14443A tag was found:\n");
  printf("    ATQA (SENS_RES): ");
  print_hex(nt.nti.nai.abtAtqa, 2);
  printf("       UID (NFCID%c): ", (nt.nti.nai.abtUid[0] == 0x08 ? '3' : '1'));
  print_hex(nt.nti.nai.abtUid, nt.nti.nai.szUidLen);
  printf("      SAK (SEL_RES): ");
  print_hex(&nt.nti.nai.btSak, 1);
  if (nt.nti.nai.szAtsLen) {
   printf("          ATS (ATR): ");
   print_hex(nt.nti.nai.abtAts, nt.nti.nai.szAtsLen);
  }
 }

 printf("Waiting for card removing...\n");
 while (0 == nfc_initiator_target_is_present(pnd, NULL)) {};
 nfc_perror(pnd, "nfc_initiator_target_is_present");
 printf("done\n");

 nfc_close(pnd);
 nfc_exit(context);
 exit(EXIT_SUCCESS);
}






2014/10/03

Connect PN532 NFC to Raspberry Pi

1. Enable SPI on Raspberry Pi


PN532 NFC提供3種介面, SPI, I2C, 與UART
先選SPI做測試,

在RPi上面, SPI與I2C預設是關的, 我們要先打開它,

$ sudo nano /etc/modprobe.d/raspi-blacklist.conf


會看到裡面有一些黑名單

blacklist spi-bcm2708
blacklist i2c-bcm2708
blacklist snd-soc-pcm512x
blacklist snd-soc-wm8804


我們要用到spi, 所以把它mark掉

#blacklist spi-bcm2708

存檔離開, 然後重開機讓設定生效


重開機之後, 來看看SPI driver有沒有起來

$ ls /dev/spidev*
/dev/spidev0.0  /dev/spidev0.1

會看到有兩個 SPI driver, 前面的數字是第幾個SPI peripheral,
兩個數字都是1表示 RPi 只有一個SPI port
第二個數字是 Chip Selectt 使用pin CS0 & CS1


2. Introduction to SPI


Serial Peripheral Interface (SPI), 跟 I2C很像
是同步序列資料協定(synchronous serial data link)

常用的接角定義:
  • SCLK—Serial Clock(自master輸出), 有時也叫SCK
  • MOSI/SIMO—Master Output, Slave Input(自master輸出)
  • MISO/SOMI—Master Input, Slave Output(自slave輸出)
  • SS—Slave Select(active low;自master輸出), 在RPi上叫SPI_CE0_N, SPI_CE1_N, 在PN532上叫SSEL

在使用上, 1個master可以對應多個slave
可能的接法如下
如果device扮演的是Master, 那麼它的MOSI就等於是它的Output, MISO就是它的Input
如果device扮演的是Slave, 那麼它的MOSI就等於是它的Input, MISO就是它的Output

3. Connect PN532 to RPi via SPi


底下是 RPi 的接腳配置


其中跟 SPI 相關的有:
SPI_MOSI
SPI_MISO
SPI_SCLK
SPI_CE0_N
SPI_CE1_N

我們這樣接:

  • PN532 的 3.3V 接到 RPi 的3V3 (可接1或17的位置)
  • PN532 的 GND 接到 RPi 的接地 (可接 6, 9, 14, 20, 25, 30, 34, 39, 我是接39)
  • PN532 的 SCK 接到 RPi 的 SPI_SCLK (23的位置)
  • PN532 的 MISO 接到 RPi 的 SPI_MISO (21的位置)
  • PN532 的 MOSI 接到 RPi 的 SPI_MOSI (19的位置)
  • PN532 的 SSEL 接到 RPi 的SPI_CE0_N


4. Install libnfc in RPi


接好之後, 有很多種方式可以與PN532溝通, 用libnfc是其中一種方便的方式

首先先更安裝有dependency的library
sudo apt-get update
$ sudo apt-get install libusb-dev libpcsclite-dev

然後抓libnfc source code, 可以上它們的網站來找
https://code.google.com/p/libnfc/

其中stable version在寫這篇文章的時候在這裡
https://bintray.com/nfc-tools/sources/libnfc

抓好之後, 將它解壓縮

$ tar -xf libnfc-1.7.1.tar.bz2

然後compile & install
$ cd libnfc-1.7.1
$ ./configure --prefix=/usr --sysconfdir=/etc
$ make
$ sudo make install

修改它的config
$ cd /etc
$ sudo mkdir nfc
$ sudo nano /etc/nfc/libnfc.conf

把底下copy paste
# Allow device auto-detection (default: true)
# Note: if this auto-detection is disabled, user has to set manually a device
# configuration using file or environment variable
allow_autoscan = true

# Allow intrusive auto-detection (default: false)
# Warning: intrusive auto-detection can seriously disturb other devices
# This option is not recommended, user should prefer to add manually his device.
allow_intrusive_scan = false

# Set log level (default: error)
# Valid log levels are (in order of verbosity): 0 (none), 1 (error), 2 (info), 3 (debug)
# Note: if you compiled with --enable-debug option, the default log level is "debug"
log_level = 1

# Manually set default device (no default)
# To set a default device, you must set both name and connstring for your device
# Note: if autoscan is enabled, default device will be the first device available in device list.
device.name = "Itead_PN532_SPI"
device.connstring = "pn532_spi:/dev/spidev0.0:500000"

設定完成, 來試試看有沒有問題, 拿一張悠遊卡放在PN532旁邊
$ nfc-list
$ nfc-list
nfc-list uses libnfc 1.7.1
NFC device: pn532_spi:/dev/spidev0.0 opened
1 ISO14443A passive target(s) found:
ISO/IEC 14443A (106 kbps) target:
    ATQA (SENS_RES): 00  04
       UID (NFCID1): 2b  6a  20  87
      SAK (SEL_RES): 08

會動之後, 就可以玩其它功能了





2014/10/01

Setting raspberry pi uart

1. How to connect


首先需要一條 USB-TTL 線
(試過RS232轉TTL, 似乎不成功)

底下是 Raspberry Pi (RPi) B+的 40-pin 配置圖


其中
USB-TTL 接地線(GND) 接到 RPi GND (JB6)
USB-TTL RX 接到 RPi TX (JB8)
USB-TTL TX 接到 RPi RX (JB10)

2. Serail setting


用putty或其它serial terminal (Ex. tera term)

類型選 serial
Serail Line 填 USB-TTL 的 COM port
Speed 填 115200
Data Bits 填 8
Stop Bits 填 1
Parity 填 None
Flow Control 填 None


3. screen size


登入之後會發現, 它的 row = 24, column = 80, 使用起來不方便
用 vim 輸入幾個tab就超出螢幕了
可以用 stty 來修改

這個可以看當前的設定
$ stty size

修改行數為40

$ stty rows 40

修改每行字元數為128

$ stty columns 128




2014/09/29

Setting raspberry pi network

1. Use wpa_cli command connect to ssid


打這個 command 進入 wpa command line interface, (提示符號會變成 ">")

$ wpa_cli

掃瞄目前的wifi訊號, 並且列出結果
> scan
OK
> scan_result
bssid / frequency / signal level / flags / ssid
e4:71:85:04:98:f4       2442    100     [WPA-PSK-TKIP+CCMP][WPA2-PSK-TKIP+CCMP][ESS]    Almond-0326_nomap_5F-7
<3>CTRL-EVENT-SCAN-RESULTS

加入網路ssid, psk是密碼
> add_network
0
> set_network 0 ssid "MYSSID"
> set_network 0 psk "passphrase"
> enable_network 0
<2>CTRL-EVENT-CONNECTED - Connection to 00:00:00:00:00:00 completed (reauth) [id=0 id_str=]

如果是open network
> set_network 0 key_mgmt NONE

把config存起來
> save_config
OK

這些動作會存在
/etc/wpa_supplicant/wpa_supplicant.conf

其它常用的command

status (看看目前的狀態)
help (查看參數與指令)
terminate (關閉wpa_supplicant)
interface (查看有那些無線網卡介面)
list_networks (查看wpa_supplicant.conf檔裡的設定)
select_network (選擇不同的AP,id為AP代號0,1,2,3...)
enable_network 
disable_network 
remove_network (刪除AP資訊)
reconfigure (重新讀取wpa_supplicant.conf設定內容)
save_config (儲存寫入到wpa_supplicant.conf中,否責變更無效)
disconnect (斷線)
reconnect (重新連線)
scan (掃描附近的AP)
scan_results (印出附近AP的相關資訊)


2. search raspberry pi in subnet


用nmap就可以掃, 在windows底下要安裝

假如電腦的ip是 192.168.1.100/24
那麼就下這個command
nmap -sn 192.168.1.0/24

掃出來的結果裡, 可能有
Starting Nmap 6.40 ( http://nmap.org ) at 2014-03-10 12:46 GMT
Nmap scan report for hpprinter (192.168.1.2)
Host is up (0.00044s latency).
Nmap scan report for Gordons-MBP (192.168.1.4)
Host is up (0.0010s latency).
Nmap scan report for ubuntu (192.168.1.5)
Host is up (0.0010s latency).
Nmap scan report for raspberrypi (192.168.1.8)
Host is up (0.0030s latency).
Nmap done: 256 IP addresses (4 hosts up) scanned in 2.41 seconds

可以看到裡面有raspberrypi

NFC NDEF message format

1. NDEF message format


下圖是 NDEF mssage format 的示意圖, 1份 NDEF message 包含 >= 1 NDEF Record
1份 NDEF Record 包含 Header & Payload
Header包含好幾個欄位, 包括 TNF (Type Name Format)



1.1 Record Layout


每個 Record 前5個 bit 定義了record 在message中的位置

  • MB (Message Begin) : True when this is the first record in a message.
  • ME (Message End) : True when this is the last record in a message.
  • CF (Chunk Flag) : True when this record is chunked. (只用在NDEF message大到無法一次傳完)
  • SR (Short Record) : True if the short record format is used for payload length.
  • IL (ID Length is present) : True if the ID Length field is present.

如果 message 有多個 record, 那麼第一個 record 的 MB=1, ME = 0, 最後一個 record 的 MB = 0, ME = 1
如果 message 只有1個 record, 那個這個 record 的 MB=1, ME = 1

1.2 Type Name Format


TNF (Type Name Format) 佔 3 bits, 值為 0~7

  • 0 Empty : Empty record that has no type or payload
  • 1 Well-Known : NFC Forum RTD (Record Type Definition) specification 定義好的其中1種
  • 2 MIME media-type : Internet media type (defined in RFC 2046 )
  • 3 Absolute URI : URI (defined in RFC 3986 )
  • 4 External : User-defined value, based on rules in NFC Forum RTD specification
  • 5 Unknown : Type is unknown. Type length must be 0.
  • 6 Unchanged : Only for middle and terminating records of chunked payloads. Type length must be 0.
  • 7 Reserved : Reserved by the NFC Forum for future use.

常見的應用程式裡, TNF 01 (Well-Known) 以及 TNF 02 (MIME media-type) 最為常用
另外, TNF 04 (External) 也很常用, 包括了 Android App

1.3 Record Type Length


在 TNF & Layout Flag 之後是 1 byte 的 Payload Type Length
它定義了 Record Type 的長度
Record Type的用途是在TNF之外提供更細的分類, 而這個欄位的長度是可變動的
而 Record Type Length 定義這個欄位的長度

1.4 Payload Length


接著是 Payload Length, 它可能佔用1個 byte 或佔用 4個 byte
如果前面的 SR (Short Record) = 1, 那麼它佔用1個 byte
如果 SR = 0, 那麼它佔用4個byte

1.5 Record Type


Record Type,  RTD (Record Type Definition)進一步描述 Payload的內容, 這個

比如說, 當 TNF = 01 (Well-Known), 那麼 Record Type = "T" 表示 text, "U" 表示 URI, "Sp" 表示 Smart poster,
當 TNF = 02 (MIME media-type), 那麼 Record Type 可以描述 "text/html," "text/json," 以及 "image/gif."
當 TNF = 04 (External), Record Type 可以描述它是 Android APP

1.6 ID length &  ID (optional)


如果前面的 flag IL(ID Length is present)=1, 那麼就會有 ID length 以及 ID 欄位

Payload ID 是 optional field, 通常是 URL, 但也可以隨便放, 它的用途是讓應用程式提供相關資訊, 或是讓不同的 NDEF record reference 其它 record
因為是 optional, 所以也可以不放

1.7 Payload


Payload 沒有規定它的 format, 通常 NDEF library 在包NDEF record時, 是直接塞data進去而不做檢查, 所以可以加密, 也可以帶二進位資料












NFC Introduction

1. Introduction


RFID (Radio Frequency IDentification)

  • 通常用於 identification, 而不是 communication
  • RFID tags 可以帶少量的 data, 通常小於1000 bytes


NFC (Near Field Communication)

  • 交換更複雜的資料
  • NFC reader 可以讀 passive RFID tags, 也可以寫入部份的memory, 或是寫入標準格式的 RFID tags
  • 有效距離 <= 10cm, 低功耗, 其它裝置的干擾較小
  • 傳輸速率比wifi/bt低, 不適合需要高速傳輸的用途



2. RFID operating


Acting rules in RFID exchange:

  • initiator: 可能是 tag reader, 或是 tag reader/writer device
  • target: 也就是 tag, 當 initiator 嘗試連線時回應 UID (Unique IDentifier number)

Communication modes:

  • Passive RFID exchange: 用於 reader/writer 和 tag, 板上沒有power, 利用 radio power 回應 data
  • Active RFID exchanges: 用於接電的 reader/writer, 有效距離較長


RFID tag 在板上有少量的 memory <= 1KB,
initiator device 可以讀上面的 data
如果是 reader/writer device, 除了讀 data, 也可以寫 data 到 memory 上
(通常用於紀錄剩下多少錢之類的, 但現在都把database建在網路上, 然後用UID在網路找這筆資料)

ISO 制定了 很多 RFID 標準
不同的標準裡定義了 radio frequency, data transfer rate, data formats,...


3. NFC operating


NFC 和 RFID 一樣有 initiator & target
通常 target 是 programmable device, 可以做複雜的操作之後再回傳 data

Communication modes in NFC

  • passive communication mode: initiator 持續供電, 使 target 持續從 initiator 那邊吃電
  • active communication mode: initiator & target 都有電源

Operating modes in NFC

  • reader/writer, 可對 targer 讀寫
  • card emulator, 當它位於其它 NFC/RFID device field 裡
  • peer-to-peer mode, 可彼此交換 data


4. NFC data format


NFC devices 交換 data 的格式是 NDEF (NFC Data Exchange Format)
一份 NDEF message 包含 >=1 NDEF record
每個 NDEF record 包含 record type, a unique ID, length, payload
有幾個知名的 record type 是每個 NFC device 應該都會實作的:

  • Simple text records: 送text, 包括metadata像是language, encoding scheme, 通常不包含要求 target 要做的事
  • URIs: 送 network address, 通常預期 target 會送到對應的app, 像是browser
  • Smart Posters: 可以帶更多的 data, 像是 URIs, text, 通常 target 收到 Smart Poster record 會開啟 browser, SMS, email ap之類的
  • Signatures: 提供 trustworthy information

NDEF 是 NFC 與 RFID 最大的不同, 雖然 NFC 和許多 RFID protocol 都操作在 13.56 MHz
但 RFID protocol 沒有 common data format


5. NFC architecture




6. NFC Tag Types


NFC forum 定義了4種 NFC Tag Types:

Type 1

  • Based on ISO-14443A specification.
  • Can be read-only, or read/write capable.
  • 96 B ~ 2 KB memory.
  • Communication speed 106Kb.
  • No data collision protection.
  • Examples: Innovision Topaz, Broadcom BCM20203.


Type 2

  • Based on NXP/Philips Mifare Ultralight tag (ISO-14443A) specification.
  • Can be read-only, or read/write capable.
  • 96 B ~ 2 KB memory.
  • Communication speed 106Kb.
  • Anti-collision support.
  • Example: NXP Mifare Ultralight.


Type 3

  • Based on the Sony FeliCa tags (ISO-18092 and JIS-X-6319-4), without the encryption and authentication support that FeliCa affords.
  • Configured by factory to be read-only, or read/write capable.
  • Variable memory, up to 1MB per exchange.
  • Two communication speeds, 212 or 424Kbps.
  • Anti-collision support.
  • Example: Sony FeliCa.


Type 4

  • Based on NXP DESFire tag (ISO-14443A) specification.
  • Configured by factory to be read-only, or read/write capable.
  • 2, 4, or 8 KB of memory.
  • Variable memory, up to 32KB per exchange.
  • Three communication speeds: 106, 212, or 424Kbps.
  • Anti-collision support.
  • Example: NXP DESFire, SmartMX-JCOP.

另外一種雖然不在 NFC 定義裡, 但已經是目前最常見的 NFC tag type

Mifare Classic tag (ISO–14443A)

  • Memory options: 192, 768, or 3,584 bytes.
  • Communication speed 106 Kbps
  • Anti-collision support.
  • Examples: NXP Mifare Classic 1K, Mifare Classic 4K, Mifare Classic Mini.




2014/09/24

FreeRTOS queue management

1. Introduction


queue 用於 task 之間的溝通, 每個queue能放有限的item
item的大小稱為size, item的數量稱為length
當 queue 被建立時, length & size都會設定好

通常 queue 使用 FIFO, 資料從tail寫入, 從head讀出, (但也有可能從head寫入)
寫入的時候, 會copy data到queue裡面
讀出的時候, 會copy data出來, 並且queue的那一份會刪掉

queue 本身就像個物件, 會自行管理queue的相關功能,
所以多個task可以同時寫入同一個queue, 也可以多個task讀出同一個queue
queue通常有多個writer, 但不常有多個reader
( 這裡的writer指的是嘗試寫入queue的接口, reader指的是嘗試從queue讀出的接口 )

每當 task 嘗試從 queue裡讀出data, 需要設定 block time, 這是因為如果 queue 是空的, task就會進入block state, 直到 bloke time timeout 而回到 ready state, 或者是有其它 task / interrupt 寫入data到queue裡, 這樣也會讓 task 回到 ready state

如果 queue 有多個 reader, 並且有多個 task 都在等空的 queue, 那麼當有新的 data 進來, 只會有一個 task變成 ready state, queue會選出priority最高的task, 如果有多個task有一樣的priority, 那麼會選等最久的

當 task 嘗試寫入 data 到 queue裡, 也需要設定 block time, 這是因為如果 queue 滿了, task就會進入 block state, 直到 block time timeout 而回到 ready state, 或者這期間 queue 騰出空間而讓task回到 ready state

如果 queue 有多個 writer, 並且有多個 task 嘗試寫入滿的 queue, 那麼當空間騰出來時, priority最高的task會變成ready state, 如果有多個 task 有一樣的 priority, 那麼會選等最久的

2. using a queue


2.1 create a queue


xQueueHandle xQueueCreate( unsigned portBASE_TYPE uxQueueLength,
                           unsigned portBASE_TYPE uxItemSize
                           );

其中

  • uxQueueLength : max queue length
  • uxItemSize : item size, 單位是 byte

如果 RAM size 不夠,就會回傳 NULL,
如果成功, 回傳 queue handle

2.2 send data to queue


portBASE_TYPE xQueueSendToBack( xQueueHandle xQueue,
                                const void * pvItemToQueue,
                                portTickType xTicksToWait
                                );

xQueueSendToBack() 會把 item 放到 queue 的後面 (tail), xQueueSend() 跟 xQueueSendToBack() 是同樣的function

portBASE_TYPE xQueueSendToFront( xQueueHandle xQueue,
                                 const void * pvItemToQueue,
                                 portTickType xTicksToWait
                                 );

xQueueSendToFromt 把 data 送到 queue 的前端 (head)

其中它的參數 :

  • xTicksToWait : 如果想讓 send 一直等下去, 可以把 xTicksToWait 設成 portMAX_DELAY, 如果不想等, 就把 xTicksToWait 設成 0

可能的回傳值 :

  • pdPASS : 表示成功寫入, 即使task是在block time timeout之前寫入也算是成功寫入
  • errQUEUE_FULL : 失敗, 如果 xTicksToWait > 0, 那麼表示在 block time timeout之前都沒辦法寫入

2.3 receive data from queue


portBASE_TYPE xQueueReceive( xQueueHandle xQueue,
                             const void * pvBuffer,
                             portTickType xTicksToWait
                             );
xQueueReceive 從 queue head 讀一個 item, 並把 queue 裡的 item 刪掉

portBASE_TYPE xQueuePeek( xQueueHandle xQueue,
                          const void * pvBuffer,
                          portTickType xTicksToWait
                          );

xQueuePeek 從 queue head 讀一個item, 但不會刪掉 item

可能的回傳值 :

  • pdPASS : 表示成功讀出, 即使 task 在 block time timeout 之前讀出也算成功讀出
  • errQUEUE_EMPTY : 失敗, 如果 xTicksToWait > 0, 表示在 block time timeout 之前沒辦法讀出

2.4 query queue


unsigned portBASE_TYPE uxQueueMessagesWaiting( xQueueHandle xQueue );

uxQueueMessagesWaiting 可以拿到目前 queue 裡的 items 數量

要注意的是, 這個不能在interrupt裡面使用,
interrupt應該使用 uxQueueMessagesWaitingFromISR()

3. sample code


/* 宣告一個queue */
xQueueHandle xQueue;

static void vSenderTask( void *pvParameters )
{
    long lValueToSend;
    portBASE_TYPE xStatus;

    /* 我們把要寫入 queue 的資料從 task 的 parameter 帶入, 型態為 long */
    lValueToSend = ( long ) pvParameters;

    for( ;; )
    {
        /* 把data寫入queue, 第1個參數是要寫入的queue,
           第2個參數是要寫入的item pointer, 第3個參數是block time */
        xStatus = xQueueSendToBack( xQueue, &lValueToSend, 0 );
        if( xStatus != pdPASS )
        {
            vPrintString( "Could not send to the queue.\r\n" );
        }

        /* taskYIELD() 通知 scheduler switch 到別的task */
        taskYIELD();
    }
}

static void vReceiverTask( void *pvParameters )
{
    long lReceivedValue;
    portBASE_TYPE xStatus;
    const portTickType xTicksToWait = 100 / portTICK_RATE_MS;

    for( ;; )
    {
        /* 檢查 queue 裡面的 item 數量 */
        if( uxQueueMessagesWaiting( xQueue ) != 0 )
        {
            vPrintString( "Queue should have been empty!\r\n" );
        }

        /* 從 queue 裡讀出data, 第1個參數是要讀出data的queue,
           第2個參數是放 data 的 pointer, 第3個參數是 block time */
        xStatus = xQueueReceive( xQueue, &lReceivedValue, xTicksToWait );
        if( xStatus == pdPASS )
        {
            /* 成功讀出 data, 把它印出來 */
            vPrintStringAndNumber( "Received = ", lReceivedValue );
        }
        else
        {
            /* 100ms內沒有讀出data */
            vPrintString( "Could not receive from the queue.\r\n" );
        }
    }
}

int main( void )
{
    /* 建 queue, 設定成長度為5個item, item大小為 sizeof(long) */
    xQueue = xQueueCreate( 5, sizeof( long ) );
    if( xQueue != NULL )
    {
        /* 建2個 task, 分別對 queue 寫入 data, 
           data 來自於 task 的 parameter, 也就是100和200 */
        xTaskCreate( vSenderTask, "Sender1", 1000, ( void * ) 100, 1, NULL );
        xTaskCreate( vSenderTask, "Sender2", 1000, ( void * ) 200, 1, NULL );

        /* 建1個 task 從 queue 讀出 data, 它的priority比較高, 
           但是在 xQueueReceive(), 如果 queue 裡沒 data, 它就會進 block state */
        xTaskCreate( vReceiverTask, "Receiver", 1000, NULL, 2, NULL );

        vTaskStartScheduler();
    }
    else
    {
        /* 建 queue 失敗 */
    }

    /* we should never reach here */
    for( ;; );
}

4. store compound data


有時候對 receiver task 來說, 它需要知道 sender 是誰, 我們可以把 data type 設計成 struct, 並且裡面加上 sender information

另一種情況是, 如果我們要在 queue 裡放大的 data, 改成用指向該 data 的 pointer 會比較好, 這樣可以避免在寫入和讀出時 copy 大量的 data, 但是要注意 memory 的 allocation & release,