PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Timer (mit externem Quarz)



Rahvin
18.07.2007, 18:08
Hallo zusammen, ich habe folgendes Problem:

Ich möchte mit dem Timer0 eine Sieben-Segment-Anzeige zum blinken bringen. Das Problem: Es funktioniert nicht ;).
Hier erstmal mein Code:



#include <avr/io.h>
#include <inttypes.h>
#include <avr/interrupt.h>
#include <../../avr/include/util/delay.h>

// gobale Variablen
uint8_t summerOn = 0;
uint8_t timeRunning = 0;

uint8_t counter = 0;
uint8_t _NUM_ARRAY[10] = {(1<<PD1) | (1<<PD1) | (1<<PD2) | (1<<PD3) | (1<<PD4) | (1<<PD5) | (1<<PD7),
(1<<PD2) | (1<<PD5),
(1<<PD0) | (1<<PD2) | (1<<PD3) | (1<<PD4) | (1<<PD7),
(1<<PD0) | (1<<PD2) | (1<<PD3) | (1<<PD5) | (1<<PD7),
(1<<PD0) | (1<<PD1) | (1<<PD2) | (1<<PD5),
(1<<PD0) | (1<<PD1) | (1<<PD3) | (1<<PD5) | (1<<PD7),
(1<<PD0) | (1<<PD1) | (1<<PD3) | (1<<PD4) | (1<<PD5) | (1<<PD7),
(1<<PD2) | (1<<PD5) | (1<<PD7),
(1<<PD0) | (1<<PD1) | (1<<PD2) | (1<<PD3) | (1<<PD4) | (1<<PD5) | (1<<PD7),
(1<<PD0) | (1<<PD1) | (1<<PD2) | (1<<PD3) | (1<<PD5) | (1<<PD7)};

uint8_t _NUM_A_ALL_ON = (1<<PD0) | (1<<PD1) | (1<<PD2) | (1<<PD3) | (1<<PD4) | (1<<PD5) | (1<<PD6) | (1<<PD7);
uint8_t _NUM_A_ALL_OFF = ~((1<<PD0) | (1<<PD1) | (1<<PD2) | (1<<PD3) | (1<<PD4) | (1<<PD5) | (1<<PD6) | (1<<PD7));


void checkSummer();
void drawDisplay();


inline uint8_t debounce(volatile uint8_t *port, uint8_t pin)
{
if ( ! (*port & (1 << pin)) )
{
// Pin wurde auf Masse gezogen, 100ms warten
_delay_ms(50); // max. 262.1 ms / F_CPU in MHz
//_delay_ms(50);
if ( *port & (1 << pin) )
{
// Anwender Zeit zum Loslassen des Tasters geben
//_delay_ms(50);
//_delay_ms(50);
return 1;
}
}
return 0;
}



void checkSummer()
{
/*if (summerOn == 1)
{
PORTB |= (1<<PB1);
PORTB ^= (1<<PB1);
_delay_ms(500);
}*/
}

void drawDisplay()
{
//if (timeRunning == 0)
//{
// PORTD = _NUM_A_ALL_OFF;
//_delay_ms(500);
//PORTD = _NUM_ARRAY[counter];
//}
}

//SIGNAL(SIG_OVERFLOW1)
ISR(TIMER0_OVF_vect)
{
//PORTD = _NUM_A_ALL_OFF;
//_delay_ms(500);
PORTD = _NUM_ARRAY[counter];
PORTB |= (1<<PB1);
}

int main(void)
{
// Ausgänge definieren
DDRD = 0xff; //Ziffern-Anzeige
DDRB |= (1<<PB1); // Summer

// Eingänge definieren
DDRB |= (1<<PB0);


// Display überprüfen
PORTD = _NUM_A_ALL_ON;
// _delay_ms(1000);
// PORTD = _NUM_A_ALL_OFF;

//Timer aktivieren
TCCR0 = (1<<CS01)|(1<<CS00);
TCNT0 = 0;
TIMSK | (1<<TOIE0);
sei();
counter = 6;

while(1)
{

if (debounce(&PINB, PB0))
{
counter += 1;
if (counter > 9)
counter = 0;

if (counter == 9)
summerOn = 1;
}

checkSummer();
drawDisplay();
}

return 0;
}


