Forum: Projekte & Code Atmega8 GrundlagenQuellcode in ASM


von chris (Gast)



Lesenswert?

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

von Kai S. (kai1986)


Lesenswert?

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 chris (Gast)


Lesenswert?

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.

von Spess53 (Gast)


Lesenswert?

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

von Kai S. (kai1986)


Lesenswert?

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

von chris (Gast)


Lesenswert?

@ 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.

von Esoteriker (Gast)


Lesenswert?

Das war chris seine Version ...
Das war Kai seine Version ....

und jetzt definiert mir bitte mal Grundlagen :

von µC Programmierer (Gast)


Lesenswert?

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.

von Spess53 (Gast)


Lesenswert?

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

von Paul (Gast)


Lesenswert?

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.

von Esoteriker (Gast)


Lesenswert?

@paul
Du solltest besser an suizid denken und mein geni.. geht dich echt 
nichts an.

von chris (Gast)


Lesenswert?

@ µ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.

von Karl H. (kbuchegg)


Lesenswert?


von Thomas (Gast)


Lesenswert?

Ein Moderator liest hier mit und lässt den Suizid-Kommentar einfach 
stehen.
Ich bin fassungslos.

von µC Programmierer (Gast)


Lesenswert?

chris schrieb:
> Leider meinen einige sich äussern zu müssen ...

du zum Beispiel, leider.

von Karl H. (kbuchegg)


Lesenswert?

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.

von Peter D. (peda)


Lesenswert?

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.

von Peter D. (peda)


Lesenswert?

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.

von Bernhard S. (bernhard)


Lesenswert?

... 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

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.