nanoFramework.Iot.Device.Pn5180 1.2.606

前缀已保留
dotnet add package nanoFramework.Iot.Device.Pn5180 --version 1.2.606                
NuGet\Install-Package nanoFramework.Iot.Device.Pn5180 -Version 1.2.606                
此命令旨在在 Visual Studio 的包管理器控制台中使用,因为它使用 NuGet 模块的 Install-Package 版本。
<PackageReference Include="nanoFramework.Iot.Device.Pn5180" Version="1.2.606" />                
对于支持 PackageReference 的项目,请将此 XML 节复制到项目文件中以引用软件包。
paket add nanoFramework.Iot.Device.Pn5180 --version 1.2.606                
#r "nuget: nanoFramework.Iot.Device.Pn5180, 1.2.606"                
#r 指令可用于 F# Interactive 和 Polyglot Notebooks。请将此内容复制到交互式工具或脚本的源代码中以引用软件包。
// Install nanoFramework.Iot.Device.Pn5180 as a Cake Addin
#addin nuget:?package=nanoFramework.Iot.Device.Pn5180&version=1.2.606

// Install nanoFramework.Iot.Device.Pn5180 as a Cake Tool
#tool nuget:?package=nanoFramework.Iot.Device.Pn5180&version=1.2.606                

PN5180 - RFID 和 NFC 读取器

PN5180 是一个 RFID 和 NFC 读取器。它支持多种标准:ISO/IEC 14443 TypeA、ISO/IEC 14443 TypeB、ISO/IEC 15693 以及 ISO/IEC 18000-3 Mode 3。它支持与 14443 类型 A 卡进行高达 848 kBit/s 的通信。

文档

官方文档可以在 此处 找到

如何在没有 的情况下操作 PN5180 的应用笔记

板子

您会发现这个板子的不同实现。所有板子都应该有完整的 SPI 引脚以及复位和忙碌引脚,并且还有 5V 和或 3.3V 以及地。

使用方法

重要:在创建 SpiDevice 之前,请确保正确设置 SPI 引脚,特别是对于 ESP32,请确保安装了 nanoFramework.Hardware.ESP32 nuget

//////////////////////////////////////////////////////////////////////
// when connecting to an ESP32 device, need to configure the SPI GPIOs
// used for the bus
Configuration.SetPinFunction(21, DeviceFunction.SPI1_MOSI);
Configuration.SetPinFunction(22, DeviceFunction.SPI1_MISO);
Configuration.SetPinFunction(23, DeviceFunction.SPI1_CLOCK);
// Make sure as well you are using the right chip select

对于 STM32 等其他设备,请确保您正在使用您想使用的 SPI 总线的预设引脚。芯片选择也可以预先设置。

您可以在示例目录中找到一个完整的例子。这个例子涵盖了大多数公共函数和属性的使用。此示例还展示了如何使用超轻量级卡片

PN5180通过SPI和GPIO进行操作。GPIO用于控制SPI行为,因为PN5180使用SPI的方式是特定的。这需要手动管理SPI的引脚选择。还有一个名为“pin busy”的引脚用于理解PN5180何时可以接收和发送信息。

以下代码显示了如何创建SPI驱动程序、重置PN5180并创建类。

// Note: the chip select used here won't be used by the module, so don't use the same pin
var spi = SpiDevice.Create(new SpiConnectionSettings(1, 12) { ClockFrequency = Pn5180.SpiClockFrequency, Mode = Pn5180.SpiMode, DataFlow = DataFlow.MsbFirst });

// Reset the device
var gpioController = new GpioController();
gpioController.OpenPin(4, PinMode.Output);
gpioController.Write(4, PinValue.Low);
Thread.Sleep(10);
gpioController.Write(4, PinValue.High);
Thread.Sleep(10);

var pn5180 = new Pn5180(spi, 27, 18);

请注意,SPI最大时钟频率已预设为Pn5180.MaximumSpiClockFrequency,最大操作频率为7MHz。通过Pn5180.DefaultSpiMode的模态相同。数据流必须是DataFlow.MsbFirst

在之前的示例中,引脚2用于忙碌状态,引脚3用于SPI选择。请注意,您必须使用特定的引脚选择,而不能使用您创建的SPI通道关联的引脚。

