Forum: Mikrocontroller und Digitale Elektronik AtMega168 friert ein


von Richard B. (rbrose)


Angehängte Dateien:

Lesenswert?

Hallo zusammen,

hab ein kommisches Problem.
Ich benutze die Schaltung (Siehe Attachemend) für eine Auswertung des 
Keypads.
PC0 ist als PCINT definiert.
Der Interrupt wird auch super ausgefürht. Doch leider friert mir der AVR 
ein wenn ich die Taste "*" drücke oder manchmal auch die Taste "2" und 
"0" drücke.

Hat einer eine Idee was das Problem sein könnte?

Hier der Gesammte Code:
1
/*
2
#  Autor: Richard Brose
3
#   Title: Keypad 3x4 Matrix V0.1
4
#   Class: main
5
*/
6
7
8
#ifndef F_CPU
9
  #warning "F_CPU war noch nicht definiert, wird nun mit 8000000 definiert"
10
#define F_CPU 8000000UL     /* Quarz mit 8.0000 Mhz */
11
#endif
12
#include <util/delay.h>     /* in älteren avr-libc Versionen <avr/delay.h> */ 
13
14
#include <avr/io.h>
15
#include <stdlib.h>
16
#include <avr/pgmspace.h>
17
#include <avr/interrupt.h>
18
#include "uart.h"
19
20
21
volatile uint8_t einer=0;
22
volatile uint8_t zweier=0;
23
volatile uint8_t dreier=0;
24
25
volatile uint16_t adcval;
26
27
28
void long_delay(uint16_t ms) {
29
    for(; ms>0; ms--) _delay_ms(1);
30
}
31
32
33
/* Einfache Funktion zum Entprellen eines Tasters */
34
inline uint8_t debounce(volatile uint8_t *port, uint8_t pin)
35
{
36
    if ( ! (*port & (1 << pin)) )
37
    {
38
        /* Pin wurde auf Masse gezogen, 100ms warten   */
39
        _delay_ms(50);  // max. 262.1 ms / F_CPU in MHz
40
        _delay_ms(50); 
41
        if ( *port & (1 << pin) )
42
        {
43
            /* Anwender Zeit zum Loslassen des Tasters geben */
44
            _delay_ms(50);
45
            _delay_ms(50); 
46
            return 1;
47
        }
48
    }
49
    return 0;
50
}
51
52
53
uint16_t ReadChannel(uint8_t mux)
54
{
55
  uint8_t i;
56
  uint16_t result;
57
 
58
  ADCSRA = (1<<ADEN) | (1<<ADPS2) | (1<<ADPS1);    // Frequenzvorteiler 
59
                               // setzen auf 8 (1) und ADC aktivieren (1)
60
 
61
  ADMUX = mux;                      // Kanal waehlen
62
  ADMUX |= (1<<REFS0); // AVCC als Referenz
63
 
64
  /* nach Aktivieren des ADC wird ein "Dummy-Readout" empfohlen, man liest
65
     also einen Wert und verwirft diesen, um den ADC "warmlaufen zu lassen" */
66
  ADCSRA |= (1<<ADSC);              // eine ADC-Wandlung 
67
  while ( ADCSRA & (1<<ADSC) ) {
68
     ;     // auf Abschluss der Konvertierung warten 
69
  }
70
  result = ADCW;  // ADCW muss einmal gelesen werden,
71
                  // sonst wird Ergebnis der nächsten Wandlung
72
                  // nicht übernommen.
73
 
74
  /* Eigentliche Messung - Mittelwert aus 4 aufeinanderfolgenden Wandlungen */
75
  result = 0; 
76
  for( i=0; i<4; i++ )
77
  {
78
    ADCSRA |= (1<<ADSC);            // eine Wandlung "single conversion"
79
    while ( ADCSRA & (1<<ADSC) ) {
80
      ;   // auf Abschluss der Konvertierung warten
81
    }
82
    result += ADCW;        // Wandlungsergebnisse aufaddieren
83
  }
84
  ADCSRA &= ~(1<<ADEN);             // ADC deaktivieren (2)
85
 
86
  result /= 4;                     // Summe durch vier teilen = arithm. Mittelwert
87
 
88
  return result;
89
}
90
91
92
ISR( SIG_PIN_CHANGE1 )
93
{
94
95
  if( PINC & (1<<PINC0 ))
96
  {
97
    PORTB ^= ( 1 << PB0 );
98
99
    DDRD |= (1<<PD2);
100
    PORTD &= ~(1<<PD2);
101
102
    adcval = ReadChannel(0);
103
104
    char buffer[7];
105
    itoa(adcval, buffer,10);
106
    uart_puts(buffer);
107
108
    DDRD &= ~(1<<PD2);
109
    PORTD &= ~(1<<PD2);
110
111
  }
112
}
113
114
 
