Source file: /~heha/mb-iwp/Anzeige mit VQC10/Firmware.zip/led.c

/*
Funktionen zum Multiplexen der LED-Anzeige, bestehend aus bis zu 10 VQC10.
*/

#include "led.h"
#include <string.h>		// memcpy
#define UNROLLED

/*********************************
 * Multiplexen einer LED-Anzeige *
 *********************************/
// Eingangslatch für Katoden der VQC10 (LOW-aktiv)
// Alle Katoden liegen auf 16-bit-Port A (Ports 1 und 2)
const BYTE K[10]={		// lies: Port A Bit 6 usw.
 0x06,0x04,0x0C,0x0F,0x09,0x07,0x0E,0x03,0x0B,0x08};
#define KOMASK 0x9250	//0b1001001001010000
#define KUMASK 0x4988	//0b0100100110001000
#define NOMASK ~(KOMASK|KUMASK)	// unbenutzte (6) Bits, die immer 1 sein müssen
#ifndef UNROLLED
// Taktleitungen, für jede Zeichenspalte eine (L/H-Flanke)
// Diese sind über die Ports A,B,C verstreut
const BYTE CL[20]={
 0x1C,0x1B,0x1A,0x19,0x13,0x10,0x0D,0x0A,0x05,0x02,
 0x25,0x20,0x2F,0x2E,0x2D,0x2C,0x2B,0x2A,0x29,0x28};
#endif
// Anoden der VQC10 über p-Kanal-MOSFET (also LOW-aktiv), obere Reihe
// G7 aktiviert die Betriebsspannung der VQC10 (Stromfresser?)
const BYTE G[8]={
 0x18,0x12,0x21,0x24,0x11,0x00,0x01  ,0x14};
// Anodenumordnung (von G auf Zeile, untere Reihe)
const BYTE AU[7]={6,4,5,1,0,3,2};	// untere Reihe

const WORD BitTab[16]={BV(0),BV(1),BV(2),BV(3),BV(4),BV(5),BV(6),BV(7),
 BV(8),BV(9),BV(10),BV(11),BV(12),BV(13),BV(14),BV(15)};

static inline void SetHigh(BYTE p) {
 volatile WORD *port=OUTADDR(p);
 *port|=BitTab[p&0x0F];
}

static inline void SetLow(BYTE p) {
 volatile WORD *port=OUTADDR(p);
 *port&=~BitTab[p&0x0F];
}
#ifdef UNROLLED
# define Pulse(k,p,b) PAOUT=*k++, P##p##OUT&=~BV(b), P##p##OUT|=BV(b)
# define PulseAll(k) \
 Pulse(k,4,4),\
 Pulse(k,4,3),\
 Pulse(k,4,2),\
 Pulse(k,4,1),\
 Pulse(k,3,3),\
 Pulse(k,3,0),\
 Pulse(k,2,5),\
 Pulse(k,2,2),\
 Pulse(k,1,5),\
 Pulse(k,1,2),\
 Pulse(k,5,5),\
 Pulse(k,5,0),\
 Pulse(k,6,7),\
 Pulse(k,6,6),\
 Pulse(k,6,5),\
 Pulse(k,6,4),\
 Pulse(k,6,3),\
 Pulse(k,6,2),\
 Pulse(k,6,1),\
 Pulse(k,6,0);
#else
static inline void PulseLow(BYTE p) {
 volatile WORD *port=OUTADDR(p);
 WORD mask=BitTab[p&0x0F];
 *port&=~mask;
 *port|=mask;
}
#endif
WORD kat[2][7][20];	// Voreingestellte Katodenwerte (10 bit), die freien 6 bit müssen 1 sein = 560 Byte
BYTE an;		// Aktuell aktive Anode
BYTE ch;		// Änderung (Bit 1), Sturz (Bit 0)

#if 1
// ISR: Compare2: LEDs ausschalten (globale Helligkeitseinstellung)
interrupt (TIMER1_A1_VECTOR) __attribute__((noint_hwmul)) LedOff() {
 switch (TA1IV) {
  case 2: SetHigh(G[an]); break;	// LED-Zeile AUS
#ifndef UNROLLED
  case 4: {	// ISR: Compare1: LEDs umschalten auf zweites Katoden-Array
   WORD*k=kat[1][an];
   for (int i=0; i<20; i++,k++) {
    PAOUT=*k;
    PulseLow(CL[i]);	// Taktimpuls ausgeben, Datenübernahme bei L-H-Flanke
   }
  }break;
#endif
 }
}
#endif

// ISR: Timer-Start
// Die Interruptserviceroutine gibt für eine von 7 LED-Zeilen alle
// Katodenwerte (20 x 10 bit) aus. Diese werden im Array <kat> bereit gehalten
// TODO: TAKTFREQUENZ HOCHSETZEN!
interrupt (TIMER1_A0_VECTOR) __attribute__((noint_hwmul)) LedLayer0() {
 register BYTE a=an;
 SetHigh(G[a]);
 a++; if (a>=7) a=0;
 WORD*k=kat[0][a];	// Multiplizierer??
#ifdef UNROLLED
 PulseAll(k);
#else
 for (int i=0; i<20; i++,k++) {
  PAOUT=*k;
  PulseLow(CL[i]);	// Taktimpuls ausgeben, Datenübernahme bei L-H-Flanke
 }
#endif
 SetLow(G[a]);
 an=a;
}

// Für den Fall gewünschter Vollgrafik liegt hier die Bitmap aus.
// Diese ist - wie beim KC85 - in Byte-Spalten organisiert
BYTE bitmap[2][2][20][7];	// 2 Ebenen, 2 Zeilen, 20 Zeichen (à 5 Bits), 7 Pixelzeilen = 560 Byte
// Ebenen-Zuordnung
// LY0	LY1	Helligkeit
// 0	0	aus
// 1	0	normal	(60%)
// 0    1	dunkler	(40%)
// 1    1	maximal (100%)

