• Willkommen im Geoclub - dem größten deutschsprachigen Geocaching-Forum. Registriere dich kostenlos, um alle Inhalte zu sehen und neue Beiträge zu erstellen.

P.O.V. in der Praxis - Die Lösung des Spiegelschriftproblems

thomas_st

Geowizard
Hallo zusammen,

nachdem ich in diesem Thread vom Problem der spiegelverkehrten Schrift bei einer POV-Stage gelesen hatte, hatte ich doch so einen leichten déjà vu: dieses Problem hatten snaky, carhu und ich im Sommer doch auch zu lösen gehabt ;) Herausgekommen ist ein Prototyp einer POV-Stage, der relativ unabhängig von der Art der Schüttelbewegung immer lesbare Schrift erzeugt und außerdem ohne Batterien auskommt - aber dazu später :). Diese will ich in Absprache mit snaky und carhu hier mal vorstellen.

Die Lösung für das Problem der spiegelverkehrten Schrift fanden wir bei einer ...
... Schüttellampe! Ihr wisst ja, Schüttellampen funktionieren ohne Batterie: man schüttelt einige Minuten die Lampe und hat dann neben einem Muskelkater in den Armen für wenige Mikrosekunden Licht ;) Technisch ist das folgendermaßen realisiert: in der Lampe ist ein Röhrchen in dem sich ein frei beweglicher Magnet befindet und um welches eine Spule gewickelt ist. Wenn man nun die Lampe schüttelt, bewegt sich der Magnet durch das Röhrchen und damit durch die Spule. Ein sich durch eine Spule bewegendes Magnetfeld: das bedeutet elektr. Spannung - Induktionsgesetz. Immer wenn sich der Magnet in die Spule hinein- oder herausbewegt, wird für einen kurzen Moment eine Spannung in der Spule induziert - siehe Oszillogramm.


Oszillogramm: Spannung in einer Spule

Der von der induzierten Spannung getriebene Strom wird über eine Gleichrichterbrücke geführt und speist einen Kondensator. Letzterer dient dann als Stromquelle für die Lampe. Soweit so gut. Was hat das jetzt mit einer POV-Stage zu tun? Ganz einfach: wenn man es erreicht, dass sich die induzierte Spannung unterscheidet, je nachdem ob die Stage nach links oder rechts bewegt wird, hätte man einen Sensor für die Bewegungsrichtung. Nur leider ist das nicht so einfach. Die Bewegungsrichtung des Magneten ändert sich zwar abhängig davon, ob nach links oder rechts geschwenkt wird, aber gleichzeitig ändert sich auch die Ausrichtung des Magneten: einmal bewegt er sich halt mit den Nordpol zuerst durch die Spule und einmal mit dem Südpol :( - so ist die induzierte Spannung leider immer gleich: im Fall des Oszillogramms: beim Hineinbewegen des Magneten ein negativer Spannungsimpuls, beim herausbewegen ein positiver.

Lösung: man nimmt zwei Spulen! In diesem Fall ist die Reihenfolge, in der sich der Magnet durch die Spulen bewegt, abhängig von der Bewegungsrichtung der POV-Stage. Wenn man nun an beiden Spulen die Spannung abgreift, kann man daraus die Bewegungsrichtung ableiten.

Im folgenden Bild ist die Schaltung dargestellt, mit der das realisiert werden kann. Die beiden Spulen L1 und L2 sind die "Sensorspulen" durch die der Magnet läuft. Die hier induzierte Spannung wird über die Dioden D1 bis D8 gleichgerichtet und speist den Kondensator C1, der die Stromversorgung der Schaltung übernimmt. Jeweils eines der beiden Spulenenden ist mit einem Eingang des ATtiny 2313 verbunden, so dass dieser über Pinchange (PC)-Interrupts (IRQ) erkennen kann, in welcher Reihenfolge die beiden Spulen durchlaufen wurden (diese Eingänge nenne ich im Folgenden Sensoreingänge). Die Widerstände R1 und R2 halten die Sensoreingänge auf einem definierten Pegel.


Schaltplan POV-Stage

An den Tiny sind dann noch die LEDs für die POV angeschlossen, wobei hier helle LEDs genutzt werden, der Strom durch sie aber mit relativ großen Widerständen (RN1 und RN2) auf unter 1mA begrenzt wird - so reicht der geringe induzierte Strom aus, um die Schaltung zu versorgen. Die Stage nutzt 2x 7 LEDs, so dass zwei Zeilen gleichzeitig dargestellt werden können.

Einen unschönen Punkt an dieser Schaltung muss ich aber auch noch erwähnen: über die Dioden D1 bis D8 fallen ungefähr 0,7V ab. Dadurch ist kurzzeitig (während eines Impulses) die Spannung an den Sensoreingängen 0,7V höher als die Versorgungsspannung bzw. 0,7V niedriger als GND. Beides ist dem Tiny nicht wirklich zuträglich, der hier eigentlich nur -0,5V ... Vcc+0,5V sehen möchte. Bisher ist er mir aber noch nicht abgeraucht ;)


Prototyp

Obiges Bild zeigt den Aufbau des Prototypen auf einer Lochrasterplatine. Abgesehen vom unordentlichen Löten (ursprünglich hatte ich die Schaltung etwas anders aufgebaut und dann alles umändern müssen - daher die Lötzinnreste) ist vielleicht der Sensor noch eine Erwähnung wert: das Röhrchen ist ein Stück Installationsrohr aus dem Baumarkt, auf welches ich zwei Spulen á 1000 Windungen aufgebracht habe. Das Ganze dann noch mit Heißkleber fixiert und auf beiden Seiten mit einem Korkstopfen verschlossen und fertig ist der Bewegungssensor.

Soweit zur Hardware, im nächsten Posting kommt das Programm ...

Viele Grüße,
Thomas(_st)
 
OP
T

thomas_st

Geowizard
Programm

