PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : LED Matrix 8 x 8 AVR C



Janiiix3
12.08.2014, 16:39
Hallo liebe Gemeinde :-P

Ich habe ein wirklich "klitze kleines" Problemchen... Am besten stelle ich auch mal direkt mein Anliegen (Projekt) vor!

Undzwar...

Bin ich dabei die Software für einen fertig entwickelten "Würfel" zu programmieren (Hardware steht schon).
Dieser soll in erster Linie, Zahlen von 1-6 darstellen. Die "gewürfelten" soll jeder Spieler zu gunsten seines Sitzplatzes sehen können ( man stelle sich ein Tisch vor an jeder Seite sitzt ein Spieler, es gibt 4 Tasten... Aufgrund der gedrückten Taste, weiß der Würfel zu welcher Seite er die Zahl darstellen muss) Das klappt soweit auch alles.

Sprich meine "CharMap" ( enthält die ganzen Zeichen ) steht auch schon. Diese kann ich auch schon wie gewünscht aufrufen und ausgeben.
Nun möchte ich noch am Anfang oder mitten drinn eine Laufschrift über das Display jagen. Diese soll von : rechts nach links "scrollen".
Imoment habe ich meine an zu zeigende Grafik in einem "VRAM" (Videospeicher) Dort steht mein Zeichen drinn was ich wie gesagt anzeigen will.
Das Multiplexen funktioniert via. Interrupt ganz gut.

Mein Problem ist jetzt, dass ich nicht weiß wie ich das mit der Laufschrift hin bekommen soll...
Stand jemand von euch schon mal vor einem ähnlichen Problem und kann mir helfen ?

Anbei der Code.





/* Definiert die Frequenz */
#define F_CPU 16000000UL

/* Umbennung der Eingabe ( Taster ) */
#define TASTER_1 (!(PIND & (1<<PD5))
#define TASTER_2 (!(PIND & (1<<PD6))
#define TASTER_3 (!(PIND & (1<<PD3))
#define TASTER_4 (!(PIND & (1<<PD4))

/* USB Ladeerkennung */
#define USB_CONNECTED PINB & (1<<PINB4)

/* Akku muss geladen werden */
#define ACCU_MUST_LOADING (!(PIND & (1<<PIND7)))

/* Summer */
#define SUMMER_AUS PORTC |= (1<<PC2)
#define SUMMER_AN PORTC &= ~(1<<PC2)
#define SUMMER_TOGGLE PORTC ^= (1<<PC2)

/* Richtung zum schieben der Anzeige */
#define RIGHT 1
#define LEFT 0

/* Schwelle für AutoOFF ( 6000 = 1 min. ) */
#define AUTO_OFF 6000

/* Verhindert ein drücken während des Betriebes */
#define INTERRUPT_INT0_DISABLE GICR &= ~(1<<INT0)
#define INTERRUPT_INT0_ENABLE GICR |= (1<<INT0)

/* Benötigte Bibliotheken */
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>
#include "avr/pgmspace.h"
#include "avr/sleep.h"
#include <string.h>


/* Grafiken */

const char charMap[71][8] PROGMEM ={
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // an
{0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF}, // aus
{0xC3,0x99,0x99,0x99,0x99,0x99,0xC3,0xFF}, // null
{0xE7,0xC7,0xE7,0xE7,0xE7,0xE7,0xE7,0xFF}, // eins
{0xC3,0x99,0xF9,0xF3,0xE7,0xCF,0x81,0xFF}, // zwei
{0xC3,0x99,0xF9,0xE3,0xF9,0x99,0xC3,0xFF}, // drei
{0xF3,0xE3,0xC3,0x93,0x81,0xF3,0xF3,0xFF}, // vier
{0x81,0x9F,0x83,0xF9,0xF9,0x99,0xC3,0xFF}, // fünf
{0xE3,0xCF,0x9F,0x83,0x99,0x99,0xC3,0xFF}, // sechs
{0x81,0xF9,0xF9,0xF3,0xE7,0xE7,0xE7,0xFF}, // sieben
{0xC3,0x99,0x99,0xC3,0x99,0x99,0xC3,0xFF}, // acht
{0xC3,0x99,0x99,0xC1,0xF9,0xF3,0xC7,0xFF}, // neun
{0xe7,0xdb,0xdb,0xdb,0xdb,0xc3,0xdb,0xdb}, // A
{0x87,0xbb,0xbb,0xbb,0x87,0xbb,0xbb,0x87}, // B
{0xC3,0x99,0x3F,0x3F,0x3F,0x99,0xC3,0xFF}, // C
{0x87,0x93,0x99,0x99,0x99,0x93,0x87,0xFF}, // D
{0x81,0x9F,0x9F,0x87,0x9F,0x9F,0x81,0xFF}, // E
{0x81,0x9F,0x9F,0x87,0x9F,0x9F,0x9F,0xFF}, // F
{0xC3,0x99,0x3F,0x3F,0x31,0x99,0xC1,0xFF}, // G
{0x99,0x99,0x99,0x81,0x99,0x99,0x99,0xFF}, // H
{0xE7,0xE7,0xE7,0xE7,0xE7,0xE7,0xE7,0xFF}, // I
{0xF9,0xF9,0xF9,0xF9,0x99,0x99,0xC3,0xFF}, // J
{0x99,0x99,0x93,0x87,0x93,0x99,0x99,0xFF}, // K
{0x9f,0x9f,0x9f,0x9f,0x9f,0x9f,0x81,0x81}, // L
{0x39,0x11,0x01,0x01,0x29,0x39,0x39,0xFF}, // M
{0x39,0x19,0x09,0x21,0x31,0x39,0x39,0xFF}, // N
{0xC3,0x99,0x99,0x99,0x99,0x99,0xC3,0xFF}, // O
{0x83,0x99,0x99,0x83,0x9F,0x9F,0x9F,0xFF}, // P
{0xC3,0x99,0x99,0x99,0x91,0xC3,0xF1,0xFF}, // Q
{0x83,0x99,0x99,0x83,0x93,0x99,0x99,0xFF}, // R
{0xC3,0x99,0x8F,0xC7,0xF1,0x99,0xC3,0xFF}, // S
{0x81,0xE7,0xE7,0xE7,0xE7,0xE7,0xE7,0xFF}, // T
{0x99,0x99,0x99,0x99,0x99,0x99,0xC1,0xFF}, // U
{0x99,0x99,0x99,0x99,0x99,0xC3,0xE7,0xFF}, // V
{0x39,0x39,0x39,0x29,0x01,0x11,0x39,0xFF}, // W
{0x99,0x99,0xC3,0xE7,0xC3,0x99,0x99,0xFF}, // X
{0x99,0x99,0x99,0xC3,0xE7,0xE7,0xE7,0xFF}, // Y
{0x01,0xF9,0xF3,0xE7,0xCF,0x9F,0x01,0xFF}, // Z
{0xFF,0xFF,0xC3,0xF9,0xC1,0x99,0xC5,0xFF}, // a
{0x9F,0x9F,0x9F,0x83,0x99,0x99,0xA3,0xFF}, // b
{0xFF,0xFF,0xC3,0x99,0x9F,0x99,0xC3,0xFF}, // c
{0xF9,0xF9,0xF9,0xC1,0x99,0x99,0xC5,0xFF}, // d
{0xFF,0xFF,0xC3,0x99,0x81,0x9F,0xC3,0xFF}, // e
{0xE3,0xC9,0xCF,0x87,0xCF,0xCF,0xCF,0xFF}, // f
{0xFF,0xFF,0xC5,0x99,0x99,0xC1,0xF9,0xC3}, // g
{0x9F,0x9F,0x93,0x89,0x99,0x99,0x99,0xFF}, // h
{0xE7,0xFF,0xE7,0xE7,0xE7,0xE7,0xE7,0xFF}, // i
{0xF3,0xFF,0xF3,0xF3,0xF3,0x33,0x33,0x87}, // j
{0x9F,0x9F,0x99,0x93,0x87,0x93,0x99,0xFF}, // k
{0xE7,0xE7,0xE7,0xE7,0xE7,0xE7,0xE7,0xFF}, // l
{0xFF,0xFF,0x39,0x11,0x01,0x29,0x39,0xFF}, // m
{0xFF,0xFF,0x83,0x99,0x99,0x99,0x99,0xFF}, // n
{0xFF,0xFF,0xC3,0x99,0x99,0x99,0xC3,0xFF}, // o
{0xFF,0xFF,0xA3,0x99,0x99,0x83,0x9F,0x9F}, // p
{0xFF,0xFF,0xC5,0x99,0x99,0xC1,0xF9,0xF9}, // q
{0xFF,0xFF,0xA3,0x89,0x9F,0x9F,0x9F,0xFF}, // r
{0xFF,0xFF,0xC1,0x9F,0xC3,0xF9,0x83,0xFF}, // s
{0xCF,0xCF,0x83,0xCF,0xCF,0xCB,0xE7,0xFF}, // t
{0xFF,0xFF,0x99,0x99,0x99,0x99,0xC5,0xFF}, // u
{0xFF,0xFF,0x99,0x99,0x99,0xC3,0xE7,0xFF}, // v
{0xFF,0xFF,0x39,0x29,0x01,0x01,0x93,0xFF}, // w
{0xFF,0xFF,0x39,0x93,0xC7,0x93,0x39,0xFF}, // x
{0xFF,0xFF,0x99,0x99,0x99,0xC1,0xF9,0xC3}, // y
{0xFF,0xFF,0x81,0xF3,0xE7,0xCF,0x81,0xFF}, // z
{0x81,0x7E,0x5A,0x7E,0x42,0x66,0x7E,0x81}, // Smiley
{0xFF,0xE7,0xC3,0xDB,0xDB,0xDB,0xDB,0xC3}, // Battery (leer)
{0xFF,0xE7,0xC3,0xDB,0xDB,0xDB,0xC3,0xC3}, // Battery ( mit einem Strich )
{0xFF,0xE7,0xC3,0xDB,0xDB,0xC3,0xC3,0xC3}, // Battery ( mit zwei Strichen )
{0xFF,0xE7,0xC3,0xDB,0xC3,0xC3,0xC3,0xC3}, // Battery ( mit drei Strichen )
{0xFF,0xE7,0xC3,0xC3,0xC3,0xC3,0xC3,0xC3}, // Battery ( mit vier Strichen )
{0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFE,0xFF} // Punkt für´s www(.)
};

const char Spieler_links[7][8] PROGMEM ={

{0xFF,0xFF,0xFD,0x80,0x80,0xFF,0xFF,0xFF}, // eins
{0xFF,0xBD,0x9C,0x8E,0xA6,0xB0,0xB9,0xFF}, // zwei
{0xFF,0xDD,0x9C,0xB6,0xB6,0x80,0xC9,0xFF}, // drei
{0xFF,0xE7,0xE3,0xE9,0x80,0x80,0xEF,0xFF}, // vier
{0xFF,0xD8,0x98,0xBA,0xBA,0x82,0xC6,0xFF}, // füns
{0xFF,0xC3,0x81,0xB4,0xB6,0x86,0xCF,0xFF} // sechs
};

const char Spieler_rechts[7][8] PROGMEM ={

{0xFF,0xE7,0xE7,0xE7,0xE7,0xE7,0xE3,0xE7},
{0xFF,0x81,0xF3,0xE7,0xCF,0x9F,0x99,0xC3},
{0xFF,0xC3,0x99,0x9F,0xC7,0x9F,0x99,0xC3},
{0xFF,0xCF,0xCF,0x81,0xC9,0xC3,0xC7,0xCF},
{0xFF,0xC3,0x99,0x9F,0x9F,0xC1,0xF9,0x81},
{0xFF,0xC3,0x99,0x99,0xC1,0xF9,0xF3,0xC7}
};

const char Spieler_gegenueber[7][8] PROGMEM ={

{0xFF,0xFF,0xFF,0x01,0x01,0xBF,0xFF,0xFF},
{0xFF,0x9D,0x0D,0x65,0x71,0x39,0xBD,0xFF},
{0xFF,0x93,0x01,0x6D,0x6D,0x39,0xBB,0xFF},
{0xFF,0xF7,0x01,0x01,0x97,0xC7,0xE7,0xFF},
{0xFF,0x63,0x41,0x5D,0x5D,0x19,0x1B,0xFF},
{0xFF,0xF3,0x61,0x6D,0x2D,0x81,0xC3,0xFF}
};




uint8_t VRAM[50] = { };
uint8_t USB_STATE = 0;
uint16_t AUTO_OFF_Counter = 0;
uint16_t CharacterIndexValue = 0;

void clear_screen();
void fill_screen();
void check_usb();
void Display_all_Symbols();
uint8_t GetCharacterIndex(char a);
void ShiftVRAM(uint8_t direction, uint16_t ms);
void StringtoCharacter(const char *s);
void check_ACCU();
void check_AUTO_OFF();
void DisplayString(uint16_t x, const char *s);


int main(void)
{
DDRA |= ((1<<PA0) | (1<<PA1) | (1<<PA2) | (1<<PA3) | (1<<PA4) | (1<<PA5) | (1<<PA6) | (1<<PA7));
DDRB |= ((1<<PB0) | (1<<PB1) | (1<<PB2) | (1<<PB3) | (1<<PB5) | (1<<PB6) | (1<<PB7));
DDRC |= ((1<<PC2) | (1<<PC3) | (1<<PC4) | (1<<PC5) | (1<<PC6) | (1<<PC7));


/* TIMER0 CompareMatch (Multiplexen der Anzeige) */
TCCR0 |= (1<<WGM01) | (1<<WGM00) | (1<<CS01) | (1<<CS00);
TIMSK |= (1<<OCIE0);


/* Timer 1 Initalisieren */
TCCR1B |= ((1<<CS12) | (1<<CS10)); // Teiler auf 1028
TCCR1B |= (1<<WGM12); // CTC = ClearTimerCompare
OCR1A = 77; // ca. jede 10 ms. ein Überlauf
TIMSK |= (1<<OCIE1A);

/* Interrupt "0" aktivieren */
GICR |= (1<<INT0);

/* Muss ausgeschaltet werden, weil der Summer sonst dauernd an ist */
SUMMER_AUS;

/* Interrupts global erlauben */
sei();



while(1)
{
INTERRUPT_INT0_DISABLE;

check_usb();
check_ACCU();
check_AUTO_OFF();


if (USB_STATE == 0)
{
Display_all_Symbols();
}



}// Ende While

}// Ende Main



