C Zwei Zähler mit gemultiplexter 7-Seg. Anzeige

PSR B1257

Neues Mitglied
14. Sep. 2012
20
0
0
Sprachen
Hallo an alle,

ich brauche - wer hätte es gedacht - etwas Hilfe bei einem Programm. Es handelt sich um zwei unabhängige Zähler (0...9) welche von vier Tasten in~/dekrementiert werden.
Der Zählerstand wird dann auf zwei 7-Segment Anzeigen ausgegeben. Bis jetzt erfolgt das Multiplexen und die Tasterabfrage aber nur in der main(). Das Problem ist, dass immer wenn ein Taster gedrückt wird, die Anzeigen kurz ausgeschaltet werden.

Wie kann dieses Problem gelöst werden?

Achja, das Programm ist für einen ATTiny2313:

Code:
#include <avr/io.h>
#define F_CPU 4e6
#include <util/delay.h>


#define Modul1 	PORTD, PORTD2
#define Modul2 	PORTD, PORTD1

//Anschlussbelegung der 7sSeg. Anzeige am Port B
#define Summer PORTB, PORTB0
#define SEGa PORTB, PORTB1
#define SEGb PORTB, PORTB2
#define SEGc PORTB, PORTB3
#define SEGd PORTB, PORTB4
#define SEGe PORTB, PORTB5
#define SEGf PORTB, PORTB6
#define SEGg PORTB, PORTB7


#define	setbit(IO) __setbit(IO)
#define	__setbit(PORT, BIT) PORT |= (1<<BIT)

#define	clearbit(IO) __clearbit(IO)
#define	__clearbit(PORT, BIT) PORT &=~ (1<<BIT)

//Entprell-Routine übernommen von "http://www.mikrocontroller.net/attachment/67964/debounce.c"
#define debounce( port, pin )										\
({																	\
  static uint8_t flag = 0;	/* new variable on every macro usage */	\
  uint8_t i = 0;													\
																	\
  if( flag ){			/* check for key release: */				\
    for(;;){			/* loop ... */								\
      if( !(port & 1<<pin) ){	/* ... until key pressed or ... */	\
	i = 0;			/* 0 = bounce */								\
	break;															\
      }																\
      _delay_us( 20 );		/* * 256 = 25ms */						\
      if( --i == 0 ){		/* ... until key >25ms released */		\
	flag = 0;		/* clear press flag */							\
	i = 0;			/* 0 = key release debounced */					\
	break;															\
      }																\
    }																\
  }else{			/* else check for key press: */					\
    for(;;){			/* loop ... */								\
      if( (port & 1<<pin) ){	/* ... until key released or ... */	\
	i = 0;			/* 0 = bounce */								\
	break;															\
      }																\
      _delay_us( 20 );		/* * 256 = 25ms */						\
      if( --i == 0 ){		/* ... until key >25ms pressed */		\
	flag = 1;		/* set press flag */							\
	i = 1;			/* 1 = key press debounced */					\
	break;															\
      }																\
    }																\
  }																	\
  i;				/* return value of Macro */						\
})


volatile int8_t zaehler[2];

//Initialisierung
void init( void )
{
	DDRB  = 0xFF;		//Port B Ausgang
	PORTB = 0xFE;		//Alle Pins, bis auf PB0 HIGH
	DDRD  = 0b0000110;	//Port D Pin6,5,4,3,0 Eingänge; Pin 2,1 Ausgänge
	PORTD = 0b1111111;	//Alle PullUp-Widerstände 6...3 ein; Ausgänge auf HIGH (PNP Transistoren aus)
}

