MSP430 Codebeispiele

Aus der Mikrocontroller.net Artikelsammlung, mit Beiträgen verschiedener Autoren (siehe Versionsgeschichte)
Wechseln zu: Navigation, Suche

MSP430 – Codebeispiele

Einleitung: Da der MSP430 eine sehr schöner Mikrocontroller für energiesparende Anwendungen ist, jedoch bei weitem nicht so verbreitet wie z. B. diverse 8051er, AVRs, PICs usw. ist, gibt es auch nicht all zu viele Codebeispiele aus Projekten für den/die Hobbybastler(in). Aus diesem Grund werden hier Grundlagen der Initialisierung diverser Hardwarefeatures sowie grundlegende Softwareroutinen und dergleichen beschrieben, so dass für Anfänger auch ein einfaches Copy&Paste möglich ist. Hierbei kommt der MSPGCC zum Einsatz, da eine professionelle unlimitierte Ausgabe des z. B. IAR für Bastler nahezu unbezahlbar ist.


Hardware

Initializing/ Configuring UARTs

Von www.mathar.com stammt diese Routine zur Initialisierung eines beliebigen UARTS. Zusätzlich müssen jedoch die Pins noch definiert werden.

void InitUSART(char USART0, char USART1, unsigned int baudrate1, unsigned int baudrate2, char IR0, char IR1)
{
  if (USART0) ME1 |= UTXE0 + URXE0;    // falls gesetzt, USART0 einschalten (TX- und RX-teil)
  if (USART1) ME2 |= UTXE1 + URXE1;    // falls gesetzt, USART1 einschalten (TX- und RX-teil)
  UCTL0 |= CHAR;                       // 8 data bits, 1 stop bit, no parity (8N1)
  UCTL1 |= CHAR;
  UTCTL0 |= SSEL1;                     // SMCLK als UCLK festlegen
  UTCTL1 |= SSEL1;
  if (baudrate1==19200)
  {
    UBR00 = 0xA0;                      // 19200 baud aus 8 MHz erzeugen
    UBR10 = 0x01;                      // siehe application note tabelle 1, seite 9
    UMCTL0 = 0x00;                     // keine korrektur der division noetig
  }
  if (baudrate2==19200)
  {
    UBR01 = 0xA0;                      // 19200 baud aus 8 MHz erzeugen
    UBR11 = 0x01;                      // siehe application note tabelle 1, seite 9
    UMCTL1 = 0x00;                     // keine korrektur der division noetig
  }
  if (USART0) UCTL0 &= ~SWRST;         // USART freigeben
  if (USART1) UCTL1 &= ~SWRST;
  if (IR0==0) IE1 |= URXIE0;           // IR0: 0 -> nur RX-interrupt anschalten
  if (IR0==1) IE1 |= UTXIE0;           //      1 -> nur TX-interrupt anschalten
  if (IR0==2) IE1 |= URXIE0 + UTXIE0;  //      2 -> TX- und RX-interrupt anschalten
  if (IR1==1||IR1==2) IFG1 &= ~UTXIFG0;  // initales interrupt-flag loeschen
  if (IR1==0) IE2 |= URXIE1;           // IR1: 0 -> nur RX-interrupt anschalten
  if (IR1==1) IE2 |= UTXIE1;           //      1 -> nur TX-interrupt anschalten
  if (IR1==2) IE2 |= URXIE1 + UTXIE1;  //      2 -> TX- und RX-interrupt anschalten
  if (IR1==1||IR1==2) IFG1 &= ~UTXIFG1;  // initales interrupt-flag loeschen

}

Initialization/configuration of USART (SPI and I2C/TWI)

void init_spi(void)
{
  ME1 |= USPIE0;                        // Enable USART0 SPI mode
  UTCTL0 = CKPH+SSEL1+SSEL0+STC;        // SMCLK, 3-pin mode
  UCTL0 = CHAR+SYNC+MM;                 // 8-bit SPI Master **SWRST**
  UBR00 = 0x02;                         // UCLK/2 
  UBR10 = 0x00;                         // 0
  UMCTL0 = 0x00;                        // no modulation
  P3SEL |= 0x0E;                        // P3.1-3 SPI option select
  P3DIR |= 0x01;                        // P3.0 output direction
  _EINT();                              // Enable interrupts
}

void init_i2c(unsigned char slave)
{
  P3SEL |= 0x0a;                            // Assign I2C pins to module
  U0CTL |= I2C + SYNC;                      // Switch USART0 to I2C mode
  U0CTL &= ~I2CEN;                          // Recommended I2C init procedure
  I2CTCTL = I2CSSEL_2;                      // SMCLK
  I2CSCLH = 0x03;                           // High period of SCL
  I2CSCLL = 0x03;                           // Low period of SCL
  I2CNDAT = 0x01;                           // Transmit one byte
  I2CSA = slave;                             // Slave address
  U0CTL |= I2CEN;                           // Enable I2C, 7 bit addr,
  I2CIE = RXRDYIE;                          // I2C receive ready interrupt enable
}

Initialisierung der Quarze