Das Programm habe ich diesem Posting als ZIP-File beigefügt. Das eigentliche Programm ist in der Datei main.c zu finden, während CharSet.c nur den Zeichensatz als große Tabelle und eine Übersetzungsfunktion ASCII -> Position des Zeichens in dieser Tabelle enthält.

Das Programm setzt mal wieder auf Interrupts (IRQ); und zwar konkret auf die Timer-IRQs von Timer 0 und 1 sowie die Pinchange-IRQs (PC-IRQ) der beiden "Sensor"-Eingänge (PB5 und PB6). Die zugehörigen Initialisierungen werden in "void initChip(void)" vorgenommen und zwar im Einzelnen:
- Ports an den die LEDs hängen auf Ausgang schalten
- Timer 1 aktivieren, so dass er in regelmäßigen Abständen Timer 1 Compare Match A-IRQs auslöst
=> dieser IRQ wird später die Ausgabe des Textes steuern indem er von einer Spalte zur nächsten umschaltet
- Timer 0 aktivieren, so dass er alle ca. 30ms einen Timer 0 Compare Match A-IRQ auslöst
=> Überprüfung, dass die beiden PC-IRQs innerhalb von 30ms aufgetreten sind
- PC-IRQs für PB5 und PB6 aktivieren
- einige interne Zustände initialisieren

Der Grundgedanke ist ja, dass aus der Reihenfolge in der die beiden PC-IRQs auftreten, die Bewegungsrichtung abgeleitet wird. Hierfür werden diese IRQs in der ISR(PCINT_vect) bearbeitet. Hier wird wie gesagt die Reihenfolge in der PC(PB5) und PC(PB6) auftreten ermittelt und überprüft, dass diese innerhalb eines Zeitfensters von ~30ms aufgetreten sind. Wenn dies der Fall ist, wird die Bewegungsrichtung festgelegt und die Ausgabe einer Zeile begonnen (der Zähler der die auszugebende Spalte hochzählt wird mit 0 initialisiert).

Gleichzeitig wird überprüft, wieviel Zeit vergangen ist, seit der letzte Ausgabestart erkannt wurde - wieviel Zeit der Cacher also gebraucht hat, um die Schaltung von links nach rechts oder von rechts nach links zu schwenken. Daraus wird abgeleitet, mit welchem Takt die neuen Spalten zu schrieben sind: wenn der Cacher flott ist halt flotter; wenn der Cacher die Schaltung langsamer bewegt halt langsamer. Hierzu wird der neue Wert berechnet, bis zu dem der Timer 1 läuft, bevor er einen IRQ auslöst (siehe "void doSyncWork(void)").

Wenn dieser IRQ ausgelöst wird, wird die nächste zu schreibende Spalte bestimmt und auf den LEDs ausgegeben. Hierfür sind die Funktionen "ISR(TIMER1_COMPA_vect)", "void outCol(BYTE bLine, char * pczText, WORD wColCounter, BYTE bStartColText, BYTE bLenText)" und "void outLine(BYTE bLine, BYTE bCode)" zuständig. Die an dieser Funktionen übergebene Parameter steuern in erster Linie, welche Zeile ausgegeben werden soll und ab wann die Ausgabe beginnen soll, damit der Text schön zentriert ausgegeben wird.

Das war eine sehr kurze Kurzfassung des Programms, aber wenn ich das ausführlich darstellen möchte, wird der Text wieder unlesbar lang. Daher: alles was unklar am Programm ist, bitte nachfragen: ich erläutere das dann lieber direkt ...

Soweit unsere Lösung für das Spiegelproblem bei POV-Stages. Die Schaltung funktioniert noch nicht perfekt (ab und an verliert sie mal die Synchronisation und zeigt dann für einige Schwenkbewegungen gar nichts an) ist aber sonst relativ gut und auch ohne Vorkenntnisse von POV-Stages zu lesen: ich hatte das Ganze mal Kollegen vorgestellt, die eigentlich sofort erkennen konnten was da geschrieben wird.

Das Programm selbst ist mit 2022 Byte nicht gerade klein und nutzt den Speicher des Tiny 2313 zu 98,7% aus - also da ist nicht mehr viel Luft ;) Der Tiny läuft mit internem 8MHz Oszillator und der Brownout-Detector sollte aktiviert werde (2,7V).

Soweit erstmal die Erklärung und nun her mit den Fragen.

Viele Grüße,
Thomas(_st)
 

Anhänge

  • POV.zip
    38,3 KB · Aufrufe: 132

jekejaerfi

Geocacher
Hallo Thomas,

schönes Teil! Nur erstmal eine Frage: So wie es aussieht habt ihr die Spulen selbst gewickelt, oder?

Schöne Grüße

Jens
 
OP
T

thomas_st

Geowizard
jekejaerfi schrieb:
Vielen Dank :)

jekejaerfi schrieb:
So wie es aussieht habt ihr die Spulen selbst gewickelt, oder?
Jep, die sind selber gewickelt. War eine Heidenarbeit - 3 oder 4 Stunden da zu sitzen und Spulen zu wickeln ... und das nur, weil der zur Verfügung stehende Wickelmotor dermaßen lahmarschig war ... :kopfwand: Beide Spulen haben 1000 Windungen.

Der Wickeldraht stammt größtenteils von einer in der Bucht geschossenen Schüttellampe [1], der Rest war dann normaler Spulendraht vom großen C - ich glaube 0,15mm Durchmesser.

Viele Grüße,
Thomas(_st)
___________
[1] so haben von der Schüttellampe der Spulendraht und der Magnet es in die POV-Stage geschafft; der Rest von Ihr war unbrauchbar.
 
OP
T

thomas_st

Geowizard
Christian und die Wutze schrieb:
Eine Bohrmaschine und ein elektrisches Zählwerk können die Zeit erheblich verkürzen.
Oder auch sehr verlängern :roll: - der Draht ist nicht wirklich stark und wenn die Bohrmaschine anläuft hätte ich doch starke Bedenken, dass er reißt - weil er sich irgendwo verhakt, die Spule von der man ihn runter wickelt hängen bleibt, ...