//Ausgabemuster für 7Segment-Anzeige
//gemeinsame Anode, clearbit schaltet Segment ein
void SiebenSegAnz(uint8_t a)	
{
	switch(a)
	{	
		case 9:	//Segment: 	gfedcba
		{		//PORTB = 0b0010000x;
			clearbit(SEGa);
			clearbit(SEGb);
			clearbit(SEGc);
			clearbit(SEGd);
			setbit(SEGe);
			clearbit(SEGf);
			clearbit(SEGg);
		}break;
		case 8:		
		{	//PORTB = 0b0000000x;
			clearbit(SEGa);
			clearbit(SEGb);
			clearbit(SEGc);
			clearbit(SEGd);
			clearbit(SEGe);
			clearbit(SEGf);
			clearbit(SEGg);	
		}break;	
		case 7:
		{	//PORTB = 0b1111000x;
			clearbit(SEGa);
			clearbit(SEGb);
			clearbit(SEGc);
			setbit(SEGd);
			setbit(SEGe);
			setbit(SEGf);
			setbit(SEGg);
		}break;
		case 6:
		{	//PORTB = 0b0000010x;
			clearbit(SEGa);
			setbit(SEGb);
			clearbit(SEGc);
			clearbit(SEGd);
			clearbit(SEGe);
			clearbit(SEGf);
			clearbit(SEGg);	
		}break;
		case 5:
		{	//PORTB = 0b0010010x;
			clearbit(SEGa);
			setbit(SEGb);
			clearbit(SEGc);
			clearbit(SEGd);
			setbit(SEGe);
			clearbit(SEGf);
			clearbit(SEGg);	
		}break;
		case 4:
		{	//PORTB = 0b0011001x;
			setbit(SEGa);
			clearbit(SEGb);
			clearbit(SEGc);
			setbit(SEGd);
			setbit(SEGe);
			clearbit(SEGf);
			clearbit(SEGg);	
		}break;
		case 3:
		{	//PORTB = 0b0110000x;
			clearbit(SEGa);
			clearbit(SEGb);
			clearbit(SEGc);
			clearbit(SEGd);
			setbit(SEGe);
			setbit(SEGf);
			clearbit(SEGg);	
		}break;
		case 2:
		{	//PORTB = 0b0100100x;
			clearbit(SEGa);
			clearbit(SEGb);
			setbit(SEGc);
			clearbit(SEGd);
			clearbit(SEGe);
			setbit(SEGf);
			clearbit(SEGg);	
		}break;
		case 1:
		{	//PORTB = 0b1111001x;
			setbit(SEGa);
			clearbit(SEGb);
			clearbit(SEGc);
			setbit(SEGd);
			setbit(SEGe);
			setbit(SEGf);
			setbit(SEGg);
		}break;
		case 0:
		{	//PORTB = 0b1000000x;
			clearbit(SEGa);
			clearbit(SEGb);
			clearbit(SEGc);
			clearbit(SEGd);
			clearbit(SEGe);
			clearbit(SEGf);
			setbit(SEGg);
		}break;
		default:
		{	//PORTB = 0b0111111x;
			setbit(SEGa);
			setbit(SEGb);
			setbit(SEGc);
			setbit(SEGd);
			setbit(SEGe);
			setbit(SEGf);
			clearbit(SEGg);	
		}
	}
}

int main(void)
{
	init();
	int i=0;

	zaehler[0]=9;
	zaehler[1]=9;

	for(;;)
	{
		if( debounce( PIND, PD3 ) ) zaehler[0]++;
		if( debounce( PIND, PD4 ) ) zaehler[0]--;
		if( debounce( PIND, PD6 ) ) zaehler[1]++; 	
		if( debounce( PIND, PD5 ) ) zaehler[1]--; 
			
		if((zaehler[0] < 1) || (zaehler[1] < 1))	//Rücksetzen erst möglich wenn ein Zähler auf Null
		{	
			setbit(Summer);
			i++;
			if (i>400) clearbit(Summer);	//Summer nach 400 durchläufen wieder abschalten
											//schöner wäre allerdings eine einstellbare Zeit	
												
			if( debounce( PIND, PD0 ) ) 
			{	
				zaehler[0]=9;
				zaehler[1]=9;
				i=0;
			}
			if (i>450) i=410;
		}

		//Zähler 1
		clearbit	(Modul1);		//Erstes Modul ein
		SiebenSegAnz (zaehler[0]);	//Wert von Zähler 1 ausgeben
		_delay_ms(1);				//Anzeige 1ms stehen lassen
		setbit		(Modul1);		//Erstes Modus aus

		//Zähler 2
		clearbit	(Modul2);
		SiebenSegAnz (zaehler[1]);
		_delay_ms(1);
		setbit		(Modul2);


	}

}
 
Willkommen in unserem Forum :)


Alles was du in main() abarbeitest, beeinflusst natürlich die Displayausgabe. Das kannst du verhindern, wenn du den Programmbereich, der für die Displayausgabe verantwortlich ist, in eine InterruptServiceroutine verlegst.

Ich würde hier einen CompareTimerInterrupt von Timer0 oder Timer1 verwenden und den Timer im CTC-Modus laufen lassen (Clear Timer On Compare).

Zum Berechnen des Compare-Wertes hilft dir unser kleines Windows-Programm
Anhang anzeigen AVR-Timer-Calculator 12.zip

