Nicht standardmäßige Schaltung: Sieben-Segment-Anzeige bei ATtiny13

Wir suchen nicht nach einfachen Wegen.


Bild

Zuvor war es meine erste Veröffentlichung, die bei den Nutzern von Habr für Resonanz sorgte. Beschlossen, nicht aufzuhören. Wir quetschen weiterhin das Unmögliche aus ATtiny13 heraus. Ich warne Sie sofort, die beschriebenen Lösungen sind wieder nicht standardisiert und jemand kann Empörung und kognitive Dissonanz verursachen ("Und worum geht es dann im Artikel? Zeigen Sie, welche Elemente verbunden werden können?"). Darüber hinaus ist eine solche Lösung auch wirklich unpraktisch, worauf ich weiter unten näher eingehen werde. Aber so kam es, dass Standardlösungen seit langem bekannt sind und das Lesen darüber nicht immer interessant ist, aber das Schreiben ist undankbar.

Ich mag dieses Kind ATtiny13 wirklich. Er hat genug Verstand, um viele alltägliche Probleme zu lösen (Licht einschalten, Belüftung, Bierladen ). Und der Preis ist einfach lächerlich. Hier sind nur ein paar Beine und es gibt überhaupt keine Griffe. Daher müssen Sie alle möglichen Tricks anwenden, um das Problem des Beinmangels zu lösen.

Während ich die Programmierung von Mikrocontrollern studierte (in der Arduino-Umgebung sagen Sie es einfach niemandem), habe ich, wie viele andere auch, den Schritt des Anschlusses eines Ultraschall-Abstandssensors durchlaufen, etwa so:

Bild

Da die Methode zur Übertragung von Informationen vom Sensor zur Steuerung einfach zu blamieren ist, kann ATtiny13 dies problemlos bewältigen. Dann war es notwendig, Informationen auf einem Sieben-Segment-Indikator unter Verwendung von Schieberegistern anzuzeigen. Das heißt, das Schema des Anzeigeteils war um ein Vielfaches größer als das der Steuerung selbst. In diesem Moment spielte ich herum und ging weiter.

Kürzlich dachte ich, was wäre sonst eine überwältigende Aufgabe, Tinka zuzuweisen? Was sie in den beschriebenen Beispielen noch nicht bewältigen konnte. Das erste, woran ich mich bei der Indikation erinnerte. Vor einiger Zeit habe ich bereits nach Informationen zu einem ähnlichen Thema gesucht. Dann fand ich eine so interessante Option.

Bild
Bild

21 Segmente von 5 Fuß des Controllers. Wow! Ich brauche nicht einmal so viel, zwei Zeichen reichen aus, plus ein Punkt, insgesamt 15 Segmente. Und wenn mit vier Beinen? Dann erhalten Sie maximal 13 Segmente, nicht genug. Beim Anblick der Schaltung entstand sofort der Wunsch, zusammenzubauen und zu versuchen, obwohl es nicht einfach ist, einen Operationsalgorithmus zu erstellen. Bei näherer Betrachtung verstehen Sie jedoch, dass das Sammeln nicht funktioniert. Solche sieben segmentierten Tiere gibt es in der Natur (höchstwahrscheinlich) nicht. Sie können natürlich machen, aber dies ist eine andere Ebene. Dann wurde die Idee auf bessere Zeiten verschoben.

Offtopic: Warum gibt es keine Sieben-Segment-Indikatoren mit integrierter Logik? Wo suchen die Entwickler? Wie bequem ist die Installation und Steuerung - zwei Power-Legs und 3 (1, 2) Daten-Legs. Und schließlich waren sie sogar in der UdSSR: 490IP1, 490IP2. In der gewöhnlichsten Anzeige für 2 ... 4 Stellen ist viel Platz für die Platzierung der Chip-Mikroschaltung, und der Preis für das Schieberegister beträgt zusammen mit dem Gehäuse 0,064 cu. Na ja.

Und so dachte ich noch einmal darüber nach, wie ich die Anzahl der Beine für die Arbeit mit dem Sieben-Segment-Indikator reduzieren kann. Die Controller-Ausgänge können drei Zustände annehmen (tatsächlich 4, aber jetzt spielt es keine Rolle). Gibt es eine Möglichkeit, dies zu nutzen? Wenn zwei Zustände in Bezug auf die LED nur als leuchtend interpretiert werden können, nicht als leuchtend, dann ist es mit drei etwas interessanter. Ich habe noch nicht herausgefunden, wie ich es verwenden soll, aber das folgende Schema kam mir in den Sinn:

Bild

Wenn sich der Controller-Ausgang im Nullzustand befindet, leuchten die LEDs nicht auf (was offensichtlich ist).

Befindet sich der Ausgang im Gerätezustand, leuchten die LEDs, was ebenfalls verständlich ist.

Wenn der Ausgang jedoch überhaupt nicht der Ausgang ist, sondern auf den Eingang eingeschaltet ist, fließt ein Strom durch die Schaltung zweier Widerstände und der LED HL1, wodurch am Verbindungspunkt der Widerstände ein Spannungsabfall von ungefähr (5-1,7) / (2,2 + 1,5) entsteht. * 1,5 + 1,7 = 3,0 V. Dies reicht nicht aus, damit der Strom durch den Stromkreis VD1_R3_HL2 fließt (ungefähr 3,4 V werden benötigt). VD1 ist eine zusätzliche LED, die als Zenerdiode verwendet wird (der Stabilisator ist korrekter), daher werden wir sie nicht als LED betrachten, um nicht verwirrt zu werden. Es spielt keine Rolle, ob der Pull-up-Widerstand im Mikrocontroller eingeschaltet ist, sein Widerstand (20 kOhm) hat praktisch keinen Einfluss auf die Situation. Ich bin nicht sofort zu solchen Bewertungen gekommen, bevor ich es mit einer normalen Diode wie VD1 versucht habe, funktioniert es auch ziemlich gut mit den gleichen Widerständen R1 und R2. Aber es ist besser, dass R2 ungefähr eineinhalb Mal größer ist als R1. Und das Wichtigste habe ich fast vergessen: Alles Beschriebene ist nur mit roten und zusätzlichen LEDs möglich. In extremen Fällen können entweder die Anzeige oder zusätzliche LEDs grün angelegt werden. Und mit einer Versorgungsspannung von 4,5 V bis 5 V.

Was haben wir am Ende? Drei Zustände: Keine LED (0) leuchtet, HL1 (1) leuchtet oder HL1 und HL2 (2) leuchten. Sehr ähnlich dem ternären System. Aber wir können HL2 nicht ohne HL1 beleuchten, das muss beachtet werden. Aber jetzt können wir mit Hilfe der vier Beine des Mikrocontrollers acht LEDs steuern (das wollte ich mir denken).

Dann habe ich versucht, die Indikatorsegmente in Paare aufzuteilen (genau wie in einem Kindergarten: ein Junge-Mädchen). Die Hauptbedingung ist, dass in jedem Paar eines der Segmente nicht für sich allein leuchten kann, eine solche Unterscheidung. Folgendes habe ich bekommen:

Bild

Vier Segmentpaare, in jedem Großbuchstaben ist das dominante Segment angegeben, das für sich arbeiten kann, das zweite nur mit ihm. Möglicherweise stellen Sie fest, dass sich das Segment „a“ mit zwei gleichzeitig aufrührt und niemand den schlechten Punkt erreicht hat. Wie es aussieht wie das Leben!

Aber mit diesen Paaren können Sie (fast) alle Zahlen anzeigen:

Bild
Bild

Jedes Paar ist in seiner eigenen Farbe bemalt. Ein aufmerksamer Betrachter bemerkte, dass etwas mit der Zwei nicht stimmte. Wir werden uns vorerst nicht darauf konzentrieren. Ich habe ein paar weitere Optionen zum Gruppieren von Segmenten ausprobiert, besser nicht. Vielleicht schlägt jemand vor. Vielleicht könnte das neuronale Netzwerk damit umgehen.

In der zweiten Phase der Experimente musste ich einen Indikator mit einer gemeinsamen Anode verwenden. Daher lautet das endgültige Schema wie folgt:

Schema
Bild

