Quelltext /~heha/ewa/Kram/Solarpaneel.zip/C-Programm/tableau.c

/*
Projekt: Photovoltaik-Präsentation "solar" mit MSP430
Detail: Steuerung der vierstelligen Siebensegmentanzeigen mit Bargraf (TablXxx)
	Der verwendete Timer ist gleichzeitig 1-kHz-Taktquelle
	für TickCount und (später) für regelmäßige Aufgaben.
060703	erstellt
*/
#include "solar.h"
#include <string.h>	// memcpy
#include <stdlib.h>	// div, div_t

/* Hardware-Anschluss:
 P1.0 .. P1.3 = 4 Adressleitungen für 1-aus-16-Dekoder
 		(zusammengesetzt aus 2x 74HCT138) zur
 		"Taktung" der 16 8-fach-Flipflops auf den Tableaus 1-8
 		(diese im Wechsel, xxx0=Katode, xxx1=Anode)
 P1.4 = Auswahl (low-aktiv) des 1-aus-16-Dekoders
 P1.5 = (frei, war als PWM-Ausgang statt D/A-Wandler vorgesehen)
 P1.6 = "Taktung" Anoden-Flipflop des 9. Tableaus
 P1.7 = "Taktung" Katoden-Flipflop des 9. Tableaus
 P2 = Datenport (nur Ausgabe)
 Die Flipflops übernehmen die Daten mit der steigenden Flanke.
 */

/************************************************************************
 * Zuordnung "Stelle zu Anode" innerhalb TTableau:
 * Index 0 -> Ziffer rechts, regulär: Segment A = Bit0 bis DP = Bit7
 * Index 1 -> 3. Ziffer
 * Index 2 -> 2. Ziffer
 * Index 3 -> Ziffer links
 * Index 4..7 -> Leuchtband byteweise von rechts nach links
 * Das Leuchtband arbeitet bitweise mit Bit0 = links.
 ************************************************************************/
// Bitspeicher als Backup für die insgesamt 64*9 = 576 LEDs
TTableau TablLeds[9];	// Es gibt 9 Tableaus auzusteuern

/************************************************************************
 * Eine beliebige Menge von Ziffern (und Balkensegmenten:-) kann mit
 * voller Helligkeit dargestellt werden, zur Hervorhebung u.ä.
 * Gesetzte Bits der Quelle "Ano2Bit" aktivieren die volle Helligkeit.
 * Alle anderen Stellen werden zur Energieeinsparung kürzer angesteuert.
 * (Man könnte auch sagen, dass die 15-Ohm-Widerstände zu klein sind...)
 ************************************************************************/
BYTE TablHiliteDigit[9];

//const BYTE Tableau_Ano2Bit[]={	// Ordnung im (leichten) Chaos
// 0x08,0x04,0x02,0x01,0x80,0x40,0x20,0x10};

static UINT gAnode;	// Nummer der gerade angesteuerten Anode, 0..7

static const BYTE Ziffern[]={
 0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x27,0x7F,0x6F,	//0..9
 0x77,0x7C,0x39,0x5E,0x79,0x71};			//A..F
/* _       _   _       _   _   _   _   _   _       _       _   _
  | |   |  _|  _| |_| |_  |_  | | |_| |_| |_| |_  |    _| |_  |_ 
  |_|   | |_   _|   |  _| |_|   | |_|  _| | | |_| |_  |_| |_  | 
Alternativen (Strom sparend):  _       _
   _                      |_    |     |_|          _
  |_|                     |_|   |       |         |_
*/

/************************************************************************
 * Led-Anzeigen (Tableaus) zur Multiplex-Ausgabe initialisieren:
 * Ports auf Ausgabe schalten, Timer starten
 * Timer_A wird auch zur Synchronisation der A/D-Umsetzung verwendet!
 ************************************************************************/
void TablInit(void){
 P1DIR  = 0xFF;		// "Adressleitungen" B[0..4],A9,K9, 1 freier Ausgang
 P2DIR  = 0xFF;		// "Datenleitungen" T[0..7]
 TACTL = 0x0214;	// SMCLK, kein Vorteiler, bis CCR0, Timer-Neustart
 CCTL0 = 0x0010;	// CCR0 Interrupts EIN
 CCTL1 = 0x0070;	// Compare-Modus, Interrupts EIN, Ausgabemodus SET/RES
 CCR0 = CPUCLK/1000;	// Zykluszeit (genau 1 kHz, für TickCount)
 CCR1 = 2500;		// Einschaltzeit (hier: ein Viertel)
}

