B
BYYUU CCSS//EECCEEnn 112244 MSMSPP443300 EExxaammpplleess 11
Topics to Co
Topics to Co
v
v
er«
er«
F2013 Blinky Example
F2013 Blinky Example
Lab 4: Blinky Lab
Lab 4: Blinky Lab
Example 2:
Example 2: Interrupts w/Timer_
Interrupts w/Timer_A
A
Example 3: S/W PWM w/Timer_A
Example 3: S/W
PWM w/Timer_A
Example 4: W
Example 4: Watchdog Clock
atchdog Clock
Example 5: Watchdog PWM
Example 5: Watchdog PWM
Example 6: SW Switch Debounce
Example 6: SW Switch Debounce
Example 7: Timer_B S/W PWM
Example 7: Timer_B S/W PWM
B
BYYUU CCSS//EECCEEnn 112244 MSMSPP443300 EExxaammpplleess 22
F2013
F2013
Blinky Example
Blinky Example
;******************************************************************************* ;******************************************************************************* ;
; CS/ECEn CS/ECEn 124 124 Lab Lab 1 1 -- blinky.ablinky.asm: sm: Software Software Toggle Toggle P1.0P1.0
;******************************************************************************* ;*******************************************************************************
.cdecls
.cdecls C,LIST, C,LIST, "msp430x20x3.h" "msp430x20x3.h" ; ; MSP430F2013MSP430F2013
;---.text
.text ; beginning ; beginning of of executable executable codecode ;---RESET:
RESET: mov.w mov.w #0x0280,SP #0x0280,SP ; ; init init stack stack pointerpointer mov.w
mov.w #WDTPW+WDTHOLD,&#WDTPW+WDTHOLD,&WDTCTL WDTCTL ; ; stop stop WDTWDT bis.b
bis.b #0x01,&P1DIR #0x01,&P1DIR ; ; set set P1.0 P1.0 as as outputoutput mainloop:
mainloop: xor.b xor.b #0x01,&P1OUT #0x01,&P1OUT ; ; toggle toggle P1.0P1.0 mov.w
mov.w #0,r15 #0,r15 ; ; use use R15 R15 as as delay delay countercounter delayloop:
delayloop: dec.w dec.w r15 r15 ; ; delay delay over?over? jnz
jnz delayloop delayloop ; ; nn jmp
jmp mainloop mainloop ; ; y, y, toggle toggle led led
;---;
; Interrupt Interrupt VectorsVectors
;---.sect
.sect ".reset" ".reset" ; ; MSP430 MSP430 RESET RESET VectorVector .short
.short RESET RESET ; ; start start addressaddress .end
.end
S
Add
Add
2
2
n
n
d
d
Delay Loop
Delay Loop
;******************************************************************************* ;******************************************************************************* ; CS/ECE
; CS/ECEn 124 Ln 124 Lab 1 -ab 1 - blinky.ablinky.asm: Sofsm: Software Ttware Toggle P1.0oggle P1.0
;******************************************************************************* ;*******************************************************************************
.cdecls
.cdecls C,LIST, C,LIST, "msp430x20x3.h" "msp430x20x3.h" ; ; MSP430F2013MSP430F2013 delay
delay .set .set 00
;---.text
.text ; beginning ; beginning of of executable executable codecode ;---RESET:
RESET: mov.w mov.w #0x0280,SP #0x0280,SP ; ; init init stack stack pointerpointer mov.w
mov.w #WDTPW+WDTHOLD,&#WDTPW+WDTHOLD,&WDTCTL WDTCTL ; ; stop stop WDTWDT bis.b
bis.b #0x01,&P1DIR #0x01,&P1DIR ; ; set set P1.0 P1.0 as as outputoutput mainloop:
mainloop: xor.b xor.b #0x01,&P1OUT #0x01,&P1OUT ; ; toggle toggle P1.0P1.0 mov.w #delay,r15
mov.w #delay,r15 ; ; use use R15 R15 as as delay delay countercounter delayloop:
delayloop: dec.w dec.w r15 r15 ; ; delay delay over?over? jnz
jnz delayloop delayloop ; ; nn delay2:
delay2: dec.w dec.w r15r15 jnz delay2 jnz delay2 jmp
jmp mainloop mainloop ; ; y, y, toggle toggle led led
;---;
; Interrupt Interrupt VectorsVectors
;---.sect
.sect ".reset" ".reset" ; ; MSP430 MSP430 RESET RESET VectorVector .short
.short RESET RESET ; ; start start addressaddress .end
.end
S
BYU CS/ECEn 124 MSP430 Examples 4
LEDs
6 LED¶s on eZ430X Development Board
P1.0 Red LED eZ430-RF2500
P1.1 Green LED eZ430-RF2500
P2.6 LED #1 (Green)
P2.7 LED #2 (Orange)
P3.3 LED #3 (Yellow)
P4.6 LED #4 (Red)
Port bits must be enabled for output by writing a 1 to the
port direction register
bis.b #0x03,&P1DIR
; eZ430-RF2500 LED's
bic.b #0xc0,&P2SEL
; select GPIO
bis.b #0x40,&P2DIR
; LED #1 (P2.6)
bis.b #0x80,&P2DIR
; LED #2 (P2.7)
bis.b #0x08,&P3DIR
; LED #3 (P3.3)
bis.b #0x40,&P4DIR
; LED #4 (P4.6)
LEDs
Turn LED off by writing a 0 to the port pin
bic.b #0x03,&P1OUT
; eZ430-RF2500 LED's
bic.b #0x40,&P2OUT
; LED #1 (P2.6)
bic.b #0x80,&P2OUT
; LED #2 (P2.7)
bic.b #0x08,&P3OUT
; LED #3 (P3.3)
bic.b #0x40,&P4OUT
; LED #4 (P4.6)
Turn LED on by writing a 1 to the port pin
bis.b #0x03,&P1OUT
; eZ430-RF2500 LED's
bis.b #0x40,&P2OUT
; LED #1 (P2.6)
bis.b #0x80,&P2OUT
; LED #2 (P2.7)
bis.b #0x08,&P3OUT
; LED #3 (P3.3)
bis.b #0x40,&P4OUT
; LED #4 (P4.6)
Toggle LED by XOR¶ing a 1 to the port pin
xor.b #0x03,&P1OUT
; eZ430-RF2500 LED's
xor.b #0x40,&P2OUT
; LED #1 (P2.6)
xor.b #0x80,&P2OUT
; LED #2 (P2.7)
xor.b #0x08,&P3OUT
; LED #3 (P3.3)
xor.b #0x40,&P4OUT
; LED #4 (P4.6)
BYU CS/ECEn 124 MSP430 Examples 6
Lab 4: Blinky Lab
Lab 4: Blinky
;******************************************************************************* ; CS/ECEn 124 Lab 4 - blinky.asm
;******************************************************************************* ; cycles =
---; MCLK = --- cycles / 10 seconds = --- Mhz ; CPI = MCLK /
---; MIPS = MCLK / CPI / 1000000 = --- MIPS
.cdecls C,LIST, "msp430x22x4.h" ; MSP430F2274 COUNT .equ 0 ; delay count
;---.text ; beginning of executable code ;---RESET: mov.w #0x0280,SP ; 2 init stack pointer
mov.w #WDTPW+WDTHOLD,&WDTCTL ;5 stop WDT
bis.b #0x01,&P1DIR 4 set P1.0 as output; mainloop: xor.b #0x01,&P1OUT 4 ;toggle P1.0
mov.w #COUNT,r15 1 use R15 as delay counter; delayloop: dec.w r15 ; 1 delay over?
jnz delayloop ; 2 n
jmp mainloop ; 2 y, toggle led
;---; Interrupt Vectors
;---.sect ".reset" ; MSP430 RESET Vector
.short RESET ; start address .end
.cdecls C,LIST, "msp430x20x3.h" ; MSP430F2013 DELAY .equ (50/8)
.text ; beginning of code
RESET: mov.w #0x0280,SP ; init stack pointer mov.w #WDTPW+WDTHOLD,&WDTCTL ; stop WDT
bis.b #0x01,&P1DIR ; set P1.0 as output mainloop: xor.b #0x01,&P1OUT ; toggle P1.0
push.w #DELAY ; pass delay count on stack
call #delay ; call delay subroutine
jmp mainloop
; delay subroutine: stack usage 4| DELAY | \
; 2| ret | subroutine frame (6 bytes)
; (SP) => 0| r15 | /
delay: push.w r15 ; callee-save
mov.w #0,r15 ; use R15 as inner counter
delay02: dec.w r15 ; inner delay over?
jne delay02 ; n
dec.w 4(SP) ; y, outer done?
jne delay02 ; n
pop.w r15 ; y, restore register(s)
mov.w @SP+,0(SP) ; pop input delay count
ret ; return from subroutine
.sect ".reset" ; MSP430 RESET Vector
.word RESET ; start address
.end
Example
1
a: Activation Recor d
4(SP)=delay count 2(SP)= return address 0(SP)= r 15
BYU CS/ECEn 124 MSP430 Examples 8
.cdecls C,LIST, "msp430x20x3.h" ; MSP430F2013 DELAY .equ (50/8)
.text ; beginning of code RESET: mov.w #0x0280,SP ; init stack pointer
mov.w #WDTPW+WDTHOLD,&WDTCTL ; stop WDT
bis.b #0x01,&P1DIR ; set P1.0 as output mainloop: xor.b #0x01,&P1OUT ; toggle P1.0
push.w #DELAY ; pass delay count on stack call #delay ; call delay subroutine jmp mainloop
; delay subroutine: stack usage 6| DELAY | \
; 4| ret | subroutine frame (8 bytes) ; 2| r12 | /(activation record)
; (SP) => 0| r15 |/
delay: push.w r12 ; callee-save push.w r15
mov.w 6(SP),r12 ; get delay count
mov.w #0,r15 ; use R15 as inner counter delay02: dec.w r15 ; delay over?
jne delay02 ; n
dec.w r12 ; y, done? jne delay02 ; n
pop.w r15 ; y, restore registers pop.w r12
mov.w @SP+,0(SP) ; pop input delay count ret ; return from subroutine .sect ".reset" ; MSP430 RESET Vector .word RESET ; start address
.end
Example
1
b: Activation Recor d
6(SP)=delay count 4(SP)= return address 2(SP)= r 12
0(SP)= r 15
.cdecls C,LIST, "msp430x20x3.h" ; MSP430F2013 DELAY .equ (50/8)
.text ; beginning of code
RESET: mov.w #0x0280,SP ; init stack pointer mov.w #WDTPW+WDTHOLD,&WDTCTL ; stop WDT
bis.b #0x01,&P1DIR ; set P1.0 as output mainloop: xor.b #0x01,&P1OUT ; toggle P1.0
push.w #DELAY ; pass delay count on stack
call #delay ; call delay subroutine
jmp mainloop
; delay subroutine: stack usage 4| DELAY | \
; 2| ret | subroutine frame (6 bytes)
; (SP) => 0| counter | /
delay: sub.w #2,SP ; activate local variable
mov.w #0,0(SP) ; use as inner counter
delay02: dec.w 0(SP) ; delay over?
jne delay02 ; n
dec.w 4(SP) ; y, done?
jne delay02 ; n
add.w #2,SP ; y, deactivate local variable mov.w @SP+,0(SP) ; pop input delay count
ret ; return from subroutine
.sect ".reset" ; MSP430 RESET Vector
.word RESET ; start address
.end
Example
1
c: Activation Recor d
4(SP)=delay count 2(SP)= return address 0(SP)= local variable
BYU CS/ECEn 124 MSP430 Examples 10
Example
2
: Interrupts w/Timer_A
cdecls C,LIST, "msp430x20x3.h" ; MSP430F2013
TA_CTL .set TASSEL_2+ID_3+MC_1+TAIE ; 000000 10 11 01 000 1 = SMCLK,/8,UP,IE TA_FREQ .set 0xffff ; clocks
;---.text ; beginning of executable code
RESET: mov.w #0x0280,SP ; init stack pointer mov.w #WDTPW+WDTHOLD,&WDTCTL ; stop WDT
bis.b #0x01,&P1DIR ; set P1.0 as output clr.w &TAR ; reset timerA
mov.w #TA_CTL,&TACTL ; set timerA control reg mov.w #TA_FREQ,&TACCR0 ; set interval (frequency) bis.w #LPM0+GIE,SR ; enter LPM0 w/interrupts
jmp $
TA_isr: ; timer A ISR
bic.w #TAIFG,&TACTL ; acknowledge interrupt xor.b #0x01,&P1OUT ; toggle P1.0
reti
;---; Interrupt Vectors
.sect ".int08" ; timer A section .word TA_isr ; timer A isr
.sect ".reset" ; MSP430 RESET Vector .word RESET ; start address
.end
Blinky Example
TASSEL_2=SMCLK ID_3= /8 MC_1= UP Mode Enable Interrupt Acknowledge Interrupt (Put its hand down)Put the processor to sleep!
Example
3
: S /W PWM w/Timer_A
.cdecls C,LIST,"msp430x20x3.h"
SMCLK .set 1200000 ; 1200000 clocks / second TA_CTL .set TASSEL_2+ID_0+MC_1+TAIE ; SMCLK, /1, UP, IE
TA_FREQ .set 120 ; FREQ / SMCLK = 0.0001 = 100 us .bss cnt,2 ; counter variable
.text
RESET: mov.w #0x0280,SP ; init stack pointer mov.w #WDTPW+WDTHOLD,&WDTCTL ; stop watchdog
bis.b #0x01,&P1DIR ; set P1.0 as output clr.w &TAR ; reset timerA
mov.w #TA_CTL,&TACTL ; set timerA control reg mov.w #TA_FREQ,&TACCR0 ; set interval (frequency) bis.w #LPM0+GIE,SR ; enter LPM0 w/interrupts
jmp $ ; will never get here! TA_isr: bic.w #TAIFG,&TACTL ; acknowledge interrupt
inc.w &cnt
cmp.w #10,&cnt ; time to pulse? jlo TA_isr2 ; n
clr.w &cnt ; y, reset counter bis.b #0x01,&P1OUT ; turn on
jmp TA_isr4
TA_isr2: bic.b #0x01,&P1OUT ; turn off
TA_isr4: reti ; return from interrupt .sect ".int08" ; timer A section
.word TA_isr ; timer A isr
.sect ".reset" ; MSP430 RESET Vector .short RESET ; start address
.end
LED Intensity
Acknowledge needed 10%Duty CycleBYU CS/ECEn 124 MSP430 Examples 12
Example 4: Watchdog Clock
.cdecls C,LIST,"msp430x20x3.h"
SMCLK .set 1200000 ; 1.2 Mhz clock
WDT_CTL .set WDT_MDLY_8 ; WDT SMCLK, 8 ms (@1 Mhz) WDT_CPS .set SMCLK/8000 ; WD clocks / second count
; Data Section ---.bss WDTSecCnt,2 ; WDT second counter
; Code Section ---.text
RESET: mov.w #0x280,SP ; initialize stack pointer mov.w #WDT_CTL,&WDTCTL ; set WD timer interval
mov.w #WDT_CPS,&WDTSecCnt ; initialize 1 sec WD counter bis.b #WDTIE,&IE1 ; enable WDT interrupt
bis.b #0x01,&P1DIR ; P1.0 output
bis.w #GIE,SR ; enable interrupts loop: ;<< program >>
jmp loop ; loop indefinitely
; Watchdog ISR --- WDT_ISR: dec.w &WDTSecCnt ; decrement counter, 0?
jne WDT_02 ; n
mov.w #WDT_CPS,&WDTSecCnt ; y, re-initialize counter xor.b #0x01,&P1OUT ; toggle P1.0
WDT_02: reti ; return from interrupt ; Interrupt Vectors
---.sect ".int10" ; Watchdog Vector .word WDT_ISR ; Watchdog ISR .sect ".reset" ; PUC Vector .word RESET ; RESET ISR .end
Watchdog
8 ms (@1MHz SMCLK) Enable WD Interrupts No acknowledge neededExample
5
: Watchdog PWM
.cdecls C,LIST,"msp430x22x4.h" ; include c header
WDT_CLK .set 500 ; 500 Mhz WD clock (@1 Mhz) STACK .set 0x0600 ; stack
.bss WDTSecCnt,2 ; WDT second counter .bss buzzON,1 ; buzzer on flag .text ; program section
RESET: mov.w #STACK,SP ; initialize stack pointer
mov.w #WDT_MDLY_0_5,&WDTCTL ; set WD timer interval to 0.5 ms mov.w #WDT_CPS,&WDTSecCnt ; initialize 1 sec WD counter mov.b #WDTIE,&IE1 ; enable WDT interrupt bis.b #0x01,&P1DIR ; P1.0 output
bis.b #0x20,&P4DIR ; P4.5 output (transducer) clr.b &buzzON ; turn off buzzer
bis.w #LPM0+GIE,SR ; enable interrupts / sleep jmp $ ; (should never get here!) WDT_ISR: cmp.b #0,&buzzON ; buzzer on?
jeq WDT_02 ; n
xor.b #0x20,&P4OUT ; y, use 50% PWM
WDT_02: dec.w &WDTSecCnt ; decrement counter, 0? jne WDT_04 ; n
mov.w #WDT_CPS,&WDTSecCnt ; y, re-initialize counter xor.b #0x01,&P1OUT ; toggle P1.0
xor.b #0xff,&buzzON ; toggle buzzer on/off
WDT_04: reti ; return from interrupt .sect ".int10" ; Watchdog Vector
.word WDT_ISR ; Watchdog ISR .sect ".reset" ; PUC Vector .word RESET ; RESET ISR
Speaker (Transducer )
PWM when buzzON is non-zero
BYU CS/ECEn 124 MSP430 Examples 14
;********************************************************************** ; Switch debounce routine
;
DB_TIME .set 10 ; 10 ms debounce time
DB_CYCS .set 13 ; instruction cycles for delay DB_DELAY .set DB_TIME*myCLOCK/DB_CYCS/1000 ; delay count
debounce_switch:
push r15 ; callee save deb02: mov.b &P2IN,r12 ; read switches
xor.b #0x0f,r12 ; SW1-4
and.b #0x0f,r12 ; any switch low?
jne deb02 ; y, wait until all off deb04: mov.w #DB_DELAY,r15 ; minimum delay
deb06: mov.b &P2IN,r12 ; read switches xor.b #0x0f,r12 ; SW1-4
and.b #0x0f,r12 ; all switches high? jeq deb04 ; n, restart delay
dec.w r15 ; y, long enough delay? jne deb06 ; n, keep going
pop r15 ; y, switches debounced ret
Example 6: SW Switch Debounce
Reset timing counter if andof the switches read0
Switch Debounce
Return if R15 counts down to0 Wait until all switches offExample 7: Timer_B S /W PWM
Timer B interrupts used to modulate transducer at 440 Hz
.cdecls C,LIST,"msp430x22x4.h"
SMCLK .set 1200000 ; 1200000 clocks / second TIME_A3 .set 1000000/440/2 ; A = 440 Hz
TB_CTL .set TBSSEL_2+ID_0+MC_1+TBIE ; SMCLK, /1, UP, IE TB_FREQ .set SMCLK/TIME_A3 ; clocks / 440 Hz reset: mov.w #0x0600,SP ; initialize SP
mov.w #WDTPW+WDTHOLD,&WDTCTL ; stop WDT
bis.b #0x20,&P4DIR ; P4.5 transducer output clr.w &TBR ; reset timerB
mov.w #TB_CTL,&TBCTL ; set timerB control reg mov.w #TB_FREQ,&TBCCR0 ; set interval (frequency) bis.w #LPM0+GIE,SR ; enter LPM0 w/interrupts
jmp $ ; will never get here!
TB_isr: bic.w #TBIFG,&TBCTL ; acknowledge interrupt xor.b #0x20,&P4OUT ; toggle (50% duty cycle) reti
.sect ".int12" ; timer B section
.word TB_isr ; timer B isr
.sect ".reset" ; reset vector section
.word reset ; reset vector ISR
.end
Timer B Interrupt
Service Routine
BYU CS/ECEn 124 MSP430 Examples 16
Step
1
: Start Simple
Tone Scale Example
.cdecls C,LIST,"msp430x22x4.h"
STACK .set 0x0600 ; top-of-stack
.text ; code Section
;********************************************************************** ; Start of program...
reset: mov.w #STACK,SP ; initialize SP call #init_MSP430 ; init MSP430 call #init_variables ; init variables
;********************************************************************** ; Main event loop...
loop: call #update_LEDs ; update LED's call #goto_sleep ; go to sleep
call #adjust_volume ; awake! adjust volume call #debounce_switch ; debounce switches
jmp loop ; go back to sleep
init_MSP430: ; init MSP430
init_variables: ; init variables
goto_sleep: ; enter LPM0
adjust_volume: ; adjust volume
update_LEDs: ; show volume in LEDx
debounce_switch: ; debounce switches
ret
.sect ".reset" ; reset vector section
.word reset ; reset vector ISR
.end
Step
2
: Initialize MSP4
30
Tone Scale Example
WDT_CLK .set 8000 ; 8 khz clock (@1 Mhz)
WDT_CTL .set WDT_MDLY_8 ; WDT SMCLK, 8 ms
;********************************************************************** ; Initialize MSP430 routine
init_MSP430:
mov.w #WDT_CTL,&WDTCTL ; configure WDT bic.b #0x03,&P1SEL ; eZ430 LED's
bis.b #0x03,&P1DIR ; P1.0-1 as output bic.b #0x01,&P1OUT ; turn off red bis.b #0x02,&P1OUT ; turn on green bic.b #0xc0,&P2SEL ; eZ430X LED's bis.b #0xc0,&P2DIR ; LED #1 & #2 bis.b #0x08,&P3DIR ; LED #3 bis.b #0x40,&P4DIR ; LED #4
bic.b #0x0f,&P2SEL ; eZ430X push buttons bic.b #0x0f,&P2DIR ; P2.0-3 as inputs bis.b #0x0f,&P2OUT ; P2.0-3 pull-ups bis.b #0x0f,&P2IES ; h to l
bis.b #0x0f,&P2REN ; enable pull-ups
bis.b #0x20,&P4DIR ; P4.5 transducer output clr.w &TBR ; Timer B SMCLK, /1, up mode mov.w #TBSSEL_2+ID_0+MC_1,&TBCTL
BYU CS/ECEn 124 MSP430 Examples 18
Add WDT 1 second data variable
Add WDT ISR and interrupt vector
Tone Scale Example
;********************************************************************** ; Watchdog Timer ISR
WDT_ISR: ; WD timer ISR
dec.w &WDTSecCnt ; 1 second?
jne WD10 ; n
xor.b #0x03,&P1OUT ; y, toggle LEDs mov.w #WDT_CPS,&WDTSecCnt ; reset counter ; <<add scale sequence code here>>
WD10: reti ; return from interrupt
;********************************************************************** ; Interrupt Vector Table Entries
.sect ".int10" ; WDT vector
.word WDT_ISR ; address of WDT ISR
.bss WDTSecCnt,2 ; WDT second counter
Initialize 1 second data variable
Enable interrupts
Tone Scale Example
myCLOCK .set 1200000 ; 1.2 Mhz clock
WDT_CLK .set 8000 ; 8 khz clock (@1 Mhz)
WDT_CTL .set WDT_MDLY_8 ; WDT SMCLK, 8 ms
WDT_CPS .set myCLOCK/WDT_CLK ; WD clocks / second count
;********************************************************************** ; Initalize variables routine
init_variables: ; init variables
mov.w #WDT_CPS,&WDTSecCnt ; WDT counts/second bis.b #WDTIE,&IE1 ; enable WDT interrupt
ret
Step
3
b: Implement Watchdog
;********************************************************************** ; Goto sleep routine (Enable interrupts and LPM0)
goto_sleep: ; enter LPM0
bis.b #LPM0+GIE,SR ; enter LPM0 w/interrupts ret
BYU CS/ECEn 124 MSP430 Examples 20
Add some tone constants equates
Create melodic array of tone values
Tone Scale Example
; chromatic scale (12 just intonation intervals)
DO .set myCLOCK*1000/261626 ; C (261.626 Hz)
RE .set DO*8/9 ; D
MI .set DO*4/5 ; E
FA .set DO*3/4 ; F
SOL .set DO*2/3 ; G
LA .set DO*3/5 ; A
TI .set DO*8/15 ; B
DO1 .set DO*1/2 ; C
Step 4a: Create Tones
.text ; code Section
Create tone index variable
Initialize tone variable in init variable subroutine
Use WDT ISR to sequence thru the scale
Tone Scale Example
Step 4b: Create Tones
push r4 ; save r4
mov.w &tone,r4 ; get tone
add.w #1,r4 ; next tone
cmp.w #MAX_TONE,r4 ; restart scale?
jlo WD02 ; n
clr.w r4 ; y
WD02: mov.w r4,&tone ; save tone ; << later adjust tone & volume here >>
pop r4
.bss tone,2 ; tone number (0-8)
MAX_TONE .set 8 ; 0-8 tones
BYU CS/ECEn 124 MSP430 Examples 22
Add TimerB ISR and interrupt vector
And start TimerB with 1
st
tone in init variables subroutine
Tone Scale Example
;********************************************************************** ; Timer B ISR
TB_ISR: ; timer B ISR
bic.w #TBIFG,&TBCTL ; acknowledge interrupt xor.b #0x20,&P4OUT ; pulse buzzer
TB04: reti ; return from interrupt
.sect ".int12" ; timer B section
.word TB_ISR ; timer B ISR
Step
5
: Output Tone
mov.w #DO/2,&TBCCR0 ; start timer clock w/1st note
Add Port 2 ISR to clear LPM0 and enter AM
And change to LPM0 when enabling interrupts
Tone Scale Example
Step 6: Enter LPM
0
;********************************************************************** ; Port 2 (switches) ISR
P2_ISR: ; Port 2 (switches) ISR
bic.b #0x0f,&P2IE ; disable P2 interrupts bic.w #LPM0,0(SP) ; clear LPM0 from TOS
reti ; return from interrupt
.sect ".int03" ; P2 interrupt vector .word P2_ISR ; address of Port 2 ISR
;********************************************************************** ; Goto sleep routine (Enable interrupts and LPM0)
goto_sleep: ; enter LPM0
bic.b #GIE,SR ; disable all interrupts
clr.b &P2IFG ; acknowledge all
bis.b #0x0f,&P2IE ; enable switch interrupts bis.b #LPM0+GIE,SR ; enter LPM0 w/interrupts
BYU CS/ECEn 124 MSP430 Examples 24
Add toggle, high, and low duty cycle variables
Use Watchdog interrupt to change tone
Tone Scale Example
Step 7a: Play Scale
.bss duty_cycle_high,2 ; high part of cycle .bss duty_cycle_low,2 ; low part of cycle .bss duty_cycle_toggle,1 ; duty cycle flag
; << adjust timer b tone here >>
push r5 ; save r5
add.w r4,r4 ; (word index)
mov.w scale(r4),r4 ; r4 = tone (frequency) ; << adjust volume here >>
rra.w r4 ; 50% duty cycle
mov.w r4,r5
; update tone duty cycle
mov.w r4,&TBCCR0 ; start clock (if necessary) mov.w r4,&duty_cycle_high ; set high duty cycle
mov.w r5,&duty_cycle_low ; set low duty cycle
Initialize high and low duty cycle variables
And add high and low duty cycles to Timer_B ISR
Tone Scale Example
Step 7b: Play Scale
;********************************************************************** ; Timer B ISR
TB_ISR: ; timer B ISR
bic.w #TBIFG,&TBCTL ; acknowledge interrupt xor.b #0x20,&P4OUT ; pulse buzzer
xor.b #0xff,&duty_cycle_toggle ; high?
jeq TB02 ; n
mov.w &duty_cycle_high,&TBCCR0 ; y, set high duty cycle jmp TB04
TB02: mov.w &duty_cycle_low,&TBCCR0 ; set low duty cycle
TB04: reti ; return from interrupt
clr.b &duty_cycle_toggle ; init toggle variable mov.w #DO/2,&duty_cycle_high ; w/1st tone
BYU CS/ECEn 124 MSP430 Examples 26
Create volume index variable to data section
Initialize volume in init_variables
Create a PWM array of duty-cycles in text section
Tone Scale Example
Step 8a: Add Volume
MAX_VOLUME .set 4 ; 0-4 volume
mov.w #MAX_VOLUME,&volume ; init volume
; duty cycle table (shift right until duty=0)
duty .word 0x7fff, 0x0020, 0x0010, 0x0004, 0x0001
; off 3.125% 6.25% 12.5% 50%
Change Watchdog ISR to use duty array
Tone Scale Example
Step 8b: Add Volume
; << adjust volume here >>
; rra.w r4 ; 50% duty cycle
; mov.w r4,r5 push r6
mov.w r4,r5 ; save in r5
mov.w volume,r6 ; get volume
add.w r6,r6 ; (word index)
mov.w duty(r6),r6 ; r6 = duty cycle
WD04: rra.w r4 ; 1/2 duty cycle, off?
jne WD06 ; n clr.w r5 ; y WD06: rra.w r6 ; done? jne WD04 ; n sub.w r4,r5 ; y, r4+r5=frequency pop r6
BYU CS/ECEn 124 MSP430 Examples 28
Use P2IFG determine which switch was pressed and
adjust volume accordingly
Tone Scale Example
Step 9: Switches Change Volume
;********************************************************************** ; Adjust volume routine
adjust_volume: ; adjust volume
bit.b #0x01,&P2IFG ; switch #1?
jeq adj02 ; n
sub.w #1,&volume ; y, down volume, too low?
jge adj04 ; n
clr.w &volume ; y, clear jmp adj04
adj02: bit.b #0x02,&P2IFG ; switch #2?
jeq adj04 ; n
add.w #1,&volume ; y, turn up volume cmp.w #MAX_VOLUME,&volume ; too high?
jlo adj04 ; n
mov.w #MAX_VOLUME,&volume ; y, set to max adj04: ret
Display the current volume setting using 4 LEDs
Tone Scale Example
Step
10
: Volume in LEDs
;********************************************************************** ; Update LEDs routine
update_LEDs: ; show volume in LEDs
bic.b #0xc0,&P2OUT ; LED #1 & #2 bic.b #0x08,&P3OUT ; LED #3 bic.b #0x40,&P4OUT ; LED #4
cmp.w #0,&volume jeq upd02
bis.b #0x40,&P2OUT ; turn on LED #1 cmp.w #1,&volume
jeq upd02
bis.b #0x80,&P2OUT ; turn on LED #2 cmp.w #2,&volume
jeq upd02
bis.b #0x08,&P3OUT ; turn on LED #3 cmp.w #3,&volume
jeq upd02
bis.b #0x40,&P4OUT ; turn on LED #4 upd02: ret
BYU CS/ECEn 124 MSP430 Examples 30
Finally, debounce switches (1¶s (up) for 10 ms)
Tone Scale Example
Step
11
: Debounce Switches
;********************************************************************** ; Switch debounce routine
;
DB_TIME .set 10 ; 10 ms debounce time
DB_CYCS .set 12 ; instruction cycles for delay DB_DELAY .set DB_TIME*myCLOCK/DB_CYCS/1000 ; delay count
debounce_switch:
push r15 ; callee save deb02: mov.b &P2IN,r12 ; read switches
xor.b #0x0f,r12 ; SW1-4
and.b #0x0f,r12 ; any switch low?
jne deb02 ; y, wait until all off deb04: mov.w #DB_DELAY,r15 ; minimum delay
deb06: mov.b &P2IN,r12 ; read switches xor.b #0x0f,r12 ; SW1-4
and.b #0x0f,r12 ; all switches high? jeq deb04 ; n, restart delay
dec.w r15 ; y, long enough delay? jne deb06 ; n, keep going
pop r15 ; y, switches debounced ret
First, eliminate a lot of code!!
Step
12
a: Use Timer Output PWM
.bss duty_cycle_high,2 ; high part of cycle .bss duty_cycle_l ow,2 ; low part of cycle .bss duty_cycle_t oggle,1 ; duty cycle flag mov.w #DO/2,&TBCCR 0 ; start clock
bis.w #TBIE,&TBCTL ; enable timer B interrupts mov.w #DO/2,&duty_ cycle_high ; set duty cycles
mov.w #DO/2,&duty_cycle_low ; << adjust volume here >>
; rra.w r4 ; 50% duty cycle ; mov.w r4,r5
mov.b volume,r5 ; get volume mov.w duty(r5),r5 ; r5 = duty cycle
sub.w r5,r4 ; subtract from frequency cmp.w #0,r5 ; turn off?
jne WD04 ; n
mov.w #0,r4 ; y, high=low=0
WD04: mov.w r4,&TBCCR0 ; start clock (if necessary) mov.w r4,&duty_cyc le_high ; set high duty cycle
mov.w r5,&duty_cyc le_low ; set low duty cycle TB_isr: ; timer B ISR
bic.w #TBIFG,&TBCT L ; acknowledge interrupt xor.b #0x20,&P4OUT ; pulse buzzer
xor.b #1,&duty_cyc le_toggle ; high? jeq TB02 ; n
mov.w &duty_cycle_ high,&TBCCR0 ; y, set high duty cycle jmp TB04
TB02: mov.w &duty_cycle_ low,&TBCCR0 ; set low duty cycle TB04: reti ; return from interrupt
.sect ".int12" ; timer B section .word TB_isr ; timer B isr
BYU CS/ECEn 124 MSP430 Examples 32
Put transducer (P4.5) as secondary function
Change WD ISR to just put the duty cycle in TBCCR2
Step
12
b: Use Timer Output PWM
bis.b #0x20,&P4SEL ; P4.5 transducer output mov.w #OUTMOD_3,&TBCCTL2 ; output mode = set/reset mov.w #DO/2,&TBCCR2 ; use TBCCR2 as volume
; << adjust timer b tone here >>
push r5 ; save r5
add.w r4,r4 ; (word index)
mov.w scale(r4),r4 ; r4 = tone (frequency)
mov.w r4,&TBCCR0 ; start clock (if necessary)
mov.b volume,r5 ; get volume
add.w r5,r5 ; (word index)
mov.w duty(r5),r5 ; r5 = duty cycle
mov.w r5,&TBCCR2 ; start clock (if necessary)
pop r5
Example 8: H /W PWM w/Timer_B
Timer B output used to modulate transducer at 440 Hz
.cdecls C,LIST,"msp430x22x4.h"
SMCLK
.set 1200000
; 1200000 clocks / second
TIME_A3 .set 1000000/440/2/2
; A = 440 Hz (2x)
TB_CTL .set TBSSEL_2+ID_0+MC_1
; SMCLK,/1,UP (No interrupts)
TB_FREQ .set SMCLK/TIME_A3
; clocks / 440 Hz
reset: mov.w #0x0600,SP
; initialize SP
mov.w #WDTPW+WDTHOLD,&WDTCTL ; stop WDT
bis.b #0x20,&P4DIR
; P4.5 transducer output
bis.b #0x20,&P4SEL
; P4.5 timerB output
clr.w &TBR
; reset timerB
mov.w #TB_CTL,&TBCTL
; set timerB control reg
mov.w #OUTMOD_3,&TBCCTL2
; TB2 output mode = set/reset
mov.w #TB_FREQ,&TBCCR0
; set interval (frequency)
mov.w #TB_FREQ/2,&TBCCR2
; load volume (duty cycle)
bis.w #LPM0+GIE,SR
; enter LPM0 w/interrupts
jmp
$
; will never get here!
.sect ".reset"
; reset vector section
.word reset
; reset vector ISR
.end
Pulse Width Modulation (H /W)
SetP4.5as output from TB2 UPmode, TB2 set/reset 50%Duty Cycle
BYU CS/ECEn 124 MSP430 Examples 34
Example 9: H /W PWM w/Timer_A
#include "msp430x22x4.h" #include <stdio.h> #include "eZ430X.h" #include "lcd.h" #include "adc.h" void main(void) {eZ430X_init(CALDCO_8MHZ); // init board ADC_init(); // init ADC
lcd_init(); // init LCD
P2DIR |= 0x10; // P2.4 speaker output P2SEL |= 0x10; // P2.4 TA2 output TAR = 0; // reset timer A
TACTL = TASSEL_2 + ID_2 + MC_1; // SMCLK, /4, UP (no interrupts) TACCTL2 = OUTMOD_3; // TA2 = set/reset
while (1) {
uint16 freq = (1023 - ADC_read(LEFT_POT)) << 6;
uint16 duty = ((long)freq * (1023 - ADC_read(RIGHT_POT)) >> 10; TACCR0 = freq; // frequency
TACCR2 = duty; // duty cycle lcd_cursor(10, 4); lcd_printf("Freq:%u ", freq); lcd_cursor(10, 2); lcd_printf("Duty:%u ", duty); } } // end main
Pulse Width Modulation (H /W)
SetP2.4 as output from TA2 Freq = LeftPot Duty Cycle = RightPot SMCLK, 1 /4, UPmode, TA2 set/reset
Example 9: H /W PWM w/Timer_A
#include "msp430x22x4.h" #include <stdio.h> #include "eZ430X.h" #include "lcd.h" #include "adc.h" void main(void) {eZ430X_init(CALDCO_8MHZ); // init board
ADC_init(); // init ADC
lcd_init(); // init LCD
P2DIR |= 0x10; // P2.4 speaker output
P2SEL |= 0x10; // P2.4 TA2 output
TAR = 0; // reset timer A
TACTL = TASSEL_2 + ID_2 + MC_1; // SMCLK, /4, UP (no interrupts) TACCTL2 = OUTMOD_3; // TA2 = set/reset
while (1) {
uint16 freq = (1023 - ADC_read(LEFT_POT)) << 6;
uint16 duty = ((long)freq * (1023 - ADC_read(RIGHT_POT)) >> 10; TACCR0 = freq; // frequency
TACCR2 = duty; // duty cycle lcd_cursor(10, 4);
lcd_printf("Duty/Freq:%u/%u ", duty, freq); }
} // end main
Pulse Width Modulation (H /W)
SetP2.4 as output from TA2 Freq = LeftPot Duty Cycle = RightPot SMCLK, 1 /4, UPmode, TA2 set/reset