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

Hi Mathias,

stell vielleicht nochmal den aktuellen Stand deines Programms in das Forum ... und hast du meinen Beitrag zuvor gelesen/umgesetzt?

Dirk :ciao:
 
stell vielleicht nochmal den aktuellen Stand deines Programms in das Forum
Wollte ich auch schon vorschlagen...

und hast du meinen Beitrag zuvor gelesen/umgesetzt?
Jup.

Code:
#include <avr/io.h>
#define F_CPU 1e6
#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];
volatile uint16_t i;

//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();

	TCCR0A = (1<<WGM01);
	OCR0A = 124;  // 1MHz Systemtakt, Interrupt-Periodendauer = 1ms
	TIMSK |= (1<<OCIE0A);
	TCCR0B = (1<<CS01);  //Prescaler 8
	sei();

	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);
			if( debounce( PIND, PD0 ) ) 
			{	
				zaehler[0]=9;
				zaehler[1]=9;
			}
		}
	}
}

ISR(TIMER0_COMPA_vect)
{
	if (PORTB & (1<<PB0))
  	{
    		i++;
    		if (i>4000)  // ergibt 4s bei 1ms Interruptzeit
    		{
      			PORTB &= ~(1<<PB0);  // = 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 2 ausgeben
    		clearbit(Modul2);  // an 
  	}
}

Mathias
 
Das sieht für mich soweit in Ordnung aus,


nur wenn die untere if-Bedingung wahr ist, dann wird der Summer permanent aktiviert.
Wenn der Interrupt den Summer nach einiger Zeit deaktiviert, wird der Portpin sofort wieder auf high gesetzt. Man hat den Eindruck der Summer ist dauernd an, da die Auszeit sehr klein sein wird.

Wenn der Summer nur einmalig aktiviert werden soll, wenn die Bedingung wahr ist, müsstest du eine Hilfsvariable (oder Hilfsbit) im Hauptprogramm verwenden.

Dirk :ciao:

Code:
 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
  {	
   [B][COLOR=#800000]setbit(Summer);[/COLOR][/B]
   if( debounce( PIND, PD0 ) ) 
   {	
    zaehler[0]=9;
    zaehler[1]=9;
   }
  }
	}
 
Abermals ein großes Dankeschön an dich, Dirk.

müsstest du eine Hilfsvariable (oder Hilfsbit) im Hauptprogramm verwenden.
Hätte ich eigentlich auch selber drauf kommen können (müssen) ^kopfklatsch^ ;)

Jetzt macht das Programm endlich, was es soll. Hier die entscheidende Erweiterung
Code:
int main(void)
{
//.....
	char SummerAlt=0;

	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]>9)	zaehler[0]=9;	//Zählerstand bei 9 begrenzen
		if(zaehler[0]<1)	zaehler[0]=0;	//Zählerstand bei 0 begrenzen
		if(zaehler[1]>9)	zaehler[1]=9;
		if(zaehler[1]<1)	zaehler[1]=0;
			
		if((zaehler[0] < 1) || (zaehler[1] < 1))	//Rücksetzen erst möglich wenn ein Zähler auf Null
		{	
			if(SummerAlt==0)
			{
				setbit(Summer);
				SummerAlt=1;
			}
			if( debounce( PIND, PD0 ) ) 
			{	
				zaehler[0]=9;
				zaehler[1]=9;
				SummerAlt=0;
			}
		}


Das nächste Problem kommt bestimmt ...bis dahin...

Mathias
 
...da ist es auch schon :D

Ich hatte vor, eine kleine Routine hinzuzufügen, mit welcher ich die Einschaltzeit des Summers vorab wählen kann.
Aber auch dies Funktioniert mal wieder überhaupt nicht.

Hier die entscheidenden Programmauszüge
Code:
//....
volatile int8_t zaehler[2];
volatile uint16_t i;
volatile uint16_t dauer;
//....
void init( void )
{
	DDRA  = 0b000;          //Port A Eingang
	PORTA = 0b011;          //Pullup-Widerstände 0 und 1 ein
	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)
}
///....
int main(void)
{
//....
switch(PORTA)
	{
		case 0b000:	dauer=1000; break;
		case 0b001:	dauer=2000; break;
		case 0b010:	dauer=3000; break;
		case 0b011:	dauer=4000; break;
	}
//....
}

