wtorek, 13 marca 2018

[21] STM32F7 - Obsługa układu PN532 - UART

Ten post chciałbym poświęcić na odczytanie numeru karty oraz sektorów znajdujących się na karcie Mifare za pomocą układu PN532. Do komunikacji z układem wykorzystałem USART. Dane odczytane jak i statusy operacji będą wyświetlane poprzez UART.

[Źródło: http://www.st.com/en/evaluation-tools/32f746gdiscovery.html]

Datasheet z komendami dla układu można pobrać pod tym linkiem.

Opisywany w tym poście moduł pozwala na obsługę takich kart jak Mifare 1k, 4k, Ultralight, DesFire, ISO/IED 14443-4(CD97BX, CD ligiht, Desfire, P5CN072), Innovision Jewel (IRT5001) czy FeliCa (RCS_860, RCS_854). Dodatkowo można komunikować się z innymi urządzeniami poprzez NFC, bądź w trybie P2P pomiędzy dwoma urządzeniami z układem PN.


Podłączenie:



Układ PN532 można obsługiwać za pomocą takich interfejsów jak SPI, I2C czy wykorzystywany w tym przykładzie UART. Wobec tego należy podłączyć zasilanie oraz TX i RX do komunikacji.


Protokół komunikacyjny:



Komendy możliwe do przesłania to:

Ogólne:

  • Diagnose - 0x00
  • GetFirmwareVersion - 0x02
  • GetGeneralStatus - 0x04
  • ReadRegister - 0x06
  • WriteRegister - 0x08
  • ReadGpio - 0x0C
  • WriteGpio - 0x0E
  • SetSerialBaudrate - 0x10
  • SetParameters - 0x12
  • SAMConfiguration - 0x14
  • PowerDown - 0x16

Komunikacja RF:

  • RFConfiguration - 0x32
  • RFRegulationTest - 0x58

Initiator:

  • InJumpForDEP - 0x56
  • InJumpForPSL - 0x46
  • InListPassiveTarget - 0x4A
  • InATR - 0x50
  • InPSL - 0x4E
  • InDataExchange - 0x40
  • InCommunicateThru - 0x42
  • InDeselect - 0x44
  • InRelease - 0x52
  • InSelect - 0x54
  • InAutoPoll - 0x60

Target:

  • TgInitAsTarget - 0x8C
  • TgSetGeneralBytes - 0x92
  • TgGetData - 0x86
  • TgSetData - 0x8E
  • TgSetMeteData - 0x94
  • TgGetInitiatorCommand - 0x88
  • TgResponseToInitiator - 0x90
  • TgGetTargetStatus - 0x8A


Programowanie:



Poniżej przejdę przez przygotowane funkcje. Obsługi UART-y nie będę tutaj opisywał. Wszystkie potrzebne pliki będą dostępne na dysku sieciowym.

Funkcja rozpoczynająca komunikację z układem:

Na samym początku do układu wysyła się komendę wybudzającą. Po niej idzie ACK.

Przesłanie komendy wybudzającej układ:

  1. void pn532_sendWakeUpFrame()
  2. {
  3.     uint8_t pn532_wakeUpFrame[] = {PN532_WAKEUP, PN532_WAKEUP, 0x00, 0x00, 0x00,
  4.             0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  5.             0x00, 0xFF, 0x03, 0xFD, 0xD4, 0x14, 0x01, 0x17, 0x00};
  6.     usart_SendArray(PN532_USART, pn532_wakeUpFrame, sizeof(pn532_wakeUpFrame)/sizeof(pn532_wakeUpFrame[0]));
  7. HAL_Delay(10);
  8. }

Przesłanie komendy ACK:

  1. uint8_t pn532_AckFrame[] = {0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00};
  2. static void pn532_sendACKFrame()
  3. {
  4.     usart_SendArray(PN532_USART, pn532_AckFrame, sizeof(pn532_AckFrame)/sizeof(pn532_AckFrame[0]));
  5. }

Jest to dwukierunkowa ramka używa do synchronizacji bądź do przerwania danych.

Druga ramka NACK wysyłana jest do synchronizacji pakietów. Przesyłana tylko przez kontroler do PN532. Informuje ona, że poprzednia odpowiedź nie została poprawnie odczytana i musi nastąpić retransmisja.

Wpisanie danych do modułu:

  1. static void pn532_writeCommandFrame(uint8_t* commandBuffer, uint8_t commandLength)
  2. {
  3.     uint8_t calcChecksum = 0;
  4.     uint8_t bufferToSend[8 + commandLength];
  5.     bufferToSend[0] = PN532_PREAMBLE;
  6.     bufferToSend[1] = PN532_STARTCODE1;
  7.     bufferToSend[2] = PN532_STARTCODE2;
  8.     bufferToSend[3] = commandLength+1;
  9.     bufferToSend[4] = ~commandLength;
  10.     bufferToSend[5] = PN532_HOSTTOPN532;
  11.     calcChecksum += PN532_HOSTTOPN532;
  12.     for (uint8_t i=6; i<6+commandLength; i++)
  13.     {
  14.         bufferToSend[i]=(*commandBuffer);
  15.         calcChecksum += (*commandBuffer);
  16.         commandBuffer++;
  17.     }
  18.     bufferToSend[6+commandLength]   = (~calcChecksum+1);
  19.     bufferToSend[6+commandLength+1] = PN532_POSTAMBLE;
  20.     pn532_sendBiggerBuffer(bufferToSend,sizeof(bufferToSend)/sizeof(bufferToSend[0]));
  21. }

Funkcja powyżej przygotowuje komendę do przesłania. Podaje się do niej wskaźnik do tablicy z danymi do wpisania oraz jej długość.

Wprowadzenie prostej komendy do układu:

  1. static void pn532_writeCommand(uint8_t command)
  2. {
  3.     uint8_t calcChecksum = 0;
  4.     uint8_t bufferToSend[9];
  5.     bufferToSend[0] = PN532_PREAMBLE;
  6.     bufferToSend[1] = PN532_STARTCODE1;
  7.     bufferToSend[2] = PN532_STARTCODE2;
  8.     bufferToSend[3] = 2;
  9.     bufferToSend[4] = 0xFE;
  10.     bufferToSend[5] = PN532_HOSTTOPN532;
  11.     bufferToSend[6] = command;
  12.     calcChecksum += PN532_HOSTTOPN532;
  13.     calcChecksum += command;
  14.     bufferToSend[7] = (~calcChecksum+1);
  15.     bufferToSend[8] = PN532_POSTAMBLE;
  16.     usart_SendArray(PN532_USART, bufferToSend, 9);
  17. }

Tutaj podaje się tylko kod komendy dla jakiej ma być przygotowana ramka.

Odczytanie oprogramowania z modułu:

  1. static pn532_OperationStatus_t pn532_WriteFirmToBufferCheckValue(uint8_t* globalBuffer, uint8_t* buffer, uint8_t bufferLen, uint8_t position)
  2. {
  3.     /* Write firmware data to buffer */
  4.     for(uint8_t i=0; i<bufferLen; i++)
  5.     {
  6.         *(buffer+i) = *(globalBuffer + position + 2 + i);
  7.     }
  8.     uint32_t receiveFirmwareVersion = (*(buffer) << 24) | (*(buffer + 1) << 16) |
  9.                                                 (*(buffer + 2) << 8) | (*(buffer + 3));
  10.     if(FIRMWARE_VERSION == receiveFirmwareVersion)
  11.     {
  12.         return PN532_OK;
  13.     }
  14.     return PN532_DIFF_FIRM_VERSION;
  15. }
  16. pn532_OperationStatus_t pn532_ReadFirmawareVersion(uint8_t* buffer, uint8_t bufferLen, uint16_t timeout)
  17. {
  18.     clearBuffIncrVar();
  19.     pn532_clearGlobRecUARTBuffer();
  20.     uint8_t command[1] = {PN532_COMMAND_GETFIRMWAREVERSION};
  21.     uint8_t responseFrame[] = { PN532_PN532TOHOST, PN532_COMMAND_GETFIRMWAREVERSION + 1};
  22.     if(pn532_writeCommandFrameCheckAck(command, 1, timeout, 1) == PN532_OK)
  23.     {
  24.         pn532_DebugMsg(USART1, "Firmware Version - CheckedData - OK");
  25.     }
  26.     else
  27.     {
  28.         pn532_DebugMsg(USART1, "Firmware Version - CheckedData - ERROR");
  29.         return PN532_ERROR;
  30.     }
  31.     glob_pn532_SearchFunctPointer = pn532_SearchFrameForBuffer;
  32.     if( pn532_searchInFrame(glob_pn532_SearchFunctPointer, responseFrame,
  33.         sizeof(responseFrame)/sizeof(responseFrame[0]), timeout) == PN532_OK)
  34.     {
  35.         pn532_DebugMsg(USART1, "Firmware Version - ReadData - OK");
  36.     }
  37.     else
  38.     {
  39.         pn532_DebugMsg(USART1, "Firmware Version - ReadData - ERROR");
  40.         return PN532_ERROR;
  41.     }
  42.     pn532_sendACKFrame();
  43.     if(bufferLen < 4)   { return PN532_MIC_SOFT_ERROR; }
  44.     else                { bufferLen = 4;               }
  45.     uint8_t positionInBuf = pn532_SearchFrameForBuffer(_USART6_Buffer.Buffer, responseFrame, 2);
  46.     pn532_OperationStatus_t operatStatus = pn532_WriteFirmToBufferCheckValue(_USART6_Buffer.Buffer, buffer,
  47.                                                                                 bufferLen, positionInBuf);
  48.     return operatStatus;
  49. }

Tutaj są dwie funkcje jedna odczytuje wersje oprogramowania przekazuje wartość do drugiej, która wprowadza dane do podanego bufora a następnie sprawdza czy wartość zgadza się z wartością podaną w definicji.

Zmiana prędkości UART:

  1. pn532_OperationStatus_t PN532_ChangeBaudrate(PN532_BaudTypeDef baudRate, uint16_t timeout)
  2. {
  3.     pn532_ClearBufferPosition();
  4.     pn532_ClearGlobRecUARTBuffer();
  5.     uint8_t changeBaudRateFrame[] = { PN532_COMMAND_SETSERIALBAUDRATE, baudRate };
  6.     uint8_t responseFrame[] = { PN532_PN532TOHOST, PN532_COMMAND_SETSERIALBAUDRATE + 1};
  7.     if(pn532_writeCommandFrameCheckAck(changeBaudRateFrame, 2, timeout, 0) == PN532_OK)
  8.     {
  9.         pn532_DebugMsg(USART1, "Change Baudrate - CheckedData - OK");
  10.     }
  11.     else
  12.     {
  13.         pn532_DebugMsg(USART1, "Change Baudrate - CheckedData - ERROR");
  14.         return PN532_ERROR;
  15.     }
  16.     glob_pn532_SearchFunctPointer = pn532_SearchFrameForBuffer;
  17.     if( pn532_searchInFrame(glob_pn532_SearchFunctPointer, responseFrame,
  18.         sizeof(responseFrame)/sizeof(responseFrame[0]), timeout) == PN532_OK)
  19.     {
  20.         pn532_DebugMsg(USART1, "Change baudrate - ReadData - OK");
  21.     }
  22.     else
  23.     {
  24.         pn532_DebugMsg(USART1, "Change baudrate - ReadData - ERROR");
  25.         return PN532_ERROR;
  26.     }
  27.     pn532_sendACKFrame();
  28.     return PN532_OK;
  29. }

Domyślnie układ ma ustawiony baudrate na 115200. Możliwe jest ustawienie innych prędkości. Do wyboru są:

  1. typedef enum
  2. {
  3.   PN532_9600     = 0x00U,
  4.   PN532_19200    = 0x01U,
  5.   PN532_38400    = 0x02U,
  6.   PN532_57600    = 0x03U,
  7.   PN532_115200   = 0x04U,
  8.   PN532_230400   = 0x05U,
  9.   PN532_460800   = 0x06U,
  10.   PN532_921600   = 0x07U,
  11.   PN532_1288000  = 0x08U
  12. } PN532_BaudRate_t;

Teraz funkcja diagnozująca układ:

  1. pn532_OperationStatus_t PN532_Diagnose(PN532_TestNumb_t testCode, uint16_t timeout)
  2. {
  3.     pn532_ClearBufferPosition();
  4.     pn532_ClearGlobRecUARTBuffer();
  5.     uint8_t diagnoseFrame[] = { PN532_COMMAND_DIAGNOSE, testCode };
  6.     uint8_t responseFrame[] = { PN532_PN532TOHOST, PN532_COMMAND_DIAGNOSE + 1 };
  7.     if(pn532_writeCommandFrameCheckAck(diagnoseFrame, sizeof(diagnoseFrame)/sizeof(diagnoseFrame[0]),
  8.                                                                                         timeout, 0) == PN532_OK)
  9.     {
  10.         pn532_DebugMsg(USART1, "Diagnose - CheckedData - OK");
  11.     }
  12.     else
  13.     {
  14.         pn532_DebugMsg(USART1, "Diagnose - CheckedData - ERROR");
  15.         return PN532_ERROR;
  16.     }
  17.     glob_pn532_SearchFunctPointer = pn532_SearchFrameForBuffer;
  18.     if( pn532_searchInFrame(glob_pn532_SearchFunctPointer, responseFrame,
  19.         sizeof(responseFrame)/sizeof(responseFrame[0]), timeout) == PN532_OK)
  20.     {
  21.         pn532_DebugMsg(USART1, "Diagnose - ReadData - OK");
  22.     }
  23.     else
  24.     {
  25.         pn532_DebugMsg(USART1, "Diagnose - ReadData - ERROR");
  26.         return PN532_ERROR;
  27.     }
  28.     pn532_sendACKFrame();
  29.     uint8_t positionInBuf = pn532_SearchFrameForBuffer(_USART6_Buffer.Buffer, responseFrame,
  30.                                             sizeof(responseFrame)/sizeof(responseFrame[0]));
  31.     PN532_CheckDiagnoseFrame(testCode, _USART6_Buffer.Buffer, positionInBuf);
  32.     return PN532_OK;
  33. }

Powyższa komenda ma za zadanie przeprowadzić test układu. Jako parametr podaje się numer testu jaki ma zostać wykonany. Tutaj znajduje się np. test komunikacji, pamięci ROM, RAM.

Wykonanie komendy getGeneralStatus:

  1. pn532_OperationStatus_t PN532_GetGeneralStatus(uint8_t* dataBuffer, uint16_t bufferLen, uint16_t timeout)
  2. {
  3.     pn532_ClearBufferPosition();
  4.     pn532_ClearGlobRecUARTBuffer();
  5.     uint8_t generalStatusFrame[1] = {PN532_COMMAND_GETGENERALSTATUS};
  6.     uint8_t responseFrame[] = { PN532_PN532TOHOST,PN532_COMMAND_GETGENERALSTATUS + 1};
  7.     if(pn532_writeCommandFrameCheckAck(generalStatusFrame, sizeof(generalStatusFrame)/sizeof(generalStatusFrame[0]),
  8.                                                                                             timeout, 1) == PN532_OK)
  9.     {
  10.         pn532_DebugMsg(USART1, "GetGeneralStatus - CheckedData - OK");
  11.     }
  12.     else
  13.     {
  14.         pn532_DebugMsg(USART1, "GetGeneralStatus - CheckedData - ERROR");
  15.         return PN532_ERROR;
  16.     }
  17.     glob_pn532_SearchFunctPointer = pn532_SearchFrameForBuffer;
  18.     if( pn532_searchInFrame(glob_pn532_SearchFunctPointer, responseFrame,
  19.             sizeof(responseFrame)/sizeof(responseFrame[0]), timeout) == PN532_OK)
  20.     {
  21.         pn532_DebugMsg(USART1, "GetGeneralStatus - ReadData - OK");
  22.     }
  23.     else
  24.     {
  25.         pn532_DebugMsg(USART1, "GetGeneralStatus - ReadData - ERROR");
  26.         return PN532_ERROR;
  27.     }
  28.     pn532_sendACKFrame();
  29.     volatile uint8_t positionInBuf = pn532_SearchFrameForBuffer(busart.Buffer, responseFrame,
  30.                                             sizeof(responseFrame)/sizeof(responseFrame[0]));
  31.     pn532_decodeGeneralStatus(busart.Buffer, positionInBuf);
  32.     return PN532_OK;
  33. }

Dzięki tej komendzie można odczytać pełny status układu. W odpowiedzi otrzymuje się np. kody błędów, status pola RF, liczbę tagów itp.

Komenda Power Down:

  1. pn532_OperationStatus_t PN532_PowerDown(uint8_t wakeUpEnable, uint16_t timeout)
  2. {
  3.     pn532_ClearBufferPosition();
  4.     pn532_ClearGlobRecUARTBuffer();
  5.     uint8_t powerDownFrame[] = { PN532_COMMAND_POWERDOWN, wakeUpEnable };
  6.     uint8_t responsePowerDownFrame[] = { PN532_PN532TOHOST, PN532_COMMAND_POWERDOWN + 1};
  7.     if(pn532_writeCommandFrameCheckAck(powerDownFrame, 2, timeout, 0) == PN532_OK)
  8.     {
  9.         pn532_DebugMsg(USART1, "Power Down - CheckedData - OK");
  10.     }
  11.     else
  12.     {
  13.         pn532_DebugMsg(USART1, "Power Down - CheckedData - ERROR");
  14.         return PN532_ERROR;
  15.     }
  16.    
  17.     glob_pn532_SearchFunctPointer = pn532_SearchFrameForBuffer;
  18.     if( pn532_searchInFrame(glob_pn532_SearchFunctPointer, responsePowerDownFrame,
  19.                                 sizeof(responsePowerDownFrame)/sizeof(responsePowerDownFrame[0]), timeout) == PN532_OK)
  20.     {
  21.         pn532_DebugMsg(USART1, "Power Down - ReadData - OK");
  22.     }
  23.     else
  24.     {
  25.         pn532_DebugMsg(USART1, "Power Down - ReadData - ERROR");
  26.         return PN532_ERROR;
  27.     }
  28.    
  29.     HAL_Delay(1);
  30.     return PN532_OK;
  31. }

Komenda wykorzystywana do zmniejszenia zużycia prądu. W argumencie podaje się źródło wybudzenia oraz opcjonalnie decyduje on czy układ ma obsługiwać pin P70_IRQ.

Realizacja komendy InAutoPoll:

  1. pn532_OperationStatus_t PN532_InAutoPoll(uint8_t* buffer, uint16_t bufferLen, uint16_t timeout)
  2. {
  3.     pn532_ClearBufferPosition();
  4.     pn532_ClearGlobRecUARTBuffer();
  5.     uint8_t inAutoPollFrame[] = { PN532_COMMAND_INAUTOPOLL, 0x01, 0x01, 0x10 };
  6.     uint8_t responseInAutoPollFrame[] = { PN532_PN532TOHOST, PN532_COMMAND_INAUTOPOLL + 1 };
  7.     uint8_t endResponseFrame[] = { 0x00, 0xFF, 0xFF, 0xFF, 0xFF };
  8.     if(pn532_writeCommandFrameCheckAck(inAutoPollFrame, sizeof(inAutoPollFrame)/sizeof(inAutoPollFrame[0]),
  9.                                                                                             timeout, 0) == PN532_OK)
  10.     {
  11.         pn532_DebugMsg(USART1, "AutoPoll - CheckedData - OK");
  12.     }
  13.     else
  14.     {
  15.         pn532_DebugMsg(USART1, "InAutoPoll - CheckedData - ERROR");
  16.         return PN532_ERROR;
  17.     }
  18.     glob_pn532_SearchFunctPointer = pn532_SearchFrameForBuffer;
  19.     if( pn532_searchInFrame(glob_pn532_SearchFunctPointer, responseInAutoPollFrame,
  20.                         sizeof(responseInAutoPollFrame)/sizeof(responseInAutoPollFrame[0]), timeout) == PN532_OK)
  21.     {
  22.         pn532_DebugMsg(USART1, "InAutoPoll - ReadData - OK");
  23.     }
  24.     else
  25.     {
  26.         pn532_DebugMsg(USART1, "InAutoPoll - ReadData - ERROR");
  27.         return PN532_ERROR;
  28.     }
  29.     HAL_Delay(10);
  30.     glob_pn532_SearchFunctPointer = pn532_SearchFrameForBuffer;
  31.     if( pn532_searchInFrame(glob_pn532_SearchFunctPointer, endResponseFrame,
  32.                         sizeof(endResponseFrame)/sizeof(endResponseFrame[0]), timeout) == PN532_OK)
  33.     {
  34.         pn532_DebugMsg(USART1, "InAutoPoll - ReadData - OK");
  35.     }
  36.     else
  37.     {
  38.         pn532_DebugMsg(USART1, "InAutoPoll - ReadEndData - ERROR");
  39.         return PN532_ERROR;
  40.     }
  41.     pn532_sendACKFrame();
  42.     uint8_t startRecBufferPosition = pn532_SearchFrameForBuffer(busart.Buffer, responseInAutoPollFrame, 2);
  43.     uint16_t messageLength = *(busart.Buffer + startRecBufferPosition - 2);
  44.    
  45.     for(uint8_t i=2; i<messageLength; i++)
  46.     {
  47.         *(buffer + i - 2) = *(busart.Buffer + startRecBufferPosition + i);
  48.     }
  49.    
  50.     return PN532_OK;
  51. }

Funkcja powyżej ma za zadanie pobieranie danych z karty. Zwraca odczytany typ karty wraz z numerem.

Następną komendą jest InDataExchange:

  1. uint8_t PN532_InDataExchange(   uint8_t* dataToSend, uint16_t dataToSendLen,
  2.                                 uint8_t Tg, uint8_t* responseBuffer,
  3.                                 uint16_t responseBufferLen, uint16_t timeout    )
  4. {
  5.     pn532_ClearBufferPosition();
  6.     pn532_ClearGlobRecUARTBuffer();
  7.     uint8_t* bufferToSend_point = malloc(dataToSendLen+2);  /* Prepre memmory for data buffer */
  8.     uint8_t responseInDataExchangeFrame[] = { PN532_PN532TOHOST, PN532_COMMAND_INDATAEXCHANGE + 1};
  9.     uint8_t responseInDataExchangeTailFrame[] = { 0x00, 0xFF, 0xFF, 0xFF, 0xFF };
  10.     /* Check if Tg and data is correct */
  11.     if((Tg > 2) || (responseBufferLen >= 261))
  12.     {
  13.         return PN532_ERROR;
  14.     }
  15.     /* Prepare data buffer */
  16.     *bufferToSend_point = PN532_COMMAND_INDATAEXCHANGE;
  17.     *(bufferToSend_point + 1) = Tg;
  18.     /* write data into buffer */
  19.     for(uint8_t i=0; i<dataToSendLen; i++)
  20.     {
  21.         *(bufferToSend_point + 2 + i) = *(dataBuffer + i);
  22.     }
  23.     if(pn532_writeCommandFrameCheckAck(bufferToSend_point, dataToSendLen + 2, timeout, 0) == PN532_OK)
  24.     {
  25.         pn532_DebugMsg(USART1, "InDataExchange - CheckedData - OK");
  26.     }
  27.     else
  28.     {
  29.         pn532_DebugMsg(USART1, "InDataExchange - CheckedData - ERROR");
  30.         return PN532_ERROR;
  31.     }
  32.     /* search for start frame */
  33.     glob_pn532_SearchFunctPointer = pn532_SearchFrameForBuffer;
  34.     if( pn532_searchInFrame(glob_pn532_SearchFunctPointer, responseInDataExchangeFrame,
  35.                         sizeof(responseInDataExchangeFrame)/sizeof(responseInDataExchangeFrame[0]), timeout) == PN532_OK)
  36.     {
  37.         pn532_DebugMsg(USART1, "InDataExchange - StartFrame - OK");
  38.     }
  39.     else
  40.     {
  41.         pn532_DebugMsg(USART1, "InDataExchange - StartFrame - ERROR");
  42.         return PN532_ERROR;
  43.     }
  44.     HAL_Delay(10);
  45.     /* search for end frame */
  46.     if( pn532_searchInFrame(glob_pn532_SearchFunctPointer, responseInDataExchangeTailFrame,
  47.                         sizeof(responseInDataExchangeTailFrame)/sizeof(responseInDataExchangeTailFrame[0]), timeout) == PN532_OK)
  48.     {
  49.         pn532_DebugMsg(USART1, "InDataExchange - EndFrame - OK");
  50.     }
  51.     else
  52.     {
  53.         pn532_DebugMsg(USART1, "InDataExchange - EndFrame - ERROR");
  54.         return PN532_ERROR;
  55.     }
  56.     pn532_sendACKFrame();
  57.     return PN532_InDataExchange_GetReceiveFrame(responseInDataExchangeFrame, responseBuffer, responseBufferLen);
  58. }

Służy ona do wymiany informacji pomiędzy mikrokontrolerem, PN532 a przyłożonym tagiem. Do funkcji podaje się bufor z danymi, ich długość, bajt określający numer docelowego taga i wskaźnik do buforu odbiorczego.

Funkcja InListPassiveTarge służy do odczytu większej ilości kart, a dokładniej maksymalnie dwóch, przyłożonych jednoczenie do układu.

  1. PN532_CardStatus PN532_InListPassiveTargets(uint8_t MaxTg, uint8_t BrTy, uint8_t* receiveBuffer, uint8_t receiveBufferLen, uint16_t timeout)
  2. {
  3.     pn532_ClearBufferPosition();
  4.     pn532_ClearGlobRecUARTBuffer();
  5.     if( MaxTg > 0x02 || BrTy > 0x04)
  6.     {
  7.         return PN532_ERROR;
  8.     }
  9.     uint8_t inListPassiveTargetsSendFrame[] = { PN532_COMMAND_INLISTPASSIVETARGET, MaxTg, BrTy };
  10.     uint8_t inListPassiveTargetsResponseFrame[] = { PN532_PN532TOHOST,PN532_COMMAND_INLISTPASSIVETARGET + 1};
  11.     if(pn532_writeCommandFrameCheckAck(inListPassiveTargetsSendFrame,
  12.                                         sizeof(inListPassiveTargetsSendFrame)/sizeof(inListPassiveTargetsSendFrame[0]),
  13.                                         timeout, 0) == PN532_OK)
  14.     {
  15.         pn532_DebugMsg(USART1, "InListPassiveTargets - CheckedData - OK");
  16.     }
  17.     else
  18.     {
  19.         pn532_DebugMsg(USART1, "InListPassiveTargets - CheckedData - ERROR");
  20.         return PN532_ERROR;
  21.     }
  22.     /* search for start frame */
  23.     glob_pn532_SearchFunctPointer = pn532_SearchFrameForBuffer;
  24.     if( pn532_searchInFrame(glob_pn532_SearchFunctPointer, inListPassiveTargetsResponseFrame,
  25.                         sizeof(inListPassiveTargetsResponseFrame)/sizeof(inListPassiveTargetsResponseFrame[0]),
  26.                                                                                 timeout) == PN532_OK)
  27.     {
  28.         pn532_DebugMsg(USART1, "InListPassiveTargets - StartFrame - OK");
  29.     }
  30.     else
  31.     {
  32.         pn532_DebugMsg(USART1, "InListPassiveTargets - StartFrame - ERROR");
  33.         return PN532_ERROR;
  34.     }
  35.     pn532_sendACKFrame();
  36.     /* Decode if receive frame with data */
  37.     return PN532_inListPassiveTarget_GetCardData(inListPassiveTargetsResponseFrame,
  38.             sizeof(inListPassiveTargetsResponseFrame)/sizeof(inListPassiveTargetsResponseFrame[0]),
  39.             receiveBuffer, receiveBufferLen);
  40. }

Teraz funkcje odpowiedzialne za wpisywanie i odczytywanie danych z sektorów.

Przed odczytem czy zapisem danych należy podać klucze dostępu do określonego sektora:

  1. Mifare_OperationStatus_t PN532_Mifare_AuthenticationSector(uint8_t keyType, uint8_t Tg, uint8_t blockNumber,
  2.                                                         uint8_t* keyToSector, uint16_t timeout)
  3. {
  4.     uint8_t cardData[10]    = { 0 };
  5.     uint8_t response[50]    = { 0 };
  6.     uint8_t keyTypeToWrite = MIFARE_CMD_AUTH_A;
  7.     if(keyType == 2)
  8.     {
  9.         keyTypeToWrite = MIFARE_CMD_AUTH_B;
  10.     }
  11.     uint8_t sendFrame[]     = { keyTypeToWrite, blockNumber,
  12.                                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  13.                                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  14.                                 0x00, 0x00, 0x00, 0x00 };
  15.     uint8_t responseFrame[] = { PN532_PN532TOHOST, PN532_COMMAND_INDATAEXCHANGE + 1, 0x00};
  16.     memcpy(sendFrame + 2, keyToSector, 6);
  17.     volatile Mifare_OperationStatus_t status = PN532_Mifare_GetCardNumber(cardData, sizeof(cardData)/sizeof(cardData[0]),
  18.                                                             0x01, 0x01, 0x10, timeout);
  19.     if( status != MIFARE_OK )
  20.     {
  21.         pn532Mifare_DebugMsg(DEBUG_MSG_PN532_MIFARE, "Can't read card number");
  22.         return status;
  23.     }
  24.     for(uint8_t i=0; i<(*cardData); i++ )
  25.     {
  26.         sendFrame[8+i] = *(cardData + 1 + i);
  27.     }
  28.     status = PN532_InDataExchange(sendFrame, 8 + *cardData, Tg, response, 255, timeout);
  29.     if(status != MIFARE_OK) { return MIFARE_PN532_ERROR; }
  30.     uint8_t statusPos = pn532_SearchFrameForBuffer(response, responseFrame, 3);
  31.     if( statusPos == SEARCH_FRAME_ERROR ) { return MIFARE_PN532_ERROR; }
  32.     status = *(response + statusPos + 2);
  33.     return MIFARE_OK;
  34. }

W przypadku odczytu należy wykonać dwie operacje. Jedna z nich ma za zadanie przeprowadzenie procesu uwierzytelniania czyli podania kluczy dostępu, druga natomiast odczytuje dane. Proces uwierzytelniania można wykonać za pomocą klucza A lub klucza B. Wszystko zależy od tego jak zostały ustawione bity kontrolne. Dla standardowych ustawień (0xFF, 0x07, 0x80, 0x69) operacje odczytu i zapisu można wykonywać tylko za pomocą klucza A.

W funkcji powyżej do komendy InDataExchange, którą należy przesłać oprócz klucza także numer karty. Do tego celu wykorzystuje następującą funkcję:

  1. Mifare_OperationStatus_t PN532_Mifare_GetCardNumber(uint8_t* outBuffer, uint16_t outBufferLen, uint8_t pollNr, uint8_t period,
  2.                                             uint8_t type1, uint16_t timeout)
  3. {
  4.     uint8_t cardNumberData[30] = {0};
  5.     volatile uint8_t dataLength = 0;
  6.     if(PN532_InAutoPoll(cardNumberData, 30, pollNr, period, type1, timeout) != PN532_OK)
  7.     {
  8.         return MIFARE_PN532_ERROR;
  9.     }
  10.     if(*cardNumberData == 0x00) { return MIFARE_NOCARD; }
  11.     if(*cardNumberData == 0x01)     /* Single card detect */
  12.     {
  13.         /* Check what card type has been read */
  14.         if(*(cardNumberData+1) != 0x10) { return MIFARE_OTHER_CARD; }
  15.         dataLength = *(cardNumberData+7);
  16.         if(dataLength > outBufferLen)
  17.         {
  18.             dataLength = outBufferLen;
  19.         }
  20.         for(uint8_t i=0; i<(dataLength + 1); i++)
  21.         {
  22.             *(outBuffer + i) = *(cardNumberData + 7 + i);
  23.         }
  24.     }
  25.     return MIFARE_OK;
  26. }

Wykorzystuje ona komendę InAutoPoll do odczytania numeru karty. Zwraca go do tablicy outBuffer.

Odczyt pojedynczego bloku należy wykonać za pomocą takiej komendy:

  1. Mifare_OperationStatus_t PN532_Mifare_ReadBlock(uint8_t Tg, uint8_t blockNumber,
  2.                                     uint8_t* outBuffer, uint8_t outBufferLength, uint16_t timeout)
  3. {
  4.     uint8_t responseFrame[]         = { PN532_PN532TOHOST, PN532_COMMAND_INDATAEXCHANGE + 1, 0x00};
  5.     uint8_t returnCommandFrame[]    = { MIFARE_CMD_READ, blockNumber };
  6.     uint8_t responseData[40]        = { 0 };
  7.     if(mif_readSector(returnCommandFrame, responseData, (sizeof(responseData)/sizeof(responseData[0])),
  8.             responseFrame, Tg, outBuffer, 0, outBufferLength, timeout) != MIFARE_OK)
  9.     {
  10.         return MIFARE_ERROR;
  11.     }
  12.     return MIFARE_OK;
  13. }

Odczyt całego sektora wykonuje się za pomocą takich komend:

  1. Mifare_OperationStatus_t PN532_Mifare_ReadSector(uint8_t Tg, uint8_t blockToRead, uint8_t* dataBuffer,
  2.                     uint8_t dataBufferLength, uint16_t timeout)
  3. {
  4.     /* Check passed buffer */
  5.     if(dataBufferLength < 48) { return MIFARE_ERROR; }
  6.     uint8_t readFrameToSend[]   = { MIFARE_CMD_READ, blockToRead };
  7.     uint8_t responseFrame[]     = { PN532_PN532TOHOST, PN532_COMMAND_INDATAEXCHANGE + 1};
  8.     uint8_t responseData[60]    = { 0 };
  9.     for(uint8_t i = 0; i<48; i+=16)
  10.     {
  11.         if(pn532_mif_readSector(readFrameToSend, responseData, (sizeof(responseData)/sizeof(responseData[0])),
  12.                 responseFrame, Tg, dataBuffer, i, dataBufferLength, timeout) != MIFARE_OK)
  13.         {
  14.             return MIFARE_ERROR;
  15.         }
  16.         blockToRead++;
  17.         readFrameToSend[1] = blockToRead;
  18.     }
  19.     return MIFARE_OK;
  20. }

Dla odczytu danych z całego sektora przechodzę przez trzy razy przez odczyt kolejnych bloków danych.

W przypadku zapisu sytuacja wygląda podobnie najpierw proces uwierzytelniania z odpowiednimi kluczami a dopiero potem zapisanie danych na karcie.

Zapis danych dla pojedynczego bloku:

  1. Mifare_OperationStatus_t PN532_Mifare_WriteBlock(uint8_t Tg, uint8_t blockNumber, uint8_t* dataToWrite,
  2.                                                     uint8_t dataToWriteLength, uint16_t timeout)
  3. {
  4.     if(dataToWriteLength > 16) { return MIFARE_PN532_ERROR; }
  5.     uint8_t sendFrame[19] = { 0x00 };
  6.     uint8_t responseRecData[50] = {0};
  7.     uint8_t responseFrame[] = { PN532_PN532TOHOST, 0x41, 0x00};
  8.     if(pn532_mif_writeBlockCommand(sendFrame, responseFrame, sizeof(responseFrame)/sizeof(responseFrame[0]), Tg,
  9.                                     blockNumber, dataToWrite, dataToWriteLength, timeout) != MIFARE_OK)
  10.     {
  11.         return MIFARE_PN532_ERROR;
  12.     }
  13.     return MIFARE_OK;
  14. }

Zapis danych w całym sektorze:

  1. Mifare_OperationStatus_t PN532_Mifare_WriteSector(uint8_t Tg, uint8_t blockNumber, uint8_t* data,
  2.                                                 uint8_t length, uint16_t timeout)
  3. {
  4.     if(length != 48)
  5.     {
  6.         return MIFARE_PN532_ERROR;
  7.     }
  8.     uint8_t frameToSend[19] = { 0x00 };
  9.     uint8_t responseRecData[50] = {0};
  10.     uint8_t responseFrame[] = { PN532_PN532TOHOST, 0x41, 0x00};
  11.     uint8_t block1Data[16] = { 0x00 };
  12.     uint8_t block2Data[16] = { 0x00 };
  13.     uint8_t block3Data[16] = { 0x00 };
  14.     memcpy(block1Data, data, 16);
  15.     memcpy(block2Data, data+16, 16);
  16.     memcpy(block3Data, data+32, 16);
  17.     if(pn532_mif_writeBlockCommand(frameToSend, responseFrame, sizeof(responseFrame)/sizeof(responseFrame[0]),
  18.             Tg, blockNumber, block1Data, sizeof(block1Data)/sizeof(block1Data[0]), timeout) != MIFARE_OK)
  19.     {
  20.         return MIFARE_PN532_ERROR;
  21.     }
  22.     blockNumber = blockNumber+1;
  23.     if(pn532_mif_writeBlockCommand(frameToSend, responseFrame, sizeof(responseFrame)/sizeof(responseFrame[0]),
  24.             Tg, blockNumber, block2Data, sizeof(block2Data)/sizeof(block2Data[0]), timeout) != MIFARE_OK)
  25.     {
  26.         return MIFARE_PN532_ERROR;
  27.     }
  28.     blockNumber = blockNumber+1;
  29.     if(pn532_mif_writeBlockCommand(frameToSend, responseFrame, sizeof(responseFrame)/sizeof(responseFrame[0]),
  30.             Tg, blockNumber, block3Data, sizeof(block3Data)/sizeof(block3Data[0]), timeout) != MIFARE_OK)
  31.     {
  32.         return MIFARE_PN532_ERROR;
  33.     }
  34.     return MIFARE_OK;
  35. }

Pliki do projektu można pobrać z dysku Google pod tym linkiem.