#include "../config.h" #include "../makros.h" #include <avr/interrupt.h> #include <avr/io.h> #include <avr/wdt.h> #include "borg_hw.h" #define PIN_DATA PB4 #define PIN_CLK PB3 #define PIN_STR PB2 //Der Puffer, in dem das aktuelle Bild gespeichert wird unsigned char pixmap[NUMPLANE][NUM_ROWS][LINEBYTES]; //zur nächsten Zeile weiterschalten inline void nextrow(uint8_t row){ //Die Zustände von der vorherigen Zeile löschen PORTC &= 0xF0; PORTD &= 0x0F; PORTB &= 0xFC; //kurze Warteschleife, damit die Treiber auch wirklich ausschalten unsigned char i; for(i=0;i<10;i++){ asm volatile("nop"); } if (row == 0){ //Zeile 0: Das erste Schieberegister initialisieren PORTB &= ~(1<<PIN_DATA); // zeile ist aktiv auf low PORTB |= (1<<PIN_CLK); PORTB &= ~(1<<PIN_CLK); PORTB |= (1<<PIN_DATA); } else { //In jeder anderen Zeile einfach nur einen weiter schieben PORTB |= (1<<PIN_CLK); PORTB &= ~(1<<PIN_CLK); } //noch eine Warteschleife, damit die Zeilentreiber bereit sind for(i=0;i<20;i++){ asm volatile("nop"); } } //Eine Zeile anzeigen inline void rowshow(unsigned char row, unsigned char plane){ //Je nachdem, welche der Ebenen wir Zeichnen, die Zeile verschieden lange Anzeigen switch (plane){ case 0: TCNT0 = 0x100-12; break; case 1: TCNT0 = 0x100-20; break; case 2: TCNT0 = 0x100-50; } uint8_t tmp, tmp1; //die Daten für die aktuelle Zeile auf die Spaltentreiber ausgeben tmp = pixmap[plane][row][0]; tmp1 = pixmap[plane][row][1]; PORTC = ( PORTC & 0xF0 ) | (tmp & 0x0F); PORTD = ( PORTD & 0x0F ) | (tmp & 0xF0); PORTB = ( PORTB & 0xFC ) | (tmp1 & 0x03); } //Dieser Interrupt wird je nach Ebene mit 50kHz 31,25kHz oder 12,5kHz ausgeführt SIGNAL(SIG_OVERFLOW0) { static unsigned char plane = 0; static unsigned char row = 0; //Watchdog zurücksetzen wdt_reset(); //Zeile und Ebene inkrementieren if(++plane==NUMPLANE){ plane=0; if(++row == NUM_ROWS){ row = 0; } nextrow(row); } //Die aktuelle Zeile in der aktuellen Ebene ausgeben rowshow(row, plane); } void timer0_off(){ cli(); /* COLPORT1 = 0; COLPORT2 = 0; ROWPORT = 0; */ TCCR0 = 0x00; sei(); } // Den Timer, der denn Interrupt auslöst, initialisieren void timer0_on(){ /* TCCR0: FOC0 WGM00 COM01 COM00 WGM01 CS02 CS01 CS00 CS02 CS01 CS00 0 0 0 stop 0 0 1 clk 0 1 0 clk/8 0 1 1 clk/64 1 0 0 clk/256 1 0 1 clk/1024 */ TCCR0 = 0x03; // clk/64 TCNT0 = 0xFF-20; // reset timer TIMSK |= (1<<TOIE0); // Compare match Interrupt on } void borg_hw_init(){ // Nötige Pins auf Ausgang DDRC=0x0F; // PC0-3 - Row 0-3 DDRD=0xF4; // PD4-7 - Row 4-7 PD2 - Masse für Joy DDRB=0x1F; // PB0-1 - Row 8-9 PB2-3 - STR, CLK, d // Alle Spalten erstmal aus, clk aus, d und str an // PC4-5, PD013 Pullup an PORTC=0x30; PORTD=0x0B; PORTB=0x14; timer0_on(); //Watchdog Timer aktivieren wdt_reset(); wdt_enable(0x00); // 17ms Watchdog }