Sorry für die vielen auskommentierten Textzeilen aber ich habe halt etwas rumprobiert um das Ding zum laufen zu bringen. Ich möchte es nun einfach erstmal schaffen, dass das Programm in die ISR-Routine springt und dort den Summer zum ertönen bringt. Sieht jemand hier irgendeinen Fehler?

Ich verwende übrigens einen externen 4 Mhz Quarz.

Mfg,
Rahvin

Steinigtmich
18.07.2007, 19:36
TIMSK | (1<<TOIE0);


Fehlt da nicht ein = ?

Bin mir auch nicht ganz sicher ob



PORTB |= (1<<PB1);
PORTB ^= (1<<PB1);


so direkt eine gute Idee ist, denn da wird ja angeschaltet und praktisch verzögerungsfrei wieder aus. Ich denke es wird wohl eher ein Geräusch geben wenn Du zwischen den beiden Befehlen zeitmässig etwas "Luft" (für die Lautsprechermembran zum Schwingen) lässt.

franzl
18.07.2007, 20:26
Hi,
also einen Eingang definiert man wenn dann so:
// Eingänge definieren
DDRB &=~ (1<<PB0); und nicht so DDRB |= (1<<PB0);
dann noch die zwei Fehler die der Vorgänger schon erwähnt hat.
Dann noch mal probieren und wenns nicht klappt noch mal fragen und wenn möglich ein bisschen die Anzahl der Leichtsinnsfehler reduzieren.
mfg franz

Rahvin
18.07.2007, 21:40
TIMSK | (1<<TOIE0);


Fehlt da nicht ein = ?


Stimmt... aber auch mit = geht es nicht.




PORTB |= (1<<PB1);
PORTB ^= (1<<PB1);



Auch da hast du Recht aber die Funktion ist ja auch auskommentiert ;).




Hi,
also einen Eingang definiert man wenn dann so:
// Eingänge definieren
DDRB &=~ (1<<PB0); und nicht so DDRB |= (1<<PB0);

Du hast Recht, das ist mir auch schon aufgefallen, das Problem ist, wenn ich den Eingang so definiere: DDRB &=~ (1<<PB0) dann gehts nicht :shock:
So allerdings funktionierts: DDRB |= (1<<PB0).

