-2

My task is to take a functional program written by my instructor in assembly language and convert it to C language. The program is written for the TI MSP430G2553, and utilizes an attached 4-digit LED display with three side-by-side push-buttons. The display is supposed to start blank. When the right button is pressed, "ECE-3362" will begin to scroll across the display from right to left.

My conversion is 'complete' as far as I can tell and the code compiles with no errors. However, the push-buttons do not have any visible effect and the display remains blank.

I do get a few "Integer conversion resulted in truncation" warnings, but I'm not sure of the effect of this on the functionality of the program. At most I would assume this would cause the display to show the wrong values if part of my constant definitions get truncated.

One part of my code that is particularly suspicious to me is my port2 interrupt service routine at the very bottom (mainly the PB & P2_IFG comparisons). I didn't fully comprehend what was going on in my instructor's assembly version of that section, so something important may have been lost in translation.

I am new to assembly in general, and C as far as microcontrollers go. I was leaning heavily on my instructors example code as a reference. I'll put my code below, and I have my instructor's code on hand if anyone would like to see it.

I know the rules say not to post entire files but as far as I can tell I could have a mistake or more just about anywhere in the program.

My code:

#include  <msp430g2553.h>
//-----------------------------------------------------------------------------
// Definition of Constants
//-----------------------------------------------------------------------------

#define TIMER_A0_COUNT_1 2000     //2000
#define TIMER_A1_COUNT_1 50000   //50000

#define MAX_TIMER_COUNT 10       //10

#define LONG_DELAY (0xFFFF)          //65535

//definitions of segment positions 
#define SEG_A  (0x01u)   // 00000001  Port pin position P1.0
#define SEG_B  (0x02u)   // 00000010  Port pin position P1.1
#define SEG_C  (0x04u)   // 00000100  Port pin position P1.2
#define SEG_D  (0x08u)   // 00001000  Port pin position P1.3
#define SEG_E  (0x10u)   // 00010000  Port pin position P1.4
#define SEG_F  (0x20u)   // 00100000  Port pin position P1.5
#define SEG_G  (0x40u)   // 01000000  Port pin position P1.6
#define SEG_DP (0x80u)   // 10000000  Port pin position P1.7

//since inverted pattern is needed for the display, this defines inverse pattern
#define SEG_A_N           ~SEG_A  // Port pin position P1.0
#define SEG_B_N           ~SEG_B  // Port pin position P1.1
#define SEG_C_N           ~SEG_C  // Port pin position P1.2
#define SEG_D_N           ~SEG_D  // Port pin position P1.3
#define SEG_E_N           ~SEG_E  // Port pin position P1.4
#define SEG_F_N           ~SEG_F  // Port pin position P1.5
#define SEG_G_N           ~SEG_G  // Port pin position P1.6
#define SEG_DP_N          ~SEG_DP // Port pin position P1.7

//NOTE: display board requires INVERSE of these patterns due to Active LOW
#define DIG_3  (0x01u)    //   00000001  Port pin position P2.0 (MSdigit)
#define DIG_2  (0x02u)    //   00000010  Port pin position P2.1
#define DIG_1  (0x04u)    //   00000100  Port pin position P2.2
#define DIG_0  (0x08u)    //   00001000  Port pin position P2.3(LSdigit)
#define DP_COM (0x10u)    //   00010000  Port pin position P2.4

//since inverted pattern is needed for the display, this defines inverse pattern
#define DIG_3_N      ~DIG_3  // Port pin position P2.0 (MSdigit)
#define DIG_2_N      ~DIG_2  // Port pin position P2.1
#define DIG_1_N      ~DIG_1  // Port pin position P2.2
#define DIG_0_N      ~DIG_0  // Port pin position P2.3(LSdigit)
#define DP_COM_N     ~DP_COM // Port pin position P2.4


//Pushbutton assignments CORRECTED to compensate for board layout swap
#define PB_0  (0x20u)     //  00100000  Port pin position P2.5  RightMost button
#define PB_1  (0x80u)     //  10000000  Port pin position P2.7  Middle button
#define PB_2  (0x40u)     //  01000000  Port pin position P2.6  LeftMost button

#define SEG_PORT  P1OUT
#define DIG_PORT  P2OUT
#define PB_PORT   P2IN

