2-Stellige LED-7Segment an Atmega

dg2ygq

Neues Mitglied
23. Juli 2007
239
0
0
Bielefeld
Sprachen
Hallo,

Ich möchte 2 LED-7-Segmentanzeigen an einem Atmega betreiben. Um nicht 2 * 7 Ports zu belegen, würd ich das gerne multiplexen. Um Platz zu sparen, möchte ich auch auf BCD-Treiber verzichten.
Angezeigt werden sollen die Zahlen von 1 bis 80, ohne 0 bei den einstelligen Zahlen.

Wie lässt sich das in Asm programmieren?
 
Hallo Michael,

du fasst zum Beispiel die Signale A-G und DP der beiden 7Segmentanzeigen (ich gehe mal von gem. Kat aus) zusammen und führst diese Signale über entsprechende Vorwiderstände an einen Port des AVR. Diese Pins dann als Ausgang definieren (high des Pins entspricht LED aus). An zwei beliebige Ausgangspins des Mikrocontrollers schließt du zum Beispiel BC327-40 (PNP) an, die jeweils eine Anzeige schalten. Angehängt ist ein Beispiel mit 4 7Segmentanzeigen (rechte um 180° gedreht, um °C anzeigen zu können), die Taster kannst du "wegdenken".

Verwendest du DP, benötigst du hier 10Pins, falls nicht, dann 9Pins.

Es bietet sich an, das Multiplexen durch eine Timerinterrupt-Routine erledigen zu lassen. In dem Programmbeispiel, welches ich in unserem anderen Thread angehängt habe, kann man dies in dem TaskManager erledigen.
Man definiert beispielsweise zwei SRAM-Variablen, die jeweils den 7Segmentcode der 7Segment-Stelle enthalten.
In dem Hauptprogramm konvertiert man die auszugebende Dezimalzahl in zwei BCD-Anteile, diese dann über eine Tabellenadressierung in den 7Segmentcode, der in die beiden SRAM-Variablen geschrieben wird.

Falls du mit der Umsetzung Schwierigkeiten hast, kannst du mir ja den letzten Stand der Software schicken. Ich könnte dir die entsprechenden Programmteile "einbauen".

Grüsse
Dirk
 

Anhänge

  • led_multiplex.jpg
    led_multiplex.jpg
    75,2 KB · Aufrufe: 185
Hallo Dirk,

Mit dem Taskmanager bzw Interrupt , das habe ich noch lange nicht durchschaut . Ist es egal wieviele Befehle abgearbeitet werden, also wieviel Zeit die Unterbrechung in Anspruch nimmt ?

Aber stimmt, gemeinsame Katode der Anzeige, also PNP-Transi zum schalten.

Auch vielen Dank für das Angebot den Code in mein Vorhandenes Proggy ein zubauen, aber ich will das ganze anders aufbauen :eek:

Vielleicht hast du einen "kurzen" Code, der die 2 Stelen ausgiebt, wo ich mich etwas rein vertiefen kann ? Vielleicht kann ich mit einem kleinen Codebeispiel besser nachvollziehen ?!

Danke dir schon mal im Vorraus

Michael
 
Hallo Michael!

Mit dem Taskmanager bzw Interrupt , das habe ich noch lange nicht durchschaut . Ist es egal wieviele Befehle abgearbeitet werden, also wieviel Zeit die Unterbrechung in Anspruch nimmt ?

Egal ist es zwar nicht, es kommt insgesamt darauf an, wie sehr der Mikrocontroller ausgelastet ist, bzw. wie oft zeitkritische Programmteile ausgeführt werden sollen, aber so eine gemultiplexte Ansteuerung zweier 7Segment-LED-Anzeigen nimmt nicht viel Rechenleistung in Anspruch. Mal grob geschätzt, die Multiplex-Periodendauer beträgt 20ms (50Hz) und alle 20ms benötigst du vielleicht 30 Maschinenzyklen (je 62,5ns bei 16MHz), das sind dann alle 20ms 1,875us für die Multiplex-Ausgabe. Die Auslastung des AVR durch Multiplexen der Anzeige beträgt hier lediglich ca. 0,01%!

Auch vielen Dank für das Angebot den Code in mein Vorhandenes Proggy ein zubauen, aber ich will das ganze anders aufbauen :eek:
Wenn du bei AVR bleiben möchtest, kann man TaskManager und 7Segment-Dekodierungstabellen beibehalten. Du meinst, du möchtest die Bedienung ändern und entsprechend auch einen anderen PLL verwenden, der ein serielles Interface besitzt, oder möchtest du noch etwas ändern?

