corni's Link- und Fundstücktonne

blog.cpoth.de
Subscribe

Artikel der Kategorie ‘Programmieren: C’

MSP430: Pipeline Architektur

Mai 24, 2013 Von: corni Kategorie: Programmieren: C, TI Noch keine Kommentare →

Nach außen ist der MSP430 der MSP430. Allerdings hat sich am CPU Kern auch einiges getan und nicht alles wird offensiv kommuniziert. TI dokumentiert etwa im Code Composer Studio User’s Guide for MSP430 (Revision SLAU157Y, stand Mai 2013) im Kapitel „Using the Integrated Debugger, Breakpoint Types“ drei unterschiedliche Architekturen, MSP430, MSP430X und MSP430Xv2, allerdings bleiben bis auf die Erweiterung des Adressbusses auf 20 bit bei den X-Architekturen weitere Details verborgen.

Ein weiterer erheblicher Unterschied zwischen MSP430 und MSP430X ist die neu eingeführte three stage pipeline architecture. Dabei werden Taktzyklen in denen der Bus ungenutzt bliebe, etwa während die CPU einen Befehl verarbeitet, genutzt um den nächsten Befehl schon zu holen (siehe auch Wikipedia Artikel zur Pipeline Architektur).

Mit folgendem Programm lassen sich Auswirkungen dieser Architekturänderung demonstrieren:

#include <msp430.h>

void main(void) {
    WDTCTL = WDTPW | WDTHOLD;	// Stop watchdog timer

    P1OUT &= ~BIT0;			// Reset P1.0
    P1DIR |= BIT0;			// P1.0 Output

    P1OUT |= BIT0;			// P1.0 high
    __delay_cycles(1000);		// 1 ms @ 1 MHz, DCO default
    P1OUT &= ~BIT0;   			// P1.0 low

    while(1);				// 
}

Lässt man das Programm zum Beispiel auf einem MSP430F5529 oder MSP430F5438A (MSP430Xv2) laufen und misst die Zeit welche der Port 1.0 ein high Signal liefert beträgt diese Zeit 1 ms, also 1000 Zyklen. Die Funktion __delay_cycles() fügt 1000 NOP Befehle ein, der DCO läuft in der Grundeinstellung mit 1 MHz.
Wird an der while(1) Schleife allerdings ein Breakpoint gesetzt ändert sich die delay Zeit von 1 ms auf rund 4 ms (zumindest beim MSP430F552x und beim MSP430F543xA). Diesen Effekt kann man bei MSP430 Derivaten welche nicht die MSP430X oder Xv2 Architektur haben nicht feststellen. Hier bleibt die delay Zeit immer 1 ms, egal ob an der while(1) Schleifen ein Breakpoint gesetzt wird oder nicht (etwa beim MSP430G2452 bzw. allen MSP430G2xxx Derivaten).

Wird der Breakpoint gesetzt passiert folgendes:
Der aktuelle Befehl 1 (P1OUT &= ~BIT0;) wird gerade ausgeführt, der Befehl 2 (hier ist der Breakpoint gesetzt) wird in die Pipeline geladen. Die EEM „sieht“ beim Befehl 2 einen Breakpoint und hält die CPU an welche allerdings noch an Befehl 1 arbeitet. Die EEM schaut auf den Bus, weiß allerdings nicht was die CPU gerade wirklich tut. Nun ist Befehl 1 halb ausgeführt, die CPU allerdings angehalten. In diesem Zustand (Befehl 1 noch nicht fertig ausgeführt) findet dann einiges an JTAG Kommunikation statt und die Pipeline wird geleert. Wenn die Pipeline leer ist wird Befehl 1 noch fertig ausgeführt. So kommt es zu den 4 ms delay bis der Port wieder getoggelt wird.
Die ursprüngliche MSP430 Architektur ohne Pipeline holt den nächsten Befehl (die while(1) Schleife mit dem Breakpoint) erst wenn der alte Befehl vollständig abgearbeitet wurde (der Port wieder auf low ist).

Sollte der Effekt beim debuggen stören kann er durch einfügen von zwei NOP Befehlen (also zwei __no_operation(); vor der while(1) Schleife) beseitigt werden. Dann werden die zwei NOPs in die Pipeline geladen und befinden sich in der Ausführung, wenn die while(1) Schleife mit dem Breakpoint geladen wird. Das Port toggeln ist dann schon fertig abgearbeitet und das delay dauert genau 1 ms. Zwei NOP Befehle, da die Pipleline drei Stages (Stufen) hat. Es können also zusätzlich zum aktuellen Befehl immer schon bis zu zwei folgende Befehle geladen werden.