/* Multiplexen der Anzeige */
ISR(TIMER0_COMP_vect)
{
static volatile uint8_t spalte = 0;

PORTC &= ~((1<<PC7) | (1<<PC6) | (1<<PC5) | (1<<PC4) | (1<<PC3));
PORTB &= ~((1<<PB2) | (1<<PB1) | (1<<PB0));

// neue Zeilendaten ausgeben

PORTA = VRAM[spalte];

// Spalte aktivieren
switch (spalte) {
case 0: PORTC |= (1<<PC3);
break;
case 1: PORTC |= (1<<PC4);
break;
case 2: PORTC |= (1<<PC5);
break;
case 3: PORTC |= (1<<PC6);
break;
case 4: PORTC |= (1<<PC7);
break;
case 5: PORTB |= (1<<PB2);
break;
case 6: PORTB |= (1<<PB1);
break;
case 7: PORTB |= (1<<PB0);
break;
default: break;
}

spalte++; // nächste Spalte
if (spalte >= 8) spalte = 0; // Spalten 0..7
}



/* Jede 10 ms wird hier die Variable "Save_Value_Counter" um + 1 erhöht */
ISR (TIMER1_COMPA_vect)
{
AUTO_OFF_Counter = AUTO_OFF_Counter + 1;
}

/* Aufwecken des Gerätes */
ISR (INT0_vect)
{
SUMMER_AN;
_delay_ms(1);
SUMMER_AUS;
}


/* Löschen des aktuellen Displays */
void clear_screen(void)
{
for(uint8_t i = 0 ; i < 8 ; i++)
{
VRAM[i] = 0xFF;
}
}

/* Display komplett anschalten */
void fill_screen(void)
{
for (uint8_t i = 0 ; i < 8 ; i++)
{
VRAM[i] = 0x00;
}
}

/* Checkt ob Maxi Dice mit USB Verbunden ist */
void check_usb(void)
{

if (USB_CONNECTED)
{
USB_STATE = 1;

for (uint8_t i = 65 ; i < 70 ; i++)
{
for (uint8_t d= 0 ; d < 8 ; d++)
{
VRAM[d] = pgm_read_byte(&charMap[i][d]);
}
_delay_ms(500);
}

}
else
{
USB_STATE = 0;
}



}

/* Routine um das gesamte Grafikaray da zu stellen */
void Display_all_Symbols()
{
if (USB_STATE == 0)
{
for (uint8_t i = 0 ; i < 70 ; i++)
{

for (uint8_t d= 0 ; d < 8 ; d++)
{
VRAM[d] = pgm_read_byte(&charMap[i][d]);
}
check_usb();
_delay_ms(500);

}// Ende for
}
}

/* Errechnet den Index des aktuellen Zeichens */
uint8_t GetCharacterIndex(char a)
{
uint8_t index = 1; // alles aus

if ((a >= '0') && (a <= '9'))
index = a+2-48; // erste Zahl ist Index in der Tabelle des ersten Zeichens, zweite Zahl ist Index in Ascii Code des ersten Zeichens
else if ((a >= 'A') && (a <= 'Z'))
index = a+12-65;
else if ((a >= 'a') && (a <= 'z'))
index = a+38-97;
else if ((a == '.'))
index = a+70-46;

return index;
}

/* Schiebt den gesamten Zwischenspeicher (VRAM) des Zeichens */
void ShiftVRAM(uint8_t direction, uint16_t ms)
{
if (direction == 1)
{
for (uint8_t d= 0 ; d < 8 ; d++)
{
VRAM[0] = VRAM[0] >> 1 | 0x80;
VRAM[1] = VRAM[1] >> 1 | 0x80;
VRAM[2] = VRAM[2] >> 1 | 0x80;
VRAM[3] = VRAM[3] >> 1 | 0x80;
VRAM[4] = VRAM[4] >> 1 | 0x80;
VRAM[5] = VRAM[5] >> 1 | 0x80;
VRAM[6] = VRAM[6] >> 1 | 0x80;
VRAM[7] = VRAM[7] >> 1 | 0x80;

for (uint16_t i = 0; i < ms ; i++ )
{
_delay_ms(1);
}
}
}
else if (direction == 0)
{
for (uint8_t d= 0 ; d < 8 ; d++)
{
VRAM[0] = VRAM[0] << 1 | 0x01;
VRAM[1] = VRAM[1] << 1 | 0x01;
VRAM[2] = VRAM[2] << 1 | 0x01;
VRAM[3] = VRAM[3] << 1 | 0x01;
VRAM[4] = VRAM[4] << 1 | 0x01;
VRAM[5] = VRAM[5] << 1 | 0x01;
VRAM[6] = VRAM[6] << 1 | 0x01;
VRAM[7] = VRAM[7] << 1 | 0x01;


for (uint16_t i = 0; i < ms ; i++ )
{
_delay_ms(1);
}
}


}


}

/* Übergibt den String der Routine "GetCharacterIndex" */
void StringtoCharacter(const char *s)
{
while (*s)

CharacterIndexValue = GetCharacterIndex(*s++);

}

/* Überprüft ob Akku leer ist */
void check_ACCU(void)
{
clear_screen();

if ((ACCU_MUST_LOADING))
{
for (uint8_t i = 65 ; i < 66 ; i++)
{

for (uint8_t d= 0 ; d < 8 ; d++)
{
VRAM[d] = pgm_read_byte(&charMap[i][d]);
}
check_usb();
_delay_ms(500);

}// Ende for

clear_screen();
_delay_ms(500);

}
}

/* Funktion für AutoOff */
void check_AUTO_OFF(void)
{
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
cli();

if (AUTO_OFF_Counter >= AUTO_OFF)
{
INTERRUPT_INT0_ENABLE;

AUTO_OFF_Counter = 0;

SUMMER_AN;
_delay_ms(4);
SUMMER_AUS;

sleep_enable();
sei();
clear_screen();
sleep_cpu();
sleep_disable();

INTERRUPT_INT0_DISABLE;

}
sei();
}








28890

radbruch
12.08.2014, 17:50
Hallo

Ich glaube, der einfachste Ansatz wäre, wenn du fürs Scrollen deinen Bildspeicher von 8 auf 16 Bit erweitern würdest. Das wären je 8 Bit für das aktuell dargestellte Zeichen und 8 Bit für das Zeichen das reingeschoben werden soll. Nach 8 Shifts ist das neue Zeichen komplett ins dargestellte Byte gewandert und du kannst das nächste Zeichen in das reinzuschiebende Byte kopieren. Je nach Scrollrichtung mußt du natürlich das High- oder das Lowbyte an die Matrix senden und das jeweils andere Byte für das Reinscrollzeichen verwenden.

Wenn's klappt wollen wir natürlich ein Filmchen sehen :)

Gruß

mic


P.S.: Meine Matrixversuche mit dem 12X10-Pingpong verwendeten am Anfang das Scrollprogramm von ELO:
https://www.roboternetz.de/community/threads/50954-Ping-Pong-umprogrammieren?p=492046&viewfull=1#post492046

Janiiix3
12.08.2014, 17:55
Hallo Mic,

Du meinst das was ich jetzt aktuell mit "VRAM[0] = VRAM[0] << 1" mache, auf 16 erweitern?
Wie bekomme ich es denn hin das dass Bild auch von "rechts" rein geschoben kommt? Dann müsste ich es doch vorher auch 8 x im VRAM nach rechts (>>) schieben oder ?
Das Problem ist noch, dass ich meine LED´ "low active" geschaltet habe... Dann müsste ich quasie noch mal alles invertieren?!

Thegon
12.08.2014, 18:19
Also ich habe bei unserer Matrix folgendes Prinzip angewandt: Ein großer langer Videospeicher enthält alle LED an/aus Informationen des gesamten anzuzeigenden Textes. Und dann gibt es zwei kleine Videospeicher, die genau so groß sind wie die Fläche, auf der angezeigt werden kann. Jetzt wird ein socher Videospeicher wie ein "Fenster" auf den großen gelegt und dann der Multiplex-Einheit zur anzeige übergeben. Währenddessen wird der zweite kleine Videospeicher beschrieben, nur dass das "Fenster" jetzt um eine Spalte weiter verschoben wurde (Sozusagen das nächste Frame, der Text ist eine Spalte weitergewandert). Nach einer gewissen Zeit (z.B. ein vielfaches der Multiplex-Frequenz, dann kann man den gleichen Timer benutzen) werden die beiden Arrays dann ausgetauscht.

Um einen Teil des großen Videospeichers in den kleinen zu bekommen ist halt eine Schleife und die entsprechenden << und >> - Anweisungen nötig.
Da hilft es ungemein wenn man sich eine Skizze des Speichers auf einem Blatt Papier macht und sich überlegt was wieviel wohin geshiftet werden muss ;-)

Alternativ, falls du es dir vom Speicher her leisten kannst, könnte man auch für jede LED ein eigenes Byte spendieren, dadurch erspart man sich die ganze Bitshifterei und kann mit übersichtlicheren Schleifen arbeiten.

Hoffentlich war das jetzt einigermaßen verständlich ausgedrückt.
Ich kann auch ein bisschen Quellcode posten, nur glaube ich nicht dass er dir viel hilft.

Grüße Thegon

Janiiix3
12.08.2014, 18:24
Hallo Thegon,

Wie hast du es realisiert, dass die Zeichen direkt hintereinander kommen ? Sprich z.B "www.google.de" das man die "leerzeichen" frei wählen kann.
Code würde mich sehr freuen, ich habe im moment echt noch keinen Ansatz wie ich das einigermaßen ordentlich umsetzen könnte.

radbruch
12.08.2014, 18:55
Hallo

Nein, das bauchst du nicht auf 16 erweitern, denn nach 8 Shifts ist das alte Zeichen rausgeschoben und das neue Zeichen wird dargestellt. Jetzt kopierst du das nächste Zeichen in das freie Byte (je nach Scrollrichtung) des 16Bit-Bildspeichers und beginnst das Schieben von neuem.

In ISR(TIMER0_COMP_vect):

Beim nach rechts schieben: PORTA = VRAM[spalte] & 0xFF;
Beim nach links schieben: PORTA = VRAM[spalte] >> 8;

In check_usb(void) schreibst du nach 8 Shifts das neue Zeichen je nach Richtung in das High- oder Lowbyte:

Beim nach rechts schieben: VRAM[d] = VRAM[d] + pgm_read_byte(&charMap[i][d]) * 0xFF; // ins Highbyte schreiben
Beim nach links schieben: VRAM[d] = VRAM[d] + pgm_read_byte(&charMap[i][d]); // ins Lowbyte schreiben

