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
<PackageReference Include="nanoFramework.Iot.Device.Pn5180" Version="1.2.606" />
paket add nanoFramework.Iot.Device.Pn5180 --version 1.2.606
#r "nuget: nanoFramework.Iot.Device.Pn5180, 1.2.606"
// 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);
请注意,ListenToCardIso14443TypeA
和ListenToCardIso14443TypeB
可以配置不同的收发器和接收器。通常配置需要匹配,但您可以调整并更改它们。有关更多信息,请参阅“射频配置”部分。
卡片将在轮询的时间范围内持续尝试被检测。如果没有检测到任何内容或出现任何问题,函数将返回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类与卡牌交换数据。请见Mifare和Ultralight以获取详细示例。
此示例展示了如何完整导出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 兼容。 |
-
- nanoFramework.CoreLibrary (>= 1.15.5)
- nanoFramework.Logging (>= 1.1.108)
- nanoFramework.Runtime.Events (>= 1.11.18)
- nanoFramework.System.Buffers.Binary.BinaryPrimitives (>= 1.2.586)
- nanoFramework.System.Device.Gpio (>= 1.1.41)
- nanoFramework.System.Device.Spi (>= 1.3.52)
- nanoFramework.System.Diagnostics.Stopwatch (>= 1.2.586)
- nanoFramework.System.IO.Ports (>= 1.0.0)
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 |