Forum: Mikrocontroller und Digitale Elektronik Menüsteuerung


von Daniel (Gast)


Lesenswert?

Hallo,

ich bin gerade dabei mal ein altes Problem wieder zu lösen. Will ein
12VDC-Motor mit PWM ansteuern, sozusagen schneller,langsamer. Dann auch
noch Links und Rechtslauf.
Dafür hab ich mir den ATTiny2313 DIP20 rausgesucht. Frage mich ob der
uC der richtige Ansatz ist. Meine Sorge ist, der benutzt nur einen
10Bit Prescaler für Timer1 und Timer0. Ob man damit Tastenentprellung
und PWM realisieren kann?
Ich will da vier taster(4 Pins) anschliesen ein LCD(6Pins) und naja
dann der Standart(reset,xtal...). Nun sind ja vier Taster nicht die
Welt.Und um alles zu organisieren darüber, wollte ich eine
Menüsteuerung dafür entwickeln.
Funktionen wie: Start, Stop, Links-, Rechtslauf,schneller, langsamer,
Demoprogramm
Nun habe ich zwar so die Grundzüge von Assembler, Speicherorganisation,
Pointern, ... verstanden. Aber mir fehlt einfach dass verständnis dass
nun zu programmieren.
wie muss ich die Auswertung vornehmen für eine Taste, damit dem
Programm klar ist das sie mehrere Bedeutungen hat.
Mach man das mit einem Register? und dann jedes Bit auswerden und
sprungmarken organisieren? Mir fehlt einfach der Ansatz.
Wenn mir jemand helfen könnte, wäre ich sehr dankbar.

daniel

von Daniel (Gast)


Lesenswert?

Hat sich geklärt :)
mir ist die Idee gekommen, nach einer Entprell funktion die ich hier im
Forum gefunden habe werde ich es mit einem cp- Befehl durch die
Funktionen springen :)
und mich so durch mein Menü hangeln.
Falls wer noch eine bessere Lösung hat :) ich bin für Tipps immer
lernbereit :)

daniel

von Hannes L. (hannes)


Lesenswert?

Nunja, "Menü" bedeutet ja, dass du einen Dialog mit dem Controller
führst, also ein Ausgabegerät (LCD) angeschlossen hast, das dir die
"Menüangebote" anbietet.

Beim Abarbeiten der "Tastendruck-Flags" (keypress) der
Entprellroutine könnte auch der Befehl "sbrc" bzw. "sbrs" in
Kombination mit einem "rjmp" oder "rcall" hilfreich sein.

Einige Beispiele für interaktive Menüs auf AVR in ASM kannst du hier
finden:
http://www.hanneslux.de/avr/stopuhr/index.html
http://www.hanneslux.de/avr/zuenduhr/index.html

...

von Freak5 (Gast)


Lesenswert?

Was bedeutet rcall eigentlich genau? rjmp kann ich mir ja vorstellen,
aber ein call geht doch immer an eine bestimmte routine :-?

von Daniel (Gast)


Lesenswert?

Hi, vielen Dank
super für die schnelle Antwort. rcall bedeutet relative call, vermute
ich zumindest. Zumindest hab ich das so beim Rabbit kennengelernt, soll
angeblich ein byte sparen da der befehl nur um 122 vorwärts und um 126
rückwärts springen kann.
Ja es soll ein LCD angeschlossen werden, denke da an das im Tutorial
vorgestellte 20x2 Zeiler LCD mit dem Controller HD44780. Wenn jemand ne
günstigere Lösung hat, lass mich gern beraten. So wie ich erfahren habe
geht ja das meiste Geld ins Display ;).
Ich habe dass so ausprobiert
main:
     BREQ  r24,r23     ;wenn gleich dann mache das nächste
     rcall Sprungmarke ;soll springen
     rjmp  main        ;IRQ gesteuerte endlosschleife