natürlich ungetestet ;)

btw: Ich glaube, du verwechselst Zeile und Spalte bei "VRAM[spalte]". Diese Codeausschitte deuten darauf hin, dass die Zeichen im Zeichensatz von oben nach unten, also zeilenweise aufgebaut sind:


{0x99,0x99,0x99,0x99,0x99,0x99,0xC1,0xFF}, // U (6 mal 0x99 ist die Öffnung des Zeichens oben)
{0x99,0x99,0x99,0x99,0x99,0xC3,0xE7,0xFF}, // V (dito)
{0x83,0x99,0x99,0x83,0x9F,0x9F,0x9F,0xFF}, // P (0x9f ist der Strich links)
{0xF3,0xFF,0xF3,0xF3,0xF3,0x33,0x33,0x87}, // j (0x33 der Strich rechts)


Wenn du an Port B/C einen Ausgang auf High setzt, aktivierst du eine Zeile die in PortA codiert ist. Da das im Moment schon funktioniert brauchst du zum Scrollen auch nichts zusätzlich zu invertieren.

Die Methode von Thegon ist auch clever, allerdings ein komplett anderer Aufbau. Viele Wege führen nach Rom...

Gruß

mic

Janiiix3
12.08.2014, 19:00
Hallo

Nein, das bauchst du nicht auf 16 erweitern, denn nach 8 Shifts ist das alte Zeichen rausgeschoben und das neue Zeichen wird dargestellt. Jetzt kopierst du das nächste Zeichen in das freie Byte (je nach Scrollrichtung) des 16Bit-Bildspeichers und beginnst das Schieben von neuem.

In ISR(TIMER0_COMP_vect):

Beim nach rechts schieben: PORTA = VRAM[spalte] & 0xFF;
Beim nach links schieben: PORTA = VRAM[spalte] >> 8;

In check_usb(void) schreibst du nach 8 Shifts das neue Zeichen je nach Richtung in das High- oder Lowbyte:

Beim nach rechts schieben: VRAM[d] = VRAM[d] + pgm_read_byte(&charMap[i][d]) * 0xFF; // ins Highbyte schreiben
Beim nach links schieben: VRAM[d] = VRAM[d] + pgm_read_byte(&charMap[i][d]); // ins Lowbyte schreiben

natürlich ungetestet ;)

btw: Ich glaube, du verwechselst Zeile und Spalte bei "VRAM[spalte]". Diese Codeausschitte deuten darauf hin, dass die Zeichen im Zeichensatz von oben nach unten, also zeilenweise aufgebaut sind:


{0x99,0x99,0x99,0x99,0x99,0x99,0xC1,0xFF}, // U (6 mal 0x99 ist die Öffnung des Zeichens oben)
{0x99,0x99,0x99,0x99,0x99,0xC3,0xE7,0xFF}, // V (dito)
{0x83,0x99,0x99,0x83,0x9F,0x9F,0x9F,0xFF}, // P (0x9f ist der Strich links)
{0xF3,0xFF,0xF3,0xF3,0xF3,0x33,0x33,0x87}, // j (0x33 der Strich rechts)


Wenn du an Port B/C einen Ausgang auf High setzt, aktivierst du eine Zeile die in PortA codiert ist. Da das im Moment schon funktioniert brauchst du zum Scrollen auch nichts zusätzlich zu invertieren.

Die Methode von Thegon ist auch clever, allerdings ein komplett anderer Aufbau. Viele Wege führen nach Rom...

Gruß

mic


Also muss ich in der Multiplex ISR noch mal eine If Abfrage einbauen, ob rechts | links geschoben wird & je-nach-dem "Maskieren".
Hört sich echt logisch an, ich habe so lange dran rum gesessen ohne auch annähernd eine gute Lösung zu bekommen.

Thegon
12.08.2014, 19:05
Also der Zeichengenerator (der den großen Videospeicher vollschreibt) besteht aus einem großen PROGMEM-Array, das die ganzen Pixel-Informationen der einzelnen Buchstaben enthält (damit alles schön in ein Byte passt kann bei mir ein Buchstabe nicht länger sein als 8 Spalten, da ich 7 Zeilen LEDs habe sind es dann 7 Bytes pro Buchstabe). Jeder Buchstabe enthält bei mir eine Leerspalte (gehört also fix zu den Pixeldaten), dass die Buchstaben nicht so aufeinander kleben. Ein Leerzeichen besteht dann halt aus vier oder fünf Leerspalten, glaube ich. Die perfekte Umsetzung ist auch das nicht, aber so schlecht schaut es meiner Meinung nicht aus. Bleibt noch, dass manche Buchstaben mehr Spalten brauchen als andere (z.B. ist B länger als i), deswegen gibt es ein zweites PROGMEM-Array, in dem die Längen abgespeichert sind für die einzelnen Buchstaben. Die beiden Arrays generiere ich mit einem simplen kleinen VB.net Programm, das geht schneller ein solches zu schreiben als alles per Hand reinzuklopfen.