ISR(TIMER0_COMPA_vect)
{
	if (PORTB & (1<<PB0))
  	{
    	i++;
    	if (i>dauer)  // ergibt 4s bei 1ms Interruptzeit
    	{
      	clearbit(Summer);
      	i = 0;
    	}
//....
}
Die switch-Routine steht vor der for()-Schleife, soll also nur einmal zu Beginn ausgeführt werden.

Irgendwelche Ideen?


Mathias
 
Hallo Mathias,



PA2 nutzt du ja in der case nicht, bzw. willst du auch später nicht nutzen (weil du die Bitposition von PA2 bei case angegeben hast)?
Am Portpin PA2 ist standardmäßig die alternative Portfunktion RESET aktiviert. Das Fusebit RSTDISBL ist nicht programmiert. Du kannst hier also PA2 nicht verwenden.
Achtung: wenn du das Fusebit RSTDISBL (Reset-Disable) programmierst, ist keine ISP-Programmierung mehr möglich!


Hier gibts es ein (zwei) Problem(e):

Code:
switch(PORTA)
    {
  case 0b000:    dauer=1000; break;
  case 0b001:    dauer=2000; break;
  case 0b010:    dauer=3000; break;
  case 0b011:    dauer=4000; break;
    }

Zum einen verwendest du PORTA, das ist das PORTA_DataLatch, dies hat den Wert 0b011, diesen hast du zuvor zugewiesen, um Pullups zu aktivieren.
Du musst hier direkt den logischen Zustand der Portpins auswerten, das geht mit PINA.

Der ATtiny2313 hat zwar nur PA2..0, ich würde aber vor dem switch sicherstellen, dass die Bits 7..3 Null sind. Zum Beispiel so ...

Code:
uint8_t d;

d = PINA & 0b11;     // PA1..0 genutzt
// d = PINA & 0b111; //PA2..0 genutzt
switch (d)

Dirk :ciao:
 
Hallo Dirk,

danke dir, so funktioniert es.

PA2 nutzt du ja in der case nicht, bzw. willst du auch später nicht nutzen
Genau. Außerdem wird der Reset-Pin über einen externen Pullup Widerstand an 5V gelegt. Von daher kann dort auch nie eine 0 anliegen und damit keine der case-Bedingungen wahr werden.

Aber wozu braucht man diese " d = PINA & 0b11; " Zeile?
Ist dies einfach nur eine Maskierung für PORTA?


Mathias
 
Hallo Mathias,
Aber wozu braucht man diese " d = PINA & 0b11; " Zeile? Ist dies einfach nur eine Maskierung für PORTA?
Ja, es ist eine Maskierung für PINA, nicht PORTA. Bei dem ATtiny2313 gibts ja nur PIN2..0, PIN7..3 nicht. Wenn du nun mit switch-case einen Vergleich machst, musst du sicherstellen, dass das ganze Byte deinem case entspricht. Wenn die nicht exisiterenden Pins im PINA ein high zurückgeben würden, treffen keine von deinen case ein! Ich gehe allerdings davon aus dass PA7..3 immer low sind, um sicherzugehen müsste man in das Datenblatt schauen.


Damit man das aber nich machen muss, maskiert man einfach die Bits, die interessieren, also die untersten beiden. Das mache ich mit PINA & 0b11 oder PINA & 3. Vorteil ist noch, man ist kompatibel zu anderen Mikrocontrollern bei dem alle Pins PA7..0 existieren. Dirk :ciao:
 
Danke Dirk!

Diese zwei Tage habe ich wohl mehr über programmieren gelernt, als die Wochen zuvor mit einem Buch ;)

für PINA, nicht PORTA.
Huch, ich bin immer geneigt, bei mehreren Pins von einem Prot zu sprechen... Habe die µC-Sprache noch nicht so ganz verinnerlicht.
Weiß aber eigentlich, dass PINx das Eingaberegister und PORTx das Ausgaberegister ist :eek:


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)