Forum: Mikrocontroller und Digitale Elektronik BCD Z ählen (AVR ASM)


von kinglazee (Gast)


Lesenswert?

Hallo,

ich Programmiere mit dem STK500 und einem AT90S8515, ich will einen
Port benutzen (vorerst mal Port B mit LED's) um folgendes zu tun:
Es soll bei den ersten 4 Bits ein BCD Code hochgezählt werden, die
anderen 4 Bits werden je nach einem Überlauf des BCD Codes um eins
verschoben.

Der Port soll 4 74LS2154 (4-to16-Line Decoder) ansteuern, mit den
ersten 4 Bits übermittel ich den BCD Code an alle IC's und mit den
anderen 4 möchte ich die Freigabe geben bzw. sperren für die einzelnen
IC's.

Wäre nett wenn ich dazu ein paar Tips bekommen könnte.

MFG kinglazee

von ...HanneS... (Gast)


Lesenswert?

abonniert...

von AxelR. (Gast)


Lesenswert?

Hi King, Hallo HanneS,
habe ich letztes Jahr für meine Xmas-Beleuchtung gemacht.
Nach Weihnachten fangen wieder alle an zu bauen, weil se sich über die
bunten Lichter vom Nachbarn geärgert haben (Weil die bunter leuchteten
und der Nachbar nu gar keine Ahnung von Elektronischem Zeuchs hat,
sondern alles bei Pflanzen Kölle gekauft hat)

 - zähle einfach drauf los.
 - nach jedem Step eine AND Verknüpfung mit 0x0F.Ergebins als
   Dateninformation für die 154er im Ausgaberegister weglegen, original
8Bit Zählerstand aufheben, zur nächsten berechnung
   heranziehen.

..... [ich lass es mal stehen]
AND 0xF0 . Ergebins ist 0x10, 0x20, 0x30 oder 0x40 (bis, logisch 0xF0 -
soweit kommen wir nicht) Dieses Ergebins in einem Extra Register
'SELECT_DECODER' aufheben. Die Dateiinformation und das ergebbnis der
letzen AND "Rechnung" einfach verodern. Da Du oben mit 0x0F die oberen
4Bit's gekillt hast, sind da auch keine, die jetzt stören, also einfach
'Dateninfo' OR 'SELECT_DECODER'.
Checken musste noch, ob im 'SELECT_DECODER' das Ergebins größer 0x40
ist,, dann setze es auf 0x00.
Das geht am einfachsten, in dem Du oben nicht AND 0xF0, sondern gleich
AND 0x40 rechnest, dann tauchen die
.........

original 8Bit Zählerstand NICHT überschreiben, siehe oben.
auf Bit4 Testen mit 'Zählerstand' AND 0x10. wenn wahr, dann
'SELECT_DECODER' mit 0x10 laden, sofern 'SELECT_DECODER' auf 0x80
stand. Sonst 'SELECT_DECODER' 1 nach links schieben (mal 2
nehmen).original 8Bit Zählerstand AND 0x0F. 'SELECT_DECODER' OR
'AUSGABEREGISTER' beinhaltet jetzt den Zählerstand 0 bis 15 in den
unteren Bits 0 bis 3 und gesetze Bits 4,5,6 oder 7 im oberen Nibble.

Codeschnipsel wieder nur auf Anfrage.
Ihr seht, auch ich komme manchmal durcheinander, ich hab's mal stehen
gelassen

Gruß
AxelR.

von kinglazee (Gast)


Lesenswert?

Hallo, AxelR

ich konnte deinen Vorschlag bis jetzt noch nicht wirklich
nachvollziehen.

Ich habe jetzt folgendes getan, ein register mit inc hochzählen lassen,
dieses Register direkt an port B gesendet, jetzt bräuchte ich eine
abfrage mit der ich einen Sprung mache wenn ich den Wert 0b00001111
habe, ich habe leider noch nichts mit dem Status register gemacht, kann
mir da jemand ne kurze erläuterung dazu geben?

besten dank im voraus
MFG kinglazee

von ...HanneS... (Gast)


Lesenswert?

Ja...
- Datenblatt,
- Istruction-set
- AVR-Studio-Help

...

von AxelR. (Gast)


Lesenswert?

Hi,

naja, sagte ich doch schon:
auf Bit4 Testen mit 'Zählerstand' AND 0x10. oder eben 0b00010000.
Kann man mit einer AND verknüpfung machen. Geht sicher auch anders...
Du musst im Prinzip die oberen 4Bits von deinem Register, welches
hochzählt, isoliert weiter verarbeiten und die neuen Informationen,
also die oberen 4Bit wieder zum 'Zählerstand' zurückschreiben. Die
oberen 'neuen' 4Bit musst du also separat berechnen und mit OR dem
'Zählerstand' wieder hinzufügen.

Gruß
Axel

von kinglazee (Gast)


Lesenswert?

Also mit dem Befehl OR bin ich nicht weit gekommen, hatte irgendwie
keinen erfolg damit, hab ich das register r20 den wert 0b00010000
geladen, dann mit dem register "zahler" (für zähler) folgendermaßen
oder verknüpft:
OR zahler, r20
Aber bit 4 wurde einfach nicht gesetzt, habs jetzt leider mit cbi
direkt über den port machen müssen.

hier mein Programm: (wenn euch dinge einfallen alles en bisl einfacher
zu machen, dann schreibt sie ruhig, ich bin sehr dankbar für jede
Hilfe!

.nolist
.include "C:\Programme\Atmel\AVR
Tools\AvrAssembler\Appnotes\8515def.inc"
.list

.def tmp=r16     ;Universalregister deklarieren
.def leds=r17
.def zahler=r19

  rjmp reset      ;Reset Handler
  rjmp interrupt0    ;IRQ0 Handler
  reti        ;IRQ1 Handler
  reti         ;Timer1 Capture Handler
  reti        ;Timer1 compare Handler
  reti         ;Timer1 Overflow Handler
  reti        ;Timer0 Overflow Handler
  reti        ;SPI Transfer Complete Handler
  reti         ;UART RX Complete Handler : RXCIE
  reti        ;UDR Empty Handler
  reti        ;UART TX Complete Handler
  reti        ;ADC Conversion Complete Interrupt Handler
  reti        ;EEPROM Ready Handler
  reti        ;Analog Comparator Handler

;======================================================================= 
=====
; Initialisierung
;======================================================================= 
=====

  reset:

    ;STACK-Initialisieren
    ldi tmp, LOW(RAMEND)  ;LOW-Byte der obersten-Adresse
    out SPL, tmp
    ldi tmp, HIGH(RAMEND)  ;HIGH-Byte der obersten-Adresse
    out SPH, tmp

    ldi tmp, 0xFF    ;8 Einsen in Register "tmp"
    out DDRB, tmp    ;an Datenrichtungsregister von Port B

    ldi leds, 0b11111111
    out PORTB, leds

    ldi tmp, 1<<INT0    ; 0100 0000
    out GIMSK, tmp
    ldi tmp, 0b00000010
    out MCUCR, tmp

    ldi zahler, 0b00000000


    sei

;======================================================================= 
=====
; Hauptprogramm
;======================================================================= 
=====

  main:

      rjmp main

  interrupt0:              ;wird aufgerufen wenn SW2 gedrückt

    ldi zahler, 0b00000000      ;Zähler auf null setzen

  _sektor_x1:
    inc zahler            ;Zähler +1
    mov leds, zahler        ;Zähler in Leds kopieren
    com leds            ;Register Leds umdrehen
    out portb, leds          ;An Port B ausgeben
    cbi portb, 4          ;Bit 4 für freigabe IC 1
    com leds            ;Register Leds wieder umdrehen
    rcall wait            ;warteschleife
  cpi zahler, 15            ;prüfen ob zähler schon auf 15 
(0b0000.11111)
  brne _sektor_x1            ;wenn nicht auf 15, dann springe auf 
_sektor_x1

    ldi zahler, 0b00000000

  _sektor_x2:
    inc zahler
    mov leds, zahler
    com leds
    out portb, leds
    cbi portb, 5
    com leds
    rcall wait
  cpi zahler, 15
  brne _sektor_x2

    ldi zahler, 0b00000000

  _sektor_x3:
    inc zahler
    mov leds, zahler
    com leds
    out portb, leds
    cbi portb, 6
    com leds
    rcall wait
  cpi zahler, 15
  brne _sektor_x3

    ldi zahler, 0b00000000

  _sektor_x4:
    inc zahler
    mov leds, zahler
    com leds
    out portb, leds
    cbi portb, 7
    com leds
    rcall wait
  cpi zahler, 15
  brne _sektor_x4

    ldi leds, 0b11111111
    out portb, leds

  reti

von ...HanneS... (Gast)


Lesenswert?

Hi...

---->8----
ich Programmiere mit dem STK500 und einem AT90S8515, ich will einen
Port benutzen (vorerst mal Port B mit LED's) um folgendes zu tun:
Es soll bei den ersten 4 Bits ein BCD Code hochgezählt werden, die
anderen 4 Bits werden je nach einem Überlauf des BCD Codes um eins
verschoben.
----8<----

BCD oder Binär??
BCD zählt (wie der Name schon sagt) nämlich nur von 0 bis 9.

---->8----
Der Port soll 4 74LS2154 (4-to16-Line Decoder) ansteuern, mit den
ersten 4 Bits übermittel ich den BCD Code an alle IC's und mit den
anderen 4 möchte ich die Freigabe geben bzw. sperren für die einzelnen
IC's
----8<----

Da du einen (eigentlich 4) "1 aus 16-Decoder" dranhängen willst (um
ein Lauflicht zu realisieren), solltest du lieber bis 15 zählen, also
nicht BCD sondern binär.

Du nimmst als Taktquelle zum Zählen den Int0.
Und da hängt sicherlich ein Taster dran.
Taster haben die unangenehme Eigenschaft, mechanisch zu prellen, was
dafür sorgt, dass dein Int etliche male hintereinander ausgelöst wird,
bis der Taster zur Ruhe kommt. Diese Methode geht also nicht.
Mechanische Schalter und Taster fragt man grundsätzlich nicht per INT
ab sondern mit einer Entprellroutine (Codesammlung -> suchen).

Ein Lösungsansatz:
- Nimm getrennte Register für Zählen und Schieben der Select-
  Leitungen und ein drittes zum Zusammenfassen und Ausgeben.
- Zähle das Zählregister hoch (oder runter) (inc zahl)
- blende die oberen 4 Bits aus (andi zahl,15)
- vergleiche zahl mit 0 oder 15 (je nach Zählrichtung)
  (cpi zahl,0)
- überspringe die nächsten Programmzeilen (mit Label), wenn
  Vergleich nicht stimmt (brne label1)
- schiebe den Inhalt des Registers zum Schieben der Select-Leitungen
  nach links oder rechts (asl schieb / asr schieb), je nachdem, wie
  die Aktivierungsreihenfolge sein soll
- falls du nach rechts schiebst, blende die 4 unteren Bits aus
  (andi schieb,$f0), denn wir nutzen ja nur die oberen 4 Bit
- überspringe (zum Label), wenn schieb dabei nicht leer wird
  (brne label1)
- setze Schieb auf Startwert (ldi schieb,16 / ldi schieb,128)
  kopiere schieb zum Ausgaberegister (mov ausgeb,schieb), eines der
  4 oberen Bits ist nun gesetzt
- solltest du zum Aktivieren (CipSelect) eine Null brauchen und keine
  Eins, dann invertiere es und blende die unteren Bits wieder aus
  (com ausgeb , andi ausgeb,$f0) nun ist eines der oberen Bits
  gelöscht und alle unteren Bits auch
- kopiere die unteren Bits von zahl zum Ausgaberegister, ohne dabei
  die oberen 4 Bits zu verändern (or ausgeb,zahl)
- gib das Register an den Port aus (out portx,ausgeb)
- warte auf das nächste Zählereignis und beginne von vorn...
  (rjmp warteauftastendruck)

Falls du das in eine Interrupt-Routine einbauen willst, dann beherzige
die 3 goldenen Regeln der Interruptprogrammierung:

http://www.mikrocontroller.net/forum/read-1-130140.html#130537

...

von AxelR. (Gast)


Lesenswert?

Hallo "König der faulen" (Kinglazee),

na, ich werd' mal nich so sein...
ich habe dein Code mal ins AVR Studio übernommen. Die Wait Routine habe
ich mal aussen vor gelassen, klar.
Wenn Du den Wert für die LED's ausgibts, ist dein Bit4,5,6 oder7
natürlich wieder "weg", weil jedesmal bei Ausgabe der LED's die
oberen 4 Bit's mit überschrieben werden. Danz ganze direkt in der
Int0-routine zu machen, ist jetz ein anderes Thema...
Ich geh' noch mal drüber.
Axel

toller nick ;-))

von AxelR. (Gast)


Lesenswert?

bei jedem Tastendruck (Int0 evtl. noch auf fallende oder steigende
Flanke einstellen) wird jetzt der zählerstand erhöht und abhängich(?)
von den oberen 4 Bits die 154 angesteuert. Ich habe das grob im
Simulator überflogen.
Die Zeitschleife zum hochzählen im Interrupt, das lass mal sein. Kannst
Du später im Timerinterrupt machen. Setz mal INT0 auf fallende Flanke
und drück den Taster, dann müssten die LED's ancheinander angehen (Vom
Tastenprellen mal abgesehen). LED's habe ich nicht "umgedreht", war
mir im Simlulator zuunübersichtlich.

.nolist
.include "C:\Programme\Atmel\AVR
Tools\AvrAssembler\Appnotes\8515def.inc"
.list

.def tmp=r16     ;Universalregister deklarieren
.def leds=r17
.def zahler=r19
.def shift_cnt=r20
  rjmp reset      ;Reset Handler
  rjmp interrupt0    ;IRQ0 Handler
  reti        ;IRQ1 Handler
  reti         ;Timer1 Capture Handler
  reti        ;Timer1 compare Handler
  reti         ;Timer1 Overflow Handler
  reti        ;Timer0 Overflow Handler
  reti        ;SPI Transfer Complete Handler
  reti         ;UART RX Complete Handler : RXCIE
  reti        ;UDR Empty Handler
  reti        ;UART TX Complete Handler
  reti        ;ADC Conversion Complete Interrupt Handler
  reti        ;EEPROM Ready Handler
  reti        ;Analog Comparator Handler

;======================================================================= 
=====
; Initialisierung
;======================================================================= 
=====

  reset:

    ;STACK-Initialisieren
    ldi tmp, LOW(RAMEND)  ;LOW-Byte der obersten-Adresse
    out SPL, tmp
    ldi tmp, HIGH(RAMEND)  ;HIGH-Byte der obersten-Adresse
    out SPH, tmp

    ldi tmp, 0xFF    ;8 Einsen in Register "tmp"
    out DDRB, tmp    ;an Datenrichtungsregister von Port B

    ldi leds, 0;0b11111111
    out PORTB, leds

    ldi tmp, 1<<INT0    ; 0100 0000
    out GIMSK, tmp
    ldi tmp, 0b00000010
    out MCUCR, tmp

    ldi zahler, 16


    sei