Der Taster an PB0 funktioniert (auch wenn der Eingang scheinbar 'falsch' definiert ist). Wenn er gedrückt wird, dann wird ein counter hochgezählt und die Zahl auf der 7-Segment-Anzeige angezeigt. Diese soll dabei jetzt noch blinken und das funktioniert leider nicht :(.

Hier jetzt nochmal der ursprüngliche Code:


#include <avr/io.h>
#include <inttypes.h>
#include <avr/interrupt.h>
#include <../../avr/include/util/delay.h>

// gobale Variablen
uint8_t summerOn = 0;
uint8_t timeRunning = 0;

uint8_t counter = 0;
uint8_t _NUM_ARRAY[10] = {(1<<PD1) | (1<<PD1) | (1<<PD2) | (1<<PD3) | (1<<PD4) | (1<<PD5) | (1<<PD7),
(1<<PD2) | (1<<PD5),
(1<<PD0) | (1<<PD2) | (1<<PD3) | (1<<PD4) | (1<<PD7),
(1<<PD0) | (1<<PD2) | (1<<PD3) | (1<<PD5) | (1<<PD7),
(1<<PD0) | (1<<PD1) | (1<<PD2) | (1<<PD5),
(1<<PD0) | (1<<PD1) | (1<<PD3) | (1<<PD5) | (1<<PD7),
(1<<PD0) | (1<<PD1) | (1<<PD3) | (1<<PD4) | (1<<PD5) | (1<<PD7),
(1<<PD2) | (1<<PD5) | (1<<PD7),
(1<<PD0) | (1<<PD1) | (1<<PD2) | (1<<PD3) | (1<<PD4) | (1<<PD5) | (1<<PD7),
(1<<PD0) | (1<<PD1) | (1<<PD2) | (1<<PD3) | (1<<PD5) | (1<<PD7)};

uint8_t _NUM_A_ALL_ON = (1<<PD0) | (1<<PD1) | (1<<PD2) | (1<<PD3) | (1<<PD4) | (1<<PD5) | (1<<PD6) | (1<<PD7);
uint8_t _NUM_A_ALL_OFF = ~((1<<PD0) | (1<<PD1) | (1<<PD2) | (1<<PD3) | (1<<PD4) | (1<<PD5) | (1<<PD6) | (1<<PD7));


void checkSummer();
void drawDisplay();

inline uint8_t debounce(volatile uint8_t *port, uint8_t pin)
{
if ( ! (*port & (1 << pin)) )
{
// Pin wurde auf Masse gezogen, 100ms warten
_delay_ms(50); // max. 262.1 ms / F_CPU in MHz
//_delay_ms(50);
if ( *port & (1 << pin) )
{
// Anwender Zeit zum Loslassen des Tasters geben
//_delay_ms(50);
//_delay_ms(50);
return 1;
}
}
return 0;
}


void checkSummer()
{
if (summerOn == 1)
{
PORTB ^= (1<<PB1);
_delay_ms(500);
}
}

void drawDisplay()
{
PORTD = _NUM_ARRAY[counter];
}

//SIGNAL(SIG_OVERFLOW1)
ISR(TIMER0_OVF_vect)
{
//PORTD = _NUM_A_ALL_OFF;
//_delay_ms(500);
//PORTD = _NUM_ARRAY[counter];
PORTB |= (1<<PB1);
}

int main(void)
{
// Ausgänge definieren
DDRD = 0xff; //Ziffern-Anzeige
DDRB |= (1<<PB1); // Summer

// Eingänge definieren
DDRB |= (1<<PB0);
//DDRB &= ~(1<<DDB0);

// Display überprüfen
PORTD = _NUM_A_ALL_ON;
_delay_ms(1000);
PORTD = _NUM_A_ALL_OFF;

//Timer aktivieren
TCCR0 = (1<<CS01)|(1<<CS00);
TCNT0 = 0;
TIMSK |= (1<<TOIE0);
sei();
counter = 0;

while(1)
{

if (debounce(&PINB, PB0))
{
counter += 1;
if (counter > 9)
counter = 0;

if (counter == 9)
summerOn = 1;
}

checkSummer();
drawDisplay();
}

return 0;
}

Dieser Code funktioniert, solange ich das sei(); auskommentiert habe. Also der Taster funktioniert, der Counter wird erhöht und das Display zeigt die entsprechende Zahl an. Ist der Counter == 9, so fängt der Summer an zu piepen.

Ist das sei(); einkommentiert, so sind alle LEDs im Display an, der Schalter reagiert nicht und die ISR-Routine wird nicht ausgeführt, denn sonst würde es ja piepen.

Ich habs mittlerweile auch mit Timer1 ausprobiert aber das klappt auch nicht :(. Hat irgendjemand noch ne Idee?... oder vielleicht eine Erklärung dafür, dass der Taster nur dann funktioniert, wenn ich den Port als Ausgang anstatt Eingang definiere :shock: ?

izaseba
18.07.2007, 22:12
Hat irgendjemand noch ne Idee?... oder vielleicht eine Erklärung dafür, dass der Taster nur dann funktioniert, wenn ich den Port als Ausgang anstatt Eingang definiere ?

Ja, ich hab eine Idee, wie ist denn Dein Taster beschaltet ?
Schaltest Du gegen VCC oder GND, hast Du einen externen Pullup oder Pulldown dran?

Und vor allem, Du räumst Dein Programm was auf, das ist eine Zumutung sowas zu posten, wo hast Du Dir das alles zusammenkopiert?

Wenn das jemand sieht, hat er wieder einen Grund sich über C lustig zu machen...

Fang mal klein an, lass mal eine LED in einem Timerinterrupt toggeln, sonst nichts.

Gruß Sebastian

franzl
18.07.2007, 22:31
Hi,
Ja das mit dem Taster könnt daran liegen dass du den internen Pull-up aktivieren musst. Und beim Programm muss ich Sebastian recht geben du kannst nicht gleich mit einem riesen Programm beginnen, wenn du die Grundlagen nicht beherscht. Und wer sich über C lustig macht hat meiner Meinung nach noch nie damit Programmiert sonst wüsst er das C wunderbar ist.
mfg franz

Rahvin
19.07.2007, 07:34
Also erstmal sorry für den Quellcode, ich werde später nochmal eine aufgeräumte Version posten O:) .
Zusammenkopiert ist da allerdings nur die Funktion für die Entprellung des Tasters, der Rest stammt von mir. Ich hab zudem ja klein angefangen und es hat alles funktioniert bis auf die ISR und die initialisierung des Timers. Ich werde wohl aber den Taster erstmal rausschmeissen und wirklich nur versuchen die LED per Timer zum blinken zu bringen ;).