/************************************************************************
 * Anzeigehelligkeit setzen, 0 = minimal, 10000 = 100%
 ************************************************************************/
void TablSetBright(unsigned x){
 CCR1=x;
}

/************************************************************************
 * Zeichenkette [meist als Ergebnis von sprintf()] auf
 * Siebensegmentanzeige konvertieren und ausgeben
 * NULL oder "" löschen die Anzeige. Ausgabe linksbündig.
 * Der Dezimalpunkt wird unter das vorhergehende Zeichen gestellt.
 ************************************************************************/
void TablStrOut(INT nTableau, char*s){
 char c=0;
 INT i=3;
 BYTE seg;
 
 for (;;) {
  if (s) c=*s;
  if (c) s++;	// Bei String-Ende nicht mehr vorrücken
  seg=0;	// Alle unbekannten Zeichen werden wie Leerzeichen ausgegeben
  if (c>='0' && c<='9') seg=Ziffern[c-'0'];
  else if (c>='A' && c<='F') seg=Ziffern[c-'A'+10];
  else switch (c){
   case '-': seg=0x40; break;
   case ',':
   case '.': seg=0x80; break;
// sonstige zur Darstellung gewünschte Zeichen hier einfügen!
  }
  if (seg==0x80 && i!=3) TablLeds[nTableau][i]|=seg; // Dezimalpunkt darunter
  else if (i>0) TablLeds[nTableau][--i]=seg;
  else break;
 }
}

/************************************************************************
 * Zahl (vzb., max. 4stellig) auf Siebensegmentanzeige ausgeben, rechtsb.
 * Es werden entsprechend Nachkommastellen simuliert
 ************************************************************************/
void TablDecimalOut(INT nTableau, int Value, INT Decimals){
 int i;
 bool neg=false;
 if (Value<0){Value=-Value; neg=true;}
 for (i=0;i<=3;i++){		// von rechts nach links
  BYTE seg;
  div_t DivErgebnis=div(Value,10);	//MSP430-Division
  seg=Ziffern[DivErgebnis.rem];
  if (Value==0 && Decimals<0) seg=0;	// nur eine Vorkommastelle
  if (Decimals==0) seg|=0x80;		// hier Dezimalpunkt
  TablLeds[nTableau][i]=seg;
  Value=DivErgebnis.quot;
  Decimals--;
 }
 if (neg) TablLeds[nTableau][3]^=0x40;	//Vorzeichen setzen - oder weg!
}	//.. dann "sieht" man das Vorzeichen auch bei 4stelligen Zahlen:
/*   _   _       _   _   _   _   _
 _|   |   | | | |   |    _| | | | |
  | |_   _|   |  _| |_|   | |_|  _|
 */
 
#define swap(x,y) {int t=(x); (x)=(y); (y)=t;}

// Grausamer Quelltext (nicht portabel!) ergibt brauchbares Kompilat,
// guter Quelltext ergab grausames Kompilat!
unsigned long EndianSwapL(unsigned long x) {
 union{
  unsigned ui[2];
  unsigned long ul;
 }u;
 unsigned xl=(unsigned)x;
 unsigned xh=(unsigned)(x>>16);
 u.ui[1]=(xl>>8)|(xl<<8);	// hier setzt der Compiler "swpb" ein
 u.ui[0]=(xh>>8)|(xh<<8);	// Array-Indizes setzen Little-Endian voraus!
 return u.ul;
}

// Der Prozessor ist etwas umständlich beim Ausdruck "1<<n"
const BYTE SHL[8]={1,2,4,8,16,32,64,128};
// ohne Tabelle dreht der Prozessor einige Runden...
const unsigned long SHLD[32]={
 1UL<<0 ,1UL<<1 ,1UL<<2 ,1UL<<3 ,1UL<<4 ,1UL<<5 ,1UL<<6 ,1UL<<7 ,
 1UL<<8 ,1UL<<9 ,1UL<<10,1UL<<11,1UL<<12,1UL<<13,1UL<<14,1UL<<15,
 1UL<<16,1UL<<17,1UL<<18,1UL<<19,1UL<<20,1UL<<21,1UL<<22,1UL<<23,
 1UL<<24,1UL<<25,1UL<<26,1UL<<27,1UL<<28,1UL<<29,1UL<<30,1UL<<31};

