MSP430 C-Programmierung: Grundlagen

In diesem Aritkel soll es einen kleinen Einstieg in die C-Programmierung für den MSP430 geben. Das Assembler-Programm aus dem Artikel “Assembler-Programmierung” wird in ein C-Programm übersetzt und beschrieben.

1. Tasterabfrage bzw. Portabfrage und Ports setzen

UPDATE: Für Launchpads mit Rev.1.5 (Pull-Up an P1.3 muss aktiviert werden):

  1. #include <msp430g2253.h>
  2.  
  3. void main(void)
  4. {
  5.     WDTCTL=WDTPW+WDTHOLD;       /* stop watchdog timer */
  6.  
  7.     P1DIR|=BIT0;                /* P1.0 (LED1) -> output */
  8.     P1DIR&=~BIT3;               /* P1.3 (S2) -> input */
  9.     P1REN|=BIT3;                /* enable P1.3 Pull-Up */
  10.     P1OUT&=~BIT0;               /* clear P1.0 (LED1 off) */
  11.  
  12.     while(1)
  13.     {
  14.         if(!(P1IN&BIT3))            /* test P1.3 (pressed?) */
  15.         {
  16.             P1OUT^=BIT0;            /* toggle P1.0 */
  17.             while(!(P1IN&BIT3));    /* test P1.3 again (pressed?) */
  18.         }
  19.     }
  20. }
#include <msp430g2253.h>

void main(void)
{
    WDTCTL=WDTPW+WDTHOLD;       /* stop watchdog timer */

    P1DIR|=BIT0;                /* P1.0 (LED1) -> output */
    P1DIR&=~BIT3;               /* P1.3 (S2) -> input */
    P1REN|=BIT3;                /* enable P1.3 Pull-Up */
    P1OUT&=~BIT0;               /* clear P1.0 (LED1 off) */

    while(1)
    {
        if(!(P1IN&BIT3))            /* test P1.3 (pressed?) */
        {
            P1OUT^=BIT0;            /* toggle P1.0 */
            while(!(P1IN&BIT3));    /* test P1.3 again (pressed?) */
        }
    }
}

 

Für Launchpads mit Rev.1.4:

  1. #include <msp430g2231.h>
  2.  
  3. void main(void)
  4. {
  5.     WDTCTL=WDTPW+WDTHOLD;       /* stop watchdog timer */
  6.  
  7.     P1DIR|=BIT0;                /* P1.0 (LED1) -> output */
  8.     P1DIR&=~BIT3;               /* P1.3 (S2) -> input */
  9.     P1OUT&=~BIT0;               /* clear P1.0 (LED1 off) */
  10.  
  11.     while(1)
  12.     {
  13.         if(!(P1IN&BIT3))            /* test P1.3 (pressed?) */
  14.         {
  15.             P1OUT^=BIT0;            /* toggle P1.0 */
  16.             while(!(P1IN&BIT3));    /* test P1.3 again (pressed?) */
  17.         }
  18.     }
  19. }
#include <msp430g2231.h>

void main(void)
{
    WDTCTL=WDTPW+WDTHOLD;       /* stop watchdog timer */

    P1DIR|=BIT0;                /* P1.0 (LED1) -> output */
    P1DIR&=~BIT3;               /* P1.3 (S2) -> input */
    P1OUT&=~BIT0;               /* clear P1.0 (LED1 off) */

    while(1)
    {
        if(!(P1IN&BIT3))            /* test P1.3 (pressed?) */
        {
            P1OUT^=BIT0;            /* toggle P1.0 */
            while(!(P1IN&BIT3));    /* test P1.3 again (pressed?) */
        }
    }
}

 

1.1 Beschreibung (Zeilenweise)

  1. P1DIR|=BIT0;                /* P1.0 (LED1) -> output */
  2. P1DIR&=~BIT3;               /* P1.3 (S2) -> input */
  3. P1OUT&=~BIT0;               /* clear P1.0 (LED1 off) */
P1DIR|=BIT0;                /* P1.0 (LED1) -> output */
P1DIR&=~BIT3;               /* P1.3 (S2) -> input */
P1OUT&=~BIT0;               /* clear P1.0 (LED1 off) */

 