115
int main(void) 
116
{
117
  uart_init(UART_BAUD_SELECT(19200, F_CPU));
118
     
119
120
  PCICR |= (1<<PCIE1);  
121
    PCMSK1 |= (1<<PCINT8);
122
  
123
  DDRB = 0xFF;
124
   PORTB = 0x00; 
125
126
  DDRD &= ~(1<<PD2);
127
  PORTD &= ~(1<<PD2);
128
129
  sei();
130
131
132
  uart_puts_P("Sender laeuft !\n");
133
134
   while(1) 
135
   {        
136
    long_delay(1000);  
137
    PORTB ^= ( 1 << PB1 );
138
139
  /*  if( !(PINC & (1<<PINC0)) )
140
    {
141
      PORTB ^= ( 1 << PB0 );
142
    } */
143
144
   }
145
146
 return 1;
147
}


Vielen Dank für eure Hilfe!

von Falk B. (falk)


Lesenswert?

@ Richard B. (rbrose)

>Ich benutze die Schaltung (Siehe Attachemend) für eine Auswertung des
>Keypads.

Hatten wir das nciht in einem anderen thread ausreichend geklärt?
Ausserdem solltest du cich über Netiquette informieren. Lange 
Quelltexte gehören in den Anhang.

>Der Interrupt wird auch super ausgefürht. Doch leider friert mir der AVR
>ein wenn ich die Taste "*" drücke oder manchmal auch die Taste "2" und
>"0" drücke.

Du greifst sowohl im Main als auch im Interrupt auf die Ports zu, 
das geht schief. Siehe Link, Atomarer Zugriff.

MfG
Falk

von Richard B. (rbrose)


Lesenswert?

Falk Brunner wrote:
> @ Richard B. (rbrose)
>
>>Ich benutze die Schaltung (Siehe Attachemend) für eine Auswertung des
>>Keypads.
>
> Hatten wir das nciht in einem anderen thread ausreichend geklärt?

Das Problem hatte ich noch nicht. Es ging nicht um das Problem.

> Ausserdem solltest du cich über Netiquette informieren. Lange
> Quelltexte gehören in den Anhang.
>

Hatte eine JPG-Datei schon als Anhang. Wollte nicht 2 Post drauf machen.
Werde das in Zukunft beherzigen.



>>Der Interrupt wird auch super ausgefürht. Doch leider friert mir der AVR
>>ein wenn ich die Taste "*" drücke oder manchmal auch die Taste "2" und
>>"0" drücke.
>
> Du greifst sowohl im Main als auch im Interrupt auf die Ports zu,
> das geht schief. Siehe Link, Atomarer Zugriff.
>

