Forum: Mikrocontroller und Digitale Elektronik Taster abfragen?


von Hannes E. (k1ngarthur) Benutzerseite


Angehängte Dateien:

Lesenswert?

Moin,

ich habe drei Taster an meinem ATtiny 2313 angeschlossen (Low-Active) 
und frage den Status eines Taster mit folg. Funktion ab:
1
uint8_t btn_check(volatile uint8_t *port, uint8_t pin){
2
3
  if( !(*port & (1 << pin)) ){
4
5
    return 1;
6
7
  }
8
9
  return 0;
10
11
}

eine Abfrage mit wie z.B.:
1
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?

von Karl H. (kbuchegg)


Lesenswert?

Nicht mit dem bischen Code.

von Hannes E. (k1ngarthur) Benutzerseite


Lesenswert?

Ok,

dann mal alles was relevant ist:
1
  // Ports initialisieren
2
  DISP_CNTRL_DDR |= (1 << DISP1) | (1 << DISP2) | (1 << DISP3);
3
  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.
1
  if( disp == 1 ) DISP_CNTRL_PORT = (1 << DISP1);
2
  else if( disp == 2) DISP_CNTRL_PORT = (1 << DISP2);
3
  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):
1
void disp_off(){
2
3
  // Complete Display off
4
  DISP_CNTRL_PORT &= ~(1 << DISP1) | (1 << DISP2) | (1 << DISP3);
5
6
}

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

von Karl H. (kbuchegg)


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?

von Karl H. (kbuchegg)


Lesenswert?

Das hier
1
void disp_off(){
2
3
  // Complete Display off
4
  DISP_CNTRL_PORT &= ~(1 << DISP1) | (1 << DISP2) | (1 << DISP3);
5
6
}

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

von Karl H. (kbuchegg)


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)

von Michi (Gast)


Lesenswert?

1
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:
1
BTN_DDR &= ~((1 << BTN_UP) | (1 << BTN_DOWN) | (1 << BTN_START));

von Hannes E. (k1ngarthur) Benutzerseite


Angehängte Dateien:

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.

von Klaus (Gast)


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?

von Gast 4711 (Gast)


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.

von Hannes E. (k1ngarthur) Benutzerseite


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.
1
  if( disp == 1 ){
2
    DISP_CNTRL_PORT |= (1 << DISP1);
3
    DISP_CNTRL_PORT &= ~( (1 << DISP2) | (1 << DISP3) );
4
  }
5
  else if( disp == 2){
6
    DISP_CNTRL_PORT |= (1 << DISP2);
7
    DISP_CNTRL_PORT &= ~( (1 << DISP1) | (1 << DISP3) );
8
  }
9
  else{
10
    DISP_CNTRL_PORT |= (1 << DISP3);
11
    DISP_CNTRL_PORT &= ~( (1 << DISP1) | (1 << DISP2) );
12
  }

Das Problem ist damit aber noch nicht verschwunden.

von Stephan S. (plonk)


Lesenswert?

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

von Hannes E. (k1ngarthur) Benutzerseite


Lesenswert?

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

von Karl H. (kbuchegg)


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
1
ISR(TIMER0_OVF_vect){
2
3
  // Zeit-Timer
4
  static uint8_t step = 0;
5
  static uint8_t cnt = 0;
6
7
  if(step >= 122){
8
9
    if( cnt ) cnt = 0;
10
    else cnt = 1;
11
12
    step = 0;
13
14
    if( time_left && status == 1 ) time_left--;
15
16
  }
17
  else{
18
    
19
    step++;
20
21
  }
22
23
  if( !time_left && !cnt ){
24
25
    show = 0;
26
    disp_off();    
27
28
  }
29
  else show = 1;
30
31
}

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
1
ISR(TIMER0_OVF_vect)
2
{
3
  // Zeit-Timer
4
  static uint8_t step = 0;
5
  static uint8_t cnt = 0;
6
7
  cnt = 1 - cnt;
8
9
  if( cnt == 0 )  // nur bei jedem 2.ten Aufruf wird überhaupt etwas getan
10
    return;
11
12
  step++;
13
  if( step >= 122 ) {
14
    step = 0;
15
16
    if( time_left != 0 && status == 1 )
17
      time_left--;
18
19
    show = 1;
20
    if( time_left == 0 ) {
21
      show = 0;
22
      disp_off();    
23
    }
24
  }
25
}

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.

