B.2  Aufgaben aus Kapitel 2

1.
Schreibe einige Codezeilen, die Port 3.2 und Port 3.4 als Eingang und Port 3.3 als Ausgang definieren, ohne die anderen Bits des Registers zu beeinflussen.
 // .... 
 P3DIR = P3DIR & ~BIT2 & ~BIT4 | Bit3; // Bits 2 und 4 auf 0 
                                       // Bit 3 auf 1 
 P3SEL = P3SEL & ~BIT2 & ~BIT4 &~Bit3; // Bits 2,3,4 auf 0 
 // ....
2.
Wozu benötige ich die Interrupt-Funktionalität der Portpins? Was ist eine Interrupt- Service-Routine?
Mittels Interrupts kann man ereignisgesteuert den Programmablauf beeinflussen, ohne dass der Mikrocontroller während der Wartezeit auf das Ereignis blockiert wäre. In der Interrupt-Service-Routine ist die ereignisabhängige Funktionalität programmiert. Diese sollte jedoch so kurz wie möglich gehalten werden.

 
 

3.
Schreibe eine Interrupt-Service-Routine für Interrupts, die an Port 1.2 ausgelöst werden, die bei jedem zehnten Aufrufen eine im Hauptprogramm zugängliche Statusvariable auf 0 setzt. Was ist die potentielle Gefahr dabei?
#include <msp430x14x.h> 
 
int status = 0;               // globale Variable 
 
/**------------------------------------------------------ 
 * Interrupt service routine für den digital port 1.2 
 * This routine manipulates the global Variable status 
 * each time pin 1.2 has caused 10x the interrupt 
 **----------------------------------------------------*/ 
#pragma vector=PORT1_VECTOR 
__interrupt void Port_1(void)  { 
static char count = 0;        // interner Counter 
 
if( P1IFG & BIT2 )            // P3.2 Schuld am Interrupt? 
  { 
   count++;                   // zähle hoch 
   if (count >=10) {          // 10x gezählt? 
    count = 0;                // reset Counter 
    status = 0;               // setze Status 
   } 
   P1IFG = 0x00;              // lösche Interrupt-Flag 
  } 
} 
/**------------------------------------------------------ 
  * MAIN PROGRAM 
 **----------------------------------------------------*/ 
int main( void ) { 
  WDTCTL  = WDTPW | WDTHOLD;  // Watchdog-Timer ausschalten 
 
  P1DIR   = 0x00;             // alle Pins Eingang 
  P1SEL   = 0x00;             // normale I/O-Funktion 
  P1IES   = 0x00;             // steigende Flanke Interrupts 
  P1IFG   = 0x00;             // lösche Interrupt-Register 
  P1IE    = BIT2;             // Interrupt für pin 3.2 
 
  _enable_interrupts();       // globale Interrupts 
  status = 0; 
  while( 1 )                  // Endlosschleife 
  status++; 
} 
Bei der Veränderung globaler Variablen in Interrupts ist zu berücksichtigen, dass diese gerade von anderen Programmteilen benutzt werden können. Dies kann im Einzelfall sehr merkwürdiges Verhalten zur Folge haben.

 
 

4.
Entwickle eine kleine Ampelsteuerung. Verbinde dazu drei Leuchtdioden (rot, grün, gelb) über Vorwiderstände mit drei Portpins. Realisiere nun eine periodische Abfolge der bekannten Ampelphasen. Verwende für die Pausen eine Warteschleife.
long index; 
int ampelphase=0; 
#define CLOCK 8000 
#define WAITING(x) for (index=0; index<CLOCK*x; index++); 
  P3DIR   = 0xFF;               // Alle Pins Ausgang 
  P3SEL   = 0x00;               // normale I/O Funktion 
