Hallo Mikrocontroller Community!
Ich bin so langsam am verzweifeln...
Ich möchte ein Programm für einen Fernsehlifter schreiben, ich hatte das
Programm auch schon fast fertig, bis ich Probleme mit nicht korrekt
ausgeführten Objekten bekommen hatte.
Hier mein code:
1
#include<avr/io.h>
2
#include<stdint.h>
3
4
#ifndef F_CPU
5
#define F_CPU 3686400UL /* Quarz mit 3.6864 Mhz */
6
#endif
7
#include<util/delay.h>
8
9
10
11
uint8_tstellung;
12
13
chartaster(void);
14
uint8_ttastenwechsel(uint8_t);
15
16
17
18
19
intmain()
20
{
21
22
DDRB=(1<<PB3)|(1<<PB4);
23
24
uint8_ta;
25
uint8_tvtast;
26
27
28
while(1)
29
{
30
vtast=taster();
31
a=tastenwechsel(vtast);
32
33
if(a==0)
34
{
35
PORTB=(1<<PB3)|(0<<PB4);
36
}
37
38
if(a==1)
39
{
40
PORTB=(0<<PB3)|(1<<PB4);
41
}
42
43
}
44
}
45
46
47
48
chartaster(void)
49
{
50
staticunsignedcharzustand;
51
charrw=0;
52
53
if(zustand==0&&(PINB&(1<<PB1)))//Taster wird gedrueckt (steigende Flanke)
54
{
55
zustand=1;
56
PORTB^=(1<<PB7);
57
rw=1;
58
}
59
elseif(zustand==1&&(PINB&(1<<PB1)))//Taster wird gehalten
60
{
61
zustand=2;
62
rw=0;
63
}
64
elseif(zustand==2&&!(PINB&(1<<PB1)))//Taster wird losgelassen (fallende Flanke)
Ich habe den Code mal ein wenig vereinfacht, wegen der Übersicht.
Wenn ich das ganze nun auf einen Atinny 2313 überspiele, wird bei
mehrmaligen betätigen des Tasters PB1, der Ausgang PB3 ausgeschaltet und
dafür der Ausgang PB4 eingeschaltet.
Wie schaffe ich es, das jeder einzelne Tastendruck gut erkannt wird und
der Ausgang PB4 wider auf PB3 wechselt(was aber Wahrscheinlich daran
liegt, das die Tastendrücke nicht richtig erkannt werden)?
Bitte um Hilfe!
MfG Titan
DIe fehlende Entprellung ist ein Problem deines Codes. Du versuchst
zwar in taster() exakt einen Tastendruck zu erkennen. Das hilft aber nix
wenn eine Prellsequenz reinkommt. Die Prellsequenz kann von deiner
taster() als Folge von Tastendrücken erkannt werden mit den
entsprechenden Konsequenzen in tastenwechsel().
Hallo
Und diese Stelle würde ich nochmal überarbeiten:
PORTB = (1<<PB3) | (0<<PB4);
^
Da ist garantiert eine Differenz zwischen dem, was du vorhast und
dem, was ausgeführt wird.
Gruß
Joachim
XXX schrieb:> Hallo>> Und diese Stelle würde ich nochmal überarbeiten:>> PORTB = (1<<PB3) | (0<<PB4);> ^> Da ist garantiert eine Differenz zwischen dem, was du vorhast und> dem, was ausgeführt wir
In seinem Fall funktioniert es zwar so wie er das vorhatte, aber den
kompletten Port neu zu beschreiben ist nicht unbedingt die feine
englische Art. Da hast du schon recht.
PS:
Das hier
1
switch(stellung)
2
{
3
case0:stellung=1;break;
4
5
case1:stellung=0;break;
6
7
}
ist eine unheimlich komplizierte Variante von
1
stellung=1-stellung;
oder
1
stellung=stellung?0:1;
oder
1
if(stellung)
2
stellung=0;
3
else
4
stellung=1;
Noch ein 2.tes PS
In C ist weniger oft mehr. Wenn eine Variable nur 0 und 1 (also nicht 0)
sein kann, dann solltest du dir explizite Vergleiche in den if sparen
if(a == 0)
...
if(a == 1)
entweder die Variable ist 'nicht 0', dann heißt es
if( a )
oder sie ist 0, dann heißt es
if( !a )
und wenn eine Variable sowieso nur 0 oder nicht 0 sein kann, dann ist es
eine ziemlich schlechte Idee, 2 getrennte if dafür einzusetzen. Dafür
gibt es das else.
if( a ) // wenn a nicht 0
...
else // a war 0
...
oder eben anders rum
if( !a ) // wenn a gleich 0
...
else // a war nicht 0
...
Interessanterweise sind solche 'weniger ist mehr' Umformungen nämlich
auch oft Fehlerverhinderer.
und wenn du jetzt anstelle von a noch einen vernünftigen Variablennamen
benutzt, wie zb MoveUp
MoveUp = tastenwechsel(vtast);
dann steht da
if( MoveUp )
PORTB = (1<<PB3) | (0<<PB4);
else
PORTB = (0<<PB3) | (1<<PB4);
und das liest sich schon viel besser als deine Version mit dem a. Der
Code erzählt mir schon fast ganz von alleine, was da passiert: Wenn die
Liftrichtung nach oben ist (wegen MoveUp), dann machst du eine bestimmte
externe Schaltung und wenn sie es nicht ist (dann soll offenbar nach
Unten verfahren werden) dann machst du eine andere Verschaltung. Da
brauch ich den ganzen anderen Rest des Programmes nicht zu verstehen um
mit dem Schaltplan feststellen zu können: OK, kann stimmen oder kann
nicht stimmen.
Scheu dich nicht, Variablennamen auch im Nachhinein noch umzubennen,
wenn du draufkommst, dass sie schlecht gewählt sind. Einbuchstabige
Variablennamen solltest du ausschliesslich für Hilfsvariablen benutzen,
die keine spezielle Bedeutung haben. Sobald du aber für eine Variable
eine einigermassen brauchbare und wichtige Bedeutung festpinnen kannst,
ist das dein Kandidat für den Variablennamen.
Und ja: es sind auch solche Kleinigkeiten, die über die Qualität und
damit auch über die potentielle Fehlerfreiheit eines Programmes
entscheiden können. Programmieren ist schon schwer genug. Also benutz
jede Hilfe, die du kriegen kannst! Sinnvolle Variablennamen gehören
(unter anderem) dazu und kosten dir nichts.
Titan schrieb:> char taster (void)> {> static unsigned char zustand;> char rw = 0;
Sollte in diesem Abschnitt nicht zustand sicherheitshalber auch mit 0
initialisiert werden? Also
egal schrieb:> Titan schrieb:>> char taster (void)>> {>> static unsigned char zustand;>> char rw = 0;>> Sollte in diesem Abschnitt nicht zustand sicherheitshalber auch mit 0> initialisiert werden? Also>
1
>staticunsignedcharzustand=0;
2
>
?
Sinnvoll ist sowas immer (auch bei globalen Variablen) und sei es nur zu
Dokumentationszwecken.
Aber für statische Variablen gilt dasselbe wie für globale Variablen:
Wenn nicht anders vereinbart, werden sie automatisch mit 0
initialisiert.
Karl Heinz Buchegger schrieb:> Sinnvoll ist sowas immer (auch bei globalen Variablen) und sei es nur zu> Dokumentationszwecken.> Aber für statische Variablen gilt dasselbe wie für globale Variablen:> Wenn nicht anders vereinbart, werden sie automatisch mit 0> initialisiert.
Danke Karl Heinz. Sicher war ich mir nämlich nicht. Macht das auch
wirklich jeder Compiler so?
egal schrieb:> Karl Heinz Buchegger schrieb:>> Sinnvoll ist sowas immer (auch bei globalen Variablen) und sei es nur zu>> Dokumentationszwecken.>> Aber für statische Variablen gilt dasselbe wie für globale Variablen:>> Wenn nicht anders vereinbart, werden sie automatisch mit 0>> initialisiert.>> Danke Karl Heinz. Sicher war ich mir nämlich nicht. Macht das auch> wirklich jeder Compiler so?
Wenn er sich 'C-Compiler' nennen will, muss er es machen.
Du kannst dir funktionslokale static-Variablen als Sonderform von
globalen Variablen vorstellen, deren Sichtbarkeit ganz einfach auf nur
diese eine Funktion eingeschränkt wurde.
In C hat das Schlüsselwort 'static' nur diese eine Bedeutung: die
Sichtbarkeit von etwas (Variable oder Funktion) einzuschränken.
Tut mir leid wegen der späten Rückmeldung...
Vielen dank schon mal für die vielen Antworten!
Ich hatte das mal ebend so in einfacher form hinprogrammiert, damit ihr
euch wie bereits gesagt nicht mit irgendwelchem Code abquälen müsst, der
keinem interessiert :)
Mein Klassenlehrer kennt sich sehr gut mit der Programmierung von
Mikrocontrollern aus und meinte, das ein externer Interrupt nötig ist,
was mir auch nach ein wenig Recherche sinnvoll erscheint, da der Code ja
eigentlich funktioniert, nur der Tastendruck einfach nicht registriert
wird.
Jedoch hat der Attiny2313 nur 2 externe Interrupt Pins(INT0, INT1) aber
ich müsste 3 Taster anschließen. Dafür würde ja eine tasten Matrix in
frage kommen, mit der ich theoretisch 4 Taster benutzen könnte...oder?!?
Aber wie stelle ich das am besten an? <- sry aber mein Kopf ist vorhin
kaputt gegangen :P
Muss ich sonst noch etwas besonderes beachten?
PS:
Also ich muss schon sagen, ich programmiere seit ca. 1 1/2 Jahren in
C++, aber da hatte ich niemals Probleme mit irgendwelchen Tastern :D
Titan schrieb:> Mein Klassenlehrer kennt sich sehr gut mit der Programmierung von> Mikrocontrollern aus und meinte, das ein externer Interrupt nötig ist,
Hm. Bitte deinen Klassenlehrer doch mal, sich dazu hier im Forum und im
Tutorial schlau zu machen. Noch besser, du machst das selber, und
erklärst ihm, wie es ohne exteren Interrupts viel besser geht.
usr schrieb:> guckst du hier : Beitrag "Re: Universelle Tastenabfrage" :)
Oliver
Oliver schrieb:> Titan schrieb:>> Mein Klassenlehrer kennt sich sehr gut mit der Programmierung von>> Mikrocontrollern aus und meinte, das ein externer Interrupt nötig ist,>> Hm. Bitte deinen Klassenlehrer doch mal, sich dazu hier im Forum und im> Tutorial schlau zu machen. Noch besser, du machst das selber, und> erklärst ihm, wie es ohne exteren Interrupts viel besser geht.
Oder er soll mal vorbeikommen. Dann erklären wir ihm warum externe
Interrupts eine schlechte Idee sind, warum da kein Mensch externe
Interrupts braucht und wie man es besser machen kann.
Weiß nicht, villeicht habe ich etwas falsch verstanden oder mein Lehrer.
Wir haben uns nur kurz darüber unterhalten.
Krapao schrieb:> DIe fehlende Entprellung ist ein Problem deines Codes. Du versuchst> zwar in taster() exakt einen Tastendruck zu erkennen. Das hilft aber nix> wenn eine Prellsequenz reinkommt. Die Prellsequenz kann von deiner> taster() als Folge von Tastendrücken erkannt werden mit den> entsprechenden Konsequenzen in tastenwechsel().
Flankenerkennung
Bei einem Taster gibt es insgesamt 4 Zustände:
1. war nicht gedrückt und ist nicht gedrückt
2. war nicht gedrückt und ist gedrückt (steigende Flanke)
3. war gedrückt und ist immer noch gedrückt
4. war gedrückt und ist nicht mehr gedrückt (fallende Flanke)
Diese einzelnen Zustände lassen sich jetzt bequem abfragen/durchlaufen.
Die Entprellung geschieht dabei durch die ganze Laufzeit des Programms.
Die Taster werden hierbei als Active-Low angeschlossen um die internen
Pull-Ups zu nutzen.
Diese Routine gibt für den Zustand "steigende Flanke" den Wert "1"
zurück, sonst "0"
1
#define TASTERPORT PINC
2
#define TASTERBIT PINC1
3
4
chartaster(void)
5
{
6
staticunsignedcharzustand;
7
charrw=0;
8
9
if(zustand==0&&!(TASTERPORT&(1<<TASTERBIT)))//Taster wird gedrueckt (steigende Flanke)
10
{
11
zustand=1;
12
rw=1;
13
}
14
elseif(zustand==1&&!(TASTERPORT&(1<<TASTERBIT)))//Taster wird gehalten
15
{
16
zustand=2;
17
rw=0;
18
}
19
elseif(zustand==2&&(TASTERPORT&(1<<TASTERBIT)))//Taster wird losgelassen (fallende Flanke)
Warum steht diese Entprellung dann im Tutorial und funktioniert bei
einem noch kleinerem Code?
usr schrieb:> guckst du hier : Beitrag "Re: Universelle Tastenabfrage" :)
Das funktioniert bei mir komischerweise nicht...
Beim start leuchten einfach alle LEDs und reagieren nicht auf die
Tasten...
XXX schrieb:> Und diese Stelle würde ich nochmal überarbeiten:>> PORTB = (1<<PB3) | (0<<PB4);> ^> Da ist garantiert eine Differenz zwischen dem, was du vorhast und> dem, was ausgeführt wird.
PB3 soll eingeschaltet werden und PB4 aus. Wie mache ich dies denn
anders?
Titan schrieb:> Warum steht diese Entprellung dann im Tutorial
Wo genau?
> und funktioniert bei> einem noch kleinerem Code?
Je nachdem was runderhum passiert und wenn sie nicht zu häufig
aufgerufen wird, kann das schon klappen. Ist die Aufrufsequenz
allerdings zu schnell, dann kann ein Tastenpreller dazu führen, dass die
Funktion beim Niederdrücken der Taste alle Zustände durchläuft und dann
einen erneuten Preller mit dem Niederdrücken verwechselt.
Auf deutsch:
Die Funktion darf nur benutzt werden, wenn ausserhalb der Funktion sagen
wir mal 5 Millisekunden von einem Aufruf zum nächsten vergehen. Das ist
aber nicht das, was ich von einer zuverlässigen Entprellung erwarten
würde. Das kanns nicht sein.
> PB3 soll eingeschaltet werden und PB4 aus. Wie mache ich dies denn> anders?
Ein Bit wird auf 1 gesetzt
PORTB |= ( 1 << PB3 );
Ein Bit wird auf 0 gesetzt
PORTB &= ~( 1 << PB3 );
gewöhn dir solche Rundumschläge ala
PORTB = ( 1 << PB3 );
(also mit einer Zuweisung) erst gar nicht an. Das führt zu sehr schwer
zu entdeckenden indirekten Zusammenhängen, wenn plötzlich der Summer am
Port PB7 anfängt zu tröten, nur weil du die LED ausschalten wolltest. Du
musst dir immer im klaren sein, dass eine derartige Zuweisung ALLE Bits
des Ports beeinflusst. Solange dort nichts angeschlossen ist, ist das
noch ok. Aber diesen Luxus wirst du nicht lange haben.
Karl Heinz Buchegger schrieb:>> Warum steht diese Entprellung dann im Tutorial>> Wo genau?
ja gut ist zwar nicht das Tutorial, aber ein Artikel:
http://www.mikrocontroller.net/articles/Entprellung
in dem Artikel sind noch mehrere entpreller, aber irgendwie sehen mir
alle nicht sicher genug aus um damit einen Fernseher zu stoppen :S
@Karl Heinz Buchegger, danke schonmal für deine Tipps, ich werde
versuchen sie zu beachten :)
Ich versuche gerade folgende Entprellung:
1
#include<stdint.h>
2
#include<avr/io.h>
3
#include<avr/interrupt.h>
4
5
#ifndef F_CPU
6
#define F_CPU 1000000 // processor clock frequency
KEY_DDR&=~ALL_KEYS;// configure key port for input
110
KEY_PORT|=ALL_KEYS;// and turn on pull up resistors
111
112
TCCR0=(1<<CS02)|(1<<CS00);// divide by 1024
113
TCNT0=(uint8_t)(int16_t)-(F_CPU/1024*10e-3+0.5);// preload for 10ms
114
TIMSK|=1<<TOIE0;// enable timer interrupt
115
116
sei();
117
118
while(1){
119
if(get_key_short(1<<KEY1))
120
LED_PORT^=1<<LED1;
121
122
if(get_key_long(1<<KEY1))
123
LED_PORT^=1<<LED2;
124
125
// single press and repeat
126
127
if(get_key_press(1<<KEY2)||get_key_rpt(1<<KEY2)){
128
uint8_ti=LED_PORT;
129
130
i=(i&0x07)|((i<<1)&0xF0);
131
if(i<0xF0)
132
i|=0x08;
133
LED_PORT=i;
134
}
135
}
136
}
Jedoch habe ich das selbe Problem wie bei diesem Entpreller:
usr schrieb:> guckst du hier : Beitrag "Re: Universelle Tastenabfrage" :)
Die LEDs leuchten einfach durchgehend -.-
Ich habe die Taster auf PB0, PB1 und PB2.
Eine LED ist zu test zwecken auf PD6 geschaltet, und 5 weitere auf PB3,
PB4, PB5, PB6 und PB7
somit müsste der Code ja in etwa so aussehen:
1
#include<stdint.h>
2
#include<avr/io.h>
3
#include<avr/interrupt.h>
4
5
#ifndef F_CPU
6
#define F_CPU 1000000 // processor clock frequency
Titan schrieb:> Ich weiß leider immernoch nicht wo das Problem bei dem Code ist -.-*
In dienem letzten Code hier
> somit müsste der Code ja in etwa so aussehen:
kann ich beim Drüberlesen nichts schlimmes finden. Müsste eigentlich
funktionieren. Mit dem Taster an PB1 wird die LED an PD6 umegschaltet.
(Die Entprellsachen hast du ja nicht angegriffen, sind noch immer
Original)
Welche Taktfrequenz hat dein µC?
Hallo,
welchen µC verwendest Du, oben habe ich ATtiny2313 gelesen. Richtig?
Titan schrieb:> TCCR0 = (1<<CS02)|(1<<CS00); // divide by 1024> TCNT0 = (uint8_t)(int16_t)-(F_CPU / 1024 * 10e-3 + 0.5); // preload for 10ms
Wenn ich in das Datenblatt des 2313 schaue, dann finde ich kein TCCR0,
nur TCCR0A und TCCR0B.
Und wenn ich mich nicht verrechnet habe und F_CPU = 1000000 ist, dann
wird TCNT0 mit 255 geladen, damit paßt der Timer nicht.
Bau doch mal in die ISR noch folgendes ein:
- Eine LED die mit jedem ISR-Aufruf toggelt
- Einen Zähler und laß eine 2. LED alle 50 Aufrufe toggeln (entspricht
dann 1Hz)
Volkmar
Volkmar Dierkes schrieb:> Und wenn ich mich nicht verrechnet habe und F_CPU = 1000000 ist, dann> wird TCNT0 mit 255 geladen, damit paßt der Timer nicht.
hab ich auch zuerst gedacht.
Macht aber nichts, dann löst der Timer eben bei jedem mal hochzählen
einen Interrupt aus. Durch den Vorteiler ist das immer noch alle 1024
Taktzyklen. Also grob 1ms.
Die 10ms sind nicht so genau. Ob das 2ms oder 40ms sind, spielt so gut
wie keine Rolle (und darum stell ich mir für meine Programme den
Vorteiler so ungefähr in diesen Zeitbereich ein und lass das Vorladen
des TCNT komplett weg).
Gehn wir auch mal in eine andere Richtung
> Ich habe die Taster auf PB0, PB1 und PB2.> Eine LED ist zu test zwecken auf PD6 geschaltet, und 5 weitere auf PB3,> PB4, PB5, PB6 und PB7
OK. Aber WIE sind die Einzelteile angeschlossen?
Der Code geht zb davon aus, dass die Taster nach Masse schalten. Tun sie
das bei dir? In deinem ersten Code sieht das nämlich nicht danach aus,
muss jetzt aber erst mal nicht viel heissen.
Volkmar Dierkes schrieb:> Und wenn ich mich nicht verrechnet habe und F_CPU = 1000000 ist, dann> wird TCNT0 mit 255 geladen, damit paßt der Timer nicht.
Ich habe mich verrechnet, TCNT0 wird mit 0xF6 geladen. 10e-3 = 10*10^-3
= 0,01!