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.
Schreibs einfach in C, dann läufts auf 8051 und AVR:
1 | #include <io.h> |
2 | #include "mydefs.h" |
3 | |
4 | |
5 | #define IN_A SBIT( PINB, 0 )
|
6 | #define IN_B SBIT( PINB, 1 )
|
7 | #define IN_C SBIT( PINB, 2 )
|
8 | #define OUT_Y SBIT( PORTB, 7 )
|
9 | #define OUT_Y_DDR SBIT( DDRB, 7 )
|
10 | |
11 | #if 0
|
12 | 0------= = ______
|
13 | = UND =-----= =
|
14 | 1------=________= = ODER =
|
15 | = =negiert----Bit7
|
16 | 2--------------negiert=________=
|
17 | #endif
|
18 | |
19 | |
20 | void logic( void ) |
21 | {
|
22 | OUT_Y_DDR = 1; // pin direction = output |
23 | |
24 | for(;;){ |
25 | OUT_Y = !( // Y = NOT |
26 | (IN_A && IN_B) // A AND B |
27 | || // OR |
28 | !IN_C // NOT C |
29 | );
|
30 | }
|
31 | }
|
Peter
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
1 | Loop: |
2 | READ_PORT PIND, PD0 |
3 | MOV R16, R17 |
4 | |
5 | READ_PORT PIND, PD1 |
6 | |
7 | AND_VALUES ; I) PD0 & PD1 |
8 | |
9 | READ_PORT PIND, PD2 |
10 | |
11 | NEGATE ; II) !PD2 |
12 | OR_VALUES ; III) ( PD0 & PD1 ) | !PD2 |
13 | |
14 | NEGATE ; IV) !( ( PD0 & PD1 ) | !PD2 ) |
15 | |
16 | WRITE_PORT PORTD, PD7 ; V) PD7 = !( ( PD0 & PD1 ) | !PD2 ) |
17 | |
18 | rjmp Loop |
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)
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.