// Daten-Bereitstellung (immer wenn sich an <bitmap> etwas ändert)
// 
void mkkat() {
 for (int z=0; z<7; z++) {
  int ao=ch&0?6-z:z;		// obere Anode
  int au=AU[ao];		// untere Anode
  for (int s=0; s<20; s++) {	// Zeichenspalten durchgehen
   WORD ko0=NOMASK, ku0=NOMASK, ko1=NOMASK, ku1=NOMASK, *kp;
   for (int b=0; b<5; b++) {	// Bitnummer
    if (ch&1) {			// gestürzt?
     if (bitmap[0][1][19-s][au]&BitTab[b]) ko0|=BitTab[K[b]];	// obere Reihe
     if (bitmap[0][0][19-s][ao]&BitTab[b]) ku0|=BitTab[K[b+5]];// untere Reihe
     if (bitmap[1][1][19-s][au]&BitTab[b]) ko1|=BitTab[K[b]];	// obere Reihe
     if (bitmap[1][0][19-s][ao]&BitTab[b]) ku1|=BitTab[K[b+5]];// untere Reihe
    }else{			// normal
     if (bitmap[0][0][s][ao]&BitTab[4-b]) ko0|=BitTab[K[b]];	// obere Reihe
     if (bitmap[0][1][s][ao]&BitTab[4-b]) ku0|=BitTab[K[b+5]];// untere Reihe
     if (bitmap[1][0][s][ao]&BitTab[4-b]) ko1|=BitTab[K[b]];	// obere Reihe
     if (bitmap[1][1][s][ao]&BitTab[4-b]) ku1|=BitTab[K[b+5]];// untere Reihe
    }
   }
   kp=&kat[0][ao][s];		// Katoden-Adresse in Bearbeitung
   *kp=(*kp&~KOMASK)|ko0;	// Katoden ändern
   kp+=7*20;
   *kp=(*kp&~KOMASK)|ko1;
   kp=&kat[0][au][s];
   *kp=(*kp&~KUMASK)|ku0;
   kp+=7*20;
   *kp=(*kp&~KUMASK)|ku1;
  }
 }
 ch&=~2;			// Dirty-Bit löschen
}

WORD text[2][20]={		// 2 Zeilen, 20 Zeichen = 40 Byte
 {L"Wiederbelebung      "},
 {L"VQC10  WF Berlin    "}};
BYTE attr[2][20];		// Attribut-Bits = 40 Byte
// Blinken
// Intensität HIGH
// Intensität LOW
// unterstrichen
BYTE curs[2];			// Kursorposition (x/y, oder Adresse?)

BYTE charmap[256][7];		// Nur Bits 0..5 benutzt = 1792 Byte

// Bitmap-Bereitstellung (im Text-Betrieb, wenn sich am Darstell-Text etwas ändert)
void mkbitmap() {
 for (int z=0; z<2; z++) {
  for (int s=0; s<20; s++) {
   int c=text[z][s];
   memcpy(bitmap[0][z][s],charmap[c],7);
   if (attr[z][s]&2) {		// hell?
    memcpy(bitmap[1][z][s],charmap[c],7);
   }
  }
 }
 ch|=2;				// Grafik-Dirty-Bit setzen
 ch&=~4;			// Text-Dirty-Bit löschen
}

void ledinit() {
// memset(kat,-1,sizeof(kat));
 PAOUT=0xFFFF;
 PADIR=0xFFFF;
 PAREN=0xFFFF;
 PBOUT=0xFFFF;
 PBDIR=0x1FFF;		// die drei freien Pins erst mal nicht festnageln
 PBREN=0xFFFF;
 PCOUT=0xFFFF;
 PCDIR=0xFFFF;
 PCREN=0xFFFF;
 PJREN=0xFFFF;
// außerdem Zeichenbildtabelle aus ROM kopieren
 memcpy(charmap,font,sizeof(charmap));
 bitmap[0][0][0][0]=0xFF;
 bitmap[0][0][1][1]=0x0F;
 bitmap[0][0][2][2]=0x17;
 bitmap[0][0][3][3]=0x1B;
 bitmap[0][0][4][4]=0x1D;
 bitmap[0][0][5][5]=0x1E;
 bitmap[0][0][6][6]=0x1F;
 bitmap[0][0][7][7]=0x1F;

 bitmap[0][1][0][0]=0xFF;
 bitmap[0][1][1][1]=0x0F;
 bitmap[0][1][2][2]=0x17;
 bitmap[0][1][3][3]=0x1B;
 bitmap[0][1][4][4]=0x1D;
 bitmap[0][1][5][5]=0x1E;
 bitmap[0][1][6][6]=0x1F;
 bitmap[0][1][7][7]=0x1F;
 ch|=6;				// Dirty-Bit setzen
// Timer-ISR starten (ca. 700 Hz erforderlich)
// mit zwei Compare-Interrupts
// Vorerst 1048576 Hz
// TA1CCTL2 =
 TA1CCTL1 =
 TA1CCTL0 = CCIE;	// Interrupt EIN
 TA1CCR0  = 1498;	// 700 Hz, Gesamtzählumfang
 TA1CCR1  = 1000;	// Helligkeit
// TA1CCR2  = 600;	// halbe Helligkeit
 TA1CTL   = 0x0210;	// SMCLK, ohne Teiler, CTC bis TA1CCR0
 SetLow(G[7]);
}
Detected encoding: UTF-80