class MFRC522
Constants
- AnalogTestReg
- AutoTestReg
- BitFramingReg
- CRCResultRegH
PCD Configuration Registers
- CRCResultRegL
- CWGsPReg
- CollReg
- ComIEnReg
- ComIrqReg
- CommandReg
PCD Command and Status Registers
- ControlReg
- DemodReg
- DivIEnReg
- DivIrqReg
- ErrorReg
- FIFODataReg
- FIFOLevelReg
- GsNReg
- MfRxReg
- MfTxReg
- ModGsPReg
- ModWidthReg
- ModeReg
PCD Command Registers
- PCD_CalcCRC
- PCD_GenRandomID
- PCD_Idle
PCD commands
- PCD_MFAuthent
- PCD_Mem
- PCD_NoCmdChange
- PCD_Receive
- PCD_SoftReset
- PCD_Transceive
- PCD_Transmit
- PICC_CT
- PICC_HLTA
- PICC_MF_ACK
- PICC_REQA
PICC
commands used by the PCD to manage communication with several PICCs (ISO 14443-3, Type A, section 6.4)- PICC_SEL_CL1
- PICC_SEL_CL2
- PICC_SEL_CL3
- PICC_WUPA
- RFCfgReg
- RxModeReg
- RxSelReg
- RxThresholdReg
- SerialSpeedReg
- Status1Reg
- Status2Reg
- TCounterValueRegH
- TCounterValueRegL
- TModeReg
- TPrescalerReg
- TReloadRegH
- TReloadRegL
- TestADCReg
- TestBusReg
- TestDAC1Reg
- TestDAC2Reg
- TestPinEnReg
- TestPinValueReg
- TestSel1Reg
PCD Test Registers
- TestSel2Reg
- TxASKReg
- TxControlReg
- TxModeReg
- TxSelReg
- VersionReg
- WaterLevelReg
Public Class Methods
# File lib/mfrc522.rb, line 101 def initialize(nrstpd = 24, chip = 0, spd = 1000000, timer = 256) chip_option = { 0 => PiPiper::Spi::CHIP_SELECT_0, 1 => PiPiper::Spi::CHIP_SELECT_1, 2 => PiPiper::Spi::CHIP_SELECT_BOTH, 3 => PiPiper::Spi::CHIP_SELECT_NONE } @spi_chip = chip_option[chip] @spi_spd = spd @timer = timer # Power it up @nrstpd = nrstpd nrstpd_pin = PiPiper::Pin.new(pin: nrstpd, direction: :out) nrstpd_pin.on sleep 1.0 / 20.0 # Wait 50ms soft_reset # Perform software reset pcd_config_reset # Set default setting antenna_on # Turn antenna on. They were disabled by the reset. end
Public Instance Methods
Modify and show antenna gain level level = 1: 18dB, 2: 23dB, 3: 33dB, 4: 38dB, 5: 43dB, 6: 48dB
# File lib/mfrc522.rb, line 192 def antenna_gain(level = nil) unless level.nil? level = 1 if level > 6 || level < 1 write_spi_set_bitmask(RFCfgReg, ((level + 1) << 4)) end (read_spi(RFCfgReg) & 0x70) >> 4 end
Turn antenna off
# File lib/mfrc522.rb, line 186 def antenna_off write_spi_clear_bitmask(TxControlReg, 0x03) end
Turn antenna on
# File lib/mfrc522.rb, line 181 def antenna_on write_spi_set_bitmask(TxControlReg, 0x03) end
Lookup PICC
name using sak
# File lib/mfrc522.rb, line 392 def identify_model(sak) # SAK coding separation reference: # http://cache.nxp.com/documents/application_note/AN10833.pdf # http://www.nxp.com/documents/application_note/130830.pdf if sak & 0x04 != 0 return :picc_uid_not_complete end if sak & 0x02 != 0 return :picc_reserved_future_use end if sak & 0x08 != 0 if sak & 0x10 != 0 return :picc_mifare_4k end if sak & 0x01 != 0 return :picc_mifare_mini end return :picc_mifare_1k end if sak & 0x10 != 0 if sak & 0x01 != 0 return :picc_mifare_plus_4k_sl2 end return :picc_mifare_plus_2k_sl2 end if sak == 0x00 return :picc_mifare_ultralight end if sak & 0x20 != 0 return :picc_iso_14443_4 end if sak & 0x40 != 0 return :picc_iso_18092 end return :picc_unknown end
Control transceive timeout value
# File lib/mfrc522.rb, line 158 def internal_timer(timer = nil) if timer write_spi(TReloadRegH, (timer >> 8) & 0xFF) write_spi(TReloadRegL, (timer & 0xFF)) end (read_spi(TReloadRegH) << 8) | read_spi(TReloadRegL) end
Start Crypto1 communication between reader and Mifare
PICC
PICC
must be selected before calling for authentication Remember to deauthenticate after communication, or no new communication can be made
Accept PICC_MF_AUTH_KEY_A or PICC_MF_AUTH_KEY_B command Checks datasheets for block address numbering of your PICC
# File lib/mfrc522.rb, line 447 def mifare_crypto1_authenticate(command, block_addr, sector_key, uid) # Buffer[12]: {command, block_addr, sector_key[6], uid[4]} buffer = [command, block_addr] buffer.concat(sector_key[0..5]) buffer.concat(uid[0..3]) communicate_with_picc(PCD_MFAuthent, buffer) # Check MFCrypto1On bit (read_spi(Status2Reg) & 0x08) != 0 end
Stop Crypto1 communication
# File lib/mfrc522.rb, line 460 def mifare_crypto1_deauthenticate write_spi_clear_bitmask(Status2Reg, 0x08) # Clear MFCrypto1On bit end
Reset PCD config to default
# File lib/mfrc522.rb, line 145 def pcd_config_reset # Clear ValuesAfterColl bit write_spi_clear_bitmask(CollReg, 0x80) # Reset transceiver baud rate to 106 kBd transceiver_baud_rate(:tx, 0) transceiver_baud_rate(:rx, 0) # Set PCD timer value for 302us default timer internal_timer(@timer) end
Instruct PICC
in ACTIVE state go to HALT state
# File lib/mfrc522.rb, line 211 def picc_halt buffer = append_crc([PICC_HLTA, 0]) status, _received_data, _valid_bits = communicate_with_picc(PCD_Transceive, buffer) # PICC in HALT state will not respond # If PICC sent reply, means it didn't acknowledge the command we sent status == :status_picc_timeout end
Wakes PICC
from HALT or IDLE to ACTIVE state Accept PICC_REQA
and PICC_WUPA
command
# File lib/mfrc522.rb, line 202 def picc_request(picc_command) pcd_config_reset status, _received_data, valid_bits = communicate_with_picc(PCD_Transceive, picc_command, 0x07) status == :status_ok && valid_bits == 0 # REQA or WUPA command return 16 bits(full byte) end
Select PICC
for further communication
PICC
must be in state ACTIVE
# File lib/mfrc522.rb, line 224 def picc_select # Description of buffer structure: # # Byte 0: SEL Indicates the Cascade Level: PICC_CMD_SEL_CL1, PICC_CMD_SEL_CL2 or PICC_CMD_SEL_CL3 # Byte 1: NVB Number of Valid Bits (in complete command, not just the UID): High nibble: complete bytes, Low nibble: Extra bits. # Byte 2: UID-data or Cascade Tag # Byte 3: UID-data # Byte 4: UID-data # Byte 5: UID-data # Byte 6: Block Check Character - XOR of bytes 2-5 # Byte 7: CRC_A # Byte 8: CRC_A # The BCC and CRC_A are only transmitted if we know all the UID bits of the current Cascade Level. # # Description of bytes 2-5 # # UID size Cascade level Byte2 Byte3 Byte4 Byte5 # ======== ============= ===== ===== ===== ===== # 4 bytes 1 uid0 uid1 uid2 uid3 # 7 bytes 1 CT uid0 uid1 uid2 # 2 uid3 uid4 uid5 uid6 # 10 bytes 1 CT uid0 uid1 uid2 # 2 CT uid3 uid4 uid5 # 3 uid6 uid7 uid8 uid9 pcd_config_reset cascade_levels = [PICC_SEL_CL1, PICC_SEL_CL2, PICC_SEL_CL3] uid = [] sak = 0 cascade_levels.each do |cascade_level| buffer = [cascade_level] current_level_known_bits = 0 received_data = [] valid_bits = 0 timeout = true # Maxmimum loop count is defined in ISO spec 32.times do if current_level_known_bits >= 32 # Prepare to do a complete select if we knew everything # Validate buffer content against non-numeric classes and incorrect size buffer = buffer[0..5] dirty_buffer = buffer.size != 6 dirty_buffer ||= buffer.any? do |byte| if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('2.4.0') !byte.is_a?(Numeric) else !byte.is_a?(Fixnum) end end # Retry reading UID when buffer is dirty, but don't reset loop count to prevent infinite loop if dirty_buffer # Reinitialize all variables buffer = [cascade_level] current_level_known_bits = 0 received_data = [] valid_bits = 0 # Continue to next loop next end tx_last_bits = 0 buffer[1] = 0x70 # NVB - We're sending full length byte[0..6] buffer[6] = (buffer[2] ^ buffer[3] ^ buffer[4] ^ buffer[5]) # Block Check Character # Append CRC to buffer buffer = append_crc(buffer) else tx_last_bits = current_level_known_bits % 8 uid_full_byte = current_level_known_bits / 8 all_full_byte = 2 + uid_full_byte # length of SEL + NVB + UID buffer[1] = (all_full_byte << 4) + tx_last_bits # NVB buffer_length = all_full_byte + (tx_last_bits > 0 ? 1 : 0) buffer = buffer[0...buffer_length] end framing_bit = (tx_last_bits << 4) + tx_last_bits # Select it status, received_data, valid_bits = communicate_with_picc(PCD_Transceive, buffer, framing_bit) if status != :status_ok && status != :status_collision raise CommunicationError, status end if received_data.empty? raise UnexpectedDataError, 'Received empty UID data' end # Append received UID into buffer if not doing full select if current_level_known_bits < 32 # Check for last collision if tx_last_bits != 0 buffer[-1] |= received_data.shift end buffer += received_data end # Handle collision if status == :status_collision collision = read_spi(CollReg) # CollPosNotValid - We don't know where collision happened raise CollisionError if (collision & 0x20) != 0 collision_position = collision & 0x1F collision_position = 32 if collision_position == 0 # Values 0-31, 0 means bit 32 raise CollisionError if collision_position <= current_level_known_bits # Calculate positioin current_level_known_bits = collision_position uid_bit = (current_level_known_bits - 1) % 8 # Mark the collision bit buffer[-1] |= (1 << uid_bit) else if current_level_known_bits >= 32 timeout = false break end current_level_known_bits = 32 # We've already known all bits, loop again for a complete select end end # Handle timeout after 32 loops if timeout raise UnexpectedDataError, 'Keep receiving incomplete UID until timeout' end # We've finished current cascade level # Check and collect all uid stored in buffer # Append UID uid << buffer[2] if buffer[2] != PICC_CT uid << buffer[3] << buffer[4] << buffer[5] # Check the result of full select # Select Acknowledge is 1 byte + CRC16 raise UnexpectedDataError, 'Unknown SAK format' if received_data.size != 3 || valid_bits != 0 raise IncorrectCRCError unless check_crc(received_data) sak = received_data[0] break if (sak & 0x04) == 0 # No more cascade level end return uid, sak end
Append CRC to buffer and check CRC or Mifare
acknowledge
# File lib/mfrc522.rb, line 465 def picc_transceive(send_data, accept_timeout = false) send_data = append_crc(send_data) puts "Sending Data: #{send_data.map{|x|x.to_s(16).rjust(2,'0').upcase}}" if ENV['DEBUG'] # Transfer data status, received_data, valid_bits = communicate_with_picc(PCD_Transceive, send_data) return [] if status == :status_picc_timeout && accept_timeout raise PICCTimeoutError if status == :status_picc_timeout raise CommunicationError, status if status != :status_ok puts "Received Data: #{received_data.map{|x|x.to_s(16).rjust(2,'0').upcase}}" if ENV['DEBUG'] # Data exists, check CRC and return if received_data.size > 1 raise IncorrectCRCError unless check_crc(received_data) return received_data[0..-3] end raise UnexpectedDataError, 'Incorrect Mifare ACK format' if received_data.size != 1 || valid_bits != 4 # ACK is 4 bits long raise MifareNakError, received_data[0] if received_data[0] != PICC_MF_ACK received_data end
Trying to restart picc
# File lib/mfrc522.rb, line 377 def reestablish_picc_communication(uid) picc_halt picc_request(PICC_WUPA) begin new_uid, _new_sak = picc_select status = true rescue CommunicationError status = false end status && uid == new_uid end
Shutdown
# File lib/mfrc522.rb, line 124 def shutdown antenna_off # TODO: handle other platform? if PiPiper::Platform.driver == PiPiper::Bcm2835 PiPiper::Platform.driver.unexport_pin(@nrstpd) end end
PCD software reset
# File lib/mfrc522.rb, line 133 def soft_reset write_spi(CommandReg, PCD_SoftReset) sleep 1.0 / 20.0 # wait 50ms write_spi(TModeReg, 0x87) # Start timer by setting TAuto=1, and higher part of TPrescalerReg write_spi(TPrescalerReg, 0xFF) # Set lower part of TPrescalerReg, and results in 302us timer (f_timer = 13.56 MHz / (2*TPreScaler+1)) write_spi(TxASKReg, 0x40) # Default 0x00. Force a 100 % ASK modulation independent of the ModGsPReg register setting write_spi(ModeReg, 0x3D) # Default 0x3F. Set the preset value for the CRC coprocessor for the CalcCRC command to 0x6363 (ISO 14443-3 part 6.2.4) end
Control transceiver baud rate value = 0: 106kBd, 1: 212kBd, 2: 424kBd, 3: 848kBd
# File lib/mfrc522.rb, line 168 def transceiver_baud_rate(direction, value = nil) reg = {tx: TxModeReg, rx: RxModeReg} if value value <<= 4 value |= 0x80 if value != 0 write_spi(reg.fetch(direction), value) end (read_spi(reg.fetch(direction)) >> 4) & 0x07 end
Private Instance Methods
# File lib/mfrc522.rb, line 601 def append_crc(data) data + calculate_crc(data) end
# File lib/mfrc522.rb, line 580 def calculate_crc(data) write_spi(CommandReg, PCD_Idle) # Stop any active command. write_spi(DivIrqReg, 0x04) # Clear the CRCIRq interrupt request bit write_spi_set_bitmask(FIFOLevelReg, 0x80) # FlushBuffer = 1, FIFO initialization write_spi(FIFODataReg, data) # Write data to the FIFO write_spi(CommandReg, PCD_CalcCRC) # Start the calculation # Wait for the command to complete i = 5000 loop do irq = read_spi(DivIrqReg) break if (irq & 0x04) != 0 raise PCDTimeoutError, 'Error calculating CRC' if i == 0 i -= 1 end write_spi(CommandReg, PCD_Idle) # Stop calculating CRC for new content in the FIFO. [read_spi(CRCResultRegL), read_spi(CRCResultRegH)] end
# File lib/mfrc522.rb, line 605 def check_crc(data) raise UnexpectedDataError, 'Data too short for CRC check' if data.size < 3 data = data.dup crc = data.pop(2) crc == calculate_crc(data) end
PCD transceive helper
# File lib/mfrc522.rb, line 535 def communicate_with_picc(command, send_data, framing_bit = 0) wait_irq = 0x00 wait_irq = 0x10 if command == PCD_MFAuthent wait_irq = 0x30 if command == PCD_Transceive write_spi(CommandReg, PCD_Idle) # Stop any active command. write_spi(ComIrqReg, 0x7F) # Clear all seven interrupt request bits write_spi_set_bitmask(FIFOLevelReg, 0x80) # FlushBuffer = 1, FIFO initialization write_spi(FIFODataReg, send_data) # Write sendData to the FIFO write_spi(BitFramingReg, framing_bit) # Bit adjustments write_spi(CommandReg, command) # Execute the command if command == PCD_Transceive write_spi_set_bitmask(BitFramingReg, 0x80) # StartSend=1, transmission of data starts end # Wait for the command to complete i = 2000 loop do irq = read_spi(ComIrqReg) break if (irq & wait_irq) != 0 return :status_picc_timeout if (irq & 0x01) != 0 return :status_pcd_timeout if i == 0 i -= 1 end # Check for error error = read_spi(ErrorReg) return :status_error if (error & 0x13) != 0 # BufferOvfl ParityErr ProtocolErr # Receiving data received_data = [] data_length = read_spi(FIFOLevelReg) while data_length > 0 do data = read_spi(FIFODataReg) received_data << data data_length -=1 end valid_bits = read_spi(ControlReg) & 0x07 status = :status_ok status = :status_collision if (error & 0x08) != 0 # CollErr return status, received_data, valid_bits end
Read from SPI communication
# File lib/mfrc522.rb, line 494 def read_spi(reg) output = 0 PiPiper::Spi.begin do |spi| spi.chip_select_active_low(true) spi.bit_order PiPiper::Spi::MSBFIRST spi.clock @spi_spd spi.chip_select(@spi_chip) do spi.write((reg << 1) & 0x7E | 0x80) output = spi.read end end output end
Write to SPI communication
# File lib/mfrc522.rb, line 510 def write_spi(reg, values) PiPiper::Spi.begin do |spi| spi.chip_select_active_low(true) spi.bit_order PiPiper::Spi::MSBFIRST spi.clock @spi_spd spi.chip_select(@spi_chip) do spi.write((reg << 1) & 0x7E, *values) end end end
Clear bits by mask
# File lib/mfrc522.rb, line 529 def write_spi_clear_bitmask(reg, mask) value = read_spi(reg) write_spi(reg, value & (~mask)) end
Set bits by mask
# File lib/mfrc522.rb, line 523 def write_spi_set_bitmask(reg, mask) value = read_spi(reg) write_spi(reg, value | mask) end