;======================================================================= 
=====
; Hauptprogramm
;======================================================================= 
=====

  main:

      rjmp main

  interrupt0:              ;wird aufgerufen wenn SW2 gedrückt

;   ldi zahler, 0b00000000      ;Zähler auf null setzen


                 ;Zähler +1
    mov leds, zahler        ;Zähler in Leds kopieren
    mov shift_cnt,zahler
  andi leds,15      ;nur die untersten 4Bits
  andi shift_cnt,240    ;bit 7,6,5 und 4 isolieren
  cpi shift_cnt,16    ;1?
  breq firstchip
  cpi shift_cnt,32    ;2?
  breq second_chip
  cpi shift_cnt,48    ;3?
  breq third_chip
  cpi shift_cnt,64    ;4?
  breq fourth_chip
firstchip:
  clr shift_cnt
  ldi shift_cnt,16
  or leds, shift_cnt
  ;com leds
  out portb,leds
  inc zahler
  reti
second_chip:
  clr shift_cnt
  ldi shift_cnt,32
  or  leds,shift_cnt
  ;com leds
  out portb,leds
  inc zahler
  reti
third_chip:
  clr shift_cnt
  ldi shift_cnt,64
  or  leds,shift_cnt
  ;com leds
  out portb, leds
  inc zahler
  reti
