Seite 1 von 1

Ein kleiner Blick hinter die Kulissen des RFID-Moduls

Verfasst: Sa 7. Mai 2022, 17:06
von Heinrichs
Der zentrale Baustein des RFID-Moduls ist der IC MFRC522. Seine Funktionsweise ist in dem Datasheet MFRC522.pdf (s. Anlage) dargestellt. Dieses Dokument hat rund hundert Seiten und ist - wie für solche Datenblätter üblich - nicht als Lehrbuch oder Einführung in die Materie konzipiert. Da ist man dankbar für Module wie z. B. mfrc522.py, das ich in den bisherigen Beiträgen zu diesem Thema benutzt habe; denn diese erleichtern die Arbeit mit dem RFID-Modul wesentlich, indem sie die komplexen Steuerungsanweisungen in Funktionen zusammenfassen, mit denen wir dann umgehen können, ohne über die Interna Bescheid zu wissen. Manch einer ist aber vielleicht doch neugierig und möchte einen Blick hinter die Kulissen wagen. Ziel dieses Beitrags wird es daher sein, an einem Beispiel eine Kommunikation mit dem RFID-Modul vorzunehmen, ohne auf ein Modul wie mfrc522.py zurückzugreifen.

Der MFRC522-Baustein besteht grob gesprochen aus zwei Teilen, einem digitalen und einem analogen Teil. Der analoge Teil besteht im Wesentlichen aus einem Transmitter; dieser kann Funksignale an eine RFID-Karte senden und entsprechende Funkantworten von der RFID-Karte empfangen. Dazu werden die Funkwellen mit den zu übertragenden digitalen Informationen moduliert.

Der MFRC522 ist üblicherweise mit einem Host (z. B. einem Mikrocontroller) verbunden. Dieser steuert die Aktionen des MFRC522: So kann er z. B. veranlassen, dass eine Reihe von Zahlen per Funk auf einer RFID-Karte gespeichert werden oder bestimmte Einstellungen am Empfänger (wie z. B. an der Empfangsleistung, englisch: gain) vorgenommen werden. Ebenso kann er die von der Karte erhaltenen analogen Funksignale auswerten und dem Host zukommen lassen. Dazu besitzt der MFRC522 eine digitale Komponente; diese kann als Vermittler zwischen dem Mikrocontroller und dem analogen Teil angesehen werden. Wie komplex dies alles ist, kann man erahnen, wenn man sich Table 2 des Datasheets anschaut.

mfrc522_table_2.jpg
Abb. 1: Table 2 vom Datasheet
mfrc522_table_2.jpg (46.49 KiB) 12275 mal betrachtet

Diese digitale Komponente muss den gesamten Kommunikationsfluss managen. Beispielsweise muss sie für die Modulation und Demodulation der Funksignale sorgen, aber auch für die Kommunikation mit dem Host. Diese Kommunikation kann über verschiedene Protokolle, z. B. SPI, I2C oder Serial UART, erfolgen.

Neben der Empfangsleistung oder dem benutzten Protokoll können bzw. müssen zahlreiche andere Einstellungen vorgenommen werden; dazu werden die entsprechenden Parameter in so genannten Controll-Registern gespeichert. Umgekehrt kann man über diese Register aber auch Informationen über den Zustand einzelner Komponenten beziehen. An einem Beispiel werden wir weiter unten zeigen, wie man dabei vorgeht.

Der MFRC522 besitzt neben diesen Controll-Registern auch Command-Register; mit deren Hilfe kann man den MFRC522 verschiedene Aktionen durchführen lassen. So kann der Mem-Befehl z. B. 25 Bytes in einen internen Puffer laden und der Transceive-Befehl diese Bytes dann aus dem internen Puffer zur Funkübertragung an die analoge Einheit weitergeben. Der Umgang mit diesen Command-Registern ist deutlich komplexer als bei den Controll-Registern. Deswegen wollen wir die Command-Register hier nicht weiter betrachten.

Kommen wir jetzt zu unserem Beispiel: Wir wollen die Empfangsleistung (d. h. den Verstärkungsgrad des Empfängers von unserem RFID-Modul) auf einen bestimmten gain (z. B. 43 dB) einstellen. Dieses Beispiel hat gleich zwei Vorteile: Zum einen ist es relativ einfach zu behandeln und zum anderen sind die Folgen leicht experimentell zu überprüfen (s. u.). Nebenbei sei noch angemerkt, dass das oben bereits erwähnte Modul mfrc522.py keine Methoden für den gain zur Verfügung stellt.

