/*
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]);
}
Vorgefundene Kodierung: UTF-8 | 0
|