Moin Moin
hier stelle ich ein paar Routinen rein, mit denen es sich hoffentlich
leicht arbeiten lässt.
SW AVRStudio4.19 Build 716
HW STK500
Sprache Assembler
Die zip enthält Grundlagenquellcode für folgende Peripherie des Atmega8
ADC
AnalogComparator
EEprom
Externe Interrupts
UART um rs232 aufzubauen
TimerCounter T0-T2
Die origin.asm ist die Hauptdatei mit der ich simuliert und gestest
habe.
3 Peripheriegruppen fehlen
SPI
2Wire
SPM
Vielleicht finden sich weitere Leute die es auf andere AVRs mitaufbohren
Gruß Chris
Hallo,
schön, das du deinen Code mit uns teilst, allerdings ist mir gleich
aufgefallen, das du in keiner Funktion auch nur irgend ein Register
sicherst/wiederherstellt, was nach meinem Verständnis in grundlegendes
Vorgehen beim Programmieren von Funktionen in ASM sein sollte. Was ist
der Grund dafür?
Gruß Kai
von welchen registern sprichst du denn genau ?
weil in den initsätzen ists egal ob ichs sicher oder net und die
wichtigen daten sind alle auf dem ram abgelegt und somit sind die
register wieder frei wenn das unterprogramm verlassen wird.
siehe:
.equ ocr2s = $0063 ;für T2
.equ ocra1h = $0064 ;;;;;
.equ ocra1l = $0065 ;;;;;;;; für T1 A channel
.equ ocrb1h = $0066 ;;;;;
.equ ocrb1l = $0067 ;;;;;;;; für T1 B channel
.equ icr1xh = $0068 ;;;;;
.equ icr1xl = $0069 ;;;;;;;; für T1 ICR
.equ hadc = $006a ;adc
.equ ladc = $006b ;adc
.equ eep_adrh= $006c ;eeprom
.equ eep_adrl= $006d ;eeprom
z.B.: EEPROM
1. Anwender ermittelt daten sollten dann im temp0 stehen
2. eeprom_write/read temp0>>eeprom/ eeprom>>temp0
3. Adresse (eep_adrh:eep_adrl) vorbereiten für die nächsten daten
4. springe zu 1
und damit liegen die wichtigen sachen auf dem Ram ausser temp0 aber da
weiß ich doch nicht was der nutzer wie machen möchte, brauch er nur
1byte oder nbytes und die entsprechende schleife schreiben sollte dann
nun auch nicht das problem sein dort könnte man ja denn die Pointer
nutzen die der AVR hat siehe routine SRAM.
Hi
>.equ ocr2s = $0063 ;für T2>.equ ocra1h = $0064 ;;;;;>.equ ocra1l = $0065 ;;;;;;;; für T1 A channel>....
Das macht man nicht so. Laß den Assembler sich selbst um die Adressen
kümmern. Also:
1
ocr2s: .byte 1
2
ocra1h: .byte 1
3
ocra1l: .byte 1
4
...
Oder z.B.:
1
...
2
adc_value : .byte 2
3
4
// dann so einlesen:
5
6
in temp0,adcl
7
sts adc_value,temp0
8
in temp1,adch
9
sts adc_value+1,temp1
Lies dir mal in der Hilfe zum Assembler
AVR Assembler->User's Guide->Directives
durch.
Bei den Namen der Interruptvektoren würde ich mich an das Datenblatt
halten.
MfG Spess
chris schrieb:> die> wichtigen daten sind alle auf dem ram abgelegt und somit sind die> register wieder frei wenn das unterprogramm verlassen wird.
Ja, das ist klar, aber was ist, wenn der Nutzer z.b. R17 im
Hauptprogramm auch benutzt da Daten drin stehen hat und jetzt die Daten
von R16 (temp0) in den EEPROM schreiben möchte. Dann ruft er die die
Routine EEPROM_write: auf, legt damit die Daten in den EEPROM, verliert
dabei aber die Daten, die ursprünglich in R17 gestanden haben, da das
Unterprogramm die Daten überschrieben hat und seinen nicht mehr
benötigten Wert darin stehen lässt.
Hier der Ausschnitt aus deinem Code:
1
EEPROM_write:
2
sbic EECR,EEWE ;** falls eewe im eecr noch gesetzt
3
rjmp EEPROM_write ;** spring zu zurück
4
lds r18,eep_adrh
5
out EEARH, r18 ;highbyte der adr
6
lds r17,eep_adrl
7
out EEARL, r17 ;lowbyte der adr
8
out EEDR,r16 ;zu speichernder wert
9
sbi EECR,EEMWE ;sperrt eeprom master write enable
10
sbi EECR,EEWE ;setze EEWE um eeprom zu schreiben
11
ret
Schöner und sicherer wäre es, wenn das Unterprogramm die verwendeten
Register zu beginn sichern würde und am Ende wiederherstellen.
Einfach so:
1
EEPROM_write:
2
push r17
3
in r17, SREG
4
push r17
5
push r18
6
7
sbic EECR,EEWE ;** falls eewe im eecr noch gesetzt
8
rjmp EEPROM_write ;** spring zu zurück
9
lds r18,eep_adrh
10
out EEARH, r18 ;highbyte der adr
11
lds r17,eep_adrl
12
out EEARL, r17 ;lowbyte der adr
13
out EEDR,r16 ;zu speichernder wert
14
sbi EECR,EEMWE ;sperrt eeprom master write enable
15
sbi EECR,EEWE ;setze EEWE um eeprom zu schreiben
16
17
pop r18
18
pop r17
19
out SREG, r17
20
pop r17
21
22
ret
Damit wird keines der Register für das Hauptprogramm verändert, sprich
es macht für die Register keinen Unterschied, ob das Unterprogramm
aufgerufen wurde, oder nicht.
Weiter könnte man auch noch den Parameter über den Stack übergeben, dann
müsste er nicht in einem speziellen Register stehen, das der
Programmierer kennen muss.
Gruß Kai
@ spess53
jo bin mit diesen deklarationen so aufgewachsen und wenn man das im
Studio mal durchsimuliert findet man die dinger im ram einfach schneller
weil man sich einmal bewusst mit der zuordnung befassen muss.
finde ist geschmackssache weil das ziel das gleiche bleibt.
@Kai S. (kai1986)
ich persönlich progge so das die register verworfen werden können bevor
sie in das U-prog eintreten.
Sieht für mich alles etwas nach:
"Ich habe gerade mal was gelernt, nun bin ich Profi und will Forenguru
werden"
aus.
Sorry, aber diese Routinen gibt's 1000mal besser geproggt und/oder
stehen in jedem App Note.
Code, den die Welt nicht braucht - aber Hauptsache, das Wort "aufbohren"
taucht auf.
Amen.
Hi
>jo bin mit diesen deklarationen so aufgewachsen und wenn man das im>Studio mal durchsimuliert findet man die dinger im ram einfach schneller>weil man sich einmal bewusst mit der zuordnung befassen muss.>finde ist geschmackssache weil das ziel das gleiche bleibt.
Hört sich eher nach nach dem Motto :'Warum einfach wenn es umständlich
geht' an. Es gibt im Debugger die Watch-Liste. Da brauchst du nur das
Label eintragen und der Inhalt wird angezeigt.
Ehrlich gesagt, würde ich dein Code keinem Anfänger empfehlen.
MfG Spess
Esoteriker schrieb:> Das war chris seine Version ...> Das war Kai seine Version ....
Der Dativ isch dem Genetif sei dod!
chris schrieb:> ich persönlich progge so das die register verworfen werden können bevor> sie in das U-prog eintreten.
Wie soll das aussehen? Wird dann alles auf den Stack gepusht, bevor Du
das Unterprogramm aufrust? Wie umständlich.
Es hat sich nicht ohne Grund durchgesetzt, alle nötigen Register beim
Unterprogramm aufruf zu retten und nach der Abarbeitung diese wieder
herzustellen. Ich glaube auch nicht, dass Du bei komplexeren Geschichten
den Überblick behalten kannst. Aber das wirst Du sicher noch lernen,
wenn Du über die Phase des APP-Notes-Abtippen gekommen bist.
@ µC Programmierer
lag die banane schon wieder vor dem käfig?
>> Sorry, aber diese Routinen gibt's 1000mal besser geproggt und/oder>> stehen in jedem App Note.
na siehste also kopierste auch nur 1:1 und bekommst es bestimmt 1000mal
besser hin... denn wie will man es auch anders machen sind ja appnotes.
@ Paul
Also über meine komplexen schaltungen kann nur sagen läuft und läuft und
läuft...... der Grund ist eigentlich ganz einfach nach etwas längerer
pause hab ich meine projekte mal durchgeschaut und gewisse sachen kommen
ja immer wieder vor, somit dachte ich mir schreibste dir mal selber
entsprechenden quellcode zu der entsprechenden peripherie (gibs ja auch
für hochsprachen)
dabei ist das rausgekommen. Das dass alles absolut nicht perfekt ist,
ist mir wohl bewusst.
@ Esotoriker
>> Das war chris seine Version ...>> Das war Kai seine Version ....
Esotorikerversion lautet doch gleich?
@datenschrott
Leider meinen einige sich äussern zu müssen obwohl die baudrate falsch
eingestellt ist. Die einzigen die sauber argumentieren sind spess und
kai.
vielleicht kanns trotzdem einer gebrauchen.
Thomas schrieb:> Ein Moderator liest hier mit und lässt den Suizid-Kommentar einfach> stehen.> Ich bin fassungslos.
Ich denke, er spricht für sich. Die eigentliche Frage ist: soll sowas in
der Codesammlung sein oder nicht?
Auf der anderen Seite hat jeder das Recht dazu, hier etwas zu
veröffentlichen.
Paul schrieb:> Es hat sich nicht ohne Grund durchgesetzt, alle nötigen Register beim> Unterprogramm aufruf zu retten und nach der Abarbeitung diese wieder> herzustellen.
Nö, hat sich nicht durchgesetzt.
Man definiert sich einige Scratchpad-Register, die zerstört werden
dürfen.
C-Compiler wie der AVR-GCC machen das auch.
Spart ne Menge Code ein und macht den Code übersichtlicher, als unnötige
Push/Pop-Orgien.
Auch nimmt man die Scratchpadregister für die Parameterübergabe. Oft
braucht der Aufrufer die Argumente nicht mehr nach dem Aufruf, wozu also
unnütz wiederherstellen. Z.B. ich übergebe ein Byte an die
UART-Senderoutine, danach ist mir das Byte schnuppe.
P.S.:
Oder Funktionen liefern Werte, dann muß man Register zerstören. Und dann
nimmt man praktischer Weise solche Register, die man als zerstörbar
vereinbart hat.
Ansonsten müßte der Aufrufer erst umständlich einen Pointer übergeben,
wo das Ergebnis hingespeichert werden soll, nur damit ja kein Register
seinen Wert ändert.
... zerstörbare Regeister ...
Der Begriff gefällt mir :-)
Gern nutze ich dieses Verfahren bei meinen Projekten:
.def ERGEBNIS = R10
.def NULL = R12 "unzerstörbar"
.def EINS = R14 "unzerstörbar"
.def VOLL = R15 "unzerstörbar"
.def temp = R16 "zerstörbar"
.def temp1 = R17 "zerstörbar"
.def temp2 = R18 "zerstörbar"
.def temp3 = R19 "zerstörbar"
.def temp4 = R20 "zerstörbar"
.def temp5 = R21 "zerstörbar"
.def temp6 = R22 "zerstörbar"
.def temp7 = R23 "zerstörbar"
.def temp8 = R24 "zerstörbar"
.def temp9 = R25 "zerstörbar"
Ich persönlich arbeite gern mit dem "Baukastenprinzip",
Beispiel :
"AUSGABE_ZAHL_5_STELLEN_FORMATIERT_TEMP1_2"
"AUSGABE_ZAHL_10_STELLEN_FORMATIERT_TEMP1_4"
im Projekt:
Beitrag "Spektrumanalyser Frequenzspektrometer Eigenbau bis 1MHz"
Die Parameter werden in temp1+temp... übergeben, die Registerinhalte
sind natürlich anschließend zerstört.
Das "Baukastenprinzip" hat den Vorteil, dass die Routinen (meist ohne
große Anpassung)auch für andere Projekte verwendet werden können.
Neuerdings schreibe ich gerade bei Ausgangsfunktionen die Registernamen
mit dazu.
Beispiel:
"EEPROM_READ_PLUS_TEMP1", hier sieht man sofort, in welchem Register
sich der Output befindet. Erspart das Nachschauen in der Routine.
Nachteil des "Baukastenprinzipes":
Der Programmcode benötigt mehr Platz und mehr Takte.
Bernhard