#define used_length 5 // d.h. der große Videospeicher ist 5*8 Spalten lang, wenn wir darüber hinaus sind fängts von vorne an

void bufcpy(char startpoint, char targetbuffer){
/*
So, was macht diese Funktion jetzt:
Sie kopiert ausgehend vom Startpoint (in Bits -> LED-Spalten im großen Videospeicher) ein Byte in den Ausgabebuffer, das in jeder Zeile

d1[5][8] ist der große Videospeicher,
wobei [5] bedeutet dass die Länge 8*5 = 40 Spalten beträgt.
[8] bedeutet, dass die Matrix 8 Zeilen hat

dm[2][8] sind die zwei Swap-Buffer
[2] bedeutet dass es zwei gibt, in die geschrieben werden kann (als Funktionsparameter übergeben,
in welchen geschrieben werden soll)
[8] bedeutet, dass die Matrix 8 Zeilen hat

*/

//Funktionsinterne Variablen:
//So viele Bits ist unterschied zwischen den Byte-Reihen der beiden Buffer
char startblock = startpoint/8;
char offset = startpoint%8;

//Zählervariablen der Schleifen
char i, j;

//Hier werden die beiden Bytes, also das halbe drüber und das halbe drunter hineinkopiert und entsprechend geshiftet
char low, high;

//Die Zeilen durchgehen
for(i=0; i < 8; i++){
//Lowbyte laden und entsprechend verschieben, gleiches für Highbyte
low = d1[(startblock )%used_length][i];
low = low >> offset;
high = d1[(startblock + 1)%used_length][i];
high = high << (8-offset);

//Nun zusammenfügen
dm[targetbuffer][i] = high | low;
}


}

Das ist jetzt ein wenig angepasst, dass es zu deinen Abmessungen passt, deshalb auch nicht getestet und eher nur als "Pseudocode" zu verstehen.
Aber ich hoffe, es ist ein wenig verständlich wie ich das meine ;-)

Grüße
Thegon

Janiiix3
12.08.2014, 19:44
Danke schon mal für eure Mühe! Sehr Lobenswert.

Die Funktion "ScrollText" habe ich ja nicht in meinem Programm so realisiert. Sollte ich mein Programm was dieses Thema angeht umschreiben ? In dem Beispiel von Mic (Link), wurde doch ohne "VRAM" gearbeitet oder sehe ich das falsch? Dort wird immer nur die aktuellen Pixelinformationen an die jeweilige "col" übergeben. Ich schiebe ja imoment die ganzen Pixelinformationen von einem Zeichen in den "VRAM" und arbeite den dann ab. Das ist doch eigentlich gar nicht nötig oder sehe ich das falsch? Das ist ohne doch viel einfacher?!





/* ------------------------------------------------------------------------- * Funktion ScrollLeft * * Verschiebt den Inhalt LED-Matrix um eine Spalte nach links. * Die erste Spalte tritt dabei an die Position der letzten Spalte. * -------------------------------------------------------------------------*/ void ScrollLeft(void) { uint8_t xcol; /* Spaltenzähler */ uint16_t first; /* Zwischenspeicher der ersten Spalte */ first = leds[0]; /* Erste Spalte sichern */ for (xcol=0;xcol<WIDTH-1;xcol++) { leds[xcol]=leds[xcol+1]; /* Spalten nach links verschieben */ } leds[WIDTH-1] = first; /* Erste Spalte an letzte Spalte kopieren */ }

Janiiix3
13.08.2014, 19:24
Hey Mic,

Habe da noch mal eine Frage...!

Was genau macht diese Deklaration ? (prog_uint8_t * fnt = (prog_uint8_t *) font; )

Ich vermute mal das dass irgendwas mit nem Pointer (*) zu tun hat oder ? Wieso machst du das mit dem Pointer ? Was hat das für Vorteile ? ( Ich habe mit Pointern keine Erfahrung )
Den Datentyp "prog_uint8_t" habe ich auch noch nie gelesen... Was macht der genau ? Flash ?

Janiiix3
15.08.2014, 16:25
Kann man das so machen ?

for (uint8_t i = 0 ; i < 50 ; i++)
{


for (uint16_t d = 0 ; d < 8 ; d++)
{
VRAM[d] = VRAM[d] + pgm_read_byte(&charMap[i][d]) * 0xFF; // ins Highbyte schreiben
VRAM[d] = VRAM[d] + pgm_read_byte(&charMap[i][d]); // ins Lowbyte schreiben
}


ShiftVRAM();
}

Thegon
15.08.2014, 20:18
Die Frage ist: was genau machst du da?
In welchem Zusammenhang wird das Codefragment verwendet, was soll es bewirken? Auch wäre gut, wenn man sieht, wie die Variablen deklariert sind.

Und dieser Ausdruck

&charMap[i][d]
ist, soweit ich mich jetzt auskenne, der Zugriff auf einen RAM-Speicherplatz, dessen Adresse in einem zweidimensionalen Array bestehend aus Pointern gespeichert ist. Ich glaube zwar, dass das prinzipiell funktioniert, ein Array aus Pointern zu machen, aber wozu?
Es kann aber auch sein, dass ich das jetzt nicht richtig lese, aber dann wird mich jemand anders aus dem Forum hoffentlich korrigieren ;-)

