Heute das Handtuch nicht vergessen:
Don't PanicDienstag, 25. April 2023
Berlin-Uhr alias "Mengenlehreuhr"
Die Berlin-Uhr (Mengenlehreuhr) aus dem Jahr 1975 eignet sich natürlich ideal für eine kleine VHDL-Spielerei.
Hier gibt es Infos dazu: Berlin-Uhr – Wikipedia
Hinter der Uhr steckt ein Fünfersystem bei den Einerstellen von Minuten und Stunden: es werden mit 4 Lampen jeweils 0..4 Minuten bzw. 0..4 Stunden gezählt, dann kommt ein Überlauf in die nächst höhere Stelle. Dort gibt es dann 11 Lampen für die 5er-Minuten , womit 55 Minuten aufsummiert werden können. Und es gibt 4 Lampen für die 5er-Stunden, was in Summe 20 Stunden ergibt.
Das ursprüngliche Design war asynchron mit Schieberegistern aufgebaut: das Überlauf-Bit der jeweils vorhergehenden Stufe sorgte dafür, dass die nachfolgende Stufe hochgezählt und die vorhergehende Stufe zurückgesetzt wird. So ein asynchrones Design hat prinzipiell das Problem, dass der Rücksetz-Impuls im Grunde nur ein kurzer Glitch ist, dieser Glitch aber auch als Zähl-Impuls verwendet werden muss. Es muss also z.B. durch eine Verzögerung des Rücksetzens garantiert werden, dass dieser Glitch ausreichend lang für das Weiterschieben/Hochzählen der nachfolgenden Stufe ist.
So etwas lässt sich nicht ohne berechtigte Warnungen aus VHDL-Code in ein FPGA synthetisieren, deshalb habe ich hier ein taktsynchrones Design für einen 50 MHz Takt aufgesetzt, das diese Glitch-Problematik sicher umgeht:
library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.NUMERIC_STD.ALL; entity berlinuhr is Generic ( fosc : integer := 50000000 ); Port ( clk : in STD_LOGIC; setslow: in STD_LOGIC; setfast: in STD_LOGIC; sec : buffer STD_LOGIC := '0'; min : buffer STD_LOGIC_VECTOR (3 downto 0) := (others=>'0'); min5 : buffer STD_LOGIC_VECTOR (10 downto 0) := (others=>'0'); hr : buffer STD_LOGIC_VECTOR (3 downto 0) := (others=>'0'); hr5 : buffer STD_LOGIC_VECTOR (3 downto 0) := (others=>'0') ); end berlinuhr; architecture verhalten of berlinuhr is signal spresc : integer range 0 to fosc-1 := 0; -- Prescaler für Sekunde signal mpresc : integer range 0 to 60-1 := 0; -- Prescaler für Minute = 60 Sekunden signal cntup : std_logic; begin process begin wait until rising_edge(clk); cntup <= '0'; if spresc < fosc-1 then spresc <= spresc + 1; else -- 1 s vorbei spresc <= 0; sec <= not sec; if mpresc < 60-1 then mpresc <= mpresc +1; else cntup <= '1'; -- 1 min vorbei mpresc <= 0; end if; end if; -- Uhrzeit stellen hat Vorrang if (setslow='1' and spresc=fosc-1) -- langsam stellen / 1 min vorwärts pro sec or (setfast='1' and spresc>=(fosc/60)-1) -- schnell stellen / 60 min (= 1 h) pro sec then cntup <= '1'; spresc <= 0; mpresc <= 0; sec <= '0'; end if; end process; process begin wait until rising_edge(clk); if cntup='1' then min <= min(2 downto 0) & '1'; if min(3)='1' then min <= (others=>'0'); min5 <= min5(9 downto 0) & '1'; if min5(10) = '1' then min5 <= (others=>'0'); hr <= hr(2 downto 0) & '1'; if hr(3) = '1' then hr <= (others=>'0'); hr5 <= hr5(2 downto 0) & '1'; end if; if hr5(3)='1' and hr(2)='1' then -- Tagesüberlauf extra und bevorzugt abhandeln hr <= (others=>'0'); hr5 <= (others=>'0'); end if; end if; end if; end if; end process; end verhalten;
Mit dieser Testbench lässt sich dann kontrollieren, ob die Uhr wie erwartet reagiert:
LIBRARY ieee; USE ieee.std_logic_1164.ALL; USE ieee.numeric_std.ALL; ENTITY tb_berlinuhr IS Generic ( fosc : integer := 5000; -- debug tosc : time := 200 us); -- debug END tb_berlinuhr; ARCHITECTURE behavior OF tb_berlinuhr IS COMPONENT berlinuhr GENERIC ( fosc : integer := fosc); PORT( clk, setslow,setfast : IN std_logic; sec : BUFFER std_logic; min : BUFFER std_logic_vector(3 downto 0); min5 : BUFFER std_logic_vector(10 downto 0); hr : BUFFER std_logic_vector(3 downto 0); hr5 : BUFFER std_logic_vector(3 downto 0) ); END COMPONENT; signal clk : std_logic := '0'; signal setslow : std_logic := '0'; signal setfast : std_logic := '0'; signal sec : std_logic; signal min : std_logic_vector(3 downto 0); signal min5 : std_logic_vector(10 downto 0); signal hr : std_logic_vector(3 downto 0); signal hr5 : std_logic_vector(3 downto 0); BEGIN uut: berlinuhr PORT MAP ( clk => clk, setslow => setslow, setfast => setfast, sec => sec, min => min, min5 => min5, hr => hr, hr5 => hr5 ); clk <= not clk after tosc/2; set_test: process begin wait for 1000000 ms; setfast <= '1'; wait for 100000 ms; setfast <= '0'; wait for 1000000 ms; setslow <= '1'; wait for 100000 ms; setslow <= '0'; wait; end process; END;
In der Testbench wird der Takt auf 5 kHz reduziert, damit die Simulation nicht so viele Taktflanken zu bearbeiten hat und deshalb langsam wird. Weil diese Taktzykluszeit über ein Genric an die Uhr gegeben wird, kann die ihre Zähler und Skalierungen entsprechend anpassen und so wird in der Testbench trotzdem alles in realen Zeiten angezeigt.
Auf dem Mikrocontroller.net wird das Thema im Thread Berlin Uhr aus ICs bauen etwas ausgiebiger diskutiert.
Dort ist der Code für einen 32,768 kHz Takt ausgelegt und braucht deswegen natürlich wesentlich weniger Flipflops. Natürlich könne man auch im obigen Code eine Taktfrequenz von 32 kHz angeben, dann kommen gleich viele Flipflops heraus, allerdings wird trotzdem mehr Logik für die Rücksetzbedingungen benötigt, als wie auf der expliziten Lösung auf µC.net nur 1 Bit abzufragen.
Dienstag, 10. Januar 2023
Drehgeberauswertung mit Beschleunigung
Hier eine Drehgeberauswertung, die auf der Basis von Peter Danneggers Code (auf www.mikrocontroller.net) zusätzlich eine dynamische Beschleunigung realisiert. Je schneller am Knopf gedreht wird, umso schneller ändert sich der Wert. Das ergibt ein gutes haptisches Gefühl, und man kann größere Bereiche einstellen, ohne dass umgegriffen werden muß.
volatile int16_t enc_pos; #define PHASE_A (PINA & 1<<PA1) #define PHASE_B (PINA & 1<<PA2) // ISR wird z.B. jede ms aufgerufen ISR(SIG_OUTPUT_COMPARE1A) { #define DYNAMIK 40 static uint8_t enc_ab = 0x01; // speichert Encoderzustand 0..3 static uint8_t enc_accel = 0; // speichert die aktuelle Beschleunigung int16_t p; // (position) lokale Variable für Positionsberechnung int8_t m = 0; // (moved) lokale Variable für Bewegungsberechnung if (PHASE_A) m = 1; // Gray nach binär wandeln if (PHASE_B) m ^= 3; m -= enc_ab; // Differenz zeigt an, ob sich der Encoder bewegt hat // gültige Werte für Differenz (m): -1, 0, +1 // sonst ist die Abtastfrequenz zu niedrig! if (enc_accel) enc_accel--; // solange >0: pro ms um 1 "entschleunigen" if (m) { // wenn m ungleich 0 --> Positionsänderung am Encoder enc_ab += m; // Bewegungsdifferenz auf alten Encoderzustand aufaddieren p = enc_pos; // nicht auf globaler Variable (enc_pos) herumrechnen if (enc_accel<255-DYNAMIK) // neuen Beschleunigungswert berechnen: enc_accel += DYNAMIK; // solange <255 Beschleunigungswert aufaddieren else enc_accel = 255; // sonst auf 255 begrenzen (diese Zeile kann weggelassen werden) if (m&2) // Bit 1 = Vorzeichen = Richtung (+/-) p += 1+(enc_accel>>6); // Erst Beschleunigungswerte >64 (wegen >>6) else // werden berücksichtigt. p -= 1+(enc_accel>>6); // Diese Division (Shift 6) evtl. anpassen... enc_pos = p; // lokale Variable auf globale Variable zurückkopieren } : // ab hier kommt sonstiger Code im Timerinterrupt }
Im Beispiel sind die beiden Spuren an den Portpins 1 und 2 vom Port A angeschlossen. In der Variablen m steht ein Wert für die Bewegung: -1, 0, +1 (11, 00 und 01). Ein Wert ungleich 0 bedeutet also: Bewegung. PeDa fragt das Bit 0 explizit ab (m&1), ich habe das reduziert auf m!=0. Im Bit 1 (m&2) steckt dann die Bewegungsrichtung, mit der die Position hoch- oder runtergezählt wird.
Mit dem #define DYNAMIK lässt sich der "Biss", also die Beschleunigung einstellen. Leider funktioniert das z.B. mit den billigen Pollin-Encodern nur eingeschränkt, weil die pro Raste zwei Schritte machen. Also ist da etwas Spielen und Ausprobieren mit dem Wert angesagt. Ich habe noch jedesmal einen brauchbaren Wert gefunden. Mit einem uint16_t für enc_accel kann der Dynamikbereich wesentlich ausgeweitet werden.
Die Umwandlung vom Gray- in den Binärcode erfolgt mit dem XOR Verfahren wie auf Software/Graycode im letzten Beispiel beschrieben.
Im PDF CY8CKIT-049-4xxx PSoC 4 Rotary Encoder für Cypress Mikrocontroller hat Reiner Wehner das Ganze ab Seite 37 fein säuberlich analysiert. Und im Forum dort wird das im Thread Quadrature decoder at PSoC 4 ausdiskutiert, dort sind dann auch Links zur Umsetzung in Software zu finden.
Donnerstag, 25. November 2021
Entkopplung
Was ist "Entkopplung"
Ein heisses Thema sind Blockkondensatoren oder Entkopplungskondensatoren oder Pufferkondensatoren an integrierten Schaltkreisen. Manch einer nimmt noch immer den altbewährten 100nF und dazu eine Anschlusstechnik aus einem Lehrbuch von 1980: irgendwie in die Nähe des ICs. Das hat bei den damals verfügbaren langsamen und wenig komplexen ICs auch ausgereicht ("langsam" bedeutet hier übrigens nicht "niedrige Taktfrequenz", sondern "langsame Flankenansteigszeit wegen hochohmiger Transistoren").
Heute haben ICs aber viele interne Komponenten und dementsprechend viele Flipflops, die gleichzeitig schalten und beim Umschalten durch das Umladen der nachfolgenden Eingangskapazitäten Stromspitzen erzeugen. Die Versorgung des ICs muss 1. diese Stromspitzen bereitstellen und 2. dafür sorgen, dass sie nicht in die weite Welt hinaus abgestrahlt werden. Für beide Aufgaben ist hier der Entkopplungskondensator wichtig. Im 1. Fall stellt er niederimpedant den Strom für die Funktion des ICs zur Verfügung, und im 2. Fall sorgt er dafür, dass diese schnellen Stromänderungen nur ganz lokal passieren.
Wie auch beim Layout von Schaltreglern geht es darum, die Stromschleifen, auf denen solche schnellen Stromänderungen passieren, möglichst klein zu halten, dass sie niederimpedant sind (der Amerikaner sagt: "Each mm has its nH") und sie aufgrund ihrer geringen Größe nicht als Störsender wirken können.
Dazu einige wichtige Grundlagen (es kann nicht schaden, diese allgemein gültigen Regeln zu kennen und IMMER im Hinterkopf zu behalten):
1. Regel
Wo ein Strom rausfließt muss auch ein genau gleich großer wieder rein.
Diese "beiden Ströme" sind ein und der selbe Strom.
Dieser Strom soll möglichst ohne Umwege "im Kreis" fließen können.
2. Regel
Leiterbahnschleifen sind Spulen und Spulen sind Elektromagnete und Antennen.
Im Allgemeinen wollen wir keine Elektromagnete und Funkgeräte auf Platinen bauen.
3. Regel
Ein Kondensator ist gleichzeitig auch ein Widerstand und eine Spule.
Seine Impedanzkurve hat deswegen eine Frequenzabhängigkeit.
Entkoppelkondensatoren versorgen ICs im ns-Bereich mit Strom. Sie puffern (deshalb auch Pufferkondensator) die Stromspitzen, die entstehen, wenn Ausgänge von CMOS-Flipflops im IC ihren Pegel umschalten. Deren Ausgangstreiberstufen müssen die Umladeströme für den nächsten CMOS-Eingangs-Kondensator liefern. Weil meist im ganzen IC ein Takt (oder davon abgeleitete synchrone Takte) verwendet werden, schalten viele Flipflops im IC gleichzeitig. Daraus resultieren schnelle Stromänderungen an den Versorgungsanschlüssen des ICs, die einen kurzen und induktivitätsarmen Anschluss des Entkoppelkondensators erfordern.
Für einen "durchschnittlichen" Schaltkreis wie z.B. einen uC, in dem viele Komponenten (Speicher, Alu, Schnittstellen, Timer...), untergebracht sind, gelten diese Betrachtungen:
So sollte man es nicht machen:
Das rechte Beispiel birgt im Besonderen die Gefahr, dass der Blockkondensator den Bezug zu "seinen beiden" Versorgungspins verliert, weil er im Laufe des Layouts (wenn man immer mehr Leitungen zwischen den Kondensator und "seine" Versorgungsanschlüsse zwängen muss) irgendwie immer weiter vom IC wegwandert.
So sieht z.B. eine brauchbare Entkopplung eines ICs aus:
Hier darf der Tantalkondensator natürlich auch weiter weg auf der Platine sitzen, da er nur noch niederfrequente Stromänderungen zu bedienen hat.
Bei vielen ICs sind für eine einfache Entkopplung die Versorgungspins paarweise herausgeführt. Das haben die Chipdesigner nicht zum Zeitvertreib gemacht, sondern damit die Blockkondensatoren optimal angeschlossen werden können. Eine gute Abblockung eines uCs gegen die Versorgung könnte so aussehen:
Diese Desings sind 4-lagig, die Durchkontaktierungen gehen direkt auf die jeweilige Versorgungslage.
SMD-Kondensatoren im 0402 oder auch 0201 Format erlauben eine Platzierung ganz dicht an den Bauteilpins:
Natürlich ist das ganze Thema namens Power Integrity bändeweise buchfüllend und die Entkopplung muss bei anderen Anforderungen auch entsprechend angepasst werden. So ist z.B. beim Anschluss von BGAs logischerweise ein anderes Entkopplungs- und Versorgungskonzept nötig, weil dort ein Versorgungspin natürlich erst auf die Versorgungsfläche durchkontaktiert und dann diese lokale Versorgungsfläche entkoppelt wird. Aber die Grundlagen sind die selben: kleine Stromschleifen, kleine Impedanzen.
Ströme über IO-Pins
Natürlich darf bei der Betrachtung des Themas "Entkopplung" nicht vergessen werden, dass auch aus den IC-Augangspins Ströme heraus und in andere IC-Eingangspins hinein fließen. Auch diese Ströme müssen wieder zurück zum treibenden IC, weil jeder Stromkreis geschlossen ist. Bei sehr IO-lastigen Anwendungen mit schnellen (steilflankigen) Ein- und Ausgängen (z.B. DDRAM) muss dementsprechend die Entkopplung einen Schritt zurücktreten und es müssen vorrangig die Strompfade auf der Masse und in der Versorgungslage zwischen den beteiligten ICs optimiert und verkürzt werden. In diesem Fall ist dann auch nicht der IC der potentielle Störer, sondern die Datenübertragung zwischen den ICs.
Versorgungskonzept
Darüber hinaus muss VOR dem Entkoppeln der ICs von der Versorgung ein vernünftiges Versorgungskonzept auf der Platine erstellt sein. "Versorgung" bedeutet übrigens nicht nur "Masse", sondern eben auch Vcc (oder V+ oder Vdd oder wie die auch immer heißen mag). Es hilft nicht, wenn der Pufferkondensator auf 1mm an die Versorgungspins platziert wurde, wenn dann die Verbindung zwischen mehreren solcher Versorgungspärchen auf der Leiterplatte nur mit dünnen Leitungen über Umwege erfolgt. In diesem Fall können sich zwischen den Versorgungspärchen Potentialverschiebungen ergeben, die die Funktion des ICs beeinträchtigen.
Zusammengefasst:
Es muss also beim Layout zuallererst eine "stabile" Masse- und Vcc-Anbindung hergestellt werden.
Von dieser Versorgung wird dann der angeschlossene IC mit den oben beschriebenen Maßnahmen entkoppelt.
Ein Kompromiss
Weil jedes Layout irgendwie immer einen Kompromiss darstellt, sollte man das Ganze nicht unnötig auf die Spitze treiben. Für eine "übliche" Mikrocontrollerschaltung z.B.
mit einem AVR oder PIC Controller reicht es meist aus, wenn überhaupt ein Blockkondensator
in der Nähe der ICs sitzt. Besser als gar nichts ist das auf jeden
Fall...
Zu diesem Thema finden sich interessante und durchaus kontroverse Diskussionen auf dem mikrocontroller.net:
www.mikrocontroller.net: Abblockkondensatoren, wie routen?
und den
www.mikrocontroller.net: Abblockkondensator 1 µF oder 100 nF besser?
und auch den hier
www.mikrocontroller.net: Abblockkondensator bei gegenüberliegenden Pins für VCC und GND
Original vom 9.10.2008
Dienstag, 3. August 2021
Verpolschutz
Für Geräte, die immer wieder an wechselnden Gleichspannungsquellen (Akkus, Batterien, Netzteile...) betrieben werden, lohnt sich u.U. wenn ein Verpolungsschutz vorgesehen wird.
Die einfachste Variante mit einer Diode hat leider einen deutlichen Spannungsabfall von bis zu 1 Volt.

