Fischertechnik AVR Raspberry Pi Elektronik Netzwerk Sonstiges Impressum |
Laufschrift aus 8x8-LED-ModulenAngeregt durch den Artikel 64pixels How To habe ich eine Platine entwickelt, die genau unter ein 8x8-Display (32mm x 32mm) passt und zusätzlich einen ISP-6-Wannenstecker und zwei parallel geschaltete dreipolige Stecker mit dem letzten freien Port-Pin des ATtiny2313 und den Stromanschlüssen aufnimmt. Mit dem freien Portanschluss ist eine Kaskadierung mit weiteren Modulen möglich. Dabei haben alle Module die selbe Software und nur die Konfiguration entscheidet über die Rolle "Master" oder "Slave an Position x". Die Software unterstützt zur Zeit 4 Displays nebeneinander, also 32x8 Bildpunkte. Der Bildschirmspeicher belegt 32 der 128 Byte RAM. Eine Erweiterung auf 6-8 Displays erscheint realistisch möglich. Die Änderung der Konfiguration ist nach dem Einschalten der Stromversorgung möglich. Die Sequenz zur Konfiguration ist so gestaltet, dass normales Einschalten der kaskadierten Module nicht zu einer Konfigurationsänderung der Module führt. Allerdings muss ich hier den Code nocheinmal verbessern, da eine leere Batterie schon mal zur Rekonfiguration geführt hat. Die Anzahl der Module ist durch das RAM der einzelnen ATtiny2313 beschränkt und sollte sich verdoppeln lassen, wenn der ATtiny4313 eingesetzt wird. Dann könnten auch weitere Zeichen, Symbole oder Schriftarten implementiert werden oder sehr lange Texte. Aktuell sind die 2kByte Flash randvoll und ich habe schon überlegt, ob der Master nicht durch einen zusätzlichen ATtiny85 realisiert werden sollte und sich die Probleme mit dem Flash damit erledigen. Damit hätte ich aber unterschiedlichen Code auf Master und Slaves zu pflegen und durch fehlenden Handlungsdruck habe ich bisher auf eine solche Erweiterung verzichtet. Ein Master mit freien Portleitungen hätte allerdings den Charme einer leichten externen Steuerung der Lauflichtinhalte z.B. per USB... Eine Optimierung des ersten Codes habe ich noch in den Flash gequetscht nachdem mich die stark unterschiedliche Helligkeit von waagerechten und senkrechten Linien zu sehr gestört hat. Im ersten Code wurden die acht Zeilen des Displays nacheinander angezeigt und die Helligket der LED hing davon ab, wieviele LED innerhalb einer Zeile gleichzeitig leuchten sollten. Horizontale Linien waren entsprechend dunkel und eine einzelne vertikale Linie sehr hell. Jetzt wechselt das Multiplexing nach jedem Bild: Nach einer zeilenweisen Darstellung folgt eine spaltenweise. Hierdurch erscheinen Linien in beide Richtungen gleichhell. Der prinzipielle Nachteil geringer und vom Bildinhalt abhängiger Helligkeit ist der sehr billigen Ansteuerung geschuldet und lässt sich auch mit der Optimierung nicht heilen. Wer mit dem existierenden Code zufrieden ist, kann auf ISP und Platine verzichten und mit der "tinkerlog.com-Bauweise" Module unter 3 Euro bauen. Meine einfarbig roten 8x8-LED haben pro Stück ca. einen Euro gekostet und die 2313 lagen auch nur unwesentlich darüber. Etwas Draht und Lötzinn dazu, das war es dann. So ganz teuer ist die Platinenversion aber auch nicht, da 15 Nutzen auf eine normale Europlatine passen. Ich habe diese bei Platinenbelichter.de herstellen lassen und kann dieses durchaus empfehlen. Der ATtiny2313 bietet durch das "Corner-Pinning" die nette Möglichkeit Sockel mit integriertem 100nF-Kondensator einzusetzen; falls also bei langen Zuleitungen Störungen oder Instabilitäten entstehen, einfach einen solchen Sockel nutzen; er kann auch nachträglich zwischen Chip und Billigsockel gesteckt werden. Alternativ liegt an allen drei Steckern die Versorgungsspannung und ein Kondensator kann dort leicht ergänzt werden. Das Display wird auf der Lötseite montiert; bei einer einseitigen Platine ohne Durchkontaktierung ist das Löten deshalb etwas fummelig und die Anschlüsse des Displays sollten so lang wie möglich bleiben. Leichter ist es sicher, wie auf den Fotos vom Prototypen zu sehen, auch hier Sockel zu verwenden. Die Bilder zeigen noch den Prototypen mit einem anderen Platinenlayout. Das oben gezeigte Layout hat zwei dreipolige Stecker zur Vereinfachung der Kaskadierung. Die Schaltung lief bisher gut mit zwei NiCd-Zellen; heute habe ich sie erstmalig an einem Billig-LiPo 1S15C 420mAh betrieben. Ob das dauerhaft geht, weiß ich noch nicht. Quelltext/* ----------------------------------------------------------------------- * Title: 8x8 LED dot matrix slave-code * Author: Christoph Niessen * Date: 29.08.2010 * Hardware: ATtiny2313V * * Fuses siehe Seite 161 ... * * Fuse-Bits: 1=nicht 110=1,8 V 1=Reset-Pin geht * Fuse High 11011101 DebugWire|......|BOD BOD BOD| RSTDISBL * Fuse Low 11100010 Clockdevider|CKOUT|SUT SUT| CKSEL * 1=nicht 1=nicht 10= 0010= * teilen ausgeben slow internal 4MHz * * Slave liest von PD6 Befehle um den Bildschirmspeicher zu beeinflussen * Bildschirmspeicher ist COUNT Displays gross und umfasst deshalb * COUNT * 8 Bytes * Jeder Slave kennt den gesamten Bildschirmspeicher, zeigt aber nur * den eigenen 8-Spalten/8-Reihen-Anteil an. * Im ersten Ansatz hat der Bildschirmspeicher die Hoehe eines Displays. * Der erste Befehl ist "verschiebe Bildschirminhalt um eine Spalte und * fuege eine Spalte rechts hinzu". * Befehle haben TLV-Format wobei im Laengenfeld T und L nicht mitzaehlen. * eine dunkle Spalte anhaengen ist dann z.B. * 0x00, 0x01, 0x00 * spaeter koennen dann evtl auch gleich mehrere angehaengt werden: * 0x00, 0x04, 0xaa, 0x55, 0xaa, 0x55 fuegt "an-aus-Raster" ueber 4 Spalten an * Sinnvollerweise ist der Bildspeicher fuer solche Operationen so * organisiert, dass jede Spalte in einem Speicherwort (BYTE) steht. * * Bituebertragung: * ================ * * Ruhepegel ist "1" * * Feste Uebertragungsrate von 1 Bit in 2 Millisekunden * * Uebertragung einer "0" durch 667 uSekunden "0" und 1333 uSekunden "1" * Uebertragung eines Bytes LSB first * */ /* * ATtiny2313/4313/8313 * R8 16 - PD0 PB7 - 1 C4 * R7 15 - PD1 PB6 - 2 C2 * C7 14 - PA1 PB5 - 3 R2 * R1 13 - PA0 PB4 - 4 R3 * C5 12 - PD2 PB3 - 5 C1 * R6 11 - PD3 PB2 - 6 R5 * R4 10 - PD4 PB1 - 7 C3 * C8 9 - PD5 PB0 - 8 C6 * C * CPM12088 BA | * B3 B6 B1 B7 D2 B0 A1 D5 +-----+ * A0 o o o o o o o o | | * B5 o o o o o o o o _+_ | * B4 o o o o o o o o \ / | * D4 o o o o o o o o __V__ | * B2 o o o o o o o o | | * D3 o o o o o o o o R ---+-----+--- * D1 o o o o o o o o | * D0 o o o o o o o o * */ #define ACT_COL_1 PORTB |= (1<< PB3) #define ACT_COL_2 PORTB |= (1<< PB6) #define ACT_COL_3 PORTB |= (1<< PB1) #define ACT_COL_4 PORTB |= (1<< PB7) #define ACT_COL_5 PORTD |= (1<< PD2) #define ACT_COL_6 PORTB |= (1<< PB0) #define ACT_COL_7 PORTA |= (1<< PA1) #define ACT_COL_8 PORTD |= (1<< PD5) #define INACT_COL_1 PORTB &= ~(1<< PB3) #define INACT_COL_2 PORTB &= ~(1<< PB6) #define INACT_COL_3 PORTB &= ~(1<< PB1) #define INACT_COL_4 PORTB &= ~(1<< PB7) #define INACT_COL_5 PORTD &= ~(1<< PD2) #define INACT_COL_6 PORTB &= ~(1<< PB0) #define INACT_COL_7 PORTA &= ~(1<< PA1) #define INACT_COL_8 PORTD &= ~(1<< PD5) #define ACT_ROW_1 PORTA &= ~(1<< PA0) #define ACT_ROW_2 PORTB &= ~(1<< PB5) #define ACT_ROW_3 PORTB &= ~(1<< PB4) #define ACT_ROW_4 PORTD &= ~(1<< PD4) #define ACT_ROW_5 PORTB &= ~(1<< PB2) #define ACT_ROW_6 PORTD &= ~(1<< PD3) #define ACT_ROW_7 PORTD &= ~(1<< PD1) #define ACT_ROW_8 PORTD &= ~(1<< PD0) #define INACT_ROW_1 PORTA |= (1<< PA0) #define INACT_ROW_2 PORTB |= (1<< PB5) #define INACT_ROW_3 PORTB |= (1<< PB4) #define INACT_ROW_4 PORTD |= (1<< PD4) #define INACT_ROW_5 PORTB |= (1<< PB2) #define INACT_ROW_6 PORTD |= (1<< PD3) #define INACT_ROW_7 PORTD |= (1<< PD1) #define INACT_ROW_8 PORTD |= (1<< PD0) /* ------------------------------------------------------------------------- */ #define COUNT 4 // maximal 4 Displays enum cmd { ADD_RIGHT , CLEAR_ALL }; /* ------------------------------------------------------------------------- */ #include <inttypes.h> #include <stdlib.h> #include <string.h> #include <avr/io.h> #include <avr/interrupt.h> #include <avr/eeprom.h> #include <util/delay.h> #include <avr/pgmspace.h> /* ------------------------------------------------------------------------- */ #define CHAR_OFFSET 0x20 const uint8_t font[] PROGMEM = { 0x00, 0x00, 0x00, 0x00, 0x00, // 20 ' ' 0x00, 0x00, 0x5F, 0x00, 0x00, // 21 '!' 0x00, 0x03, 0x00, 0x03, 0x00, // 22 '"' 0x14, 0x7F, 0x14, 0x7F, 0x14, // 23 '#' 0x24, 0x2A, 0x7F, 0x2A, 0x12, // 24 '$' 0x43, 0x33, 0x08, 0x66, 0x61, // 25 '%' 0x36, 0x49, 0x56, 0x20, 0x50, // 26 '&' 0x00, 0x00, 0x03, 0x00, 0x00, // 27 ''' 0x00, 0x3E, 0x41, 0x00, 0x00, // 28 '(' 0x00, 0x00, 0x41, 0x3E, 0x00, // 29 ')' 0x2A, 0x1C, 0x7F, 0x1C, 0x2A, // 2A '*' 0x08, 0x08, 0x3E, 0x08, 0x08, // 2B '+' 0x00, 0x80, 0x40, 0x20, 0x00, // 2C ',' 0x08, 0x08, 0x08, 0x08, 0x08, // 2D '-' 0x00, 0x00, 0x40, 0x00, 0x00, // 2E '.' 0x40, 0x20, 0x1C, 0x02, 0x01, // 2F '/' 0x3E, 0x61, 0x5D, 0x43, 0x3E, // 30 '0' 0x02, 0x41, 0x7F, 0x40, 0x00, // 31 '1' 0x62, 0x51, 0x49, 0x49, 0x46, // 32 '2' 0x22, 0x49, 0x49, 0x49, 0x36, // 33 '3' 0x18, 0x14, 0x12, 0x79, 0x10, // 34 '4' 0x2F, 0x49, 0x49, 0x49, 0x31, // 35 '5' 0x3E, 0x49, 0x49, 0x49, 0x32, // 36 '6' 0x00, 0x01, 0x71, 0x0D, 0x03, // 37 '7' 0x36, 0x49, 0x49, 0x49, 0x36, // 38 '8' 0x26, 0x49, 0x49, 0x49, 0x3E, // 39 '9' 0x00, 0x00, 0x22, 0x00, 0x00, // 3A ':' 0x00, 0x00, 0x40, 0x24, 0x00, // 3B ';' 0x08, 0x14, 0x22, 0x41, 0x00, // 3C '<' 0x14, 0x14, 0x14, 0x14, 0x14, // 3D '=' 0x00, 0x41, 0x22, 0x14, 0x08, // 3E '>' 0x02, 0x01, 0x51, 0x09, 0x06, // 3F '?' 0x3E, 0x41, 0x5D, 0x55, 0x1E, // 40 '@' 0x7C, 0x0A, 0x09, 0x0A, 0x7C, // 41 'A' 0x7F, 0x49, 0x49, 0x49, 0x36, // 42 'B' 0x3E, 0x41, 0x41, 0x41, 0x22, // 43 'C' 0x7F, 0x41, 0x41, 0x41, 0x3E, // 44 'D' 0x7F, 0x49, 0x49, 0x49, 0x41, // 45 'E' 0x7F, 0x09, 0x09, 0x01, 0x01, // 46 'F' 0x3E, 0x41, 0x49, 0x49, 0x3A, // 47 'G' 0x7F, 0x08, 0x08, 0x08, 0x7F, // 48 'H' 0x00, 0x41, 0x7F, 0x41, 0x00, // 49 'I' 0x21, 0x41, 0x41, 0x41, 0x3F, // 4A 'J' 0x7F, 0x08, 0x14, 0x22, 0x41, // 4B 'K' 0x7F, 0x40, 0x40, 0x40, 0x40, // 4C 'L' 0x7F, 0x02, 0x04, 0x02, 0x7F, // 4D 'M' 0x7F, 0x02, 0x1C, 0x20, 0x7F, // 4E 'N' 0x3E, 0x41, 0x41, 0x41, 0x3E, // 4F 'O' 0x7F, 0x09, 0x09, 0x09, 0x06, // 50 'P' 0x3E, 0x41, 0x51, 0x21, 0x5E, // 51 'Q' 0x7F, 0x09, 0x19, 0x29, 0x46, // 52 'R' 0x26, 0x49, 0x49, 0x49, 0x32, // 53 'S' 0x01, 0x01, 0x7F, 0x01, 0x01, // 54 'T' 0x3F, 0x40, 0x40, 0x40, 0x3F, // 55 'U' 0x1F, 0x20, 0x40, 0x20, 0x1F, // 56 'V' 0x7F, 0x20, 0x10, 0x20, 0x7F, // 57 'W' 0x41, 0x22, 0x1C, 0x22, 0x41, // 58 'X' 0x01, 0x02, 0x7C, 0x02, 0x01, // 59 'Y' 0x61, 0x51, 0x49, 0x45, 0x43, // 5A 'Z' 0x00, 0x7F, 0x41, 0x41, 0x00, // 5B '[' 0x03, 0x04, 0x08, 0x10, 0x60, // 5C '\' 0x00, 0x41, 0x41, 0x7F, 0x00, // 5D ']' 0x04, 0x02, 0x01, 0x02, 0x04, // 5E '^' 0x40, 0x40, 0x40, 0x40, 0x40, // 5F '_' 0x00, 0x01, 0x02, 0x04, 0x00, // 60 '`' 0x38, 0x44, 0x44, 0x28, 0x7C, // 61 'a' 0x7F, 0x28, 0x44, 0x44, 0x38, // 62 'b' 0x38, 0x44, 0x44, 0x44, 0x28, // 63 'c' 0x38, 0x44, 0x44, 0x28, 0x7F, // 64 'd' 0x38, 0x44, 0x54, 0x54, 0x18, // 65 'e' 0x08, 0x7C, 0x0A, 0x02, 0x04, // 66 'f' 0x18, 0x54, 0x54, 0x54, 0x3C, // 67 'g' 0x7F, 0x08, 0x04, 0x04, 0x78, // 68 'h' 0x00, 0x48, 0x7A, 0x40, 0x00, // 69 'i' 0x00, 0x20, 0x44, 0x44, 0x3D, // 6A 'j' 0x7E, 0x10, 0x28, 0x44, 0x00, // 6B 'k' 0x00, 0x00, 0x42, 0x7E, 0x40, // 6C 'l' 0x78, 0x04, 0x78, 0x04, 0x78, // 6D 'm' 0x7C, 0x08, 0x04, 0x04, 0x78, // 6E 'n' 0x38, 0x44, 0x44, 0x44, 0x38, // 6F 'o' 0x7C, 0x18, 0x24, 0x24, 0x18, // 70 'p' 0x18, 0x24, 0x24, 0x18, 0x7C, // 71 'q' 0x04, 0x7C, 0x08, 0x04, 0x08, // 72 'r' 0x48, 0x54, 0x54, 0x54, 0x24, // 73 's' 0x04, 0x3E, 0x44, 0x20, 0x00, // 74 't' 0x3C, 0x40, 0x40, 0x20, 0x7C, // 75 'u' 0x1C, 0x20, 0x40, 0x20, 0x1C, // 76 'v' 0x3C, 0x40, 0x20, 0x40, 0x3C, // 77 'w' 0x44, 0x28, 0x10, 0x28, 0x44, // 78 'x' 0x04, 0x08, 0x70, 0x08, 0x04, // 79 'y' 0x44, 0x64, 0x54, 0x4C, 0x44, // 7A 'z' 0x38, 0x45, 0x44, 0x29, 0x7C, // 7B a-Umlaut 0x38, 0x45, 0x44, 0x45, 0x38, // 7C o-Umlaut 0x3C, 0x41, 0x40, 0x21, 0x7C // 7D u-Umlaut }; /* ------------------------------------------------------------------------- */ const prog_char LAUFSCHRIFT[] PROGMEM = "Lauflicht C. Niessen... "; /* ------------------------------------------------------------------------- */ uint8_t slave_ee EEMEM = 0; // stores the slave number in eeprom static uint8_t screen_mem[8*COUNT]; // screen memory static uint8_t active_col; // active col static volatile uint16_t counter = 0; // used for delay function static uint8_t slave = 0; // actual slave number from eeprom static uint8_t *slave_mem = screen_mem; // screen memory for this slave static uint8_t Sync = (0==0); // Empfangskanal synchronisiert ? static uint8_t get_command[10]; static uint8_t get_lastbyte=0, get_bitcount=0, *get_ptr=get_command; // prototypes void delay_ms(uint16_t delay); void show_char(); void clear_screen(void); /* ------------------------------------------------------------------------- */ /* * ISR TIMER0_OVF_vect * Handles overflow interrupts of timer 0. * * 4MHz * ---- * Prescaler 8 ==> 1953.1 Hz * Complete display = 244 Hz * * Stand 2010-09-19 125 +/- 4 Zyklen also knapp 32 uSekunden */ volatile uint8_t row_or_col=0; ISR(TIMER0_OVF_vect) { uint8_t col; counter++; PORTA = (1 << PA0); PORTB = (1 << PB5) | (1 << PB4) | (1 << PB2) | (0 << PB0) ; INACT_COL_5; INACT_COL_8; INACT_ROW_4; INACT_ROW_6; INACT_ROW_7; INACT_ROW_8; // next col active_col = (active_col+1) % 8; if(active_col==0)row_or_col=~row_or_col; if(row_or_col==0){ col = slave_mem[active_col]; if ((col & 0x01) != 0 ) ACT_ROW_1; if ((col & 0x02) != 0 ) ACT_ROW_2; if ((col & 0x04) != 0 ) ACT_ROW_3; if ((col & 0x08) != 0 ) ACT_ROW_4; if ((col & 0x10) != 0 ) ACT_ROW_5; if ((col & 0x20) != 0 ) ACT_ROW_6; if ((col & 0x40) != 0 ) ACT_ROW_7; if ((col & 0x80) != 0 ) ACT_ROW_8; // activate col asm volatile ("mov r30,%0":: "d" (active_col) : "r30" ); asm volatile ("add r30,r30":::"r30"); asm volatile ("ldi r31,lo8(0)":::"r31"); asm volatile ("subi r30,lo8(-(gs(SprngLst1)))":::"r30"); asm volatile ("sbci r31,hi8(-(gs(SprngLst1)))":::"r31"); asm volatile ("ijmp\n" "SprngLst1:\n" ); ACT_COL_1;asm volatile ("rjmp FERTIG1"); ACT_COL_2;asm volatile ("rjmp FERTIG1"); ACT_COL_3;asm volatile ("rjmp FERTIG1"); ACT_COL_4;asm volatile ("rjmp FERTIG1"); ACT_COL_5;asm volatile ("rjmp FERTIG1"); ACT_COL_6;asm volatile ("rjmp FERTIG1"); ACT_COL_7;asm volatile ("rjmp FERTIG1"); ACT_COL_8;asm volatile ("FERTIG1:"); } else { col = (1<<active_col); asm volatile ("mov r25,%0":: "d" (col) : "r25" ); asm volatile ("lds r26,slave_mem":::"r26"); asm volatile ("lds r27,slave_mem+1":::"r27"); asm volatile ("ld r24,X+" "\n\t" "and r24,r25" "\n\t" "breq .+2":::"r24", "r26", "r27"); ACT_COL_1; asm volatile ("ld r24,X+" "\n\t" "and r24,r25" "\n\t" "breq .+2":::"r24", "r26", "r27"); ACT_COL_2; asm volatile ("ld r24,X+" "\n\t" "and r24,r25" "\n\t" "breq .+2":::"r24", "r26", "r27"); ACT_COL_3; asm volatile ("ld r24,X+" "\n\t" "and r24,r25" "\n\t" "breq .+2":::"r24", "r26", "r27"); ACT_COL_4; asm volatile ("ld r24,X+" "\n\t" "and r24,r25" "\n\t" "breq .+2":::"r24", "r26", "r27"); ACT_COL_5; asm volatile ("ld r24,X+" "\n\t" "and r24,r25" "\n\t" "breq .+2":::"r24", "r26", "r27"); ACT_COL_6; asm volatile ("ld r24,X+" "\n\t" "and r24,r25" "\n\t" "breq .+2":::"r24", "r26", "r27"); ACT_COL_7; asm volatile ("ld r24,X+" "\n\t" "and r24,r25" "\n\t" "breq .+2":::"r24", "r26", "r27"); ACT_COL_8; // activate col asm volatile ("mov r30,%0":: "d" (active_col) : "r30" ); asm volatile ("add r30,r30":::"r30"); asm volatile ("ldi r31,lo8(0)":::"r31"); asm volatile ("subi r30,lo8(-(gs(SprngLst)))":::"r30"); asm volatile ("sbci r31,hi8(-(gs(SprngLst)))":::"r31"); asm volatile ("ijmp\n" "SprngLst:\n" ); ACT_ROW_1;asm volatile ("rjmp FERTIG"); ACT_ROW_2;asm volatile ("rjmp FERTIG"); ACT_ROW_3;asm volatile ("rjmp FERTIG"); ACT_ROW_4;asm volatile ("rjmp FERTIG"); ACT_ROW_5;asm volatile ("rjmp FERTIG"); ACT_ROW_6;asm volatile ("rjmp FERTIG"); ACT_ROW_7;asm volatile ("rjmp FERTIG"); ACT_ROW_8;asm volatile ("FERTIG:"); }; } /* ------------------------------------------------------------------------- */ /* * delay_ms nutzt die regelmaessigen Unterbrechungen mit ca. 2 KHz * also ca. 0.5 Millisekunden pro Tick * Maximal ca. 30 Sekunden */ void delay_ms(uint16_t delay) { uint16_t t = delay * 2; counter = 0; while (counter < t) {} } /* ------------------------------------------------------------------------- */ void add_right(uint8_t x){ uint8_t i; for(i=0;i<8*COUNT-1;i++) screen_mem[i]=screen_mem[i+1]; screen_mem[COUNT*8-1]=x; } /* ------------------------------------------------------------------------- */ void clear_all(){ uint8_t i; for(i=0;i<8*COUNT;i++) screen_mem[i]=0; } /* ------------------------------------------------------------------------- */ /* Schleifendurchlauf hat 6 Zyklen, also 1,5 uSekunden Achtung: Overhead durch ISR ist nicht beruecksichtigt 1cc: 00 00 1 nop 1ce: 01 96 2 adiw r24, 0x01 ; 1 1d0: 84 17 1 cp r24, r20 1d2: 95 07 1 cpc r25, r21 1d4: d8 f3 1 brcs .-10 ; 0x1cc <send_one_bit+0x14> */ #define TOTAL (1333-2) // Korrektur fuer Overhead etc. #define TOTAL33 (TOTAL/3) #define TOTAL66 (2*TOTAL33) /* ------------------------------------------------------------------------- */ void send_one_bit(uint8_t b){ uint16_t breite,i; breite=(b==0)?TOTAL33:TOTAL66; PORTD &= ~(1<< PD6); for(i=0;i<breite;i++)asm volatile ("nop"); breite=TOTAL-breite; PORTD |= (1<< PD6); for(i=0;i<breite;i++)asm volatile ("nop"); } /* ------------------------------------------------------------------------- */ void send_one_byte(uint8_t b){ uint8_t i,j; for(i=0;i<8;i++){ j=b&0x01; send_one_bit(j); b>>=1; }; } /* ------------------------------------------------------------------------- */ void send_one_row_right(uint8_t b){ send_one_byte(ADD_RIGHT); send_one_byte(1); send_one_byte(b); } /* ------------------------------------------------------------------------- */ /* 1 if condition is false (no skip) 2 if condition is true (skip is executed) and the instruction skipped is 1 word 3 if condition is true (skip is executed) and the instruction skipped is 2 words 15e: 01 96 2 adiw r24, 0x01 ; 1 160: 86 9b 1(,2,3) sbis 0x10, 6 ; 16 162: fd cf 2 rjmp .-6 ; 0x15e <get_one_bit+0xa> Schleife benoetigt pro Durchlauf 5 Zyklen, also ca. 1,25 uSekunden in 666 uSekunden werden ca. 533 Durchlaeufe geschafft, in 1,33 Millesekunden etwa 1066; beides Werte, die in 16 Bit passen. 800 ist genau die Mitte. Der Timer-Interrupt schlaegt alle 500 uSekunden zu und benoetigt ca. 90 Zyklen fuer eine 0 (666 uSekunden) kommt er ein oder zwei Mal; entsprechend ist die erwartete Breite hier 18 oder 36 kleiner, also 505 bzw 497. Fuer eine 1 (1333 uSekunden) kommt er zwei oder drei Mal; entsprechend ist die erwartete Breite hier 36 oder 54 kleiner, also 1030 oder 1012 */ uint8_t get_one_bit(){ uint16_t breite=0; // warte bis fallende Flanke ... while ( (PIND & (1 << PD6))){ if(1500<breite++){ get_lastbyte=0; get_bitcount=0; get_ptr=get_command; } } // ... und zaehle bis zur steigenden. breite=0; while ((!(PIND & (1 << PD6))) || breite <20) breite++; return( (breite<800)?0:0x80 ); } /* ------------------------------------------------------------------------- */ uint8_t get_one_byte(){ uint8_t b,i,rc; rc=0; for(i=0;i<8;i++){ b=get_one_bit(); rc>>=1; // LSB first rc|=b; } return(rc); } /* ------------------------------------------------------------------------- */ uint8_t get_cmd(){ uint8_t i,l; get_ptr=get_command; *get_ptr++=get_one_byte(); *get_ptr++=l=get_one_byte(); if(l<=sizeof(get_command)-2) for(i=0;i<l;i++)*get_ptr++=get_one_byte(); return(get_ptr-get_command); } /* ------------------------------------------------------------------------- */ void main(void) { uint8_t i = 0; // timer 0 setup, prescaler 8 TCCR0B |= (1 << CS01); // enable timer 0 interrupt TIMSK |= (1 << TOIE0); // define outputs DDRA = 0x03; DDRB = 0xFF; DDRD = 0x3F; // inaktiviere Zeilen(1) and Spalten(0) aber Pull-UP fuer den Eingang D6 PORTA = (1 << PA0); PORTB = (1 << PB5) | (1 << PB4) | (1 << PB2) | (0 << PB0) ; PORTD = (1 << PD4) | (1 << PD3) | (1 << PD1) | (1 << PD0) | (1 << PD6); sei(); slave_mem=screen_mem; if(PIND & (1<<PD6)){ slave = eeprom_read_byte(&slave_ee); if(slave>=COUNT){ slave=0; eeprom_write_byte(&slave_ee, slave); }; } else { slave=0; while( !(PIND & (1<<PD6)) ){ uint8_t x,k; slave=((slave+1)%COUNT); x=(slave+'0'-' ');x+=(x<<2); for(k=0;k<5;k++) slave_mem[k]=pgm_read_byte(&font[x+k]); delay_ms(2000); }; eeprom_write_byte(&slave_ee, slave); }; if(slave==0){ slave_mem[0]=0x7f; // ******* slave_mem[1]=0x02; // * slave_mem[2]=0x04; // * slave_mem[3]=0x02; // * slave_mem[4]=0x7f; // ******* delay_ms(1000); // Master wartet laenger als der Slave !!! } else { int k,x; x=(slave+'0'-' ');x+=(x<<2); for(k=0;k<5;k++) slave_mem[k]=pgm_read_byte(&font[x+k]); } delay_ms(1500); slave_mem=screen_mem+8*slave; switch(slave){ case 0: // Master DDRD |= (1<< PD6); i=0; while(1==1){ uint8_t j,k,c; uint16_t h1,h2; c=pgm_read_byte(&LAUFSCHRIFT[0]); for(j=1;c!=0;j++){ h1=c-' '; h1+=h1<<2; // *5 for(k=0;k<5;k++){ h2=pgm_read_byte(&font[h1++]); send_one_row_right(h2);add_right(h2); delay_ms(50); }; send_one_row_right(0);add_right(0); delay_ms(50); c=pgm_read_byte(&LAUFSCHRIFT[j]); } delay_ms(50); } break; default: // Slave while (1) { if(get_cmd()>=2) switch(get_command[0]){ case ADD_RIGHT: add_right(get_command[2]); break; case CLEAR_ALL: clear_all(); break; } } }; } /* ------------------------------------------------------------------------- */ |