In der Timer-ISR schaltest du immer zwischen den Anzeigen um, eine ist immer an. Ich würde eine Interrupt-Zeit von 20ms wählen (eventuell auch etwas kleiner, 20ms -> 50Hz). In der ISR kein delay verwenden, das brauchst du nicht mehr. Als Flag, für die aktive Anzeige, benötigst du keine neue Variable, du kannst hier ja einfach das Portregister abfragen.

Noch ein Tipp: Die Siebensegmentdekodierung machst du in einer Routine mit case, erheblich kürzer geht es über die Adressierung einer Tabelle im Flash. ... aber da möchte ich dich jetzt nicht durcheinander bringen.

Vielleicht hilft dir das schon mal weiter.

Dirk :ciao:
 
Hallo Dirk,

danke für deine Antwort. Dass ich ein Interrupt brauchen werde, habe ich mir nach einiger Suche auch schon befürchtet.
Bis jetzt war die ganze Programmiererei ja noch halbwegs harmlos, aber wenn ich mir im Datenblatt die Abschnitte zu Timern und Interrupt ansehe, schaue ich wie das sprichwörtliche Schwein ins Uhrwerk.

Habe auch mal versucht, so eine ISR einzubauen. Es wird zwar ohne Fehler kompiliert, aber beide Anzeigen bleiben dunkel.

Code:
#include <avr/io.h>
#define F_CPU 4e6
#include <util/delay.h>
#include <avr/interrupt.h>

#define Modul1 	PORTD, PORTD2
#define Modul2 	PORTD, PORTD1

//Anschlussbelegung der 7Seg. Anzeige am Port B
#define Summer PORTB, PORTB0
#define SEGa PORTB, PORTB1
#define SEGb PORTB, PORTB2
#define SEGc PORTB, PORTB3
#define SEGd PORTB, PORTB4
#define SEGe PORTB, PORTB5
#define SEGf PORTB, PORTB6
#define SEGg PORTB, PORTB7

#define	setbit(IO) __setbit(IO)
#define	__setbit(PORT, BIT) PORT |= (1<<BIT)
#define	clearbit(IO) __clearbit(IO)
#define	__clearbit(PORT, BIT) PORT &=~ (1<<BIT)

//Entprell-Routine übernommen von "http://www.mikrocontroller.net/attachment/67964/debounce.c"
#define debounce( port, pin )										\
({																	\
  static uint8_t flag = 0;	/* new variable on every macro usage */	\
  uint8_t i = 0;													\
																	\
  if( flag ){			/* check for key release: */				\
    for(;;){			/* loop ... */								\
      if( !(port & 1<<pin) ){	/* ... until key pressed or ... */	\
	i = 0;			/* 0 = bounce */								\
	break;															\
      }																\
      _delay_us( 20 );		/* * 256 = 25ms */						\
      if( --i == 0 ){		/* ... until key >25ms released */		\
	flag = 0;		/* clear press flag */							\
	i = 0;			/* 0 = key release debounced */					\
	break;															\
      }																\
    }																\
  }else{			/* else check for key press: */					\
    for(;;){			/* loop ... */								\
      if( (port & 1<<pin) ){	/* ... until key released or ... */	\
	i = 0;			/* 0 = bounce */								\
	break;															\
      }																\
      _delay_us( 20 );		/* * 256 = 25ms */						\
      if( --i == 0 ){		/* ... until key >25ms pressed */		\
	flag = 1;		/* set press flag */							\
	i = 1;			/* 1 = key press debounced */					\
	break;															\
      }																\
    }																\
  }																	\
  i;				/* return value of Macro */						\
})


volatile int8_t zaehler[2];

//Initialisierung
void init( void )
{
	DDRB  = 0xFF;		//Port B Ausgang
	PORTB = 0xFE;		//Alle Pins, bis auf PB0 HIGH
	DDRD  = 0b0000110;	//Port D Pin6,5,4,3,0 Eingänge; Pin 2,1 Ausgänge
	PORTD = 0b1111111;	//Alle PullUp-Widerstände 6...3 ein; Ausgänge auf HIGH (PNP Transistoren aus)
}

