-         
Seite 3 von 3 ErsteErste 123
Ergebnis 21 bis 24 von 24

Thema: Ping Pong umprogrammieren?

  1. #21
    Moderator Robotik Visionär Avatar von radbruch
    Registriert seit
    27.12.2006
    Ort
    Stuttgart
    Alter
    56
    Beiträge
    5.794
    Blog-Einträge
    8
    Anzeige

    Hallo

    Man kann die LED-Matrix auch als Helligkeitssensor missbrauchen:


    http://www.youtube.com/watch?v=MhpReBX-s7w
    http://www.youtube.com/watch?v=2_mNqzsTawU


    Vielleicht kann man das später mal nutzen. Arbeitscode der Messfunktion:

    Code:
    // https://www.roboternetz.de/phpBB2/ze...ag.php?t=39560
    // http://cs.nyu.edu/~jhan/ledtouch/index.html
    uint16_t get_line(uint8_t y) // Helligkeit der Zeile y als 16Bit-Wert einlesen
    {
    	uint16_t temp=0;
    
    	clear_line(); 		// Erst alle 120 Kathoden auf high
    	PORTB &= ~0x03;   // und 120 Anoden auf low schalten
    	DDRB |= 0x03;
    	PORTD &= ~0xf0;
    	DDRD |= 0xf0;
    	PORTC &= ~0x0f;
    	DDRC |= 0x0f;
    	//_delay_ms(5); 	// und schlieslich alle LEDs "aufladen" (Strom?)
    	//asm volatile ("nop");
    
    	switch(y)
    	{
    	   case 0: 	DDRB &= ~2; 			// Anoden der gewünschten Zeile auf Eingang
    					while(!(PINB & 2))   // warten bis Pegel high erreicht ist
    						temp++;          	// solange Zähler erhöhen
    					return(temp);   		// Messwert zurückgeben
    
    	   case 1: 	DDRB &=  ~1; while(!(PINB &  1)) temp++; return(temp);
    
    	   case 2: 	DDRD &=~128; while(!(PIND &128)) temp++; return(temp);
    	   case 3: 	DDRD &= ~64; while(!(PIND & 64)) temp++; return(temp);
    	   case 4: 	DDRD &= ~32; while(!(PIND & 32)) temp++; return(temp);
    	   case 5: 	DDRD &= ~16; while(!(PIND & 16)) temp++; return(temp);
    
    	   case 6: 	DDRC &=  ~8; while(!(PINC &  8)) temp++; return(temp);
    	   case 7: 	DDRC &=  ~4; while(!(PINC &  4)) temp++; return(temp);
    	   case 8: 	DDRC &=  ~2; while(!(PINC &  2)) temp++; return(temp);
    	   case 9: 	DDRC &=  ~1; while(!(PINC &  1)) temp++; return(temp);
    		default: return(0);
    	}
    }
    
    void clear_line(void) // setzt alle Ausgänge der Schieberegister auf high
    {
    	uint8_t c;
    	PORTB |= (1<<4);
    	for(c=0; c<12; c++)
    	{
    		PORTB |= (1 << 3);        	/* PB3 = 1 (cl) */
    		PORTB &= ~(1 << 3);        /* PB3 = 0 (!cl) */
    	}
    	PORTB |= (1 << 2);         /* PB2 = 1 (str) */
    	PORTB &= ~(1 << 2);        /* PB2 = 0 (!str) */
    }
    Gruß

    mic

    Atmel’s products are not intended, authorized, or warranted for use
    as components in applications intended to support or sustain life!

  2. #22
    Moderator Robotik Visionär Avatar von radbruch
    Registriert seit
    27.12.2006
    Ort
    Stuttgart
    Alter
    56
    Beiträge
    5.794
    Blog-Einträge
    8
    Hallo

    Eine Anfrage per PN hat mich dazu verleitet hier mal die Ansteuerung der LED-Matrix genauer zu erklären. Basis der Betrachtung ist natürlich der Schaltplan:


    (Plan und Bild von http://www.elo-web.de/elo/mikrocontr...und-selbsttest)

    Sowohl im Plan wie auch auf der Platine erkennt man klar die Aufteilung in 10 Zeilen und 12 Spalten. Jede Zeile ist über einen Widerstand mit einem Pin des Mega8 verbunden, jede Spalte mit einem Ausgang der Schieberegister. Die LEDs sind mit der Anode an einer Zeilenleitung angeschlossen, die Kathode hängt an einer Spaltenleitung. Eine LED leuchtet, wenn am Portpin ihrer Zeile ein High und am Schieberegister ihrer Spalte ein Low ausgegeben wird.

    Weil es etwas einfacher ist, betrachten wir zuerst die Funktion der Schieberegister (4094D). Diese werden mit drei Signalen angesteuert: Daten, Clock und Strobe (eigentlich sind es vier Signale, aber Enable ist mit Vcc gebrückt und deshalb sind die Schieberegister immer aktiv. Schade eigentlich, denn so kann man den Z-State des 4094D nicht nutzen...) Die Schieberegister haben jeweils acht Ausgänge und sie sind kaskadiert (über QS an IC2). Sie verhalten sich deshalb wie ein grosses Schieberegister mit 16 Ausgängen, von denen wir aber nur 12 für die Spaltenansteuerung nutzen.

    Um nun die Ausgänge der Schieberegister anzusteuern, muss man zuerst eine Art Schattenregister mit dem gewünschten Bitmuster füllen. Dazu legt man am Datenanschluß den gewünschten Pegel des Bits an, das man als nächstes in das Schattenregister schieben möchte. Dann übernimmt man mit einem High-Low-Impuls an der Clockleitung dieses Bit an die ganz linke Stelle des Schattenregisters. Zuvor werden aber alle schon im Schattenregister gespeicherten Bits um eine Stelle nach rechts verschoben. Das Bit ganz rechts fällt dabei raus. Man kann beliebig viele Bits in das Schattenregister schieben. Wenn es das gewünschte Bitmuster enthält, kann man mit deinem High-Low-Impuls auf der Strobeleitung das Bitmuster an den Ausgängen ausgeben.

    Wie man am AVR die Ausgänge steuert sollte eigentlich klar sein, deshalb hier nur ein kleines Beispiel für die oberste Zeile an PC0:

    // Pin auf Ausgang schalten
    DDRC |= (1<<PC0);
    // Auf Eingang schalten
    DDRC &= ~(1<<PC0);
    // Pin high
    PORTC |= (1<<PC0);
    // Pin low
    PORTC &= ~(1<<PC0);

    Soweit zur Theorie, nun steuern wir mal ein Muster an:

    Code:
    #include <avr/interrupt.h>
    #include <util/delay.h>
    #include <inttypes.h>
    
    uint8_t c, spalte, bremsen=5;
    
    int main(void)
    {
       // initialisieren
    	DDRB = 0x1f; 				// PB4 bis PB0 sind Ausgang  76543210
       PORTB &= ~0x1f; 			// und low                 0b000DCS00
       DDRC = 0x0f;      		// PC0 bis PC3
       PORTC &= ~0x0f;
       DDRD = 0xf0;      		// PD4 bis PD7
       PORTD &= ~0xf0;
    
    	// alle Ausgänge der Schieberegister auf high schalten
    	PORTB |= (1<<4);     	// Datenleitung auf high
    	for(c=0; c<12; c++) 		// Schattenregister mit Einsen füllen für 12 Spalten
    	{
    		PORTB |= (1 << 3);	// Clockleitung high       Schiebt das Datenbit in
    		PORTB &= ~(1 << 3);	// Clockleitung low        das Schattenregister
    	}
    	PORTB |= (1 << 2);		// Strobe high 				gibt das Schattenregister
    	PORTB &= ~(1 << 2);		// Strobe low     			an die Ausgänge
    
    	// ein Low am ersten Ausgang der Schieberegister ausgeben für erste Spalte
    	PORTB &= ~(1<<4);    // Datenleitung auf low
    	PORTB |= (1 << 3);   // Clockleitung high       Schiebt das Low links in
    	PORTB &= ~(1 << 3);  // Clockleitung low        das Schattenregister und
    	PORTB |= (1 << 2);   // Strobe high             gibt es an die Ausgänge
    	PORTB &= ~(1 << 2);  // Strobe low              weiter.
    
    	// alle Zeilen einschalten
    	PORTC |= 0x0f;    	// Zeile 0 bis 3
    	PORTD |= 0xf0;    	// Zeile 4 bis 7
    	PORTB |= 3;       	// Zeile 8 und 9
    
    	_delay_ms(1000);     // eine Sekunde leuchten
    
    	// Zeilenansteuerung ausschalten
    	PORTC &= ~0x0f;		// Zeile 0 bis 3
    	PORTD &= ~0xf0; 		// Zeile 4 bis 7
    	PORTB &= ~3;     		// Zeile 8 und 9
    
    	// Ein High ins Schattenregister schieben. Dadurch wandert das Low nach rechts
    	PORTB |= (1<<4);     // Datenleitung auf high
    	PORTB |= (1 << 3);   // Clockleitung high       Bits schieben
    	PORTB &= ~(1 << 3);  // Clockleitung low
    	PORTB |= (1 << 2);   // Strobe high             und ausgeben
    	PORTB &= ~(1 << 2);  // Strobe low
    
    	// Ausgänge für Spalte 2 setzen
    	PORTC |= 0b00001010;
    	PORTD |= 0b10100000;
    	PORTB |= 0b00000010;
    
    	_delay_ms(1000);     // eine Sekunde leuchten
    
    	// Zeilenansteuerung ausschalten
    	PORTC &= ~0x0f;		// Zeile 0 bis 3
    	PORTD &= ~0xf0; 		// Zeile 4 bis 7
    	PORTB &= ~3;     		// Zeile 8 und 9
    
    	// Low weiterschieben
    	PORTB |= (1<<4);     // Datenleitung auf high
    	PORTB |= (1 << 3);   // Clockleitung high       Bits schieben
    	PORTB &= ~(1 << 3);  // Clockleitung low
    	PORTB |= (1 << 2);   // Strobe high             und ausgeben
    	PORTB &= ~(1 << 2);  // Strobe low
    
    	// Ausgänge für Spalte 3 setzen
    	PORTC |= 0b00000101;
    	PORTD |= 0b01010000;
    	PORTB |= 0b00000001;
    
    	_delay_ms(1000);     // eine Sekunde leuchten
    
    	spalte=2;
    	while(1)
    	{
    		// Zeilenansteuerung ausschalten
    		PORTC &= ~0x0f;		// Zeile 0 bis 3
    		PORTD &= ~0xf0; 		// Zeile 4 bis 7
    		PORTB &= ~3;     		// Zeile 8 und 9
    
    		// Low weiterschieben
    		if(spalte) PORTB |= (1<<4); //Nur wenn spalte 0 ist wird ein Low geschoben
    		   else PORTB &= ~(1<<4);   // sonst füllen wir mit high auf
    		PORTB |= (1 << 3);   // Clockleitung high       Bits schieben
    		PORTB &= ~(1 << 3);  // Clockleitung low
    		PORTB |= (1 << 2);   // Strobe high             und ausgeben
    		PORTB &= ~(1 << 2);  // Strobe low
    		
    		// Muster erzeugen
    		if(spalte & 2)
    		{
    			PORTC |= 0b00000011; // Ausgänge für ungerade Spalten setzen
    			PORTD |= 0b00110000;
    			PORTB |= 0b00000011;
    		}
    		else
    		{
    			PORTC |= 0b00001100; // Ausgänge für gerade Spalten setzen
    			PORTD |= 0b11000000;
    			PORTB |= 0b00000000;
    		}
          if(bremsen) _delay_ms(100*bremsen);     // variable Leuchtdauer
          
          spalte++;
          if(spalte>11)
    		{
    			spalte=0;
    			if(bremsen) bremsen--;
    		}
    	}
    	return(0);
    }
    Das Programm beginnt mit der Initialisierung der Ports des Mega8. An Port B werden neben den Bits 0 und 1 für die Matrix auch die Bits 2 bis 4 für die Ansteuerung der Schieberegister als Ausgang definiert. Dann wird das Schieberegister mit 12 Highs gefüllt und diese ausgegeben. Dadurch werden alle Kathoden auf High geschaltet und die LEDs sind ausgeschaltet.

    Nun werden am Mega8 die Ausgänge für die Zeilen mit dem Muster für die erste Spalte gesetzt, high bedeutet "LED an", low bedeutet "LED aus" für die jeweilige Zeile. Da wir bei der ersten Spalte beginnen, füllen wir zuerst ein Low in das Schattenregister und geben es aus (D=0 -> clock -> strobe). Nun leuchten die ausgewählten Bits der ersten Spalte, im Programm sind das alle. Nach einer kurzen Wartezeit schaltet das Programm alle Ausgänge des Mega8 auf Low, dadurch gehen die Leds wieder aus.

    Für die zweite und jede weitere Spalte müssen wir nun ein High(D=1 -> clock -> strobe) nachschieben und ausgeben damit immer nur eine Spalte mit Low angesteuert wird. Nach dem Schieben folgt dann wieder Zeilenmuster ausgeben, warten und Zeilenmuster wieder löschen. Nach 12 mal schieben sind alle Spalten abgearbeitet und alles beginnt wieder von vorne mit Low reinschieben...

    Das muss jetzt erst mal reichen. Wie man alternativ zeilenorientierte Bitmuster ausgeben kann, wie man einen Bildspeicher aufbaut, wie man das in eine ISR packt, wie man Zeichen, Werte oder Grafiken darstellen kann, beschreibe ich bei Interesse gerne in einem anderen Beitrag.

    Gruß

    mic

    Atmel’s products are not intended, authorized, or warranted for use
    as components in applications intended to support or sustain life!

  3. #23
    Neuer Benutzer Öfters hier
    Registriert seit
    21.09.2008
    Beiträge
    5
    Upps. Ist da ein kleiner Fehler?
    Code:
          if(bremsen) _delay_ms(100*bremsen);     // variable Leuchtdauer
    Also meine Version von AVR Studio veträgt hier nur ein Konstante.

  4. #24
    Moderator Robotik Visionär Avatar von radbruch
    Registriert seit
    27.12.2006
    Ort
    Stuttgart
    Alter
    56
    Beiträge
    5.794
    Blog-Einträge
    8
    Hallo

    Im Grunde hat dein AVR-Studio ja recht, im Eifer des Gefechts habe ich das unterschlagen und mein KamAvr/GCC ignoriert es einfach. In util/delay.h kann man es auch nachlesen:

    \note In order for these functions to work as intended, compiler
    optimizations must be enabled, and the delay time
    must be an expression that is a known constant at
    compile-time.
    If these requirements are not met, the resulting
    delay will be much longer (and basically unpredictable)...
    Es soll ja nur gezeigt werden, dass ab einer gewissen Wiederholgeschwindigkeit der Bildaufbau im Detail nicht mehr erkennbar ist. Da kommt es auf die absolut genaue Zeit nicht an und bei bremsen <1 wird die Verzögerung sowieso wirkungslos. Sauberer (aber ungetestet) wäre es etwa so:

    Code:
          // if(bremsen) _delay_ms(100*bremsen);
    
          uint8_t temp;
    		if(bremsen) // variable Leuchtdauer
    			for(temp=0; temp<bremsen; temp++)
    				_delay_ms(100);
    Bei weiteren Versuchen mit dem Pong wird man um Interrupts nicht herumkommen. Dann kann man auf delay.h verzichten und sich die Zeitbasis in der ISR selbst erzeugen.

    Gruß

    mic

    Atmel’s products are not intended, authorized, or warranted for use
    as components in applications intended to support or sustain life!

Seite 3 von 3 ErsteErste 123

Stichworte

Berechtigungen

  • Neue Themen erstellen: Nein
  • Themen beantworten: Nein
  • Anhänge hochladen: Nein
  • Beiträge bearbeiten: Nein
  •