1. Zeile Port 1.0 wird als Ausgang definiert (das LSB (BIT0 oder 0x01) wird gesetzt). Dies geschiet durch eine Standard-C-Bitoperation (ODER “|”). In dem Code wird eine Kurzform benutzt um ein einzelnes Bit zu manipulieren, ausgeschrieben könnte es so aussehen:P1DIR = P1DIR | (0x01 << bit_nummer)“bit_nummer” steht hier für das zu manipulierende Bit. Der hintere Teil in der Klammer “shiftet” ein Bit (00000001) an Position “bit_nummer” nach links. Wäre bit_nummer=3 so würde sich folgendes ergeben: 00000001 (0x01) wird zu 00001000 (0x10) -> Das LSB wird an die 3. Stelle geschoben. Somit wird durch die “|”-Operation das 3. Bit gesetzt (wenn P1DIR den Anfangswert 0x00 hat).
2. Zeile P1.3 wird geleert bzw. auf 0 gesetzt, ausgeschriebener Befehl:P1DIR = P1DIR & ~(0x01<< bit_nummer)Diesmal wird die “&”-Operation und “~” (komplement) benutzt. Sei der Anfangswert von P1DIR=0x10 (00001000), also das Bit was in Zeile 1 gesetzt wurde. Dies soll jetzt wieder auf 0 gesetzt werden. Schauen wir uns die Klammer an: bit_nummer=3 -> 00001000 (das LSB wird an Stelle 3 geschoben), das Komplement daraus: 11110111. Die UND-Verknpüfung daraus ergibt 0x00 oder binär 00000000.
3. Zeile P1.0 (LED1) wird auf 0 gesetzt, hier muss das P1OUT-Register benutzt werden.

 

  1. if(!(P1IN&BIT3))                /* test P1.3 (pressed?) */
  2. {
  3.     P1OUT^=BIT0;                /* toggle P1.0 */
  4.     while(!(P1IN&BIT3));        /* test P1.3 again (pressed?) */
  5. }
if(!(P1IN&BIT3))                /* test P1.3 (pressed?) */
{
    P1OUT^=BIT0;                /* toggle P1.0 */
    while(!(P1IN&BIT3));        /* test P1.3 again (pressed?) */
}

 

1. Zeile Falls der Taster (P1.3) betätigt wurde, wird in die in if-Abfrage gesprungen. Wenn der Taster nicht gedrückt wurde, hat das P1IN-Register den Wert: 00001000, gedrückt: 00000000. Die UND-Operation zwischen BIT3 und P1IN ergibt 0, somit wird die if-Abfrage wahr.
3. Zeile XOR-Operation, ist die LED an und der Taster wird betätigt, wird P1.0 auf 0 gesetzt. Ist die LED aus und der Taster wird wieder betätigt, wird P1.3 auf 1 gesetzt.
4. Zeile Vergleiche Zeile 1. Hier wird so lange in der while-Schleife abgefragt bis der Taster wieder losgelassen wird. Durch die Endlosschleife (while(1)) wird wieder an die if-Abfrage gesprungen usw.

2. Interrupts

Das gleiche Programm (siehe oben) kann natürlich auch mit Interrupts bzw. einem Interrupt realisiert werden. Dazu folgendes Beispiel-Programm:

  1. #include <msp430g2231.h>
  2.  
  3. void main(void)
  4. {
  5.     WDTCTL=WDTPW+WDTHOLD;       // stop WDT
  6.     // every IO is defined as input by default
  7.     P1DIR&=~BIT3;               // P1.3 (S2) input
  8.     P1IE|=BIT3;                 // enable P1.3 interrupt
  9.     P1IES|=BIT3;                // interrupt @ high/low edge
  10.     P1IFG&=~BIT3;               // clear interrupt flag
  11.     P1DIR|=BIT0;                // P1.0 (LED1) output
  12.     P1OUT&=~BIT0;               // LED off
  13.     _EINT();
  14.  
  15.     for(;;);
  16. }
  17.  
  18. // ISR will be terminated with RETI (return from interrupt)
  19. #pragma INTERRUPT (ISR_Port1)
  20. #pragma vector=PORT1_VECTOR     // start address 0xFFE4 Port 1
  21. void ISR_Port1(void)
  22. {
  23.     P1OUT^=BIT0;                // toggle LED1
  24.     P1IFG&=~BIT3;               // clear interrupt flag
  25. }
#include <msp430g2231.h>

void main(void)
{
	WDTCTL=WDTPW+WDTHOLD;		// stop WDT
	// every IO is defined as input by default
	P1DIR&=~BIT3;				// P1.3 (S2) input
	P1IE|=BIT3;					// enable P1.3 interrupt
	P1IES|=BIT3;				// interrupt @ high/low edge
	P1IFG&=~BIT3;				// clear interrupt flag
	P1DIR|=BIT0;				// P1.0 (LED1) output
	P1OUT&=~BIT0;				// LED off
	_EINT();

	for(;;);
}

