본문 바로가기

카테고리 없음

마라톤 기록 측정 시스템 개발 3 - PN532 NFC 통신 모듈

반응형

 

PN532 칩을 기반으로 하여 13.56MHz에서 근거리 통신에 사용되는 NFC 모듈입니다. UART, SPI, I2C 세 가지 통신 방법을 지원하는데, 그중 SPI, I2C 방법으로 사용해보도록 하겠습니다. 

 

상품 사양

- 작동 전압: 5V

- 작동 전류: 100(대기) ~ 120(사용) ~ 150(최대) mA

- 통신 가능 거리: 약 5cm

- 사용 칩셋: PN532

- 지원 통신 방법: UART, SPI, I2C

- 13.56mHz 비접촉식 통신 

 

 

 

 

 

이 사항을 먼저 읽어보고 진해하기로 합니다.

 

 

How to use the PN532 module to quickly recognize multiple cards 한글
 
 
PN532 모듈은 여러 카드를 빠르게 인식하기 위한 충돌 방지(Anti-collision) 기능을 지원합니다
. 이 기능은 여러 개의 태그가 동시에 리더기 범위 안에 들어왔을 때, 각 태그와 차례대로 통신하여 고유 ID(UID)를 읽어내는 기술입니다. 하지만 기본 예제 코드는 하나의 태그만 읽도록 설정되어 있어, 다중 카드 인식을 위해서는 추가적인 코드가 필요합니다. 
다음은 Adafruit PN532 라이브러리를 사용하여 여러 카드를 빠르게 인식하는 방법입니다. 
 
하드웨어 준비 및 연결
  1. PN532 모듈: I2C 또는 SPI 모드를 지원합니다. I2C 모드는 배선이 간단하고, SPI 모드는 더 빠를 수 있습니다. 대부분의 예제는 I2C 모드를 사용하며, Adafruit 보드는 기본적으로 I2C로 설정되어 있습니다.
  2. 마이크로컨트롤러: 아두이노, ESP32 등
  3. 점퍼 와이어 및 NFC 카드/태그 
I2C 연결 예시(아두이노 우노)
  • PN532 VCC  아두이노 5V
  • PN532 GND  아두이노 GND
  • PN532 SDA  아두이노 A4
  • PN532 SCL  아두이노 A5
  • PN532 IRQ  아두이노 D2 (인터럽트 사용 시. 응답성 향상에 도움) 
다중 카드 인식을 위한 코드
PN532는 InListPassiveTarget 명령을 사용하여 최대 2개의 카드를 동시에 감지할 수 있습니다. 감지된 카드는 HALT 상태로 전환되어, 다음 탐색 시 중복으로 인식되지 않도록 합니다. 다음은 이 원리를 적용한 아두이노 예제입니다.
 
1. 라이브러리 설치

아두이노 IDE의 라이브러리 매니저에서 Adafruit PN532  Adafruit BusIO를 설치합니다. 
 
2. 코드 작성

아래 코드는 두 개의 카드를 번갈아 감지하고, 이전에 감지된 카드는 HALT 상태로 만들어 다음 루프에서 다른 카드를 빠르게 찾도록 하는 로직을 포함합니다. 
 
cpp
#include <Wire.h>
#include <Adafruit_PN532.h>

#define PN532_IRQ   (2)
#define PN532_RESET (3)

// I2C 연결용
Adafruit_PN532 nfc(PN532_IRQ, PN532_RESET);

// 마지막으로 감지된 카드 UID를 저장할 변수
uint8_t lastUID[7];
uint8_t lastUIDLen = 0;

void setup(void) {
  Serial.begin(115200);
  Serial.println("PN532 Multi-Card Detection Test");
  nfc.begin();
  uint32_t versiondata = nfc.getFirmwareVersion();
  if (!versiondata) {
    Serial.print("PN53x 모듈을 찾을 수 없습니다!");
    while (1);
  }
  nfc.SAMConfig(); // 일반 모드로 설정
}

void loop(void) {
  uint8_t success;
  uint8_t uid[] = {0, 0, 0, 0, 0, 0, 0};
  uint8_t uidLength;

  // 카드 감지를 시작하고, 감지 성공 시 UID를 읽음
  success = nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uidLength);

  if (success) {
    // 마지막으로 감지된 카드와 UID가 다를 경우에만 처리
    if (uidLength != lastUIDLen || memcmp(uid, lastUID, uidLength) != 0) {
      Serial.println("카드를 감지했습니다!");
      Serial.print("UID: ");
      nfc.PrintHex(uid, uidLength);
      Serial.println();
      
      // 마지막 UID 업데이트
      memcpy(lastUID, uid, uidLength);
      lastUIDLen = uidLength;
      
      // 감지된 카드를 일시 중지(HALT) 상태로 만듦
      // 이렇게 하면 다음 탐색 시 다른 카드를 찾을 수 있음
      nfc.inRelease(uid, uidLength);
    }
  } else {
    // 범위 내에 카드가 없을 경우, 이전 UID 초기화
    if (lastUIDLen > 0) {
      Serial.println("모든 카드가 사라졌습니다.");
      memset(lastUID, 0, sizeof(lastUID));
      lastUIDLen = 0;
    }
  }
  delay(100); // 딜레이를 줄여서 빠르게 감지
}
코드를 사용할 때는 주의가 필요합니다.
 