void init_XT2(void)
{
  unsigned int i;
  WDTCTL = WDTPW + WDTHOLD;             // Stop WDT
  BCSCTL1 &= ~XT2OFF;                   // XT2 = HF XTAL
  do 
  {
    IFG1 &= ~OFIFG;                       // Clear OSCFault flag
    for (i = 0xFF; i > 0; i--);           // Time for flag to set
  }
  while ((IFG1 & OFIFG) != 0);          // OSCFault flag still set?                
  BCSCTL2 |= SELM1;                     // MCLK = XT2 (safe)
}
void init_XT(void) // high frequenz resonators ( 455 kHz - 8MhZ )
{
  unsigned int i;
  WDTCTL = WDTPW + WDTHOLD;             // Stop WDT
  BCSCTL1 |= XTS;                       // ACLK = LFXT1 = HF XTAL
  do 
  {
    IFG1 &= ~OFIFG;                       // Clear OSCFault flag
    for (i = 0xFF; i > 0; i--);           // Time for flag to set
  }
  while ((IFG1 & OFIFG) == OFIFG);      // OSCFault flag still set?                
  BCSCTL2 |= SELM1+SELM0;               // MCLK = LFXT1 (safe)
}

Bei den neueren MSP430F55xx nicht vergessen, dass bei CPU-Taktfrequenzen über 8 MHz vorher die Kernspannung erhöht werden muss, sonst drohen Abstürze oder „rätselhaftes“ Verhalten. Eins der TI-Beispiele (12 MHz) „vergisst“ das sogar.

Initialisierung des ADCs

void init_ADC(void)
{
  ADC12CTL0 = ADC12ON;	// ADC12ON / reference on Avcc
  P6SEL |= 0x01;        // P6.0 ADC option select 
}

unsigned int sampling_ADC(void)
{
  ADC12CTL0 |= ADC12SC + ENC;   // Sampling open
  ADC12CTL0 &= ~ADC12SC;        // Sampling closed, start conversion
  while ((ADC12CTL1 & ADC12BUSY) == 1);   // ADC12BUSY?
  return(ADC12MEM0);	// return the value read from ADC P6.0
}

Initialisierung des DACs

void init_DAC(void)
{
  ADC12CTL0 = REF2_5V + REFON;              // Internal 2.5V ref on
  DAC12_0CTL = DAC12IR + DAC12AMP_5 + DAC12ENC;   // Internal ref gain 
}

void write_DAC(unsigned int val)
{
  DAC12_0DAT = val;
}

Initialisierung des Timers A

void init_TimerA(unsigned int cycles )
{
  TACTL = TASSEL1 + TACLR;              // SMCLK, clear TAR
  CCTL0 = CCIE;                         // CCR0 interrupt enabled
  CCR0 = cycles;
  TACTL |= MC_2;                         // Start Timer_A in continuous mode
  _EINT();                              // interrupt enable
}

// Timer A0 interrupt service routine
interrupt (TIMERA0_VECTOR) Timer_A(void)
{
  P1OUT ^= 0x01;                        // Toggle P1.0
  CCR0 += 50000;                        // Add Offset to CCR0
}

Initialisierung des Timers B

void init_TimerB(unsigned int cycles)
{
  TBCTL = TBSSEL1 + TBCLR;              // SMCLK, clear TAR
  TBCCTL0 = CCIE;                       // CCR0 interrupt enabled
  TBCCR0 = cycles;
  TBCTL |= MC_2;                         // Start Timer_B in continuous mode
  _EINT();                              // interrupt enable
}

// Timer B0 interrupt service routine
interrupt (TIMERB0_VECTOR) Timer_B(void)
{
  P1OUT ^= 0x01;                        // Toggle P1.0
  TBCCR0 += 50000;                      // Add Offset to CCR0
}

Initialisierung des Watchdogs

void init_wdt(void)
{
  WDTCTL = WDT_MDLY_32;                 // Set Watchdog Timer interval to ~30ms
  IE1 |= WDTIE;                         // Enable WDT interrupt
}

// Watchdog Timer interrupt service routine
interrupt (WDT_VECTOR) watchdog_timer(void)
{
  // do this, in an case of an interrupt
}

int main(void)
{
  WDTCTL = WDTPW + WDTHOLD;	// Watchdog anhalten
  P1DIR |= 0x01;		// P1.0 als Ausgang

  for (;;)
  {
    volatile unsigned i;	// volatile, sonst wird die Warteschleife „wegoptimiert“
    P1OUT ^= 0x01;		// P1.0 umschalten mit Exklusiv-ODER

    i = 10000;			// Warteschleife in Software
    do; while (--i);		// (CPU-Leistung „verheizen“)
  }
}

Initialisierung der GPIO

Die Ein/Ausgabeleitungen haben beim MSP430, je nach Ausbaustufe, folgende Fähigkeiten:

  • Einzeln (bitweise) programmierbare Ein/Ausgabe (PxDIR, PxOUT)
  • Unabhängige Lese-Adresse (PxIN)
  • Ansprechbar als 8-Bit-Ports oder je paarweise als 16-bit-Port, bspw. P1 + P2 = PA usw.
  • Interrupt auf Pegelwechsel einzelner Portpins (nur P1 und P2) (PxIES)
  • Pull-Up, Pull-Down oder kein Widerstand auswählbar (PxREN, PxOUT)
  • Einstellbare Treiberstärke (reduziert und voll) (PxDS)
  • Ein bis zwei Peripheriefunktionen pro Pin (PxSEL), auswählbar mittels PxDIR (d.h. bei zwei Peripheriefunktionen ist eine Ausgang und die andere Eingang)
  • Das JTAG-Port (4 Pins) und das USB-Port (2 Pins) ist standardmäßig ein E/A-Port (MSP430F55xx)
  • Erweiterte Peripheriezuordnung bei P4 (MSP430F55xx) mittels Port Mapping Controller
  • Nicht 5-V-verträglich!! Keinem Portpin darf ohne genügend großen Vorwiderstand 5 V angeboten werden. Ableitströme dürfen nicht zum Ansteigen der Speisespannung führen!