fourth_chip:
  clr shift_cnt
  ldi shift_cnt,128
  or  leds,shift_cnt
  ;com leds
  out portb, leds
  inc zahler
  reti

von kinglazee (Gast)


Lesenswert?

@...HanneS...:
Mein Code zählt Binär und soll als BCD am Port am unteren halben Byte
anstehen.   "cpi zahler, 15" - Wie man sehen kann zählt er bis 15,
dann erst gehts zum nächsten IC.

Zum Thema Entprellen, beim Tastendruck wird einmal komplett
durchgezählt, mit dem Porgramm sollen nachher 64 LED's angesteuert
werden die bei jedem Takt am Eingang einmal durchlaufen sollen.
Also fällt die Entprellroutine schonmal komplett weg!

@AxelR.:
----------
Die Zeitschleife zum hochzählen im Interrupt, das lass mal sein.
Kannst
Du später im Timerinterrupt machen.
----------

Wie meinst du das genau? Kannst du mir das mal beschreiben oder
Erläutern?

P.S. König der faulen steht im Bezug aufs Coden, denn ich bin manchmal
einfach zu faul um soviel Code hinzuschreiben und desshalb überlege ich
mir lieber wie ich mir das ganze ersparen kann mit Routinen oder
Makros.


@:
Das Programm läuft mit dem geposteten Code genau wie ich es haben will
von den Ausgangssignalen her, ich wollte nur wissen ob ihr
kleinigkeiten evtl. anders machen würdet um so noch ein paar Wörter zu
sparen.

