mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik Taster abfragen?


Autor: Hannes E. (k1ngarthur) Benutzerseite
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Moin,

ich habe drei Taster an meinem ATtiny 2313 angeschlossen (Low-Active) 
und frage den Status eines Taster mit folg. Funktion ab:
uint8_t btn_check(volatile uint8_t *port, uint8_t pin){

  if( !(*port & (1 << pin)) ){

    return 1;

  }

  return 0;

}

eine Abfrage mit wie z.B.:
if ( btn_check(&BTN_PORT, BTN_UP) ) time_left = 10;
für dazu, dass die Funktion 1 zurückliefert, auch wenn der Taster nicht 
gedrückt ist.

Der Pegel am Pin des µC ist ungedrückt 5V, gedrückt 1-2mV.

Hat jemand eine Ahnung wo hier das Problem liegt?

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Nicht mit dem bischen Code.

Autor: Hannes E. (k1ngarthur) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ok,

dann mal alles was relevant ist:
  // Ports initialisieren
  DISP_CNTRL_DDR |= (1 << DISP1) | (1 << DISP2) | (1 << DISP3);
  BTN_DDR &= ~(1 << BTN_UP) | (1 << BTN_DOWN) | (1 << BTN_START);
DISP_CNTRL_DDR und BTN_DDR sind identisch (PORTD)

Auf den Port wird zudem in folg. Programmteil zugegriffen.
  if( disp == 1 ) DISP_CNTRL_PORT = (1 << DISP1);
  else if( disp == 2) DISP_CNTRL_PORT = (1 << DISP2);
  else DISP_CNTRL_PORT = (1 << DISP3);
Dieser wird in main() bei jedem durchlauf dür die Anzeige auf 7-Segment 
anzeigen verwendet (Steuerung der Anode).

Dann habe ich noch folgende Funktion, welche die 7-Seg.-Anzeigen 
ausschaltet (wird nur gelegentlich aufgerufen):
void disp_off(){

  // Complete Display off
  DISP_CNTRL_PORT &= ~(1 << DISP1) | (1 << DISP2) | (1 << DISP3);

}

Alles andere beeinflusst die Ports nicht.
Aktiv ist nur noch der Timer0.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Poste doch bitte der Einfachheit halber ALLES!

Manchmal steckt der Fehler gar nicht dort, wo du ihn vermutest. Zb die 
Fragestellung: Woher weißt du eigentlich, dass die Funktion immer 1 
liefert?

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Das hier
void disp_off(){

  // Complete Display off
  DISP_CNTRL_PORT &= ~(1 << DISP1) | (1 << DISP2) | (1 << DISP3);

}

schaltet nicht DISP1, DISP2 und DISP3 auf 0, sondern nur DISP1

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Dieser wird in main() bei jedem durchlauf dür die Anzeige
> auf 7-Segment anzeigen verwendet (Steuerung der Anode).

Hä?
Du hast die gemeinsame Anoden von irgendwelchen 7-Segment auf denselben 
Pins wie die Taster? Oder wie? Oder was?

Wie geht das, ohne dass sich die gegenseitig ins Gehege kommen?
(Schaltplan)

