MSP430 PWM (Pulsweitenmodulation)

msp430_pwm00In diesem kleinen Beispiel-Programm wird eine LCD-Hintergrundbeleuchtung per PWM (Pulsweitenmodulation) gesteuert. Das PWM Signal wird in Abhängigkeit von einem angeschlossenen Potentiometer am MSP430 erzeugt. Zum Einsatz kommt ein DOGM162-Display + das zugehörige Beleuchtungsmodul. Das LCD zeigt die aktuelle LED-Helligkeit in Prozent an (entspricht der Pulsbreite bzw. Duty-Cycle).

Video

PWM-Erzeugung

Es gibt verschiedene Möglichkeiten eine Pulsweitenmodulation zu erzeugen. Da der MSP430 (in diesem Beispiel kommt ein MSP4302452 zum Einsatz) einen Timer besitzt, kann direkt per Hardware eine PWM erzeugt werden. Der Timer besitzt zwei “Capture/Compare Register”, TACCR1 und TACCR0. In diesen beiden Register stehen die Werte, bei denen etwas passieren soll, wenn das TA-Register (TimerA Counter) den Wert im TACCR1 oder TACCR0 erreicht (Interrupt auslösen, Port togglen, Port setzen, Port zurücksetzen). Danach wird TAR wieder auf 0 gesetzt oder zählt wieder von einem bestimmten Wert hoch bzw. runter (je nach Modus).

In diesem Beispiel steht die Periodendauer in TACCR0 und die Pulsbreite (Duty-Cylce) in TACCR1. Der Timer läuft im Up-Mode, zählt also bis zu den zwei Werten in TACCR0 und TACCR1 hoch, wird danach wieder auf 0 gesetzt und zählt wieder hoch usw. Wird der erste Wert in TACCR1 (Pulsbreite) erreicht, wird der Port zurückgesetzt. Danach wird nach einer gewissen Zeit der Wert in TACCR0 (Periodendauer) erreicht und der Port wird wieder gesetzt. Der zeitliche Verlauf:

timer_pwmAbbildung 1 – PWM mit Timer in Up-Mode

Kleine Werte in TACCR1 erzeugen eine kurze Pulsdauer, große Werte eine längere Pulsdauer.

Über das angeschlossene Potentiometer (am 10bit-ADC), werden Integer-Werte zwischen 0-1023 eingelesen. Über eine Geradengleichung wird die Pulsbreite (Zyklen für den Timer) berechnet.

pwm04_1 pwm104_2 pwm04_3

Schaltplan

Das Launchpad wird über einen externen LDO (3,3V) betrieben.

msp430_pwm

C-Programm

main.c

  1. /*******************************
  2.  *          __                   
  3.  *         /\ \                  
  4.  *  __  _  \_\ \     __    ___   
  5.  * /\ \/'\ /'_' \  /'__'\ /'___\
  6.  * \/>  <//\ \ \ \/\  __//\ \__/
  7.  *  /\_/\_\ \___,_\ \____\ \____\
  8.  *  \//\/_/\/__,_ /\/____/\/____/
  9.  *
  10.  *  Author: declis (xdec.de)
  11.  ********************************/
  12.  
  13. #include <msp430g2452.h>
  14. #include "lib_lcd.h"
  15. #include "delay.h"
  16.  
  17. #define cycle       326             // 100Hz, T=10ms
  18. #define delay_ms    5               // delay in ms
  19. #define adc_res     1023            // 10bit ADC
  20. const char bright[]={"Brightness:"};
  21.  
  22. void main(void)
  23. {
  24.     unsigned long duty_cycle=0;
  25.     unsigned int adc_old=0;
  26.  
  27.     WDTCTL=WDTPW+WDTHOLD;           // stop WDT
  28.     BCSCTL1=CALBC1_1MHZ;            // set DCO to 1MHz, set range
  29.     DCOCTL=CALDCO_1MHZ;             // set DCO step+modulation
  30.     init_USI();                     // initialize USI
  31.     init_LCD();                     // initialize LCD (DOGM162)
  32.  
  33.     P1DIR|=BIT2;                    // P1.2 -> output
  34.     P1SEL|=BIT2;                    // P1.2 -> compare: Out1 output (TA0.1)
  35.     TACCR0=cycle;                   // CCR0 -> cycle time (10ms)
  36.     TACCR1=0;                       // CCR1 -> duty cycle
  37.     TACCTL1=OUTMOD_7;               // reset/set
  38.     TACTL=TASSEL_1+MC_1;            // CLK-source=32kHz, up-mode
  39.  
  40.     BCSCTL3|=XCAP_3;                // CL=~12.5pF
  41.     do
  42.     {
  43.         IFG1&=~OFIFG;
  44.         wait_ms(1);
  45.     }
  46.     while(IFG1&OFIFG);
  47.  
  48.     //Channel 0 (Poti); ADC10 CLK divider = /4; CLK=ADC10OSC
  49.     ADC10CTL1=INCH_0+ADC10DIV_3+ADC10SSEL_0;
  50.     //Vref+=Vcc (ext. LDO 3.3V) and Vss=Vr-; S&H time 16x; ADC10 on
  51.     ADC10CTL0=SREF_0+ADC10SHT_3+ADC10ON;
  52.     // P1.0 is analog input
  53.     ADC10AE0|=BIT0;
  54.  
  55.     write_string(0,0,bright);
  56.     write_char(14,0,'%');
  57.     while(1)
  58.     {
  59.         wait_ms(delay_ms);
  60.         ADC10CTL0|=ENC+ADC10SC;                 // start & enable conversion
  61.         while(ADC10CTL0&ADC10BUSY);             // ADC busy?
  62.  
  63.         // if we don't have a new ADC value, do nothing and wait
  64.         if(adc_old!=ADC10MEM)
  65.         {
  66.             // y=mx+b
  67.             duty_cycle=((cycle*(unsigned long)ADC10MEM)/adc_res);
  68.             TACCR1=duty_cycle;
  69.             adc_old=ADC10MEM;
  70.         }
  71.         // duty cycle in percent -> duty-cycle=(cycle/100)*percent
  72.         char_to_num_out(12,0,(duty_cycle*100)/cycle);
  73.     }
  74. }