//Ausgabemuster für 7Segment-Anzeige
//gemeinsame Anode, clearbit schaltet Segment 
void SiebenSegAnz(uint8_t a)	
{
	switch(a)
	{	
		case 9:	//     	gfedcba
		{	//PORTB = 0b0010000x;
			clearbit(SEGa);
			clearbit(SEGb);
			clearbit(SEGc);
			clearbit(SEGd);
			setbit(SEGe);
			clearbit(SEGf);
			clearbit(SEGg);
		}break;
		case 8:		
		{	//PORTB = 0b0000000x;
			clearbit(SEGa);
			clearbit(SEGb);
			clearbit(SEGc);
			clearbit(SEGd);
			clearbit(SEGe);
			clearbit(SEGf);
			clearbit(SEGg);	
		}break;	
		case 7:
		{	//PORTB = 0b1111000x;
			clearbit(SEGa);
			clearbit(SEGb);
			clearbit(SEGc);
			setbit(SEGd);
			setbit(SEGe);
			setbit(SEGf);
			setbit(SEGg);
		}break;
		case 6:
		{	//PORTB = 0b0000010x;
			clearbit(SEGa);
			setbit(SEGb);
			clearbit(SEGc);
			clearbit(SEGd);
			clearbit(SEGe);
			clearbit(SEGf);
			clearbit(SEGg);	
		}break;
		case 5:
		{	//PORTB = 0b0010010x;
			clearbit(SEGa);
			setbit(SEGb);
			clearbit(SEGc);
			clearbit(SEGd);
			setbit(SEGe);
			clearbit(SEGf);
			clearbit(SEGg);	
		}break;
		case 4:
		{	//PORTB = 0b0011001x;
			setbit(SEGa);
			clearbit(SEGb);
			clearbit(SEGc);
			setbit(SEGd);
			setbit(SEGe);
			clearbit(SEGf);
			clearbit(SEGg);	
		}break;
		case 3:
		{	//PORTB = 0b0110000x;
			clearbit(SEGa);
			clearbit(SEGb);
			clearbit(SEGc);
			clearbit(SEGd);
			setbit(SEGe);
			setbit(SEGf);
			clearbit(SEGg);	
		}break;
		case 2:
		{	//PORTB = 0b0100100x;
			clearbit(SEGa);
			clearbit(SEGb);
			setbit(SEGc);
			clearbit(SEGd);
			clearbit(SEGe);
			setbit(SEGf);
			clearbit(SEGg);	
		}break;
		case 1:
		{	//PORTB = 0b1111001x;
			setbit(SEGa);
			clearbit(SEGb);
			clearbit(SEGc);
			setbit(SEGd);
			setbit(SEGe);
			setbit(SEGf);
			setbit(SEGg);
		}break;
		case 0:
		{	//PORTB = 0b1000000x;
			clearbit(SEGa);
			clearbit(SEGb);
			clearbit(SEGc);
			clearbit(SEGd);
			clearbit(SEGe);
			clearbit(SEGf);
			setbit(SEGg);
		}break;
		default:
		{	//PORTB = 0b0111111x;
			setbit(SEGa);
			setbit(SEGb);
			setbit(SEGc);
			setbit(SEGd);
			setbit(SEGe);
			setbit(SEGf);
			clearbit(SEGg);	
		}
	}
}

int main(void)
{
	init();

	TCCR0B = 0x05;
	TCCR0A = (1 << COM0A1);
	TIMSK = 2;
	sei();

	int i=0;

	zaehler[0]=9;
	zaehler[1]=9;

	for(;;)
	{
		if( debounce( PIND, PD3 ) ) zaehler[0]++;
		if( debounce( PIND, PD4 ) ) zaehler[0]--;
		if( debounce( PIND, PD6 ) ) zaehler[1]++; 	
		if( debounce( PIND, PD5 ) ) zaehler[1]--; 
			
		if((zaehler[0] < 1) || (zaehler[1] < 1))	//Rücksetzen erst möglich wenn ein Zähler auf Null
		{	
			setbit(Summer);
			i++;
			if (i>400) clearbit(Summer);	
												
			if( debounce( PIND, PD0 ) ) 
			{	
				zaehler[0]=9;
				zaehler[1]=9;
				i=0;
			}
			if (i>450) i=410;
		}
/*
		//Zähler 1
		clearbit	(Modul1);		//Erstes Modul ein
		SiebenSegAnz (zaehler[0]);	//Wert von Zähler 1 ausgeben
		_delay_ms(1);				//Anzeige 1ms stehen lassen
		setbit		(Modul1);		//Erstes Modus aus

		//Zähler 2
		clearbit	(Modul2);
		SiebenSegAnz (zaehler[1]);
		_delay_ms(1);
		setbit		(Modul2);
*/

	}

}