von ...HanneS... (Gast)


Lesenswert?

Aha, du zählst also binär und willst BCD ausgeben???

BCD heißt doch aber Binär Codierter Dezimalcode und reicht nur von 0
bis 9. Fallst du das nicht willst, dann nenn' es nicht BCD.

Wenn du meinst, Taster ohne Entprellen mittels Ext-Int. abfragen zu
können, dann mach...

Timer-Interrupt... Axel meint sicherlich, du solltest einen Timer
einrichten, der in regelmäßigen Abständen einen Interrupt auslöst. In
dieser ISR wird dann der Zähler hochgezählt. So würde ich das auch
machen, wobei ich gleich die gesamte Zähl- Schieberei und Ausgabe mit
in die ISR packen würde.

Du wolltest wissen was wir anders machen würden, um etwas Code
einzusparen?
Das habe ich dir weiter oben schon geschrieben. Schau dir die
Erklärungen und die zugehörigen Codezeilen an und versuche sie
nachzuvollziehen. Das kann so wie es ist in die ISR eines Timers und
läuft. Macht genau das, was du vorgegeben hast, allerdings mit rund 10%
deiner Codemenge.

Ich habe den Eindruck, du hast das entweder nicht verstanden oder
garnicht erst gelesen. Daher denke ich auch, dass sich
"Faulpelzkönig" eher auf das Lesen (auch von Datenblättern) bezieht
als auf das Einsparen von Code.

...

von kinglazee (Gast)


Lesenswert?

@...HanneS...:
Yop, alles klar!

von AxelR. (Gast)


Angehängte Dateien:

Lesenswert?

Hi,
da hassu!
Bei fallender Flanke am Taster wird der Timer0 gestartet und die 64 LED
werden der Reihe nach eingeschaltet. Danach wird Timer0 wieder
angehalten und die Ausgabe gelöscht. Man muss neu drücken.
Das SREG habe ich nicht gesichert!
Die Interruptvectortabelle scheint nicht vom 8515 zu sein. nochmal
kontrollieren.
Kann sein, dass das Lauflicht mal so von alleine losläuft, da der
Taster nicht entprellt ist...
Gruß
Axel

von kinglazee (Gast)


Lesenswert?

@AxelR.:
Mit Interruptvectortabelle meinst du doch bestimmt die Liste am Anfang
des Programms, in der die vielen RETI stehen? Tut mir echt Leid, ich
hab mit dem ganzen noch nicht so recht den Umgang gefunden, aber Danke
dass du mir dabei hilfst! Werde mir mal eine Vectortabelle zu meinem
8515 suchen.

-------------
Kann sein, dass das Lauflicht mal so von alleine losläuft, da der
Taster nicht entprellt ist...
-------------
Also bis jetzt hatte ich mein STK500 schon stundenlang eingeschaltet,
aber es ist noch nichts einfach so losgelaufen, es scheint wirklich so
als wäre der Taster gut entprellt, oder es Funktioniert über den
Interrupt so gut, ich weiß es noch nicht genau!


Das Programm werde ich nachher noch Testen und schauen ob alles so ist
wie ich es wollte, und dann werd ich mir ansehen was genau getan
wurde!

Recht Herzlichen Dank!
MFG kinglazee

von AxelR. (Gast)


Lesenswert?

nebenbei, ein 47HC138 hätte es auch getan...

von AxelR. (Gast)


Lesenswert?

uuuund?

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.