MSP-EXP430FR5739: ADC10B Sequenz per DMA in Array ablegen

August 17, 2012 Von: corni Kategorie: Programmieren: C, TI Noch keine Kommentare →

Das Beispielprogramm ruft mit 50 Hz den ADC auf welcher eine Serie an Messungen macht (Channel A1, dann A0). Der ADC triggert nach jeder Messung die DMA welche das Messergebnis abholt und in das Array ADC_Result schreibt. Das alles läuft völlig ohne CPU Interaktion.

#include "msp430fr5739.h"

unsigned int ADC_Result[2];

void main(void)
{
	WDTCTL = WDTPW + WDTHOLD;				// Stop WDT

	// Setup clock system
	CSCTL0_H = 0xA5;						// Password
	CSCTL1 |= DCOFSEL0 + DCOFSEL1;          // DCO 8 MHz
	CSCTL2 = SELA_3 + SELS_3 + SELM_3;      // set ACLK = SMCLK = DCO/8

	// Port Setup
	P1SEL0 |= BIT0;                      	// P1.0 is input for ADC
	P1SEL1 |= BIT0;							// Channel A0
	P1SEL1 |= BIT1;							// P1.1 is input for ADC
	P1SEL0 |= BIT1;							// Channel A1

	// Setup der DMA
	DMACTL0 |= DMA0TSEL__ADC10IFG;			// ADC10 interrupt soll die DMA triggern
	__data16_write_addr((unsigned short)&DMA0SA,(unsigned long) &ADC10MEM0);
					// DMA soll das ADC Ergebnis lesen...
	__data16_write_addr((unsigned short)&DMA0DA,(unsigned long) &ADC_Result[0]);
					// ...und ins array ADC_Result[] schreiben
	DMA0SZ = 2;								// One word per transfer
	DMA0CTL |= DMADT_4;						// Repeated single transfer
	DMA0CTL &= ~DMADSTBYTE;					// DMA destination is a word
	DMA0CTL &= ~DMASRCBYTE;					// DMA source is a word
	DMA0CTL |= DMASRCINCR_0;				// Source address is unchanged (immer gleiches Register)
	DMA0CTL |= DMADSTINCR_3;				// Destination address is incremented (nächstes array element)
	DMA0CTL &= ~DMALEVEL;					// Trigger on low level (otherwise DMA is too fast)
	DMA0CTL |= DMAIE;						// Enable Interrupts
	DMA0CTL |= DMAEN;						// Enable DMA

	// Setup Timer
	TA0CCR0 = 20000 - 1;					// PWM Period 50 Hz
	TA0CCTL1 = OUTMOD_7;					// CCR1 reset/set
	TA0CCR1 = 2000;							// CCR1 PWM duty cycle 10 %
	TA0CTL = TASSEL_2 + MC_1 + TACLR;		// SMCLK, up mode, clear TAR

	// Setup ADC
	ADC10CTL0 &= ~ADC10ENC;
	ADC10CTL1 |= ADC10CONSEQ_3;				// Repeat-Sequence-of-Channels Mode
	ADC10CTL1 |= ADC10SHS_1;				// Set PWM as trigger source
	ADC10CTL1 &= ~ADC10SHP;					// Bypass Sample timer
	ADC10CTL2 |= ADC10RES;                  // 10-bit conversion results
	ADC10MCTL0 |= ADC10INCH_1;				// Start Sequence with Channel A1
	ADC10CTL0 |= ADC10ON;					// ADC on
	ADC10CTL0 |= ADC10ENC;					// Enable ADC conversation

	_BIS_SR(GIE);							// General interrupt enable

	while(1) __bic_SR_register(CPUOFF);		// CPU off
}


#pragma vector=DMA_VECTOR
__interrupt void DMA0_ISR (void)
{
	switch(__even_in_range(DMAIV,16))
	{
		case  0: break;						// No interrupt
		case  2:
			// ADC and DMA are ready, values are now stored in Array ADC_Result
			__no_operation();				// Set breakpoint here
			break;							// DMA0IFG
		case  4: break;						// DMA1IFG
		case  6: break;						// DMA2IFG
		case  8: break;						// Reserved
		case 10: break;						// Reserved
		case 12: break;						// Reserved
		case 14: break;						// Reserved
		case 16: break;						// Reserved
		default: break;
	}
}

Enter MSP430F5xxx LPMx.5

August 13, 2012 Von: corni Kategorie: Programmieren: C, TI Noch keine Kommentare →

Das geht in C so:

	PMMCTL0_H = PMMPW_H;					// Enter PMM password
	PMMCTL0_L |= PMMREGOFF;					// Enter LPMx.5 when PMMREGOFF is set

	__bis_SR_register([LPM3;LPM4]_bits);	// Enter LPM

MSP-EXP430FR5739: ADC10B Sequenz mit PWM triggern

August 08, 2012 Von: corni Kategorie: Programmieren: C, TI Noch keine Kommentare →

Der ADC10B des MSP430 kann auch automatisch mehrere Kanäle nacheinander messen, ohne dass manuell hin und her geschaltet werden muss.
Das folgende Programm misst zunächst Kanal A1 und dann A0. Der ADC fängt immer beim höchsten in ADC10MCTL0 eingestellen Kanal an und zählt dann herunter. Nach jeder Messung wird in diesem Programm ein Interrupt ausgelöst und der gerade gemessene Wert kann ausgelesen werden.

#include "msp430fr5739.h"

unsigned int ADC_Result;

void main(void)
{
	WDTCTL = WDTPW + WDTHOLD;				// Stop WDT

	// Setup clock system
	CSCTL0_H = 0xA5;						// Password
	CSCTL1 |= DCOFSEL0 + DCOFSEL1;          // DCO 8 MHz
	CSCTL2 = SELA_3 + SELS_3 + SELM_3;      // set ACLK = SMCLK = DCO/8

	// Port Setup
	P1SEL0 |= BIT0;                      	// P1.0 is input for ADC
	P1SEL1 |= BIT0;							// Channel A0
	P1SEL1 |= BIT1;							// P1.1 is input for ADC
	P1SEL0 |= BIT1;							// Channel A1

	// Setup Timer
	TA0CCR0 = 20000 - 1;						// PWM Period 50 Hz
	TA0CCTL1 = OUTMOD_7;						// CCR1 reset/set
	TA0CCR1 = 2000;								// CCR1 PWM duty cycle 10 %
	TA0CTL = TASSEL_2 + MC_1 + TACLR;			// SMCLK, up mode, clear TAR

	// Setup ADC
	ADC10CTL0 &= ~ADC10ENC;
	ADC10CTL0 |= ADC10SHT_2 + ADC10ON;      // ADC10ON, S&H=16 ADC clks
	ADC10CTL1 |= ADC10CONSEQ_3;				// Repeat-Sequence-of-Channels Mode
	ADC10CTL1 |= ADC10SHS_1;				// Set PWM as trigger source
	ADC10CTL1 &= ~ADC10SHP;					// Bypass Sample timer
	ADC10CTL2 |= ADC10RES;                  // 10-bit conversion results
	ADC10MCTL0 |= ADC10INCH_1;				// Start Sequence with Channel A1
	ADC10IE |= ADC10IE0;                    // Enable ADC conv complete interrupt
	ADC10CTL0 |= ADC10ENC;					// Enable ADC conversation

	_BIS_SR(GIE);

	while(1) __bic_SR_register(CPUOFF);
}

#pragma vector=ADC10_VECTOR
__interrupt void ADC10_ISR(void)
{
	__no_operation();
  switch(__even_in_range(ADC10IV,12))
  {
    case  0: break;                          // No interrupt
    case  2: break;                          // conversion result overflow
    case  4: break;                          // conversion time overflow
    case  6: break;                          // ADC10HI
    case  8: break;                          // ADC10LO
    case 10: break;                          // ADC10IN
    case 12:
    	ADC_Result = ADC10MEM0;
    	__no_operation();
        __bic_SR_register_on_exit(CPUOFF);
        break;

    default: break;
  }
}

MSP-EXP430FR5739: ADC10B mit PWM triggern

August 06, 2012 Von: corni Kategorie: Programmieren: C, TI Noch keine Kommentare →

Im folgenden Beispiel wird mit dem Timer A0 eine PWM (50 Hz, 10 % duty cycle) erzeugt welche ohne CPU Interaktion den ADC 10B des MSP430FR5739 triggert und eine Messung auslöst. Gemessen wird die Spannung an Port 1.1. Ist der ADC fertig wird ein Interrupt ausgelöst.
Nur während die ISR läuft wacht die CPU kurz auf.