Manche Portpins haben ein gemeinsames PxSEL, etwa:

  • das JTAG-Port (entweder alle 4 oder kein Pin zugeordnet)
  • das USB-Port (entweder Portpin oder USB D+ und D–)
  • die Quarz-Anschlüsse (das niederwertige PxSEL schaltet beide Portpins)

Alle Portpins sind beim Einschalten (PUC) wie folgt initialisiert:

  • Eingang ohne Pull-Up oder Pull-Down
  • Reduzierte Treiberstärke
  • Kein Pegelwechsel-Interrupt
  • Keine Peripherie-Zuordnung
  • Der Inhalt des Ausgaberegisters PxOUT ist undefiniert!

Alle ungenutzten Anschlüsse sollten per PullDown (PxREN und PxOUT) festgelegt werden.

Kein MSP430 hat einen herausgeführten Busanschluss, etwa um mehr RAM anzubinden.

Initialisierung von PWM

void init_PWM_TimerA(void)
{
  TACTL = TASSEL1 + TACLR;              // SMCLK, Clear Tar
  CCR0 = 512-1;                         // PWM Period
  CCTL1 = OUTMOD_7;                     // CCR1 reset/set
  P1DIR |= 0x04;                        // P1.2 PWM output
  P1SEL |= 0x04;                        // P1.2 and TA1/2 otions
  TACTL |= MC0;                         // Start Timer_A in up mode
}

void set_PWM_duty_cycle(unsigned char duty)
{
  CCR1 = duty;                           // CCR1 PWM duty cycle 
}

Persistente Daten im Information Memory

Kodebeispiel für mspgcc und MSP430F1610

// Eine INFOMEM-Struktur und deren Vorbelegung
struct PERSISTENT {
 int a,b;		// Man achte sinnfälligerweise auf korrekte Ausrichtung, um späteren Portierungsproblemen vorzubeugen!
 long c,d;		// mspgcc legt die Daten in little-endian ab
 char e,f;		// hier beispielhaft 14 Bytes insgesamt
}epersistent __attribute__((section(".infomem")))={
 1,2, 3,4, 5,6};

// RAM-Kopie derselben Struktur (für gehäuften Schreibzugriff)
struct PERSISTENT persistent;

// Initialisierungskode
 FCTL2 = 0xA549;		// Flash-Takt festlegen, bspw. MCLK = 4,6 MHz, ÷ 10 = 460 kHz
 persistent=epersistent;	// svw. memcpy

// Rückschreib-Kode (im Flash, ohne <mspgcc/flash.h>, gelegentlich aufrufen):
// Das Flash-Schreiben blockiert den Prozessor!
 if (memcmp(epersistent,persistent,sizeof(epersistent))) {
  FCTL3 = 0xA500;		// LOCK entfernen
  FCTL1 = 0xA502;		// ERASE setzen
  epersistent.a = 0xFFFF;	// Segment löschen (≈ 10 ms)
  FCTL1 = 0xA540;		// SCHREIBEN setzen
  epersistent=persistent;	// svw. memcpy, byteweise in Flash schreiben (≈ 1 ms)
  FCTL1 = 0xA500;		// SCHREIBEN sperren
  FCTL3 = 0xA510;		// LOCK setzen
 }

// Mit <mspgcc/flash.h>:
#include <mspgcc/flash.h>
 if (memcmp(epersistent,persistent,sizeof(epersistent))) {
  flash_lock_segmentA(0);
  flash_erase_segment(&epersistent);	// Segment löschen (≈ 10 ms)
  flash_write(epersistent,persistent,sizeof(epersistent)); //schreiben (≈ 1 ms)
  flash_lock_segmentA(1);
 }

Ganz so komfortabel wie der EEPROM-Bereich der AVR-Controller ist der MSP430 Information Memory nicht, da dieser nur als Ganzes gelöscht werden kann und alle Lösch- und Schreiboperationen den Prozessor blockieren, es sei denn, der Schreibkode liegt im RAM.

Weil das Speichern von ein paar Konfigurationsdaten eher nebenbei zum normalen Programmablauf erfolgen wird, lohnt sich die Mühe zumeist nicht, den Schreib-Kode in den RAM auszulagern. Der Flash-Zugriff erleichtert sich dadurch erheblich, und wie man im Beispiel „ohne <mspgcc/flash.h>“ sieht, ist auch nicht viel zu tun. Ein einfaches memcpy oder jeder andere Schreibzugriff genügt zum Beschreiben.

Ist nicht CPU-blockierender Schreibzugriff erwünscht, können IMHO keine Interrupts währenddessen verwendet werden. Sämtlicher aktiver Kode muss sich im RAM befinden, etwa, um eine serielle Schnittstelle abzufragen.

Einige MSP430-Derivate enthalten in einem Segment des Information Memory ein paar Konfigurationsdaten, die man retten und wiederherstellen sollte, oder man benutzt ein anderes Segment im Information Memory.


