Hallo Leute,
nachdem ich ein blutjunger Anfänger bin in diesem Sektor, habe ich mir
mal das Lehrbuch Microkontroller Programmierung gekauft und
durchstudiert und bin ein wenig am rätseln, da ich einfach nicht
weiterkomme.
Ich habe das myAVR Board MK2 und würde es gerne realisieren, dass wenn
ich den Taster (PORTD2) drücke der Ausgang (LED // PORTB0) für eine
eingestellte Zeit (zB 0,5s) eingeschaltet wird und wieder ausgeht, auch
wenn der Taster noch gedrückt ist.
Prozessor : ATmega8
Takt : 3,6864 MHz
Ich kann den Taster betätigen - ich kann damit das LED einschalten
(solange der Taster gedrückt ist), aber die Zeiteinbindung bekomme ich
leider nicht heraus.
Kann mir jemand dabei helfen bitte???
Ich werde sicherlich ein wenig Hilfe benötigen bei ein paar Beispielen -
aber ich lerne schnell; wenn mir jemand ein paar Sachen beibrigen
könnte, wäre ich sehr dankbar!!!
lg
Michael
Michael Hagen schrieb:>> Prozessor : ATmega8> Takt : 3,6864 MHz>> Ich kann den Taster betätigen - ich kann damit das LED einschalten> (solange der Taster gedrückt ist), aber die Zeiteinbindung bekomme ich> leider nicht heraus.
Wie weit bist du mit deinem Können?
Das zu wissen ist wichtig, damit man dir eine für dich verstehbare
Lösung anbieten kann.
Die saubere Profilösung läuft über einen Timer. Wenn du allerdings mit
deinem Können noch weit davon entfernt bist, einen Timer sauber in Zaum
zu halten, dann ist das erst mal eventuell noch nichts für dich und du
kommst erst mal mit einer _delay_ms Lösung weiter, auch wenn die aus
verschiedenen Gründen im Grunde abzulehnen ist.
AVR-GCC-Tutorial
Die Banallösung mit delay sieht so aus
while( 1 ) {
if( Taste gedrückt ) {
schalte LED ein
warte 500 Millisekunden
schalte LED aus
while( Taste gedrückt ) // warte bis der Benutzer die Taste
; // wieder losgelassen hat
}
}
Nicht schön, aber leicht zu verstehen.
Schalte den Optimizer deiners Compilers ein, und diese Zählschleife
fliegt raus.
Genau deshalb gibt es nämlich unter anderem auch die _delay_ms Funktion.
Die hat dann auch noch den Vorteil, dass man nicht selbst mühsam den
Zählwert anpassen muss, sondern man schreibt einfach
_delay_ms( 500 );
und die Funktion wartet (mehr oder weniger genau, aber auf ein paar µs
soll es nicht ankommen) dann auch 500 Millisekunden.
ANonsten hast du natürlich recht. Timer ist immer die bessere Variante,
wenn auch vom Verständnis her etwas schwieriger.
Hi
Nun, helfen kann dir fast jeder hier.. also est mal die
Kernfrage:"Welche Sprache" und bitte sag jetzt nicht "deutsch"
In Assembler würd ich es so lösen:
Die IVT überspringen, aber in der IVT den Timer-Interrupt aktivieren
(RCALL auf die ISR)
den Stack setzen RAMEND -> SP
Den Timer parametrieren ( Init_Timer )
Die IO parametrieren ( Init_IO )
Die Programmschleife
die Eingänge lesen ( Read_IO)
die Bitlage drehen (Complement bilden)
Änderung erkennen ( XOR alt mit neu )
Prellzeit abwarten ( XOR mit Controllbit über, Zeit)
Änderungen ablegen ( Eventflags setzen)
Programm nach Flags prüfen
Ausgaben aufbereiten ( Bits für Ausgabe aufbereiten)
Bits ausgeben ( Write_IO )
nächster Schleifendurchlauf
Nach dem Einlesen der Eigänge schreibe ich die Bits in eine Variable und
drehe die Bitlage. In der Regel werden Taster/Schalter nach GND
verdrahtet, damit der interne Pull-Up genutzt werden kann. Durch das
Drehen der Signallage erreiche ich den logischen Zusammenhang Taster
gedrückt ="1". Das spart viele Stunden Fehlersuche, weil der Kopf
natürlich automatisch eine Tasterbetätigung mit einer "1" verbindet.
Nun wird der Wert dieses Bytes mit dem Wert eines abgelegten Bytes
exclusiv - oder verknüpft. Im Ergebnis stehen nur "1"er, wenn die neuen
Bits geändert wurden. Nicht veränderte Bits sind "0". Ist das Ergebnis
dieser Exclusiv-Oder Verküpfung im Byte 0, so zähle ich einen Wert in
einer anderen Variable herunter, bei ungleich 0 setze ich wieder einen
Wert in die Variable und kopiere die neuen Eingangsbits in die Ablage.
Ist dieser Wert irgendwann einmal 0, so ist entsprechend der Wertigkeit
x-mal keine Änderung in den Eingängen aufgetaucht. Das Prellen von
Kontakten ist beendet. Jetzt liegt ein gültiger Wert vor. Nachteil: die
einzelnen Bits werden nicht gesondert betrachtet, aber das ist in den
meisten Fällen auch nicht erforderlich.
Der gültige Wert wird mit einer anderen Ablage ebenfalls
exclusiv-Oder-verknüpft. Nun entstehen Änderungsbits gültiger Eingänge.
Wenn diese nun mit einer Und-Verknüpfung der neu gültigen Bits gefiltert
werden erhalte ich Ereignisbits für Signalwechsel nach "1".
Nun komme ich zu deinem ansinnen. Mit diesem Bit setzut du deinen
Ausgang auf "1" und löscht das Ereignisbit. Damit wird es dir möglich,
nicht mehr auf den Taster/Schalter zu reagieren, egal wie lange du
diesen festhälst. Erst ein loslassen und erneutes Betätigen setzt wieder
das Ereignisflag auf "1". Hört sich kompliziert an, ist es aber nicht
hier mal der Code:
1
Read_IO:
2
IN Reg_A, Port_C ; Port einlesen
3
COM Reg_A ; Bits drehen
4
ANDI Reg_A, 0b00011100 ; nicht relevante Portpins ausblenden
5
LDS Reg_B, in_Debounce ; kontrollierte Bits holen
6
EOR Reg_B, Reg_A ; nur 0, wenn beide Bytes gleich
7
STS In_Debounce, Reg_A ; und zur Kontrolle ablegen
8
BREQ Chk_Deb_Time ; keine Änderung festgestellt
9
LDI Reg_B, 5
10
STS Deb_Time, Reg_B ; Kontrollzeit wieder neu setzen
11
RJMP End_Read_IO
12
Chk_Deb_Time:
13
LDS Reg_B, Deb_Time
14
CPI Reg_B,0 ; bereits erledigt
15
BREQ End_Read_IO
16
DEC Reg_B ; Zähle Zeit/ Durchlauf herunter
17
STS Deb_Time, Reg_B ; und speicher das Ergebnis
18
BRNE End_Read_IO ; wenn nicht 0 dann ende
19
LDS Reg_B, Old_In ; die letzten gültigen Bits holen
20
EOR Reg_B, Reg_A ; Reg_A hat immer noch die neuen gültigen Bits
21
AND Reg_B, Reg_A ; In Reg_B sind Taster gedrückt-Ereignisse
22
STS In_to_1, Reg_B ; Flags merken
23
STS Old_In, Reg_B ; neue Bits in Ablage legen
24
End_Read_IO
Mit den Bits in der Variablen in_to_1 kannst du nun eine einmalige
Bearbeitung aufrufen
1
Chk_taster_1:
2
LDS Reg_A, In_To_1
3
ANDI Reg_A, 0b00000100 ; es sind ja nur Bits 2-5 relevant
4
BREQ End_Chk_Taster_1 ; kein neu Taster gedrückt
5
LDS Reg_A, In_To_1
6
ANDI Reg_A, 0b11111011 ; nur Bit für Taster 1 wieder aus
7
STS In_To_1, Reg_A ; gibt evtl. weitere Taster zu prüfen
8
LDS Reg_A, Ausgabe ; Variable für Ausgabebits
9
ORI Reg_A, 0b00000001 ; nur Bit 0 setzen
10
STS Ausgabe, Reg_A ; die Zuweisung an den Port in Write_IO
11
End_Chk_Taster_1:
12
Ret
Das Bit in der Variablen Ausgabe ist solange gesetzt, bis in einer
anderen Bearbeitung dieses explizit gelöscht wird. z.B. in der Timer_Isr
1
Timer_ISR: ; Interrupt alle msek
2
Push Register ; benutzte Register sichern
3
...... ; irgendwelche Bearbeitungen.....
4
LDS Reg_A, mSek_0 ; millisekunden zählen
5
INC Reg_A
6
STS mSek_0, Reg_A
7
CPI Reg_A, 10
8
BRLO End_Timer_ISR ; kleiner 10. dann Ende
9
CLR Reg_A
10
STS mSek_0, Reg_A ; bei 0 wieder anfangen
11
LDS Reg_A, mSek_1 ; hundertstel zählen
12
INC Reg_A
13
STS mSek_1, Reg_A
14
CPI Reg_A, 10
15
BRLO End_Timer_ISR ; kleiner 10. dann Ende
16
CLR Reg_A
17
STS mSek_1, Reg_A ; bei 0 wieder anfangen
18
LDS Reg_A, mSek_2 ; hundertstel zählen
19
INC Reg_A
20
STS mSek_2,Reg_A
21
CPI Reg_A, 5 ; 0,5 Sek.
22
BRNE Chk_10
23
LDS Reg_B, Ausgabe
24
ANDI Reg_B, 0b11111110 ; Bit 0 für Ausgabe wieder auf 0 setzen
25
STS Ausgabe, Reg_A
26
Chk_10:
27
CPI Reg_A, 10
28
BRLO End_Timer_ISR
29
CLR Reg_A
30
STS mSek_2, Reg_A ; und weiter mit sekunden etc....
31
.....
32
End_Timer_ISR:
33
POP Register ; alle verwendeten Register zurückholen
34
RETI
Ich hab das mal ungeprüft so dahingeschmiert, also Fehler mußt du dir
dann selber raussuchen. Immer dran denken, das in ISR die verwendeten
Register mit Push und POP gesichert werden müssen, auch das
Statusregister.
Und nun viel Spaß
Gruß oldmax
Also zunächst einmal:
VIELEN VIELEN DANK an EUCH ALLE!!!!!
Ihr seid so hilfsbereit; das sieht man wirklich sehr sehr selten in
manchen Foren.
Ich habe (lt. dem Übungsbuch) natürlich nur das Sisy AVR 3 in dem
Assebler benützt wird, was für mich etwas schwierig ist.
Ich habe zwar SPS Programmieren gehabt und auch einige komplizierte IF
Abfragen in manchen Programmen realisiert und auch in der Abendbulme
etwas C gelernt, habe aber geglaubt, dass man Mikrocontroller NUR mit
Assembler programmieren kann.
Ich habe einige Zusammenhänge bei der C Sprache (schon ab dem ersten
Eintrag) gesehen und bin jetzt schon wieder SEEEEEEHR viel schlauer, als
zuvor.
Ich werde mir die angebotenen Befehle und Hilfestellungen einmal ansehen
und programmieren und eventuell etwas verändern... nur so lernt man am
besten... ich zumindest ;-) Bin so ein "learning by doing"-Typ lach
Jedenfalls vielen vielen Dank nochmals an euch alle!!! Falls ich nochwas
brauchen sollte, melde ich mich einfach hier...
lg
Michael
@oldmax:
vielen Dank für deine ausführliche Hilfe.
Leider lässt sich das so nicht kompilieren... das Programm schreibt für
jede Zeile jedesmal:
"Error: register name or number from 0 to 31 required"
bzw. bei deinem Befehl
1
COM Reg_A ; Bits drehen
"Error: constant value required"
Ich weiß, was es heißt und was zu tun wäre; ich weiß nur nicht wie....
Kannst du mir helfen?
lg
Hi
Ok, dann steigen wir mal etwas früher ein....
Der Compiler bekommt Informationen, wie er ein Programm zusammen setzen
soll. Die Sprache ist eigentlich erst mal unwichtig. Beginnen wir mal
mit der Benennung der Register. Wenn man Registernamen nicht mag, so ist
es kein Problem einfach einen anderen Namen zu vergeben.
Def. Reg_A = r16 - heißt einfach, mach aus r16 ein Reg_A.
Man muß bei der Verwendung von Registern nur wissen, das r0 nicht alle
Befehle mitmacht. So sind Befehle, die eine Constante beinhalten erst ab
Register 16 einzusetzen
LDI r0, 1 geht nicht, LDI r16 wohl
Auch
CPI r0, 7 wird angemeckert, CPI r16 nicht
Zugriffe auf Variablen sind auch erst mit Registern ab 16 machbar
LDS r0, Old_In geht nicht, aber LDS r16, Old_In
Will man diese Register nutzen, z.B. für Bitverküpfung zwischen zwei
Registern, so muß mit einem Mov ein r0 erst mit einem Wert versorgt
werden.
Mov r0, r16 schiebt den Inhalt von r16 nach r0. Ein Vergleich
Cp r0, r17 ist gültig.
Weitere Infos hol dir aus den Tutorials.
Es gibt in einem µC verschiedene physikalische Speicher. Dafür sind
Speichersegmente benannt:
DSeg: (Datensegment) für unbegrenzte Schreibzyklen
CSeg: (Codesegment) Schreibzyklen sind begrenzt, da der Speicher bei
jedem Schreibvorgang "verbraucht" wird.
ESeg: (EEProm) Schreibzyklen ebenfalls begrenzt.
Variablen werden im DSeg. deklariert, weil ja ständig Schreibzugriffe
darauf getätigt werden. Würde man den EEProm so nutzen, wär der
Controller bald dahin. Angegeben sind zwar 100 000 Schreibzyklen, aber
bei einigen MHz sind diese u.U. schnell durch.
Eine Variable wird nun im DSeg. bekannt gemacht:
.DSeg
old_In: .Byte 1 ; 1 Byte Variable
Buffer: .Byte 10 ; 10 Byte Variable
Zeiger: .Byte 1
Man muss sich nun vorstellen, das der Compiler die Adresse im Code
einträgt, wenn er einen Variablennamen entdeckt. Das sieht etwa so aus
Adresse Variable
xxxx old_in
xxxx+1 Buffer(0)
xxxx+2 Buffer(1)
...
xxxx+9 Buffer(9)
xxxx+10 Zeiger
usw.Auch hierzu findest du nützliche Beispiele in den Tutorials. So, nun
korrigier die Fehler und dann viel Spass
Gruß oldmax
@oldmax:
vielen Dank...ich werde mir das mal genauer jetzt ansehen.
BTW habe ich auch eine andere Lösung einmal generiert und wollte fragen,
was ihr jetzt dazu haltet (auf Basis von Karl Heinz)
1
while(1){
2
3
if(DDRD&=0b00000001){
4
DDRB=0b00000001;
5
_delay_ms(500);
6
DDRB=0b00000000;
7
8
while(DDRD&=0b00000000)
9
;
10
}
11
}
Hätte das Potential?
lg
P.S.: was bedeutet das genau?
Kompiliere die Datei Test_C.cc.
In file included from Test_C.cc:17:
C:\Program Files (x86)\SiSy3
AVR\Compiler\winavr\avr\include/avr\delay.h:36:2: warning: #warning
"This file has been moved to ."
Linke die Datei Test_C.elf.
Ende.
>> Hätte das Potential?
Nein.
Bitte lies die Tutorien bzw. dein Buch von VORNE und arbeite die
Beispiele bzw. die Übungen, wenn im Buch welche enthalten sind, durch.
Wenn im Buch keine Übungen angegeben sind, dann erfinde selbst welche,
die im Schwierigkeitsgrad dem Gelesenen entsprechen. Ist im ersten
Kapitel deines Buches nur von Ausgängen und zb LEDs an Ausgängen die
Rede, dann erfinde nicht irgendwelche, dir interessant vorkommende,
Übungen, sondern beschränke dich auf Ausgänge. Auch damit kann man schon
einiges machen:
Led einschalten
Led ausschalten
Blinken
Lauflicht
etc.
DDRx Datenrichtungsregister.
Damit wird festgelegt, ob ein Pin ein Eingang oder
ein Ausgang ist
PORTx wenn ein Pin auf Ausgang (mittels DDRx) geschaltet
wurde, dann legt das PORTx Register fest, ob an diesem
Ausgang eine 0 oder eine 1 aufscheint.
(und wenn ein Pin auf Eingang ist, dann hat das PORTx
Register eine Sonderfunktion)
PINx wenn ein Pin auf Eingang geschaltet wurde,
dann bekommt man hier den Zustand des Pins (ob 0 oder 1)
so wie ihn die externe Beschaltung des Pins erzwingt.
Das sind relativ einfache Grundlagen. Wenn du die noch nicht intus hast,
dann bedeutet das, dass du mit deiner selbst gewählten Aufgabenstellung
schon einen zu großen Bissen vom Apfel abbeisst.
Den Fehler, dass dir die DDRx und die PINx bzw. PORTx durcheinander
kommen, darfst du bei deinen ersten 2 oder 3 Programmen machen. Danach
darf dir sowas aber nicht mehr passieren bzw. das musst du selber
merken.
Genauso, wie du nach dem 2.ten oder 3.ten Programm die Schreibweise
intus haben müsstest (oder zumindest weißt wo du nachsehen musst), wie
man
* einen Ausgangs-Pin, und zwar nur diesen Pin, gezielt auf 1 setzt.
* einen Ausgangs-Pin, und zwar nur diesen Pin, gezielt auf 0 setzt.
* einen Eingangs-Pin abfrägt.
Und dann könnte man sich noch über Schreibweisen etc. auslassen.
Aber alles zu seiner Zeit. Alles auf einmal geht nicht.