#include "msp430fr5739.h"

unsigned int ADC_Result;

void main(void)
{
	WDTCTL = WDTPW + WDTHOLD;				// Stop WDT

	// Setup clock system
	CSCTL0_H = 0xA5;						// Password
	CSCTL1 |= DCOFSEL0 + DCOFSEL1;          // DCO 8 MHz
	CSCTL2 = SELA_3 + SELS_3 + SELM_3;      // set ACLK = SMCLK = DCO/8

	// Port Setup
	P1DIR |= BIT0;                          // Set P1.0 to output direction
	P1SEL0 |= BIT0;                      	// P1.0 is output for PWM for debugging
	P1SEL1 |= BIT1;							// P1.1 is input for ADC
	P1SEL0 |= BIT1;							// P1.1 is input for ADC

	// Setup Timer
	TA0CCR0 = 20000 - 1;						// PWM Period 50 Hz
	TA0CCTL1 = OUTMOD_7;						// CCR1 reset/set
	TA0CCR1 = 2000;								// CCR1 PWM duty cycle 10 %
	TA0CTL = TASSEL_2 + MC_1 + TACLR;			// SMCLK, up mode, clear TAR

	// Setup ADC
	ADC10CTL0 &= ~ADC10ENC;
	ADC10CTL0 |= ADC10SHT_2 + ADC10ON;      // ADC10ON, S&H=16 ADC clks
	ADC10CTL1 |= ADC10CONSEQ_2;				// Single-channel repeat
	ADC10CTL1 |= ADC10SHS_1;				// Set PWM as trigger source
	ADC10CTL1 &= ~ADC10SHP;					// Bypass Sample timer
	ADC10CTL2 |= ADC10RES;                  // 10-bit conversion results
	ADC10MCTL0 |= ADC10INCH_1;              // A1 ADC input select; Vref=AVCC
	ADC10IE |= ADC10IE0;                    // Enable ADC conv complete interrupt
	ADC10CTL0 |= ADC10ENC;					// Enable ADC conversation

	_BIS_SR(GIE);

	while(1) __bic_SR_register(CPUOFF);
}

#pragma vector=ADC10_VECTOR
__interrupt void ADC10_ISR(void)
{
	__no_operation();
  switch(__even_in_range(ADC10IV,12))
  {
    case  0: break;                          // No interrupt
    case  2: break;                          // conversion result overflow
    case  4: break;                          // conversion time overflow
    case  6: break;                          // ADC10HI
    case  8: break;                          // ADC10LO
    case 10: break;                          // ADC10IN
    case 12:
    	ADC_Result = ADC10MEM0;
    	__no_operation();
        __bic_SR_register_on_exit(CPUOFF);
        break;

    default: break;
  }
}

MSP430 LaunchPad SPI Example

August 01, 2012 Von: corni Kategorie: Programmieren: C, TI Noch keine Kommentare →

Das folgende Programm verschickt auf Tasterdruck (S2) ein byte (0x22) per SPI.

// P1.5 = SCLK
// P1.6 = DOUT
// P1.7 = DIN

#include <msp430.h>

void main(void)
{

	WDTCTL = WDTPW + WDTHOLD;             // Stop watchdog timer

	// Port 1.3: Switch Input
	P1DIR &= ~BIT3;						// P1.3 = input
	P1REN |= BIT3;						// Pullup/down resistor enabled
	P1OUT |= BIT3;						// Pin is pulled enabled
	P1IE |= BIT3;						// Enable interrupts from switch
	P1IES |= BIT3;						// rising edge

	// Setup USI -> SPI Master
	USICTL0 |= USIPE7 +  USIPE6 + USIPE5 + USIMST + USIOE;	// Port, SPI master
	USICTL1 |= USIIE;					// Counter interrupt, flag remains set
	USICTL1 |= USICKPH;					// Data is captured on the first SCLK edge and changed on the following edge
	USICKCTL = USIDIV_4 + USISSEL_2;	// /16 SMCLK
	USICTL1 &= ~USIIFG;					// reset IRQ flag
	USICTL0 &= ~USISWRST;				// USI released for operation

	_BIS_SR(GIE);
	while(1);
}