Interrupts:

UARTs

// z. B. ein Echo mit dem MSPGCC-Compiler:
interrupt (UART0RX_VECTOR) usart0_rx(void)
{
   TXBUF0=RXBUF0;
}

// z. B. ein Echo mit dem ImageCraft ICC430-Compiler:
#pragma interrupt_handler usart0_rx:UART0RX_VECTOR
void usart0_rx(void)
{
   TXBUF0=RXBUF0;
}

Die Funktionen selbst können natürlich auch anders genannt werden.

Timer

IOs


Softwareroutinen:

I2C/TWI in Software

Diese Routinen wurden ursprünglich für einen MSP430F149 geschrieben. Es werden ausschließlich IOs benutzt. Da Code in C geschrieben wurde sollte er sich einfach auf alle MSP430s ohne (aber auch mit I²C) portieren lassen. Der ist zwar nicht ganz schön, aber er funktioniert soweit ganz gut. Auch ein Beispiel ist dabei.

http://www.mikrocontroller.net/attachment.php/324877/soft-i2c.zip

PWM

Untenstehender Code ist nur mit msp320-gcc getestet.

Folgende Routine initialisiert den Timer als Up-Counter mit der Clock-Quelle SMCLK und einem Teiler von 8. Zudem wird der Interrupt aktiviert.

void timerA_init(void)
{
	TACTL =  TASSEL_SMCLK | TACLR | ID_DIV8 | TAIE;
	TACTL |= MC_UPTO_CCR0;
}

Um den Timer zu starten:

void timerA_start(int timing[2], int mode)
{
	TACCTL0 = mode;
	TACCTL0 |= CCIE ;                // enable interrupt
	TACCTL1 = mode;
	TACCR0 = timing[0];
	TACCR1 = timing[1];
	TAR = 0;
}


Die Funktion wird normalerweise mit 'mode' 3 aufgerufen:

timerA_start(timing, OUTMOD_SET_RESET);

timing[0] enthaelt die komplette Zyklendauer einer PWM-Phase (H und L) Beispiel: Um eine Pulsfrequenz von 125 Hz bei einem SMCLK von 1.0 MHz zu erhalten, gilt:

[math]\displaystyle{ timing_0 = 1.0e6 / 8 / 125 = 1000 }[/math]

timing[1] schliesslich enthält die Zyklendauer des H-Pulses und ist immer kleiner als timing[0]. Bei einem 'duty cycle' (Tastverhältnis) 50% gilt also

[math]\displaystyle{ timing_1 = 500 }[/math]

Zu beachten ist, dass der Timerkanal 0 bei manchen Modi (wie dem obenstehenden) für die PWM nicht genutzt wird, "TACCTL0 = mode" keinen speziellen Effekt zeigt (siehe auch Manual).

Deswegen wird das PWM-Signal auch an TA1 abgegriffen, nicht an TA0.

Schlussendlich muss der entsprechende Ausgangspin für TA1 konfiguriert werden. Beim F2013 z. B. geschieht dies für P1.2 per

	P1SEL |= 0x04;  // Enable primary peripheral
	P1DIR |= 0x04;

Was war nun mit dem Interrupt? Falls wir z. B. die Zyklen zählen wollen, definieren wir einen Interrupt-Handler:

#include <signal.h> // 'interrupt' makro
interrupt (TIMERA0_VECTOR) timera0_isr(void)
{
	g_count++;
}

Schlussendlich müssen wir noch die Interrupts im Initialisierungscode aktivieren:

_EINT();

Senden eines Bytes

void SendUSART0c(char c)             // ein einzelnes zeichen über die serielle schnittstelle (USART0) senden
                                     // FJG: Obacht: x12xx : IFG1 => IFG2 !
{
  while (!(IFG1 & UTXIFG0));           // warten, bis USART0 TX-buffer sendebereit
  TXBUF0 = c;
}

void SendUSART1c(char c)             // ein einzelnes zeichen über die serielle schnittstelle (USART1) senden
{
  while (!(IFG2 & UTXIFG1));           // warten, bis USART1 TX-buffer sendebereit
  TXBUF1 = c;
}

Senden eines Strings

Nullterminierte Strings; zugehörige Interrupts dürfen nicht aktiviert sein

void SendUSART0(const char* str)	// einen String über die serielle Schnittstelle (USART0) senden
{
  while (*str != 0)
  {
    while (!(IFG1 & UTXIFG0));		// warten bis USART0 TX-Puffer frei (leer)
    TXBUF0 = *str++;
  }
}

void SendUSART1(const char* str)	// einen String über die serielle Schnittstelle (USART1) senden
{
  while (*str)
  {
    while (!(IFG2 & UTXIFG1));		// warten bis USART1 TX-Puffer frei (leer)
    TXBUF1 = *str++;
  }
}

Binärdaten mittels DMA (hier: DMA-Kanal 0 an UART1)