Vielleicht hast du einen "kurzen" Code, der die 2 Stelen ausgiebt, wo ich mich etwas rein vertiefen kann ? Vielleicht kann ich mit einem kleinen Codebeispiel besser nachvollziehen ?!

Hier müsste ich wissen, wie die LED-Anzeigen an den Mikrocontroller angeschlossen sind und ob man das Grundgerüst des letzten Programmstands verwenden kann.

Grüße,
Dirk
 
Was möchte ich Ändern ....
Hauptsächlich vom 8515 weg, weil zu groß.

Ich denke dass ich max 6 Pins für Irgendwelche Eingaben (up - down - scan - TX usw) brauche, dazu 3Pin zum PLL, und eben 9 Pin für die Anzeige.
Der ATMega8 wäre von der Baugröße schon super....

Welche Ports ich nun wofür nehme, ist egal, denn ich habe mit basteln noch garnicht begonnen.

Das Grundgerüst ..... hmmm ja, die up/down-Geschichte sowie die Anzeige, denke ich schon dass das gleich bleibt.
Die Data-Zeilen fürs PLL werden sich ändern müssen, da ich für RX und TX unterschiedliche "Werte" benötige, also pro Kanal 2 Sätze ...
 
Hallo Michael,

ja wahrschenlich würde der ATmega8 passen, dieser hat 23 programmierbare I/Os. Bevor man die Software erstellt sollte man prüfen, ob das mit dem PLL funktioniert. Das Protokoll für den PLL ist relativ schnell geschrieben, um es zu testen, könnte man feste Werte übertragen. Die anderen Teile wie Tastaturabfrage, Displaymultiplex, 7Segment-Dekodierung, Speicherung von Werten im EEPROM usw. sind im Gegensatz dazu schon relativ aufwendig. Ich hatte heute nicht so viel Zeit, mache mir aber nochmal Gedanken wegen dem PLL.

Gruß,
Dirk
 