Viele Grüße,
Thomas(_st) - ideal wäre ein Modelbaumotor mit ein, zwei Umdrehungen pro Sekunde gewesen und nicht einer mit einer Umdrehung in 3 bis 4 Sekunden
 
Wir haben hier Spulen mit 10000 Windungen (und dünnem Drähtchen) gemacht, und dafür eine Bohrmaschine mit stufenlos regelbarer Drehzahl verwendet. Dann kannst du langsam loslegen. Funktioniert gut. Du solltest nur die Rolle mit dem Draht gut gelagert haben, damit der Draht leicht abrollt.
 
OP
T

thomas_st

Geowizard
Christian und die Wutze schrieb:
Wir haben hier Spulen mit 10000 Windungen (und dünnem Drähtchen) gemacht, und dafür eine Bohrmaschine mit stufenlos regelbarer Drehzahl verwendet.
Ich habe hier nur eine ca. 15 Jahre alte AEG-Maschine - die gestattet die Drehzahlregelung in (geschätzt) 10 Schaltstufen. Allerdings - und das ist das Problem - selbst mit gedrosselter Drehzahl läuft sie mit einem relativ starken Ruck an ...

Viele Grüße,
Thomas(_st)
 

waste1

Geocacher
Hallo thomas,

die Idee finde ich Klasse, die Spule sowohl als Versorgung als auch Sensor zu nehmen. Aber eigentlich sollte eine Spule mit 2 Magneten auch ausreichen. Ich habe das allerdings noch nicht überprüft, ist nur mal so ein Gedanke.

Einen unschönen Punkt an dieser Schaltung muss ich aber auch noch erwähnen: über die Dioden D1 bis D8 fallen ungefähr 0,7V ab. Dadurch ist kurzzeitig (während eines Impulses) die Spannung an den Sensoreingängen 0,7V höher als die Versorgungsspannung bzw. 0,7V niedriger als GND. Beides ist dem Tiny nicht wirklich zuträglich, der hier eigentlich nur -0,5V ... Vcc+0,5V sehen möchte. Bisher ist er mir aber noch nicht abgeraucht ;)
Da könnstet Du Schottky-Dioden nehmen, dann wäre der Spannungsabfall nicht so groß (<0,5V). Oder in die Sensorleitungen vor den Tiny Schutzwiderstände (1kOhm) einbauen.

Viele Grüße
Waste1
 

jmsanta

Geoguru
thomas_st schrieb:
Christian und die Wutze schrieb:
Wir haben hier Spulen mit 10000 Windungen (und dünnem Drähtchen) gemacht, und dafür eine Bohrmaschine mit stufenlos regelbarer Drehzahl verwendet.
Ich habe hier nur eine ca. 15 Jahre alte AEG-Maschine - die gestattet die Drehzahlregelung in (geschätzt) 10 Schaltstufen.[...]
die meisten Akkubohrschrauber können das doch ganz gut...
 
OP
T

thomas_st

Geowizard
waste1 schrieb:
die Idee finde ich Klasse, die Spule sowohl als Versorgung als auch Sensor zu nehmen.
Vielen Dank! Ich werde es auch an snaky und carhu weiterleiten ... (wobei, dass haben beide bestimmt auch schon gelesen ...)

waste1 schrieb:
Aber eigentlich sollte eine Spule mit 2 Magneten auch ausreichen. Ich habe das allerdings noch nicht überprüft, ist nur mal so ein Gedanke.
Ähm? Das verstehe ich jetzt nicht ganz. Wie sollen denn die beiden Magnete in das Röhrchen rein? Entweder sie ziehen sich an - dann verhalten sie sich wie ein großer Magnet oder sie stoßen sich ab ... dann sehe ich aber arge Probleme, dass es überhaupt funktioniert ...

waste1 schrieb:
Da könnstet Du Schottky-Dioden nehmen, dann wäre der Spannungsabfall nicht so groß (<0,5V).
Gute Idee - könnte ich ausprobieren ...

waste1 schrieb:
Oder in die Sensorleitungen vor den Tiny Schutzwiderstände (1kOhm) einbauen.
Widerstände werden schwierig, da der Stromfluss in die Sensoreingänge sehr gering ist. Allerdings hatte ich schon daran gedacht in diese Leitungen einfach zwei Dioden in Durchlassrichtung einzubauen, dann hat man den selben Spannungsabfall wie über die Gleichrichterbrücke.

Viele Grüße,
Thomas(_st)
 

ng-ebe

Geocacher
thomas_st schrieb:
Ähm? Das verstehe ich jetzt nicht ganz. Wie sollen denn die beiden Magnete in das Röhrchen rein? Entweder sie ziehen sich an - dann verhalten sie sich wie ein großer Magnet oder sie stoßen sich ab ...

2 Magnete hintereinander, mit leichtem Abstand - OK, ggf. Platzproblem - etwa so wie ein ganz kurzer Linearmotor - müsste funktionieren; u.U. die Spule(n) geschickt anordnen ...
 

waste1

Geocacher
thomas_st schrieb:
waste1 schrieb:
Aber eigentlich sollte eine Spule mit 2 Magneten auch ausreichen. Ich habe das allerdings noch nicht überprüft, ist nur mal so ein Gedanke.
Ähm? Das verstehe ich jetzt nicht ganz. Wie sollen denn die beiden Magnete in das Röhrchen rein? Entweder sie ziehen sich an - dann verhalten sie sich wie ein großer Magnet oder sie stoßen sich ab ... dann sehe ich aber arge Probleme, dass es überhaupt funktioniert ...
Ich stelle mir da 2 Neodym-Pillen vor, die auf die Enden eines kleinen Kunststoff- oder Holz-Stabs geklebt sind. Also keine langen Stabmagneten. Das Röhrchen mit der Spule müsste länger sein als die Spule selbst, damit beide Magnete durch gleiten können.

