Forum: Mikrocontroller und Digitale Elektronik Ausgang nur kurz schalten


von Michael H. (shadowkahn)


Lesenswert?

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

von Karl H. (kbuchegg)


Lesenswert?

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.

von dummschwaetzer (Gast)


Lesenswert?

taster gedrückt: Led an und timer oder anderwertigen Zähler starten
nach ablauf der zeit: Led aus
1
if(taste_gedrückt)
2
{
3
 led_an();
4
 for(unsigned long i=0;i=/*Wert anpassen*/;i++)
5
 {
6
   ;
7
 }
8
 led_aus();
9
}
währe die variante mit Zähler, hat den nachteil, dass die CPU in der 
Zeit nichts anders macht als den Zähler zu erhöhen
besser:
1
if(taste_gedrückt)
2
{
3
 led_an();
4
 init_timer();
5
}
6
 
7
void init timer(void)
8
{
9
  /*read the f******* Manual*/
10
  // Initialisiere den Timer so, dass er nach deiner Zeit einen Interupt auslöst
11
}
12
13
#pragma vector=//lies hier die Hilfe zu deinem compiler...
14
void timer_isr(void)
15
{
16
  //Timer anhalten
17
  led_aus();
18
}
hier kann dein controler in den 0,5s auch andere dinge tun

von Karl H. (kbuchegg)


Lesenswert?

dummschwaetzer schrieb:

>
1
> if(taste_gedrückt)
2
> {
3
>  led_an();
4
>  for(unsigned long i=0;i=/*Wert anpassen*/;i++)
5
>  {
6
>    ;
7
>  }
8
>  led_aus();
9
> }
10
>

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.

von oldmax (Gast)


Lesenswert?

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

von Michael H. (shadowkahn)


Lesenswert?

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

von Michael H. (shadowkahn)


Lesenswert?

@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

von spess53 (Gast)


Lesenswert?

Hi

>Ich weiß, was es heißt und was zu tun wäre; ich weiß nur nicht wie....

Ersetze doch einfach Reg_A, Reg_B durch r16 bzw. r17.

MfG Spess

von oldmax (Gast)


Lesenswert?

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

von Michael H. (shadowkahn)


Lesenswert?

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

von Karl H. (kbuchegg)


Lesenswert?

Michael Hagen schrieb:

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

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.