und was macht der gute ?! der macht nicht den Sprung obwohls gleich ist
:( der macht auch nicht den rjmp, der geht weiter und da bei mir als
letzes ein sprung zur init kam, machte er den Sprung. Verstanden hab
ich es nicht, warum das so ist. Aber die Idee ist doch erstmal korrekt
oder?

Bei sbrs kann ich nur ein Bit prüfen nicht die ganze Summe und
vergleichen. Durch eine Entprellfunktion ( ;) hier aus dem Forum) gibts
ein Register (Status von 4 Tasten) wo auch ein gleichzeitiges drücken
mehrerer Tasten vorkommen kann und das wollte ich als ungültig
erachten.

Vielen Dank für die Links, das sieht interresant aus.
Aber noch eine Frage mal am Rande verlegen , was hat das eigentlich
mit diesen Makros auf sich? sind das eine Art unterprogramme oder wie
kann man sie verstehen?

daniel

von Daniel (Gast)


Lesenswert?

>>>> NACHTRAG <<<<<<
Sorry, ich hatte nicht BREQ benutzt sondern CPSE.
Da stand irgendwo CPSE r24,r25 ; Vergleiche r24,r25 springe wenn
gleich
habs dann so geschrieben

main:
CPSE r24,r25
rcall Sprungmarke
rjmp main

Wie gesagt ging aber nicht :(, aber ich werd es mal mit BREQ
Sprungmarke probieren, natürlich muss dann aber ein CP r24,r25 davor
:)

Noch eine Frage zu diesem hier
txt_10:     .db "10ms",0,0
was bedeuten die ,0,0 dahinter?


daniel

von Hannes L. (hannes)


Lesenswert?

> txt_10:     .db "10ms",0,0
> was bedeuten die ,0,0 dahinter?

Die erste 0 ist die Ende-Kennung für die Stringausgabe-Routine, die
zweite 0 ist erforderlich, damit die Anzahl der Bytes pro .db-Zeile
geradzahlig wird, da der Flash word (Doppelbyte)-adressiert wird.

Du bemängelst, dass sbrc/sbrs nur ein Bit prüft. Das ist ja gerade der
Vorteil. Die PeDa-Entprellroutine (die im Timer-Interrupt läuft und bis
zu 8 Tasten an einem Port gleichzeitig entprellt) stellt neben dem
Tastenstatus (keystate, bei mir tas) auch die Tastenflags (keypress,
bei mir tfl) zur Verfügung, in dem jedes Bit für eine andere Taste
steht. Um nun auf die Tastendrücke zu reagieren, können die einzelnen
Bits geprüft und danach verzweigt werden. Dass mehrere gleichzeitig
gedrückte Tasten auch erkannt werden, ist kein Mangel, sondern ein
echter Vorteil. Somit sind z.B. Shift-Tasten möglich, die nicht separat
ausgewertet werden (das Bit in keypress/tfl einfach nicht auswerten),
sondern deren Status (Bit in keystate/tas) beim Auswerten der anderen
Tasten nebenbei zur Fallunterscheidung mit herangezogen wird. So kann
eine Taste mehrere Funktionen bekommen, abhängig vom Zustand einer
Shift-Taste.

Durch eine kleine Erweiterung der Tastenentprellroutine kann man auch
auf das Loslassen der Tasten triggern und sogar auf unterschiedlich
lang gedrückte Tasten unterschiedlich reagieren. Dann braucht die
Routine aber einige Variablen mehr, weshalb ich dann die Variablen
nicht mehr in Registern halte sondern im SRAM.

> Was bedeutet rcall eigentlich genau?

rcall ist der Aufruf eines Unterprogramms, welches mit ret beendet
wird, worauf das Programm direkt hinter dem rcall fortgeführt wird.

Informationen zu den Befehlen findest du übrigens im "AVR instruction
set", das jedem AVR-Studio als PDF beiliegt und in der Onlinehilfe zum
AVR-Studio (Cursor auf fraglichen Befehl, F1-Taste...).

...

von Simon K. (simon) Benutzerseite


Lesenswert?

Hi Daniel und Feak5

RCALL heißt Relative Call, soweit völlig korrekt. Daniels Beschreibung
ist auch richtig. Ein Unterschied gibts aber: Beim AVR ist es so, dass
er +/- 127 Schritte springen kann.

Unterschied zwischen RCALL und RJMP:

Wenn du RCALL benutzt, dann wird die aktuelle Adresse des
Befehlszeigers auf dem Stack gesichert und an das angegebene Label
gesprungen. Wenn der Controller jetzt auf ein 'RET' stößt, holt er
die Adresse wieder vom Stack und springt an die Stelle zurück, von wo
RCALL ausgeführt wurde.
Eine typische Anwendung hierfür ist ein Funktionsaufruf. Also ein
Unterprogramm, wo reingesprungen wird (RCALL), was gemacht wird und
anschließend wieder zurückgesprungen wird (RET).

Der Unterschied zu RJMP ist nun folgender: RJMP speichert keine Adresse
auf dem Stack. RJMP springt einfach nur an das angegebene Label. Benutzt
man hier (fälschlicherweise) ein 'RET', so wird der Controller an eine
zufällige Adresse springen, da zuvor noch keine validen Daten auf den
Stack gelegt wurden.

Man kann sagen, RCALL ist "bidirektional" und RJMP ist
"unidirektional".

Wenn man mit RJMP/RCALL weiter als 127 Bytes springen will, so muss man
die 2 Byte langen Equivalente "JMP" und "CALL" verwenden.




>>     BREQ  r24,r23     ;wenn gleich dann mache das nächste

Genau, das funktioniert nicht, wie du schon sagtest.

Wenn du statt BREQ aber CPSE nimmst, sollte das ganze eigentlich
funktionieren Grübel. Der Fehler muss woanders liegen.




Makros sind keine "Funktionen" oder ähnliches. Sie sind einfach nur
Definitionen.

Der Precompiler (Also der Compiler, der vor dem eigentlichen Compiler
durchläuft) sucht das gesamte Programm nach zuvor definierten Makros
ab. Stößt er auf ein Makro, dann ersetzt er diesen Makronamen durch den
Inhalt des Makros.
Man kann sagen, dass ein Makro einfach nur ein Synonym ist. Also
einfach ein "Suchen und Ersetzen" durch den Precompiler. (Wobei es
auch kompliziertere, verschachtelte, Makros gibt.. Aber das ist mir zu
wurstelig).
Wenn dann alles ersetzt ist, wird das Programm kompiliert.




Die Nullen hinter dem Text schließen den Text ab. Das findet man
besonders bei C-Programmierung und Assemblerprogrammierung wieder. Es
wird benutzt, um zu bestimmen, wo der Text zuende ist. Stößt also
irgndeine Funktion auf eine 0 in dem String, so wird der String als
zuende erkannt. (Nicht verwechseln mit dem Zeichen '0'. Es ist die
"binäre Null").

Warum Hannes da zwei Nullen hingeschrieben hat, kann ich mir nicht
erklären.
Ich könnte mir aber vorstellen, dass es daran liegt, dass der
Flash-Speicher (Wo er die Daten da ablegt) Doppel-byte orientiert ist.
Er speichert aber diese 0 (wegen ".db" (= data BYTE)) als ein Byte
ab. Um aber jetzt ein Doppelwort vollzukriegen, darf es keine ungrade
Anzahl an Bytes hinter einem .db geben. Und als "Ausgleich" hat er
dann noch eine Null dahintergehangen. (Ist so gesehen natürlich
"toter" Speicherbereich, aber 1. Hat man ja genug und 2. Gehts auch
nicht, oder nur mit Umständen anders).

von Simon K. (simon) Benutzerseite


Lesenswert?

Sorry, ich meinte "Freak5" :-)

Für andere Tippfehler schau ich meinen Post jetzt nicht mehr nach. Ich
geh nun schlafen.

von Simon K. (simon) Benutzerseite


Lesenswert?

Verdammt Hannes! Du warst vor mir. Ich hab wohl zu lange geschrieben :-)

von Hannes L. (hannes)


Lesenswert?

> Verdammt Hannes!

Haste Töne? Jetzt werde ich auch noch verdammt... ;-)

Simon, ich bin ganz bewusst nicht so ins Detail gegangen, um zu
erreichen, dass Freak5 (ein langjähriger Forumbenutzer) mal die Nase in
die auf seinem Rechner vorhandenen Informationsquellen steckt.

...

von Freak5 (Gast)


