Kassettenplayer

Mit Atmega und VFD
Links oben die Steuerplatine, rechts das Kassettenlaufwerk, unten der Schaltplan

TL;DR
Da hab ich mal ein Autoradio mit Kassette aufm Schrott gefunden - das hätte ich normalerweise nicht mitgenommen, aber die Andruckrollen hatten noch keinerlei Bandabrieb - ich kann doch nicht zulassen, dass so'n jungfräuliches Kassettenlaufwerk im Schrott landet.
Also hab ich das Radio in mein Auto eingebaut und - es ist codiert. Wer codiert n billiges Kassettenradio Baujahr irgendwann so um 2005? Naja, Laufwerk raus, Radio weggeschmissen.
Jetzt ist das leider so ein Full-Logic-Laufwerk, für das man einen Computer braucht, um es anzusteuern. Ich hab zwar seit 15 Jahren nix mehr mit Atmega gemacht, aber ich hab noch ein paar rumliegen, also leg ich mal los.
Zuerst hab ich das Laufwerk Reverse-Engineert, das hat auf der Unterseite einen Lagesensor, der den Laufwerkszustand codiert, damit der Computer weiß, wann das Laufwerk die Funktion angefahren hat, die der Benutzer angefordert hat. Außerdem sind zwei Lichtschranken für die Wickelteller und ein CrO2-Schalter vorhanden.
Auf der Platine ist ein integrierter, wohldokumentierter und TTL/CMOS-Pegelkompatibler Treiberbaustein für den Laufwerksmotor und den Funktionsservo.
Da ich vor vielen vielen Jahren ein VFD aus einem Videorekorder ausgeschlachtet habe, das alle möglichen Symbole für Kassettenbetrieb und ein vierstelliges Bandzählwerk hatte, wollte ich dieses VFD ansteuern. Erstmal begnügte ich mich aber mit einem Standard Character-LCD, weil mein Hallo-Welt Programm, das noch geladen war, das eingebaut hatte.
Um möglichst viel I/O freizuhaben, habe ich mich für die Gemeinheit der Ingenieure entschieden, Buttons über Widerstände mittels ADC an nur einem Pin auszulesen. Warum das fies ist? Naja, die Buttons altern, und nach wenigen Jahrzehnten sind die Übergangswiderstände so hoch, dass Buttons falsch/nicht gelesen werden. Hab ich in 20 Jahren wieder was zu reparieren.

Also zuerst ein Programm geschrieben, das Widerstände ausliest und anhand einer Look-Up-Tabelle ausgibt, welcher Knopf gedrückt wurde.
Danach habe ich eine Look-Up-Tabelle gebaut, die den Laufwerkszustand auf die Positionsnummer umrechnet - was der Lagesensor ausgibt, entspricht keiner mir bekannten Form, irgendetwas in eine Reihenfolge zu bringen.
Das Ziel der Übung ist, wenn ein Knopf gedrückt wird, dass der Computer sagen kann, ob der größer oder kleiner als der jetzige Zustand ist und ich den Funktionsservo vorwärts oder rückwärts ansteuern muss. Was mer net all mache muss, nur um so'n Teil zu steuern.
Damit ist schon mal das Minimalprodukt fertig, das komplett manuell zu steuern ist. Also habe ich mir einen Schaltplan für einen Tonkopfverstärker ausm Netz geladen und nachgebaut. Stufe 1 vier Mal, dann ein CD4052 zum Umschalten der Seite - um I/O zu sparen, erstmal über den Lageschalter, um Music Search implementieren zu können, muss ich das irgendwann über den Atmega schalten, und dann noch eine Op-Amp-Stufe. Ist von HiFi ziemlich weit entfernt, aber man kann's sich anhören.
Als Nächstes habe ich die Lichtschranken an die beiden Interrupt-Pins angeschlossen, bzw. an je einen Transistor, da die Lichtschranken leider keine Empfängertransistoren sondern wohl nur ne Empfängerdiode haben, musste ich mit etwas Trial&Error einen Widerstandswert nehmen, mit dem die Unterschiede zwischen An und Aus stark genug waren, um einen Transistor zu schalten - und dessen Output geht in den Interrupt-Pin.
Die Interrupts machen nicht viel - der von der Wickelspule wenn Seite 1 spielt, treibt das Bandzählwerk. Beide Interrupts setzen außerdem Flags. Dann gibt es noch einen Timer1-Interrupt, der setzt die Flags zurück - wenn die Flags aber nicht gesetzt waren, dann dreht sich eine der Spulen nicht - geprüft wird immer die jeweilige Wickelspule. Wenn die von Seite 1 nicht dreht, wird auf Seite 2 geschaltet. Wenn die von Seite 2 nicht dreht, wird die Kassette ausgeworfen. Damit haben wir Auto Reverse, hurra.