von Karl H. (kbuchegg)


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.

von Karl H. (kbuchegg)


Lesenswert?

Wenn
1
    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:
1
    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

von Karl H. (kbuchegg)


Lesenswert?

Hier
1
#define BTN_PORT PORTD

Du musst das PIN Register abfragen, nicht das Port Register

von Karl H. (kbuchegg)


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.
1
// TIMER ATtiny 2313
2
// Rev. 0.1
3
// by Hannes Eilers
4
5
6
#define F_CPU 8000000UL                                // CPU-Geschwindigkeit
7
8
#include <stdint.h>
9
#include <stdio.h>
10
#include <stdlib.h>
11
#include <avr/io.h>
12
#include <avr/interrupt.h>
13
#include <avr/eeprom.h>
14
#include <util/delay.h>
15
16
#ifndef TRUE
17
#define TRUE  1
18
#define FALSE 0
19
#endif
20
21
//
22
// LED
23
//
24
#define LED_PORT  PORTA
25
#define LED_DDR   DDRA
26
#define LED       PA0
27
28
//
29
// Relais
30
//
31
#define RL_PORT   PORTA
32
#define RL_DDR    DDRA
33
#define RL        PA1
34
35
//
36
// Buttons
37
//
38
#define BTN_PORT  PIND
39
#define BTN_DDR   DDRD
40
41
#define BTN_UP    PD0
42
#define BTN_DOWN  PD1
43
#define BTN_START PD2
44
45
//
46
// 7-Segment
47
//
48
#define DISP_CNTRL_PORT PORTD
49
#define DISP_CNTRL_DDR  DDRD
50
#define DISP1           PD6
51
#define DISP2           PD5
52
#define DISP3           PD4
53
#define DISP_PORT       PORTB
54
#define DISP_DDR        DDRB
55
56
uint8_t SegCodes[] = {
57
  0b00010001,   // 0
58
  0b01111101,   // 1
59
  0b00100011,   // 2
60
  0b00101001,   // 3
61
  0b01001101,   // 4
62
  0b10001001,   // 5
63
  0b10001001,   // 6
64
  0b00111101,   // 7
65
  0b00000001,   // 8
66
  0b00001001,   // 9
67
};
68
69
volatile uint8_t DigitCode[3] =             // Das Bitmuster für die 7-Seg Datenleitungen
70
   { 0xFF, 0xFF, 0xFF };
71
volatile uint8_t DigitControlCode[3] =      // welcher Pin muss auf Low für welche Anzeige
72
   { 1<<DISP1, 1<<DISP2, 1<<DISP3 };