// ISR will be terminated with RETI (return from interrupt)
#pragma INTERRUPT (ISR_Port1)
#pragma vector=PORT1_VECTOR		// start address 0xFFE4 Port 1
void ISR_Port1(void)
{
	P1OUT^=BIT0;				// toggle LED1
	P1IFG&=~BIT3;				// clear interrupt flag
}

2.1 Beschreibung (Zeilenweise)

7. Zeile Port 1.3 wird als Eingang definiert, standardmäßig sind alle Ports als Eingang definiert und die Bits im P1DIR-Register müssen nicht unbedingt auf “0” gesetzt werden.
8. Zeile Für P1.3 wird ein Interrupt aktiviert. Dies geschieht über das P1IE-Register (Port 1 Interrupt Enable). Wer für Port 2 einen Interrupt aktivieren will, benutzt das P2IE-Register usw.
9. Zeile Über das P1IES-Register (Interrupt Edge Select) wird festgelegt, bei welcher Flanke ein Interrupt ausgelöst werden soll. In diesem Beispiel wird bei einer High-Low-Flanke (“1” -> “0”) ein Interrupt ausgelöst.
10. Zeile Das P1IFG-Register wird geleert (bzw. das 3. Bit). Das IFG kann möglicherweise gesetzt werden, falls am P1DIR oder P1IE-Register etwas geändert wurde, so könnte direkt ein Interrupt ausgelöst werden in der Initialsierung (muss aber nicht).
11+12. P1.0 wird als Ausgang definiert (LED) und auf “0” gesetzt.
13. Zeile Mit diesem Befehl werden Interrupts global aktiviert.
15. Zeile Endlosschleife, hier passiert nichts. Der Rest wird in der Interrupt Service Routine bearbeitet.
19. Zeile Mit der #pragma-Direktive wird dem Compiler mitgeteilt, die zuständige Funktion (Interrupt Service Routine) mit einem RETI zu verlassen -> Return from Interrupt. ISRs sind immer parameterlos (keine Übergabewerte, keine Rückgabewerte). In Assembler muss “RETI” am Ende einer ISR geschrieben werden.
20. Zeile Mit dieser Direktive wird die Startadresse der ISR eingetragen. Falls ein Interrupt ausgelöst wird, muss der Compiler wissen wohin er überhaupt springen soll. Die Interrupt-Tabelle (Source, Address, Priority) unterscheiden sich natürlich in der MSP430-Reihe.
22. Zeile Die LED wird bei betätigen des Tasters, ein- oder ausgeschaltet (XOR-Verknüpfung).
23. Zeile Das Interrupt Flag für P1.3 wird zurücksetzt. Somit wurde die ISR abgearbeitet und kann verlassen werden.

3. C und Assembler vermischen

Oder auch Assembler Inlining genannt. Natürlich ist es möglich in C, Assembler Code auszuführen. Dazu ein kleines Beispiel-Programm:

  1. #include <msp430g2231.h>
  2. // clear negative bit, zero bit and carry bit
  3. #define SR_CLEAR_CZN asm ("\t CLRC\n\t CLRZ\n\t CLRN")
  4. // #define SR_CLEAR_CZN asm ("\t bic.w #1,SR\n\t bic.w #2,SR\n\t bic.w #4,SR")
  5.  
  6. void main(void)
  7. {
  8.     volatile char test_var=255;
  9.     WDTCTL=WDTPW+WDTHOLD;       // stop WDT
  10.     test_var++;
  11.     SR_CLEAR_CZN;               // clear SR
  12. }
#include <msp430g2231.h>
// clear negative bit, zero bit and carry bit
#define SR_CLEAR_CZN asm ("\t CLRC\n\t CLRZ\n\t CLRN")
// #define SR_CLEAR_CZN asm ("\t bic.w #1,SR\n\t bic.w #2,SR\n\t bic.w #4,SR")

void main(void)
{
	volatile char test_var=255;
	WDTCTL=WDTPW+WDTHOLD;		// stop WDT
	test_var++;
	SR_CLEAR_CZN;				// clear SR
}

In diesem Programm werden Carry, Zero und Negative-Bit im Status-Register (SR) auf “0” gesetzt. Dies geschieht über die Direktive in Zeile 3. Wichtig hier ist: “\t” (Tabulator). Der ASM-Code muss immer eingerückt sein, da vor dem OP-Code das Label steht.

Leave a Reply

Your email address will not be published. Required fields are marked *

Time limit is exhausted. Please reload CAPTCHA.