while(1){                       // Endlosschleife 
switch(ampelphase) { 
  case 0:  P3OUT = BIT1;      break; // rot 
  case 1:  P3OUT = BIT1|BIT2; break; // rot + gelb 
  case 2:  P3OUT = BIT3;      break; // grün 
  case 3:  P3OUT = BIT2;      break; // gelb 
  default: P3OUT= 0;          break; 
} 
WAITING(10000) 
ampelphase = (ampelphase+1) % 4;     // Phasensteuerung 
}
5.
Erweitere die Schaltung um einen Taster, der an einen anderen Portpin angeschlossen ist. Wenn der Taster gedrückt wird, soll das Ampelprogramm zwischen Tagbetrieb (normale Signalfolge) und Nachtbetrieb (gelbes Blinklicht) wechseln.
long index; 
int ampelphase=0; 
#define CLOCK 8000 
#define WAITING(x) for (index=0; index<CLOCK*x; index++); 
  P3DIR   = 0xFF;                // alle Pins Output 
  P3SEL   = 0x00;                // normale I/O-Funktion 
  P1DIR   = 0x00;                // alle Pins Input 
  P1SEL   = 0x00;                // normale I/O-Funktion 
while(1){                        // Endlosschleife 
 switch(ampelphase) { 
  case 0:  P3OUT = BIT1;      break; // rot 
  case 1:  P3OUT = BIT1|BIT2; break; // rot + gelb 
  case 2:  P3OUT = BIT3;      break; // grün 
  case 3:  P3OUT = BIT2;      break; // gelb 
  default: P3OUT= 0;          break; 
 } 
 WAITING(10000); 
 if ((P1IN & BIT1) > 0)          // Taste gedrückt?? 
 { 
  ampelphase = (ampelphase+1);   // Phasensteuerung Nacht 
  if (ampelphase >4) {ampelphase = 3;} 
 } 
 else 
 {ampelphase = (ampelphase+1)%4;}// Phasensteuerung normal 
}
6.
Warum ist die zeitliche Steuerung mit Warteschleifen nicht unbedingt zu empfehlen?
Warteschleifen blockieren die Abarbeitung anderer Programmteile und haben unter Umständen keine fest definierte Länge, wenn z.B. Interrupts während der Laufzeit aufgerufen werden. Zeitliche Vorgänge können mit Timern sehr genau gesteuert werden. Wie das geht, ist in Kapitel 4 beschrieben.

 
 

7.
Wie kann ich sicherstellen, dass eine Befehlsfolge nicht durch einen Interrupt unterbrochen wird? Was sollte ich dabei beachten?
In einigen C-Compilern, nicht jedoch beim MSP430, gibt es den sogenannten ATOMIC-Befehl, mit dem eine Befehlsfolge als ”unteilbar” definiert werden kann. Als manuelle Alternative werden vor der Befehlsfolge die Interrupts global deaktiviert und danach wieder aktiviert. Wichtig ist, die Befehlsfolge so kurz wie möglich zu halten, da ja eben nicht z.B. auf äußere Interrupt- Ereignisse reagiert werden kann. Unter Umständen geht so ein Ereignis verloren.

 
 

8.
Bei der Abfrage von vielen Tastern werden diese zu sogenannten ”Matrix-” Tastaturen verschaltet. Den Aufbau eines solchen Matrix-Tastenfeldes zeigt Bild 2.10. Wie kann ich herausfinden, welche Tasten gedrückt sind? Schreibe dazu ein Programm. Welche zusätzlichen Bauteile sind noch erforderlich?
int i;                          // Indexvariable 
char Tasten [3][3];               // Ergebnisfeld 
  P3DIR   = 0xFF;                 // alle Pins Output 
  P3SEL   = 0x00;                 // normale I/O-Funktion 
  P1DIR   = 0x00;                 // alle Pins Input 
  P1SEL   = 0x00;                 // normale I/O-Funktion 
while(1){                         // Endlosschleife 
  for(i=0; i<3; i++) {            // scanne Spalten 
  P3OUT = 0x01<<i;                // aktiviere Spalte 
  Tasten[i][0] = (P1IN & BIT1)>0; // erste Taste 
  Tasten[i][1] = (P1IN & BIT2)>0; // zweite 
  Tasten[i][2] = (P1IN & BIT3)>0; // dritte 
}
Die Pins a,b,c werden an einen Ausgangsport, die Pins 1,2,3 an einen Eingangsport angeschlossen. Im Prinzip sind keine weiteren Bauteile notwendig, da der MSP aktiv sowohl gegen VCC als auch gegen Masse treiben kann. Zur Sicherheit sollte jedoch in jede Leitung ein Widerstand von 330 Ω eingeschleift werden, siehe Kapitel 2.1.1, Seite §.