thomas_st schrieb:
waste1 schrieb:
Oder in die Sensorleitungen vor den Tiny Schutzwiderstände (1kOhm) einbauen.
Widerstände werden schwierig, da der Stromfluss in die Sensoreingänge sehr gering ist. Allerdings hatte ich schon daran gedacht in diese Leitungen einfach zwei Dioden in Durchlassrichtung einzubauen, dann hat man den selben Spannungsabfall wie über die Gleichrichterbrücke.
Jeder Eingang eines AVRs hat Schutzdioden gegen GND und VCC. So weit ich mich erinnere, sollte der Strom durch die Schutzdioden nicht größer als 1mA werden. Wenn also Strom fließt, dann wird es gefährlich. In deiner jetzigen Schaltung liegen die Schutzdioden des AVRs parallel zu den Gleichrichterdioden. Da könnte es durchaus sein, dass der AVR mehr als 1mA abbekommt. Deshalb die Schutzwiderstände von 1kOhm vorschalten, dann ist man auf der sicheren Seite.

Für die Funktion der Schaltung haben die 1kOhm-Widerstände keine Auswirkung, da ja kaum ein Strom fließt, wenn die Spannung innerhalb von VCC und GND ist.

Viele Grüße
Waste1
 

Geomane

Geocacher
Wie es der Zufall so will, habe ich mich auch gerade mit der Problematik der stehenden POV-Anzeige befasst. Allerdings habe ich eine andere Lösungsmöglichkeit verfolgt. Nachdem gleich der erste Versuch recht brauchbar war, stelle ich das mal hier vor.

POV01.jpg

Hier sieht man die Lochrasterplatte mit dem ATTiny1323, einer LED-Zeile, einem ISP-Stecker und einem Beschleunigungssensor.

POV04.jpg

Hier ist eine Detailaufnahme des Beschleunigungssensors. Er besteht nur aus einer mit GND verlöteten Spiralfeder mit einem Lötzinntropfen am freien Ende als Beschleunigungsmasse, sowie zwei abgewinkelten Drähten, die mit zwei Eingängen des ATTiny verbunden sind. Der linke Draht hat einen kleinen Abstand zur Feder, der rechte Draht liegt leicht aber deutlich an der Feder an. Wenn die Schaltung geschüttelt wird, öffnet sich kurz der Kontakt zwischen Feder und Draht, was ich in der Software als Startsignal verwende. Der andere Draht, der normalerweise keinen Kontakt zur Feder hat, wird momentan nicht benutzt. Damit habe ich Vergleichstests gemacht, wie sich das Ganze verhält, wenn man die Feder auf den Draht aufschlagen lassen will - aber das ist wesentlich unempfindlicher, da schüttelt man sich einen Tennisarm...

Der Vollständigkeit halber hier noch die Ober- und Unterseite der Schaltung:
POV03.jpg


POV05.jpg

Auf der Unterseite kann man auch, wenn man genau hinschaut, die SMD-Widerstände zur Begrenzung des LED-Stroms sehen.

Hier noch die Software dazu:

Code:
/*
 * -------------------------------------------------------------------------
 * POV (Schwinganzeige)
 * Atmel ATTINY 2313
 * -------------------------------------------------------------------------
 *
*/

#define VersNr 0.01.0000

/*
ATtiny2313

SELFPRGEN=0
DWEN=0
EESAV=0
SPIEN=1
WDTON=0
BODLEVEL=4,3V
RSTDISBL=0
CKDIV8=0
CKOUT=0
SUT_CKSEL=Int. RC Osc. 4MHz
*/

#define F_CPU 4000000 // Taktfrequenz 4MHz

#include <avr/io.h>
#include <avr/pgmspace.h>
#include <util/delay.h>

typedef unsigned char BYTE;

#define Textbloecke 3
#define AnzZchn1 9
#define AnzZchn2 6
#define AnzZchn3 6

#define Dauer1 10
#define Dauer2 15
#define Dauer3 15

#define Kont 0x10
#define Trigger 0x08

const BYTE Zeichen1[5*AnzZchn1] PROGMEM = {
  0x26,0x49,0x49,0x49,0x32,  // 83 = "S"
  0x04,0x3E,0x44,0x44,0x44,  // 116 = "t"
  0x20,0x54,0x54,0x54,0x78,  // 97 = "a"
  0x04,0x3E,0x44,0x44,0x44,  // 116 = "t"
  0x44,0x44,0x7D,0x40,0x40,  // 105 = "i"
  0x38,0x44,0x44,0x44,0x38,  // 111 = "o"
  0x7C,0x04,0x04,0x04,0x78,  // 110 = "n"
  0x00,0x00,0x00,0x00,0x00,  // 32 = " "
  0x22,0x49,0x49,0x49,0x36}; // 51 = "3"

const BYTE Zeichen2[5*AnzZchn2] PROGMEM = {
  0x7F,0x04,0x08,0x10,0x7F,  // 78 = "N"
  0x00,0x00,0x00,0x00,0x00,  // 32 = " "
  0x00,0x60,0x60,0x60,0x00,  // 46 = "."
  0x26,0x49,0x49,0x49,0x3E,  // 57 = "9"
  0x42,0x61,0x51,0x49,0x46,  // 50 = "2"
  0x22,0x49,0x49,0x49,0x36}; // 51 = "3"