/************************************************************************
 * Bargraf von LED nFrom bis nTo setzen, sowie eine Einzel-LED bei nPeak
 * Links ist Bit 1 (Bit 0 unbestückt), rechts Bit 30 (Bit 31 unbestückt)
 ************************************************************************/
unsigned long TablCalcBargraf(INT nFrom, INT nTo, INT nPeak){
 unsigned long ret=0;
 if (nFrom>nTo){int swap=nFrom; nFrom=nTo; nTo=swap;}
 for (;nFrom<=nTo; nFrom++){
  ret|=SHLD[nFrom];
 }
 ret|=SHLD[nPeak];
 return ret;
}

// Aktuellen Leucht-Zustand erfragen (zwecks Aktualisierung im HyperTerminal)
unsigned long TablGetBargraf(INT nTableau) {
 unsigned long* pLong=(unsigned long*)&TablLeds[nTableau][4];
 return EndianSwapL(*pLong);
}

// Leucht-Zustand setzen
void TablSetBargraf(INT nTableau, unsigned long bits) {
 unsigned long* pLong=(unsigned long*)&TablLeds[nTableau][4];
 *pLong=EndianSwapL(bits);
}

/************************************************************************
 * Erzeuge Einblendsignal für eins der 18 8-fach-Flipflops		*
 * a=0: Strobe für Signal K1; a=1: Strobe für A1; a=2: für A2 usw.
 * (siehe Schaltplan)
 ************************************************************************/
static void strobe(INT a){
 if (a<16){
  P1OUT=0xc0|a;	// 1100aaaa
 }else if (a==16){	// Extrawurst für neuntes Tableau ohne Dekoderchip
  P1OUT=0x50;	// 01010000
 }else /*if (a==17)*/{
  P1OUT=0x90;	// 10010000
 }
 P1OUT=0xd0;	// 11010000 (Ruhezustand)
}

volatile unsigned TickCount;

// Timer A0 Interruptserviceroutine: Überlauf (1kHz)
interrupt(TIMERA0_VECTOR) Timer_A_Overflow(void) {
// Anzeigen (9 Stück) auffrischen
 INT i;		// i läuft in Zweierschritten
 BYTE AnoBit;
 gAnode=(gAnode+1)&7;	// Anoden-Nummer hochzählen
 AnoBit=SHL[gAnode];	// Bitmaske erzeugen
 for (i=0; i<18; i++){
  P2OUT=0;
  strobe(++i);	// Anoden abschalten
  P2OUT=~TablLeds[--i/2][gAnode];	// richtiges Byte herausfischen
  strobe(i);	// Katode
  P2OUT=AnoBit;
  strobe(++i);	// Anode aktivieren
 }
 TickCount++;
// P3OUT ^= 0x04;	// Debug: Frequenz/Phasenmessung
}

// Timer A1 Interruptserviceroutine: Compare
interrupt(TIMERA1_VECTOR) Timer_A_CapCom(void) {
// Anzeigen (9 Stück) abschalten zur Helligkeitsreduktion
 INT i;
 BYTE AnoBit;
 if (TAIV!=0x02) return;
 AnoBit=SHL[gAnode];	// Bitmaske erzeugen
 P2OUT=0;
// gcc ist glücklicherweise so schlau,
// hier eine nichtabweisende Schleife zu generieren
 for (i=1; i<18; i+=2){
  P2OUT=AnoBit&TablHiliteDigit[i/2];
  strobe(i);
 }
// P3OUT ^= 0x08;	// Debug: Frequenz/Phasenmessung
}

/************************************************************************
 * Alle LEDs einschalten zu Testzwecken. Oder ausschalten zum Strom sparen
 ************************************************************************/
// TablLampentest: Makro!

/************************************************************************
 * Warten in 1-ms-Schritten (wie Windows), mindestens 1 Aufruf von Idle()
 ************************************************************************/
void Sleep(unsigned ms) {
 unsigned tic=GetTickCount();
 do Idle(); while(GetTickCount()-tic<ms);
}
Vorgefundene Kodierung: UTF-80