/*******************************
 *          __                   
 *         /\ \                  
 *  __  _  \_\ \     __    ___   
 * /\ \/'\ /'_' \  /'__'\ /'___\ 
 * \/>  <//\ \ \ \/\  __//\ \__/ 
 *  /\_/\_\ \___,_\ \____\ \____\
 *  \//\/_/\/__,_ /\/____/\/____/
 * 
 *  Author: declis (xdec.de)
 ********************************/ 

#include <msp430g2452.h>
#include "lib_lcd.h"
#include "delay.h"

#define cycle		326				// 100Hz, T=10ms
#define delay_ms	5				// delay in ms
#define adc_res		1023			// 10bit ADC
const char bright[]={"Brightness:"};

void main(void)
{
	unsigned long duty_cycle=0;
	unsigned int adc_old=0;

	WDTCTL=WDTPW+WDTHOLD;			// stop WDT
	BCSCTL1=CALBC1_1MHZ;        	// set DCO to 1MHz, set range
  	DCOCTL=CALDCO_1MHZ;				// set DCO step+modulation
	init_USI();						// initialize USI
	init_LCD();						// initialize LCD (DOGM162)

	P1DIR|=BIT2;					// P1.2 -> output
	P1SEL|=BIT2;					// P1.2 -> compare: Out1 output (TA0.1)
	TACCR0=cycle;					// CCR0 -> cycle time (10ms)
	TACCR1=0;						// CCR1 -> duty cycle
	TACCTL1=OUTMOD_7;				// reset/set
	TACTL=TASSEL_1+MC_1;			// CLK-source=32kHz, up-mode

	BCSCTL3|=XCAP_3;				// CL=~12.5pF
	do
	{
		IFG1&=~OFIFG;
		wait_ms(1);
	}
	while(IFG1&OFIFG);

	//Channel 0 (Poti); ADC10 CLK divider = /4; CLK=ADC10OSC
  	ADC10CTL1=INCH_0+ADC10DIV_3+ADC10SSEL_0;
  	//Vref+=Vcc (ext. LDO 3.3V) and Vss=Vr-; S&H time 16x; ADC10 on
  	ADC10CTL0=SREF_0+ADC10SHT_3+ADC10ON;
  	// P1.0 is analog input
  	ADC10AE0|=BIT0;

	write_string(0,0,bright);
	write_char(14,0,'%');
	while(1)
	{
		wait_ms(delay_ms);
		ADC10CTL0|=ENC+ADC10SC;					// start & enable conversion
		while(ADC10CTL0&ADC10BUSY);				// ADC busy?

		// if we don't have a new ADC value, do nothing and wait
		if(adc_old!=ADC10MEM)
		{
			// y=mx+b
			duty_cycle=((cycle*(unsigned long)ADC10MEM)/adc_res);
			TACCR1=duty_cycle;
			adc_old=ADC10MEM;
		}
		// duty cycle in percent -> duty-cycle=(cycle/100)*percent
		char_to_num_out(12,0,(duty_cycle*100)/cycle);
	}
}

 

DIe restlichen Files gibt es hier: DOWNLOAD SOURCE

Leave a Reply

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

Time limit is exhausted. Please reload CAPTCHA.