Grüße
Thegon

Janiiix3
15.08.2014, 21:02
-.-* Sorry...

Also ich habe jetzt vor meine Laufschrift so zu gestalten wie es "Mic" (zweiter Post) gemacht hat, sprich ich deklariere eine Variable als "uint16_t" und knalle dort meine Zeichen rein ("Low Byte | High Byte") Das hat mit zwei Zeichen (fest programmiert) auch schon funktioniert.

Nun möchte ich es so machen, dass ich die Zeichen aus meinem "8 Byte" Zeichensatz in den "uint16_t" Speichere (erstes Zeichen "Low Byte [0-7], zweites Zeichen "High Byte [8-15])

So sehen meine zwei "fest programmierten" Zeichen aus

uint16_t VRAM[16] = {0xE7FF,0xE7FF,0xE7C3,0xE799,0xE781,0xE79F,0xE7C3} ;...

Mit einer Funktion wollte ich das "automatisieren" so das ich halt einen String ausgeben kann, sprich...

DisplayString("Test")...
ShiftVram();

Ich hoffe das war jetzt verständlich ?

Thegon
15.08.2014, 21:41
Ich denke Mic wird besser Auskunft geben können über seinen Vorschlag als ich, aber ich versuche es mal:

Als erstes wird man sich einmal auf eine Scroll-Richtung festlegen müssen, weil bei dieser Methode (16bit VRAM) ist das fest im Code verankert.
Und dann:

VRAM[d] = VRAM[d] + pgm_read_byte(&charMap[i][d]) * 0xFF; // ins Highbyte schreiben
VRAM[d] = VRAM[d] + pgm_read_byte(&charMap[i][d]); // ins Lowbyte schreiben
sowohl ins High-Byte als auch ins Low-Byte zu schreiben ist ja nicht erwünscht. Je nach dem ob man nach links oder rechts scrollt muss man eben rechts (Low-Byte) oder links (high-Byte) "nachfüllen" mit Information, sprich neuen Zeichen.

Und dann muss das Programm irgendwie zeitgesteuert werden, weil wenn man alles in eine Schleife schreibt, dann wird ja alles in einem Rutsch durchgespielt und man sieht nix.

Prinziell wird der Ablauf des Programms so ausschauen: (unter der Annahme, dass nach links geshiftet wird, was Sinn macht, weil wir von links nach rechts lesen)
Als erstes schreiben wir ein Zeichen in das Lowbyte des VRAM (also jeweils ein Byte in jede Zeile) und legen das Highbyte (aller 8 Zeilen, nacheinander -> Multiplexing) auf die Anzeige.

Ein Timer shiftet jetzt den VRAM um 1 nach links, und es wird erneut das Highbyte angezeigt. Jetzt ist ja bereits ein Bit, das wir am Anfang in das Lowbyte geschrieben haben, zum Highbyte gewandert, d.h. man sieht die erste Spalte des kommenden buchstaben. Das macht er 8 mal, bis der Buchstabe vollständig angezeigt wird. Beim Achten mal wird eine zusätzliche Funktion aufgerufen (heißt, soweit ich das verstanden habe, check_usb, vielleicht aber auch nicht?) Jetzt ist ja das Lowbyte komplett leer (weil beim shiften wird immer an den frei gewordenen Platz 0 gesetzt). Wenn man jetzt also das neue Low-Byte einfach dazuaddiert, dann nehmen die neuen Bytes genau die frei gewordenen 8 bit ein. Und jetzt kann wieder geshiftet und angezeigt werden, 8 mal, bis das Lowbyte wieder leer ist. Dann muss wieder nachgefüllt werden usw.

Soweit das Prinzip. Wie man das jetzt in den Programmcode hineinbastelt, den du verwenden möchtst, hab ich mir jetzt nicht angeschaut.

Ich meine es ist aber im Beitrag #6 dieses Threads von Mic recht gut beschrieben.

Mehr weiß ich jetzt auch nicht dazu zu sagen...

Grüße
Thegon

radbruch
16.08.2014, 08:26
Hallo


Das hat mit zwei Zeichen (fest programmiert) auch schon funktioniert.... und das ist das Ergebniss von gefühlt endloser privater Beratung, die ich total hasse, weil das fürs RN-Forum und die Allgemeinheit überhaupt keinen Nutzen hat. (Wollte ich nochmals betonen!)



Mehr weiß ich jetzt auch nicht dazu zu sagen...Weil's das im Wesenlichen auch ist, was man dazu sagen kann. Schön, wie du meinen Ansatz aufgreifst und zu Ende denkst:)



Was genau macht diese Deklaration ? (prog_uint8_t * fnt = (prog_uint8_t *) font; )

Das ist aus dem Laufschrift-Code von ELO und wurde von mir ungeprüft übernommen:
prog_uint8_t * fnt = (prog_uint8_t *) font; /* Zeiger auf den Zeichensatz im Flash */

Gruß

mic