Ohje, Verzweifelung macht sich breit :(

Ich habe den "benutzen" Code versucht um zuschreiben. PortB hat die 2 7Segment angeschlossen, PortC Pin0 schaltet die Eineranzeige, Pin1 die Zehner.
Nun hab ich das auch dass es schön abwechselnt funktioniert (Dank einiger Pausen), aber lass ich die Pausen weg, ist von der 10er Stelle nichts mehr zu sehen.

Und dann muss das ganze ja noch irgendwie Interrupt gesteuert werden!
Auch dafür hatte ich ja schon mal einen Code bekommen, wo es um Drehgeber ging ....

Aber da blicke ich überall noch nicht durch,

Hilfeeeeee

Michael
 
Mein erster Versuch, der natürlich wieder nichts tut :(

$regfile = "m8515.dat"
$crystal = 1000000

On Int0 Int0_isr
Enable Int0
Enable Interrupts

Dim Kanal As Byte
Dim Einer As Byte
Dim Zehner As Byte

Ddra = &B11111111 'led ausgang
Ddrb = &B11111111 'led multi

Main:

Do

For Kanal = 1 To 80
Einer = Kanal Mod 10
Zehner = Kanal / 10
Waitms 100
Next Kanal

Loop

End

Int0_isr:
Portb = &B00000001
Porta = Einer
Portb = &B00000010
Porta = Zehner

Return

Segmente:
'Data &H3F , &H06 , &H5B , &H4F , &H66 , &H6D , &H7D , &H07 , &H7F , &H6F , &H6F
Data &B11000000 , &B11111001 , &B10100100 , &B10110000 , &B10011001 , &B10010010 , &B10000010 , &B11111000 , &B10000000 , &B10010000

Mir scheint als würd der Interrupt garnicht erst angesprungen ???



Ahhh jeeee, Die Tabelle wird ja in diesem Code garnicht ausgelesen . Trotz dieser Verbesserung und anderen Versuchen bekomme ich den Interrupt "wohl" nicht ans laufen ?!
 
Hallo Michael,

der INT0 scheint beim 8515 genau wie beim ATmega8 der erste externe Interrupt zu rein, richtig?

Üblicher Weise sollte man bei der Konfiguration des externen Interrupts noch konfigurieren, wie er arbeiten soll - falling, rising, change, usw.

Hierzu gibt es in BASCOM das CONFIG INT0 Kommando. Also würde ich den Interrupt wie folgt konfigurieren:

Code:
On Int0 Int0_isr
Enable Int0
Config Int0 = Falling
Enable Interrupts


Noch eine Frage zu meinem Verständnis? Zu was möchtest Du einen externen Interrupt benutzen und mit was steuerst Du den an?

Grüße,
Markus
 
Hallo zusammen!
Noch eine Frage zu meinem Verständnis? Zu was möchtest Du einen externen Interrupt benutzen und mit was steuerst Du den an?

Ich "schalte" mich mal kurz dazwischen.

Als wir noch bei Assembler waren, wollte Michael ganz am Anfang die Kanalanzeige über einen Inkrementalencoder (Digitalpoti) ändern. Ich hatte ihm da mal ein bisschen Programmcode geschrieben, genutzt wurde hier der externe INT0. Vielleicht möchte er das wieder so realisieren.

In seinem oberen Programm verbindet er die Displayausgabe mit dem INT0, zumindest lese ich das so raus, das macht hier wenig Sinn, Multiplexen der Anzeige müsste über einen TimerInt realisiert werden.

Auch wenn die untere ISR durch einen TimerInterrupt aufgerufen würde, funktioniert Multiplexen hier nicht. (Es würde nur funktionieren, wenn die Routine in einer Endlosschleife aufgerufen würde, dann hätte man aber eine Multiplexfrequenz von etwa 3MHz.)
Code:
 Int0_isr:
    Portb = &B00000001
    Porta = Einer
    Portb = &B00000010
    Porta = Zehner  ; [B][COLOR=DarkRed]PORTA wir hier sofort wieder mit Zehner überschrieben!
[/COLOR][/B]    Return
Ich würde hier an Michaels Stelle eventuell einfach einen AVR mit mehr Pins verwenden und die LED-Anzeigen statisch ansteuern, so viele Pins spart man hier nicht und das Programm wird einfacher.

Grüße
Dirk
 
Hall Dirk,

aha, ja OK!

Dann hat die Ausgabe der einer und zener in der Tat nix in der Serviceroutine für INT0 zu suche. Hätte ja sein können Michael will über einen externen Frequenzgenerator ein paar Takte anlegen und damit (von extern getrieben) die LED multiplexen.

Richtig ist auch das die zener den einer wieder überschreiben. Das muss abwechselnd geschehen. Habe aber gesehen, dass Du bereits eine saubere Lösung a la Ping-Pong als Antwort auf den anderen Beitrag
http://www.avr-praxis.de/forum/showthread.php?p=423#post423
schon formuliert hast. So würde ich das ganze auch realisieren.

Hier noch eine kleine Hilfestellung für möglich Timereinstellungen bei 50Hz für einen ATmega mit 1 Mhz:

Verwendeter Timer0 (8-bit Timer):
Prescaler 256
Timervorgabe 178
Genaueste mögliche Frequenz 50,08 Hz
Interrupt erfolgt im Abstand von 19968 µs
Verwendeter Timer1 (16-bit Timer).
Prescaler 1
Timervorgabe 45536
Genauste mögliche Frequenz 50 Hz
Interrupt erfolgt im Abstand von 20000 µs
Wenn Ihr z.B. auf 75 Hz gehen wollt wegen Interferenzen dann folgendes:

Verwendeter Timeo0 (8-bit Timer):
Prescaler 64
Timervorgabe 48
Genaueste mögliche Frequenz 75,12 Hz
Interrupt erfolgt im Abstand von 13312 µs

Verwendeter Timer1 (16-bit Timer).
Prescaler 1
Timervorgabe 52203
Genauste mögliche Frequenz 75,002 Hz
Interrupt erfolgt im Abstand von 13333 µs

Grüße,
Markus
 
Stimmt, für den Int0 fehlte eindeutig die Bedingung :eek:
Also Timer-Interrupt her.

Aber ich bekomme den Timer-Interrupt einfach nicht hin.
In meinem Versuch sollen einfach nur die LEDs an Porta und Portb eingeschaltet werden, aber der Interrupt wird garnicht erst angesprungen?
$regfile = "m8515.dat"
$crystal = 1000000

Config Timer0 = Timer , Prescale = 256
Start Timer0
Enable Timer0
Enable Interrupts
On Timer0 Timer0_isr

Dim Kanal As Byte
Dim Einer As Byte
Dim Zehner As Byte
Dim Raus As Byte
Dim Led1 As Byte
Dim Led2 As Byte

Ddra = &B11111111 'led einer - ausgang
Ddrb = &B11111111

Main:

Do

For Kanal = 1 To 80
'Gosub Leddisplay
Waitms 200
Next Kanal

Loop

End

Segmente:
Data &H3F , &H06 , &H5B , &H4F , &H66 , &H6D , &H7D , &H07 , &H7F , &H6F , &H6F

Timer0_isr:

Portb = &B00001111
Porta = &B11110000

Return

Wo liegt der Fehler dass Timer0_isr: nicht angesprochen wird?
 
Hallo Michael,

in Deiner Timer-Routine und bei der Initialisierung fehlt das Laden des Timer-Registers. Eine vollständige Umsetzung für einen 8-Bit Timer (Timer0) sieht im Rahmen wie folgt aus:

Code:
[COLOR="Blue"]$regfile = "m8515.dat"
$crystal = 1000000

Config Timer0 = Timer, Prescale = 256
On Timer0 Timer0_isr
Const Timervorgabe = 178

Enable Timer0
Enable Interrupts

Do
     ' tue nix
Loop

Timer0_isr:
     Timer0 = Timervorgabe

     ' Dein Code, was immer der auch machen soll :-)
     Portb = &B00001111
     Porta = &B11110000
Return[/COLOR]


Was Du dann in der Interrupt-Service-Routine machst, das überlasse ich erst mal Dir. Ein bissle was zum knobeln möchte ich Dir auch noch lassen.

Vielleicht gehst Du erst mal hin und läßt einfach nur zum Test der Routine eine LED toggeln damit Du siehst ob die Routine sauber funktioniert. Dann würde ich so nach & nach das Ding mit Leben füllen.

Wie gesagt, der Interrupt läuft nach obiger Konfiguration mit 50 Hz. Bedeutet ich würde immer Abwechselnd einer schreiben und beim nächsten Interrupt zener schreiben und dann wieder einer und dann wieder zener und ....
So hast Du effektiv 25 mal pro Sekunde jedes LED-Segment AN und AUS, das müsste reichen.

Viel Spass beim Weiterknobeln :)

Grüße,
Markus

PS: Vielleicht noch ein paar Anmerkungen zu Deinem Code:

1. In BASCOM läßt sich mit folgendem Statement ein gesamter Port als Ausgang definieren:
Code:
[COLOR="Blue"]Config Portb = Output[/COLOR]

2. Datendefinitionen würde ich prinzipiell immer ganz an den Ende des Codes hinter dem End einbauen. Also:
Code:
[COLOR="blue"]
bla
bla
bla
bla

End

Segmente:
Data &H3F , &H06 , &H5B , &H4F , &H66 , &H6D , &H7D , &H07 , &H7F , &H6F , &H6F[/COLOR]
 
Hallo Markus

Nun funktioniert es , naja, zumindest Ansatzweise.

Prescale musste ich auf 64 setzen, um ein halbwegs vernünftiges "Bild" zu erreichen. Läuft irgendwie sehr sehr langsam ?! Könnte natürlich auch an meiner Ausgabe liegen :confused:

Meine Ausgabe ...... ja klar, das bekomme ich "vielleicht" auch noch hin, zum probieren hab ich es erstmal so gelöst:

$regfile = "m8def.dat"
$crystal = 1000000

Config Timer0 = Timer , Prescale = 64
On Timer0 Test
Const Timervorgabe = 178

Enable Timer0
Enable Interrupts

Dim A As Byte 'fuer umschaltung einer/zehner
Dim Kanal As Byte
Dim Einer As Byte
Dim Zehner As Byte
Dim Raus As Byte
Dim Led1 As Byte 'einerstelle
Dim Led2 As Byte 'zehnerstelle

Config Portb = Output ' ziffer
Config Portc = Output ' segmentanzeige

Main:
Do

For Kanal = 1 To 80
Gosub Leddisplay
Waitms 100
Next Kanal

Loop

Leddisplay:
Einer = Kanal Mod 10
Zehner = Kanal / 10
Led1 = Lookup(einer , Segmente)

Raus = Lookup(zehner , Segmente)
If Raus = &H3F Then Raus = 0
Led2 = Raus
Return

End

Segmente:
Data &H3F , &H06 , &H5B , &H4F , &H66 , &H6D , &H7D , &H07 , &H7F , &H6F , &H6F

Test:
Timer0 = Timervorgabe
If A = 1 Then
Portc = &B00000010 'einerstelle aktiv

Portb = Led1
A = 0

Else
Portc = &B00000001 'zehnerstelle aktiv
Portb = Led2
A = 1

End If
Return

Was mich auch noch irritiert : Pinb6 und Pinb7 funktionieren nicht am ATMega8. Sind ja die Ports wo der Quarz angeschlossen wird. Muss ich da noch irgendwas Einstellen um diesen Port voll zu nutzen ?
 
Hallo Michael,

eine schnelle 100% Antwort habe ich nicht parat. Da müsste ich selber jetzt mal schnell testen, bin aber im Büro und habe anderes Zeug vor meiner Nase als meine Entwicklungsumgebung :(

Aber, das Datenblatt sagt folgendes:
[1] Depending on the clock selection fuse settings, PB6 can be used as input to the inverting Oscillator amplifier and input to the internal clock operating circuit.
[2] Depending on the clock selection fuse settings, PB7 can be used as output from the inverting Oscillator amplifier.
[3] If the Internal Calibrated RC Oscillator is used as chip clock source, PB7..6 is used as TOSC2..1 input for the Asynchronous Timer/Counter2 if the AS2 bit in ASSR is set.
[4] PB6 (XTAL1/TOSC1) and PB7(XTAL2/TOSC2) can be used as either general I/O pins or Timer Oscillator pins..

Also gehe ich aktuell davon aus, das durch entsprechende Konfiguration ich die PINs als normale IO pins verwenden kann. Aber wie gesagt das müsste ich selbst erst nochmal kurz verifizieren.

Vieleicht folgendes vorab:
- Schau Dir mal eine FUSES an, ob die richtig gesetzt sind oder ob er aktuell von einer externen Anbindung über XTAL ausgeht.
- Solange Du die Finger von den Timer-Konfigurationen läßt müsste dann PB6..7 auch als IO funktionieren.

Probiers einfach mal aus. Vielleicht gibt es noch einen anderen ATmega Profi der was zur Konfiguration sagen kann. Aus der Ferne müsste ich jetzt raten.

Grüße und ein gutes Gelingen,

Markus
 
Das mit den Fuses hatte ich mir schon gedacht, das einzigste was ich dort finde ist Reset (PC6) als I/O zu nehmen.
Dem Datenblatt nach seh ich es auch so, dass PB6 und PB7 auch als I/O funktionieren sollten.
Die Jumper auf dem STK500 (Xtal1 und Oscsel) haben keinen Einfluss auf PB6 / PB7 vom Mega8.

Ohhh, nun hab ich mal ein Haken bei "disable Reset...." gemacht, nun mag er sich garnicht mehr programmieren lassen.
Lässt sich mein Mega8 noch retten, oder war es gut dass ich gleich 3 gekauft habe ?
 
Hallo Michael,

so ein Pech aber auch :eek:

Das RSTDISBL Fuse Bit steuert die Funktion des Reset Pin. Wird es programmiert kann man den Reset Pin als normalen IO Pin verwenden.
Achtung: Wird dieses Bit programmiert kann der Controller nicht mehr über die ISP Schnittstelle erreicht werden. Das ist natürlich dumm aber halt leider so.

Habe mal gehört, dass es noch eine parallele Programmiermethode für den ATmega gibt. Habe diese aber bisher nie ausprobiert. Habe keine Infos ob ich über die parallele Programmierung nochmals an die relevante Fuse komme und den Zustand zurücksetzten kann.

Zunächst wäre ich mal froh noch zwei Andere Mega8 auf meinem Schreibtisch zu haben.:)
Such einfach mal im Internet ob es Informationen zum Thema parallele Programmierung gibt und ob das beim Mega8 anwendbar ist und ob Du damit wieder auf den "zerschossenen" Mega8 kommst.


So, nun zum Clock. Betrachten wir zunächste die ATmega-Seite:
Wie ist den die Programmierung von Deiner Fuse CKOPT? Wie ist die Programmierung der Fuses CKSEL? Durch CKOPT wird ein interner 36 pF Kondensator aktiviert welcher Dir ggf. bei IO-Betrieb Probleme machen kann.
CKSEL darf nicht 0000 sein sondern sollte 0001, 0010, 0011 oder 0100 sein.

Richten wir noch einen Blick auf das STK500. Im Installationsverzeichnis für die AVR Tools und das AVR-Studio findest Du eine ausführliche Hilfe zum STK500. Zu finden unter:
C:\Program Files\Atmel\AVR Tools\Help\STK500.chm
Hierüber findest Du auch die vollständigen Schalt- und Bestückpläne zum STK500. Lass uns da mal einen Blick reinwerfen, was für den ATmega8 im Fall PB6 und PB7 noch so alles in der Beschaltung hängt. So also:

[1] 28 DIP Sockel PB6 = PIN9 und PB7 = PIN10. Die beiden PINs sind beschriftet mit XT1 und XT2. So nun mal sehen wo die hingehen.....

[2] So wie es im Schaltplan aussieht sind die Clocks noch auf dem PortE-Connector bei PE6 und PE7 aufgelegt.

[3] Außerdem gibt es einen Jumper XT1 (JP901). Der sollte meiner Meinung nach auf jeden Fall gezogen sein wenn Du die PINs als IO-Pins verwenden möchtest. Schauen wir mal weiter ....

[4] Ich glaube Du hast es hier mit einem Sonderfall des STK500 zu tun. Beim ersten Überfliegen der Schaltpläne bin ich der Meinung, Du musst um an die XTAL-PINs von PB6 und PB7 zu kommen den Port-Header PE6 und PE7 verwenden. Schaus Dir am besten nochmal im Schaltplan vom STK500 an. Einige weitere Hinweise könnte auch noch die Dokumentation zum STK500 (kleines grünes Büchle) bieten. Eine Schlüsselstelle habeich im Anhang als Bild beigefügt.

So, das müsste erst mal als Hilfestellung reichen. Jetzt würde ich sagen du gräbst Dich da mal ein und wir werden sehen wo Du raus kommst ;)