Lesenswert?

Ich programmiere auch schon lange die AVRs die Aussage oben, dass man
rjmp und rcall verwenden soll, hat in mir nur die Frage hervorgerufen,
was daran so besonders ist, dass genau das in diesem speziellen Fall
genutzt werden muss. Ich benutze, wenn nichts optimiert werden muss
immer call und jmp.

Diese Aussage
"Wenn man mit RJMP/RCALL weiter als 127 Bytes springen will, so muss
man
die 2 Byte langen Equivalente "JMP" und "CALL" verwenden."
hat eigentlich schon alles beantwortet. Dass RJMP und RCALL relative
Jump und relative CALL bedeutet, wusste ich auch, da ich schließlich
auch im Besitz des Instruction Set Datenblattes bin. Nur da steht nicht
die Größe des Befehls drin.

OK, HanneS hat auch recht, dass ich mir mal die Aufrufe wie ICall,
EICaLL usw. ansehen sollte.

von Hannes L. (hannes)


Lesenswert?

> Ich benutze, wenn nichts optimiert werden muss
> immer call und jmp.

Hast du denn schon mal versucht, einen Tiy15 oder Mega8 mit call und
jmp zu programmieren?

Oder schießt du immer mit Kanonen auf Spatzen? Ich meine damit, oder
ignorierst du die kleineren AVRs, die viele Aufgaben sehr gut erledigen
können. Es muss nicht immer der Mega16 oder größer sein...

...

von Hannes L. (hannes)


Lesenswert?

Sorry, da fehlt'n "n"...

...

von Freak5 (Gast)


Lesenswert?

@Hannes: Naja, ich habe jetzt schon seit einem Jahr nicht mehr
programmiert, da ich mir einen Programmer gelötet habe, der aufgrund
eines Krümelchens Lötzinn zwischen dem Sockel und GND meine AVRs
gekillt hat(ich will in 2 Wochen wieder mit dem Programmieren
anfangen), aber vorher habe ich das immer so gemacht, dass ich mir ganz
schnell einen Sockel auf eine Lochrasterplatine gelötet habe und dann
die Schaltung darüber aufgebaut habe(ich habe kein Breadboard).
Da die Sockel günstig sind und man die AVRs schnell tauschen kann, habe
ich dann immer einen AVR für X Schaltungen verwendet. Dumm ist nur, wenn
man den Code verliert, aber bis jetzt habe ich so wie so nur
Testschaltungen gemacht. (OK, einen AVR 32 benutze ich als Oszillator.
Mit den letzten 3 Pins, die den Kurzschluss überlebt haben, ist der
auch nicht mehr zu viel zu gebrauchen.)

Wenn ich einen AVR in eine Schaltung bauen würde, wo ich weiß, dass er
da auch für immer stecken bleibne soll, würde ich mir ein paar Gedanken
machen, ob ich nicht einen Tiny nehmen kann, wenn ich nicht x
Erweiterungen im Kopf habe, die vielleicht einen ATmega128
voraussetzen.

Bis jetzt habe ich ja so wie so nur aus Spaß programmiert um private
Probleme zu lösen. In der Schule wird man höchstens für einen Spinner
gehalten( Ich glaube, dass die meinsten Lehrer sowas nicht zum Bereich
des Machbaren zählen ).

von Daniel (Gast)


Lesenswert?

Hallo,
Vielen Dank für die Superschnellen Antworten.
Bin gerade dabei mich durch das Datenblatt zu kämpfen. ;) Stück für
Stück gehts voran. :)
Ich hab noch eine Frage zu den Tasten, man kann maximal nur 2 tasten
Interrput fähig machen, INT0 und INT1 oder?
müssen die anderen dann gepollt werden?
Vielen dank, ich denke die Auswertung wird nicht mehr meine Sorge sein,
viel mehr die Gestaltung und Strukturierung und dann die Ausgabe
dessen.
Aber dazu kann ich mich durch die Beispiele arbeiten :)

daniel

von Hannes L. (hannes)


Lesenswert?

> man kann maximal nur 2 tasten
> Interrput fähig machen, INT0 und INT1 oder?