重置是通过引脚4完成的。建议在创建类之前重置板子。

一旦创建,您在实际上与卡片交换数据之前需要选择一张卡片。以下是ISO 14443 Type A卡的选择方法:

Data106kbpsTypeA cardTypeA;
do
{
    // This will try to select the card for 1 second and will wait 300 milliseconds before trying again if none is found
    var retok = _pn5180.ListenToCardIso14443TypeA(TransmitterRadioFrequencyConfiguration.Iso14443A_Nfc_PI_106_106, ReceiverRadioFrequencyConfiguration.Iso14443A_Nfc_PI_106_106, out cardTypeA, 1000);
    if (retok)
    {
        Debug.WriteLine($"ISO 14443 Type A found:");
        Debug.WriteLine($"  ATQA: {cardTypeA.Atqa}");
        Debug.WriteLine($"  SAK: {cardTypeA.Sak}");
        Debug.WriteLine($"  UID: {BitConverter.ToString(cardTypeA.NfcId)}");
        // This is where you do something with the card
    }
    else
    {
        Thread.Sleep(300);
    }
}
while (true);

以及ISO 14443 Type B卡:

Data106kbpsTypeB card;

do
{
    // This will try to select the card for 1 second, if no card detected wait for 300 milliseconds and try again
    retok = _pn5180.ListenToCardIso14443TypeB(TransmitterRadioFrequencyConfiguration.Iso14443B_106, ReceiverRadioFrequencyConfiguration.Iso14443B_106, out card, 1000);
    if (!retok)
    {
        Thread.Sleep(300);
        continue;
    }

    Debug.WriteLine($"ISO 14443 Type B found:");
    Debug.WriteLine($"  Target number: {card.TargetNumber}");
    Debug.WriteLine($"  App data: {BitConverter.ToString(card.ApplicationData)}");
    Debug.WriteLine($"  App type: {card.ApplicationType}");
    Debug.WriteLine($"  UID: {BitConverter.ToString(card.NfcId)}");
    Debug.WriteLine($"  Bit rates: {card.BitRates}");
    Debug.WriteLine($"  Cid support: {card.CidSupported}");
    Debug.WriteLine($"  Command: {card.Command}");
    Debug.WriteLine($"  Frame timing: {card.FrameWaitingTime}");
    Debug.WriteLine($"  Iso 14443-4 compliance: {card.ISO14443_4Compliance}");
    Debug.WriteLine($"  Max frame size: {card.MaxFrameSize}");
    Debug.WriteLine($"  Nad support: {card.NadSupported}");

    // Do something else, all operations you want with the card
    // Halt card
    if (_pn5180.DeselecCardTypeB(card))
    {
        Debug.WriteLine($"Card unselected properly");
    }
    else
    {
        Debug.WriteLine($"ERROR: Card can't be unselected");
    }
}
while (true);

请注意,ListenToCardIso14443TypeAListenToCardIso14443TypeB可以配置不同的收发器和接收器。通常配置需要匹配,但您可以调整并更改它们。有关更多信息,请参阅“射频配置”部分。

卡片将在轮询的时间范围内持续尝试被检测。如果没有检测到任何内容或出现任何问题,函数将返回false。

针对类型B卡片,它们有一个目标号码。与卡片交换任何信息都需要这个目标号码。PN5180一次可以支持多达14张卡片。但您一次只能选择一张卡片,因此如果您需要同时选择并操作多张卡片,建议将卡片检测与您需要同时选择和操作的张数链式调用。请注意,根据卡片的不同,它们可能不会被读卡器视为已选择。

您应在结束时取消类型B卡的选择以释放目标号码。如果不这样做,在下次轮询期间,此实现将测试卡片是否仍然存在,在本例中将其保留。

EEPROM

您可以完全访问PN5180 EEPROM。以下是如何操作的示例。

