MSP430 Codebeispiele – Mikrocontroller.net (2024)

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.

Inhaltsverzeichnis

  • 1 Hardware
    • 1.1 Initializing/ Configuring UARTs
    • 1.2 Initialization/configuration of USART (SPI and I2C/TWI)
    • 1.3 Initialisierung der Quarze
    • 1.4 Initialisierung des ADCs
    • 1.5 Initialisierung des DACs
    • 1.6 Initialisierung des Timers A
    • 1.7 Initialisierung des Timers B
    • 1.8 Initialisierung des Watchdogs
    • 1.9 Initialisierung der GPIO
    • 1.10 Initialisierung von PWM
    • 1.11 Persistente Daten im Information Memory
  • 2 Interrupts:
    • 2.1 UARTs
    • 2.2 Timer
    • 2.3 IOs
  • 3 Softwareroutinen:
    • 3.1 I2C/TWI in Software
    • 3.2 PWM
    • 3.3 Senden eines Bytes
    • 3.4 Senden eines Strings
    • 3.5 Empfangen eines Bytes (interruptgesteuert) // Receiving a byte-> Interrupt controlled
    • 3.6 Ermitteln der Temperatur
    • 3.7 Ansteuerung eines LCD – Zeichendisplays // Controlling the LCD character

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 routineinterrupt (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 routineinterrupt (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 routineinterrupt (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 Vorbelegungstruct 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.

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_VECTORvoid usart0_rx(void){ TXBUF0=RXBUF0;}

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

Timer

IOs

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 interruptTACCTL1 = 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 peripheralP1DIR |= 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' makrointerrupt (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 verwendetStartcondition wird erkannt, aber nicht Stop, da man die Flanken(die deninterrupt 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 )#elseint 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 LCDconst 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 memoryvoid Clear_LCD(void);void lcd_init(void); in inivoid 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...11extern 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 ----------------------
MSP430 Codebeispiele – Mikrocontroller.net (2024)

References

Top Articles
Latest Posts
Article information

Author: Dr. Pierre Goyette

Last Updated:

Views: 6142

Rating: 5 / 5 (50 voted)

Reviews: 81% of readers found this page helpful

Author information

Name: Dr. Pierre Goyette

Birthday: 1998-01-29

Address: Apt. 611 3357 Yong Plain, West Audra, IL 70053

Phone: +5819954278378

Job: Construction Director

Hobby: Embroidery, Creative writing, Shopping, Driving, Stand-up comedy, Coffee roasting, Scrapbooking

Introduction: My name is Dr. Pierre Goyette, I am a enchanting, powerful, jolly, rich, graceful, colorful, zany person who loves writing and wants to share my knowledge and understanding with you.