Leider war es nicht das Problem :-(
Habe die Main in folgendes geändert:
1
int main(void) 
2
{
3
  uart_init(UART_BAUD_SELECT(19200, F_CPU));
4
     
5
6
  PCICR |= (1<<PCIE1);  
7
    PCMSK1 |= (1<<PCINT8);
8
  
9
  DDRB = 0xFF;
10
   PORTB = 0xFF; 
11
12
  DDRD &= ~(1<<PD2);
13
  PORTD &= ~(1<<PD2);
14
15
  sei();
16
17
18
  uart_puts_P("Sender laeuft !\n");
19
20
   while(1) 
21
   {        
22
23
   }
24
25
 return 1;
26
}

Das Problem ist ja das der AVR bei der Taste "*" immer einfriert.

von Falk B. (falk)


Lesenswert?

Hier passt was nicht. Ersten feht das Setzen von PD2, ausserdem misst du 
auf der falschen Flanke. Denn beim Tastendruck wird eine FALLENDE Flanke 
ausgelöst. IMMER!
1
ISR( SIG_PIN_CHANGE1 )
2
{
3
4
  if( !(PINC & (1<<PINC0 )))    // fallende Flanke
5
  {
6
    PORTB ^= ( 1 << PB0 );
7
8
    DDRD |= (1<<PD2);
9
   // PORTD &= ~(1<<PD2);    // falsch, du musst ja PD2 SETZEN
10
    PORTD |= (1<<PD2);
11
    
12
    adcval = ReadChannel(0);
13
14
    char buffer[7];
15
    itoa(adcval, buffer,10);
16
    uart_puts(buffer);
17
18
    DDRD &= ~(1<<PD2);
19
    PORTD &= ~(1<<PD2);
20
21
  }
22
}

MfG
Falk

von Richard B. (rbrose)


Lesenswert?

Hab folgendes gemessen: An PIN PD2 liegt bei jeder Taste die ich drücke 
kleiner als 1V Spannung an. Wenn ich aber Taste "*" Drücke wird V5,08 
angezeigt. Ist das Normal`? Kann das, dass Problem sein?

von Richard B. (rbrose)


Lesenswert?

@Falk

Wenn ich PORTD &= ~(1<<PD2); in PORTD |= (1<<PD2); ändere, ist egal 
welche Taste ich drücke, friert der AVR sofort ein.

von Richard B. (rbrose)


Lesenswert?

Wenn ich es so mache friert er nicht ein egal welche Taste .. sogar 
nicht bei "*" aber der ADC-Wert ist immer 1023.
1
ISR( SIG_PIN_CHANGE1 )
2
{
3
4
  if( PINC & (1<<PINC0 ))    // <-- friert nicht ein!! be 
5
  {
6
    PORTB ^= ( 1 << PB0 );
7
8
    DDRD |= (1<<PD2);
9
   // PORTD &= ~(1<<PD2);    // falsch, du musst ja PD2 SETZEN
10
    PORTD |= (1<<PD2);
11
    
12
    adcval = ReadChannel(0);
13
14
    char buffer[7];
15
    itoa(adcval, buffer,10);
16
    uart_puts(buffer);
17
18
    DDRD &= ~(1<<PD2);
19
    PORTD &= ~(1<<PD2);
20
21
  }
22
}

Wenn ich aber wieder auf if( !(PINC & (1<<PINC0 )))  und PORTD |= 
(1<<PD2); lasse friert es sofort beim tasten druc ein.

von Falk B. (falk)


Lesenswert?

@ Richard B. (rbrose)

>angezeigt. Ist das Normal`? Kann das, dass Problem sein?

Nein, das ist nciht normal. Und das ist scheinbar das Problem. Sieht so 
aus al ob die taste defekt wäre.

>Wenn ich PORTD &= ~(1<<PD2); in PORTD |= (1<<PD2); ändere, ist egal
>welche Taste ich drücke, friert der AVR sofort ein.

Dann mach mal die doofe if Abfrage im Interrupt raus. PD2 MUSS auf HIGH 
gesetzt werden.

MFG
Falk

von Richard B. (rbrose)


Lesenswert?

Wenn ich die if-abfrage ganz weglasse und PORTD |= (1<<PD2); lasse .. 
friert es ein. Er friert als immer bei FALLENDE Flanke ein. Sehr 
komisch.

von Falk B. (falk)


Lesenswert?

@  Richard B. (rbrose)

>Wenn ich die if-abfrage ganz weglasse und PORTD |= (1<<PD2); lasse ..
>friert es ein. Er friert als immer bei FALLENDE Flanke ein. Sehr
>komisch.

Pack mal die Routine in deine while-Schleife und schalte die Interrupts 
aus. Ich fürchte das kann man nicht mit Pin-Change Interrupt machen, 
weil die Spannung nicht immer niedrig genug ist, um die Schaltschwellen 
zu erreichen. Es ist sicher besser, die Tasten per Timer zyklisch zu 
pollen.

MfG
Falk

von Richard B. (rbrose)


Lesenswert?

ok ich teste es mal. Aber ich glaube ich weiss warum bei Taste "*" ich 
5V kriege ... weil alle Widerstände zusammen 12,4kOhm sind und die 
10kOhm sind ja weniger ... deshalb geht der Strom da drüber weil PD2 ja 
5V dann ist oder?

von Falk B. (falk)


Lesenswert?

@  Richard B. (rbrose)

>ok ich teste es mal. Aber ich glaube ich weiss warum bei Taste "*" ich
>5V kriege ... weil alle Widerstände zusammen 12,4kOhm sind und die
>10kOhm sind ja weniger ... deshalb geht der Strom da drüber weil PD2 ja
>5V dann ist oder?

???
Das ist ein Spannungsteiler! Der spukt maximal Vcc aus. Und wenn er 
das tut, dann ist KEINE Taste gedrückt.

MFG
Falk

von Richard B. (rbrose)


Lesenswert?

Und was bezwegt das Schalten von PD2? Wieso wird es zwischen Eingang und 
Ausgang geschaltet?

von Falk B. (falk)


Lesenswert?

@ Richard B. (rbrose)

>Und was bezwegt das Schalten von PD2? Wieso wird es zwischen Eingang und
>Ausgang geschaltet?

Das musst DU doch wissen! War das nicht mal aus Energiespargründen 
gemacht worden? Im Normalfall ist PD2 immer HIGH und Ausgang, eigentlich 
kann man den Widerstand direkt an Vcc hängen.

MfG
Falk

von STK500-Besitzer (Gast)


Lesenswert?

Ich vermute, dass das Umschalten des PD2 dafür sorgt, dass man man den 
Pin PC0 auch noch als PCINT-Pin benutzen kann und etwas Strom spart.

Im Normalzustand ist PD2 hochohmig (Eingang, Pull-Up ausgeschaltet).
Dann sorgt ein Tastendruck dafür, dass PC0 auf GND-Pegel gezogen wird.
Dadurch wird die ISR angesprungen, in der PD2 zum Ausgang mit H-Pegel 
wird.
Dadurch fällt R6 nicht mehr ins Gewicht und man hat eine ADC-Tastatur.
PC0 wird jetzt als ADC-Eingang benutzt.
Sobald die ADC-Wandlung abgeschlossen ist, wird die Tastatur bzw. das 
Widerstandsnetzwerk wieder zu einem PCINT-Trigger gemacht.
Fertig ist die Laube...

>bezwegt

Nach 5-10maligem Lesen habe ich dann auch verstanden, dass "bezweckt" 
gemeint ist, was mit dem "Zweck" und nicht dem "Weg" zu tun hat.

von Falk B. (falk)


Lesenswert?

@ STK500-Besitzer (Gast)

>Ich vermute, dass das Umschalten des PD2 dafür sorgt, dass man man den
>Pin PC0 auch noch als PCINT-Pin benutzen kann und etwas Strom spart.

Schon klar, aber, das hat seine Tücken.

>Im Normalzustand ist PD2 hochohmig (Eingang, Pull-Up ausgeschaltet).
>Dann sorgt ein Tastendruck dafür, dass PC0 auf GND-Pegel gezogen wird.

Soweit, so gut.

>Dadurch wird die ISR angesprungen, in der PD2 zum Ausgang mit H-Pegel
>wird.
>Dadurch fällt R6 nicht mehr ins Gewicht und man hat eine ADC-Tastatur.
>PC0 wird jetzt als ADC-Eingang benutzt.
>Sobald die ADC-Wandlung abgeschlossen ist, wird die Tastatur bzw. das
>Widerstandsnetzwerk wieder zu einem PCINT-Trigger gemacht.
>Fertig ist die Laube...

Ja? Und was ist, wenn die Taste gedrückt bleibt, ewas sie im allegmeinen 
auch tut, schliesslich ist die Messung sehr kurz? Und was ist mit 
Prellen?
Alles heiss.

MfG
Falk

von Richard B. (rbrose)


Lesenswert?

hallo STK500-Besitzer,

hab die schaltung auch auf einem STK500 aufgebaut :-)

So funktioniert es auch wie du gesagt hast. Aber kannst du dir erklären 
wieso der AVR bei der Taste "*" oder manchmal auch "0" und "2" 
einfriert?

von Richard B. (rbrose)


Lesenswert?

@Falk Brunner
OK wenn das alles heiss ist ... was ist dein Vorschlag? Wie soll ich die 
Tastatur am besten auslesen? Nicht mit ADC?

von Falk B. (falk)


Lesenswert?

@ Richard B. (rbrose)

>OK wenn das alles heiss ist ... was ist dein Vorschlag? Wie soll ich die
>Tastatur am besten auslesen? Nicht mit ADC?

DOCH, aber nicht per Pin Change Interrupt sondern per Timer alle 
10..100ms. pack die Routone ohne das doofe if in einen Timerinterrupt 
und sie sollte prima laufen.

MFG
Falk

von STK500-Besitzer (Gast)


Lesenswert?

>Aber kannst du dir erklären wieso der AVR bei der Taste "*" oder manchmal >auch 
"0" und "2" einfriert?

Wo friert es denn?
Ich habe mir nicht den ganzen Thread (und das Programm) angeguckt.
Ich vermute ein Schaltungsproblem.

>Ja? Und was ist, wenn die Taste gedrückt bleibt, ewas sie im allegmeinen
auch tut, schliesslich ist die Messung sehr kurz?

Dann sollte das Programm bei nächster Gelegenheit wieder in die ISR 
springen.

>Und was ist mit Prellen?

Ich habe nie behauptet, dass das Progemm perfekt funktioniert. ;)


Ich würde den Controller schon per PCINT oder sowas "wecken" und dann 
mit der Standard-Methode des Polling per Timer-Interrupt die Tastatur 
auslesen.
Das könnte man dann solange machen, bis keine Taste über längere Zeit 
gedrückt wurde. Dann kann der Controller wieder Heia machen.

von Richard B. (rbrose)


Lesenswert?

Ja Richtig ... das hatte ich doch schon mal so und hat auch prima 
funktioniert. Aber da es eine Fernbedingung werden soll, meinte man mir 
das die Batterie schnell leergesaugt würde wenn der ADC jede 10..100ms 
misst.

Über wieviel mAh reden wir da?
Ist es viel mehr als wenn ich die Tastatur mit allen 7 Leitungen 
anschliesse in im timer alle pins durchtesten würde?

von Richard B. (rbrose)


Lesenswert?

"Standard-Methode des Polling"
Was ist die Standard-Methode? also nicht ADC?

von Falk B. (falk)


Lesenswert?

@ Richard B. (rbrose)

>Ja Richtig ... das hatte ich doch schon mal so und hat auch prima
>funktioniert. Aber da es eine Fernbedingung werden soll, meinte man mir
>das die Batterie schnell leergesaugt würde wenn der ADC jede 10..100ms
>misst.

Naja, kommt drauf an. Pin Change wäre in diesem Fall schon besser, 
stimmt schon.

>Über wieviel mAh reden wir da?

Rechne doch mal. Siehe Sleep Mode.

>Ist es viel mehr als wenn ich die Tastatur mit allen 7 Leitungen
>anschliesse in im timer alle pins durchtesten würde?

Nein. Du brauchst dann halt 7 IOs. Die Messung per ADC ist schon OK, 
aber irgendwie hakt das Ganze noch.

Und was passiert ohne die if Abfrage im interrupt? Dann darf sich der 
AVR eigentlich nicht aufhängen, nur halt viel "Müll" ausspucken.

MfG
Falk

von STK500-Besitzer (Gast)


Lesenswert?

Bei Atmel gibt es eine ApplicationNote, die genau dieses Thema 
behandelt:
"AVR240: 4x4 Keypad-Wake Up on Keypress"

Pdf:
http://www.atmel.com/dyn/resources/prod_documents/doc1232.pdf

Zip:
http://www.atmel.com/dyn/resources/prod_documents/AVR240.zip

von Falk B. (falk)


Lesenswert?

@ Richard B. (rbrose)

>"Standard-Methode des Polling"
>Was ist die Standard-Methode? also nicht ADC?

NEIN!

Polling oder Interrupt, das ist der Unterschied!

von Richard B. (Gast)


Lesenswert?

Ich habe eine neue Vermutung: Kann es daran liegen das die Taste "*" bei 
PC0 zwischen 2.4V und 2.5V ist und somit der PCINT sich nicht 
entscheiden kann ob es hight oder low ist. Und dadurch der Interrupt 
1000 mal ausgeführt wird wenn die Taste gedrückt wird?

Soll ich anstatt den 4.7kOhm, 3.3kOhm drantun?
Und kann ich im PCINT Interrupt cli() und sei() jeweils am Anfang und am 
Ende benutzen damit kein Interrupt gemacht wird bis der noch in dem 
Interrupt ist?

Was meint ihr?

von Peter D. (peda)


Lesenswert?

Richard B. wrote:
> Ich habe eine neue Vermutung: Kann es daran liegen das die Taste "*" bei
> PC0 zwischen 2.4V und 2.5V ist und somit der PCINT sich nicht
> entscheiden kann ob es hight oder low ist. Und dadurch der Interrupt
> 1000 mal ausgeführt wird wenn die Taste gedrückt wird?

Kann sein, aber dann hast Du das Prinzip noch immer nicht verstanden.
Im Sleep ist ja nur der 100k Pullup aktiv und damit liefert jede Taste 
einen richtigen Low-Pegel.

Der PCINT dient allein nur zum Aufwachen und dann wird er abgeschaltet.
Es ist also völlig wurscht, was dann für ein Analogpegel anliegt.

Ist die CPU aufgewacht, macht sie die ganz normale Tastenauswertung mit 
ADC und Timerinterrupt, vorzugsweise mit der 4-fach Entprellung wie in 
meinem Beispiel.

Erst wenn wieder erkannt wird, daß keine einzige Taste gedrückt ist, 
dann wird auf Sleep und PCINT umgeschaltet.

Das Sleep ist etwas tricky, damit auch wirklich kein Aufwachen 
fehlschlägt:
Zuerst müssen die Interrupts global disabled werden, dann den PCINT 
enablen, dann nochmal prüfen, ob der Pin wirklich high ist.

Und dann unmittelbar hintereinander SEI und SLEEP!

Es darf keine Möglichkeit bestehen, daß der Interrupt schon vor dem 
Sleep zuschlägt.
Und der SEI-Befehl bewirkt, daß genau der nächste Befehl noch unter 
Interruptsperre ausgeführt wird.

Der PCINT muß völig leer bleiben, er soll ja nur aufwachen und es geht 
dann nach dem Sleep-Befehl weiter.


> Soll ich anstatt den 4.7kOhm, 3.3kOhm drantun?

Nö.


Peter

von Richard B. (rbrose)


Lesenswert?

ASO, ich dachte ich habe den PCINT bei jedem Tasten Druck und so nur 
messe wenn einer Drückt.
Ich habe das ganze im Moment nur ohne Sleep getestet.

Aber es sollte ja auch ohne Sleep funktionieren oder?

Szenario:
PCINT(PC0) ist Aktiviert und PD2 ist als Eingang und Pullups aus(0).
Wenn eine Taste gedrückt wird, dann wird im PCINT Interrupt der PCINT 
Interrupt ausgeschaltet, PD2 wird zum Ausgang und High und ein Timer für 
die Tasten mit ADC gestartet. Im Timer wird auch eine Variable 
hochgezählt für die verflossene Zeit ... wenn ca nach 30 Sekunden keine 
Taste gedrückt wurde ... wird der PCINT wieder aktiviert, PD2 zum 
Eingang + Pullups aus(0) und der timer wird auch gestoppt.


Ist der Gedanke Richtig? Wenn das super funktioniert werde ich es mit 
Sleep versuchen.
@Peter
>"4-fach Entprellung"
Wo finde ich diesen Code? du hast viele Entprellung schon gemacht :-)

von Richard B. (rbrose)


Lesenswert?

abo

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.