Der Taster ist übrigens gegen VCC geschaltet und es ist kein externer Pullup oder Pulldown-Widerstand dran. Muss ich den Taster gegen GND schalten? Den interner Pull-up hatte ich bei den ersten Versuchen aktiviert (ist nicht mehr im Quellcode), hat aber auch nicht funktioniert.

Rahvin
19.07.2007, 07:52
Ups, eine Frage hab ich mir wohl gerade selbst beantwortet, ich muss den Taster gegen GND schalten 8-[.
Ich werde das heute Abend sofort mal ändern ;).

franzl
19.07.2007, 09:36
Hallo,
ja wie du schon selber gemerkt hast musst du den Taster gegen GND schalten und dann eben den internen Pull-up aktivieren.
mfg franz

Rahvin
19.07.2007, 18:55
So, ich hab den Taster jetzt mal gegen Ground geschaltet und das funktioniert einwandfrei. Was allerdings immer noch nicht funktioniert, dass ist der Timer :(. Ich hab den Code mal auf ein minimum reduziert:



#include <avr/io.h>
#include <avr/interrupt.h>


ISR(TIMER1_OVF_vect)
{
PORTB |= (1<<PB1);
}

int main(void)
{
// Ausgänge definieren
DDRB |= (1<<PB1); // Summer

//Timer aktivieren
TCCR1A = 0;
TCCR1B |= (1<<CS10);
TIMSK = (1<<TOIE1);
sei();

while(1)
{

}

return 0;
}


Es soll einfach der Summer an Port PB1 aktiviert werden, wenn die ISR-Routine aufgerufen wird aber das scheint nicht zu passieren :(. Wenn ich den Summer im Hauptprogramm 'manuell' aufrufe, dann summt er...

Weiß jemand, was ich falsch mache...

franzl
19.07.2007, 20:58
Hallo,
also wenn ich dein Programm im Simulator ausprobiere funktioniert es wunderbar und springt auch in die ISR.
mfg franz

izaseba
19.07.2007, 21:38
Hallo,
also wenn ich dein Programm im Simulator ausprobiere funktioniert es wunderbar und springt auch in die ISR.

Seh ich auch so, ich habe noch 2 Ideen:

1. Es gab hier vor kurzem einen, er hat den Watchdog aktiviert, und der hat zugeschlagen, bevor die ISR zu tragen kam #-o
Gott sei dank, daß er selber darauf kam, sonst hätte ich keine Idee mehr gehabt ;-)
2. Was ist das für ein Summer ? Summt der einfach vor sich hin, wenn der Spannung kriegt, oder ist das eher ein Piezo, der schonmal eine Frequenz braucht ?
wenn ja, mach mal folgendes ist der ISR:
PORTB^=(1<<PB1);
dann sollte der irgendwie knacken...

Hab ich jetzt nicht gründlich genug gelesen, oder hast Du uns Deinen Controller nicht genannt ?

Gruß Sebastian

Rahvin
19.07.2007, 21:55
Hallo,
Hab ich jetzt nicht gründlich genug gelesen, oder hast Du uns Deinen Controller nicht genannt ?

Sorry, ich verwende nen Atmega32 ;).