ISR(TIMER0_OVF_vect)
{
	static uint8_t j=0;
	
	if(j==1)
	{
		clearbit		(Modul1);	//Erstes Modul ein
		SiebenSegAnz 	(zaehler[0]);	//Wert von Zähler 1 ausgeben
		setbit 			(Modul1);
		j=0;
	}	
	if(j==0) 
	{
		clearbit		(Modul2);	//Erstes Modul ein
		SiebenSegAnz 	(zaehler[1]);	//Wert von Zähler 1 ausgeben
		setbit 			(Modul2);
		j++;
	}
}

erheblich kürzer geht es über die Adressierung einer Tabelle im Flash. ... aber da möchte ich dich jetzt nicht durcheinander bringen.
Besser ist das :)
Ich war schon froh, dass das Programm überhaupt funktioniert hat. Mit irgendeiner Speicherverwaltung möchte ich mich als Programmiereinsteiger nicht unbedingt rumärgern.


Mathias
 
Hallo Mathias!

Das Bit COM0A1 solltest du nicht setzen, es wirkt auf den OC0A Pin. Das brauchst du nicht.
Du schaltest immer ein Display sofort wieder aus (setbit), du musst hier eher zwischen den Displays wechseln, eins ist immer 20ms an, dann das andere 20ms ...

Hier ein Beispiel (ich hoffe es funktioniert, es ist nicht getestet) in dem ich den Timer im CTC Mode laufen lasse. Nach 20ms
wird ein CompareInterrupt erzeugt. In der ISR nutze ich direkt ein Bit im Portregister, um zwischen den beiden Anzeigen umzuschalten.

Dirk :ciao:

Code:
// Timer0 im CTC Mode mit CompareA Interrupt initialisieren
TCCR0A = (1<<WGM01);
OCR0A = 77;  // 4MHz Systemtakt, Prescaler 1024 -> Interrupt-Periodendauer = 20ms
TIMSK |= (1<<OCIE0A);
TCCR0B = (1<<CS02) | (1<<CS00);  // entspricht deiner 0x05, also 1024 Prescaler, nun läuft der Timer

sei();

// ...


ISR(TIMER0_COMPA_vect)
{

  if (PORTD & (1<<PD2))
  {
    setbit(Modul2); // aus
    SiebenSegAnz(zaehler[0]);    //Wert von Zähler 1 ausgeben
    clearbit(Modul1);  // an
  } else {
    setbit(Modul1); // aus
    SiebenSegAnz(zaehler[1]);    //Wert von Zähler 1 ausgeben
    clearbit(Modul2);  // an 
  }

}
 
Nochmals einen riesen Dank an dich, Dirk :flowers:

Ich hätte wahrscheinlich noch bis Weihnachten daran gesessen ;)

Eine kleine Änderung musste ich jedoch vornehmen, und zwar den Interruptzyklus deutlich zu verkürzen. Die beiden Anzeigen sollten ja simultan leuchten, und bei 50Hz sieht man sie eben noch deutlich blinken (ich hatte mich schon gewundert, warum du 20ms vorgeschlagen hattest, und gadacht, dass das schon seinen Grund habe - hätte wohl klarer sagen sollen, dass beide Anzeigen simultan leuchten sollen). Der Prescaler steht jetzt auf 64 und das OCR zählt jetzt bis 60 (entspricht 1kHz).

Wo das funktioniert, kommt aber schon das nächste Problem: Sobald einer der Zähler auf Null steht, soll ja PB0 (aka "Summer") für eine bestimmte Zeit x (um die 4s) geschaltet werden.
Dies funktioniert jetzt allerdings nicht mehr. (zugegeben, mein Codeblock für diese Funktion war auch sehr abenteuerlich :( - aber er hatte ja funktioniert)

Wenn du mir diesbezüglich auch noch weiterhelfen könntest, wird dir mein Dank ewig nachschleichen :)


Mathias
 
Hallo Mathias,

ich vermute, dass man den Summer nicht mehr hört, weil die beiden _delay_ms(1) in der while(1) Schleife von main() nicht mehr vorhanden sind. Die Variable i erreicht dann doch ziemlich schnell die 400 und der Summer ist sofort wieder aus, so dass man nichts hört... vielleicht ein Knacksen.


Dirk :ciao:
 
Der Summer ist für Gleichspannung geeignet, den sollte man also hören.