Grüße,
Markus
 

Anhänge

  • XTAL_STK500.gif
    XTAL_STK500.gif
    3,7 KB · Aufrufe: 47
Hallo Michael!
Ohhh, nun hab ich mal ein Haken bei "disable Reset...." gemacht, nun mag er sich garnicht mehr programmieren lassen.
Lässt sich mein Mega8 noch retten, oder war es gut dass ich gleich 3 gekauft habe ?

Wenn man den Reset-Pin PC6 als IO-Pin durch Programmieren der Fuse RSTDISBL einstellt, kann man den AVR nicht mehr über SPI seriell programmieren, er läßt sich dann nur noch parallel programmieren.


Wegen den Pins PB6 und PB7 sehen ich das genauso wie Markus:

Der ATmega8 wird mit Fuse CKSEL = 0001 ausgeliefert, der AVR wird somit durch den internen Oszillator mit 1MHz getaktet. Solange CKSEL in dem Wertebereich CKSEL3...0: 0100 - 0001 eingestellt ist, ist der interne Oszillator aktiviert und die Pins PB6 und PB7 können als ganz normale IO-Pins oder als Timer-Oszillator-Pins (TOSC1, TOSC2) verwendet werden.

Grüße
Dirk

EDIT: Jetzt war Markus aber schneller ;)
 
