3.8 🍀 SPI Schnittstelle
- Funktionsprinzip der SPI-Schnittstelle
- Einen 74HC595 als PortExpander verwenden
- Übertragung zu MAX7219 mit 8×8 Matrix-LED-Anzeige
- Aufgabe: Einen Würfel bauen
- Matrix-Animationen ausgeben: TG IT 12
- Zwei Anzeigen kaskadieren
- Mit MAX7219 7-Segment Modul Zeitzähler bauen
- RFID-Modul MFRC522
- 🤐 LED&KEY Module mit TM1638
Synopsis: [az-delivery.de/products/saleae-logic-analyzer]
[mikrocontroller.net/articles/Serial_Peripheral_Interface]
[mikrocontroller.net/articles/SPI_Daisychain]
[docs.arduino.cc/learn/communication/spi]
[analog.com/media/en/technical-documentation/data-sheets/MAX7219-MAX7221.pdf]
[learn.sparkfun.com/tutorials/serial-peripheral-interface-spi/all]
[ti.com/lit/ds/symlink/sn74hc595.pdf]
1. Funktionsprinzip der SPI-Schnittstelle
SPI bedeutet Serial Peripheral Interface und dient hauptsächlich zur seriellen Datenübertragung von einem µC zu einer Peripherie und von der Peripherie zum µC. Die Datenübertragung wird durch einen Master/Controller gesteuert. Er gibt den Takt der Übertragung über eine Leitung SCK (Serial Clock) und wählt über eine Leitung CS/SS (Chip Select/Slave Select) die gewünschte Peripherie aus. Der µC sendet seine Daten bitweise über eine Leitung MOSI (Master Out Slave In) und erhält ggfs. Daten der Peripherie über eine Leitung MISO (Master In Slave Out). Im Prinzip werden einfach Bits durch Schieberegister im Takt von SCK geschoben. !CS steuert den Beginn der Übertragung (low) und wann die Daten aus den Schieberegistern übernommen werden sollen (low↑high). Hier eine Demo für eine 8Bit Übertragung (MSB-First, SPI-Modus 0). Der Master sendet 0xC3 und empfängt 0x47. Das Bild zeigt den Zustand nach der Übertragung.
- SCK bis zu 10MHz schnell
- 8 oder 16 Bit Übertragungen
- Bidirektionale Übertragung möglich
- Sehr einfaches Protokoll
Video zum Verständnis
Anschluss mehrerer Bausteine
Wenn mehrere Peripherie-Bausteine an den Bus angeschlossen werden sollen gibt es zwei Prinzipien, auch die Kombination wäre möglich:
Bei der Sternverkabelung wählt der µC über mehrere CS-Leitungen den gewünschten Kommunikationspartner aus. Nur dieser darf dann auch die MISO-Leitung belegen.
Bei der Kaskadierung werden die Daten nacheinander durch alle Empfänger geschoben.
Die Benennung der Signale ist nicht genormt
Eine Vielfalt von Bezeichnungen für gleiche Leitungen und neue Bezeichnungen mit Varianten wegen „Political Correctness„
Master/Slave (alte Bezeichnungen) | Controller/Peripheral (neuere Bezeichnungen) | Bedeutung | Aus Sicht der Peripherie |
---|---|---|---|
Master In Slave Out (MISO) | Controller In, Peripheral Out (CIPO) [POCI] | Datenübertragung vom µC zur Peripherie | Serial Data Out (SDO) [DOUT] |
Master Out Slave In (MOSI) | Controller Out Peripheral In (COPI) [PICO] | Datenübertragung von Peripherie zu µC | Serial Data In (SDI) [DIN] |
Slave Select pin (SS) | Chip Select Pin (CS) | Die Peripherie als Empfänger auswählen (low aktiv) | |
Serial Clock (SCK) [SCLK] | Serial Clock (SCK) [SCLK] {CLK} | Takt für die Datenübertragung |
Die Reihenfolge der Bit-Übertragung kann eingestellt werden
Reihenfolge der Bits bei der Datenübertragung: MSB- oder LSB-First? In der Regel wird das höchstwertige Bit, das MSB zuerst übertragen:
SPI.setBitOrder(MSBFIRST); // Hoechstwertiges Bit zuerst
SPI-Modus: Bei welchem Zustand von SCK werden die Daten übernommen?
Es wurde nicht festgelegt, welches der Ruhezustand von SCK ist und bei welchem SCK-Zustand die Daten übernommen werden sollen, daher entwickelten sich 4 Varianten, die wir nun berücksichtigen dürfen. In der Regel wird SPI-Modus 0 angewendet:
SPI (SCK) Modus | CPOL (Clock Polarity) | CPHA (Clock Phase) | Bedeutung |
---|---|---|---|
0 | 0 Clock Idle Low | 0 Übernahme bei 1. Flanke | SCK in Ruhe 0 Übernahme bei steigender Flanke _↑¯|_↑¯|_ |
1 | 0 Clock Idle Low | 1 Übernahme bei 2. Flanke | SCK in Ruhe 0 Übernahme bei fallender Flanke _|¯↓_|¯↓_ |
2 | 1 Clock Idle High | 0 Übernahme bei 1. Flanke | SCK in Ruhe 1 Übernahme bei fallender Flanke ¯↓_|¯↓_|¯ |
3 | 1 Clock Idle High | 1 Übernahme bei 2. Flanke | SCK in Ruhe 1 Übernahme bei steigender Flanke ¯|_↑¯|_↑¯ |
SPI.setDataMode(SPI_MODE0); // SCK in Ruhe Low, Uebernahme bei steig. Flanke
2. Einen 74HC595 als PortExpander verwenden
Hier im Beispiel sollen 2 LED über den SPI-Bus mittels eines einfachen 74HC595 angesteuert werden, es werden 8Bit übertragen. Bit0 lässt die rote- und Bit1 die grüne LED leuchten.
- !OE wird mit GND verbunden, damit die LED leuchten.
- RCLK ist mit CS verbunden, bei steigender Flanke werden die Werte im Schieberegister übernommen.
- !SRCLR ist mit VCC verbunden, es ist kein Reset des Schieberegisters notwendig.
- SRCLK ist mit SCK verbunden, damit bei steigender Flanke die Werte in das Schieberegister übernommen werden.
- SER ist mit MOSI verbunden, die Daten werden in das Schieberegister rein geschoben.
Beispiel für Anwendung: Bei einem 8*8*8 LED-Cube werden typischerweise 8 Ebenen mit jeweils 64 LED im Zeitmultiplexverfahren angesteuert. Für die LED werden 64 Ausgänge benötigt, mit 8 74HC595 und SPI ist das ein Kinderspiel. Einfach die Chips kaskadieren und die 8 Byte für die jeweilige Ebene mit SPI verschicken.
Testcode
#include <SPI.h>
#define CS D10 // CS Signal auf D10
#define UserButton PC13 // Zum Umschalten der LED
void send_data (unsigned char lsb){ // Hilfsfunktion zur Datenuebertragung
digitalWrite(CS, LOW); // Uebertragung beginnt
SPI.transfer(lsb); // 8 Bit Transfer LSB
digitalWrite(CS, HIGH); // Uebertragung fertig, uebernehme Daten!
}
void setup(){
pinMode(UserButton, INPUT); // Zum Umschalten des Musters
pinMode(CS, OUTPUT);
digitalWrite(CS, HIGH);
SPI.begin(); // SPI-Schnittstelle einrichten
SPI.setBitOrder(MSBFIRST); // Hoechstwertiges Bit zuerst
SPI.setClockDivider(SPI_CLOCK_DIV32); // SCK mit 1 MHz, koennte auch 10 MHz
SPI.setDataMode(SPI_MODE0); // SCK in Ruhe Low, Uebername bei steigender Flanke
delay(10); // kurz warten bis verdaut..
}
void loop(){
if(digitalRead(UserButton)) send_data(1); // Rote LED leuchten lassen
else send_data(2); // Gruene LED leuchten lassen
//send_data(digitalRead(UserButton)?1 :2); // Sende 1 oder 2 je nach UserButton-Zustand
delay(1000);
}
Mit Logic-Analyzer von Analog Discovery Studio zuschauen
3. Übertragung zu MAX7219 mit 8×8 Matrix-LED-Anzeige
Der MAX7219 ist für die Ansteuerung von 8 7-Segment-Anzeigen oder einer 8×8 LED Matrix gedacht. Die LEDs werden im Zeitmultiplex-Verfahren angesteuert. Dazu muss im laufenden Betrieb nur der Inhalt der Anzeigen/Zeilen bei Änderung übertragen werden.
ToDo: Bild mit Zeilen/Spalten gemäß Doku des Shields:
Zeilen gehen von oben nach unten. Spalten von rechts nach links. Rechts oben ist (0,0) links unten ist (7,7).
Synopsis: [Fritzing-Part 🔗] [wolles-elektronikkiste.de/led-matrix-display-ansteuern 🔗]
Testcode
#include <SPI.h>
#define CS D10 // CS Signal auf D10
#define UserButton PC13 // Zum Umschalten des Musters
const unsigned char tabX[]= {0x81,0x42,0x24,0x18,0x18,0x24,0x42,0x81}; // Muster fuer X
const unsigned char tabO[]= {0x00,0x18,0x24,0x42,0x42,0x24,0x18,0x00}; // Muster fuer o
void send_data (unsigned char msb, unsigned char lsb){ // Hilfsfunktion zur Datenuebertragung
digitalWrite(CS, LOW); // Uebertragung beginnt
SPI.transfer(msb); // 8 Bit Transfer MSB
SPI.transfer(lsb); // 8 Bit Transfer LSB
//SPI.transfer16(msb<<8 | lsb); // 16 Bit Transfer
digitalWrite(CS, HIGH); // Uebertragung fertig, uebernehme Daten!
}
void setup(){
pinMode(UserButton, INPUT); // Zum Umschalten des Musters
pinMode(CS, OUTPUT);
digitalWrite(CS, HIGH);
SPI.begin(); // SPI-Schnittstelle einrichten fuer MAX7219
SPI.setBitOrder(MSBFIRST); // Hoechstwertiges Bit zuerst
SPI.setClockDivider(SPI_CLOCK_DIV32); // SCK mit 1 MHz, koennte auch 10 MHz
SPI.setDataMode(SPI_MODE0); // SCK in Ruhe Low, Uebername bei steigender Flanke
delay(10); // kurz warten bis verdaut..
// MAX7219 Initialisierung des Chips
send_data(0x09, 0x00); // Decoding: Aus: keine BCD Dekodierung fuer 7 Segment
send_data(0x0A, 0x05); // Intesity: Helligkeit auf 5 von 15 einstellen
send_data(0x0B, 0x07); // Scan Limit: Es sind 8 Zeilen mit LEDs
send_data(0x0C, 0x01); // Shutdown: Bit0 = 1: Normal operation mode, kein Ruhezustand
send_data(0x0F, 0x01); // Display Test: Bit0 = 1: Displaytest An: Alle LEDs an!
delay(500); // 500 ms delay
send_data(0x0F,0x00); // Display Test: Bit0 = 0: Displaytest Aus: Anzeigen was in Register steht
for(int adr=1; adr<=8; adr++){ // alle LEDs ausschalten, sonst wird zufaelliges Muster ausgegeben
send_data(adr,0x00); // Zeile = 0
}
delay(500); // 500 ms delay
}
void loop(){
for(int adr=1; adr<9; adr++){ // alle Zeilen ausgeben
send_data(adr,digitalRead(UserButton)?tabX[adr-1] :tabO[adr-1] ); //MSB = digit 1-8 Spalte LSB Segment zeile high aktiv
}
delay(1000);
}
Mit Logic-Analyzer bei der Datenübertragung zuschauen
Mit dem Logic-Analyzer von Az-Delivery und der Software Logic2 von saleae dem Geschehen zuschauen, diese Einstellungen dafür machen:
Die Bits werden einfach durch die Schieberegister mit dem SCK-Takt durchgeschoben. Bei steigender Flanke von !CS werden sie Werte von den Bausteinen übernommen und ausgewertet. Die Ausgabe DOUT des Moduls wurde mit Kanal 3 des Logic-Analyzers gemessen, es ist gut zu sehen, dass einfach der Inhalt des 16Bit Schieberegisters weiter geschoben wird. Ein nachfolgendes Modul würde ihn erhalten.
Den MAX7219 prinzipiell verstehen
Im MAX7219 gibt es 14 8-Bit Register mit dem der Chip eingestellt und die Ausgabe in die Matrix gesteuert werden kann. Es werden 16-Bit Daten zum Chip übertragen, das MSByte gibt die Register-Adresse vor das LSByte den Inhalt. Hier ein Schnellüberblick. (Mehr Info? RTFM! Datenblatt )
Name | D15-D12 | D11 | D10 | D9 | D8 | Adresse Hex | Beschreibung |
---|---|---|---|---|---|---|---|
No-OP | X | 0 | 0 | 0 | 0 | 0x00 | Tue nichts, wird zum Daten durchschieben zum nächsten Modul verwendet |
Digit 0 | X | 0 | 0 | 0 | 1 | 0x01 | Inhalt Digit0, Zeile 0 |
Digit 1 | X | 0 | 0 | 1 | 0 | 0x02 | Inhalt Digit1, Zeile 1 |
Digit 2 | X | 0 | 0 | 1 | 1 | 0x03 | Inhalt Digit2, Zeile 2 |
Digit 3 | X | 0 | 1 | 0 | 0 | 0x04 | Inhalt Digit3, Zeile 3 |
Digit 4 | X | 0 | 1 | 0 | 1 | 0x05 | Inhalt Digit4, Zeile 4 |
Digit 5 | X | 0 | 1 | 1 | 0 | 0x06 | Inhalt Digit5, Zeile 5 |
Digit 6 | X | 0 | 1 | 1 | 1 | 0x07 | Inhalt Digit6, Zeile 6 |
Digit 7 | X | 1 | 0 | 0 | 0 | 0x08 | Inhalt Digit7, Zeile 7 |
Decode Mode | X | 1 | 0 | 0 | 1 | 0x09 | Bei 0 wird nichts dekodiert, sonst kann eine BCD-Dekodierung ausgewählt werden |
Intensity | X | 1 | 0 | 1 | 0 | 0x0A | Das untere Nibble gibt die Helligkeit vor (0..15) |
Scan Limit | X | 1 | 0 | 1 | 1 | 0x0B | Wie viele Stellen/Zeilen angeschlossen sind (0..7) |
Shutdown | X | 1 | 1 | 0 | 0 | 0x0C | Bit0 = 1 Normal, bei Bit0 = 0 gilt Shutdown (aus) |
Display Test | X | 1 | 1 | 1 | 1 | 0x0F | Bit0 = 0 Normal, bei Bit0 = 1 gilt Test, alle LED an |
4. Aufgabe: Einen Würfel bauen
Entwickeln Sie ein Programm für einen elektronischen Würfel mit Matrixanzeige.
Der User-Button dient dabei als Würfeln-Taste. Der Zufall entsteht durch die millis()-Funktion, die die Millisekunden seit dem Reset zählt.
Ein Zustandsdiagramm, eine Tabelle für die Würfelbildausgabe und ein wenig Quellcode sollen den Weg weisen.
wuerfel[] | Zeile 0 | Zeile 1 | Zeile 2 | Zeile 3 | Zeile 4 | Zeile 5 | Zeile 6 | Zeile 7 |
---|---|---|---|---|---|---|---|---|
0 | ||||||||
1 | ||||||||
2 | 0xC0 | 0xC0 | 0x00 | 0x18 | 0x18 | 0x00 | 0x03 | 0x03 |
3 | ||||||||
4 | ||||||||
5 |
Tabelle Lösung
wuerfel[] | Zeile 0 | Zeile 1 | Zeile 2 | Zeile 3 | Zeile 4 | Zeile 5 | Zeile 6 | Zeile 7 |
---|---|---|---|---|---|---|---|---|
0 | 0x00 | 0x00 | 0x00 | 0x18 | 0x18 | 0x00 | 0x00 | 0x00 |
1 | 0xC0 | 0xC0 | 0x00 | 0x00 | 0x00 | 0x00 | 0x03 | 0x03 |
2 | 0xC0 | 0xC0 | 0x00 | 0x18 | 0x18 | 0x00 | 0x03 | 0x03 |
3 | 0xC3 | 0xC3 | 0x00 | 0x00 | 0x00 | 0x00 | 0xC3 | 0xC3 |
4 | 0xC3 | 0xC3 | 0x00 | 0x18 | 0x18 | 0x00 | 0xC3 | 0xC3 |
5 | 0xC3 | 0xC3 | 0x00 | 0xC3 | 0xC3 | 0x00 | 0xC3 | 0xC3 |
Codevorgabe
#include <SPI.h>
#define CS D10 // CS Signal auf D10
#define USER_BUTTON PC13 // Zum Umschalten des Musters
typedef enum {ANZEIGEN,WUERFELN} zustandstyp; // Aufzaehlungstyp
zustandstyp zustand = ANZEIGEN; // Startzustand
unsigned char zahl=0;
const unsigned char wuerfel[6][8]= {{}, // Muster fuer Wuerfel
{},
{0xC0,0xC0,0x00,0x18,0x18,0x00,0x03,0x03},
{},
{},
{}};
void send_data (unsigned char msb, unsigned char lsb){ // Hilfsfunktion zur Datenuebertragung
digitalWrite(CS, LOW); // Uebertragung beginnt
SPI.transfer(msb); // 8 Bit Transfer MSB
SPI.transfer(lsb); // 8 Bit Transfer LSB
//SPI.transfer16(msb<<8 | lsb); // 16 Bit Transfer
digitalWrite(CS, HIGH); // Uebertragung fertig, uebernehme Daten!
}
void setup(){
pinMode(USER_BUTTON, INPUT); // Zum Umschalten des Musters
pinMode(CS, OUTPUT);
digitalWrite(CS, HIGH);
SPI.begin(); // SPI-Schnittstelle einrichten fuer MAX7219
SPI.setBitOrder(MSBFIRST); // Hoechstwertiges Bit zuerst
SPI.setClockDivider(SPI_CLOCK_DIV32); // SCK mit 1 MHz, koennte auch 10 MHz
SPI.setDataMode(SPI_MODE0); // SCK in Ruhe Low, Uebername bei steigender Flanke
delay(10); // kurz warten bis verdaut..
// MAX7219 Initialisierung des Chips
send_data(0x09, 0x00); // Decoding: Aus: keine BCD Dekodierung fuer 7 Segment
send_data(0x0A, 0x03); // Intesity: Helligkeit auf 5 von 15 einstellen
send_data(0x0B, 0x07); // Scan Limit: Es sind 8 Zeilen mit LEDs
send_data(0x0C, 0x01); // Shutdown: Bit0 = 1: Normal operation mode, kein Ruhezustand
send_data(0x0F, 0x01); // Display Test: Bit0 = 1: Displaytest An: Alle LEDs an!
delay(500); // 500 ms delay
send_data(0x0F,0x00); // Display Test: Bit0 = 0: Displaytest Aus: Anzeigen was in Register steht
for(int adr=1; adr<=8; adr++){ // alle LEDs ausschalten, sonst wird zufaelliges Muster ausgegeben
send_data(adr,0x00); // Zeile = 0
}
delay(500); // 500 ms delay
anzeigen();
}
void anzeigen(){
}
void loop(){
switch(zustand){
}
}
Aufgabe: Beim Würfeln heller leuchten
Erweitern Sie das Programm entsprechend des Zustandsdiagramms, damit beim Würfeln (solange USER_BUTTON) die Anzeige etwas heller leuchtet.
Lösungsvorschlag
#include <SPI.h>
#define CS D10 // CS Signal auf D10
#define USER_BUTTON PC13 // Zum Umschalten des Musters
typedef enum {ANZEIGEN,WUERFELN} zustandstyp; // Aufzaehlungstyp
zustandstyp zustand = ANZEIGEN; // Startzustand
unsigned char zahl=0;
const unsigned char wuerfel[6][8]= {{0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00}, // Muster fuer Wuerfel
{0xC0,0xC0,0x00,0x00,0x00,0x00,0x03,0x03},
{0xC0,0xC0,0x00,0x18,0x18,0x00,0x03,0x03},
{0xC3,0xC3,0x00,0x00,0x00,0x00,0xC3,0xC3},
{0xC3,0xC3,0x00,0x18,0x18,0x00,0xC3,0xC3},
{0xC3,0xC3,0x00,0xC3,0xC3,0x00,0xC3,0xC3}};
void send_data (unsigned char msb, unsigned char lsb){ // Hilfsfunktion zur Datenuebertragung
digitalWrite(CS, LOW); // Uebertragung beginnt
SPI.transfer(msb); // 8 Bit Transfer MSB
SPI.transfer(lsb); // 8 Bit Transfer LSB
//SPI.transfer16(msb<<8 | lsb); // 16 Bit Transfer
digitalWrite(CS, HIGH); // Uebertragung fertig, uebernehme Daten!
}
void setup(){
pinMode(USER_BUTTON, INPUT); // Zum Umschalten des Musters
pinMode(CS, OUTPUT);
digitalWrite(CS, HIGH);
SPI.begin(); // SPI-Schnittstelle einrichten fuer MAX7219
SPI.setBitOrder(MSBFIRST); // Hoechstwertiges Bit zuerst
SPI.setClockDivider(SPI_CLOCK_DIV32); // SCK mit 1 MHz, koennte auch 10 MHz
SPI.setDataMode(SPI_MODE0); // SCK in Ruhe Low, Uebername bei steigender Flanke
delay(10); // kurz warten bis verdaut..
// MAX7219 Initialisierung des Chips
send_data(0x09, 0x00); // Decoding: Aus: keine BCD Dekodierung fuer 7 Segment
send_data(0x0A, 0x03); // Intesity: Helligkeit auf 5 von 15 einstellen
send_data(0x0B, 0x07); // Scan Limit: Es sind 8 Zeilen mit LEDs
send_data(0x0C, 0x01); // Shutdown: Bit0 = 1: Normal operation mode, kein Ruhezustand
send_data(0x0F, 0x01); // Display Test: Bit0 = 1: Displaytest An: Alle LEDs an!
delay(500); // 500 ms delay
send_data(0x0F,0x00); // Display Test: Bit0 = 0: Displaytest Aus: Anzeigen was in Register steht
for(int adr=1; adr<=8; adr++){ // alle LEDs ausschalten, sonst wird zufaelliges Muster ausgegeben
send_data(adr,0x00); // Zeile = 0
}
delay(500); // 500 ms delay
anzeigen();
}
void anzeigen(){
for(int adr=1; adr<9; adr++){ // alle Zeilen ausgeben
send_data(adr,wuerfel[zahl][adr-1]); //MSB = digit 1-8 Spalte LSB Segment zeile high aktiv
}
}
void loop(){
switch(zustand){
case ANZEIGEN:
if(!digitalRead(USER_BUTTON)){
zustand = WUERFELN;
delay(20);
send_data(0x0A, 0x0F);
}
break;
case WUERFELN:
if(digitalRead(USER_BUTTON)){
zustand = ANZEIGEN;
delay(20);
send_data(0x0A, 0x02);
}
zahl=millis()%6;
anzeigen();
}
}
Bonus: 4 Würfel auf einer Anzeige
Es sollen nun 4 Würfel angezeigt werden.
Entwickeln Sie eine Lösung…
ToDo: Lösung erstellen
5. Matrix-Animationen ausgeben: TG IT 12
Bitmuster erstellen z.B. mit [tools.mezmedia.de/LEDCube-Pattern-Tool-master/index.html] Quelle: [github.com/stocka/LEDCube-Pattern-Tool]
Animationscode
#include <SPI.h>
#define CS D10
struct imElt { // Datenstruktur fuer Daten und Dauer der Anzeige
unsigned char z[8];
unsigned long int imageDuration;
}
const imageTab[] = { // TG IT 12
{ 0b00000000, 0b11100110, 0b01001000, 0b01001000, 0b01001010, 0b01001001, 0b01000110, 0b00000000, 10000 },
{ 0b00000000, 0b01011100, 0b01001000, 0b01001000, 0b01001000, 0b01001000, 0b01001000, 0b00000000, 10000 },
{ 0b00000000, 0b00100100, 0b01101010, 0b00100010, 0b00100100, 0b00101000, 0b00101110, 0b00000000, 10000 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0 },
};
void send_data (unsigned char msb, unsigned char lsb){ // Hilfsfunktion
digitalWrite(CS, LOW); // Uebertragung beginnt
SPI.transfer(msb); // 8 Bit
SPI.transfer(lsb);
//SPI.transfer16(msb<<8 | lsb); // 16 Bit
digitalWrite(CS, HIGH); // Uebertragung fertig
}
void setup(){
pinMode(CS, OUTPUT);
digitalWrite(CS, HIGH);
SPI.begin(); // SPI-Schnittstelle einrichten fuer MAX7219
SPI.setBitOrder(MSBFIRST); // Hoechstwertiges Bit zuerst
SPI.setClockDivider(SPI_CLOCK_DIV32); // SCK mit 1 MHz, koennte auch 10 MHz
SPI.setDataMode(SPI_MODE0); // SCK in Ruhe Low, Uebername bei steigender Flanke
delay(10); // kurz warten bis verdaut..
// MAX7219 Initialisierung des Chips
send_data(0x09, 0x00); // Decoding: Aus: keine BCD Dekodierung fuer 7 Segment
send_data(0x0A, 0x05); // Intesity: Helligkeit auf 5 von 15 einstellen
send_data(0x0B, 0x07); // Scan Limit: Es sind 8 Zeilen mit LEDs
send_data(0x0C, 0x01); // Shutdown: Bit0 = 1: Normal operation mode, kein Ruhezustand
send_data(0x0F, 0x01); // Display Test: Bit0 = 1: Displaytest An: Alle LEDs an!
delay(500); // 500 ms delay
send_data(0x0F,0x00); // Display Test: Bit0 = 0: Displaytest Aus: Anzeigen was in Register steht
for(int adr=1; adr<=8; adr++){ // alle LEDs ausschalten, sonst wird zufaelliges Muster ausgegeben
send_data(adr,0x00); // Zeile = 0
}
delay(500); // 500 ms delay
}
void displayImage(int index){ // Daten zur Anzeige uebertragen
unsigned long int duration = imageTab[index].imageDuration;
unsigned char adr;
for(int adr=1; adr<9; adr++){
send_data(adr,imageTab[index].z[adr-1]);
}
delay(duration/10);
}
void loop(){
int i = 0;
do {
displayImage(i);
i++;
} while (imageTab[i].imageDuration != 0); // Bis Animation Ende
}
Weiteres Tool zum Erstellen
Allerdings können damit keine Zeiten eingestellt werden: [xantorohara.github.io/led-matrix-editor/]
ToDo: Code zur Auswertung erstellen…
6. Zwei Anzeigen kaskadieren
Einfach die Ausgänge der ersten Anzeige mit den Eingängen der zweiten Anzeige verbinden. Zunächst ist zu sehen, dass die zweite Anzeige eigentlich das gleiche wie die erste Anzeige ausgibt, es wird ja schlicht mit jedem neuen Transfer der Inhalt des letzten Transfers weiter geschoben. Will man etwas unterschiedliches anzeigen müssen die Daten passend bei der Übernahme durch CS an der jeweiligen Anzeige anliegen, hier ein Testcode:
#include <SPI.h>
#define CS D10 // CS Signal auf D10
#define UserButton PC13 // Zum Umschalten des Musters
const unsigned char tabX[]= {0x81,0x42,0x24,0x18,0x18,0x24,0x42,0x81}; // Muster fuer X
const unsigned char tabO[]= {0x00,0x18,0x24,0x42,0x42,0x24,0x18,0x00}; // Muster fuer o
void send_data (unsigned char msb, unsigned char lsb){ // Hilfsfunktion zur Datenuebertragung
digitalWrite(CS, LOW); // Uebertragung beginnt
SPI.transfer(msb); // 8 Bit Transfer MSB
SPI.transfer(lsb); // 8 Bit Transfer LSB
//SPI.transfer16(msb<<8 | lsb); // 16 Bit Transfer
digitalWrite(CS, HIGH); // Uebertragung fertig, uebernehme Daten!
}
void send_data_2 (unsigned char msb, unsigned char lsb,unsigned char lsb2){ // Hilfsfunktion zur Datenuebertragung
digitalWrite(CS, LOW); // Uebertragung beginnt
SPI.transfer(msb); // 8 Bit Transfer MSB
SPI.transfer(lsb); // 8 Bit Transfer LSB
SPI.transfer(msb); // 8 Bit Transfer MSB
SPI.transfer(lsb2); // 8 Bit Transfer LSB
//SPI.transfer16(msb<<8 | lsb); // 16 Bit Transfer
digitalWrite(CS, HIGH); // Uebertragung fertig, uebernehme Daten!
}
void setup(){
pinMode(UserButton, INPUT); // Zum Umschalten des Musters
pinMode(CS, OUTPUT);
digitalWrite(CS, HIGH);
SPI.begin(); // SPI-Schnittstelle einrichten fuer MAX7219
SPI.setBitOrder(MSBFIRST); // Hoechstwertiges Bit zuerst
SPI.setClockDivider(SPI_CLOCK_DIV32); // SCK mit 1 MHz, koennte auch 10 MHz
SPI.setDataMode(SPI_MODE0); // SCK in Ruhe Low, Uebername bei steigender Flanke
delay(10); // kurz warten bis verdaut..
// MAX7219 Initialisierung des Chips
send_data(0x09, 0x00); // Decoding: Aus: keine BCD Dekodierung fuer 7 Segment
send_data(0x0A, 0x05); // Intesity: Helligkeit auf 5 von 15 einstellen
send_data(0x0B, 0x07); // Scan Limit: Es sind 8 Zeilen mit LEDs
send_data(0x0C, 0x01); // Shutdown: Bit0 = 1: Normal operation mode, kein Ruhezustand
send_data(0x0F, 0x01); // Display Test: Bit0 = 1: Displaytest An: Alle LEDs an!
send_data(0,0); // noch einen weiter schieben, damit Anzeige 2 auch initialisiert ist
delay(500); // 500 ms delay
send_data(0x0F,0x00); // Display Test: Bit0 = 0: Displaytest Aus: Anzeigen was in Register steht
for(int adr=1; adr<=8; adr++){ // alle LEDs ausschalten, sonst wird zufaelliges Muster ausgegeben
send_data(adr,0x00); // Zeile = 0
}
delay(500); // 500 ms delay
}
void loop(){
for(int adr=1; adr<9; adr++){ // alle Zeilen ausgeben
send_data_2(adr,!digitalRead(UserButton)?tabX[adr-1] :tabO[adr-1],digitalRead(UserButton)?tabX[adr-1] :tabO[adr-1]);
delay(1000);
}
7. Aufgaben: Mit MAX7219 7-Segment Modul Zeitzähler bauen
Schließen Sie statt der Punktmatrixanzeige das Modul mit den 8 7-Segmentanzeigen an. Um nun Ziffern anzeigen zu können kann entweder in den BCD-Modus umgeschaltet werden oder mit einem Array const unsigned char tab7Seg[] = {..} die Ziffer umgewandelt werden. Die Reihenfolge der Segmente ist in der Tabelle zu sehen.
Erstellen Sie den Hex-Code für das Array und den Programm-Code für eine Millisekundenanzeige.
Ziffer | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | HexWert |
---|---|---|---|---|---|---|---|---|---|
DP | A | B | C | D | E | F | G | ||
0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0x7E |
Lösungsvorschlag
#include <SPI.h>
#define CS D10 // CS Signal auf D10
const unsigned char tab7Seg[] = {0x7E, 0x30, 0x6D, 0x79, 0x33, 0x5B, 0x5F, 0x70, 0x7F, 0x7B}; // Umwandlung
void send_data (unsigned char msb, unsigned char lsb){ // Hilfsfunktion zur Datenuebertragung
digitalWrite(CS, LOW); // Uebertragung beginnt
SPI.transfer(msb); // 8 Bit Transfer MSB
SPI.transfer(lsb); // 8 Bit Transfer LSB
//SPI.transfer16(msb<<8 | lsb); // 16 Bit Transfer
digitalWrite(CS, HIGH); // Uebertragung fertig, uebernehme Daten!
}
void setup(){
pinMode(CS, OUTPUT);
digitalWrite(CS, HIGH);
SPI.begin(); // SPI-Schnittstelle einrichten fuer MAX7219
SPI.setBitOrder(MSBFIRST); // Hoechstwertiges Bit zuerst
SPI.setClockDivider(SPI_CLOCK_DIV32); // SCK mit 1 MHz, koennte auch 10 MHz
SPI.setDataMode(SPI_MODE0); // SCK in Ruhe Low, Uebername bei steigender Flanke
delay(10); // kurz warten bis verdaut..
// MAX7219 Initialisierung des Chips
send_data(0x09, 0x00); // Decoding: Aus: keine BCD Dekodierung fuer 7 Segment
//send_data(0x09, 0xFF); // Decoding: An: BCD Dekodierung fuer 7 Segment
send_data(0x0A, 0x05); // Intesity: Helligkeit auf 5 von 15 einstellen
send_data(0x0B, 0x07); // Scan Limit: Es sind 8 Zeilen mit LEDs
send_data(0x0C, 0x01); // Shutdown: Bit0 = 1: Normal operation mode, kein Ruhezustand
send_data(0x0F, 0x01); // Display Test: Bit0 = 1: Displaytest An: Alle LEDs an!
delay(500); // 500 ms delay
send_data(0x0F,0x00); // Display Test: Bit0 = 0: Displaytest Aus: Anzeigen was in Register steht
for(int adr=1; adr<=8; adr++){ // alle LEDs ausschalten, sonst wird zufaelliges Muster ausgegeben
send_data(adr,0x00); // Zeile = 0
}
delay(500); // 500 ms delay
}
void loop(){
unsigned long ausgabe=millis(); // 87654321;
for(int adr=1; adr<9; adr++){ // alle Stellen ausgeben
send_data(adr,tab7Seg[ausgabe%10]); // ohne Verwendung des Dekoders mit eigener Tabelle
//send_data(adr,ausgabe%10); // falls der BCD-Dekoder verwendet wird
ausgabe/=10;
}
}
Aufgabe: Führende Nullen unterdrücken
Die führenden Nullen sollen nun unterdrückt werden, ausserdem soll ein Dezimalpunkt bei Sekunde eingefügt werden.
Tipp: Verwenden Sie ein Array unsigned char stelle[8] in dem Sie zunächst die Ausgabe einspeichern und bei der Ausgabe bei den führenden Nullen entweder 0xF bei Verwendung des BCD-Modus oder tab7Seg[8] mit Erweiterung der Tabelle um einen Element 0x00 ausgeben. Mit | 0x80 lässt sich der Dezimalpunkt einschalten.
Erstellen Sie den Code.
Lösungsvorschlag
void loop(){
unsigned long ausgabe=millis();
unsigned char stelle[8];
for(int i=0;i<8;i++){
stelle[i]=ausgabe%10;
if (i==3) stelle[i]|=0x80; // Dezimalpunkt einbauen
ausgabe/=10;
}
unsigned char blank=1;
for(int adr=8; adr>=1; adr--){ // alle Stellen ausgeben
if(blank && stelle[adr-1]==0){
send_data(adr,0); // Blank ausgeben ohne BCD-Dekoder
//send_data(adr,0xF); // Blank ausgeben mit BCD-Dekoder
}
else{
send_data(adr,tab7Seg[ausgabe%10]); // ohne Verwendung des BCD-Dekoders mit eigener Tabelle
//send_data(adr,stelle[adr-1]); // mit BCD-Dekoder
blank=0;
}
}
}
Aufgabe: Anzeige von millis() in Stunden.Minuten.Sekunden.Hundertstel
Die Millisekunden sollen nun im Format Stunden.Minuten.Sekunden.Hundertstel angezeigt werden: 2.59.59.99
Verwenden Sie diesmal den BCD-Dekoder des Bausteins, ausserdem sollen dabei führende Nullen unterdrückt werden.
Code-Tipp anzeigen
stelle[3]=ausgabe%6;
ausgabe/=6;
stelle[4]=ausgabe%10 | 0x80; // Minuten mit Dezimalpunkt
ausgabe/=10;
Lösungsvorschlag
void loop(){
unsigned long ausgabe=millis();
unsigned char stelle[8]={0,0,0,0,0,0,0,0};
ausgabe/=10; // in hundertstel umwandeln
stelle[0]=ausgabe%10; // hundertstel
ausgabe/=10;
stelle[1]=ausgabe%10; // zehntel
ausgabe/=10;
stelle[2]=ausgabe%10 | 0x80; // Sekunden mit Dezimalpunkt
ausgabe/=10;
stelle[3]=ausgabe%6;
ausgabe/=6;
stelle[4]=ausgabe%10 | 0x80; // Minuten mit Dezimalpunkt
ausgabe/=10;
stelle[5]=ausgabe%6;
ausgabe/=6;
stelle[6]=ausgabe%10 | 0x80; // Stunden mit Dezimalpunkt
ausgabe/=10;
stelle[7]=ausgabe%10;
unsigned char blank=1;
for(int adr=8; adr>=1; adr--){ // alle Stellen ausgeben
if(blank && (stelle[adr-1]&~0x80)==0){
send_data(adr,0xF); // Blank ausgeben
}
else{
send_data(adr,stelle[adr-1]);
blank=0;
}
}
}
🚧 Aufgabe: Countdownzähler bis ABI-Prüfung
Eine 7-Segmentanzeige zeigt die verbleibende Zeit in Tagen 0.9999 4-stellig mit unterdrückten führenden Nullen bis zur zur Prüfung an z.B: ABI 60d siehe Bild. Wegen der Buchstaben kann der BCD-Dekoder des Bausteins nicht verwendet werden, Sie benötigen eine Umrechnungstabelle für die Ziffern 0..9 tab7Seg[] und die Kenntnis der Hexcodes für A,B,C,d,!.
Ziffer | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | HexWert |
---|---|---|---|---|---|---|---|---|---|
DP | A | B | C | D | E | F | G | ||
A | 0 | 1 | 1 | 1 | 0 | 1 | 1 | 1 | 0x77 |
B | |||||||||
I | |||||||||
d | |||||||||
! |
Die Restzeit in Tagen kann mit Datumseingabe im Format ttmmjj zweier Tage (ABI-Datum und heute) über Serielle Schnittstelle oder stellenweise mittels Tasten PB0,PB3,PB4 eingegeben werden (die PA-Tasten kollidieren mit der SPI-Schnittstelle). Um die Tagedifferenz zu ermitteln wird das Datum auf das Julianische Datum🔗 umgerechnet.
long julianTag(int tag, int monat, int jahr){ // Umrechnen auf Tag
jahr+=8000;
if(monat<3) { jahr--; monat+=12; }
return (jahr*365L) +(jahr/4) -(jahr/100) +(jahr/400) -1200820 +(monat*153+3)/5-92 +tag-1;
}
Teilaufgabenideen
- Serielle Schnittstelle erklären und Timingdiagramm analysieren.
- ISR einstellen und Array mit Resttagen erniedrigen
- Zustandsdiagramm in Code umsetzen
- Helligkeit der Anzeige einstellen
Lösungsvorschlag
#include <SPI.h>
#define CS D10 // CS Signal auf D10
static HardwareTimer mytimer = HardwareTimer(TIM2); // Timerinstanz sowie Timeruaswahl
const unsigned char tab7Seg[] = {0x7E, 0x30, 0x6D, 0x79, 0x33, 0x5B, 0x5F, 0x70, 0x7F, 0x7B}; // Umwandlung
unsigned char tage[4]={0,0,0,0}; // Tageanzahl mit 4 Stellen 0..9999 Einer zuerst
const unsigned char maxDatum[3]={31,12,99}; // Maximalwerte bei Datumseingabe via UART
int stelle; // Stelle zum Einstellen 0..3
int helligkeit=5;
int tagessekunden=0; // max: 60*60*24 = 86400
long tagedifferenz=0;
enum zustandstyp {ANZEIGEN,STELLEN}; // definiere Aufzählungstyp
enum zustandstyp zustand = ANZEIGEN; // definiere und initialisiere Variable
#define BUTTONREADER GPIOB->IDR & 0b00000011001 // nur die relevanten Tastenbits
int buttonOld = 0; // alter Tasten-Zustand
int buttonEnter,buttonExit; // gedrueckte und losgelassene Tasten
void buttonCheck(){ // Tastaturabfrage mit Flankendedektion
int buttonTest,tmp;
buttonEnter = 0, buttonExit = 0;
buttonTest = BUTTONREADER; // Einlesen
if (buttonOld != buttonTest){ // hat sich was getan
delay(10); // 5ms Prellen abwarten
tmp = BUTTONREADER; // noch mal Einlesen
if (tmp == buttonTest){ // ist es stabil?
buttonEnter = (~buttonOld) & buttonTest; // steigende Flanke !alt und neu
buttonExit = buttonOld & (~buttonTest); // fallende Flanke alt und !neu
buttonOld = buttonTest;
}
}
}
long julianDay(int tag, int monat, int jahr){ // Rückgabewert dieser Funktion ist "long"
jahr+=8000;
if(monat<3) { jahr--; monat+=12; }
return (jahr*365L) +(jahr/4) -(jahr/100) +(jahr/400) -1200820 +(monat*153+3)/5-92 +tag-1;
}
void isr_Sec(){ // Sekunden verarbeiten
tagessekunden++;
if(tagessekunden>=86400-1){ // ein Tag ist rum, Resttage erniedrigen 86400-1
tagessekunden=0;
int i=0;
while(i<4){
if(tage[i]>0){ // abziehen möglich
tage[i]--;
break;
}
tage[i]=9;
i++;
}
if (tage[0]==0 && tage[1]==0 && tage[2]==0 && tage[3]==0){
mytimer.pause();
for(i=0;i<6;i++){
send_data(i,0xB0); // ! ausgeben
}
return;
}
}
ausgebenZeit(); // Zeit ausgeben
}
void ausgebenZeit(){ // Zeit auf Anzeige ausgeben
static int n=0;
n++;
unsigned char blank=1;
for(int adr=5; adr>=2; adr--){ // alle Stellen ausgeben
if(zustand==STELLEN && n%8 && adr-2!=stelle){ // dunkler machen der anderen Stellen
send_data(adr,0x00); // Blank ausgeben
}
else if(blank && tage[adr-2]==0){
if(zustand==STELLEN) send_data(adr,tab7Seg[0]); // 0 ausgeben
else send_data(adr,0x00); // Blank ausgeben
}
else{
send_data(adr,tab7Seg[tage[adr-2]]);
blank=0;
}
}
if(tagessekunden&1)send_data(1,0); // 'd' blinken lassen..
else send_data(1,0x3D); // d
}
void stelleUhrMitString(String s){ // Zeit mittels String einstellen
unsigned char datum[3]={0,0,0}; // Tag Monat Jahr
if(s.length()!=6){
Serial.printf("Laenge stimmt nicht: %d ",s.length());
Serial.println(s); // printf ist bei %s gerade kaputt..
return;
}
int i,c,t; // temporäre Variablen
for(i=0;i<6;i++){ // String durchlaufen
c=s[i];
if (c>='0' && c<='9'){ // ist es eine Ziffer?
t= (c-'0')*10; // Ziffer in Wert umwandlen
}
else goto fehler;
i++;
c=s[i];
if (c>='0' && c<='9'){
t+= c-'0';
}
else goto fehler;
if(t>maxDatum[i/2]) goto fehler;
datum[i/2]=t;
}
t=julianDay(datum[0],datum[1],datum[2]+2000);
Serial.printf("JulianTag: %d \n",t);
if(tagedifferenz==0) tagedifferenz=t;
else if(tagedifferenz>t)tagedifferenz-=t;
else tagedifferenz=t-tagedifferenz;
Serial.printf("tagedifferenz: %d \n",tagedifferenz);
t=tagedifferenz;
for(i=0;i<4;i++){ // Stellen einstellen
tage[i]=t%10;
t/=10;
}
return;
fehler:
Serial.printf("Ziffer stimmt nicht an Stelle: %d ",i);
Serial.println(s); // printf ist bei %s gerade kaputt..
}
void incStelle(){ // Stelle erhöhen
if(tage[stelle]<9){
tage[stelle]++;
}
else tage[stelle]=0;
}
void decStelle(){ // Stelle erniedrigen
if(tage[stelle]==0){
tage[stelle]=9;
}
else tage[stelle]--;
}
void send_data (unsigned char msb, unsigned char lsb){ // Hilfsfunktion zur Datenuebertragung
digitalWrite(CS, LOW); // Uebertragung beginnt
SPI.transfer(msb); // 8 Bit Transfer MSB
SPI.transfer(lsb); // 8 Bit Transfer LSB
//SPI.transfer16(msb<<8 | lsb); // 16 Bit Transfer
digitalWrite(CS, HIGH); // Uebertragung fertig, uebernehme Daten!
}
void setup(){
pinMode(CS, OUTPUT);
digitalWrite(CS, HIGH);
pinMode(PB0,INPUT_PULLDOWN); // Zeit einstellen
pinMode(PB3,INPUT_PULLDOWN); // rauf
pinMode(PB4,INPUT_PULLDOWN); // runter
SPI.begin(); // SPI-Schnittstelle einrichten fuer MAX7219
SPI.setBitOrder(MSBFIRST); // Hoechstwertiges Bit zuerst
SPI.setClockDivider(SPI_CLOCK_DIV32); // SCK mit 1 MHz, koennte auch 10 MHz
SPI.setDataMode(SPI_MODE0); // SCK in Ruhe Low, Uebername bei steigender Flanke
delay(10); // kurz warten bis verdaut..
// MAX7219 Initialisierung des Chips
send_data(0x09, 0x00); // Decoding: Aus: keine BCD Dekodierung fuer 7 Segment
//send_data(0x09, 0xFF); // Decoding: An: BCD Dekodierung fuer 7 Segment
send_data(0x0A, 0x05); // Intesity: Helligkeit auf 5 von 15 einstellen
send_data(0x0B, 0x07); // Scan Limit: Es sind 8 Zeilen mit LEDs
send_data(0x0C, 0x01); // Shutdown: Bit0 = 1: Normal operation mode, kein Ruhezustand
send_data(0x0F, 0x01); // Display Test: Bit0 = 1: Displaytest An: Alle LEDs an!
delay(300);
send_data(0x0F,0x00); // Display Test: Bit0 = 0: Displaytest Aus: Anzeigen was in Register steht
send_data(8,0x77); // A
send_data(7,0x7F); // B
send_data(6,0x06); // I
send_data(1,0x3D); // d
ausgebenZeit();
mytimer.setOverflow(1000); // 1000 ms
mytimer.setPrescaleFactor(32000); // 1 kHz
mytimer.attachInterrupt(isr_Sec); // Sekunde ist rum
Serial.begin(9600); // Serielle Schnittstelle mit 9600 Baud starten
}
#define T_EINSTELLEN buttonEnter&(1<<0)
#define T_HOCH buttonEnter&(1<<3)
#define T_RUNTER buttonEnter&(1<<4)
void loop(){
String s;
buttonCheck();
switch (zustand){
case ANZEIGEN:
if(Serial.available()){ // wenn Zeichen gesendet wurden
s = Serial.readStringUntil('\n'); // lese Daten in String ein
stelleUhrMitString(s);
tagessekunden=0;
mytimer.resume();
}
if(T_EINSTELLEN){ // wenn Taste PB0 gedrückt
zustand=STELLEN;
stelle=0;
mytimer.pause(); // Timer stoppen
}
if(T_HOCH){ // wenn Taste PB3 gedrückt
if(helligkeit<15) helligkeit++;
send_data(0x0A, helligkeit);
}
if(T_RUNTER){ // wenn Taste PB4 gedrückt
if(helligkeit>0) helligkeit--;
send_data(0x0A, helligkeit);
}
break;
case STELLEN:
ausgebenZeit();
if(T_EINSTELLEN){ // wenn Taste PB0 gedrückt
if(stelle==3){
zustand=ANZEIGEN;
tagessekunden=0;
mytimer.resume(); // Timer starten
}
else{
stelle++;
}
}
if(T_HOCH){ // wenn Taste PB3 gedrückt
incStelle();
}
if(T_RUNTER){ // wenn Taste PB4 gedrückt
decStelle();
}
}
delay(2);
}
8. 🚧 RFID-Modul MFRC522
Synopsis: [funduino.de/nr-18-rfid-kit] [Arduino RFID Library for MFRC522] [www.nxp.com/docs/en/data-sheet/MFRC522.pdf] [Security Access using MFRC522 RFID Reader with Arduino]
Die günstig zu erwerbenden RFID-Module werden über die SPI-Schnittstelle angeschlossen, obgleich der Chip auch I2C und RS232 könnte.
Folgender Code soll die UID („Unique Identification Number“) einer Karte aus lesen und ausgeben. Dazu muss die Library MFRC522 Installiert werden siehe z.B. funduino.de/nr-18-rfid-kit . Leider wird dabei eine Fehlermeldung wegen Pointer-Vergleich angegeben, die Library muss für STM32 modifiziert werden:
Statt > 0 sollte ein != 0 in den Zeilen der Fehlermeldung stehen:
// in MFRC522Extended.cpp Zeile 824 und 847
if (backData && (backLen != 0)) { // statt backLen > 0
#include "MFRC522.h" // RFID-Bibiothek hinzufügen
#define SDA 10 // SS bzw. CS
#define RST 9 // Reset bzw. Sleep bei Log. 0
MFRC522 mfrc522(SDA, RST); // RFID-Empfänger benennen, Pins zuordnen
void setup() {
Serial.begin(9600);
SPI.begin();
mfrc522.PCD_Init(); // Initialisierung des RFID-Empfängers
}
void loop() {
String WertHEX;
if (!mfrc522.PICC_IsNewCardPresent()) { // Wenn keine Karte in Reichweite ist ..
// .. wird die Abfrage wiederholt.
return;
}
if (!mfrc522.PICC_ReadCardSerial()) { // Wenn kein RFID-Sender ausgewählt wurde ..
// .. wird die Abfrage wiederholt.
return;
}
Serial.println("Karte entdeckt!");
for (byte i = 0; i < mfrc522.uid.size; i++) { // für alle Werte
WertHEX = WertHEX + String(mfrc522.uid.uidByte[i], HEX) + " ";
}
Serial.println("Hexwerte: " + WertHEX);
delay(1000);
}
Mit dem Logic Analyzer wurde die ständige Abfrage ob ein RFID-Tag vorhanden ist aufgenommen:
Aufgaben
- Erklären Sie am Beispiel des Timing-Diagramms die Funktionsweise der SPI Datenübertragung.
- Mit welchem SPI-Mode werden die Daten übertragen, Begründung.
- Wird MSB- oder LSB-First übertragen, Begründung.
- Mit welcher Frequenz wird übertragen, begründete Abschätzung reicht.
9. 🤐 LED&KEY Module mit TM1638
In den Abi-Übungen taucht ein für mich neues Shield auf: [amzon] Doku fand ich das, leider kein schönes Datenblatt dabei:
- https://www.handsontec.com/dataspecs/display/TM1638.pdf
- 😄 https://g-heinrichs.de/attiny/module/TM1638.pdf
- https://jetpackacademy.com/wp-content/uploads/2018/06/TM1638_cheat_sheet_download.pdf
- http://softgeniedoc.dk/contents/projects/TM1638/TM1638en.pdf
Dachte erst es wäre mit SPI einfach, aber besser nutzt man wohl die Library. Hab ein paar Dinger bestellt, ob ich sie gut finden werde?