// Maximum size of the EEPROM
Span<byte> eeprom = stackalloc byte[255];
// This will read fully the EEPROM
var ret = _pn5180.ReadAllEeprom(eeprom);
Debug.WriteLine($"EEPROM dump: success: {ret}, Data: {BitConverter.ToString(eeprom.ToArray())}");
// This reads only the unique Identifier
ret = _pn5180.ReadEeprom(EepromAddress.DieIdentifier, eeprom.Slice(0, 16));
Debug.WriteLine($"EEPROM read, unique identifier: success: {ret}, Data: {BitConverter.ToString(eeprom.Slice(0, 16).ToArray())}");
// Same as above
ret = _pn5180.GetIdentifier(eeprom.Slice(0, 16));
// So you should see the exact same result than from reading manully the 16 bytes of the unique identifier
Debug.WriteLine($"GetIdentifier: success: {ret}, Data: {BitConverter.ToString(eeprom.Slice(0, 16).ToArray())}");
// This tries to write in a read only part of the EEPROM
ret = _pn5180.WriteEeprom(EepromAddress.DieIdentifier, eeprom.Slice(0, 1));
// So you'll receive false as an answer from the PN5180
Debug.WriteLine($"Trying to write a read only EEPROM, this should return false: {ret}");
// This is important to understand, if you write in the EEPROM and then try to read right after,
// in most of the cases, the value won't change. After a reboot, you'll get the new value
Debug.WriteLine($"EEPROM writing will not be immediate. Some are only active after a reboot");
Debug.WriteLine($"changing second byte of UUID when acting as a card (first is always fix to 0x08)");
ret = _pn5180.ReadEeprom(EepromAddress.NFCID1, eeprom.Slice(0, 3));
eeprom[0]++;
Debug.WriteLine($"IRQ_PIN_CONFIG: success: {ret}, Data: {BitConverter.ToString(eeprom.Slice(0, 3).ToArray())}");
Debug.WriteLine($"New value to write: {BitConverter.ToString(eeprom.Slice(0, 1).ToArray())}");
ret = _pn5180.WriteEeprom(EepromAddress.NFCID1, eeprom.Slice(0, 3));
Debug.WriteLine($"Wrote IRQ_PIN_CONFIG: {ret}");
ret = _pn5180.ReadEeprom(EepromAddress.NFCID1, eeprom.Slice(0, 3));
Debug.WriteLine($"IRQ_PIN_CONFIG: success: {ret}, Data: {BitConverter.ToString(eeprom.Slice(0, 3).ToArray())}");

已经实现了读取和写入EEPROM部分或全部内容的函数。您需要小心缓冲区的大小,它不能超过255字节,也不能大于您希望写入的起始地址和总大小。因此,如果您写入位置250,则缓冲区大小和最大只能是5。

PN5180版本

您可以通过GetVersion函数检索PN5180的版本。将返回3个版本,分别是产品、固件和EEPROM版本。

var versions = _pn5180.GetVersion();
Debug.WriteLine($"Product: {versions.Product.ToString()}, Firmware: {versions.Firmware.ToString()}, EEPROM: {versions.Eeprom.ToString()}");

您应该看到这样的东西

Product: 3.5, Firmware: 3.5, EEPROM: 145.0

当前固件版本为3.12(3.C)和4.0。也就是说,此实现支持旧版本固件。较新版本的固件提供更好的自动校准支持,修复了错误,并添加了特定EMVco(支付)的低级功能。请注意,产品版本是安装的原始固件版本,因此如果您已升级固件,则产品版本将始终是原始固件的版本。

请注意,此实现不支持固件更新。如果您想更新固件,应使用NXP工具。

射频配置

PN5180允许设置大量配置。好消息是这些配置可以保存和加载。您也可以调整它们。以下代码演示了如何加载、提取配置,以及如何以相同的方式写回配置(如果需要)。请在此处参考文档,以了解您想进行的更改。

// Number of configuration
var sizeConfig = _pn5180.GetRadioFrequencyConfigSize(TransmitterRadioFrequencyConfiguration.Iso14443B_106);
// The RadioFrequencyConfiguraitonSize is 5, 1 for the register and 4 for the register data
SpanByte configBuff = new byte[Pn5180.RadioFrequencyConfiguraitonSize * sizeConfig];
var ret = _pn5180.RetrieveRadioFrequencyConfiguration(TransmitterRadioFrequencyConfiguration.Iso14443B_106, configBuff);
for (int i = 0; i < sizeConfig; i++)
{
    Debug.WriteLine($"Register: {configBuff[Pn5180.RadioFrequencyConfiguraitonSize * i]}, Data: {BitConverter.ToString(configBuff.Slice(Pn5180.RadioFrequencyConfiguraitonSize * i + 1, Pn5180.RadioFrequencyConfiguraitonSize - 1).ToArray())}");
}

