In 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:
Abbildung 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.
Schaltplan
Das Launchpad wird über einen externen LDO (3,3V) betrieben.
C-Programm
main.c
/*******************************
* __
* /\ \
* __ _ \_\ \ __ ___
* /\ \/'\ /'_' \ /'__'\ /'___\
* \/> <//\ \ \ \/\ __//\ \__/
* /\_/\_\ \___,_\ \____\ \____\
* \//\/_/\/__,_ /\/____/\/____/
*
* 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