Jemand könnte fragen: Wo sind die 100-Ohm-Widerstände geblieben? Es ist seit langem bekannt (und wird aktiv genutzt), dass bei korrekt gemachten dynamischen Anzeigen auf strombegrenzende Widerstände verzichtet werden kann. Selbst wenn die Spannung vom Ausgang des Controllers ständig an die beiden versehentlich in Reihe geschalteten LEDs angelegt wird, halten der Mikrocontroller und die LEDs dies normalerweise aus, wird der Strom durch den Übergangswiderstand im MC begrenzt. Und mehr über Widerstände. Der maximale Strom durch HL1 gemäß dem vorherigen Schema beträgt ungefähr 2 mA und durch HL2 erreicht er 25 ... 40 mA (vermutlich werde ich Ihnen später sagen, woher diese Zahlen stammen). Dies bedeutet, dass die Lichtleistung verschiedener Segmente unterschiedlich ist. Da jedoch eine dynamische Anzeige verwendet wird, kann dies aufgrund der unterschiedlichen Anzeigezeit der Segmente leicht gelöst werden.

Alle Experimente habe ich am Arduino Nano in der Arduino IDE durchgeführt. Ein exzellentes Prototyping-Board, es wird gut im Steckbrett, es wird problemlos über USB geflasht. Ist etwas gescheitert? Ich habe die Skizze repariert und in einer Minute eine neue Firmware hochgeladen. Und wenn Sie den Code debuggt haben, können Sie die Firmware in ATtiny13 aufrufen, es sind noch ein wenig mehr Gesten erforderlich.

Übrigens blinke ich auch mit Arduino in der Arduino-Umgebung, dies eliminiert praktisch die Möglichkeit, MK mit den falschen Sicherungen zu verriegeln, und ist viel einfacher.

Hier ist ein Beispiel für die Anzeige von 4 im Code:

pinMode(f_a, INPUT); //    f_a   digitalWrite(f_a, 1); //   F pinMode(d_e, OUTPUT); //    d_e   digitalWrite(d_e, 1); //     1,     D  E pinMode(b_a, INPUT); //    b_a   digitalWrite(b_a, 1); //   B pinMode(c_g, OUTPUT); //    c_g   digitalWrite(c_g, 0); //     0,   C,  G delayMicroseconds (150); // F, B, C, G c  150  pinMode(c_g, INPUT); //    c_g   digitalWrite(c_g, 1); //   G ,     delay (time_2); // F, B, C c  2  

Im Prinzip sollte alles klar sein, auch für diejenigen, die nicht mit Arduino vertraut sind, aber ein wenig über die Controller verstehen. Zahlen von 150 Mikrosekunden und 2 Millisekunden werden experimentell durch die Helligkeit der Segmente ausgewählt. Im endgültigen Code müssen Sie sie in separate Variablen einfügen, damit Sie sie während des Debuggens ändern können. Aus diesen Figuren ist es möglich, die Reihenfolge der Stromdifferenz über zwei Segmente in einem Paar ungefähr zu bestimmen. Da das G-Segment etwa 13-mal weniger leuchtet als die anderen und die gleiche Helligkeit bietet, kann davon ausgegangen werden, dass der Strom durch dieses Segment 13-mal höher ist als durch die anderen. Tatsächlich ist die Abhängigkeit der Helligkeit vom Strom nichtlinear, so dass der Strom 25-mal größer sein kann, d. H. 50 mA. Das mit einem solchen Arbeitszyklus ist für die Ausgabe von MK ziemlich sicher. Übrigens spielte dieser Unterschied in den Strömen bei der Lösung des Problems von Abbildung 2 in die Hände. Wie ich oben geschrieben habe, kann Segment G nur zusammen mit Segment C beleuchtet werden. Wenn Sie jedoch 0 für 150 μs und danach an das MK-Bein übergeben, das für C und G verantwortlich ist 2 ms, um 1 darauf zu halten, dann "arbeitet" das G-Segment bei voller Helligkeit, und das C-Segment hat nur wenig Zeit, um für die gleichen 150 Mikrosekunden zu leuchten. Wir bekommen fast volle zwei. So gelang es mir, die von mir selbst festgelegte Regel zu brechen. Was kann man nicht aus Hoffnungslosigkeit machen.