const BYTE Zeichen3[5*AnzZchn3] PROGMEM = {
  0x7F,0x49,0x49,0x49,0x41,  // 69 = "E"
  0x00,0x00,0x00,0x00,0x00,  // 32 = " "
  0x00,0x60,0x60,0x60,0x00,  // 46 = "."
  0x3E,0x49,0x49,0x49,0x32,  // 54 = "6"
  0x22,0x49,0x49,0x49,0x36,  // 51 = "3"
  0x3E,0x41,0x41,0x41,0x3E}; // 48 = "0"

  /*
  0x00,0x00,0x00,0x00,0x00,  // 32 = " "
  0x00,0x00,0x5F,0x00,0x00,  // 33 = "!"
  0x00,0x07,0x00,0x07,0x00,  // 34 = """
  0x14,0x7F,0x14,0x7F,0x14,  // 35 = "#"
  0x24,0x2A,0x7F,0x2A,0x12,  // 36 = "$"
  0x63,0x13,0x08,0x64,0x63,  // 37 = "%"
  0x00,0x00,0x07,0x00,0x00,  // 39 = "'"
  0x00,0x1C,0x22,0x41,0x00,  // 40 = "("
  0x00,0x41,0x22,0x1C,0x00,  // 41 = ")"
  0x15,0x0E,0x1F,0x0E,0x15,  // 42 = "*"
  0x08,0x08,0x3E,0x08,0x08,  // 43 = "+"
  0x00,0x90,0x90,0x70,0x00,  // 44 = ","
  0x08,0x08,0x08,0x08,0x08,  // 45 = "-"
  0x00,0x60,0x60,0x60,0x00,  // 46 = "."
  0x60,0x10,0x08,0x04,0x03,  // 47 = "/"
  0x3E,0x41,0x41,0x41,0x3E,  // 48 = "0"
  0x42,0x42,0x7F,0x40,0x40,  // 49 = "1"
  0x42,0x61,0x51,0x49,0x46,  // 50 = "2"
  0x22,0x49,0x49,0x49,0x36,  // 51 = "3"
  0x18,0x14,0x12,0x7F,0x10,  // 52 = "4"
  0x2F,0x49,0x49,0x49,0x31,  // 53 = "5"
  0x3E,0x49,0x49,0x49,0x32,  // 54 = "6"
  0x61,0x11,0x09,0x05,0x03,  // 55 = "7"
  0x36,0x49,0x49,0x49,0x36,  // 56 = "8"
  0x26,0x49,0x49,0x49,0x3E,  // 57 = "9"
  0x00,0x6C,0x6C,0x6C,0x00,  // 58 = ":"
  0x00,0x2C,0x2C,0xEC,0x00,  // 59 = ";"
  0x00,0x08,0x14,0x22,0x41,  // 60 = "<"
  0x14,0x14,0x14,0x14,0x14,  // 61 = "="
  0x41,0x22,0x14,0x08,0x00,  // 62 = ">"
  0x02,0x01,0x51,0x09,0x06,  // 63 = "?"
  0x3E,0x41,0x5D,0x55,0x5E,  // 64 = "@"
  0x7C,0x12,0x11,0x12,0x7C,  // 65 = "A"
  0x7F,0x49,0x49,0x49,0x36,  // 66 = "B"
  0x3E,0x41,0x41,0x41,0x22,  // 67 = "C"
  0x7F,0x41,0x41,0x41,0x3E,  // 68 = "D"
  0x7F,0x49,0x49,0x49,0x41,  // 69 = "E"
  0x7F,0x09,0x09,0x09,0x01,  // 70 = "F"
  0x3E,0x41,0x49,0x49,0x7A,  // 71 = "G"
  0x7F,0x08,0x08,0x08,0x7F,  // 72 = "H"
  0x41,0x41,0x7F,0x41,0x41,  // 73 = "I"
  0x30,0x40,0x41,0x41,0x3F,  // 74 = "J"
  0x7F,0x08,0x08,0x14,0x63,  // 75 = "K"
  0x7F,0x40,0x40,0x40,0x40,  // 76 = "L"
  0x7F,0x02,0x0C,0x02,0x7F,  // 77 = "M"
  0x7F,0x04,0x08,0x10,0x7F,  // 78 = "N"
  0x3E,0x41,0x41,0x41,0x3E,  // 79 = "O"
  0x7F,0x09,0x09,0x09,0x06,  // 80 = "P"
  0x3E,0x41,0x51,0x61,0x7E,  // 81 = "Q"
  0x7F,0x09,0x19,0x29,0x46,  // 82 = "R"
  0x26,0x49,0x49,0x49,0x32,  // 83 = "S"
  0x01,0x01,0x7F,0x01,0x01,  // 84 = "T"
  0x3F,0x40,0x40,0x40,0x3F,  // 85 = "U"
  0x1F,0x20,0x40,0x20,0x1F,  // 86 = "V"
  0x3F,0x40,0x3E,0x40,0x3F,  // 87 = "W"
  0x63,0x14,0x08,0x14,0x63,  // 88 = "X"
  0x07,0x08,0x70,0x08,0x07,  // 89 = "Y"
  0x61,0x51,0x49,0x45,0x43,  // 90 = "Z"
  0x00,0x7F,0x41,0x41,0x00,  // 91 = "["
  0x03,0x04,0x08,0x10,0x60,  // 92 = "\"
  0x00,0x41,0x41,0x7F,0x00,  // 93 = "]"
  0x04,0x02,0x01,0x02,0x04,  // 94 = "^"
  0x40,0x40,0x40,0x40,0x40,  // 95 = "_"
  0x00,0x01,0x02,0x04,0x00,  // 96 = "`"
  0x20,0x54,0x54,0x54,0x78,  // 97 = "a"
  0x7F,0x44,0x44,0x44,0x38,  // 98 = "b"
  0x38,0x44,0x44,0x44,0x28,  // 99 = "c"
  0x38,0x44,0x44,0x44,0x7F,  // 100 = "d"
  0x38,0x54,0x54,0x54,0x18,  // 101 = "e"
  0x04,0x7E,0x05,0x05,0x05,  // 102 = "f"
  0x38,0x44,0x44,0x44,0xFC,  // 103 = "g"
  0x7F,0x04,0x04,0x04,0x78,  // 104 = "h"
  0x44,0x44,0x7D,0x40,0x40,  // 105 = "i"
  0x00,0x00,0x04,0x04,0xFD,  // 106 = "j"
  0x7F,0x10,0x10,0x28,0x44,  // 107 = "k"
  0x41,0x41,0x7F,0x40,0x40,  // 108 = "l"
  0x7C,0x04,0x78,0x04,0x78,  // 109 = "m"
  0x7C,0x04,0x04,0x04,0x78,  // 110 = "n"
  0x38,0x44,0x44,0x44,0x38,  // 111 = "o"
  0xFC,0x44,0x44,0x44,0x38,  // 112 = "p"
  0x38,0x44,0x44,0x44,0xFC,  // 113 = "q"
  0x7C,0x04,0x04,0x04,0x08,  // 114 = "r"
  0x48,0x54,0x54,0x54,0x24,  // 115 = "s"
  0x04,0x3E,0x44,0x44,0x44,  // 116 = "t"
  0x3C,0x40,0x40,0x40,0x3C,  // 117 = "u"
  0x1C,0x20,0x40,0x20,0x1C,  // 118 = "v"
  0x3C,0x40,0x3C,0x40,0x3C,  // 119 = "w"
  0x44,0x28,0x10,0x28,0x44,  // 120 = "x"
  0x3C,0x40,0x40,0x40,0xFC,  // 121 = "y"
  0x44,0x64,0x54,0x4C,0x44,  // 122 = "z"
  0x00,0x08,0x36,0x41,0x00,  // 123 = "{"
  0x7F,0x7F,0x7F,0x7F,0x7F,  // 124 = "|"
  0x00,0x41,0x36,0x08,0x00,  // 125 = "}"
  0x02,0x01,0x03,0x02,0x01,  // 126 = "~"
  0x3E,0x55,0x55,0x41,0x22,  // 128 = "€"
  0x7F,0x40,0x40,0x7F,0x00,  // 176 = "°"
  0x78,0x14,0x12,0x14,0x78,  // 196 = "Ä"
  0x3C,0x42,0x42,0x42,0x3C,  // 214 = "Ö"
  0x3E,0x40,0x40,0x40,0x3E,  // 220 = "Ü"
  0x7F,0x40,0x40,0x7F,0x00,  // 223 = "ß"
  0x20,0x55,0x54,0x55,0x78,  // 228 = "ä"
  0x38,0x45,0x44,0x45,0x38,  // 246 = "ö"
  0x3C,0x41,0x40,0x41,0x3C,  // 252 = "ü"
  */