Die Controll-Register können jeweils 1 Byte = 8 Bit aufnehmen. Jedes Controll-Register hat eine bestimmte Adresse. Das für den gain zuständige Controll-Register hat die Bezeichnung RFCfgReg und besitzt die “Adresse” 26h (vgl. Table 20 des Datasheet). Aus Table 98 des Datasheet entnehmen wir, dass z. B. der gain von 43 dB einem Wert (Value) von 6 = 110(bin) entspricht. Dieses Bitmuster muss nun die binären Stellen 4 bis 6 im Controll-Register annehmen. Die restlichen Bits sind für einen zukünftigen Gebrauch reserviert; deswegen werden werden wir diese auf 0 setzen. In dem Register RFCfgReg muss demzufolge die Zahl 0110 0000(bin) = 60h gespeichert werden.

Wie befördern wir dieses Byte in das Register RFCfgReg? In Abschnitt 8.1.2 des Datasheets wird beschrieben, wie man ein Byte mit dem SPI-Protokoll in ein Controll-Register überträgt. Dazu werden zwei Bytes gesendet, das erste gibt im Wesentlichen die Adresse des Registers an, das zweite den (gewünschten) Inhalt.

In Abschnitt 8.1.2.3 erfahren wir, dass als erstes Byte nicht einfach die Adresse 26h genommen werden darf. Vielmehr muss man bei dem zu übertragenden Byte - wir nennen es der Einfachheit halber Adressbyte - ein bestimmtes Format einhalten:

1. Das Bit 0 (LSB) muss den Wert 0 haben.
2. Die Zahl 26h muss im Byteraum [1:6] stehen.
3. Das Bit 7 (MSB) muss - weil es sich um einen Schreibvorgang handelt - den Wert 0 haben.

An diesem MSB wird der MFRC522-Baustein erkennen, wie er sich verhalten soll, wenn er das Adress-Byte empfangen hat: Ist dieses Bit 0, so wartet er anschließend darauf, dass auf der MOSI-Leitung ein weiteres Byte (in unserem Fall der Wert für den gain) ankommt. Hat das MSB hingegen den Wert 1, so sendet der MRFC522 den aktuell im Controll-Register vorliegenden Wert über die MISO-Leitung an den Host.

Abschnitt_8_1_2_3.jpg
Abb. 2: Abschnitt 8.1.2.3 vom Datasheet
Abschnitt_8_1_2_3.jpg (38.68 KiB) 12274 mal betrachtet

In Micropython können wir das leicht folgendermaßen erreichen:

reg = 0x26
reg = reg << 1 # Shift des Bitmuster von reg um 1 Stelle nach links


Sicherheitshalber maskieren wir diesen Wert mit 0x7e = 01111111(bin):

reg = reg & 0x7e

Damit ist sichergestellt, dass das Bit 7 von reg den Wert 0 hat. Durch den Links-Shift hat Bit 0 automatisch den Wert 0 erhalten. Dieser Wert von reg muss nun in ein Bytes-Objekt verwandelt werden; die write-Methode von spi akzeptiert nämlich nur diese Objekte:

reg_byte = bytes([reg])

Die bytes-Funktion erwartet eine Liste von Zahlen; hier besitzt diese Liste nur ein einziges Element, nämlich reg.

Jetzt wird das Adressbyte gesendet:

spi.write(reg_byte)

Nun muss auch das gain-Byte gesendet werden. Wir gehen von dem gain-Value 6 = 110(bin) aus. Mit der Anweisung

val = val << 4

wird das Bitmuster an die korrekte Position [4:6] gebracht; anschließend wird val mit

val_byte = bytes([val])
spi.write(val_byte)


zu einem Bytes-Objekt gewandelt und an den MRFC522 gesendet.

Zu beachten ist noch, dass vor dem Senden des ersten Bytes die SDA-Leitung mit

sda.value(0)

auf Low gezogen wird und nach dem Senden des zweiten Bytes die SDA-Leitung mit

sda.value(1)

wieder auf High gezogen wird.

Hier ein entsprechendes Programm (Sie finden es auch in der Anlage):

Code: Alles auswählen

# mfrc_write_read_gain.py: Legt zunächst den gain des RFID-Moduls fest,
# ermittelt sodann den gain vom RFID-Modul und UID der Karte und gibt beides am Terminal aus

# nach: https://github.com/Tasm-Devil/micropython-mfrc522-esp32; ergänzt und modifiziert für TTGO-T-Display

# Initialisierungen...

from time import sleep_ms
from machine import Pin, SoftSPI
from mfrc522 import MFRC522

sck = Pin(17, Pin.OUT)
mosi = Pin(15, Pin.OUT)
miso = Pin(12, Pin.OUT)
softspi = SoftSPI(baudrate=100000, polarity=0, phase=0, sck=sck, mosi=mosi, miso=miso) 

sda = Pin(25, Pin.OUT)

# Funktionen...