73
uint8_t          actualDigit = 0;           // welche Anzeige ist zur Zeit aktiv
74
75
76
//************************************************************************************
77
//  Hilfsfunktionen: Buttons
78
79
uint8_t btn_check( volatile uint8_t *port, uint8_t pin )
80
{
81
  return ((*port & (1 << pin)) == 0 );
82
}
83
84
//************************************************************************************
85
// Hilfsfunktionen: Display
86
87
void disp_off()
88
{
89
  DigitCode[0] = DigitCode[1] = DigitCode[2] = 0xFF;
90
}
91
92
//
93
// Funktion: Multiplex
94
// Diese Funktion muss regelmässig aufgerufen werden, zb in einem Timer Interrupt
95
// Sie schaltet nacheinander die 7-Segment Codes aus DigitCode auf die einzelnen
96
// Anzeigeelemente durch.
97
//
98
// Um eine Ausgabe zu machen, genügt es daher, die gewünschte Segmentbelegung in
99
// DigitCode zu hinterlassen
100
//
101
void disp_multiplex()
102
{
103
  // zur Zeit aktive Anzeige abschalten
104
  DISP_CNTRL_PORT &= ~DigitControlCode[actualDigit];
105
106
  // weiterschalten zur nächsten
107
  actualDigit++;
108
  if( actualDigit == 3 )
109
    actualDigit = 0;
110
111
  // das Bitmuster an die Datenleitungen
112
  DISP_PORT = DigitCode[actualDigit];
113
114
  // und die Anzeige einschalten
115
  DISP_CNTRL_PORT |= DigitControlCode[actualDigit];
116
}
117
118
void disp_i( int16_t number )
119
{
120
  DigitCode[2] = SegCodes[ number % 10 ];
121
  number = number / 10;
122
  DigitCode[1] = SegCodes[ number % 10 ];
123
  number = number / 10;
124
  DigitCode[0] = SegCodes[ number ];
125
}
126
127
//************************************************************************************
128
// Hilfsfunktionen: Timer
129
130
volatile uint16_t time_left = 0;
131
volatile uint8_t  subTick = 0;
132
volatile uint8_t  time_counting = FALSE;
133
134
ISR( TIMER0_OVF_vect )
135
{
136
  disp_multiplex();     // Anzeige updaten
137
138
  if(subTick >= 122) {
139
    subTick = 0;
140
141
    if( time_counting && time_left > 0 )
142
      time_left--;
143
    disp_i( time_left );
144
  }
145
}
146
147
int main()
148
{
149
  LED_DDR |= (1 << LED);
150
  RL_DDR |= (1 << RL);
151
152
  DISP_CNTRL_DDR = ( 1 << DISP1 ) | ( 1 << DISP2 ) | ( 1 << DISP3 );  // Control Leitungen
153
  DISP_DDR       = 0xFF;                                              // Daten Leitungen
154
155
  BTN_DDR &= ~( (1 << BTN_UP) | (1 << BTN_DOWN) | (1 << BTN_START) );
156
157
  TIMSK = (1 << TOIE0);
158
  TCCR0B = (1 << CS02);  // Prescaler 256 -> 122,07Hz Timer
159
160
161
  disp_i( time_left );
162
163
  sei();
164
165
  while(1) {
166
167
    if( btn_check( &BTN_PORT, BTN_UP ) ) {
168
      cli();
169
      if( time_left < 999 - 10 )
170
        time_left += 10;
171
      else
172
        time_left = 999;
173
      disp_i( time_left );
174
      sei();
175
      _delay_ms( 200 );
176
    }
177
178
    if( btn_check( &BTN_PORT, BTN_DOWN ) ) {
179
      cli();
180
      if( time_left > 0 )
181
        time_left -= 10;
182
      else
183
        time_left = 0;
184
      disp_i( time_left );
185
      sei();
186
      _delay_ms( 200 );
187
    }
188
189
    if( btn_check( &BTN_PORT, BTN_START ) ) {
190
      time_counting = 1;
191
    }
192
193
    if( time_left == 0 )
194
      time_counting = FALSE;
195
196
    // LED & RELAIS ----------------------------------------
197
    if( time_left == 0 ) {
198
      LED_PORT &= ~(1 << LED);
199
      RL_PORT  &= ~(1 << RL);
200
    }
201
    else {
202
      LED_PORT |= (1 << LED);
203
      RL_PORT  |= (1 << RL);
204
    }
205
  }
206
}

von Hannes E. (k1ngarthur) Benutzerseite


Lesenswert?

Danke für die Antworten.
Ich werde mir das mal in ruhe anschauen.

von Hannes E. (k1ngarthur) Benutzerseite


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!

von Karl H. (kbuchegg)


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!

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.