Die Variable i erreicht dann doch ziemlich schnell die 400 und der Summer ist sofort wieder aus
Schnell ist er, ja. Allerdings wird der Port nach dem Zählerdurchlauf mit nichten abgeschaltet.
Der Pin ist abwechselnd voll eingeschaltet, bzw. gibt ein PWM-Signal mit einer Frequenz von etwa 16kHz mit einem Tastverhältnis von etwa 0,2 aus (bei Vergrößerung auf 4000) :fie:
 
Der Summer ist für Gleichspannung geeignet, den sollte man also hören.

Schnell ist er, ja. Allerdings wird der Port nach dem Zählerdurchlauf mit nichten abgeschaltet.
Der Pin ist abwechselnd voll eingeschaltet, bzw. gibt ein PWM-Signal mit einer Frequenz von etwa 16kHz mit einem Tastverhältnis von etwa 0,2 aus (bei Vergrößerung auf 4000) :fie:

Hallo Mathias,

so ganz verstehe ich das Programm noch nicht. Bei deinem ursprünglichen Programm, müsste der Summer für etwa 800ms gepiepst haben, wenn mindestens einer von beiden Zählern Null war.

Im Moment fehlt im unteren Bereich von main() die Displayausgabe, die ist nun in der ISR. Es fehlt aber auch zweimal das _delay_ms(1). Dadurch wird deine Zählvariable i so schnell inkrementiert, dass sie in etwa in 100us die 400 erreicht, hier wird der Summer wieder abgeschaltet. Die Variable i wird dann bei 410 begrenzt und das Toggeln des Pins für den Summer macht dann folgender Codebereich:

Code:
setbit(Summer);
i++;
if (i>400) clearbit(Summer);

Das könnten dann auch die 16MHz sein!


Da der Summer nur Gleichspannung benötigt, musst du eigentlich nur den Portpin einschalten und nach einer bestimmten Zeit wieder ausschalten. Wenn du hier _delay_ms verwendest (Anschaltzeit), ist dies nicht so schön, da ja dein Hauptprogramm in dieser Zeit blockiert wird. Das ist soweit nicht tragisch, wenn du sonst keine wichtigen Sachen abarbeitest, aber deine Tasterabfrage wird dann auch beeinflusst.

Eleganter ist es, wenn du in der TimerISR einen Zähler verwendest, und damit den Portpin steuerst. Im Hauptprogramm könntest du an beliebiger Stelle den Summer aktivieren, die TimerISR "kümmert" sich dann um den Rest.
Dirk :ciao:
 
Hallo Dirk,

müsste der Summer für etwa 800ms gepiepst haben
Nein, es waren schon um die 4s.

Wenn du hier _delay_ms verwendest (Anschaltzeit), ist dies nicht so schön, da ja dein Hauptprogramm in dieser Zeit blockiert wird.
Das wäre kein Problem, da unmittelbar nach Ablauf eines Zählers keine weiteren Tasten gedrückt werden. Wenn der Summer dann aus ist, werden beide Zähler wieder zurückgesetzt.
Wenn ich allerdings einfach ein
Code:
setbit(Summer);
_delay_ms(2000);
clearbit(Summer);
in die main einfüge, bleibt das Programm an dieser Stelle hängen. Tastendrücke werden nicht mehr registriert und der Pin bleibt auf HIGH.

Eleganter ist es, wenn du in der TimerISR einen Zähler verwendest, und damit den Portpin steuerst. Im Hauptprogramm könntest du an beliebiger Stelle den Summer aktivieren, die TimerISR "kümmert" sich dann um den Rest.
Hört sich ja ganz gut an, habe ich auch gleich mal versucht, es klappt aber nicht. Der Pin bleibt weiterhin auf HIGH. Dafür werden die Taster jetzt wieder erkannt. So sieht mein Versuch aus:
Code:
int main(void)
{
//........
if((zaehler[0] < 1) || (zaehler[1] < 1))	
			setbit(Summer);
//........
}
ISR(TIMER0_COMPA_vect)
{
	int i=0;

	if(Summer==1) i++;
	if(i==4000) clearbit(Summer); i=0;


 	if (PORTD & (1<<PD2))
  	{
    	setbit(Modul2); // aus
    	SiebenSegAnz(zaehler[0]);    //Wert von Zähler 1 ausgeben
    	clearbit(Modul1);  // an
  	} 
	else 
	{
    	setbit(Modul1); // aus
    	SiebenSegAnz(zaehler[1]);    //Wert von Zähler 1 ausgeben
    	clearbit(Modul2);  // an 
  	}
}