코드 설명
  • nfc.readPassiveTargetID(): 이 함수는 카드를 탐색하고 UID를 읽어옵니다. 다중 카드 감지 시 충돌 방지 메커니즘을 내부적으로 처리합니다.
  • lastUID 변수: 이전에 감지된 카드의 UID를 저장하여, 새로운 카드가 감지될 때만 메시지를 출력하도록 합니다.
  • nfc.inRelease(): 이 코드는 Adafruit 라이브러리에는 존재하지 않으므로, 충돌 방지를 위한 추가적인 처리가 필요합니다. Adafruit 포럼에서는 InListPassiveTarget 명령에서 MaxTg를 2로 설정하면 첫 번째 카드를 HALT 상태로 만들지만, 이를 명시적으로 제어하는 예제는 잘 알려져 있지 않습니다.
  • 빠른 감지를 위한 방법:
    • IRQ 핀 사용: IRQ 핀을 연결하면 모듈이 카드 감지 시 인터럽트를 발생시키므로, 카드가 감지되었을 때만 프로세싱을 시작하여 대기 시간을 줄일 수 있습니다.
    • 짧은 delay(): loop() 함수 내의 delay()를 짧게 설정하면 폴링 주기를 단축하여 더 빠르게 감지할 수 있습니다.
    • 더 빠른 탐색: Adafruit 라이브러리의 기본 예제는 느릴 수 있습니다. 더 빠른 감지를 원한다면 InListPassiveTarget 명령어의 상세 옵션을 직접 제어하거나, 더 최적화된 라이브러리 또는 코드를 찾아야 합니다. 
추가적인 팁
 
  • 여러 리더기 사용: 여러 카드를 동시에, 그리고 빠르게 인식해야 하는 경우, 여러 개의 PN532 모듈을 사용하고 각각 다른 제어 핀(SPI SS 핀 등)을 할당하여 개별적으로 제어할 수 있습니다.
  • 카드 배치: 카드를 리더기에 겹치지 않도록 배치하면 충돌 방지 처리가 더 효율적으로 이루어질 수 있습니다.
  • 공식 문서 참고: NXP의 PN532 데이터시트를 참고하면 InListPassiveTarget 등 여러 명령에 대한 더 자세한 정보를 얻을 수 있습니다. 

 


 

연결 회로도

 

PN532 모듈은 통신 방법에 따라 측면의 스위치를 조작해야 합니다. I2C 통신을 사용한다면 왼쪽 사진과 같이 SET0을 H로, SET1을 L로 설정하고, SPI통신을 사용한다면 오른쪽 사진과 같이 SET0을 L로, SET1을 H로 설정해 줍니다. 

 

 

 

 

 

1. SPI 연결

 

아두이노
센서
D2
SCK
D3
MO/SDA/TX (MOSI)
D4
NSS/SCL/RX (SS)
D5
M (MISO)
5V
5V
GND
GND

 

 

2. I2C 연결

 

아두이노
센서
D2
IRQ
D3
RST
SDA (A4)
MO/SDA/TX (MOSI)
SCL (A5)
NSS/SCL/RX (SS)
5V
5V
GND
GND

 

 

샘플 코드

 

가장 먼저 아두이노 IDE2 개발 환경에서 PN532 라이브러리를 설치한다. 아래 이미지 참고

 

 

 

SPI 인터페이스 샘플 코드

 

/**************************************************************************/
/*! 
    @file     readMifare.pde
    @author   Adafruit Industries
	@license  BSD (see license.txt)

    This example will wait for any ISO14443A card or tag, and
    depending on the size of the UID will attempt to read from it.
   
    If the card has a 4-byte UID it is probably a Mifare
    Classic card, and the following steps are taken:
   
    - Authenticate block 4 (the first block of Sector 1) using
      the default KEYA of 0XFF 0XFF 0XFF 0XFF 0XFF 0XFF
    - If authentication succeeds, we can then read any of the
      4 blocks in that sector (though only block 4 is read here)
	 
    If the card has a 7-byte UID it is probably a Mifare
    Ultralight card, and the 4 byte pages can be read directly.
    Page 4 is read by default since this is the first 'general-
    purpose' page on the tags.


This is an example sketch for the Adafruit PN532 NFC/RFID breakout boards
This library works with the Adafruit NFC breakout 
  ----> https://www.adafruit.com/products/364
 
Check out the links above for our tutorials and wiring diagrams 
These chips use SPI or I2C to communicate.

Adafruit invests time and resources providing this open source code, 
please support Adafruit and open-source hardware by purchasing 
products from Adafruit!

*/
/**************************************************************************/
#include <Wire.h>
#include <SPI.h>
#include <Adafruit_PN532.h>

