Hallo an alle,
ich bin recht neu auf dem Gebiet mit den Atmel Controller.
Ich habe vorher in der Schule mit Infinion Controller programmiert, was
ich sehr einfach fand. Aber aller Anfang ist schwer.
Manchmal finde ich es aber schon umständlich, z.B. dass man den
Stackpointer inizialisieren muß.
Ich arbeite meine Unterlagen mit Programmübungen durch. Aber manchmal
weiß ich nicht weiter, da ich den vergleichbaren Befehl für den Atmel
nicht finde.
Wie folgendes:
DJNZ = decrement and jump if not zero (gibt es sowas bei Atmel?)
Ich nehme diesen Befehl gerne für Zeitschleifen her. Hab ihn dann mit
dem Atmel so realisiert:
loop: dec r16
brne loop
.
.
=========================================
Jetzt mein Hauptproblem:
Ich will Logikschaltungen wie bei einer SPS realisieren. Mit dem
Infinion ging das ganz einfach:
Sagen wir ich habe drei Eingabe Pins (0, 1, 2 von Portd) und einen als
Ausgabe (Bit 7 Portb).
______
0------= = ______
= UND =-----= =
1------=________= = ODER =
= =negiert----Bit7
2--------------negiert=________=
MOV C, PORTD 0 ;bei Infinion ist es normalerweise P1.0
ANL C, PORTD 1 ;UND Verknüpfung 0 und 1 Ergebnis = CarryBit
ORL C, /PORTD 2 ;ODER Verknüpfung, Portd 2 ist negiert
CPL C ;Compliment CarryBit,
MOV PORTB 7, C ;CarryBit auf Ausgang Bit 7 von Portb
Frage wie kann ich genauso einfach dieses mit dem Atmel 8515
realisieren.
Im Tutorial unter Tastern habe ich schon was gefunden, aber das ist mehr
als umständlich. Vorallem weil es vielleicht bei diesem Beispiel gerade
noch überschaubar ist, aber wenn ich dieses um 5 Gatter erweitere kenn
sich keiner mehr aus was überhaupt abgefragt wird, UND verknüpft wird
usw....
Wie kann man einzelne Bits beim Atmel abfragen und das CarryBit
complimentieren? Oder kann man da nur mit Registern arbeiten?
Vielen Dank im vorraus!
Gruß
Dirk
Dirk wrote:
>> Wie kann man einzelne Bits beim Atmel abfragen
Du meinst von einem Port?
Mittels cbis und sbis, aber das hilft dir nicht viel.
AVR sind Risc Prozessoren, für fast alles muss der entsprechende Wert in
einem Register vorliegen.
Ok verstehe, muß also immer alles als Registerwerte angeben. Damit kann
ich leben.
Kann mir vielleicht bitte jemand das obige Programm so umschreiben, dass
es in einem AVR funktioniert?
Komme einfach nicht drauf. Wollte es auch immer mit der Carry Abfrage
machen, aber irgendwie checke ich es nicht.
Wäre wirklich nett...
RISC-Prozessoren in ASM zu programmieren, ist nicht der reine Genuß. Die
sind eigentlich dafür gedacht, in einer höheren Programmiersprache
gefüttert zu werden.
Den Assembler gibts nur so dazu.
Warum arbeitest du nicht einfach mal das (auf dieser Webseite
angebotene) Tutorial durch? Danach dürften sich deine Fragen von selbst
in Luft auflösen.
> Den Assembler gibts nur so dazu.
Schadet aber jedenfalls nicht, wenn man sich mal damit befasst hat :-)
Ne mein Lieber.
Der Assembler, bzw. der Maschinencode, welcher aus ihm erzeugt wird, ist
das Maß aller Dinge für diesen Prozessor. Nur diesen kann er
"verstehen" und abarbeiten.
Jedes Compilat aus einer Hochsprache ist ein Kompromiß zwischen
Speicherbedarf / Geschwindigkeit auf der einen Seite und
Lesbarkeit/Verständlichkeit für den Menschen und Effizienz beim
Programmieren auf der anderen Seite.
Zwar könnte man sagen, daß viele RISC-Befehlssätze schon mit Hinblick
auf Hochsprachen-Compiler entwickelt und optimiert wurden, allerdings
bedeutet das keineswegs, daß man diese RISC-Befehlssätze als Mensch
nicht effizient einsetzen könnte. Ganz im Gegenteil. Gerade beim Atmel
AVR ist genau all das da, was man sich als "Assembler-Programmierer"
wünscht. Schleifen, "case"-Anweisungen, Bit-Operationen, all das läßt
sich in AVR-Assembler sehr schön intuitiv umsetzen. Ich empfinde es
sogar als hilfreich und nicht als Einschränkung, daß nur rund 100
Befehle zur Verfügung stehen.
Wenn es um Schnelligkeit und Speichereffizienz geht, dagegen
Entwicklungsaufwand und Portierbarkeit eine eher untergeordnete Rolle
spielen, (Großserie zum Beispiel), dann ist Assembler genau das, was du
willst.
Um auf die Anfrage, das ganze mal in Assembler umzusetzen zurückzukommen
Das hier ist nicht der Weisheit letzter Schluss, sondern nach einem
Schema programmiert:
Ich fang damit an, mir eine verbindliche Registerzuordnung zu
definieren:
R16 enthält immer den einen Operanden
R17 enthält immer den anderen Operanden
R16 enthält immer das Ergebnis der Operation
R19 ist ein temporäres Register, das während des Einlesens/Ausgebens
benutzt werden kann
In jedem Register gilt nur das Bit 0, weil da ja nur Logik im Spiel ist.
zusätzlich beenutze ich noch R20, das immer das Bitmuster für
1 enthält, bei manchen Operationen, bei denen keine Konstante
angegeben werden kann (EOR) mag sich das als nützlich erweisen.
*************
Die Negierung ist damit leicht: Bit 0 in R18 wird umgedreht. Das ist
ganz einfach ein
EOR R16, R20
*************
Eine UND Verknüpfung ist damit auch simpel:
AND R16, R17
*************
Und ein Oder ditto
OR R16, R17
*************
Bleibt nur nach das Abfragen eines Pins. Einlesen findet immer nach R19
statt. Dort wird das interessierende Bit freigestellt und je nachdem ob
dann 0 oder nicht 0 übrigbleibt, wird das Zielregister auf 0 oder 1
gesetzt.
CLR Zielregister ; Zielregister mal auf 0
SBIC gewünschter Port, gewünschter Pin
BREQ Fertig
LDI Zielregister, 1
Fertig:
***********
Ausgabe eines Pins mach ich mir ebenfalls als Baustein. Wenn R16 0 ist,
wird der Zielpin auf 0 gesetzt, ansonsten auf 1
CPI R16, 0
BREQ SetZero
SBI ZielPort, gewünschter Pin
RJMP Fertig
SetZero:
CBI ZielPort, gewünschter Pin
Fertig:
Damit hab ich meinen Baukasten, mit dem sich Operationen
zusammenstoppeln lassen. Wenn du etwas mehr Übung hast, könnte man für
die Operationen Makros definieren, dann wird auch der Schreibaufwand für
das eigentliche Programm kleiner.
Noch die Ports für Ein/Ausgabe richtig initialisieren und es kann
losgehen.
So in etwa würd ich an die Aufgabe rangehen.
0------= = ____
= UND =-----= =
1------=________= = ODER =
= =negiert----Bit7
2--------------negiert=________=
Alles soll sich am Port D abspielen. PD0, PD1, PD2 sind Eingang,
brauchen also nichts spezielles, PD7 ist Ausgang
1
SBI DDRD, PD7
2
LDI R20, 1 ; eine 1 ins Register R20 bringen
3
4
Loop:
5
; PD0 einlesen
6
CLR R16
7
SBIC PIND, PD0
8
BREQ Fertig
9
LDI R16, 1
10
Fertig:
11
12
; PD1 einlesen
13
CLR R17
14
SBIC PIND, PD1
15
BREQ Fertig1
16
LDI R17, 1
17
Fertig1:
18
19
; PD0 und PD1
20
AND R16, R17
21
22
; PD2 einlesen
23
CLR R17
24
SBIC PIND, PD1
25
BREQ Fertig2
26
LDI R17, 1
27
Fertig2:
28
29
; PD2 negieren
30
EOR R17, R20
31
32
; Ergebnis von PD0 und PD1 (steht noch in R16) ODER PD2 negiert
33
OR R16, R17
34
35
; Ergebnis bisher negieren
36
EOR R16, R20
37
38
; damit ist das Gesamt Ergebnis in R16, auf PD7 ausgeben
39
CPI R16, 0
40
BREQ SetZero
41
SBI PORTD, PD7
42
RJMP Fertig3
43
SetZero:
44
CBI PORTD, PD7
45
Fertig3:
46
47
rjmp Loop
Wie gesagt, das ist jetzt nach einem Baukastenschema zusammengesetzt. Da
kann man sicher noch vereinfachen. Vor allem die Ein/Ausgabeoperationen,
da ist noch Handlungsbedarf. Vielleicht fällt ja jemandem etwas besseres
ein, welches in das Baukastensystem passt. Aber im Grunde müsste das so
funktionieren.
Danke Karl heinz, dass Du Dir die Arbeit gemacht und mir ein
Beispielprogramm gepostet hast.
Boa, dass is beim AVR dann doch ganz schön kompliziert. Vorallem wenn
ich jetzt größere Netzwerke aufbauen möchte oder was ändern will....
Irgendwann will ich ja auch mal kompliziertere Sachen wie eine
Heizungssteuerung mit Display etc. programmieren. Aber ich glaube da ist
es für mich noch ein langer Weg...
Ist es vielleicht für mich doch besser jetzt in C weiterzulernen?
Habe heute angefangen das AVR-GCC-Tutorial durchzulesen. Hatte früher in
der Schule C++, kenntnisse also solala.
Wie hätte sowas in C ausgesehen?
Assembler ist eine sehr feine Sache um die Hardware kennenzulernen. Ich
würde einiges damit machen um die Architektur und die Feinheiten des AVR
kennenzulernen.
Für ganze Steuerungen mit Display usw. bietet sich C natürlich gradezu
an.
Dirk wrote:
> Boa, dass is beim AVR dann doch ganz schön kompliziert.
Halb so wild. letztendlich ist das nur Programmierbaustein an
Programmierbaustein gesetzt. Noch ein paar Makros drübergestülpt, und es
sieht so aus
Und das liest dich dann schon vieeel einfacher.
Und wer weiß, wenn du mehr Erfahrung hast, könnte man ja auch einen
kleinen 'Compiler' schreiben, der aus PD7 = !( ( PD0 & PD1 ) | !PD2 )
genau obige Sequenz erzeugt. Wär ne schöne Fingerübung um mal wieder was
in Compilerbau zu tun.
> Vorallem wenn> ich jetzt größere Netzwerke aufbauen möchte oder was ändern will....
Das wäre bei deinem Infinion auch nicht anders.
Immer im Auge zu behalten, welche Operation nun als letztes das Carry
Bit beeinflusst hat und wie sich die Operationen gegenseitig
beeinflussen, ...
Letztendlich gehts ja sowieso immer nur darum, den Überblick zu
behalten, welche Operation mit welcher anderen Operation interagiert.
Würde auch sagen: Schreibs in C.
Da kümmert sich der Compiler um die kleinen Details, und du hast viel
mehr Freiheiten
Ideal wäre es natürlich, wenn man für sowas eine graphische
Programmiersprache hätte.
Man zieht die ganzen Gatter (und sonstige Bausteine) auf eine
Arbeitsfläche, verbindet Ausgang mit Eingang entsprechend der Logik und
ein Compiler erzeugt dafür den Code, bzw. ein Interpreter arbeitet in
einer Schleife am µC die ganze Logik durch.
Aber für ein Einzelstück lohnt sich sowas klarerweise nicht.
Vielen Dank an euch alle, dass ihr mir immer so schnelle und informative
Antworten gebt!
Auf den ersten Blick finde ich, dass es in C viel Ausagekräftiger ist.
Man weiß sofort um was es geht.
Peter wrote:
>Schreibs einfach in C, dann läufts auf 8051 und AVR:
Diesen Vorteil sehe ich natürlich auch, ich dachte nur ich fange jetzt
erstmal in Assembler an, damit ich evtl. spätere Fehler die in C
auftauchen verstehe.
Karl heinz wrote:
>Ideal wäre es natürlich, wenn man für sowas eine graphische
Programmiersprache hätte.
Ja so wie bei der Siemens Logo!. Aber die kostet halt richtig Asche,
deswegen will ich mich ja in die AVR´s einarbeiten.
Mein Ziel ist es, A/D-Wandler, Sensoren, LCD´s und Motoren mit Hilfe des
AVR ansteuern zu können. Deswegen hätte ich eine Bitte, aus eurer
Erfahrung könnt ihr mir bestimmt sagen, wie ich zum Ziel komme.
Gibt es besondere Bücher (welche?) die zu empfehlen sind? Oder ein Art
"Kochrezept" wie:
1. C Programmierung lernen (mit Buch xy oder www.Seite xy)
2. AVR-GCC Tutorial komplett durcharbeiten
3. Programmbeispiele auf Seite www.xy.. durcharbeiten
usw..
Ich habe das AVR-GCC Tutorial noch nicht ganz durch, aber was kann ich
danach? Ich denke es wirft danach immer noch viele Fragen auf. Ich würde
gerne ein Buch haben, dass ganz einfach anfängt. C für Mikrocontroller,
dass dann immer mehr und mehr Komponenten behandelt und viele praktische
Beispiele beinhaltet. Wirklich von 0 auf! Ich weiß es gibt so viele
elektronische Bauteile die man behandeln könnte, aber die gängisten
sollten es halt sein (Tempfühler bzw. Analoge Messwerte verarbeiten,
Schrittmotor, LCD- Display, Eingabe über ein Display z.B. Zeitschaltuhr,
usw...)
Ich sehe mir immer wieder gerne die Seite http://www.mikrocontroller.com
an. Und ich muß staunen wie man sowas machen kann. Nicht das Löten und
zusammenbauen sondern, ein Programm dafür zu schreiben. Wenn ich mir
dann in C so ein Programm anschaue kapier ich garnix..
Dirk wrote:
> Peter wrote:>>Schreibs einfach in C, dann läufts auf 8051 und AVR:> Diesen Vorteil sehe ich natürlich auch, ich dachte nur ich fange jetzt> erstmal in Assembler an, damit ich evtl. spätere Fehler die in C> auftauchen verstehe.
Schreibs in C und laß dir den ASM-Code ausgeben. Dann siehst du, wie der
Compiler die C-Anweisungen in Maschinencode umsetzt.
So lernst du, was der Compiler macht und gleichzeitig den Assembler - so
hab ichs schon für einen ganzen Sack voll verschiedener Prozessoren
gemacht...
Wenn man will, kann man das ja auch mit ner Tabelle machen. In C (z.B.)
geht das sehr schön.
Angenommen, ich will ein 2fach NAND programmieren.
Wahrheitstabelle:
Nr Eingang Ausgang
0 00 1
1 01 1
2 10 1
3 11 0
Jetzt könnte man folgendes Programmieren:
void main(void)
{
char data[] = {1, 1, 1, 0};
char inputcode;
char outputcode;
while(1)
{
inputcode = (dein eingangsport, ggf. noch bits maskieren)
outputcode = data[inputcode];
(dein ausgangsport) = outputcode;
}
}
Karl heinz Buchegger wrote:
> da ist noch Handlungsbedarf. Vielleicht fällt ja jemandem etwas besseres> ein, welches in das Baukastensystem passt. Aber im Grunde müsste das so> funktionieren.
...
> In jedem Register gilt nur das Bit 0, weil da ja nur Logik im Spiel ist.>> zusätzlich beenutze ich noch R20, das immer das Bitmuster für> 1 enthält, bei manchen Operationen, bei denen keine Konstante> angegeben werden kann (EOR) mag sich das als nützlich erweisen.>> *************> Die Negierung ist damit leicht: Bit 0 in R18 wird umgedreht. Das ist> ganz einfach ein>> EOR R16, R20
Wenn man nur auf Bit 0 schaut, geht auch einfach
INC R16 oder
DEC R16 oder
COM R16
und R20 wird nicht belegt.
> Bleibt nur nach das Abfragen eines Pins. Einlesen findet immer nach R19> statt. Dort wird das interessierende Bit freigestellt und je nachdem ob> dann 0 oder nicht 0 übrigbleibt, wird das Zielregister auf 0 oder 1> gesetzt.
CLR Zielregister ; Zielregister mal auf 0
SBIC gewünschter Port, gewünschter Pin
INC Zielregister ; Zielregister auf 1
oder
IN Zielregister ; Alle Pins lesen
BST gewünschter Pin ; T = gewünschter Pin
BLD Zielregister, 0 ; Zielregister.0 = T
> Ausgabe eines Pins mach ich mir ebenfalls als Baustein. Wenn R16 0 ist,> wird der Zielpin auf 0 gesetzt, ansonsten auf 1
SBRC R16, 0
SBI ZielPort, gewünschter Pin
SBRS R16, 0
CBI ZielPort, gewünschter Pin
oder
BST R16, 0 ; T = R16.0
IN R?, ZielPort
BLD R?, gewünschter Pin
OUT ZielPort, R?
Johann
Ok, danke nochmal für die Problemlösungen in Assembler.
Ich habe jetzt für mich rausgefunden, dass ich alles weitere in C machen
werde. Bei den ersten Versuchen bin ich schneller und einfacher zum Ziel
gekommen.
Auf dieser Seite habe ich Beispielprogramme gefunden:
http://www.blafusel.de/books/avr.html (Download |= (1<<Übungsdateien);-)
Manche Programme haben Fehler. Ist aber eine gute Übung diese
auszubessern.
Am Anfang steht immer dabei, was das Programm können soll. Somit hat man
auch gleich eine Aufgabenstellung und kann später sein Programm
vergleichen.
Und das wichtigste, man hat Erfolgserlebnisse und das entschädigt für
die ganze Zeit die man sich in das Thema reingelesen hat.
Ich lerne am schnellsten, wenn ich ein Programm schon fertig habe und es
im AVR-Studio mit "Step Into (Taste-F11)" analysiere. Nennt man dieses
Teil eingentlich Debugger?
Werde jetzt meine Beispielprgramme mit Hilfe des AVR-GCC Tutorial
durcharbeiten und hoffe, dass ihr mir bei Problemen weiter so gut helfen
werdet.
PS: für alle die schon beim ersten Programm(LED2) verzweifeln warum es
im AVR-Studio nicht geht. Einfach wie unten abändern oder besser noch
von dieser Seite markieren und kopieren.
==============================================================
/* Laesst eine LED an PB0 und PB7 im wechsel endlos blinken
*
*/
#include <avr/io.h> //Header-Datei für Aus- und Eingabe
#include <util/delay.h> // Header_Datei für das Unterprogramm,
//das mit _delay_ms() aufgerufen wird
int main()
{
DDRB = 0xFF; // PORTB als Ausgang definieren
while (1) //"Grundschleife"
{
PORTB = 0b00000001; // LED an PB0 ein, Bsp. als Binärzahl
_delay_ms (250); // 250 Millisekunden Schleife
PORTB = 0x80; // LED an PB7 ein, Bsp. als Hexzahl
_delay_ms (250);
}
return 0;
}
==============================================================
Eine Frage habe ich noch, es sind ja keine 250 Milisekunden wenn ich mir
die "Stop Watch" später anschaue.
Hat jemand eine bessere Schleife, die man vielleicht vor "MAIN"
schreibt, die dann wirklich von 0 bis XX ms annähernd stimmt?
Oder ist das immer ein Problem zwecks unterschiedlicher Zykluszeit der
einzelnen MCs.
Dirk wrote:
> Ich lerne am schnellsten, wenn ich ein Programm schon fertig habe und es> im AVR-Studio mit "Step Into (Taste-F11)" analysiere. Nennt man dieses> Teil eingentlich Debugger?
Ja
> Oder ist das immer ein Problem zwecks unterschiedlicher Zykluszeit der> einzelnen MCs.
Die Wartezeiten von _delay_xx stimmen nur dann, wenn der Optimizer des
Compilers eingesetzt wird. Dies deshalb, da die Wartezeit durch
Verbraten von Taktzyklen durch Schleifenkonstrukte erreicht wird. Das
funktinioniert aber nur dann, wenn genau bekannt ist, was der Compiler
aus einer C-Schleife genau erzeugt. Das ist aber unterschiedlich, wenn
der Compiler optimieren darf oder nicht. Die Berechnung der notwendigen
Schleifenwieerholungen wurde daher so gemacht, dass es dann passt, wenn
der Compiler optimieren darf.
Ist aber in der Praxis weit weniger ein Problem als es sich jetzt
anhört. Vor allem deswegen, weil man solch lange Wartezeiten nicht mit
_delay_xx machen wird, sondern über irgendwelche Timerkonstruktionen.
_delay_xx blockiert dir sonst nämlich die ganze Maschine und spätestens
wenn dein Rechner mehrere Aufgaben quasi gleichzeitig erledigen soll,
ist der Einsatz von _delay_xx sowieso kontraproduktiv (ausser vielleicht
für ganz kurze Wartezeiten)