Mathias
 
Hallo Mathias!
Nein, es waren schon um die 4s.

Der Systemtakt beträgt wirklich 4MHz? Ich komme auf die 800ms wegen: 400 x (1ms + 1ms)

Das geht nicht, der Compiler müsste hier eigentlich einen Fehler melden:
Code:
#define Summer PORTB, PORTB0

//...

[B][COLOR=#b22222]if(Summer==1) i++;   [/COLOR][/B][COLOR=#b22222]  // Compiler müsste Fehler melden[/COLOR]
if(i==4000) clearbit(Summer); [COLOR=#b22222]i=0[/COLOR]; [COLOR=#b22222]// hier ist i nicht in if eingeklammert, also [B]immer [/B]danach 0![/COLOR]

So wäre es richtig:
Code:
  if (PORTB & (1<<PB0))
  {
    i++;
    if (i>4000)  // ergibt 4s bei 1ms Interruptzeit
    {
      PORTB &= ~(1<<PB0);  // = clearbit(Summer)
      i = 0;
    }
  }




Dirk
:ciao:
 
Hallo Dirk,

Der Systemtakt beträgt wirklich 4MHz?
Tja, dachte ich zumindest, oder ist diese Anweisung
Code:
#define F_CPU 4e6
falsch?

Das geht nicht, der Compiler müsste hier eigentlich einen Fehler melden:
Macht er (AVR Studio 4.16) aber nicht. "0 Fehler, 0 Warnungen"

So wäre es richtig:
Funktioniert aber leider auch nicht. Ich habe diesen Code in die main eingefügt, mit dieser Ergänzung:
Code:
	if((zaehler[0] < 1) || (zaehler[1] < 1))	//Rücksetzen erst möglich wenn ein Zähler auf Null
		{	
			[COLOR="#FF0000"]setbit(Summer);[/COLOR]
			if (PORTB & (1<<PB0))
		  	{
		    	i++;
		    	if (i>400)  // ergibt 4s bei 1ms Interruptzeit
		    	{
		      	PORTB &= ~(1<<PB0);  // = clearbit(Summer)
		      	i = 0;
		    	}
		  	}
												
			if( debounce( PIND, PD0 ) ) 
			{	
				zaehler[0]=9;
				zaehler[1]=9;
			}
		
		}
Dann wird der Pin zwar gesetzt, aber auch wieder nicht zurückgesetzt.
Das gleiche habe ich dann auch noch mal in der ISR probiert, ebenfalls ohne Erfolg.


Noch eine kleine Frage: Was sagt diese Zeile
Code:
if (PORTB & (1<<PB0))
eigentlich aus?


Mathias
 
Mit der F_CPU-Definition sagt man doch nur der Programmierumgebung, mit welcher Frequenz der Controller (angeblich) läuft (damit die irgendwelche Zeiten korrekt berechnen kann - delay zB). Wie schnell der wirklich läuft, legt die Hardware (und ggf ein Prescaler) fest, sowie die Fuses für die Taktquelle).
Zur letzten If-Frage:
-hinter dem if käme ja ein bedingter code/-block. Die Bedingung ist hier meiner Meinung nach ein bitweises logischen AND zwischen dem Register PORTB und 00000001binär.
Aus (1<<PB0) wird erstmal eine 0 erzeugt, in diese wird eine 1 PB0-oft von rechts eingeschoben. PB0 ist in der Prozessordefinitionsdatei definiert. Die Bedingung lautet also "Wenn PB0 (in PORTB) = 1, dann..."
 
Hallo,
Ich betreibe den µC derzeit nämlich ohne Quarz. Dann müsste man wohl erstmal den inneren Systemtakt aktivieren, oder?
der innere Systemtakt ist immer aktiviert wenn du es nicht durch die Fuses geändert hast.
Normalerweise (per Default) arbeitet der interne Oszillator auf 8 MHz und wird nochmal durch einen /8-Teiler auf 1MHz runtergeteilt. Im Auslieferungszustand würde die CPU also normalerweise mit 1MHz laufen.

Du hast im Quelltext 4MHz angegeben. Darum werden die internen Berechnungen vom Compiler auch darauf ausgerichtet. Das heißt das bei 1MHz realem Takt und 4MHz angegebenem Takt alles etwa um den Faktor 4 zu langsam abläuft. Kommt dann auch hin mit den 800ms die von Dirk berechnet wurden und deinen etwa 4s.

Gruß
Dino
 
interne Oszillator auf 8 MHz und wird nochmal durch einen /8-Teiler auf 1MHz runtergeteilt
Danke für die Erklärung. Habe den Header entsprechend angepasst.

Allerdings besteht mein Problem, dass PB0 nachdem er einmal gesetzt wurde, nicht mehr zurückgesetzt wird, weiterhin :confused:

Weiß jemand diesbezüglich einen guten Rat?
 
Hallo,

Habe den Header entsprechend angepasst.
im Programm kannst du die Fuses nicht ändern. Du kannst die Fuses (und damit den Systemtakt) nur bei der Programmierung des Controllers ändern.

Das HEX-File das du in den Controller schiebst enthält lediglich das reine Programm ohne die Fuses. Die Fuses liegen in einem eigenen Bereich der extra angesprochen werden muß.

Gruß
Dino
 
Ich meinte damit, dass jetzt
Code:
#define F_CPU 1e6
im Header steht, dann sollte es nach meinem Verständnis wieder stimmen.
Die Fuse-Bits wollte ich erst mal nicht verändern.
 
Hi,

Ich meinte damit, dass jetzt
Code:
#define F_CPU 1e6
im Header steht, dann sollte es nach meinem Verständnis wieder stimmen.
Die Fuse-Bits wollte ich erst mal nicht verändern.
aaachhhhsssooooooo ;) Dann sollten Hardware und Software wieder im "Gleichtakt" sein ;)

