pn5180_diag.py 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  1. #!/usr/bin/env python3
  2. """PN5180 NFC reader diagnostic script.
  3. Connects to a PN5180 over SPI on a Raspberry Pi and reads
  4. hardware status, version info, and register state.
  5. Wiring (from spoolbuddy/README.md):
  6. PN5180 VCC -> Pi Pin 1 (3.3V)
  7. PN5180 GND -> Pi Pin 20 (GND)
  8. PN5180 SCK -> Pi Pin 23 (GPIO11)
  9. PN5180 MISO -> Pi Pin 21 (GPIO9)
  10. PN5180 MOSI -> Pi Pin 19 (GPIO10)
  11. PN5180 NSS -> Pi Pin 16 (GPIO23, manual CS)
  12. PN5180 BUSY -> Pi Pin 22 (GPIO25)
  13. PN5180 RST -> Pi Pin 18 (GPIO24)
  14. """
  15. import os
  16. import sys
  17. import time
  18. sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "daemon")))
  19. from pn5180 import ( # noqa: E402
  20. NSS_PIN as DRIVER_NSS_PIN,
  21. PN5180,
  22. RST_PIN as DRIVER_RST_PIN,
  23. SPI_BUS as DRIVER_SPI_BUS,
  24. SPI_DEVICE as DRIVER_SPI_DEVICE,
  25. )
  26. REG_SYSTEM_CONFIG = 0x00
  27. REG_IRQ_ENABLE = 0x01
  28. REG_IRQ_STATUS = 0x02
  29. REG_IRQ_CLEAR = 0x03
  30. REG_TRANSCEIVE_CONTROL = 0x04
  31. REG_TIMER1_RELOAD = 0x0C
  32. REG_TIMER1_CONFIG = 0x0F
  33. REG_RX_WAIT_CONFIG = 0x11
  34. REG_CRC_RX_CONFIG = 0x12
  35. REG_RX_STATUS = 0x13
  36. REG_CRC_TX_CONFIG = 0x19
  37. REG_RF_STATUS = 0x1D
  38. REG_SYSTEM_STATUS = 0x24
  39. REG_SIGPRO_CONFIG = 0x1A # Signal Processing Configuration
  40. REG_TEMP_CONTROL = 0x25
  41. # ---------------------------------------------------------------------------
  42. # EEPROM addresses
  43. # ---------------------------------------------------------------------------
  44. EEPROM_DIE_IDENTIFIER = 0x00 # 16 bytes
  45. EEPROM_PRODUCT_VERSION = 0x10 # 2 bytes
  46. EEPROM_FIRMWARE_VERSION = 0x12 # 2 bytes
  47. EEPROM_EEPROM_VERSION = 0x14 # 2 bytes
  48. EEPROM_IRQ_PIN_CONFIG = 0x1A # 1 byte
  49. def _check_spi_device_access() -> str:
  50. """Check that the configured spidev exists and can be opened."""
  51. spi_path = f"/dev/spidev{DRIVER_SPI_BUS}.{DRIVER_SPI_DEVICE}"
  52. if not os.path.exists(spi_path):
  53. raise FileNotFoundError(f"SPI device not found: {spi_path}")
  54. fd = os.open(spi_path, os.O_RDWR)
  55. os.close(fd)
  56. return spi_path
  57. def _self_test_control_pins(nfc: PN5180):
  58. """Toggle NSS and RST pins and print observed line state.
  59. Uses public set_pin/get_pin methods to avoid direct access to driver internals.
  60. """
  61. for pin_name, pin_num in (("NSS", DRIVER_NSS_PIN), ("RST", DRIVER_RST_PIN)):
  62. nfc.set_pin(pin_num, True)
  63. time.sleep(0.005)
  64. active_state = nfc.get_pin(pin_num)
  65. nfc.set_pin(pin_num, False)
  66. time.sleep(0.005)
  67. inactive_state = nfc.get_pin(pin_num)
  68. # Restore idle-high level used by this driver.
  69. nfc.set_pin(pin_num, True)
  70. print(
  71. f" {pin_name} pin {pin_num}: "
  72. f"ACTIVE->{'ACTIVE' if active_state else 'INACTIVE'}, "
  73. f"INACTIVE->{'ACTIVE' if inactive_state else 'INACTIVE'}"
  74. )
  75. def run_diagnostics():
  76. print("=" * 60)
  77. print("PN5180 NFC Reader Diagnostics")
  78. print("=" * 60)
  79. nfc = None
  80. try:
  81. print("\n[1] SPI device check...")
  82. spi_path = _check_spi_device_access()
  83. print(f" SPI device OK: {spi_path}")
  84. nfc = PN5180()
  85. print("\n[2] Control pin self-test (NSS/RST)...")
  86. _self_test_control_pins(nfc)
  87. # Reset
  88. print("\n[3] Hardware reset...")
  89. nfc.reset()
  90. print(" Reset OK")
  91. # Version info
  92. print("\n[4] Version info (EEPROM)")
  93. product = nfc.read_eeprom(EEPROM_PRODUCT_VERSION, 2)
  94. firmware = nfc.read_eeprom(EEPROM_FIRMWARE_VERSION, 2)
  95. eeprom = nfc.read_eeprom(EEPROM_EEPROM_VERSION, 2)
  96. die_id = nfc.read_eeprom(EEPROM_DIE_IDENTIFIER, 16)
  97. print(f" Product version : {product[1]}.{product[0]}")
  98. print(f" Firmware version : {firmware[1]}.{firmware[0]}")
  99. print(f" EEPROM version : {eeprom[1]}.{eeprom[0]}")
  100. print(f" Die identifier : {die_id.hex()}")
  101. # Register dump
  102. print("\n[5] Register dump")
  103. # Use register names from the script (not in pn5180.py)
  104. REGISTER_NAMES_DUMP = {
  105. 0x00: "SYSTEM_CONFIG",
  106. 0x01: "IRQ_ENABLE",
  107. 0x02: "IRQ_STATUS",
  108. 0x03: "IRQ_CLEAR",
  109. 0x04: "TRANSCEIVE_CONTROL",
  110. 0x0C: "TIMER1_RELOAD",
  111. 0x0F: "TIMER1_CONFIG",
  112. 0x11: "RX_WAIT_CONFIG",
  113. 0x12: "CRC_RX_CONFIG",
  114. 0x13: "RX_STATUS",
  115. 0x19: "CRC_TX_CONFIG",
  116. 0x1A: "SIGPRO_CONFIG",
  117. 0x1D: "RF_STATUS",
  118. 0x24: "SYSTEM_STATUS",
  119. 0x25: "TEMP_CONTROL",
  120. }
  121. for addr, name in sorted(REGISTER_NAMES_DUMP.items()):
  122. val = nfc.read_reg(addr)
  123. print(f" 0x{addr:02X} {name:<24s} = 0x{val:08X}")
  124. # SIGPRO_CONFIG ISO/IEC14443 mode check
  125. sigpro_val = nfc.read_reg(REG_SIGPRO_CONFIG)
  126. sigpro_mode = (sigpro_val >> 0) & 0b111
  127. baudrate_map = {
  128. 0b100: "106 kBd (ISO/IEC14443 type A/B)",
  129. 0b101: "212 kBd (FeliCa 212 kBd)",
  130. 0b110: "424 kBd (FeliCa 424 kBd)",
  131. 0b111: "848 kBd",
  132. }
  133. baudrate_str = baudrate_map.get(sigpro_mode, "Unknown or reserved")
  134. print(f"\n[5b] SIGPRO_CONFIG (0x1A) bits 2:0 = 0b{sigpro_mode:03b} ({baudrate_str})")
  135. # IRQ status breakdown
  136. irq = nfc.read_reg(REG_IRQ_STATUS)
  137. print(f"\n[6] IRQ status flags (0x{irq:08X})")
  138. irq_flags = [
  139. (0, "RX_IRQ"),
  140. (1, "TX_IRQ"),
  141. (2, "IDLE_IRQ"),
  142. (3, "MODE_DETECTED_IRQ"),
  143. (4, "CARD_ACTIVATED_IRQ"),
  144. (5, "STATE_CHANGE_IRQ"),
  145. (6, "RFOFF_DET_IRQ"),
  146. (7, "RFON_DET_IRQ"),
  147. (8, "TX_RFOFF_IRQ"),
  148. (9, "TX_RFON_IRQ"),
  149. (10, "RF_ACTIVE_ERROR_IRQ"),
  150. (14, "LPCD_IRQ"),
  151. ]
  152. for bit, name in irq_flags:
  153. state = "SET" if irq & (1 << bit) else "---"
  154. print(f" bit {bit:2d}: {name:<28s} [{state}]")
  155. # RF status
  156. rf = nfc.read_reg(REG_RF_STATUS)
  157. print(f"\n[7] RF status (0x{rf:08X})")
  158. tx_rf_on = bool(rf & (1 << 0))
  159. rx_en = bool(rf & (1 << 1))
  160. print(f" TX RF active : {tx_rf_on}")
  161. print(f" RX enabled : {rx_en}")
  162. # System status
  163. sys_stat = nfc.read_reg(REG_SYSTEM_STATUS)
  164. print(f"\n[8] System status (0x{sys_stat:08X})")
  165. # System status bit breakdown
  166. sys_stat_bits = [
  167. (9, "LDO_TVDD_OK"),
  168. (8, "PARAMETER_ERROR"),
  169. (7, "SYNTAX_ERROR"),
  170. (6, "SEMANTIC_ERROR"),
  171. (5, "STBY_PREVENT_RFLD"),
  172. (4, "BOOT_TEMP"),
  173. (3, "BOOT_SOFT_RESET"),
  174. (2, "BOOT_WUC"),
  175. (1, "BOOT_RFLD"),
  176. (0, "BOOT_POR"),
  177. ]
  178. for bit, symbol in sys_stat_bits:
  179. state = "SET" if sys_stat & (1 << bit) else "---"
  180. print(f" bit {bit:2d}: {symbol:<18s} [{state}]")
  181. # Temperature
  182. temp_ctrl = nfc.read_reg(REG_TEMP_CONTROL)
  183. print(f"\n[9] Temp control register (0x{temp_ctrl:08X})")
  184. # TEMP_DELTA bits 1:0
  185. temp_delta = (temp_ctrl >> 0) & 0b11
  186. temp_delta_map = {
  187. 0b00: "85°C",
  188. 0b01: "115°C",
  189. 0b10: "125°C",
  190. 0b11: "135°C",
  191. }
  192. temp_delta_str = temp_delta_map.get(temp_delta, "Unknown")
  193. print(f" bits 1:0 TEMP_DELTA = 0b{temp_delta:02b} ({temp_delta_str})")
  194. print("\n" + "=" * 60)
  195. print("Diagnostics complete - PN5180 is responding over SPI.")
  196. print("=" * 60)
  197. except TimeoutError as e:
  198. print(f"\nERROR: {e}")
  199. print("Check wiring and ensure SPI is enabled (dtparam=spi=on in /boot/firmware/config.txt)")
  200. sys.exit(1)
  201. except Exception as e:
  202. print(f"\nERROR: {e}")
  203. sys.exit(1)
  204. finally:
  205. if nfc is not None:
  206. nfc.close()
  207. if __name__ == "__main__":
  208. run_diagnostics()