Das Einlesen der Tasten über diese Interrupts ist die schlechteste Art,
Tasten einzulesen. Das gibt nur Ärger. Nimm stattdessen einen
Timer-Interrupt und benutze die Entprellroutine von Peter Dannegger,
dann bist du alle Sorgen los. Beispiele, wie das geht, habe ich weiter
oben
http://www.mikrocontroller.net/forum/read-1-375525.html#375614
bereits verlinkt.

Weitere Informationen dazu findest du in der Artikelsammlung (nach
"entprellung" suchen) und in der Codesammlung (nach "bulletproof"
suchen).

...

von Daniel (Gast)


Lesenswert?

Hallo,
ohh wusste ich nicht, beim Rabbit hatte ich es immer so gemacht. ja
diesen Routine von PD hab ich mir schon erlaubt zu verwenden ;). Ich
brauch den Timer0 aber für mein PWM signal, hab es auf Phase Correct (
Zählt rauf und wieder runter)eingestellt, mit dem Compare Match A, kann
ich da noch ein Compare Match B verwenden für die Entprellroutine.
Aber man braucht doch den Interrupt um den guten uC wieder zu wecken
wenn man ihn schlafen geschickt hat? da müsste ich doch meinen Start-
Taster dranhängen.

daniel

von Hannes L. (hannes)


Lesenswert?

Das Wecken aus dem Tiefschlaf funktioniert nur mit dem Level-Interrupt.
Das ist der einzige sinnvolle Grund, Taster an einen externen Interrupt
zu schalten. Die Entprellung würde ich aber trotzdem per
PeDa-Entprellung im 10..20ms-Abstand machen.

Hat der Tiny2313 nicht 2 Timer? (Sorry, ist nicht mein Favorit)
Ist es evtl. möglich, zusätzlich zur Hardware-PWM-Erzeugung einen
Interrupt auszulösen und darin einen Softwaretimer für die Entprellung
hochzuzählen? Die eigentliche Entprellung (und das Rücksetzen des
Softwaretimers) kann ja die Mainloop übernehmen.

In einem größeren Projekt habe ich 5 Bytes zu entprellen und noch auf
kurzen oder langen Tastendruck zu unterscheiden, da läuft die
Entprellung auch in der Mainloop, allerdings von einem Timer per Flag
synchronisiert.Aufgrund der Menge der Variablen werden diese dann
natürlich im SRAM gehalten. Es gibt also immer einen Weg...

...

von Hannes L. (hannes)


Lesenswert?

Vergessen: Ein Beispiel, wie der AVR (Tiny15) per Level-Interrupt aus
dem Tiefschlaf geweckt wird, die Tastenentprellung aber per
PeDa-Routine erfolgt, findest du hier:
http://www.hanneslux.de/avr/divers/melody/melody04.html

Da wird übrigens auch noch der ADC-Interrupt als Timer missbraucht...

...

von Daniel (Gast)


Lesenswert?