so ein Pech aber auch
:p

Jawoll, der "Fehler" lag nicht am Mega8, sondern an der Schaltung auf dem STK 500.
Es wird tatsächlich PinB6 und PinB7 auf PortE ausgegeben :eek:
Hätt ich mal das grüne Heftchen aufmerksam gelesen ...
CKOPT stört keineswegs, ist egal ob er ein oder aus geschaltet ist.
CKSEL = 0001 (default)
Auch die Jumper XTAL1 und OSCSEL ändern nichts (zumindest wenn ich mit der internen Frequenz arbeite).


Tja, ich weiß nicht ob sich die Mühe wirklich lohnt, den "zerschossenen" Mega8 parallel zu programmieren?
Was für Vorteile hat das parallele programmieren denn noch? Wenn ich das NUR brauchen kann um diesen einen zu retten .... war nur nen guter Euro Lehrgeld!

Nun nochmal zurück zum "langsamen" Interrupt:
Prescale = 64 , um eine flackerfreie Anzeige zu bekommen.
Liegt das nun an meiner vielleicht zu aufwendigen Ausgaberoutine ?
Die Ausgabe darf natürlich auch nicht zu viel Zeit in anspruch nehmen, sonst gefährde ich doch noch die Tastenabfrage und die Datenübertragung zum PLL?
Oder "denkt" der MC während des Interrupts weiterhin mit?