void send(const void*buf, unsigned len) {
// könnte globaler Initialisierungskode sein:
 DMACTL0 = 0x000A;		// DMA-Kanal 0 dem UART1-TxD zuordnen
// genauer: DMACTL0 = DMACTL0 & 0xFFF0 | 0x000A;
 DMA0DA  = (unsigned)&U1TXBUF;	// Zieladresse ist der Sendepuffer
// hier aufrufspezifischer Kode:
 DMA0SA  = (unsigned)buf;	// Quelladresse sind die Sendedaten
 DMA0SZ  = len;			// Anzahl der Byte-Transfers
 DMA0CTL = 0x03F0;		// Start des Single-DMA-Transfers, byte-weise mit steigender Quelladresse
// Abwarten aufs Ende, könnte man vorteilhaft asynchron erledigen.
// In so einem Fall muss der Aufrufer <buf> beibehalten bis der DMA-Transfer beendet ist
 while (!(DMA0CTL&DMAIFG));
}

Beachte: Die Interruptfreigabe des UART priorisiert Interruptverarbeitung über DMA! Das kann man auch gezielt ausnutzen, um mal Interrupt- und mal DMA-Betrieb zu realisieren.

Anmerkung: Der MSP430, insbesondere die F1xxx-Serie, hat für einen sonst AVR-verwöhnten Anwender vergleichsweise starke Peripherie und eine schwache CPU. Deshalb ist die Benutzung von DMA und eines günstigen Binärdatenübertragungsprotokolls essentiell für Hochgeschwindigkeitsanwendungen, bei denen man beim AVR noch gut mit Interrupts zurecht kam.

Empfangen eines Bytes (interruptgesteuert) // Receiving a byte-> Interrupt controlled

/*Hab diese Interrupt Funktion für meine I²C Kommunikation verwendet
Startcondition wird erkannt, aber nicht Stop, da man die Flanken(die den
interrupt auslösen) umkehren müsste, aber dann keine Startkondition erkannt werden kann.

Hab mir jetzt nicht die mühe gemacht alles rauszupicken was ihr nicht braucht.
Wer sich auskennt, kann das auch selber machen.

Das Busy wird fürs stretching verwendet(CLK wird auf low gezogen)
*/  