Hi,
Vielen Dank, ich werde es mir Anschauen, nur hab ich heute leiderns
keine Zeit :(. Es stehen die Prüfungen an :(
Aber naja so ganz zum lernen komm ich auch nicht ;). Ich hab mir da
noch Gedanken gemacht, appropo ADC: der Tiny2313 hat gar keinen ADC.
ich wollte aber die Drehzahl des Motors mittels eines
Strom-Spannungswandlers messen und dann die Spannung in die Drehzahl
umwandeln,mittels Tabelle oder direkt berechnen.
da brauch ich doch ein ADC?
deswegen denke ich werde ich umsteigen auf den ATtiny26L der hat einen,
nur leiderns hat er keinen direkten PWM Ausgang mehr :(.
Oder täusche ich mich und der hat beides. Zumindest im Datenblatt
konnte ich keinen besagten Pin finden :(. Vielleicht sollte ich doch
ganz umsteigen auf einen ATmega8?

daniel

von Hannes L. (hannes)


Lesenswert?

Ohne jetzt ins Datenblatt zu schaun sagt mir meine Erinnerung, dass der
Tiny26 zwei PWM-Kanäle hat, die auf 4 PWM-Ausgänge (zwei davon
invertiert) geführt sind. Es wird sich lohnen, das Datenblatt mal zu
befragen... ;-)

...

von daniel (Gast)


Lesenswert?

Hi,
ja du hast recht, der Tiny26L hat sowohl 4PWM Ausgänge,sowie 10 ADC
eingänge. Schlecht wird nur das ich meine Tasten zum Beispiel nicht
alle auf einen Port legen kann, denn ich muss mich entscheiden wer 4
Eingänge hintereinander bekommt, denn XTAL1,XTAL2,Reset sind auch schon
wieder 3 Pins weg. Und 4 hinereinanderfolgende nehme ich für das
Display. Es dürfte kein Problem sein Tasten von mehreren Ports
abzufragen, oder?
Wie sind deine Menüs eigentlich aufgebaut, von der Steuerung
betrachtet?
Du hast ein steuermenü das den Pointer festlegt, welcher dann in ein
Menue springt und dort die Aufgaben für das Menue abarbeitet.
Speicherst du in einem Register oder Flag, in welchen Menü sich die
Steuerung gerade befindet? Noch eins ist mir nicht ganz klar. Mit
welchen Sprungmarken du im Hauptprgramm dann angibst, was zum
jeweiligen Menue dann textmässig auf dem LCD erscheint?
Vielen Dank für die kurzen Infos bereits. Das hat mir schon sehr
geholfen und auch die ASM-Files

daniel

von Hannes L. (hannes)


Lesenswert?

>
Du hast ein steuermenü das den Pointer festlegt, welcher dann in ein
Menue springt und dort die Aufgaben für das Menue abarbeitet.
Speicherst du in einem Register oder Flag, in welchen Menü sich die
Steuerung gerade befindet?
<

Es gibt die Variable namens "mp" (Menüpunkt), die als Zeiger auf die
Menüroutine (über ijmp) genutzt wird, aber auch als Zeiger auf den
Menütext. Diese Variable (Register) repräsentiert immer den momentanen
Status der Menüsteuerung.

>
Noch eins ist mir nicht ganz klar. Mit
welchen Sprungmarken du im Hauptprgramm dann angibst, was zum
jeweiligen Menue dann textmässig auf dem LCD erscheint?
<

Jeder Menüpunkt-Job wird mit "rjmp tastaus" verlassen, die Routine
"tastaus" löscht alle Tastenflags (was die Menü-Job-Routinen
schlanker macht) und gibt die beiden Menütexte (obere und untere Zeile
des Displays) aus und fällt dann in die Mainloop. Schau dir mal dort
den Aufruf des Macros und in der Print-Include das Macro und die
Ausgaberoutine für indizierte Texte an, dann wird dir das klar. Da wird
nämlich auch die Variable "mp" zur Auswahl des Textes genutzt, kurz
vor den Texten (am Ende des Quelltextes) stehen dann die Tabellen
(Listen) mit den Zeigern auf die Texte, ist der Zeiger erstmal
positioniert, erfolgt die Textausgabe wie eine gewöhnliche Ausgabe
eines 0-terminierten Strings.
Diese Form der indizierten Ausgabe ist recht effizient, was Codegröße
und auch Prozessorbelastung betrifft, sie eignet sich auch gut zur
Ausgabe von Wochentagsnamen, Monatsnamen usw.. Ob es noch effizienter
geht, kann ich nicht sagen, die Routine wurde nicht "gefunden"
sondern selbst erdacht. Meinen Ansprüchen genügt sie aber (noch).

...

von Frank Z. (frankovic)


Lesenswert?

Gibt eigentlich ein "übliches" Programmierschema, welches 
Menüsteuerungen etwas abstrahiert? Mir schwebt eine Art Adjaszenzmatrix 
(Verbindung der Menüs) mit fest hinterlegten Bildschirminhalten vor.

Bisher programmiere ich das immer unelegant á la
"Wenn ich im Menü A bin und es wird der Knopf 1 gedrückt, springe ins 
Menü B" usw. Das geht bis zu einem gewissen Grad, wird dann aber 
unübersichtlich, besonders wenn auch Rück- oder Übersprünge
möglich sein sollen.

Frank

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.