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):

#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:

#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)

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

 

1. ZeilePort 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. ZeileP1.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. ZeileP1.0 (LED1) wird auf 0 gesetzt, hier muss das P1OUT-Register benutzt werden.

 

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

 

1. ZeileFalls 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. ZeileXOR-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. ZeileVergleiche 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:

#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. ZeilePort 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. ZeileFü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. ZeileDas 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. ZeileMit diesem Befehl werden Interrupts global aktiviert.
15. ZeileEndlosschleife, hier passiert nichts. Der Rest wird in der Interrupt Service Routine bearbeitet.
19. ZeileMit 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. ZeileMit 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. ZeileDie LED wird bei betätigen des Tasters, ein- oder ausgeschaltet (XOR-Verknüpfung).
23. ZeileDas 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:

#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 *

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

This site uses Akismet to reduce spam. Learn how your comment data is processed.