/*****************************************
* Ausgabe-Latches nibbleweise ansteuern *
*****************************************/
#include "hardware.h"
#include <arch/avr/gcc.h> // _NOP()
#include <dev/nvmem.h> // NutNvMemSave()
#include <sys/timer.h> // NutSleep()
// Kopie der Ausgabe-Latches (Startupcode setzt Null)
static u_char OutData[8];
// Alles ist in der Schaltung systematisch verkehrtherum:
// * Die LEDs sind LOW-aktiv
// * Die Bits sind vertauscht, d.h. die linke LED ist Bit 7
// * Die Latches sind vertauscht, d.h. die linke LED hängt an Latch 7
// Diese Probleme werden in den hardwarenahen Treiberfunktionen egalisiert.
// Bit-Anordnung im Byte stürzen
static u_char bitswap(u_char b) {
u_char i,r;
for (i=0; i<8; i++) {
asm volatile("ror %1\n\t"
"rol %0\n\t"
:"=&r"(r)
:"r"(b));
}
return r;
}
// Ausgabetreiber initialisieren
extern void OutInit(void) {
DDRB=DDRD=PORTD=PORTB=0xFF;
// alles Ausgänge, alle Latches transparent, alle LEDs aus
PORTB=0; // alle Latches haltend
}
// Ein Byte ausgeben, dabei steuert i = 0 die linken 8 LEDs an.
// Bit 0 von b steuert die jeweils linke LED an.
// Bit-Schaltzustand: 1 = EIN und 0 = AUS
// i muss < 8 sein!
static void OutOut(u_char i, u_char b) {
PORTD=~bitswap(b); // LEDs sind allesamt low-aktiv
PORTB=0x80>>i; // Latch transparent schalten
_NOP();
PORTB=0;
}
static void OutUpdate(u_char i) { // i = 0..7
OutOut(i,OutData[i]);
}
// Alle Latches aktualisieren (bspw. nach EEPROM rücklesen)
void OutUpdateAll(void) {
u_char i;
for (i=0; i<8; i++) OutUpdate(i);
}
// Schaltzustand aus EEPROM laden und ausgeben
extern void OutRestore(void) {
NutNvMemLoad(CONFOUT_EE_OFFSET,OutData,8);
OutUpdateAll();
}
// Byte setzen und in EEPROM schreiben
static void OutSetByte(u_char i, u_char b) {
if (i>=8) return;
if (OutData[i]==b) return; // nichts tun wenn unverändert
OutData[i]=b;
OutUpdate(i);
NutNvMemSave(CONFOUT_EE_OFFSET+i,OutData+i,1);
}
// Nibble ausgeben
// port = 1..16 (v.l.n.r.), nibble = 0..15
static void OutSetNibble(u_char port, u_char nibble) {
u_char i=--port>>1,b;
if (port&1) b=(OutData[i]&0x0F)|(nibble<<4); // High-Teil
else b=(OutData[i]&0xF0)|(nibble&0x0F); // Low-Teil
OutSetByte(i,b);
}
// Bootkonfiguration wählen (von WWBMU wird nur 1-aus-4 unterstützt)
// port = 1..16, sel = 0..4 (0 = keine LED, 1..4 = LED 1-aus-4)
void OutSetBootsel(u_char port, u_char sel) {
OutSetNibble(port,sel?1<<(sel-1):0);
}
// Angelegte Bootkonfiguration abfragen
// (so wie WWBMU es tut; das niederwertigste Bit entscheidet)
// port = 1..16, liefert 0 (kein Bit gesetzt) bis 4 (nur MSB gesetzt)
u_char OutGetBootsel(u_char port) {
u_char b=OutData[--port>>1],r=0;
if (port&1) b>>=4;
b&=0x0F;
if (b) for(;;) {
r++;
if (b&1) break;
b>>=1;
}
return r;
}
// Eindrucksvolle Funktionsprobe
// Hinterlässt alle LEDs ausgeschaltet, ändert OutData nicht.
// Dauer: 20 ms * 128 = 2,5 Sekunden
// Mehr Zeit für Spielerei ist nicht, weil bei Stromzuschaltung
// die angeschlossenen Rechner ihre Bootkonfiguration haben wollen.
void OutKnightrider(void) {
u_char i,b;
for (i=0; i<8; i++) { // von links nach rechts
for (b=1; b; b<<=1) {
OutOut(i,b);
NutSleep(20);
}
OutOut(i,b);
}
for (; --i<8;) { // von rechts nach links
for (b=0x80; b; b>>=1) {
OutOut(i,b);
NutSleep(20);
}
OutOut(i,b);
}
}
Detected encoding: UTF-8 | 0
|