#pragma vector = PORT1_VECTOR           // Port 1 - Interrupt-Funktions-"Alias"
__interrupt void Interrupt_Port1()
{

//  I2CTransfer = 0;

    TACTL |= TACLR;
    TACTL |= TAIE;
#ifdef WD
    WDTCTL = WDTPW + WDTCNTCL;
#endif
    
//----------------------------------------------------------------------------
  if ((P1IFG & 0x04) && !(I2CCLK)) { //if startdataimpuls but clk low is detected --return--
    P1IFG &= ~0x0C;
    return;}
//----------------------------------------------------------------------------
  

//    P1IE &= ~0x0C;

  
//----------------------------------------------------------------------------
  if ((P1IFG & 0x04) && I2CCLK)   //Start Condition       (I2CStart)
    {
      P1IFG &= ~0x0C;
      
//    I2CTransfer = 1;
  

        P1IE &= ~0x0C;
//        TACTL |= TACLR;
//        TACTL |= TAIE;
//        _EINT();

      
/*#ifdef Debug
      P2OUT |= 0x04;
      P2DIR |= 0x04; 
#endif*/
      
//      Wait(10);
//      while(I2CCLK)  {CheckTimer;}

//      set_Busy;
  
      BitCnt = 0x01;
      ByteCnt = 0;
//      I2CByte = 0;
      StartCondition = 1;
//      CRCByte = 0x00;
      GetData = 0;
      I2CTransfer = 0;
    
//    clr_Busy;
  

        _DINT();
        P1IE |= 0x0C;

      return;
    }
//----------------------------------------------------------------------------

  P1IFG &= ~0x0C;

  
    TACTL |= TACLR;
    _EINT();


//----------------------------------------------------------------------------
  if (I2CCLK)
  {
  
 
      TACTL |= TAIE;

    
    I2CByte <<= 1; 
    if (I2CDAT){
      I2CByte += 0x01;
     }
    I2CTransfer = 1;
    while(I2CCLK) {CheckTimer;}
    set_Busy;
    BitCnt <<= 1;
  
    if(!(BitCnt == 0x00))
    {
      clr_Busy;  
    }
    else
    {
      _NOP();
    }
 
  }
//----------------------------------------------------------------------------  

P1DIR &= ~0x0C;
P1IFG = 0x00;
P1IE |= 0x0C;

Ermitteln der Temperatur

// von FJG info@aqua-sun.net :
// ---------------   Temperatur des Chips   ---------------------

#define T_Faktor_a1000       ((long)103)     // REF 1,5V
#define T_Faktor_b1000       ((long)172)     // REF 2,5V
#define T_Faktor_c1000       ((long)278000)

int Hole_Temp(void)	// Temperatur ch 10
{			// Temp = N* 0,17193 -278  1000Temp = N*172 -278000 bei 2,5V
  long Temp_L;
  int  Temp , Delta ;

  Start_AD();

  Temp_L = ((long)ADC12MEM10 * T_Faktor_a1000) - T_Faktor_c1000 ;
  Temp   = (int)( Temp_L / 1000);

  Delta = Read_EEPROM( EPROM_INTERN_BL1_R_ADR,DELTA_TEMP_ADR );	   // falls Temp Messung
  if( Delta <= 120 && Delta >= 80 ) Delta -= 100 ; else Delta = 0; // ungenau war
                                                                   // siehe dazu TI (Frage an ursprünglichen Autor, wann soll  Delta <= 120 && Delta >= 80 zutreffen?)
  return (Temp + Delta);
}

// INIT AD
// AD muss angestoßen werden mit :   Start_AD()

// #define ADC12TL0WERT15	(SHT1_8 + SHT0_3 + MSC + REFON + ADC12ON)
#define ADC12TL0WERT15		(SHT1_3 + SHT0_3 + MSC + REFON + ADC12ON)
#define ADC12TL0WERT25		(SHT1_3 + SHT0_3 + MSC + +REF2_5V + REFON + ADC12ON)

//                        Start CH0 , ADC12SCBIT , SampleT , F/2 , MCLK , Sequenze of CH
#define ADC12CTL1WERT   ( CSTARTADD_0 + SHS_0 + SHP + ADC12DIV_1 + ADC12SSEL_2 + CONSEQ_1 )

void adc12_init()	// ADC12CTL0 modifizieren nur mit ENC = 0
{
  ADC12CTL0   = 0x00;
  ADC12CTL1   = ADC12CTL1WERT ;

  ADC12MCTL0  = INCH_0;                  
  ADC12MCTL1  = INCH_1;                  
  ADC12MCTL2  = INCH_2;
  ADC12MCTL3  = INCH_3;
  ADC12MCTL4  = INCH_4;
  ADC12MCTL5  = INCH_5;
  ADC12MCTL6  = INCH_6;
  ADC12MCTL7  = INCH_7;                  
  ADC12MCTL8  = INCH_8;		// VeREF+
  ADC12MCTL9  = INCH_9;		// VeREF-

  ADC12MCTL10 = INCH_10 + SREF_1 ;
  ADC12MCTL11 = INCH_11 + SREF_1 +EOS;

  ADC12IE     = 0x00;
  ADC12CTL0   = ADC12TL0WERT15 ;
  ADC12CTL0  |= 0x0003;		// ENC + ADC12SC = Enable Conversation + StartConversation
}

// --------------------   Diverses AD_Wandler   --------------------

void Start_AD(void)
{
  ADC12CTL0 &= ~ENC;
  ADC12CTL0 |= (ADC12SC+ENC);
  NOP();
  NOP();
  ADC12CTL0 &=~ADC12SC;
}

#ifndef TEST_AD
#define Ist_AD_Busy()        ( ADC12CTL1 & ADC12BUSY )
#else
int Ist_AD_Busy()
{
  if (ADC12CTL1 & ADC12BUSY)
  {
     printf("\n\r... BUSY ..."); 
     delay_ms(500);
     return(1);
  }
  return(0);
}
#endif

Ansteuerung eines LCD – Zeichendisplays // Controlling the LCD character

Der folgende Quelltext-Schnipsel ist für Siebensegmentanzeigen gedacht, nicht für die mit dem HD44780-kompatiblen Controller.

  // hier nun der vollständige Code 14 Jan 08
  // von mir seinerseits entwickelt, ich hoffe, er ist eine Hilfe
 
  // Aquasun Germany  Remscheid  Franz-Josef Günther
  // Development info@aqua-sun.net

  // LCD.h Header für Allgemeines zum LCD ab 02.12.04 Aquasun GUE
  // C-Compiler ICC

  // -------------------------------------------------------------

  // Beispiel :

int Aepfel = 8;

print_LCD("\nRotkaepchen hat %d Aepfel im Korb",Aepfel);

  // -------------------------------------------------------------

#include <_const.h>
#include <stdarg.h>

#include < STRING.H>
#include < stdio.h >

#define LCD_MAX          8
#define Mem_LCD         16

#ifndef Mem_LCD
#define Mem_LCD 20
#endif


/*
    Header für 8 x 7 Segmentanzeigen 2 MUX MSP430 x4xx

    V3 = V1 (VDD) , V5 = 0 ,

    für : LCD siehe TI Info dazu,
    für : LED's :

    LED mit gemeinsamer Kathode, SP über Buffer 74HC245 (8x) o.ä.
    /G=L , DIR=H A ==> B an 5V , COM mit Emitterfolger PNP ViL=0,8V ViH=2V

    LED mit gemeinsamer Anode, COM über PNP Transistor invertiert
    und SP über ULN2804(8x) getrieben
            _   ___   ___   ___
    COM0 :   |_|   |_|   |_|
            ____   ___   ___
    COM1 :      |_|   |_|   |_|
             __    __    __
    SPon :  |  |__|  |__|  |__| mit COM0

    SPin :   0  1  2  3  4  5  6  7    8  9 10 11 12 13 14 15
    COM0 :  1a 1b 1c 1d 1e 1f 1g 1h   2a 2b 2c 2d 2e 2f 2g 2h
    COM1 :  5a 5b 5c 5d 5e 5f 5g 5h   6a 6b 6c 6d 6e 6f 6g 6h

    SPin :  16 17 18 19 20 21 22 23   24 25 26 27 28 29 30 31
    COM0 :  3a 3b 3c 3d 3e 3f 3g 3h   4a 4b 4c 4d 4e 4f 4g 4h
    COM1 :  7a 7b 7c 7d 7e 7f 7g 7h   8a 8b 8c 8d 8e 8f 8g 8h

    COM        3 2   1  0   3 2   1  0
    MAP 0A0    - -   8h 4h  - -   8g 4g
        09F    - -   8f 4f  - -   8e 4e
        09E    - -   8d 4d  - -   8c 4c
        09D    - -   8b 4b  - -   8a 4a

        09C    - -   7h 3h  - -   7g 3g
        09B    - -   7f 3f  - -   7e 3e
        09A    - -   7d 3d  - -   7c 3c
        099    - -   7b 3b  - -   7a 3a

        098    - -   6h 2h  - -   6g 2g
        097    - -   6f 2f  - -   6e 2e
        096    - -   6d 2d  - -   6c 2c
        095    - -   6b 2b  - -   6a 2a

        094    - -   5h 1h  - -   5g 1g
        093    - -   5f 1f  - -   5e 1e
        092    - -   5d 1d  - -   5c 1c
        091    - -   5b 1b  - -   5a 1a

             ---a----
            |        |
          f |        | b
            |        |
             ---g----
            |        |
          e |        | c
            |        |    _
              ---d---    | | h
                          -

*/
#define a0      0x01  // Char durch rechtsschiften & 0x..
#define b0      0x10
#define c0      0x02
#define d0      0x20
#define e0      0x04
#define f0      0x40
#define g0      0x08
#define h0      0x80

#define ch_0    ( e0+f0+a0+b0+c0+d0 )
#define ch_1    ( b0+c0 )
#define ch_2    ( a0+b0+g0+e0+d0 )
#define ch_3    ( a0+b0+g0+c0+d0 )
#define ch_4    ( f0+g0+b0+c0 )
#define ch_5    ( a0+f0+g0+c0+d0 )
#define ch_6    ( a0+f0+c0+d0+e0+g0 )
#define ch_7    ( a0+b0+c0 )
#define ch_8    ( a0+b0+c0+d0+e0+f0+g0 )
#define ch_9    ( a0+b0+c0+f0+g0 )

#define ch_a    ( a0+b0+c0+d0+e0+g0 )
#define ch_A    ( e0+f0+a0+b0+c0+g0 )
#define ch_B    ( a0+b0+c0+d0+e0+f0+g0 )
#define ch_b    ( f0+e0+d0+c0+g0 )
#define ch_c    ( g0+e0+d0 )
#define ch_C    ( a0+f0+e0+d0 )
#define ch_d    ( b0+c0+d0+e0+g0 )
#define ch_D    ( e0+f0+a0+b0+c0+d0 )
#define ch_e    ( f0+e0+d0+a0+b0+g0 )
#define ch_E    ( a0+f0+g0+d0+e0 )
#define ch_F    ( a0+f0+g0+e0 )
#define ch_G    ( a0+f0+c0+d0+e0+g0 )
#define ch_g    ( a0+b0+c0+d0+f0+g0 )
#define ch_h    ( f0+c0+e0+g0 )
#define ch_H    ( f0+c0+e0+g0+b0 )
#define ch_I    ( f0+e0 )
#define ch_i    ( e0 )
#define ch_k    ( b0+e0+f0+g0 )
#define ch_L    ( f0+e0+d0 )
#define ch_l    ( b0+c0 )
#define ch_M    ( c0+b0+e0+f0 )

#define ch_n    ( e0+g0+c0 )
#define ch_N    ( a0+b0+c0+e0+f0 )
#define ch_o    ( e0+g0+c0+d0 )
#define ch_O    ( e0+f0+a0+b0+c0+d0 )
#define ch_P    ( e0+f0+a0+b0+g0 )
#define ch_r    ( g0+e0 )
#define ch_S    ( a0+f0+g0+c0+d0 )
#define ch_T    ( a0+f0+e0 )
#define ch_U    ( f0+e0+d0+c0+b0 )
#define ch_u    ( e0+d0+c0 )
#define ch_X    ( b0+g0+e0 )
#define ch_Y    ( f0+g0+b0+c0+d0 )
#define ch_Z    ( a0+b0+g0+e0+d0 )
#define ch_Blank  0
#define Unter_  ( d0 )
#define DP_Pkt  ( h0 )
#define Minus   ( g0 )
#define Tilde   ( a0+g0+d0 )
#define GLEICH  ( g0+d0 )
#define FRAGE   ( a0+f0+g0+c0+d0+h0 )

// #define Digi_X1ste         LCDM17       // die erste freie LCD_Mem_Stelle 18,19,20
                                           // in lcd_init() auf 0

// extern unsigned int Digi_X;             // für LCD

const char  letter[] = "0123456789 _.-~?=AaBbCcDdEeFGgHhIiKkLlMmNnOoPRrSsTtUuxXYyZz";

const int hex_Wert[] = {
      ch_0,    ch_1, ch_2, ch_3, ch_4, ch_5, ch_6, ch_7, ch_8, ch_9,
      ch_Blank, Unter_, DP_Pkt, Minus, Tilde, FRAGE , GLEICH ,
      ch_A, ch_a, ch_B, ch_b, ch_C, ch_c, ch_D, ch_d, ch_E, ch_e, ch_F, ch_G, ch_g,
      ch_H, ch_h, ch_I, ch_i, ch_k, ch_k, ch_L, ch_l , ch_M, ch_M, ch_N, ch_n, ch_O, ch_o,
      ch_P, ch_r, ch_r, ch_S, ch_S, ch_T, ch_T, ch_U, ch_u, ch_X, ch_X,
      ch_Y, ch_Y, ch_Z , ch_Z  };



void All_LCD(void);
void Clear_LCD_M(void);                     // Clears LCD memory
void Clear_LCD(void);
void lcd_init(void); in ini
void Write_LCD_Hex(int Digit_n , int HEX_Zeichen);
void SetzeDP( int welches_Displ);
int putchar_LCD(char);
int print_LCD(CONST char *fmt, ...)  ;      // LCD 1-7 Digit nur realisiert sonst 1...11
extern int _print(void (*_put)(char), const char *fmt, va_list va);


    // ----------------------------------------

void Clear_LCD_M(void)
{
    int i;
    for (i =0; i<Mem_LCD; i++) LCDMEM[i] = 0;   // Mem_LCD extern , abhängig vom Display
}

    // ----------------------------------------

void Clear_LCD(void)
{
      print_LCD("\n_");                     // beim Cursor wird DIGIT_X nicht weitergezählt
}

    // ----------------------------------------

void All_LCD(void)                          // alle = 8888..
{
    int i;
    for (i=0; i<Mem_LCD; i++) LCDMEM[i] = 0xFF;   // Mem_LCD extern , abhängig vom Display
}

    // ----------------------------------------

int putchar_LCD(char c_in)
{
                                            //  Digi_X  == LCDM17
    int i, leng;
    char c = c_in;
    leng = strlen(letter);                  // Länge ohne /0 , 1..

      if( (c=='\n') || (c=='\r'))           // nichts gefunden => CLR_Displ
        {
          Digi_X =1;  Clear_LCD_M(); return c;
        }

    for(i=0;i<leng;i++) { if( c == letter[i] ) break; }        // Char gefunden

    if(i == leng)
      {
        for(i=0;i<leng;i++) { if( ' ' == letter[i] ) break; }  // Char ' ' als Ersatz
      }

    if(i<leng)                              // es gibt '.' als Ersatz
      {
        Write_LCD_Hex((int)Digi_X , i);     // Digi beschreiben oder löschen
                                            // if(Digi_X) Write_LCD_Hex(Digi_X , i);
        if(C != '_')Digi_X++;
        if(Digi_X > LCD_MAX) Digi_X =1;     // if(Digi_X >= Mem_LCD) Digi_X =1;
      }
    return c;
}

    // ----------------------------------------

int print_LCD(CONST char *fmt, ...)
{
    va_list va;
    int val;

    va_start(va, fmt);
    val = _print((void (*)(char))putchar_LCD, fmt, va);
    va_end(va);

    return val;
}

   // ----------------------------------------

void lcd_init(void)             // LCD init
{
 LCDCTL=0xAD;                   // LCDM567.2,LCDM567.0,LCDM234.1,LCDM234.0,LCDM0
 LCDM1 =0x00;                   // 0xAD = 101 01 101
 LCDM2 =0x00;                   // 101 = LCDP2 ,0, LCDP0 => S0..S31
 LCDM3 =0x00;                   //  01 = LCDMX1,LCDMX0   => 2-mux
 LCDM4 =0x00;                   // 101 = LCDSON,x,LCDON Segments ON ,x,LCD ON
 LCDM5 =0x00;
 LCDM6 =0x00; LCDM7 =0x00; LCDM8 =0x00; LCDM9 =0x00; LCDM10 =0x00;LCDM11 =0x00;
 LCDM12=0x00; LCDM13=0x00; LCDM14=0x00; LCDM15=0x00; LCDM16=0x00; LCDM17=0x00;
 LCDM18=0x00; LCDM19=0x00; LCDM20=0x00;
}

   // ----------------------------------------

void Write_LCD_Hex(int Digit_n , int char_Nr)   // Digit1 bis 4 und 5 bis 8
{
  int i ,i_End , ch , Maske_loesch , Maske_setz ;

  if((Digit_n == 0 ) || (Digit_n > LCD_MAX))
    {
        Clear_LCD_M(); i=0;
    }
   else i= (Digit_n - 1);                   // Startadresse = Digit_n x4 siehe unten

  if(Digit_n < (LCD_MAX/2+1))               // Digit 1 ... 4 , i 0...3
    {
        Maske_setz   = 0x0011;
        Maske_loesch = 0x00FF - Maske_setz ;
        ch           = hex_Wert[char_Nr];
    } else                                  // Digit 5 ... 8
    {
        Maske_setz   = 0x0022;
        Maske_loesch = 0x00FF - Maske_setz ;
        ch           = hex_Wert[char_Nr] << 1;
        i           -= (LCD_MAX/2) ;
    }

  i <<= 2; i_End = i +4;                    // Berechnung der Start- und End -adresse

  for(; i < i_End;i++)
    {
       LCDMEM[i] &= Maske_loesch;
       LCDMEM[i] |= ( ch & Maske_setz );
       ch >>=1;
    }
}

  // -----------------------------------------------

void SetzeDP( int welches_Displ)          // Displ 8 , 7 , 6 ...
{
  int i , i1;
    for (i=0,i1=1;i<8;i++,i1 <<=1)
      {
        if(welches_Displ & i1)
        switch(i)
         {
           case 0 : LCDM4  |= 0x10; break;
           case 1 : LCDM8  |= 0x10; break;
           case 2 : LCDM12 |= 0x10; break;
           case 3 : LCDM16 |= 0x10; break;
           case 4 : LCDM4  |= 0x20; break;
           case 5 : LCDM8  |= 0x20; break;
           case 6 : LCDM12 |= 0x20; break;
           case 7 : LCDM16 |= 0x20; break;
           default: break;
         }
      }  // for
}

  // ------------------  ENDE  ----------------------