Das sind dann bei z.B. 12V Versorgungsspannung immerhin schon 8% Verlust.
Nächster Versuch: die zerstörerische Variante.

Die Sicherung brennt durch, wenn die Eingangsspannung verpolt ist. Dazu ist natürlich eine Spannungsversorgung nötig, die mindestens den Auslösestrom liefern kann. Denn sonst kann es sein, dass in der Diode eine kleine Siliziumschmelze angeworfen wird. Dazu kommt, dass bis zur Auslösung der Sicherung eine verpolte Spannung von bis zu 1V an der Schaltung anliegt. Das ist weniger schön
Am elegantesten ist die Lösung mit einem P-Kanal MOSFET.

Der P-Kanal-MOSFET IRF9530 leitet, wenn das Gate um Ugsth negativer als die Source wird. Beim Anlegen einer korrekt gepolten positiven Spannung am Eingang leitet erst mal die Bulk-Diode, so dass an der Source die Eingangsspannung ankommt. Weil die Spannung an der Source nun positiver ist als Ugsth, leitet der MOSFET, und dem Strom steht nur noch der kleine Kanalwiderstand im Weg. Die Z-Diode begrenzt Ugs auf einen für den MOSFET ungefährlichen Wert. Dass der Mosfet dadurch quasi "verkehrt herum" leitet, also der Drain positiver ist als die Source, kümmert den Mosfet dabei herzlich wenig, denn das sind wegen des durchgeschalteten Kanals sowieso nur ein paar Millivolt.
Beim Anlegen einer verpolten negativen Spannung sperrt die Bulk-Diode und der MOSFET kommt nicht in den leitenden Zustand.