2. Was ist das für ein Summer ? Summt der einfach vor sich hin, wenn der Spannung kriegt, oder ist das eher ein Piezo, der schonmal eine Frequenz braucht ?

Der Summer summt sobald er Strom kriegt.

Ich habe vorhin mal die alte Variante der ISR, also SIGNAL ausprobiert und das klappt :shock:. Zudem hatte ich die signal.h nicht eingebunden aber einen Fehler hat mir der Compiler auch nicht geworfen, daher ist mir das nur durch Zufall aufgefallen.
Könnte mir vielleicht jemand erklären, warum es mit SIGNAL klappt und mit ISR nicht und wo da der Unterschied ist?

izaseba
19.07.2007, 22:03
:shock: :shock:
Du fragst nach dem Unterschied ? Na gut, hier ein Auszug aus der iom32.h


/* Timer/Counter1 Overflow */
#define TIMER1_OVF_vect _VECTOR(9)
#define SIG_OVERFLOW1 _VECTOR(9)

Und was fällt uns auf ?
Richtig, es gibt keinen Unterschied :-k

Frage, welche avr-gcc Version nutzt Du ?

Gruß Sebastian

Rahvin
20.07.2007, 09:02
Hm, aber wieso gibt es 2 Versionen, wenn es keinen Unterschied gibt?

Sorry übrigens erstmal für die vielen, vielleicht etwas dummen Fragen, dafür aber erstmal tausend Dank an alle für die Beantwortung derselben ;).

PCMan
20.07.2007, 13:10
Der unterschied den gängigen Tutorials nach ist, dass das "TIMER1_OVF_vect" eben "neuer" (und somit ja gleich "besser" ;) ) ist. Wahrscheinlich wird "SIG_OVERFLOW1" "bald" verschwinden oder einige Compiler können damit nicht mehr arbeiten. Btw: ich finde die aussagekraft von "SIG_OVERFLOW1" ziemlich schleierhaft, bei "TIMER1_OVF_vect" weiß man gleich:
1) gehört zu Timer(x)
2) Wenn Overflov
3) Vektor, der ISR veranlasst

izaseba
20.07.2007, 16:41
Der unterschied den gängigen Tutorials nach ist, dass das "TIMER1_OVF_vect" eben "neuer" (und somit ja gleich "besser" ) ist.

Ich weiß dieses ;-) zu deuten ;-) .
Der Grund SIGNAL durch ISR zu ersetzen war eben bessere Portierbarkeit der Programme, schade nur, daß es viele Tutorials im Netz noch gibt, wo man noch SIGNAL sieht, das sorgt für Verwirung :-(
Der beste Weg ist hin und wieder auf der avrlibc Seite vorbeizuschauen um Uptodate zu bleiben.
@Ravhin,
welche avr-gcc Version hast Du, nicht, daß Du noch auf einer alten Krücke reitest....

Was noch interessant wäre die beiden *.lss/*lst Dateien durchzugucken, an welcher Stelle es wohl Unterschiede gibt
Gruß Sebastian

PCMan
21.07.2007, 05:51
Also das Tutorial auf microcontroller.net (sorry für Schleichwerbung) wird relativ gut Up-To-Date gehalten - da wird für die Verarbeitung ISR und TIMER1_OVF_vect in den Beispielen verwendet. Wenn ich jetzt schon dabei bin: "OVF" ist auch nicht optimal gelöst. Warum nicht gleich OVERFLOW? Ist zwar mehr getippe, aber hey, was machen die paar Millisekunden? ;)
Grüße,
Simon

Rahvin
23.07.2007, 10:07
Danke erstmal für die Antwort :).

Ich werde nachher mal nachschauen, welche Version ich habe und mir dann ggf. mal ne neue Version draufspielen.