// If using the breakout with SPI, define the pins for SPI communication.
#define PN532_SCK  (2)
#define PN532_MOSI (3)
#define PN532_SS   (4)
#define PN532_MISO (5)

// If using the breakout or shield with I2C, define just the pins connected
// to the IRQ and reset lines.  Use the values below (2, 3) for the shield!
#define PN532_IRQ   (2)
#define PN532_RESET (3)  // Not connected by default on the NFC Shield

// Uncomment just _one_ line below depending on how your breakout or shield
// is connected to the Arduino:

// Use this line for a breakout with a software SPI connection (recommended):
Adafruit_PN532 nfc(PN532_SCK, PN532_MISO, PN532_MOSI, PN532_SS);

// Use this line for a breakout with a hardware SPI connection.  Note that
// the PN532 SCK, MOSI, and MISO pins need to be connected to the Arduino's
// hardware SPI SCK, MOSI, and MISO pins.  On an Arduino Uno these are
// SCK = 13, MOSI = 11, MISO = 12.  The SS line can be any digital IO pin.
//Adafruit_PN532 nfc(PN532_SS);

// Or use this line for a breakout or shield with an I2C connection:
//Adafruit_PN532 nfc(PN532_IRQ, PN532_RESET);

#if defined(ARDUINO_ARCH_SAMD)
// for Zero, output on USB Serial console, remove line below if using programming port to program the Zero!
// also change #define in Adafruit_PN532.cpp library file
   #define Serial SerialUSB
#endif

void setup(void) {
  #ifndef ESP8266
    while (!Serial); // for Leonardo/Micro/Zero
  #endif
  Serial.begin(115200);
  Serial.println("Hello!");

  nfc.begin();

  uint32_t versiondata = nfc.getFirmwareVersion();
  if (! versiondata) {
    Serial.print("Didn't find PN53x board");
    while (1); // halt
  }
  // Got ok data, print it out!
  Serial.print("Found chip PN5"); Serial.println((versiondata>>24) & 0xFF, HEX); 
  Serial.print("Firmware ver. "); Serial.print((versiondata>>16) & 0xFF, DEC); 
  Serial.print('.'); Serial.println((versiondata>>8) & 0xFF, DEC);
  
  // configure board to read RFID tags
  nfc.SAMConfig();
  
  Serial.println("Waiting for an ISO14443A Card ...");
}


