/*
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: ANSI (CP1252) | 4
|
|