def write_gain(val): # val zwischen 0 (18 dB) und 7 (48 dB), genauer: 0>18dB, 1>23dB, 2>18dB, 3>23dB, 4>33dB, 5>38dB, 6>43dB, 7>48dB
    val = ((val & 0x07) << 4) # Das Bitmuster von val muss im Bereich von Bit4 bis Bit 6 des Registers eingetragen werden; Bit 4 ist (bei mir) standardmäßig auf 1
    reg = 0x26
    sda.value(0) # Chip enable
    # sende Registernummer an mrfc255:
    reg = (reg << 1) & 0x7e # Die Registernummer muss im Bitbereich [1:6] stehen; Bit 7 muss 0 sein, vgl. Abschnitt 8.1.2 vom Datasheet
    softspi.write(bytes([reg])) # SPI erwartet Bytes-Objekt
    # sende Wert an mfrc522:
    softspi.write(bytes([val])) # s. o.
    sda.value(1) # Chip disable
    
def read_gain():
    reg = 0x26
    sda.value(0) # Chip enable
    # sende Registernummer an mrfc255:
    reg = (reg << 1) & 0x7e # s. o.
    reg = reg | 0x80 # Bit 7 muss auf ein gesetzt sein (SPI-read); vgl. 8.1.2.3 des Datasheet
    softspi.write(bytes([reg])) # SPI erwartet Bytes-Objekt
    received = softspi.read(1) # liest ein einziges Byte
    sda.value(1) # Chip disable
    gain = received[0] # 1. Byte aus dem Bytes-Objekt
    gain = (gain & 0x7F) >> 4 # 4-facher Shift nach rechts
    return gain

def do_uid_gain():
    dbliste = ['18dB', '23dB', '18dB', '23dB', '33dB', '38dB', '43dB', '48dB']
    weiter = True
    while weiter:
        gain = int(input('gain (0-7): '))
        # Wenn gain = 7, gibt es Leseprobleme bei kleineren Abständen (Übersteuerung?)
        if gain < 0 or gain > 7:
            print('Ungültiger Eingabewert!')
        else:
            weiter = False
    try:
        while True:
            rdr = MFRC522(softspi, sda)
            write_gain(gain) # eigene Funktion zur Festlegung von gain, welche direkt auf das SPI zurückgreift; 0>18dB, 1>23dB, 2>18dB, 3>23dB, 4>33dB, 5>38dB, 6>43dB, 7>48dB
            sleep_ms(100)
            gain = read_gain()
            print('Gain: ', dbliste[gain])
           
            (stat, tag_type) = rdr.request(rdr.REQIDL)
            if stat == rdr.OK:
                (stat, raw_uid) = rdr.anticoll()
                if stat == rdr.OK:
                    uid = ("0x%02x%02x%02x%02x" % (raw_uid[0], raw_uid[1], raw_uid[2], raw_uid[3]))
                    print('uid: ', uid)
            else:
                print('Karte!')
                    
    except KeyboardInterrupt:
        print('Tschüss')

# Hauptprogramm...

print('Karte!')
print('Exit mit Strg-C')
do_uid_gain()     
Beachten Sie, dass in diesem Programm das Modul mit der Klasse MFRC522 nicht für das Setzen und Lesen des gain benutzt worden ist!


Mit ähnlichen Methoden können wir auch den aktuellen gain-Wert auslesen. Im Unterschied zum Schreibvorgang muss jetzt beim Adressbyte das Bit 7 auf 1 gesetzt werden; dies geschieht (nach dem Links-Shift) durch eine ODER-Vernüpfung:

reg = (reg << 1) | 0x80

Wenn das reg-Byte gesendet worden ist, erhalten wir mit

received = spi.read(1)

ein Bytes-Objekt mit 1 Byte. Mit

gain = received[0]

erhalten wir schließlich den Wert von Byte 0. Sicherheitshalber löschen wir Bit 7 und führen dann einen 4-fachen Rechts-Shift durch:

gain = (gain & 0x7e) >> 4

So erhalten wir unseren gain-Wert zwischen 0 und 7.

Im Anhang befindet sich das Programm mfrc_write_read_gain.py, mit dem Sie sowohl das Schreiben als auch das Lesen des Registers RFCfgReg durchführen können. Es zeigen sich deutliche Unterschiede bei der Reichweite: Bei mir betrug sie beim gain-Wert 0 nur wenige mm; bei einem gain-Wert 6 lag die Reichweite bei etwa 5 cm.

Zusätzlich finden Sie in dem Anhang:
  • ein read-write-Programm mit Display-Anzeige
  • ein um die beiden Methoden read_gain und write_gain erweitertes Modul für die Klasse MFRC522
  • ein Test-Programm für diese Methoden.
.