/*********************************
*                                *
*  Anzeigewert an LEDs ausgeben  *
*                                *
*********************************/

void Ausgabe (BYTE Wert)
{
  PORTB = Wert & 0x1F;
  PORTD = (Wert & 0x60) | Kont | Trigger;
}

/**************************************
*                                     *
*  Verzögerung (in 100-us-Schritten)  *
*                                     *
**************************************/

void Delay100us (BYTE time)
{
  while (time-- != 0)
    _delay_us(100);
}

/*****************
*                *
*  Hauptroutine  *
*                *
*****************/

int main(void) 
{
  BYTE Textblock, Zchn, Spalte;
  BYTE i;
  BYTE AnzZchn[] = {AnzZchn1,AnzZchn2,AnzZchn3};
  BYTE Dauer[] = {Dauer1,Dauer2,Dauer3};
  
  /*******************************************************
  
  Portbelegung (PDIP20 bzw SOIC20):
  =================================

  PA0  5 out
  PA1  4 out
  PA2  1 out (reserviert für ISP)

  PB0 12 out LED1 (oben)
  PB1 13 out LED2
  PB2 14 out LED3
  PB3 15 out LED4
  PB4 16 out LED5
  PB5 17 out (reserviert für ISP)
  PB6 18 out (reserviert für ISP)
  PB7 19 out (reserviert für ISP)

  PD0  2 out
  PD1  3 out
  PD2  6 out
  PD3  7 in  Kontakt
  PD4  8 in  Trigger (Aktivierung)
  PD5  9 out LED6
  PD6 11 out LED7 (unten)

  *******************************************************/

  DDRA  = 0xFF;
  PORTA = 0;
  DDRB  = 0xFF;
  PORTB = 0;
  DDRD  = ~(Kont | Trigger);
  PORTD = Kont | Trigger;

  MCUCR = 0;

  while (1) 
  {   
    for (Textblock=0;Textblock<Textbloecke;Textblock++)
    {
      for (i=0;i<Dauer[Textblock];i++)  // jeder Text wird mehrmals hintereinander ausgegeben
      {   
        while (~PIND & Kont);  // auf Kontakt warten
        Delay100us(250);  // 50ms warten
        Delay100us(250);
        
        for (Zchn=0; Zchn<AnzZchn[Textblock]; Zchn++)
        {
          for (Spalte=0; Spalte<5; Spalte++)
          {
            switch (Textblock)
            {
              case 0 : Ausgabe(pgm_read_byte(&Zeichen1[5*Zchn+Spalte])); break;
              case 1 : Ausgabe(pgm_read_byte(&Zeichen2[5*Zchn+Spalte])); break;
              case 2 : Ausgabe(pgm_read_byte(&Zeichen3[5*Zchn+Spalte])); break;
            }
            Delay100us(5);  // Dauer eines "Punktes"
          }
  
          Ausgabe(0);  // alle LEDs aus
          Delay100us(15);  // Pause zwischen zwei Zeichen
        }

       _delay_ms(100);  // spiegelverkehrte Anzeige verhindern
      }
    }
  }

  // Testroutine für Kontakt und Trigger

  while (1) 
  {
    i = 0;

    if (PIND & Kont)
      i |= 0x03;

    if (~PIND & Trigger)
      i |= 0x7C;

    Ausgabe(i);
  }

  return 0;
}
Manches mag darin vielleicht im Hau-Ruck-Modus programmiert worden sein, aber mir kommt es momentan nur aufs Ergebnis an. Deshalb habe ich auch (noch) keinen Wert auf die Senkung des Stromverbrauchs gelegt, wenn nichts angezeigt wird. Im Programm können mehrere "Texte" abgelegt werden, die der Reihe nach angezeigt werden. Im Beispiel wird der Reige nach gezeigt: "Station 3" - "N .923" - "E .630".