#pragma vector=USI_VECTOR
__interrupt void universal_serial_interface(void)
{
	USICTL1 &= ~USIIFG;					// reset IRQ flag
}

#pragma vector=PORT1_VECTOR
__interrupt void Port_1(void)
{
	P1IE &= ~BIT3;						// Disable interrupts from switch

	USISRL = 0x22;						// write data to send into fifo
	USICNT = 8;							// reload counter

	P1IFG &= ~BIT3; 					// clear Interrupt Flag
	P1IE |= BIT3;						// Enable interrupts from switch
}

Mit dem folgenden Programm können die Daten auf einem zweiten LaunchPad empfangen werden:

#include <msp430.h>

void main(void)
{
	int received_data[1];
	WDTCTL = WDTPW + WDTHOLD;             // Stop watchdog timer

	// Setup USI -> SPI Slave
	USICTL0 |= USIPE7 +  USIPE6 + USIPE5 + USIOE;	// Port setup
	USICNT &= ~USI16B;					// only 8 bit mode
	USICTL0 &= ~USIMST;					//  SPI slave
	USICTL1 |= USIIE;					// Counter interrupt, flag remains set
	USICTL1 |= USICKPH;					// Data is captured on the first SCLK edge and changed on the following edge
	USICKCTL = USIDIV_4 + USISSEL_2;	// /16 SMCLK
	USICTL1 &= ~USIIFG;					// reset IRQ flag
	USICTL0 &= ~USISWRST;				// USI released for operation
	received_data[0] = USISRH;
	received_data[0] = USISRL;			// Empty USISRL
	//USISRL = 0x22;
	USICNT = 7;                           // init-load counter

	_BIS_SR(GIE);
	while(1);

}

#pragma vector=USI_VECTOR
__interrupt void universal_serial_interface(void)
{
	int received_data[1];
	received_data[0] = USISRH;
	received_data[0] = USISRL;

	USICNT = 7;
	USICTL1 &= ~USIIFG;					// reset IRQ flag
}

MSP430 LaunchPad PWM Example (MSP430G2231)

Juli 11, 2012 Von: corni Kategorie: Programmieren: ASM MSP430, Programmieren: C, TI Noch keine Kommentare →

Mit dem MSP430 ist es möglich eine PWM zu erzeugen – ganz ohne CPU Interaktion und ohne Interrupts. Die ISR in diesem Beispiel dient ausschließlich dazu den duty cycle zu verändern.

Der Trick ist den Output der Caputre/Compare Unit zu verwenden. Er kann direkt an den GPIO Port 1.2 „angeschlossen“ werden.

Das LaunchPad ist für dieses Beispiel mit einem MSP430G2231 bestückt.

#include <msp430.h>				

int main(void) {

	while(1){							// All in a while loop just for safety
		WDTCTL = WDTPW + WDTHOLD;		// Stop watchdog timer

		// GPIO Port 1
		// Port 1.2: PWM Output
		P1DIR |= BIT2;					// P1.2 = output
		P1SEL |= BIT2;					// P1.2 = TA1 output
		// Port 1.3: Switch Input
		P1DIR &= ~BIT3;					// P1.3 = input
		P1REN |= BIT3;					// Pullup/down resistor enabled
		P1OUT |= BIT3;					// Pin is pulled enabled
		P1IE |= BIT3;					// Enable interrupts from switch
		P1IES |= BIT3;					// rising edge

		// Clock System
		BCSCTL3 |= LFXT1S_2;			// ACLK source is VLO, 12 kHz

		// Timer A
		TACCTL1	|= OUTMOD_7;			// TACCR1 reset/set
		TACTL |= TASSEL_1;				// Select ACLK as source
		TACTL	|= MC_1;				// Up mode: the timer counts up to TACCR0
		TACCR0	= 0x009F;				// PWM Period - up to int 159
		TACCR1	= 0x0000;				// TACCR1 PWM Duty Cycle, 0 %

		_BIS_SR(LPM3_bits + GIE);		// Go to LPM 3 and enable interrupts in general
	}
}

#pragma vector=PORT1_VECTOR
__interrupt void Port_1(void)
{
	int TACCR1_reg = TACCR1;
	P1IE &= ~BIT3;					// Disable interrupts from switch

	if(TACCR1_reg >= 160)			// PWM Duty Cycle weiterzählen
	{								// Zähler zählt bis 0x009F, also 159
		TACCR1 = 0;					// 100 % duty cycle entspricht dann 160
	} else {
		TACCR1 = TACCR1_reg + 20;
	}

	P1IFG &= ~BIT3; 				// clear Interrupt Flag
	P1IE |= BIT3;					// Enable interrupts from switch
}