//NOTE: display bd requires the INVERSE of these patterns due to Active LOW
#define ONE                (0x06u) // 00000110
#define TWO                (0x5Bu) // 01011011
#define THREE              (0x4Fu) // 01001111
#define FOUR               (0x66u) // 01100110
#define FIVE               (0x6Du) // 01101101
#define SIX                (0x7Du) // 01111101
#define SEVEN              (0x03u) // 00000111
#define EIGHT              (0x7Fu) // 01111111
#define NINE               (0x67u) // 01100111
#define ZERO               (0x3Fu) // 00111111

//since inverted pattern is needed for the display, this defines inverse pattern
#define ONE_N        (~0x06u)       // ~00000110
#define TWO_N        (~0x5Bu)       // ~01011011
#define THREE_N      (~0x4Fu)       // ~01001111
#define FOUR_N       (~0x66u)       // ~01100110
#define FIVE_N       (~0x6Du)       // ~01101101
#define SIX_N        (~0x7Du)       // ~01111101
#define SEVEN_N      (~0x03u)       // ~00000111
#define EIGHT_N      (~0x7Fu)       // ~01111111
#define NINE_N       (~0x67u)       // ~01100111
#define ZERO_N       (~0x3Fu)       // ~00111111

//other figures for scrolling display
#define E_N          (~0x79u)    //  ~01111001
#define C_N          (~0x39u)    //  ~00111001
#define DASH_N       (~0x40u)    //  ~01000000
#define BLANK_N      (~0x00u)    //  ~00000000



//------------------------------------------------------------------------------
// Definition of Variables
//------------------------------------------------------------------------------

int DisplayValue  =  0;         // contains 4 digit value to display in BCD format
                                // BCDdig3 | BCDdig2  | BCDdig1  | BCDdig0
                                // xxxx      xxxx       xxxx       xxxx

char CurrentDigitPos  =  0;     // global variable used by WriteDigitToDisplay ISR
                                // holds digit position of current digit to write

char CurrentDigitValue = 0;     // global variable used by WriteDigitToDisplay ISR
                                // holds digit value of next digit to write    
char StartFlag     =   0;       // Boolean state flags
char PauseFlag     =   0;
char ContinueFlag  =   0;
char ScrollingStateFlag  =  0;

char PB_0_Mode       =   0;
char PB_1_Mode       =   0;
char PB_2_Mode       =   0;

char Hundred_mS      =   0;
char TotalINTCount1  =   0;

int PatternsToScroll[12] = {0x0000, 0x0006, 0x0065, 0x0656, 0x6561, 0x5613, 
0x6133, 0x1334, 0x3342, 0x3420, 0x4200, 0x2000};

char PatternsIndex = 0;
int CurrentPattern = 0;
char PatternsLeft = 12;

char SegPatterns[7] = {BLANK_N, DASH_N, TWO_N, THREE_N, SIX_N, C_N, E_N};

//-----------------------------------------------------------------------------
// Functions
//-----------------------------------------------------------------------------
void WriteNextDigitToDisplay(int DisplayValue, char CurrentDigitPos, char CurrentDigitValue)
{
  int DisplayValueCopy = 0; // initialize function variable

  DIG_PORT |= DIG_0+DIG_1+DIG_2+DIG_3+DP_COM; // eliminate ghosting

  if ((CurrentDigitPos - 0) == 0)
  {
    DisplayValueCopy = DisplayValue;
    DisplayValueCopy &= 0x000F;
    SEG_PORT = SegPatterns[DisplayValueCopy];
    DIG_PORT = DIG_0_N;
    CurrentDigitPos++;
  }

  if ((CurrentDigitPos - 1) == 0)
  {
    DisplayValueCopy = DisplayValue;
    DisplayValueCopy &= 0x00F0;
    DisplayValueCopy >>=4;               //rra 4 times to get val into LSnibble
    SEG_PORT = SegPatterns[DisplayValueCopy];
    DIG_PORT = DIG_1_N;
    CurrentDigitPos++;
  }

  if ((CurrentDigitPos - 2) == 0)
  {
    DisplayValueCopy = DisplayValue;
    DisplayValueCopy &= 0x0F00;
    DisplayValueCopy = __swap_bytes(DisplayValueCopy);
    SEG_PORT = SegPatterns[DisplayValueCopy];
    DIG_PORT = DIG_2_N;
    CurrentDigitPos++;
  }

  if ((CurrentDigitPos - 3) == 0)
  {
    DisplayValueCopy = DisplayValue;
    DisplayValueCopy &= 0xF000;
    DisplayValueCopy = __swap_bytes(DisplayValueCopy);
    DisplayValueCopy >>=4;
    SEG_PORT = SegPatterns[DisplayValueCopy];
    DIG_PORT = DIG_3_N;
    CurrentDigitPos++;
  }

  if ((CurrentDigitPos - 4) == 0)
  {
    CurrentDigitPos = 0;
  }
}