Der Eingang, mit dem die Anzeigesequenz gestartet wird, heißt "Kont", der andere (momentan unbenutzte) Kontakt heißt "Trigger". Als "Bonus" steht im Listing noch eine (fast) komplette ASCII-Tabelle zur Erzeugung der Zeichen, die Ihr gerne verwenden könnt.

Ein paar Verbesserungsideen habe ich auch schon. Es wäre vielleicht besser, die Feder parallel zur Platine anzubringen, weil dann der Kontaktdraht kürzer ist und sich nicht so leicht unbeabsichtigt verbiegt. Die Schaltung ist durch Wartezeiten in der Software unempfindlich gegen Prellen. Es wäre also möglich, eine größere Beschleunigungsmasse zu verwenden, dann wäre das Ganze noch empfindlicher.

Versorgt wird die Schaltung übrigens momentan von einem 18650-Lithiumakku, aber das ist eigentlich nebensächlich.
 

waste1

Geocacher
@thomas_st
Mir ist da noch was aufgefallen. Wie hoch wird denn die Versorgungspannung, wenn man sehr stark schüttelt? Vielleicht braucht es noch eine Zenerdiode zur Spannungsbegrenzung. Nicht dass ein Superschüttler den AVR abrauchen lässt. :D

@Geomane
Kann man das neue Feature auf dem nächsten Stammtisch bewundern?

Viele Grüße
Waste1
 
OP
T

thomas_st

Geowizard
waste1 schrieb:
Ich stelle mir da 2 Neodym-Pillen vor, die auf die Enden eines kleinen Kunststoff- oder Holz-Stabs geklebt sind. Also keine langen Stabmagneten. Das Röhrchen mit der Spule müsste länger sein als die Spule selbst, damit beide Magnete durch gleiten können.
Also so etwas:
Code:
+---+-----------+---+
|N S|    Stab   |S N|
+---+-----------+---+
wobei die Nordpole oder die Südpole beider Magneten zueinander weisen. Jep, das konnte funktionieren. :) Würde das Ganze etwas einfacher machen, da mann dann nur eine Spule braucht. Man müsste dann beide Spulenenden als Sensoreingänge nutzen ... mal überlegen, das könnte klappen. :)

waste1 schrieb:
Jeder Eingang eines AVRs hat Schutzdioden gegen GND und VCC. So weit ich mich erinnere, sollte der Strom durch die Schutzdioden nicht größer als 1mA werden. Wenn also Strom fließt, dann wird es gefährlich. In deiner jetzigen Schaltung liegen die Schutzdioden des AVRs parallel zu den Gleichrichterdioden. Da könnte es durchaus sein, dass der AVR mehr als 1mA abbekommt. Deshalb die Schutzwiderstände von 1kOhm vorschalten, dann ist man auf der sicheren Seite.
Ok. Ich hatte gedacht, dass Du mit dem Widerstand die Spannung herabsetzen wolltest (Spannungsteiler halt), das wäre aber zu kompliziert und diffizil geworden, da dann das Ganze von absoluten Höhe an Vcc abhängig gewesen wäre.

Geomane schrieb:
Wie es der Zufall so will, habe ich mich auch gerade mit der Problematik der stehenden POV-Anzeige befasst.
Jep. Sieht so aus, als wenn sich damit in letzter Zeit viele beschäftigt haben ;)

Geomane schrieb:
Allerdings habe ich eine andere Lösungsmöglichkeit verfolgt. Nachdem gleich der erste Versuch recht brauchbar war, stelle ich das mal hier vor.
Schöne Idee und schöner Aufbau :) Der Beschleunigungssensor gefällt mir sehr.

Wenn ich das Programm und Deine Beschreibung richtig verstanden habe, dann schreibst Du nur in einer Bewegungsrichtung und auf dem Rückweg ist die POV-Stage dann dunkel? Damit spart man sich einiges an Arbeit, die beiden Schriftzüge mehr oder weniger deckungsgleich zu bekommen.

Geomane schrieb:
Auf der Unterseite kann man auch, wenn man genau hinschaut, die SMD-Widerstände zur Begrenzung des LED-Stroms sehen.
Ich hatte ein Widerstandsnetzwerk verwendet - spart deutlich an Lötarbeit ;)

Geomane schrieb:
Im Programm können mehrere "Texte" abgelegt werden, die der Reihe nach angezeigt werden. Im Beispiel wird der Reige nach gezeigt: "Station 3" - "N .923" - "E .630".
Ist unserer Stage auch so. So zeigt sie nacheinander:
Code:
  Hallo      =>     Du wolle    =>   Final von   =>  N49°xx.xxx'
Moin, Moin!  =>   Koordinaten?  =>  GC1A1QW ist: =>  E008°xx.xxx'

waste1 schrieb:
@thomas_st
Mir ist da noch was aufgefallen. Wie hoch wird denn die Versorgungspannung, wenn man sehr stark schüttelt? Vielleicht braucht es noch eine Zenerdiode zur Spannungsbegrenzung. Nicht dass ein Superschüttler den AVR abrauchen lässt. :D
Die Gefahr ist wirklich gegeben; und das hatte ich sogar schonmal gemessen :eek:ps: :

Spannung an den Sensoreingängen

Das Ganze ist die zeitliche Abhängigkeit der Spannung an den Sensoreingängen. Damit wollte ich eigentlich ermitteln, wieviel Zeit maximal zwischen den beiden Synchronisationsimpulsen der Spulen vergeht. Man kann aber auch erkennen wie die Spannung dieser Impulse (zur Erinnerung: im Maximum = Vcc+0,7V) mit der Schüttelgeschwindigkeit ansteigt. Beim schnellen Schütteln liegt Vcc bei knapp über 6V :eek:ps: :dead: . Über die Z-Diode werde ich also mal nachdenken - hoffentlich liest hier keiner mit, der dann mit Muskelkater im Wald steht und sich fragt, wieviel von seiner Leistung in der Diode verbraten wurde :hilfe:.

