corni's Link- und Fundstücktonne

blog.cpoth.de
Subscribe

Artikel der Kategorie ‘Programmieren: ASM MSP430’

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