Taster abfragen?

OP Persönliche Seite #1647737
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?
Angehängte Dateien:
OP Persönliche Seite #1647780
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.
#1647813
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)
Gast #1647816
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));
OP Persönliche Seite #1647910
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.
Angehängte Dateien:
Gast #1647918
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?
Gast #1647925
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.
OP Persönliche Seite #1647945
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.
#1647996
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.
#1648018
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
#1648101
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
}
#1648193
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

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren