2

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
New contributor
Sofia Happy Banya Ceregido is a new contributor to this site. Take care in asking for clarification, commenting, and answering. Check out our Code of Conduct.
3
  • 3
    I havn't got the header file, but is WDTPW+WDTHOLD the same as WDTPW|WDTHOLD? Even if it is, you've diverted from the C code at the first instruction. Commented yesterday
  • 3
    Can you compare the hand written assembly with the C disassembly? Commented yesterday
  • 3
    I was going to to suggest similar to @Erik. Print the two listings and place them side by side on the desk and work your way through them with a pencil, explaining each line to your "rubber duck". Commented yesterday

2 Answers 2

8
CONST_1M:
        .long   1000000
        mov.w   CONST_1M, R14   ; load 1000000

The largest 16-bit number is 65,535. The processor has 16 bit registers. This does not copy value you were hoping for.

You need to do 32 bit values in r14 and r15.

        .data
CONST_1M_H:    .word   0x000F
CONST_1M_L:    .word   0x4240

        mov.w   #CONST_1M_L, R14    ; low word 
        mov.w   #CONST_1M_H, R15    ; high word 

Then implement a divide 32bit instead of divide_16 & divide_2f. That is a non trivial endeavor.

Sign up to request clarification or add additional context in comments.

1 Comment

About the non-trivial division, you could consider the multiply-shift trick. It is sometimes 1 off, which may be acceptable in the use case, or can be detected and adjusted. It is based on a / b = a * k / 2ⁿ where the division is performed by shifting. k and n are tuned to give maximum headroom.
4

Should you care to eliminate the unneeded floating point code, use only integer math.

// freq = HIGH_FREQ_MIN + ((float)pot / 1023) * (HIGH_FREQ_MAX - HIGH_FREQ_MIN);
uint32_t pot32 = pot;
pot32 *= HIGH_FREQ_MAX - HIGH_FREQ_MIN;
freq = HIGH_FREQ_MIN + (unsigned) ((pot32 + 1023/2)/1023);

From what I see of OP's ASM, there might be an overflow (if unsigned is 16-bit) and lack of rounding to nearest in forming the quotient.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.