Und für ganz Wagemutige ist sogar der Gatewiderstand überflüssig und kann durch eine Brücke ersetzt werden. Ich lasse diesen Angstwiderstand aber gern drin, damit die hauchdünne Oxidschicht des Gates nicht allzu direkt an der Aussenwelt hängt.
Allerdings kann die MOSFET-Schaltung eine Diode nicht immer komplett ersetzen:
Machen wir mal hinter dem MOSFET einen dicken Kondensator, der z.B. eine gewisse Zeit einen Spannungsausfall überbrücken soll. Dann ist es ohne weiteres möglich, dass der Kondensator über den MOSFET und andere Verbraucher, die von der Eingangsspannung versorgt werden, entladen wird.

Denn in diesem Fall arbeitet der MOSFET sogar so, wie der Hersteller sich das ausgedacht hat: die Source ist positiver als der Drain (wegen des Kondensators am Ausgang) und das Gate ist negativer als die Source --> der P-Kanal-MOSFET leitet.
Danke an Matthias Gottke für diesen Tipp
Achtung: Sperrverzug!
Wenn
man eine dieser Mosfet-Schaltungen als Ersatz für eine
Gleichrichterdiode nehmen will, dann gibt es das Problem, dass durch den
hochohmigen 100k Widerstand das Gate recht langsam umgeladen wird.
Solange am Mosfet dann die Ugsth überschritten ist, leitet der Mosfet.
Somit dauert der Übergang von "leitend" zu "gesperrt" ein paar µs, und
während dieser Zeit lässt der Mosfet auch verpolte Spannungen durch.
Ähnliches passiert auch, wenn eine Spannungsquelle korrekt gepolt angeschlossen wird: durch diese korrekt gepolte Spannung wird das Gate geladen und der Mosfet leitend. Wird jetzt die Spannungsquelle abgeklemmt, kann es je nach nachfolgender Schaltung sein, dass sich der Gatekondensator nicht entlädt und so der Mosfet weiter leitend bleibt. Wird nun die Spannungsquelle verpolt angeschlossen, dauert es wieder einige µs, bis der Mosfet sperrt. Während dieser Zeit liegt eine verpolte Spannung am Verbraucher an.
Hier würde es helfen, wenn man zwischen die Eingangspins zwischen Vin und GND ebenfalls einen 100k Widerstand löten würde. Denn dann kann sich bei abgeklemmter Eingangsspannung das Gate über den Gatewiderstand und den zusätzlichen Widerstand entladen. Diese Entladung dauert bei 10nF Gatekapazität und 2x100k etwa 10ms, was bei manuellem Verpolen hinreichend schnell ist.
Den Spannungsabfall über dem Mosfet weiter reduzieren:
Obwohl sich die P-Kanal Mosfets mit ihren Bahnwiderständen Rdson schon in recht brauchbaren Bereichen um 50mOhm bewegen, lässt sich mit einem N-Kanal Mosfet nochmal einiges rausholen. Ein IRF1404 hat da gerade mal 4mOhm. Damit lässt sich also die Verlustleistung und der Spannungsabfall nochmal um den Faktor 10 reduzieren.

Allerdings muß jetzt darauf geachtet werden, dass kein Massebezug zwischen Versorgung (Batterie) und Verbraucher mehr besteht, und auch nicht hergestellt werden darf. Denn sonst ist der Mosfet ja einfach überbrückt.
Verpolunschutz mit ÜberspannungsabschaltungAuf dem mikrocontroller.net findet sich auch noch eine Lösung, die Verpolschutz und Überspannungsabschaltung beinhaltet: http://www.mikrocontroller.net/Verpolungs- und Überspannungsschutz mit P-MOSFET