Gruß
Dino
 
Hallo Mathias,

dieser Code war eigentlich für die ISR gedacht:

(definiere i global folgendermaßen: volatile uint16_t i )

Code:
  if (PORTB & (1<<PB0))   //[B][COLOR=#006400]wahr wenn Bit PB0 gesetzt, also wenn Summer an ist[/COLOR][/B]
  {    
    i++;    
    if (i>4000)  // ergibt 4s bei 1ms Interruptzeit     
    {       
      PORTB &= ~(1<<PB0);  // = clearbit(Summer)  Bit PB0 löschen   
      i = 0;     
    } 
  }

als Ersatz für deinen Code:

Code:
if(Summer==1) i++;     // Compiler müsste Fehler melden
if(i==4000) clearbit(Summer); i=0; // hier ist i nicht in if eingeklammert, also immer danach 0!




Zu deinem Code in der ISR:
wenn ich die Definition von Summer bei deinem Code auflöse, dann steht dort:
Code:
[COLOR=#800000]  if (PORTB, PORTB0 == 1) i++;[/COLOR]

  // da ja: #define Summer PORTB, PORTB0

Ich denke mal der Compiler kann damit nichts anfangen.



Dirk :ciao:
 
Hallo Dirk,

habe gerade noch mal mein Oszilloskop bezüglich des PB0-Status konsultiert. Er ist und bleibt voll eingeschaltet, nachdem er einmal gesetzt wurde.

Dafür weiß ich jetzt, dass die Interrupt-Frequenz halbwegs stimmt. Sie ist zwar nicht ganz symmetrisch (1,04ms/0,92ms), aber damit kann ich leben.


Mathias
 

Über uns

  • Makerconnect ist ein Forum, welches wir ausschließlich für einen Gedankenaustausch und als Diskussionsplattform für Interessierte bereitstellen, welche sich privat, durch das Studium oder beruflich mit Mikrocontroller- und Kleinstrechnersystemen beschäftigen wollen oder müssen ;-)
  • Dirk
  • Du bist noch kein Mitglied in unserer freundlichen Community? Werde Teil von uns und registriere dich in unserem Forum.
  •  Registriere dich

User Menu

 Kaffeezeit

  • Wir arbeiten hart daran sicherzustellen, dass unser Forum permanent online und schnell erreichbar ist, unsere Forensoftware auf dem aktuellsten Stand ist und der Server regelmäßig gewartet wird. Auch die Themen Datensicherheit und Datenschutz sind uns wichtig und hier sind wir auch ständig aktiv. Alles in allem, sorgen wir uns darum, dass alles Drumherum stimmt :-)

    Dir gefällt das Forum und unsere Arbeit und du möchtest uns unterstützen? Unterstütze uns durch deine Premium-Mitgliedschaft!
    Wir freuen uns auch über eine Spende für unsere Kaffeekasse :-)
    Vielen Dank! :ciao:


     Spende uns! (Paypal)