void delay()
{
  for (int i = 0; i < LONG_DELAY; i++);
}

int main( void )
{
  //---------------------------------------------------------------------------
  // Setup
  //---------------------------------------------------------------------------

  // Stop watchdog timer to prevent time out reset
  WDTCTL = WDTPW + WDTHOLD;

  // Setup Port 1 (all outputs for segment display)
  P1DIR = SEG_A + SEG_B + SEG_C + SEG_D + SEG_E + SEG_F + SEG_G + SEG_DP;

  // Setup Port 2 
  P2DIR = 0x1F; // (00011111 : 3MSbits as inputs (pushbuttons) 5LSbits as outputs)
  P2OUT = PB_0 + PB_1 + PB_2;  // 11100000 or 0xE0 defines pushbutton positions
  P2REN |= PB_0 + PB_1 + PB_2; // turn on internal pull-up for the pushbuttons

  // Activate the General Purpose Digital I/O mode for P2.6 and P2.7
  P2SEL &= ~PB_1 + ~PB_2;     

  // Setup Port 2 interrupts for the pushbuttons
  P2IE |= PB_0 + PB_1 + PB_2;
  P2IES |= PB_0 + PB_1 + PB_2;

  // Turn off all the segments and digits
  SEG_PORT = 0xFF;
  DIG_PORT = 0xFF;

  // SetupCalibratedClock       
  // Set up the clock (calibrated mode at 1 MHz)
  // Get the calibrated data for the DCO clock
  // Set DCO to 1 MHz:  (this directly from TI Family Guide page283 and 284
          DCOCTL = 0;                // Select lowest DCOx  and MODx settings
          BCSCTL1 = CALBC1_1MHZ;     // Set range
          DCOCTL = CALDCO_1MHZ;      // Set DCO step + modulation     

  // Set up Timers

  // TimerA0
  TA0CCR0 = TIMER_A0_COUNT_1;              // load a count "up to"value into timer
  TA0CTL = TASSEL_2+ID_3 + MC_1;        // SMCLK, input div = 8, upmode
  TA0CCTL0 = CCIE;                        //  interrupt enabled for Timer0

  // TimerA1
  TA1CCR0 = TIMER_A1_COUNT_1;              // load a count "up to"value into timer
  TA1CTL = TASSEL_2+ID_3 + MC_1;        // SMCLK, input div = 8, upmode
  TA1CCTL0 = CCIE;                        //  interrupt enabled for Timer1 

  // Start of main program   

  // Initialize Boolean state flags and some other variables
            StartFlag = 0;
            PauseFlag = 0;
            ContinueFlag = 0;
            ScrollingStateFlag  = 0;
            PB_0_Mode = 0;      
            PB_1_Mode = 0; 
            PB_2_Mode = 0;   

            DisplayValue = 0;  


  // Clear Interrupt Flags
           P1IFG = 0;    // clear the Int flag register for Port 1 
           P2IFG = 0;    // clear the Int flag register for Port 2 

  // Enable General Interrupts
            _BIS_SR(GIE);         // enable the general interrupts bit   

  //----------------------------------------------------------------------------
  // Top of main program loop structure
  //----------------------------------------------------------------------------  

    while(1)  // forever loop
    {

      // test the Pushbutton mode Boolean variables to see what to do 
      if ((PB_0_Mode - 1) == 0) // (START CONDITION)
      {
        // Rightmost button (START)
        PB_0_Mode = 0;
        ScrollingStateFlag = 1; // make it TRUE
        PatternsIndex = 0; // beginning of pattern array
        PatternsLeft = 12;
        CurrentPattern = PatternsToScroll[PatternsIndex]; // might be redundant
      }

      if ((PB_1_Mode - 1) == 0) // (CONTINUE CONDITION)
      {
        // Middle button (CONTINUE)
        PB_1_Mode = 0;
        ScrollingStateFlag = 1; // make it TRUE
      }

      if ((PB_2_Mode - 1) == 0) // (PAUSE CONDITION)
      {
        // Leftmost button (PAUSE)
        PB_2_Mode = 0;
        ScrollingStateFlag = 0; // make it FALSE
      }

      else
      {
        if ((ScrollingStateFlag - 1) == 0)
        {
         CurrentPattern = PatternsToScroll[PatternsIndex];
         DisplayValue = CurrentPattern; // save pattern array element
         PatternsIndex++;               // move to next element
         PatternsLeft--;                // one less pattern to display
         if ((PatternsLeft - 0) == 0)   // done all the patterns --> reset variables
         {
           PatternsIndex = 0;
           CurrentPattern = PatternsToScroll[PatternsIndex]; // might be redundant
           PatternsLeft = 12;
         }
         delay();   // update the scrolling slowly
         delay();        
        }
      }
    }





            return 0;
} // end of MAIN

