In diesem Beispiel-Programm (C und Assembler) wird der Timer initialisiert und es wird mit Interrupts gearbeitet. Wie im vorherigen Programm (Blinklicht ohne Timer) wird wieder eine LED zum blinken gebracht.
ASM-Programm
In dem Programm gehe ich von einer Frequenz SMCLK=1MHz (was auch einigermaßen genau passt) aus um die Zyklen zu berechnen. In dem ASM-Programm wird die LED jede Sekunde für eine Sekunde zum leuchten gebracht. Da die maximale Grenze der Zyklen (2^16)-1 enspricht, also etwa knapp über 500ms (500ms=62500 Zyklen), kommt hier noch ein Faktor ins Spiel um auf die eine Sekunde zu kommen (2*500ms, siehe Programm). Der Faktor wird in der Timer-ISR immer dekrementiert und geprüft.
FILE: main.asm
;*********************************************** ; ___ _ _ ; | |_ _ _| |___ ___ _| |___ ; | | |_'_| . | -_| _|_| . | -_| ; |___|_,_|___|___|___|_|___|___| ; ; FILE: main.asm ; Author: declis (xdec.de) ;*********************************************** .cdecls "msp430g2231.h" .ref init_TimerA,ISR_TimerA .global main .text main: mov.w #0x280,SP ;initialize stack pointer mov.w #WDTPW+WDTHOLD,&WDTCTL ;stop watchdog timer bis.b #BIT6,&P1DIR ;P1.6 -> output bic.b #BIT6,&P1OUT ;clear P1.6 (LED2 off) mov.w #2,R4 ;factor for TimerA-ISR (2*500ms) call #init_TimerA ;initialize TimerA EINT ;GIE=1, or bis.w #GIE,SR -> enable interrupt jmp $ .sect TIMERA0_VECTOR ;0xFFF2 Timer A CC0 (".int09") .word ISR_TimerA ;interrupt service routine for TimerA .end
FILE: timer.asm
;*********************************************** ; ___ _ _ ; | |_ _ _| |___ ___ _| |___ ; | | |_'_| . | -_| _|_| . | -_| ; |___|_,_|___|___|___|_|___|___| ; ; FILE: timer.asm ; Author: declis (xdec.de) ;*********************************************** .cdecls "msp430g2231.h" cycles .equ 62500 .def init_TimerA,ISR_TimerA .text ;------------------------------------------ ; Name: init_TimerA ; Description: initialize Timer A ; cycles=(seconds*10^6Hz)/8 ; seconds=(8*cycles)/10^6Hz ; 62500 cycles = 500ms ; SMCLK=~1MHz ; Input: nothing ; Returns: nothing ; Destroys: nothing ;------------------------------------------ init_TimerA: bis.w #TASSEL_2+ID_3,&TACTL ;SMCLK, 8x divider mov.w #cycles,&TACCR0 bis.w #CCIE,&TACCTL0 ;enable TimerA interrupt bis.w #MC_1,&TACTL ;start timer in up-mode ret ;------------------------------------------ ; Name: ISR_TimerA ; Description: Interrupt Service Routine TA ; toggle LED2 every second ; Input: nothing ; Returns: nothing ; Destroys: R4 ;------------------------------------------ ISR_TimerA: dec.w R4 ;decrement factor/counter jnz isrTAend ;R4!=0 xor.b #BIT6,&P1OUT ;toggle P1.6 (LED2) mov.w #2,R4 ;reset factor/counter isrTAend: reti .end
C-Programm
FILE: main.c
/*************************************************
* ___ _ _
* | |_ _ _| |___ ___ _| |___
* | | |_'_| . | -_| _|_| . | -_|
* |___|_,_|___|___|___|_|___|___|
*
* FILE: main.c
* Author: declis (xdec.de)
************************************************/
#include <msp430g2231.h>
#include "timer.h"
void main(void)
{
WDTCTL=WDTPW+WDTHOLD; /* stop watchdog timer */
BCSCTL1=CALBC1_1MHZ; /* set DCO to 1MHz, set range */
DCOCTL=CALDCO_1MHZ; /* set DCO step+modulation */
P1DIR|=BIT6; /* P1.6 -> output */
P1OUT&=~BIT6; /* clear P1.6 (LED2 off) */
init_TimerA(); /* initialize TimerA */
_EINT(); /* enable interrupt */
for(;;);
}
FILE: timer.c
/*************************************************
* ___ _ _
* | |_ _ _| |___ ___ _| |___
* | | |_'_| . | -_| _|_| . | -_|
* |___|_,_|___|___|___|_|___|___|
*
* FILE: timer.c
* Author: declis (xdec.de)
************************************************/
#include <msp430g2231.h>
#include "timer.h"
#define cycles 62500u /* 500ms */
volatile char count; /* counter for 2*500ms=1s */
/* cycles=(seconds*10^6Hz)/8 */
/* seconds=(8*cycles)/10^6Hz */
void init_TimerA(void)
{
TACTL|=TASSEL_2+ID_3; /* SMCLK, 8x divider */
TACCR0=cycles;
count=2; /* set counter */
TACCTL0|=CCIE; /* enable TimerA interrupt */
TACTL|=MC_1; /* start timer in up-mode */
}
/* Timer_A ISR */
#pragma INTERRUPT (ISR_TimerA)
#pragma vector=TIMERA0_VECTOR
void ISR_TimerA(void)
{
count--;
if(!count)
{
P1OUT^=BIT6; /* toggle P1.6 */
count=2; /* reset counter */
}
}
FILE: timer.h
/*************************************************
* ___ _ _
* | |_ _ _| |___ ___ _| |___
* | | |_'_| . | -_| _|_| . | -_|
* |___|_,_|___|___|___|_|___|___|
*
* FILE: timer.h
* Author: declis (xdec.de)
************************************************/
#ifndef TIMER_H_
#define TIMER_H_
void init_TimerA(void);
#endif /*TIMER_H_*/
Update (11.01.13): Das Programm wurde um zwei Zeilen erweitert um einmal den Unterschied zwischen “DCO calibrated (@1MHz) und “DCO uncalibrated” zu sehen. Wie man aus dem User-Guide auf Seite 279 entnehmen kann (und auch im Header-FIle), gibt es kalibrierte Einstellungen für bestimmte Frequenzen (bei dem G2231 1MHz, bei z.B. G2452 1MHz, 8MHz und 16MHz). Diese Einstellung wird folgendermaßen vorgenommen:
BCSCTL1=CALBC1_1MHZ; /* set DCO to 1MHz, set range */
DCOCTL=CALDCO_1MHZ; /* set DCO step+modulation */
Möchte man auf 8,12 oder 16MHz takten, so ersetzt man “…1MHz” logischerweise durch 8 oder 16. Man kann mit einem einfachen Messgerät einen deutlichen unterschied sehen:
P1.6, LED (calibrated) | P1.6, LED (default) |
P1.4, SMCLK (calibrated) | P1.4, SMCLK (default) |