void loop(void) {
  uint8_t success;
  uint8_t uid[] = { 0, 0, 0, 0, 0, 0, 0 };  // Buffer to store the returned UID
  uint8_t uidLength;                        // Length of the UID (4 or 7 bytes depending on ISO14443A card type)
    
  // Wait for an ISO14443A type cards (Mifare, etc.).  When one is found
  // 'uid' will be populated with the UID, and uidLength will indicate
  // if the uid is 4 bytes (Mifare Classic) or 7 bytes (Mifare Ultralight)
  success = nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uidLength);
  
  if (success) {
    // Display some basic information about the card
    Serial.println("Found an ISO14443A card");
    Serial.print("  UID Length: ");Serial.print(uidLength, DEC);Serial.println(" bytes");
    Serial.print("  UID Value: ");
    nfc.PrintHex(uid, uidLength);
    Serial.println("");
    
    if (uidLength == 4)
    {
      // We probably have a Mifare Classic card ... 
      Serial.println("Seems to be a Mifare Classic card (4 byte UID)");
	  
      // Now we need to try to authenticate it for read/write access
      // Try with the factory default KeyA: 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
      Serial.println("Trying to authenticate block 4 with default KEYA value");
      uint8_t keya[6] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
	  
	  // Start with block 4 (the first block of sector 1) since sector 0
	  // contains the manufacturer data and it's probably better just
	  // to leave it alone unless you know what you're doi

 

 

I2C 인터페이스 샘플 코드

 

 

/**************************************************************************/
/*! 
    @file     readMifare.pde
    @author   Adafruit Industries
	@license  BSD (see license.txt)

    This example will wait for any ISO14443A card or tag, and
    depending on the size of the UID will attempt to read from it.
   
    If the card has a 4-byte UID it is probably a Mifare
    Classic card, and the following steps are taken:
   
    - Authenticate block 4 (the first block of Sector 1) using
      the default KEYA of 0XFF 0XFF 0XFF 0XFF 0XFF 0XFF
    - If authentication succeeds, we can then read any of the
      4 blocks in that sector (though only block 4 is read here)
	 
    If the card has a 7-byte UID it is probably a Mifare
    Ultralight card, and the 4 byte pages can be read directly.
    Page 4 is read by default since this is the first 'general-
    purpose' page on the tags.


This is an example sketch for the Adafruit PN532 NFC/RFID breakout boards
This library works with the Adafruit NFC breakout 
  ----> https://www.adafruit.com/products/364
 
Check out the links above for our tutorials and wiring diagrams 
These chips use SPI or I2C to communicate.

Adafruit invests time and resources providing this open source code, 
please support Adafruit and open-source hardware by purchasing 
products from Adafruit!

*/
/**************************************************************************/
#include <Wire.h>
#include <SPI.h>
#include <Adafruit_PN532.h>

// If using the breakout with SPI, define the pins for SPI communication.
#define PN532_SCK  (2)
#define PN532_MOSI (3)
#define PN532_SS   (4)
#define PN532_MISO (5)

// If using the breakout or shield with I2C, define just the pins connected
// to the IRQ and reset lines.  Use the values below (2, 3) for the shield!
#define PN532_IRQ   (2)
#define PN532_RESET (3)  // Not connected by default on the NFC Shield

// Uncomment just _one_ line below depending on how your breakout or shield
// is connected to the Arduino:

// Use this line for a breakout with a software SPI connection (recommended):
//Adafruit_PN532 nfc(PN532_SCK, PN532_MISO, PN532_MOSI, PN532_SS);

// Use this line for a breakout with a hardware SPI connection.  Note that
// the PN532 SCK, MOSI, and MISO pins need to be connected to the Arduino's
// hardware SPI SCK, MOSI, and MISO pins.  On an Arduino Uno these are
// SCK = 13, MOSI = 11, MISO = 12.  The SS line can be any digital IO pin.
//Adafruit_PN532 nfc(PN532_SS);

// Or use this line for a breakout or shield with an I2C connection:
Adafruit_PN532 nfc(PN532_IRQ, PN532_RESET);

#if defined(ARDUINO_ARCH_SAMD)
// for Zero, output on USB Serial console, remove line below if using programming port to program the Zero!
// also change #define in Adafruit_PN532.cpp library file
   #define Serial SerialUSB
#endif

void setup(void) {
  #ifndef ESP8266
    while (!Serial); // for Leonardo/Micro/Zero
  #endif
  Serial.begin(115200);
  Serial.println("Hello!");

  nfc.begin();

  uint32_t versiondata = nfc.getFirmwareVersion();
  if (! versiondata) {
    Serial.print("Didn't find PN53x board");
    while (1); // halt
  }
  // Got ok data, print it out!
  Serial.print("Found chip PN5"); Serial.println((versiondata>>24) & 0xFF, HEX); 
  Serial.print("Firmware ver. "); Serial.print((versiondata>>16) & 0xFF, DEC); 
  Serial.print('.'); Serial.println((versiondata>>8) & 0xFF, DEC);
  
  // configure board to read RFID tags
  nfc.SAMConfig();
  
  Serial.println("Waiting for an ISO14443A Card ...");
}


void loop(void) {
  uint8_t success;
  uint8_t uid[] = { 0, 0, 0, 0, 0, 0, 0 };  // Buffer to store the returned UID
  uint8_t uidLength;                        // Length of the UID (4 or 7 bytes depending on ISO14443A card type)
    
  // Wait for an ISO14443A type cards (Mifare, etc.).  When one is found
  // 'uid' will be populated with the UID, and uidLength will indicate
  // if the uid is 4 bytes (Mifare Classic) or 7 bytes (Mifare Ultralight)
  success = nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uidLength);
  
  if (success) {
    // Display some basic information about the card
    Serial.println("Found an ISO14443A card");
    Serial.print("  UID Length: ");Serial.print(uidLength, DEC);Serial.println(" bytes");
    Serial.print("  UID Value: ");
    nfc.PrintHex(uid, uidLength);
    Serial.println("");
    
    if (uidLength == 4)
    {
      // We probably have a Mifare Classic card ... 
      Serial.println("Seems to be a Mifare Classic card (4 byte UID)");
	  
      // Now we need to try to authenticate it for read/write access
      // Try with the factory default KeyA: 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
      Serial.println("Trying to authenticate block 4 with default KEYA value");
      uint8_t keya[6] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
	  
	  // Start with block 4 (the first block of sector 1) since sector 0
	  // contains the manufacturer data and it's probably better just
	  // to leave it alone unless you know what you're doi

 

 

 

위 설명과 코드 참고 링크는 여기를 클릭하세요.

 

 

반응형

더욱 좋은 정보를 제공하겠습니다.~ ^^