Viele Grüße,
Thomas(_st)
 

Millhouse

Geocacher
Ich finde die Lösung von Geomane einfach genial. Einfach eine Feder, die gegen einen Kontakt schlägt, als Synchronisation zu verwenden. Das ist sehr einfach umzusetzen.

Habe direkt mal eine experimentelle Lochrasterplatine auf Basis des ATtiny84 entsprechend modifiziert um es mal auszuprobieren. Funktioniert prima!

b5jvq0ssamskprlzr.jpg


Die Feder mit dem Kontakt sieht man im Bild unten rechts.

Die "Mechanik muss natürlich noch irgendwie vor roher Gewalt geschützt werden. Meinen C-Code ist angehängt. Der Nachteil der POV-Displays ist aber das nur wenig Text in eine Zeile passt. Ich habe deshalb einen Zähler implementiert um zwischen max. 255 Zeilen Text umzuschalten. Die "Zeitdauer" kann mit #define TIMECOUNT geändert werden. 50 ist schon recht lang. Fügt man weitere Textzeilen ein, so muss #define TEXTMAX angepasst werden.

Auch Stromsparfunktionen kann man implementieren von man den Tiny nach einer Zeile schlafen legt und durch einen PINCHANGE-Interrpt am Trigger Pin wieder aufwachen lässt. Habe ich hier aber nicht implementiert. Der Code war nur zum austesten gedacht.

Hier mal meine main.c. Der Rest ist in der angehängten Zip-Datei.

Millhouse.

Code:
/*
 * pov display with ATMEL ATtiny84
 * WinAVR-20081205
 *
 * history :
 * 20090114 millhouse V 1.00 
 *
 */   
#include <avr/io.h>
#include <avr/pgmspace.h>
#include <util/delay.h>
#include "font.h"


// Fuses (left as shipped)
// 8Mhz internal RC-Oscillator
// CKDIV divide by 8
// -> 1Mhz Clock

// Hardware definitions.
//
//            +---+ +---+
// Vcc       #|1  +-+ 14|# GND
//            |         |
// PB0       #|2      13|# PA0
//            |         | 
// PB1       #|3      12|# PA1
//            |         | 
// /Reset    #|4      11|# PA2
//            |         | 
// PB2       #|5      10|# PA3
//            |         | 
// PA7       #|6       9|# PA4 / SCL
//            |         | 
// PA6/Mosi  #|7       8|# PA5 / Miso
//            +---------+

// Display LEDS ((Leds on Port A)
//
// connection: Portpin ---100Ohm---LED---GND
//
#define LEDPORT		PORTA
#define LEDDDR  	DDRA
#define LEDPIN  	PINA

// Trigger pin PB2, activated Pullup resistor
//
// Trigger -> GND
//
#define TRIGGER  	2
#define TRIGDDR  	DDRB
#define TRIGPORT 	PORTB
#define TRIGPIN  	PINB

// on/off/toggle macros
#define ON(portpin)     LEDPORT |=  (1<<(portpin))
#define OFF(portpin)    LEDPORT &= ~(1<<(portpin))
#define TOGGLE(portpin) LEDPIN  |=  (1<<(portpin))

//define boolean constants
#define FALSE (0!=0)
#define TRUE !FALSE

// delays in ms
#define PIXEL_DELAY 	1
#define CHAR_DELAY 	( PIXEL_DELAY * 6 )
#define PRE_DELAY 	50
#define POST_DELAY 	50

// text switch timing
#define TIMECOUNT 	50    // elapsed loops before switching to next text
#define TEXTMAX 	2	// two strings to display

/*
 * Umlaut()
 *      Umlaut conversion
 */
char Umlaut(char c)
{
        // c für die Umlaute korrigieren
        if (     c=='Ä') return(1);
        else if (c=='Ö') return(2);
        else if (c=='Ü') return(3);
        else if (c=='ä') return(4);
        else if (c=='ö') return(5);
        else if (c=='ü') return(6);
        else if (c=='ß') return(7);
        else if (c=='°') return(8);
        return(c);
};

/*
 * print one character on display
 */
void print_char(uint8_t c)
{
uint16_t fontpointer = Umlaut(c)*6;
uint8_t i;

	for (i=0; i<6 ;i++){
		LEDPORT = pgm_read_byte(font6x8 + fontpointer++);
		_delay_ms(PIXEL_DELAY);
	};
}

/*
 * print a string from rom on display
 */
void print_string_P(char *s)
{
char c;
	
	while( (c = pgm_read_byte(s++)) ){
		print_char(c);
	};
	LEDPORT = 0x00;	
};

/*
 * main()
 *	main loop
 */
int main (void)
{
uint8_t  text;
uint32_t timer;
	
	LEDPORT = 0x00;
	LEDDDR = 0xFF;

	// switch pullup for Trigger on
	TRIGPORT |= (1<<TRIGGER);

	while(1){
		// wait until trigger goes low
		while ( TRIGPIN & (1 << TRIGGER) ){
			;
		};		

		// wait a moment
		_delay_ms(PRE_DELAY);

		// wite data on pov display
		switch (text){
		case 0: print_string_P( PSTR("N51°12.345'") ); break;
		case 1: print_string_P( PSTR("E6°54.321'") ); break;
		};
		// wait a momment
		_delay_ms(POST_DELAY);
		if (timer++ > TIMECOUNT){
			timer = 0;
			text++;
			if (text>=TEXTMAX){
				text = 0;
			};
		};
	}
	return (0);
}
 

Anhänge

  • POV_ATtiny84.zip
    9,4 KB · Aufrufe: 60
Oben