Also haben wir die Figur mit den vier Beinen des MK beleuchtet. Eigentlich habe ich diese Etappe für mich verpasst, sofort zwei Charaktere angezeigt. Trennen Sie dazu den Ausgang der gemeinsamen Anode eines der Anzeigebits vom Power Plus und verbinden Sie ihn mit einem anderen MK-Ausgang und die Anode der anderen Entladung mit dem nächsten Ausgang (bereits 6 Beine). Setzen Sie nun wiederum 1 auf die Anode der niedrigstwertigen Ziffer, zeigen Sie die Ziffer der niedrigstwertigen Ziffer an, dann 1 auf der Anode der niedrigstwertigen Ziffer und zeigen Sie die Ziffer der höchsten Ziffer usw. in einem Kreis an. Ich habe dieses Experiment mit Arduino Nano durchgeführt, sie hat genug Beine. Der gesamte Code wurde darauf debuggt, nicht beim ersten Mal. Und so funktionierte es, wie es sollte.

Schema
Bild

Da die Anoden nacheinander mit Hilfe einer einfachen Verfeinerung verbunden sind, kann ein weiterer Ausgang des MK freigegeben werden. Hier ist das zusammenfassende Diagramm:

Schema
Bild

Verwenden Sie insgesamt 5 Beine MK, um eine zweistellige Zahl anzuzeigen. Zu diesem Zeitpunkt können Sie es bereits mit dem Baby ATtiny versuchen. Was ich getan habe. Aber nicht sofort. Eine in einer Arduino-Umgebung für ATtiny13 erstellte Skizze beanspruchte ungefähr 1,7 kB Speicher, wobei 1 kB verfügbar war. Um die Größe zu reduzieren, musste ich mich direkt an die Ports wenden, was ich später tun würde. Übrigens habe ich auf Arduino die gleichen Ports verwendet, die ich auf ATtiny verwenden wollte. Das ist sehr praktisch. Sie sind bereits im letzten Diagramm angegeben. Nach der Verarbeitung verlor der Code ein Kilobyte.

Hier ist der resultierende Code für ATtiny13:

Code
 #define time_2 2 //    ,  #define time_3 150 //    ,  byte in1_; byte in2_; int disp_; int d_ = 0; void setup() { } void loop() { d_ = d_ + 1; if (d_ > 150) { //    150     1 d_ = 0; disp_ = disp_ + 1; if (disp_ > 99)(disp_ = 0); //   0  99 } in2_ = disp_ / 10; //     -    10 in1_ = disp_ % 10; //          10 } switch (in1_) { case 0: DDRB = B00001111; PORTB = B00010000; delayMicroseconds (time_3); DDRB = B00000001; delay (time_2); break; case 1: DDRB = B00000111; PORTB = B00000110; delay (time_2); break; case 2: DDRB = B00011111; PORTB = B00000010; delayMicroseconds (time_3); DDRB = B00010011; PORTB = B00010010; delay (time_2); break; case 3: DDRB = B00011011; PORTB = B00000110; delayMicroseconds (time_3); DDRB = B00000011; delay (time_2); break; case 4: DDRB = B00010101; PORTB = B00001110; delayMicroseconds (time_3); DDRB = B00000101; delay (time_2); break; case 5: DDRB = B00011011; PORTB = B00001100; delayMicroseconds (time_3); DDRB = B00001001; delay (time_2); break; case 6: DDRB = B00011111; PORTB = B00001000; delayMicroseconds (time_3); DDRB = B00001001; delay (time_2); break; case 7: DDRB = B00001111; PORTB = B00010110; delayMicroseconds (time_3); DDRB = B00000111; delay (time_2); break; case 8: DDRB = B00011111; PORTB = B00000000; delayMicroseconds (time_3); DDRB = B00000001; delay (time_2); break; case 9: DDRB = B00011011; PORTB = B00000100; delayMicroseconds (time_3); DDRB = B00000001; delay (time_2); break; } switch (in2_) { case 0: DDRB = B00001111; PORTB = B00010001; delayMicroseconds (time_3); DDRB = B00000001; delay (time_2); break; case 1: DDRB = B00000111; PORTB = B00000111; delay (time_2); break; case 2: DDRB = B00011111; PORTB = B00000011; delayMicroseconds (time_3); DDRB = B00010011; PORTB = B00010011; delay (time_2); break; case 3: DDRB = B00011011; PORTB = B00000111; delayMicroseconds (time_3); DDRB = B00000011; delay (time_2); break; case 4: DDRB = B00010101; PORTB = B00001111; delayMicroseconds (time_3); DDRB = B00000101; delay (time_2); break; case 5: DDRB = B00011011; PORTB = B00001101; delayMicroseconds (time_3); DDRB = B00001001; delay (time_2); break; case 6: DDRB = B00011111; PORTB = B00001001; delayMicroseconds (time_3); DDRB = B00001001; delay (time_2); break; case 7: DDRB = B00001111; PORTB = B00010111; delayMicroseconds (time_3); DDRB = B00000111; delay (time_2); break; case 8: DDRB = B00011111; PORTB = B00000001; delayMicroseconds (time_3); DDRB = B00000001; delay (time_2); break; case 9: DDRB = B00011011; PORTB = B00000101; delayMicroseconds (time_3); DDRB = B00000001; delay (time_2); break; } DDRB = B00011111;//   PORTB = B00011110; delay (5); } 