Markus, Dirk:
Eure Erklärungen sind wirklich Spitze! Ihr sagt nicht nur einfach nur die Lösung oder sagt bloss was man versuchen kann / sollte, ihr vermittelt gleich eine geballte Ladung Hintergrundwissen dazu. Super gut verständlich, und leicht zu merken für die nächsten Probleme!

Ein dickes Dankeschön
Michael
 
Hallo Michael,

ich bin mir garnicht so sicher ob Dein Interrupt wirklich so langsam ist. Wenn man sich den Assambler-Code den BASCOM erzeugt mal ansieht dann macht der aus Deiner Interrupt-Routine das folgende draus:

Code:
+0000007A:   920F        PUSH    R0               Push register on stack
+0000007B:   921F        PUSH    R1               Push register on stack
+0000007C:   922F        PUSH    R2               Push register on stack
+0000007D:   923F        PUSH    R3               Push register on stack
+0000007E:   924F        PUSH    R4               Push register on stack
+0000007F:   925F        PUSH    R5               Push register on stack
+00000080:   927F        PUSH    R7               Push register on stack
+00000081:   92AF        PUSH    R10              Push register on stack
+00000082:   92BF        PUSH    R11              Push register on stack
+00000083:   930F        PUSH    R16              Push register on stack
+00000084:   931F        PUSH    R17              Push register on stack
+00000085:   932F        PUSH    R18              Push register on stack
+00000086:   933F        PUSH    R19              Push register on stack
+00000087:   934F        PUSH    R20              Push register on stack
+00000088:   935F        PUSH    R21              Push register on stack
+00000089:   936F        PUSH    R22              Push register on stack
+0000008A:   937F        PUSH    R23              Push register on stack
+0000008B:   938F        PUSH    R24              Push register on stack
+0000008C:   939F        PUSH    R25              Push register on stack
+0000008D:   93AF        PUSH    R26              Push register on stack
+0000008E:   93BF        PUSH    R27              Push register on stack
+0000008F:   93CF        PUSH    R28              Push register on stack
+00000090:   93DF        PUSH    R29              Push register on stack
+00000091:   93EF        PUSH    R30              Push register on stack
+00000092:   93FF        PUSH    R31              Push register on stack
+00000093:   B78F        IN      R24,0x3F         In from I/O location
+00000094:   938F        PUSH    R24              Push register on stack
+00000095:   EB82        LDI     R24,0xB2         Load immediate
+00000096:   93800052    STS     0x0052,R24       Store direct to data space
+00000098:   91000060    LDS     R16,0x0060       Load direct from data space
+0000009A:   3001        CPI     R16,0x01         Compare with immediate
+0000009B:   F009        BREQ    PC+0x02          Branch if equal
+0000009C:   C00A        RJMP    PC+0x000B        Relative jump
+0000009D:   E082        LDI     R24,0x02         Load immediate
+0000009E:   BB85        OUT     0x15,R24         Out to I/O location
+0000009F:   E6A5        LDI     R26,0x65         Load immediate
+000000A0:   E0B0        LDI     R27,0x00         Load immediate
+000000A1:   918C        LD      R24,X            Load indirect
+000000A2:   BB88        OUT     0x18,R24         Out to I/O location
+000000A3:   E080        LDI     R24,0x00         Load immediate
+000000A4:   93800060    STS     0x0060,R24       Store direct to data space
+000000A6:   C009        RJMP    PC+0x000A        Relative jump
+000000A7:   E081        LDI     R24,0x01         Load immediate
+000000A8:   BB85        OUT     0x15,R24         Out to I/O location
+000000A9:   E6A6        LDI     R26,0x66         Load immediate
+000000AA:   E0B0        LDI     R27,0x00         Load immediate
+000000AB:   918C        LD      R24,X            Load indirect
+000000AC:   BB88        OUT     0x18,R24         Out to I/O location
+000000AD:   E081        LDI     R24,0x01         Load immediate
+000000AE:   93800060    STS     0x0060,R24       Store direct to data space
+000000B0:   918F        POP     R24              Pop register from stack
+000000B1:   BF8F        OUT     0x3F,R24         Out to I/O location
+000000B2:   91FF        POP     R31              Pop register from stack
+000000B3:   91EF        POP     R30              Pop register from stack
+000000B4:   91DF        POP     R29              Pop register from stack
+000000B5:   91CF        POP     R28              Pop register from stack
+000000B6:   91BF        POP     R27              Pop register from stack
+000000B7:   91AF        POP     R26              Pop register from stack
+000000B8:   919F        POP     R25              Pop register from stack
+000000B9:   918F        POP     R24              Pop register from stack
+000000BA:   917F        POP     R23              Pop register from stack
+000000BB:   916F        POP     R22              Pop register from stack
+000000BC:   915F        POP     R21              Pop register from stack
+000000BD:   914F        POP     R20              Pop register from stack
+000000BE:   913F        POP     R19              Pop register from stack
+000000BF:   912F        POP     R18              Pop register from stack
+000000C0:   911F        POP     R17              Pop register from stack
+000000C1:   910F        POP     R16              Pop register from stack
+000000C2:   90BF        POP     R11              Pop register from stack
+000000C3:   90AF        POP     R10              Pop register from stack
+000000C4:   907F        POP     R7               Pop register from stack
+000000C5:   905F        POP     R5               Pop register from stack
+000000C6:   904F        POP     R4               Pop register from stack
+000000C7:   903F        POP     R3               Pop register from stack
+000000C8:   902F        POP     R2               Pop register from stack
+000000C9:   901F        POP     R1               Pop register from stack
+000000CA:   900F        POP     R0               Pop register from stack
+000000CB:   9518        RETI                     Interrupt return