Bringt man den MSP wie im Beispielcode in den Low Power Mode 3 messe ich rund 64 uA Stromaufnahme (Stromversorgung per USB). Im LPM2 waren es 93 uA, im LPM1 157 uA und im Active Mode ca. 450 uA.
Entfernt man die Jumper der LEDs (J5), die des BiWire JTAG und die der UART Schnittstelle (TEST, RST, RXD, TXD an J3) misst man im LPM3 noch 0,8 bis 0,9 uA (mein Messgerät ist nicht so genau…). Fasst man jedoch mit der Hand an die Pins des MSP430 schwankt die Stromaufnahme stark. Das liegt an den uninitialisierten Ports welche in undefinierten Zuständen floaten können. Um das zu ändern kann man diese entweder alle auf GND oder Vdd legen oder als Output initialisieren bevor die eigentliche Portinitialisierung vorgenommen wird.

	P1DIR = 0xFF;	// Output
	P2DIR = 0xFF;
	P1OUT = 0x00;	// Value = low
	P2OUT = 0x00;

Jetzt misst man noch wie vor ca. 0,7 bis 0,8 uA (ein bisschen weniger als vorher) aber dieser Wert bleibt konstant wenn man mit der Hand die Pins berührt. Entferne ich mein Oszi von Port 1.2 sind es nur noch 0,6 uA.

Mit dem folgenden Programm kann man duty cycle und Frequenz einer PWM messen:

#include <msp430.h>				

void init_board(void);

unsigned int RISE1 = 0;
unsigned int RISE2 = 0;
unsigned int FALL1 = 0;
unsigned int PERIOD = 0;
unsigned int DUTYCY = 0;
unsigned int DUTYCYPERCENT = 0;

int main(void) {

	while(1){							// All in a while loop just for safety
		WDTCTL = WDTPW + WDTHOLD;		// Stop watchdog timer

		init_board();					// Set all pins to output low

		// GPIO Port 1
		P1DIR &= ~BIT1;					// P1.1 is input
		P1REN |= BIT1;					// Enable Pullup/down
		P1OUT &= ~BIT1;					// Pull down
		P1SEL |= BIT1;					// Enable primary peripheral module function

		// Clock System
		BCSCTL3 |= LFXT1S_2;			// ACLK source is VLO, 12 kHz

		// Timer A
		TACTL |= MC_2;					// Continues mode: the timer counts up to 0xFFFF
		TACTL |= TASSEL_1;				// Select ACLK as source

		// Capture/Compare Block 0
		TACCTL0 |= CM_1;				// Capture rising edge
		TACCTL0 |= CCIS_0;				// Capture input select: 0 - CCIxA
		TACCTL0 |= CAP;					// Capture mode
		TACCTL0 |= CCIE;				// Capture/compare interrupt enable.

		_BIS_SR(LPM3_bits + GIE);		// Go to LPM 3 and enable interrupts in general
	}
}

#pragma vector=TIMERA0_VECTOR
__interrupt void TimerA0(void)
{
	int CM0_REG = TACCTL0 & 0xC000;		//
	TACCTL0 &= ~CCIE;					// Capture/compare interrupt disable.

	if(CM0_REG == CM_1)					// If rising edge captured
	{
		RISE1 = TAR;
		PERIOD = RISE1 - RISE2;
		RISE2 = RISE1;

		TACCTL0 &= ~CM_1;
		TACCTL0 |= CM_2;				// Next time: capture falling edge
	}

	if(CM0_REG == CM_2)					// If falling edge captured
	{
		FALL1 = TAR;
		DUTYCY = FALL1 - RISE1;

		// Duty cycle in Prozent berechnen
		// Funktioniert nicht bei 0 % und 100 % da keine flanken mehr auftreten
		if(PERIOD != 0) DUTYCYPERCENT = ((100 * DUTYCY) / PERIOD);

		TACCTL0 &= ~CM_2;
		TACCTL0 |= CM_1;				// Next time: capture rising edge
	}

	TACCTL0 &= ~CCIFG;					// Clear interrupt flag
	TACCTL0 |= CCIE;					// Capture/compare interrupt enable.
}