Mit dem obigen Code kann Ihr ATtiny13 von 0 bis 99 lesen. Es wäre korrekter, die Möglichkeit einer Neuzuweisung der MK-Beine vorzusehen. Programmiergurus können den Code mehrmals reduzieren ( Wo ist das Mindestlimit für Hello World bei AVR? ).

Sie können dem Code die erforderliche Funktion hinzufügen, damit MK etwas Bewusstes anzeigt. Zwar hat Tinki schon alle Beine besetzt. Es gibt auch einen Reset-Zweig, der als Eingabe- / Ausgabeport verwendet werden kann. Aber es zu benutzen war schwieriger als ich dachte. Deshalb gehe ich für mich "für später". Aber es gibt eine interessante Funktion, die nicht jeder kennt. Der Analogeingang ADC0 wird an denselben Zweig ausgegeben und funktioniert! Wenn die Spannung weniger als 1/4 der Versorgungsspannung beträgt, wechselt der MK in den Reset-Modus. Von 1/4 bis zur Versorgungsspannung ist es jedoch durchaus möglich, die Eingangsspannung zu messen. Ich habe das ausgenutzt:

Code
 #define time_2 2 //    ,  #define time_3 150 //    ,  byte in1_; byte in2_; int disp_; int d_ = 0; void setup() { } void loop() { d_ = d_ + 1; if (d_ > 50) { //   50 ... d_ = 0; disp_ = analogRead(A0) / 10; // ...   ,   10,    . if (disp_ > 99)(disp_ = 99); in2_ = disp_ / 10; //     -    10 in1_ = disp_ % 10; //          10 } switch (in1_) { case 0: DDRB = B00001111; PORTB = B00010000; delayMicroseconds (time_3); DDRB = B00000001; delay (time_2); break; case 1: DDRB = B00000111; PORTB = B00000110; delay (time_2); break; case 2: DDRB = B00011111; PORTB = B00000010; delayMicroseconds (time_3); DDRB = B00010011; PORTB = B00010010; delay (time_2); break; case 3: DDRB = B00011011; PORTB = B00000110; delayMicroseconds (time_3); DDRB = B00000011; delay (time_2); break; case 4: DDRB = B00010101; PORTB = B00001110; delayMicroseconds (time_3); DDRB = B00000101; delay (time_2); break; case 5: DDRB = B00011011; PORTB = B00001100; delayMicroseconds (time_3); DDRB = B00001001; delay (time_2); break; case 6: DDRB = B00011111; PORTB = B00001000; delayMicroseconds (time_3); DDRB = B00001001; delay (time_2); break; case 7: DDRB = B00001111; PORTB = B00010110; delayMicroseconds (time_3); DDRB = B00000111; delay (time_2); break; case 8: DDRB = B00011111; PORTB = B00000000; delayMicroseconds (time_3); DDRB = B00000001; delay (time_2); break; case 9: DDRB = B00011011; PORTB = B00000100; delayMicroseconds (time_3); DDRB = B00000001; delay (time_2); break; } switch (in2_) { case 0: DDRB = B00001111; PORTB = B00010001; delayMicroseconds (time_3); DDRB = B00000001; delay (time_2); break; case 1: DDRB = B00000111; PORTB = B00000111; delay (time_2); break; case 2: DDRB = B00011111; PORTB = B00000011; delayMicroseconds (time_3); DDRB = B00010011; PORTB = B00010011; delay (time_2); break; case 3: DDRB = B00011011; PORTB = B00000111; delayMicroseconds (time_3); DDRB = B00000011; delay (time_2); break; case 4: DDRB = B00010101; PORTB = B00001111; delayMicroseconds (time_3); DDRB = B00000101; delay (time_2); break; case 5: DDRB = B00011011; PORTB = B00001101; delayMicroseconds (time_3); DDRB = B00001001; delay (time_2); break; case 6: DDRB = B00011111; PORTB = B00001001; delayMicroseconds (time_3); DDRB = B00001001; delay (time_2); break; case 7: DDRB = B00001111; PORTB = B00010111; delayMicroseconds (time_3); DDRB = B00000111; delay (time_2); break; case 8: DDRB = B00011111; PORTB = B00000001; delayMicroseconds (time_3); DDRB = B00000001; delay (time_2); break; case 9: DDRB = B00011011; PORTB = B00000101; delayMicroseconds (time_3); DDRB = B00000001; delay (time_2); break; } DDRB = B00011111;//   PORTB = B00011110; delay (5); } 