Wie zu sehen ist wird die meiste Zeit im Retten (Push) der Register und im Zurücklesen (Pop) der Register in der ISR verbraten. Die eigentliche Routine zum schreiben der Daten auf den Ports ist schlank und ich sehe auch keine Möglichkeit diese zu optimieren.

In der Simulation benötigt die reine Routine nur ca. 19 Zyklen und bei 1 MHz sind das 19 µS. Dazu kommen noch die vielen POPs und PUSHs. Ich würde mal schätzen, dass die ISR in 100µsec komplett durch ist. Samit dürfte es nicht an der Abarbeitungsgeschwindigkeit liegen.

Falls Du noch Lust zum Spielen und optimieren hast dann probier mal die Option NOSAVE bei der Konfiguration des Interrupt. NOSAVE bedeutet das die Register nicht gerettet werden. Dann wirst Du sehen ob Du einen Geschwindigkeitvorteil hast.

Aber, überlegen wir doch mal folgendes:
Du hast 1 MHz Clock. Mit einem Prescaler von 64 bedeutet dies 1.000.000 / 64 = 15.625 kHz. Im Code stelst Du den Timer auf 178. Bedeutet er zählt von 178 bis 255 mit einer Taktrate von 15.625 kHz und Löst dann einen Interrupt aus. Bedeutet Du hast so alle 5 ms einen Interrupt = 200 Hz bedeutet Du baust mit 100 Hz jedes Segment auf. Da dürfte nix mehr flackern.
Mit Prescaler 256 und einem registerinhalt von 178 kommst Du auf 50 Hz bedeutet 25 Bilder pro Segment in einer Sekunde. Das kann Flackern.
Spiel einfach mal ein bissle rum und probiers aus.
Bei 200 Hz und 5 ms pro Interrupt kannst Du auf jeden Fall locker 100 µs für die ISR spendieren. Sonst passiert ja auch nicht viel im Programm so das man das durchaus verschmerzen kann.