Autor: Michi (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
BTN_DDR &= ~(1 << BTN_UP) | (1 << BTN_DOWN) | (1 << BTN_START);
An die Experten: Fehlt da nicht eine Klammer vor dem ~?
Die Auswertungsreihenfolge in C wertet doch den ~-Operator vor dem 
|-Operator aus, oder?
Das müsste meiner Meinung nach so geschrieben sein:
BTN_DDR &= ~((1 << BTN_UP) | (1 << BTN_DOWN) | (1 << BTN_START));

Autor: Hannes E. (k1ngarthur) Benutzerseite
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Hier mal der gesamte Code inkl. Schaltplan.
Der Tiny soll als Timer fungieren. Die Zeit soll runtegzählt werden und 
dabei einen Pin (RL) auf high legen und diesen nach Ablauf der Zeit 
wieder ausschalten.

Die Zeitanzeige funktioniert auch, jetzt hängts halt am Polling der 
Taster.

>Das müsste meiner Meinung nach so geschrieben sein
Nein, C-Code ist rechtsassoziativ. Das heisst, dass der Code von rechts 
an interpretiert wird. Es wird also zuerst das Bitmuster erstellt und 
dieses dann invertiert und dann zugewiesen.
Das hat bisher auch so funktioniert und ist auch so im AVR-GCC-Tutorial 
zu finden.

Autor: Klaus (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hannes E. schrieb:
> Nein, C-Code ist rechtsassoziativ. Das heisst, dass der Code von rechts
> an interpretiert wird. Es wird also zuerst das Bitmuster erstellt und
> dieses dann invertiert und dann zugewiesen.

Aber was Operatorprioritäten sind, weißt du schon, oder?

Autor: Gast 4711 (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hannes E. schrieb:
> Das hat bisher auch so funktioniert und ist auch so im AVR-GCC-Tutorial
> zu finden.

An welcher Stelle?

Ich habe gerade mal das Tutorial überflogen, und
bei allen Beispielen in denen mehr als 1 Bit
zurückgesetzt wird ist die Klammer um den ganzen Ausdruck.

Autor: Hannes E. (k1ngarthur) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
tatsache. komisch nur, dass es bei anderen beispielen klappt.

habe den code jetzt ensprechend angepasst. zudem noch folg.Änderung in 
disp_send_data(), damit nur die Bits für die 7-Seg.-Anz. geändert 
werden.
  if( disp == 1 ){
    DISP_CNTRL_PORT |= (1 << DISP1);
    DISP_CNTRL_PORT &= ~( (1 << DISP2) | (1 << DISP3) );
  }
  else if( disp == 2){
    DISP_CNTRL_PORT |= (1 << DISP2);
    DISP_CNTRL_PORT &= ~( (1 << DISP1) | (1 << DISP3) );
  }
  else{
    DISP_CNTRL_PORT |= (1 << DISP3);
    DISP_CNTRL_PORT &= ~( (1 << DISP1) | (1 << DISP2) );
  }

Das Problem ist damit aber noch nicht verschwunden.

Autor: Stephan S. (plonk)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Schon mal überprüft, ob (evtl. vorhanden) JTAG noch aktiviert ist?

Autor: Hannes E. (k1ngarthur) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
wie meinst du das?
Ich benutze den JTAG-Programmer um die Schaltung mit Strom zu versorgen.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Dein Code ist ziemlich unübersichtlich

Das Ansteuern der 7-Segment ist nicht sehr glücklich gemacht. So etwas 
macht man in einer Timer ISR, die bei jedem Aufruf die nächste Stelle 
aktiviert.

Ausserdem hast du ziemlich viele ! in deinen Code eingestreut, die jetzt 
erst einmal alle untersucht werden müssten.

Alles in allem denke ich, du hast den Code zu verzahnt aufgebaut mit 
viel zu vielen Status und sonstigen Flags und dabei hast du dich 
irgendwo verzettelt.

AUch ist mir gerade aufgefallen, dass deine ganzenn ISR-main globalen 
Variablen nicht volatile sind.

Zb hier
ISR(TIMER0_OVF_vect){

  // Zeit-Timer
  static uint8_t step = 0;
  static uint8_t cnt = 0;

  if(step >= 122){

    if( cnt ) cnt = 0;
    else cnt = 1;

    step = 0;

    if( time_left && status == 1 ) time_left--;

  }
  else{
    
    step++;

  }

  if( !time_left && !cnt ){

    show = 0;
    disp_off();    

  }
  else show = 1;

}


Was ist der Sinn von cnt? Ich glaube herausgelesen zu haben, dass du 
damit die ISR Frequenz halbieren möchtest, also nur bei jedem 2.ten 
Aufruf etwas tun willst. Richtig?

Warum nicht so
ISR(TIMER0_OVF_vect)
{
  // Zeit-Timer
  static uint8_t step = 0;
  static uint8_t cnt = 0;

  cnt = 1 - cnt;

  if( cnt == 0 )  // nur bei jedem 2.ten Aufruf wird überhaupt etwas getan
    return;

  step++;
  if( step >= 122 ) {
    step = 0;

    if( time_left != 0 && status == 1 )
      time_left--;

    show = 1;
    if( time_left == 0 ) {
      show = 0;
      disp_off();    
    }
  }
}

Deine ganze Verwendung von 'status' und 'show' und was weiß ich was es 
da noch so alles gibt, ist mir auch nicht ganz koscher. Warum da in der 
ISR der status überhaupt abgefragt wird ... ich habs auf die Schnelle 
nicht rausgefunden.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Das Hauptproblem dürfte sein, dass deine ISR-Main globalen Variablen 
alle nicht volatile sind.

Damit ist

   if ( btn_check(&BTN_PORT, BTN_UP) ) time_left = 10;

wirklungslos. Die Änderung wird wahrscheinlich in der ISR nie ankommen.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wenn
    if( !time_left && status == 1 ) status = 0;

Die Zeit abgelaufen ist und status auf 1 ist ....

(Bitte, bitte, bitte. Benutze für Zählvariablen kein logisches !. Sei 
explizit:
    if( time_left == 0 && status == 1 ) status = 0;
dann braucht man nicht um die Ecke denken
)

.... dann wird status soweit ich das gesehen habe, nie wieder auf 1 
gesetzt. Noch nicht mal dann, wenn du den Button drückst

Offenbar wird 'status' als eine Art 'Die Uhr läuft' Variable benutzt.
Die brauchst du aber im Grunde nicht.
Entweder time_left ist ungleich 0, dann läuft die Uhr. Oder aber 
time_left ist gleich 0, dann läuft die Uhr nicht. Und schon hast du 
wieder ein Flag wegrationalisiert, das nur Gefahr läuft, nicht mit dem 
time_left konsistent zu sein.

Edit: Selbiges für das show-Flag

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hier
#define BTN_PORT PORTD

Du musst das PIN Register abfragen, nicht das Port Register

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Mal etwas zum studieren.

Bitte beachten:
Da ich die reale Hardware nicht hier habe, muss ich blind programmieren. 
Ich hab zwar versucht im Simulator das meiste zu fixen, aber 100% 
sicher, ob das auf der Hardware läuft bin ich natürlich nicht.

Auch: Für deine Tastenabfragen musst du dir noch was besseres suchen. 
Such in der Artikelsammlung nach "Entprellung". Dort findest du 
Komfortroutinen.

Ansonsten:
Sieh dir an und versuch zu verstehen, wie ich die 7-Segmentanzeige 
multiplexe. Es gibt eine Funktion dafür, die nur dafür zuständig ist, 
reihum immer 1 Anzeige einzuschalten und mit dem richtigen Muster zu 
beschicken. Dynamik bekommt das ganze dadurch, dass diese Funktion aus 
dem Timer Interrupt immer wieder aufgerufen wird.
Dadurch brauchst du dich im restlichen Programm nicht mehr um die 
7-Segment kümmern. Alles was du in die Variablen DigitCode[0..3] 
hineinschreibst, landet magisch auf der Anzeige.

// TIMER ATtiny 2313
// Rev. 0.1
// by Hannes Eilers


#define F_CPU 8000000UL                                // CPU-Geschwindigkeit

#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/eeprom.h>
#include <util/delay.h>

#ifndef TRUE
#define TRUE  1
#define FALSE 0
#endif

//
// LED
//
#define LED_PORT  PORTA
#define LED_DDR   DDRA
#define LED       PA0

//
// Relais
//
#define RL_PORT   PORTA
#define RL_DDR    DDRA
#define RL        PA1

//
// Buttons
//
#define BTN_PORT  PIND
#define BTN_DDR   DDRD

#define BTN_UP    PD0
#define BTN_DOWN  PD1
#define BTN_START PD2

//
// 7-Segment
//
#define DISP_CNTRL_PORT PORTD
#define DISP_CNTRL_DDR  DDRD
#define DISP1           PD6
#define DISP2           PD5
#define DISP3           PD4
#define DISP_PORT       PORTB
#define DISP_DDR        DDRB

uint8_t SegCodes[] = {
  0b00010001,   // 0
  0b01111101,   // 1
  0b00100011,   // 2
  0b00101001,   // 3
  0b01001101,   // 4
  0b10001001,   // 5
  0b10001001,   // 6
  0b00111101,   // 7
  0b00000001,   // 8
  0b00001001,   // 9
};

volatile uint8_t DigitCode[3] =             // Das Bitmuster für die 7-Seg Datenleitungen
   { 0xFF, 0xFF, 0xFF };
volatile uint8_t DigitControlCode[3] =      // welcher Pin muss auf Low für welche Anzeige
   { 1<<DISP1, 1<<DISP2, 1<<DISP3 };
uint8_t          actualDigit = 0;           // welche Anzeige ist zur Zeit aktiv


//************************************************************************************
//  Hilfsfunktionen: Buttons

uint8_t btn_check( volatile uint8_t *port, uint8_t pin )
{
  return ((*port & (1 << pin)) == 0 );
}

//************************************************************************************
// Hilfsfunktionen: Display

void disp_off()
{
  DigitCode[0] = DigitCode[1] = DigitCode[2] = 0xFF;
}

//
// Funktion: Multiplex
// Diese Funktion muss regelmässig aufgerufen werden, zb in einem Timer Interrupt
// Sie schaltet nacheinander die 7-Segment Codes aus DigitCode auf die einzelnen
// Anzeigeelemente durch.
//
// Um eine Ausgabe zu machen, genügt es daher, die gewünschte Segmentbelegung in
// DigitCode zu hinterlassen
//
void disp_multiplex()
{
  // zur Zeit aktive Anzeige abschalten
  DISP_CNTRL_PORT &= ~DigitControlCode[actualDigit];

  // weiterschalten zur nächsten
  actualDigit++;
  if( actualDigit == 3 )
    actualDigit = 0;

  // das Bitmuster an die Datenleitungen
  DISP_PORT = DigitCode[actualDigit];

  // und die Anzeige einschalten
  DISP_CNTRL_PORT |= DigitControlCode[actualDigit];
}

void disp_i( int16_t number )
{
  DigitCode[2] = SegCodes[ number % 10 ];
  number = number / 10;
  DigitCode[1] = SegCodes[ number % 10 ];
  number = number / 10;
  DigitCode[0] = SegCodes[ number ];
}

//************************************************************************************
// Hilfsfunktionen: Timer

volatile uint16_t time_left = 0;
volatile uint8_t  subTick = 0;
volatile uint8_t  time_counting = FALSE;

ISR( TIMER0_OVF_vect )
{
  disp_multiplex();     // Anzeige updaten

  if(subTick >= 122) {
    subTick = 0;

    if( time_counting && time_left > 0 )
      time_left--;
    disp_i( time_left );
  }
}

int main()
{
  LED_DDR |= (1 << LED);
  RL_DDR |= (1 << RL);

  DISP_CNTRL_DDR = ( 1 << DISP1 ) | ( 1 << DISP2 ) | ( 1 << DISP3 );  // Control Leitungen
  DISP_DDR       = 0xFF;                                              // Daten Leitungen

  BTN_DDR &= ~( (1 << BTN_UP) | (1 << BTN_DOWN) | (1 << BTN_START) );

  TIMSK = (1 << TOIE0);
  TCCR0B = (1 << CS02);  // Prescaler 256 -> 122,07Hz Timer


  disp_i( time_left );

  sei();

  while(1) {

    if( btn_check( &BTN_PORT, BTN_UP ) ) {
      cli();
      if( time_left < 999 - 10 )
        time_left += 10;
      else
        time_left = 999;
      disp_i( time_left );
      sei();
      _delay_ms( 200 );
    }

    if( btn_check( &BTN_PORT, BTN_DOWN ) ) {
      cli();
      if( time_left > 0 )
        time_left -= 10;
      else
        time_left = 0;
      disp_i( time_left );
      sei();
      _delay_ms( 200 );
    }

    if( btn_check( &BTN_PORT, BTN_START ) ) {
      time_counting = 1;
    }

    if( time_left == 0 )
      time_counting = FALSE;

    // LED & RELAIS ----------------------------------------
    if( time_left == 0 ) {
      LED_PORT &= ~(1 << LED);
      RL_PORT  &= ~(1 << RL);
    }
    else {
      LED_PORT |= (1 << LED);
      RL_PORT  |= (1 << RL);
    }
  }
}

Autor: Hannes E. (k1ngarthur) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Danke für die Antworten.
Ich werde mir das mal in ruhe anschauen.

Autor: Hannes E. (k1ngarthur) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Dein Code funktioniert super!
Danke schön.

Ledeglich die Anzeige flackert ein wenig und wenn die Zeit abgelaufen 
ist, blinkt die Anzeige nicht mehr, aber das sollte ich hinbekommen :-)

Vielen Dank für die Mühe!

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hannes E. schrieb:
> Dein Code funktioniert super!
> Danke schön.
>
> Ledeglich die Anzeige flackert ein wenig

Das kann schon sein.
Die ISR wird 122 mal in der Sekunde aufgerufen.
Es sind 4 7-Segment Anzeigen.
D.h. jede 7-Segment geht in der Sekunde 122/4, also ungefähr 30 mal 
ein/aus.

30Hz ist ein bischen wenig für eine 7-Segment. Da sieht man das noch 
flackern

ISR Frequenz hochziehen!

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.