VFD in Betrieb und Netzteil

Nun galt es, das VFD anzusteuern. VakuumFluoreszenzDisplays oder VFDs (ich sollte VFD Display sagen weil alle Leute sagen ja auch LCD Display...) sind im Prinzip Vakuumröhren, sie brauchen verschiedene exotische Spannungen - ca. 3V für die Heizung und dann so um die 20-30V fürs Display selbst. Es braucht einen Spaltentreiber für die 6 Teile des Displays - Symbole links, Symbole Mitte und die vier Zählwerksstellen. Es gilt - je höher die Spannung, desto heller das Display - für die Heizung gilt jedoch - bei 5V sieht man die Heizstäbe deutlich glimmen, das gilt es zu vermeiden.
Ich habe erstmal experimentiert - 3V auf die Heizung und 20V auf den Rest und geguckt wie hell es wirkt. Im Wissen, dass das Display in Betrieb höchstens 1/6 der Helligkeit aufbringt, da ja nur eine von 6 Spalten auf einmal leuchten kann, dachte ich, 12V sind zu dunkel (der Motor läuft mit 12V, das wäre praktisch gewesen). Um auch hier wieder I/O zu sparen, habe ich mich entschieden, die Spalten mit dem guten alten CD4017 (siehe letzter Artikel) zu machen, mit denen kann ich um mich schmeißen. Ich habe ja noch nicht mal erwähnt, dass ich einen Bipolar-PROM-Brenner für den Harris 7603 32 Byte PROM gebaut habe - davon hatte ich 6 Stück und ich musste Farb-PROMs für einen Galaxian und einen Crazy Climber brennen. Jetzt hab ich nur noch 2 Stück. Und ja - als Haupttreiber habe ich zwei CD4017 genommen - weil der Chip nach jedem Bit, das gebrannt wird, eine ebenso lange Pause braucht.
Da der CD4017 High-Aktive Ausgänge hat und zwar 18V problemlos aushält, bei 24V die Gefahrenzone bereits überschritten ist, muss ein Open Collector Inverter (74xx06) und eine Batterie PNP-Transistoren dahinter (kann man auch mit NPN machen, ich wollte aber mal PNP nehmen - außerdem geht es darum, eine Last nach +24V zu schalten, da sind PNPs besser. NPNs sind, um eine Last nach Masse zu schalten. Und ich habe eine Tüte BC558, die werden seit 8 Jahren nicht wirklich weniger, während die Tüte BC548, die ich gleichzeitig gekauft habe, schon ersetzt wurde und diese schon wieder fast leer ist.
Um die Segmente zu treiben, benutze ich einen CD4094 - da hab ich auch genug von. Nie benutzt bisher und ich hab massig auf Lager. Das ist ein Seriell-zu-Parallel-Shifter IC, wie der gute 74LS595, den alle anderen nehmen. Blöderweise hat der Hersteller entschieden, NEUN Anschlüsse für Segmente zu nehmen. Zwei Spalten benutzen Bit 9, obwohl nur eine Spalte 9 Symbole hat, was nicht nötig gewesen wäre, wenn die mittlere Spalte das Symbol für S und M mit aufgenommen hätte. Mein Trick ist: Ich lasse ja jede Spalte eine Zeit lang an. Dann shifte ich in 8 MHz Geschwindigkeit die Daten in den 4094 - warum nehme ich nicht den Datenausgang selbst als 9. Bit?
Wegen Ghosting. Man kann es ein wenig im Zählwerk erahnen, aber S sollte nicht leuchten. Das hängt am 9. Bit und die Datentransfers dauern lang genug um es ein bisschen leuchten zu lassen.
Hinter dem 4094 müssen natürlich auch wieder Open Collector Treiber hängen (diesmal nehme ich 74xx07, da hab ich welche da und ich weiß, dass ich den 06 auch noch anderswo brauche *hust* Commodore 1541 *hust* und das kann man ja in Software kompensieren.

Links oben die Steuerplatine, rechts das Kassettenlaufwerk, unten der Schaltplan

Die Strobes mache ich mit Timer0, den hatte ich anfangs für Auto Reverse vorgesehen, aber selbst mit /256 geht der schneller ins Timeout als das Signal vom Bandwickel, wenn das Band fast zuende ist, daher habe ich das dann mit Timer1 gemacht.
Übrigens - die Spannung für die Heizung nehme ich mit einem 16 Ohm Widerstand von den 5V ab - ich schätze mal, 12-56 Ohm sind OK - mit 16 Ohm (hatte ich gerade da) kann man das Leuchten der Heizung nur noch im Dunkeln erahnen. Mit 56 Ohm ist das Display schon ein bisschen dunkel.
Ich habe dann noch eine Look-Up-Tabelle für Dezimal nach 7 Segment Umwandlung gebaut. Mit dem Character-LCD lag itoa zum Umwandeln des Counters nahe, also habe ich das auch hier wieder gemacht.
Auf dem Foto sieht man oben den Vorverstärker - übrigens, den muss man unbedingt von der Spannungsversorgung entkoppeln, an der der Motor hängt - und wenn das Labornetzteil auch nur ein kleines Bisschen rauscht, wird das unendlich an den Ausgang verstärkt - ich habe das gelöst, indem ich noch einen 7810 10V Spannungsregler eingebaut habe. Jetzt rauscht nur noch der Vorverstärker (ziemlich ordentlich, muss ich anmerken - ich benutze LM358, davon hab ich auch mehr als mir lieb ist).
Direkt darunter in der Mitte ist das Poti und der Header für das Character LCD. Wenn man da jetzt eins anschließt, passiert darauf wenig - die Bibliothek hab ich aus dem Tapedeck-Programm entfernt (im Hallo Welt ist sie noch drin).
Die 4 Tonköpfe gehen in 4 Op-Amps (2 pro Chip). Deren Ausgänge gehen in den CD4052 neben dem Atmega (rechts Mitte) und die Ausgangsstufe ist links Mitte.
Die beiden Transistoren zwischen CD4017 und CD4094 sind für die Lichtschranken. Rechts der 4017 mit Widerstandspaket (ja, das war Arbeit. Hätte ich bestellen müssen, aber dann hätte ich warten müssen und viel Porto verschwendet), 74LS06 und Transistorbank, links der CD4094 mit zwei 74LS07, Widerstandspaket und Transistorbank. Unten das VFD.
Das wars im Wesentlichen! Und hinterher stellte sich raus, dass das VFD mit 12V ein bisschen dunkel, aber durchaus benutzbar ist.

Verwendete hilfreiche Programmier- und Hardware-Tricks
Das erste Problem war es, herauszufinden, in welche Richtung der Load-Motor sich drehen muss.
Der Mode-Switch liefert recht wahllos erscheinende Werte für die Zuordnung der Laufwerkszustände.
Physische Reihenfolge der Zustände:
Eject, Pause, <<, >>, <Play, Play>.
Diese werden vom Mode-Switch wie folgt codiert (wenn man den linken Pin als MSB und den rechten als LSB nimmt):
6,3,0,2,1,5 (7 = Illegaler Zustand oder Transit)
Die Lösung ist eine Look-Up-Table, welche für die vom Mode-Switch gelesene Zahl in die entsprechende physische Position des Laufwerks umrechnet:
lut[]=[3,5,4,2,1,6,0,1] (1 ist hierbei ein ungültiger Zustand)
Wenn vom Mode-Switch also eine 2 gelesen wird, holt das den Wert 4 aus der LUT. Das entspricht dem Schnellvorlauf.
Wenn jetzt also die Bedien-Knöpfe mit derselben Reihenfolge codiert werden (viele Wege führen zum Ziel), sieht der Algorithmus folglich sehr einfach aus:
Button < lut[Mode-Switch] -> mech_motor(-1)
Button > lut[Mode-Switch] -> mech_motor(1)
Button == lut[Mode-Switch] -> NOP (nichts tun, wir sind schon da!)

Eine weitere Hürde war, dass die Lichtschranken-Empfänger der Wickelteller nicht einfach mit einem Pullup-Widerstand versehen werden können und diesen dann auf GND ziehen. Stattdessen musste ich verschiedene Widerstandswerte ausprobieren, bis ich einen hatte, mit dem der Unterschied zwischen Reflektor und Plastik maximal war. Das war gerade so im Grenzbereich von TTL, aber zu heikel, also habe ich dieses Signal benutzt, um einen Transistor zu schalten. Dieser erzeugt dann ein TTL-kompatibles Signal.
I/O Einsparungen
Ein gängiger Trick, der leider auch im wirklichen Leben oft verwendet wird, ist, die Schalter mit Widerständen an einen einzelnen Pin mit einem ADC zu hängen.
Dadurch liest man den Spannungswert (entweder durch genau berechnete Widerstandswerte oder man nimmt eben wieder eine LUT) und führt abhängig von diesem unterschiedliche Funktionen aus.
Warum das keine gute Vorgehensweise ist? Es spart doch massig Leiterbahnen und macht meine Chips billiger.... ja und nach ein paar Jahren oder Jahrzehnten machen die Buttons beim Drücken keinen guten Kontakt mehr - dadurch verändert sich der Widerstand und das Gerät macht Gott weiß was, nur nicht mehr was es soll. Shift-Register
74xx595 oder CD4094 usw. kann man verwenden, um sich weitere digitale Ausgänge zu verschaffen - der 4094 braucht 3 Inputs und man erhält dafür 5 (oder mit Biegen & Brechen 6) zusätzliche Outputs. Und man kann sie kaskadieren, d.h. tausche Rechenzeit gegen mehr Outputs.
Pläne dafür finden sich massig im Netz.
Du brauchst was, das Strobes erzeugt? Warum nicht ein CD4017?
Sequentiell angesteuerte Displays wie 7-Segment LEDs oder VFDs benötigen für jede Stelle (oder "Stelle" bei VFDs - da eine Stelle statt Segmenten auch eine Ansammlung an Symbolen enthalten kann) eine einzelne Steuerleitung. Von diesen darf immer nur eine einzige gleichzeitig aktiv sein. Wenn es 10 oder weniger sind, ist ein CD4017 die perfekte Lösung. Mit nur 2 Inputs (Clock, Reset) kann man bis zu 10 Segmente stroben. Also kann man z.B. eine zehnstellige 7-Segment-Anzeige bauen und braucht nur 9 Pins.
Der Microcontroller muss nur mitzählen und wenn er bei der letzten Stelle angekommen ist, statt Clock das Reset Pin vom CD4017 pulsen.
VFD-Ansteuerung
Ach ja - die Spannung der Heizung ermittelt man mit einem Labornetzteil, indem man in einem dunklen Raum langsam aufdreht und die Spannung notiert, bei der die Heizfäden gerade so nicht glühen.
Eine der Seiten der Heizung schließt man an Masse an. Sowohl die "Stellen" als auch die Displays benötigen relativ hohe Spannungen zum Leuchten. Man kann aber ruhig dieselbe Spannung für beides nehmen, beim Experimentieren habe ich Stellen und Segmente an 24V kurzgeschlossen und sie leuchteten.
VFDs kann man eigentlich nur kaputt machen, indem man die Heizung zu hoher Spannung aussetzt. Wenn die Drähte bei Raumbeleuchtung deutlich sichtbar glühen, sofort Spannung weg. Ein bisschen mehr und sie brennen durch. Die Segmente selbst können auch Schaden nehmen, wenn sie massiver Überspannung ausgesetzt werden - sie brennen dann aus. Das betrifft dann aber nur die Segmente, die man beim Experimentieren Überspannung ausgesetzt hat, der Rest vom Display bleibt funktionieren. ITOA benutzen um 7-Segment Displays anzusteuern
itoa (Integer To ASCII) wandelt Integer-Zahlen in Strings um, z.B. um sie auf ASCII Displays (also z.B. HD44780 LCD oder serielles Terminal) darzustellen. Den Trick kann man aber auch auf 7-Segment-Anzeigen erweitern. Nachteil: Die Anzeige ist dann linksbündig, nicht rechtsbündig wie man es gewohnt ist.
Der Trick ist, dass itoa einem bereits die Stellen erzeugt, also Einer- Zehner- Hunderter- usw. Man muss nur den ASCII Wert von 0 abziehen (48 dezimal) und dann noch eine LUT mit dem Bitmap der 7 Segmente bauen. Bei 7-Segment Anzeigen sind die Segmente typischerweise von A bis G codiert im Uhrzeigersinn beginnend mit dem oberen Segment. Bei der Zahl 0 sind alle Segmente außer G an, das wird als 63 codiert. Bei der 1 sind z.B. Segmente B und C an, damit wird diese als 6 codiert..