Prinzipiell:
Während des Interrupt macht der µC nix anderes als Interrupt, außer er bekommt zwischenrein einen höher prioren Interrupt reingeballert. Dann wird die aktuelle ISR unterbrochen, die neue ISR abgearbeitet und dann kehrt der µC an die alte Stelle in der alten ISR zurück und macht dort weietr. Danach sieht es aber nicht aus denn Du hast im Code keine wieteren Interrupts aktiviert.

Ich bin überzeugt das die ISR schnell genug ist und die Kommunikation zur PLL nicht stört. Die ist noch sehr genügsam.

Aber ich selbst handhabe es in der Praxis auch so, probieren geht über studieren und ruhig mit den Werten mal rumspielen :D

Grüße,
Ma
 

Über uns

  • Makerconnect ist ein Forum, welches wir ausschließlich für einen Gedankenaustausch und als Diskussionsplattform für Interessierte bereitstellen, welche sich privat, durch das Studium oder beruflich mit Mikrocontroller- und Kleinstrechnersystemen beschäftigen wollen oder müssen ;-)
  • Dirk
  • Du bist noch kein Mitglied in unserer freundlichen Community? Werde Teil von uns und registriere dich in unserem Forum.
  •  Registriere dich

User Menu

 Kaffeezeit

  • Wir arbeiten hart daran sicherzustellen, dass unser Forum permanent online und schnell erreichbar ist, unsere Forensoftware auf dem aktuellsten Stand ist und der Server regelmäßig gewartet wird. Auch die Themen Datensicherheit und Datenschutz sind uns wichtig und hier sind wir auch ständig aktiv. Alles in allem, sorgen wir uns darum, dass alles Drumherum stimmt :-)

    Dir gefällt das Forum und unsere Arbeit und du möchtest uns unterstützen? Unterstütze uns durch deine Premium-Mitgliedschaft!
    Wir freuen uns auch über eine Spende für unsere Kaffeekasse :-)
    Vielen Dank! :ciao:


     Spende uns! (Paypal)