void init_board(void)
{
	P1DIR = 0xFF;
	P2DIR = 0xFF;
	P1OUT = 0x00;
	P2OUT = 0x00;
}

Das Erzeugen einer PWM könnte in Assembler dann so aussehen (hier ist der duty cycle aber fest eingestellt):

	;
			.cdecls C,LIST,  "msp430x20x3.h"

			.text

RESET       mov.w   #0280h,SP               ; Initialize stackpointer
StopWDT     mov.w   #WDTPW+WDTHOLD,&WDTCTL  ; Stop WDT

mainf		; Basic device setup for power consumption reduction
			mov.b	#0FFh,&P1DIR			; All Port 1 Pins are outputs
			mov.b	#0FFh,&P2DIR			; All Port 2 Pins are outputs
			mov.b	#00h,&P1OUT				; All Port 1 outputs are low
			mov.b	#00h,&P2OUT				; All Port 2 outputs are low

			; GPIO Port 1, P1.2 is PWM Output
			bis.b	#04h,&P1DIR				; P1.2 is an output
			bis.b	#04h,&P1SEL				; P1.2 is connected to timer A

			; Clock system setup
			bis.w	#LFXT1S_2,&BCSCTL3		; ACLK source is VLO

			; Timer A setup
			bis.w	#OUTMOD_7,&TACCTL1		; TACCR1 reset/set
			bis.w	#TASSEL_1,&TACTL		; Select ACLK as source
			bis.w	#MC_1,&TACTL			; Up mode: the timer counts up to TACCR0
			mov.w	#009Fh,&TACCR0			; PWM Period - up to int 159
			mov.w	#002Fh,&TACCR1			; TACCR1 PWM Duty Cycle

stophere	nop
			bis.w   #LPM3+GIE,SR
			nop
			nop
			jmp		stophere


	; Interrupt Vectors
            .sect   ".reset"                ; MSP430 reset vector
            .short  RESET
            .end

State Machine in C: Vorlage

November 25, 2011 Von: corni Kategorie: Programmieren: C Noch keine Kommentare →

Kleine Vorlage wie man eine State Machine in C realisieren kann.

Datei bla.c:

#include "typedef.h"
#include "state_fkt.h"
#include "change_state_fkt.h"

volatile STATES current_state = STATE_1;
volatile STATES next_state = STATE_1;

int main(void)
{
    while (1)
    {
        // If state changed
        if (next_state != current_state)
        {
            char Interrupts = Interrupt_Register;
            Interrupt_Register = off;               // IRQ off
            exit_current_state(current_state);      // Clean up old state
            enter_next_state(next_state);           // Setup new state
            current_state = next_state;             // Switch to next state
            Interrupt_Register = Interrupts;        // IRQ old state
        }

        // Execute state
        switch (current_state)
        {
        case STATE_1:
            fkt_state1();
            break;

        case STATE_2:
            fkt_state2();
            break;

        case STATE_...:
            fkt_state...();
            break;

        default:
            break;
        }
    }
}

Datei typedef.h:

#ifndef typedef_H
#define typedef_H

typedef enum
{
    STATE_1,
    STATE_2,
    STATE_...
} STATES;

#endif

Datei state_fkt.h:

void fkt_state1(void);
void fkt_state2(void);
void fkt_state...(void);

Datei state_fkt.c:

#include "typedef.h"

extern volatile STATES current_state;
extern volatile STATES next_state;

void fkt_state1(void)
{
    // Do magic...

    next_state = STATE_2;
}

void fkt_state2(void)
{
    // Do magic...

    next_state = STATE_...;
}

void fkt_state...(void)
{
    // Do magic...

    next_state = STATE_...;
}

Datei change_state_fkt.h:

void enter_next_state(STATES next_state);
void exit_current_state(STATES current_state);

Datei change_state_fkt.c:

#include "typedef.h"

void enter_next_state(STATES next_state)
{
    switch (next_state)
    {

    case STATE_1:
        // Do magic...
        break;

    case STATE_2:
        // Do magic...
        break;

    case STATE_...:
        // Do magic...
        break;

    default:
        break;
    }
}

void exit_current_state(STATES current_state)
{
    switch (current_state)
    {
    case STATE_1:
        // Do magic...
        break;

    case STATE_2:
        // Do magic...
        break;

    case STATE_...:
        // Do magic...
        break;

    default:
        break;
    }
}