//------------------------------------------------------------------------------
//           Subroutines
//------------------------------------------------------------------------------

//-------------------------------------------------------------------------------
// WriteNextDigitToDisplay
//  passed in - DisplayValue, CurrentDigitPos 
//  returned - nothing
//  accomplishes - Writes next digit to the expansion bd display
//  uses: R15, global variable CurrentDigitPos, CurrentDigitValue
//-------------------------------------------------------------------------------

//-------------------------------------------------------------------------------
//          Interrupt Service Routines
//-------------------------------------------------------------------------------
//-------------------------------------------------------------------------------
// Interrupt Service Routine for Timer_A 1
//  Passed in: nothing
//  Activated every time TimerA_1 times out
//  Updates global variable TotalINTCount1 to keep track of number of TimerA_1 
//   interrupt events
//  Uses: nothing except modifies global variable TotalINTCount
//  For this example, set up to trigger every 100 mS
//-------------------------------------------------------------------------------
//Timer0_A0 ISR
#pragma vector=TIMER0_A0_VECTOR   // this line tells the C compiler to put
                                  // the start address of the following ISR
                                  // into the Interupt Vector table

__interrupt void Timer_A0_ISR (void)   // required syntax for first line of ISR
{
  WriteNextDigitToDisplay(DisplayValue, CurrentDigitPos, CurrentDigitValue);
}

//Timer0_A1 ISR
#pragma vector=TIMER1_A1_VECTOR   // this line tells the C compiler to put
                                  // the start address of the following ISR
                                  // into the Interupt Vector table

__interrupt void Timer_A1_ISR (void)   // required syntax for first line of ISR
{
  Hundred_mS++;
  TotalINTCount1++;
}

// Port2_ISR
//  passed in - nothing
//  returned - nothing
//  accomplishes - updates global Boolean variables for Pushbutton status
//  uses: nothing
//-------------------------------------------------------------------------------
//Port2_ISR
    // if we get to here, an interrupt occurred on the Port 2
#pragma vector=PORT2_VECTOR
__interrupt void Port_2(void)
{
  if ((PB_0 & P2IFG) == 1)
  {
    PB_0_Mode |= 1;
    PB_1_Mode &= ~1;
    PB_2_Mode &= ~1;
  }

  if ((PB_1 & P2IFG) == 1)
  {
    PB_0_Mode &= ~1;
    PB_1_Mode |= 1;
    PB_2_Mode &= ~1;
  }

  if ((PB_2 & P2IFG) == 1)
  {
    PB_0_Mode &= ~1;
    PB_1_Mode &= ~1;
    PB_2_Mode |= 1;
  }

  P2IFG = 0;
}

Can't post instructor's entire code due to character limit. Here is his port 2 ISR:

; Port2_ISR
;  passed in - nothing
;  returned - nothing
;  accomplishes - updates global Boolean variables for Pushbutton status
;  uses: nothing
;-------------------------------------------------------------------------------
Port2_ISR
    ; if we get to here, an interrupt occurred on the Port 2
    bit.b  #PB_0, &P2IFG   ; PB_0 Pushbutton?  (if 1 it is pressed)
    jnz  PB_0_Case        ; it is PB_0
    ;no so try the next case

    bit.b  #PB_1, &P2IFG   ; PB_1 Pushbutton?  (if 1 it is pressed)
    jnz  PB_1_Case        ; it is PB_1
    ;no so try the next case

    bit.b  #PB_2, &P2IFG   ; PB_2 Pushbutton?  (if 1 it is pressed)
    jnz  PB_2_Case        ; it is PB_2
    jmp  DoneWithPort_2_ISR       ; no, so don't do anything

PB_0_Case
    bis.b  #1,  &PB_0_Mode 
    bic.b  #1,  &PB_1_Mode   ;clear other modes
    bic.b  #1,  &PB_2_Mode        
    jmp DoneWithPort_2_ISR

PB_1_Case
    bis.b  #1,  &PB_1_Mode
    bic.b  #1,  &PB_0_Mode   ;clear other modes
    bic.b  #1,  &PB_2_Mode        
    jmp DoneWithPort_2_ISR    

PB_2_Case
    bis.b  #1,  &PB_2_Mode
    bic.b  #1,  &PB_1_Mode   ;clear other modes
    bic.b  #1,  &PB_0_Mode        
    jmp DoneWithPort_2_ISR    

DoneWithPort_2_ISR   
    clr.b &P2IFG    ; clear the flag so system is ready for another interrupt

    reti         ; return from interrupt
;-------------------------------------------------------------------------------
;  end of Port2_ISR
;-------------------------------------------------------------------------------


;-------------------------------------------------------------------------------
Ian Preglo
  • 421
  • 2
  • 10
jettg
  • 15
  • 2
  • 2
    Do you have a debugger, JTAG or similar? If not, you will have a very hard time with this. Embedded developers do not get bugs out by staring at a wall of code:( – Martin James Apr 18 '19 at 07:04
  • I'm using the IAR embedded workbench IDE built-in debugger. Are you suggesting I step through the program line-by-line and observe the values in memory? If you're talking about a physical hardware debugger then no, unfortunately I don't have that. Sadly my instructor seems to believe that I should be able to do just that - "get bugs out by staring at a wall of code." – jettg Apr 18 '19 at 07:29
  • To say that code compiles means absolutely nothing. To compile, code merely has to satisfy the syntactic requirements of a language. Whether the logic makes sense or not is an entirely different thing. – dandan78 Apr 18 '19 at 13:53

2 Answers2

0

You have set all your PB_N_MODE to 0 and checking if subtracting 1 to those will equate to 0, can't you set them to 1 or perhaps check if they equate to -1?

PB_1_MODE = 1
PB_2_MODE = 1
...
Ian Preglo
  • 421
  • 2
  • 10
  • The PB_N_MODE variables are intended to act like booleans. I check in the main loop whether subtracting 1 from them equals 0 to see if they are 1 (true). They are initialized to 0 (false) but are supposed to change to 1 if the corresponding button is pressed. The Port2_ISR at the bottom of my code is supposed to recognize the button press and make the change but I'm not sure I did that correctly. At least, that's where I suspect my problem might be simply because that's the part of my code that I understand the least. – jettg Apr 18 '19 at 08:13
  • I see, so you've confirmed that setting those to `1` displays something? – Ian Preglo Apr 18 '19 at 08:21
  • Ideally, yes. Pressing the right-most button on the board should set PB_0_MODE to 1, in turn causing the condition ```if ((PB_0_Mode - 1) == 0)``` to be satisfied, and starts the scrolling display. But when I push the button on my board, nothing happens. – jettg Apr 18 '19 at 08:32
0
#define PB_0  (0x20u)

  if ((PB_0 & P2IFG) == 1)

The value of the expression PB_0 & P2IFG is either 0x20 or 0; it can never be 1.

All your ifs have the same structure (if ((x) == 0) or if ((x) == 1)), which is confusing and can lead to errors. You should treat the x properly as a boolean expression, and use if (x) to check for a non-zero value, or if (!(x)) for zero.

CL.
  • 173,858
  • 17
  • 217
  • 259