每个配置大小为5字节,第一个字节是寄存器号,接下来的4个字节是数据本身。

与卡牌传输数据

一旦正确选择了卡牌,您就可以使用CardTranscive类与卡牌交换数据。请见MifareUltralight以获取详细示例。

此示例展示了如何完整导出Mifare(ISO 14443类型A)卡的内容。

Data106kbpsTypeA cardTypeA;

// Let's pull for 20 seconds and see the result
var retok = _pn5180.ListenToCardIso14443TypeA(TransmitterRadioFrequencyConfiguration.Iso14443A_Nfc_PI_106_106, ReceiverRadioFrequencyConfiguration.Iso14443A_Nfc_PI_106_106, out cardTypeA, 20000);
Debug.WriteLine();

if (!retok)
{
    Debug.WriteLine("Can't read properly the card");
}
else
{
    Debug.WriteLine($"ATQA: {cardTypeA.Atqa}");
    Debug.WriteLine($"SAK: {cardTypeA.Sak}");
    Debug.WriteLine($"UID: {BitConverter.ToString(cardTypeA.NfcId)}");

    MifareCard mifareCard = new MifareCard(_pn5180, cardTypeA.TargetNumber) { BlockNumber = 0, Command = MifareCardCommand.AuthenticationA };
    mifareCard.SetCapacity(cardTypeA.Atqa, cardTypeA.Sak);
    mifareCard.SerialNumber = cardTypeA.NfcId;
    mifareCard.KeyA = new byte[6] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
    mifareCard.KeyB = new byte[6] { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
    for (byte block = 0; block < 64; block++)
    {
        mifareCard.BlockNumber = block;
        mifareCard.Command = MifareCardCommand.AuthenticationB;
        var ret = mifareCard.RunMifiCardCommand();
        if (ret < 0)
        {
            // Try another one
            mifareCard.Command = MifareCardCommand.AuthenticationA;
            ret = mifareCard.RunMifiCardCommand();
        }

        if (ret >= 0)
        {
            mifareCard.BlockNumber = block;
            mifareCard.Command = MifareCardCommand.Read16Bytes;
            ret = mifareCard.RunMifiCardCommand();
            if (ret >= 0)
            {
                Debug.WriteLine($"Bloc: {block}, Data: {BitConverter.ToString(mifareCard.Data)}");
            }
            else
            {
                Debug.WriteLine($"Error reading bloc: {block}, Data: {BitConverter.ToString(mifareCard.Data)}");
            }

            if (block % 4 == 3)
            {
                // Check what are the permissions
                for (byte j = 3; j > 0; j--)
                {
                    var access = mifareCard.BlockAccess((byte)(block - j), mifareCard.Data);
                    Debug.WriteLine($"Bloc: {block - j}, Access: {access}");
                }

                var sector = mifareCard.SectorTailerAccess(block, mifareCard.Data);
                Debug.WriteLine($"Bloc: {block}, Access: {sector}");
            }
        }
        else
        {
            Debug.WriteLine($"Authentication error");
        }
    }
}

示例中还包括一个实现,用于完整导出其他卡的内容。

当前实现

通信支持

  • 完全支持硬件SPI控制器
  • 完全支持GPIO控制器

杂项

  • 完全读取EEPROM
  • 完全写入EEPROM
  • 读取EEPROM的任何部分
  • 写入EEPROM的任何部分
  • 获取产品、硬件和固件版本
  • CardTransceive支持重用现有的Mifare信用卡,支持ISO 14443类型A或类型B协议
  • 安全固件更新
  • 自有板上GPIO访问

RF通信命令

  • 加载特定配置
  • 读取特定配置
  • 写入特定配置

PN5180作为发起者(读取器)的命令

  • 自动轮询ISO 14443类型A卡
  • 自动轮询ISO 14443类型B卡
  • 取消选择ISO 14443类型B卡
  • 同时支持多卡:部分,取决于卡,所有14443类型B通信中CID均为必填项
  • ISO 14443-4通信协议
  • 自动轮询ISO/IEC 18000-3卡
  • 支持ISO/IEC 18000-3卡的通信
  • 低功耗卡检测
  • Mifare特定认证
  • 快速212、424、848 kbtis通信:部分

PN5180作为目标(充当卡牌)

  • 作为目标的初始化
  • 作为目标处理与其他读取器之间的通信
  • 支持透明数据传输
产品 兼容的和额外计算的目标框架版本。
.NET Framework net 兼容。
兼容的目标框架
包含的目标框架(在包中)
了解更多关于目标框架.NET Standard的信息。

NuGet包

此包不被任何NuGet包使用。

GitHub仓库

此包未被任何流行的GitHub仓库使用。

版本 下载 最后更新
1.2.606 45 8/2/2024
1.2.595 76 7/24/2024
1.2.590 55 7/17/2024
1.2.570 90 6/14/2024
1.2.560 93 5/29/2024
1.2.548 85 5/15/2024
1.2.536 89 4/15/2024
1.2.486 107 2/2/2024
1.2.479 86 1/27/2024
1.2.446 193 11/17/2023
1.2.436 81 11/10/2023
1.2.416 84 11/8/2023
1.2.329 131 5/26/2023
1.2.313 111 5/12/2023
1.2.297 134 5/3/2023
1.2.212 286 1/5/2023
1.2.203 275 12/28/2022
1.2.159 342 11/14/2022
1.2.153 351 11/5/2022
1.2.141 376 10/25/2022
1.2.128 357 10/22/2022
1.2.122 403 10/12/2022
1.2.118 413 10/11/2022
1.2.114 367 10/8/2022
1.2.95 398 9/22/2022
1.2.89 406 9/16/2022
1.2.87 465 9/15/2022
1.2.73 382 9/8/2022
1.2.40 411 8/6/2022
1.2.5 447 7/13/2022
1.1.141.41205 410 7/6/2022
1.1.116.8772 419 6/24/2022
1.1.113.2032 426 6/23/2022
1.1.111.5739 415 6/17/2022
1.1.109.32999 404 6/16/2022
1.1.99.36719 405 6/14/2022
1.1.97.17326 409 6/13/2022
1.1.92.53000 417 6/8/2022
1.1.48.19401 424 5/19/2022
1.1.38 429 5/4/2022
1.1.27 430 4/26/2022
1.1.20 426 4/21/2022
1.1.3 441 4/15/2022
1.1.1 426 4/14/2022
1.0.300 428 4/3/2022
1.0.288-preview.114 115 3/25/2022
1.0.288-preview.113 111 3/25/2022
1.0.288-preview.104 103 3/22/2022
1.0.288-preview.100 114 3/19/2022
1.0.288-preview.99 118 3/18/2022
1.0.288-preview.94 116 3/15/2022
1.0.288-preview.90 111 3/11/2022
1.0.288-preview.87 109 3/10/2022
1.0.288-preview.86 118 3/8/2022
1.0.288-preview.73 120 2/25/2022
1.0.288-preview.59 111 2/11/2022
1.0.288-preview.51 115 2/8/2022
1.0.288-preview.48 124 2/4/2022
1.0.288-preview.41 127 1/31/2022
1.0.288-preview.29 124 1/28/2022
1.0.288-preview.20 130 1/27/2022
1.0.288-preview.18 130 1/27/2022
1.0.288-preview.5 132 1/24/2022
1.0.288-preview.3 120 1/21/2022
1.0.272 162 1/10/2022
1.0.259 303 12/9/2021
1.0.221 154 10/19/2021
1.0.219 167 10/19/2021
1.0.218 192 10/18/2021
1.0.217 178 10/16/2021
1.0.207 182 10/11/2021
1.0.194 198 10/1/2021
1.0.191 161 9/29/2021
1.0.146 161 7/22/2021
1.0.140 166 7/20/2021