I am trying to use an MSP-EXP3430G2ET with a Buzzer and a potentiometer on CCS. The goal of the program is to have 2 modes, a high frequency mode and a low frequency mode. If you press the button, it will change from one mode to the other and the potentiometer is meant to alter the frequency values inside the mode. Here is the working C version. Now I need to do the same but in assembley.
#include <msp430.h>
#define BUZZER_PIN1 BIT1 // P1.1
#define BUZZER_PIN2 BIT2 // P1.2
#define BUTTON_PIN BIT3 // P1.3
#define POT_PIN BIT0 // P1.0
// Frequency ranges
#define LOW_FREQ_MIN 300
#define LOW_FREQ_MAX 1000
#define HIGH_FREQ_MIN 1000
#define HIGH_FREQ_MAX 3000
// Global variable
volatile unsigned char modeHigh = 1; // 1 = high freq mode, 0 = low freq mode
// Function prototypes
void setFrequency(unsigned int freq);
unsigned int readPot(void);
int main(void)
{
WDTCTL = WDTPW | WDTHOLD; // Stop watchdog timer
// Clock 1 MHz
DCOCTL = CALDCO_1MHZ;
// Ports
P1DIR |= BUZZER_PIN1 | BUZZER_PIN2; // Buzzer pins as output
P1SEL |= BUZZER_PIN1 | BUZZER_PIN2; // Connect buzzer pins to TimerA
// Button setup (P1.3)
P1DIR &= ~BUTTON_PIN; // input
P1REN |= BUTTON_PIN; // enable pull-up/down
P1OUT |= BUTTON_PIN; // pull-up
P1IES |= BUTTON_PIN; // falling edge trigger
P1IFG &= ~BUTTON_PIN; // clear interrupt flag
P1IE |= BUTTON_PIN; // enable interrupt
// ADC setup (P1.0)
ADC10CTL1 = INCH_0; // channel A0
ADC10CTL0 = SREF_0 + ADC10SHT_3 + ADC10ON; // V+ = Vcc, ADC on
// TimerA setup
TA0CTL = TASSEL_2 + MC_1; // SMCLK, up mode
TA0CCTL0 = OUTMOD_4; // Toggle output (P1.1)
TA0CCTL1 = OUTMOD_3; // Set/Reset output (P1.2)
// Initial frequency
setFrequency(HIGH_FREQ_MIN);
__enable_interrupt();
// Main loop: continuously read potentiometer and update frequency
while(1)
{
unsigned int pot = readPot();
unsigned int freq;
if (modeHigh)
{
// High frequency mode
freq = HIGH_FREQ_MIN + ((float)pot / 1023) * (HIGH_FREQ_MAX - HIGH_FREQ_MIN);
}
else
{
// Low frequency mode
freq = LOW_FREQ_MIN + ((float)pot / 1023) * (LOW_FREQ_MAX - LOW_FREQ_MIN);
}
setFrequency(freq);
__delay_cycles(1000); // small delay to reduce CPU usage
}
}
// Button ISR with debounce
#pragma vector=PORT1_VECTOR
__interrupt void Port1_ISR(void)
{
if (P1IFG & BUTTON_PIN)
{
__delay_cycles(50000); // ~50ms debounce at 1MHz
if (!(P1IN & BUTTON_PIN)) // still pressed
{
modeHigh ^= 1; // toggle mode
}
P1IFG &= ~BUTTON_PIN; // clear interrupt flag
}
}
// Set buzzer frequency
void setFrequency(unsigned int freq)
{
if (freq == 0) return;
unsigned int period = 1000000 / (2 * freq); // half-period for toggle
TA0CCR0 = period;
TA0CCR1 = period / 2;
}
// Read potentiometer (0–1023)
unsigned int readPot(void)
{
ADC10CTL0 |= ENC + ADC10SC; // start conversion
while (ADC10CTL1 & ADC10BUSY); // wait
return ADC10MEM; // return 0–1023
}
This is the non working assembly code. I managed to get the buzzer working but it sounds cracky.
;*******************************************************************************
.cdecls C,LIST, "msp430.h"
;-------------------------------------------------------------------------------
.def RESET ; Export program entry-point to
; make it known to linker.
;------------------------------------------------------------------------------
.text ; Progam Start
;------------------------------------------------------------------------------
RESET mov.w #0280h,SP ; Initialize stackpointer
;-------------------------------------------------------------------------------
; Global Variables
;-------------------------------------------------------------------------------
.bss modeHigh, 1
.bss potValue, 2
.bss freqValue, 2
;-------------------------------------------------------------------------------
; Code Section
;-------------------------------------------------------------------------------
.text
.global _main
.global Port1_ISR
_main:
; Stop watchdog
mov.w #WDTPW+WDTHOLD, &WDTCTL
; DCO ~1 MHz
mov.b &CALBC1_1MHZ, &BCSCTL1
mov.b &CALDCO_1MHZ, &DCOCTL
; Buzzer pins P1.1 + P1.2
bis.b #0x06, &P1DIR
bis.b #0x06, &P1SEL
; Button P1.3 input with pull-up + interrupt
bic.b #0x08, &P1DIR
bis.b #0x08, &P1REN
bis.b #0x08, &P1OUT
bis.b #0x08, &P1IES
bic.b #0x08, &P1IFG
bis.b #0x08, &P1IE
; ADC10 A0 setup
mov.w #INCH_0, &ADC10CTL1
mov.w #SREF_0 + ADC10SHT_3 + ADC10ON, &ADC10CTL0
bic.w #ADC10IE, &ADC10CTL0
bis.b #0x01, &ADC10AE0
; TimerA config
mov.w #TASSEL_2 + MC_1, &TA0CTL
mov.w #OUTMOD_4, &TA0CCTL0
mov.w #OUTMOD_3, &TA0CCTL1
; Initial high mode
mov.b #1, modeHigh
; Start at 1000 Hz
mov.w #1000, R12
CALL setFrequency
eint
;-------------------------------------------------------------------------------
; Main Loop
;-------------------------------------------------------------------------------
main_loop:
CALL readPot
mov.w potValue, R12
mov.b modeHigh, R13
cmp.b #1, R13
jne low_mode
; --- HIGH MODE ---
mov.w R12, R14
mov.w #2000, R15
CALL scale1023
add.w #1000, R14
mov.w R14, R12
jmp freq_done
; --- LOW MODE ---
low_mode:
mov.w R12, R14
mov.w #700, R15
CALL scale1023
add.w #300, R14
mov.w R14, R12
freq_done:
mov.w R12, freqValue
CALL setFrequency
; Small delay
mov.w #1000, R15
delay_loop:
dec.w R15
jnz delay_loop
jmp main_loop
;-------------------------------------------------------------------------------
; Subroutines
;-------------------------------------------------------------------------------
; Read ADC (A0) → potValue
readPot:
bis.w #ENC + ADC10SC, &ADC10CTL0
wait_adc:
bit.w #ADC10BUSY, &ADC10CTL0
jnz wait_adc
mov.w &ADC10MEM, potValue
ret
; Set frequency Hz in R12
setFrequency:
cmp.w #0, R12
jeq sf_exit
mov.w CONST_1M, R14 ; load 1000000
mov.w R12, R15
CALL divide_2f
mov.w R14, &TA0CCR0
rra.w R14
mov.w R14, &TA0CCTL1
sf_exit:
ret
;-------------------------------------------------------------------------------
; NEW SOFTWARE scale1023 (no hardware multiplier)
; R14 = value, R15 = range → R14 = (value * range) / 1023
;-------------------------------------------------------------------------------
scale1023:
clr.w R12 ; product low
clr.w R13 ; product high
mov.w R14, R11 ; multiplicand
mov.w R15, R10 ; multiplier
; --- 16-bit software multiply ---
scale_mul_loop:
bit.w #1, R11
jz scale_no_add
add.w R10, R12
addc.w #0, R13
scale_no_add:
rla.w R10
rrc.w R11
jnz scale_mul_loop
; Now R13:R12 = product
mov.w #1023, R10
clr.w R11
; --- Divide 32-bit by 1023 ---
scale_div_loop:
cmp.w #0, R13
jne scale_do_sub
cmp.w R10, R12
jc scale_done
scale_do_sub:
sub.w R10, R12
subc.w #0, R13
inc.w R11
jmp scale_div_loop
scale_done:
mov.w R11, R14
ret
;-------------------------------------------------------------------------------
; divide_2f : R14 = 1000000 / (2*R15)
;-------------------------------------------------------------------------------
divide_2f:
add.w R15, R15
mov.w R14, R13
mov.w R15, R14
mov.w R13, R15
CALL div16
mov.w R15, R14
ret
; 16-bit divide (unsigned): R15 / R14 → R15
div16:
clr.w R13
div16_loop:
cmp.w R14, R15
jc div16_done
sub.w R14, R15
inc.w R13
jmp div16_loop
div16_done:
mov.w R13, R15
ret
;-------------------------------------------------------------------------------
; Port 1 ISR
;-------------------------------------------------------------------------------
Port1_ISR:
push.w SR
push.w R12
bit.b #0x08, &P1IFG
jz p1_exit
mov.w #50000, R12
deb1:
dec.w R12
jnz deb1
bit.b #0x08, &P1IN
jnz clear_flag
mov.b modeHigh, R12
xor.b #1, R12
mov.b R12, modeHigh
clear_flag:
bic.b #0x08, &P1IFG
p1_exit:
pop.w R12
pop.w SR
reti
;-------------------------------------------------------------------------------
; Constants
;-------------------------------------------------------------------------------
.data
CONST_1M:
.long 1000000
;-------------------------------------------------------------------------------
; Interrupt Vector Table
;-------------------------------------------------------------------------------
.sect ".int05" ; PORT1_VECTOR
.short Port1_ISR
.sect ".reset" ; Reset vector
.short _main
.end
WDTPW+WDTHOLDthe same asWDTPW|WDTHOLD? Even if it is, you've diverted from the C code at the first instruction.