Die Erfahrung zeigt, dass die Anzeige auf bis zu 21 reduziert werden kann. Erst dann wechselt der MK in den Reset-Modus und beginnt zu arbeiten, wenn er auf etwa 25 und höher zurückkehrt. Es ist also möglich, eine sehr falsche „Anzeige“ für die Anzeige einer Spannung von 25 bis 99 Volt zu erstellen, natürlich mit einem Teiler am Messeingang.

Nun zur praktischen Anwendung. Die ursprüngliche Idee, Daten von einem Abstandssensor anzuzeigen, wurde aufgrund des Fehlens eines digitalen Eingangs auf bessere Zeiten verschoben. Warum sonst können Sie das Schema anwenden, bis Ideen kommen. Noch eine Einschränkung: Von Rentabilität kann keine Rede sein. Selbst wenn Sie alle Segmente auszahlen, fließt ein Strom von 2,5 mA durch den Widerstand R2 (gemäß dem ersten Schema), insgesamt 10 mA pro Indikator, und die Steuerung des Transistors fügt etwa 5 weitere hinzu. Ich habe nicht erwähnt, der Transistor von fast jedem PNP ist modern.

Über Zweckmäßigkeit. Die billigste Option für die Ausgabe in das Siebensegment ist ATtiny13 plus 74HC595. Zwei SMD-Fälle kosten mich ungefähr 0,50 cu Das einfachste ist ATmega8 (und das war's, keine Widerstände, nichts weiter), es ist 0,68 cu Und die oben beschriebene Option sind die Kosten für ATtiny, 9 Widerstände, 4 LEDs, einen Transistor (alle SMDs) - das sind ungefähr 0,46 cu, obwohl es Stück für Stück alles manchmal teurer ist. Darüber hinaus ist das Zusammenstellen komplizierter als in den vorherigen Versionen.

Eigentlich ist die einzige Option, die ich sehen kann, wenn Sie ein volles ATtiny13 haben, aber Sie müssen immer noch für ATmega in den Laden gehen. Nun, wenn eine Sieben-Segment-Anzeige die Hauptdekoration Ihres Geräts ist, würde ich dieses Schema nicht empfehlen, die Anzeige ist nicht perfekt, in einigen Kombinationen werden unnötige Segmente leicht hervorgehoben. Es kommt vor, dass die Anzeige gelegentlich beim Einrichten benötigt wird, dann der Ort selbst.

Im Allgemeinen habe ich ein paar Tage vergebens verbracht.

Neben der Kritik warte ich auf Vorschläge zur Verbesserung des Codes und zur Vereinfachung des Schemas. Oder verbesserte Funktionalität ohne Komplikationen. Was mich am meisten interessiert, ist, wie man einen Punkt beleuchtet, dies würde den Umfang erweitern. Aber wenn wir auch eine Schlussfolgerung aus den 5 Beteiligten herausholen könnten, wäre es möglich, sich zu bewegen.

Ich würde mich freuen, wenn meine nicht standardmäßige Lösung irgendwann jemandem zugute kommt, nicht jetzt.

UPD: Ich weiß über Charliplexing Bescheid! Das Schema von Drittanbietern, das ich am Anfang dieses Artikels zitiert habe, ist Charliplexing. Und dort schrieb ich, warum Charliplexing nicht geeignet ist.
Bitte schreiben Sie keine Kommentare, wenn Sie den Artikel diagonal lesen.

Source: https://habr.com/ru/post/de446796/


All Articles